11cb0ef41Sopenharmony_ci'use strict'
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ci// The ABNF grammar in the spec is totally ambiguous.
41cb0ef41Sopenharmony_ci//
51cb0ef41Sopenharmony_ci// This parser follows the operator precedence defined in the
61cb0ef41Sopenharmony_ci// `Order of Precedence and Parentheses` section.
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_cimodule.exports = function (tokens) {
91cb0ef41Sopenharmony_ci  var index = 0
101cb0ef41Sopenharmony_ci
111cb0ef41Sopenharmony_ci  function hasMore () {
121cb0ef41Sopenharmony_ci    return index < tokens.length
131cb0ef41Sopenharmony_ci  }
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_ci  function token () {
161cb0ef41Sopenharmony_ci    return hasMore() ? tokens[index] : null
171cb0ef41Sopenharmony_ci  }
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ci  function next () {
201cb0ef41Sopenharmony_ci    if (!hasMore()) {
211cb0ef41Sopenharmony_ci      throw new Error()
221cb0ef41Sopenharmony_ci    }
231cb0ef41Sopenharmony_ci    index++
241cb0ef41Sopenharmony_ci  }
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ci  function parseOperator (operator) {
271cb0ef41Sopenharmony_ci    var t = token()
281cb0ef41Sopenharmony_ci    if (t && t.type === 'OPERATOR' && operator === t.string) {
291cb0ef41Sopenharmony_ci      next()
301cb0ef41Sopenharmony_ci      return t.string
311cb0ef41Sopenharmony_ci    }
321cb0ef41Sopenharmony_ci  }
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ci  function parseWith () {
351cb0ef41Sopenharmony_ci    if (parseOperator('WITH')) {
361cb0ef41Sopenharmony_ci      var t = token()
371cb0ef41Sopenharmony_ci      if (t && t.type === 'EXCEPTION') {
381cb0ef41Sopenharmony_ci        next()
391cb0ef41Sopenharmony_ci        return t.string
401cb0ef41Sopenharmony_ci      }
411cb0ef41Sopenharmony_ci      throw new Error('Expected exception after `WITH`')
421cb0ef41Sopenharmony_ci    }
431cb0ef41Sopenharmony_ci  }
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ci  function parseLicenseRef () {
461cb0ef41Sopenharmony_ci    // TODO: Actually, everything is concatenated into one string
471cb0ef41Sopenharmony_ci    // for backward-compatibility but it could be better to return
481cb0ef41Sopenharmony_ci    // a nice structure.
491cb0ef41Sopenharmony_ci    var begin = index
501cb0ef41Sopenharmony_ci    var string = ''
511cb0ef41Sopenharmony_ci    var t = token()
521cb0ef41Sopenharmony_ci    if (t.type === 'DOCUMENTREF') {
531cb0ef41Sopenharmony_ci      next()
541cb0ef41Sopenharmony_ci      string += 'DocumentRef-' + t.string + ':'
551cb0ef41Sopenharmony_ci      if (!parseOperator(':')) {
561cb0ef41Sopenharmony_ci        throw new Error('Expected `:` after `DocumentRef-...`')
571cb0ef41Sopenharmony_ci      }
581cb0ef41Sopenharmony_ci    }
591cb0ef41Sopenharmony_ci    t = token()
601cb0ef41Sopenharmony_ci    if (t.type === 'LICENSEREF') {
611cb0ef41Sopenharmony_ci      next()
621cb0ef41Sopenharmony_ci      string += 'LicenseRef-' + t.string
631cb0ef41Sopenharmony_ci      return { license: string }
641cb0ef41Sopenharmony_ci    }
651cb0ef41Sopenharmony_ci    index = begin
661cb0ef41Sopenharmony_ci  }
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_ci  function parseLicense () {
691cb0ef41Sopenharmony_ci    var t = token()
701cb0ef41Sopenharmony_ci    if (t && t.type === 'LICENSE') {
711cb0ef41Sopenharmony_ci      next()
721cb0ef41Sopenharmony_ci      var node = { license: t.string }
731cb0ef41Sopenharmony_ci      if (parseOperator('+')) {
741cb0ef41Sopenharmony_ci        node.plus = true
751cb0ef41Sopenharmony_ci      }
761cb0ef41Sopenharmony_ci      var exception = parseWith()
771cb0ef41Sopenharmony_ci      if (exception) {
781cb0ef41Sopenharmony_ci        node.exception = exception
791cb0ef41Sopenharmony_ci      }
801cb0ef41Sopenharmony_ci      return node
811cb0ef41Sopenharmony_ci    }
821cb0ef41Sopenharmony_ci  }
831cb0ef41Sopenharmony_ci
841cb0ef41Sopenharmony_ci  function parseParenthesizedExpression () {
851cb0ef41Sopenharmony_ci    var left = parseOperator('(')
861cb0ef41Sopenharmony_ci    if (!left) {
871cb0ef41Sopenharmony_ci      return
881cb0ef41Sopenharmony_ci    }
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_ci    var expr = parseExpression()
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ci    if (!parseOperator(')')) {
931cb0ef41Sopenharmony_ci      throw new Error('Expected `)`')
941cb0ef41Sopenharmony_ci    }
951cb0ef41Sopenharmony_ci
961cb0ef41Sopenharmony_ci    return expr
971cb0ef41Sopenharmony_ci  }
981cb0ef41Sopenharmony_ci
991cb0ef41Sopenharmony_ci  function parseAtom () {
1001cb0ef41Sopenharmony_ci    return (
1011cb0ef41Sopenharmony_ci      parseParenthesizedExpression() ||
1021cb0ef41Sopenharmony_ci      parseLicenseRef() ||
1031cb0ef41Sopenharmony_ci      parseLicense()
1041cb0ef41Sopenharmony_ci    )
1051cb0ef41Sopenharmony_ci  }
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci  function makeBinaryOpParser (operator, nextParser) {
1081cb0ef41Sopenharmony_ci    return function parseBinaryOp () {
1091cb0ef41Sopenharmony_ci      var left = nextParser()
1101cb0ef41Sopenharmony_ci      if (!left) {
1111cb0ef41Sopenharmony_ci        return
1121cb0ef41Sopenharmony_ci      }
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ci      if (!parseOperator(operator)) {
1151cb0ef41Sopenharmony_ci        return left
1161cb0ef41Sopenharmony_ci      }
1171cb0ef41Sopenharmony_ci
1181cb0ef41Sopenharmony_ci      var right = parseBinaryOp()
1191cb0ef41Sopenharmony_ci      if (!right) {
1201cb0ef41Sopenharmony_ci        throw new Error('Expected expression')
1211cb0ef41Sopenharmony_ci      }
1221cb0ef41Sopenharmony_ci      return {
1231cb0ef41Sopenharmony_ci        left: left,
1241cb0ef41Sopenharmony_ci        conjunction: operator.toLowerCase(),
1251cb0ef41Sopenharmony_ci        right: right
1261cb0ef41Sopenharmony_ci      }
1271cb0ef41Sopenharmony_ci    }
1281cb0ef41Sopenharmony_ci  }
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_ci  var parseAnd = makeBinaryOpParser('AND', parseAtom)
1311cb0ef41Sopenharmony_ci  var parseExpression = makeBinaryOpParser('OR', parseAnd)
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_ci  var node = parseExpression()
1341cb0ef41Sopenharmony_ci  if (!node || hasMore()) {
1351cb0ef41Sopenharmony_ci    throw new Error('Syntax error')
1361cb0ef41Sopenharmony_ci  }
1371cb0ef41Sopenharmony_ci  return node
1381cb0ef41Sopenharmony_ci}
139