diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6995712..1a479b0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,9 +42,12 @@ jobs: npm install - name: Test run: npm test + - name: Build + run: node bin/package.js - run: | VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,' | sed -e 's/^v//') - jq '.version="'"$VERSION"'"' package.json > /tmp/a + jq '.version="'"$VERSION"'"" | .main="./src/RuleParser.production.js"' package.json > /tmp/a + mv /tmp/a package.json if: "startsWith(github.ref, 'refs/tags/v')" - run: | diff --git a/.gitignore b/.gitignore index 40b878d..2ea8efb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules/ \ No newline at end of file +node_modules/ +RuleParser.production.js \ No newline at end of file diff --git a/bin/package.js b/bin/package.js new file mode 100644 index 0000000..46c4be4 --- /dev/null +++ b/bin/package.js @@ -0,0 +1,11 @@ +const fs = require('fs') + +const ParserRules = require('../src/RuleParser.ebnf.js') +const rules = JSON.stringify(ParserRules) + +fs.writeFileSync('src/RuleParser.ebnf.json', rules) + +const ruleParserJs = fs.readFileSync('src/RuleParser.js', 'utf8') +const ruleParserJsFixed = ruleParserJs.replace("require('./RuleParser.ebnf.js')", 'require(\'./RuleParser.ebnf.json\')') + +fs.writeFileSync('src/RuleParser.production.js', ruleParserJsFixed) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index edb37d0..f6b4867 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "rule-parser", - "version": "1.0.0", + "name": "@halleyassist/rule-parser", + "version": "0.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -42,9 +42,9 @@ "dev": true }, "assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, "balanced-match": { @@ -90,16 +90,18 @@ "dev": true }, "chai": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.0.tgz", - "integrity": "sha512-kDZ7MZyM6Q1DhR9jy7dalKohXQ2yrlXkk59CR52aRKxJrobmlBNqnFQxX9xOX8w+4mz8SYlKJa/7D7ddltFXCw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", "dev": true, "requires": { - "assertion-error": "^2.0.1", - "check-error": "^2.0.0", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" } }, "chalk": { @@ -124,10 +126,13 @@ } }, "check-error": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.0.0.tgz", - "integrity": "sha512-tjLAOBHKVxtPoHe/SA7kNOMvhCRdCJ3vETdeY0RuAc9popf+hyaSV6ZEg9hr4cpWF7jmo/JSWEnLDrnijS9Tog==", - "dev": true + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "requires": { + "get-func-name": "^2.0.2" + } }, "chokidar": { "version": "3.5.3", @@ -195,10 +200,13 @@ "dev": true }, "deep-eql": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.1.tgz", - "integrity": "sha512-nwQCf6ne2gez3o1MxWifqkciwt0zhl0LO1/UwVu4uMBuPmflWM4oQ70XMqHqnBJA+nhzncaqL9HVL6KkHJ28lw==", - "dev": true + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } }, "diff": { "version": "5.0.0", @@ -406,9 +414,9 @@ } }, "loupe": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.0.tgz", - "integrity": "sha512-qKl+FrLXUhFuHUoDJG7f8P8gEMHq9NFS0c6ghXG1J0rldmZFQZoNVv/vyirE9qwCIhWZDsvEFd1sbFu3GvRQFg==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, "requires": { "get-func-name": "^2.0.1" @@ -497,9 +505,9 @@ "dev": true }, "pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, "picomatch": { @@ -591,6 +599,12 @@ "is-number": "^7.0.0" } }, + "type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true + }, "workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", diff --git a/package.json b/package.json index c3192e9..a5ef6cd 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "ebnf": "^1.9.1" }, "devDependencies": { - "chai": "^4.4.1", + "chai": "^4", "mocha": "^10.4.0" }, "publishConfig": { diff --git a/src/RuleParser.ebnf.js b/src/RuleParser.ebnf.js new file mode 100644 index 0000000..1ecb19b --- /dev/null +++ b/src/RuleParser.ebnf.js @@ -0,0 +1,78 @@ +const {Grammars} = require('ebnf'); + +const grammar = ` +statement_main ::= statement EOF +logical_operator ::= AND | OR +statement ::= expression (logical_operator expression)* +expression ::= not_expression | standard_expression | parenthesis_expression +parenthesis_expression::= BEGIN_PARENTHESIS WS* statement WS* END_PARENTHESIS +not_expression ::= NOT (result | parenthesis_expression) +standard_expression ::= result ((WS* eq_approx) | (WS* basic_rhs) | ((WS+ IS)? WS+ between))? +basic_rhs ::= operator WS* result +eq_approx ::= eq_operator WS* "~" WS* result + +PLUS ::= "+" +MINUS ::= "-" +MULTIPLY ::= "*" +DIVIDE ::= "/" +MODULUS ::= "%" +DEFAULT_VAL ::= "??" +arithmetic_operator ::= PLUS | MINUS | MULTIPLY | DIVIDE | MODULUS | DEFAULT_VAL +arithmetic_result ::= simple_result WS* arithmetic_operator WS* ( arithmetic_result | simple_result ) + +simple_result ::= fcall | value +result ::= arithmetic_result | simple_result +value ::= false | true | array | number_time | number | number_tod | time_period | string +BEGIN_ARRAY ::= WS* #x5B WS* /* [ left square bracket */ +BEGIN_OBJECT ::= WS* #x7B WS* /* { left curly bracket */ +END_ARRAY ::= WS* #x5D WS* /* ] right square bracket */ +END_OBJECT ::= WS* #x7D WS* /* } right curly bracket */ +NAME_SEPARATOR ::= WS* #x3A WS* /* : colon */ +VALUE_SEPARATOR ::= WS* #x2C WS* /* , comma */ +WS ::= [#x20#x09#x0A#x0D]+ /* Space | Tab | \n | \r */ + +operator ::= GTE | LTE | GT | LT | EQ | NEQ +eq_operator ::= EQ | NEQ + +BEGIN_ARGUMENT ::= "(" +END_ARGUMENT ::= ")" + +BEGIN_PARENTHESIS ::= "(" +END_PARENTHESIS ::= ")" + +argument ::= statement WS* ("," WS*)? +arguments ::= argument* +fname ::= [a-zA-z0-9]+ +fcall ::= fname WS* BEGIN_ARGUMENT arguments? END_ARGUMENT + +between_number ::= number ((WS+ ("and" | "AND") WS+) | (WS* "-" WS*)) number +between_tod ::= number_tod ((WS+ ("and" | "AND") WS+)) number_tod +between ::= ("between" | "BETWEEN") WS+ (between_number | between_tod) + +AND ::= (WS* "&&" WS*) | (WS+ ("AND"|"and") WS+) +OR ::= (WS* "||" WS*) | (WS+ ("OR"|"or") WS+) +GT ::= ">" +LT ::= "<" +GTE ::= ">=" +LTE ::= "<=" +IS ::= "is" | "IS" +EQ ::= "==" | "=" +NEQ ::= "!=" +NOT ::= ("!" WS*) | ("not" WS+) +false ::= "false" | "FALSE" +null ::= "null" | "NULL" +true ::= "true" | "TRUE" +array ::= BEGIN_ARRAY (value (VALUE_SEPARATOR value)*)? END_ARRAY + +unit ::= "seconds" | "second" | "minutes" | "minute" | "min" | "mins" | "min" | "hours" | "hour" | "days" | "day" | "weeks" | "week" +number ::= "-"? ([0-9]+) ("." [0-9]+)? (("e" | "E") ( "-" | "+" )? ("0" | [1-9] [0-9]*))? +number_time ::= number WS+ unit +number_tod ::= ([0-9]+) ":" ([0-9]+) + +time_period_const ::= "today" +time_period ::= time_period_const | between + +string ::= '"' (([#x20-#x21] | [#x23-#x5B] | [#x5D-#xFFFF]) | #x5C (#x22 | #x5C | #x2F | #x62 | #x66 | #x6E | #x72 | #x74 | #x75 HEXDIG HEXDIG HEXDIG HEXDIG))* '"' +HEXDIG ::= [a-fA-F0-9] +` +module.exports = Grammars.W3C.getRules(grammar); \ No newline at end of file diff --git a/src/RuleParser.ebnf.json b/src/RuleParser.ebnf.json new file mode 100644 index 0000000..8db0529 --- /dev/null +++ b/src/RuleParser.ebnf.json @@ -0,0 +1 @@ +[{"name":"statement_main","bnf":[["statement","EOF"]]},{"name":"logical_operator","bnf":[["AND"],["OR"]]},{"name":"%statement0","bnf":[["logical_operator","expression"]],"fragment":true},{"name":"statement","bnf":[["expression","%statement0*"]]},{"name":"expression","bnf":[["not_expression"],["standard_expression"],["parenthesis_expression"]]},{"name":"parenthesis_expression","bnf":[["BEGIN_PARENTHESIS","WS*","statement","WS*","END_PARENTHESIS"]]},{"name":"%not_expression1","bnf":[["result"],["parenthesis_expression"]],"fragment":true},{"name":"not_expression","bnf":[["NOT","%not_expression1"]]},{"name":"%%standard_expression23","bnf":[["WS*","eq_approx"]],"fragment":true},{"name":"%%standard_expression24","bnf":[["WS*","basic_rhs"]],"fragment":true},{"name":"%%%standard_expression256","bnf":[["WS+","IS"]],"fragment":true},{"name":"%%standard_expression25","bnf":[["%%%standard_expression256?","WS+","between"]],"fragment":true},{"name":"%standard_expression2","bnf":[["%%standard_expression23"],["%%standard_expression24"],["%%standard_expression25"]],"fragment":true},{"name":"standard_expression","bnf":[["result","%standard_expression2?"]]},{"name":"basic_rhs","bnf":[["operator","WS*","result"]]},{"name":"eq_approx","bnf":[["eq_operator","WS*","\"~\"","WS*","result"]]},{"name":"PLUS","bnf":[["\"+\""]]},{"name":"MINUS","bnf":[["\"-\""]]},{"name":"MULTIPLY","bnf":[["\"*\""]]},{"name":"DIVIDE","bnf":[["\"/\""]]},{"name":"MODULUS","bnf":[["\"%\""]]},{"name":"DEFAULT_VAL","bnf":[["\"??\""]]},{"name":"arithmetic_operator","bnf":[["PLUS"],["MINUS"],["MULTIPLY"],["DIVIDE"],["MODULUS"],["DEFAULT_VAL"]]},{"name":"%arithmetic_result7","bnf":[["arithmetic_result"],["simple_result"]],"fragment":true},{"name":"arithmetic_result","bnf":[["simple_result","WS*","arithmetic_operator","WS*","%arithmetic_result7"]]},{"name":"simple_result","bnf":[["fcall"],["value"]]},{"name":"result","bnf":[["arithmetic_result"],["simple_result"]]},{"name":"value","bnf":[["false"],["true"],["array"],["number_time"],["number"],["number_tod"],["time_period"],["string"]]},{"name":"BEGIN_ARRAY","bnf":[["WS*",{},"WS*"]]},{"name":"BEGIN_OBJECT","bnf":[["WS*",{},"WS*"]]},{"name":"END_ARRAY","bnf":[["WS*",{},"WS*"]]},{"name":"END_OBJECT","bnf":[["WS*",{},"WS*"]]},{"name":"NAME_SEPARATOR","bnf":[["WS*",{},"WS*"]]},{"name":"VALUE_SEPARATOR","bnf":[["WS*",{},"WS*"]]},{"name":"%WS8","bnf":[[{}]]},{"name":"WS","bnf":[["%WS8+"]]},{"name":"operator","bnf":[["GTE"],["LTE"],["GT"],["LT"],["EQ"],["NEQ"]]},{"name":"eq_operator","bnf":[["EQ"],["NEQ"]]},{"name":"BEGIN_ARGUMENT","bnf":[["\"(\""]]},{"name":"END_ARGUMENT","bnf":[["\")\""]]},{"name":"BEGIN_PARENTHESIS","bnf":[["\"(\""]]},{"name":"END_PARENTHESIS","bnf":[["\")\""]]},{"name":"%argument9","bnf":[["\",\"","WS*"]],"fragment":true},{"name":"argument","bnf":[["statement","WS*","%argument9?"]]},{"name":"arguments","bnf":[["argument*"]]},{"name":"%fname10","bnf":[[{}]]},{"name":"fname","bnf":[["%fname10+"]]},{"name":"fcall","bnf":[["fname","WS*","BEGIN_ARGUMENT","arguments?","END_ARGUMENT"]]},{"name":"%%%between_number111213","bnf":[["\"and\""],["\"AND\""]],"fragment":true},{"name":"%%between_number1112","bnf":[["WS+","%%%between_number111213","WS+"]],"fragment":true},{"name":"%%between_number1114","bnf":[["WS*","\"-\"","WS*"]],"fragment":true},{"name":"%between_number11","bnf":[["%%between_number1112"],["%%between_number1114"]],"fragment":true},{"name":"between_number","bnf":[["number","%between_number11","number"]]},{"name":"%%%between_tod151617","bnf":[["\"and\""],["\"AND\""]],"fragment":true},{"name":"%%between_tod1516","bnf":[["WS+","%%%between_tod151617","WS+"]],"fragment":true},{"name":"%between_tod15","bnf":[["%%between_tod1516"]],"fragment":true},{"name":"between_tod","bnf":[["number_tod","%between_tod15","number_tod"]]},{"name":"%between18","bnf":[["\"between\""],["\"BETWEEN\""]],"fragment":true},{"name":"%between19","bnf":[["between_number"],["between_tod"]],"fragment":true},{"name":"between","bnf":[["%between18","WS+","%between19"]]},{"name":"%AND20","bnf":[["WS*","\"&&\"","WS*"]],"fragment":true},{"name":"%%AND2122","bnf":[["\"AND\""],["\"and\""]],"fragment":true},{"name":"%AND21","bnf":[["WS+","%%AND2122","WS+"]],"fragment":true},{"name":"AND","bnf":[["%AND20"],["%AND21"]]},{"name":"%OR23","bnf":[["WS*","\"||\"","WS*"]],"fragment":true},{"name":"%%OR2425","bnf":[["\"OR\""],["\"or\""]],"fragment":true},{"name":"%OR24","bnf":[["WS+","%%OR2425","WS+"]],"fragment":true},{"name":"OR","bnf":[["%OR23"],["%OR24"]]},{"name":"GT","bnf":[["\">\""]]},{"name":"LT","bnf":[["\"<\""]]},{"name":"GTE","bnf":[["\">=\""]]},{"name":"LTE","bnf":[["\"<=\""]]},{"name":"IS","bnf":[["\"is\""],["\"IS\""]]},{"name":"EQ","bnf":[["\"==\""],["\"=\""]]},{"name":"NEQ","bnf":[["\"!=\""]]},{"name":"%NOT26","bnf":[["\"!\"","WS*"]],"fragment":true},{"name":"%NOT27","bnf":[["\"not\"","WS+"]],"fragment":true},{"name":"NOT","bnf":[["%NOT26"],["%NOT27"]]},{"name":"false","bnf":[["\"false\""],["\"FALSE\""]]},{"name":"null","bnf":[["\"null\""],["\"NULL\""]]},{"name":"true","bnf":[["\"true\""],["\"TRUE\""]]},{"name":"%%array2829","bnf":[["VALUE_SEPARATOR","value"]],"fragment":true},{"name":"%array28","bnf":[["value","%%array2829*"]],"fragment":true},{"name":"array","bnf":[["BEGIN_ARRAY","%array28?","END_ARRAY"]]},{"name":"unit","bnf":[["\"seconds\""],["\"second\""],["\"minutes\""],["\"minute\""],["\"min\""],["\"mins\""],["\"min\""],["\"hours\""],["\"hour\""],["\"days\""],["\"day\""],["\"weeks\""],["\"week\""]]},{"name":"%%number3031","bnf":[[{}]]},{"name":"%number30","bnf":[["%%number3031+"]],"fragment":true},{"name":"%%number3233","bnf":[[{}]]},{"name":"%number32","bnf":[["\".\"","%%number3233+"]],"fragment":true},{"name":"%%number3435","bnf":[["\"e\""],["\"E\""]],"fragment":true},{"name":"%%number3436","bnf":[["\"-\""],["\"+\""]],"fragment":true},{"name":"%%%number343738","bnf":[[{}]]},{"name":"%%number3437","bnf":[["\"0\""],[{},"%%%number343738*"]],"fragment":true},{"name":"%number34","bnf":[["%%number3435","%%number3436?","%%number3437"]],"fragment":true},{"name":"number","bnf":[["\"-\"?","%number30","%number32?","%number34?"]]},{"name":"number_time","bnf":[["number","WS+","unit"]]},{"name":"%%number_tod3940","bnf":[[{}]]},{"name":"%number_tod39","bnf":[["%%number_tod3940+"]],"fragment":true},{"name":"%%number_tod4142","bnf":[[{}]]},{"name":"%number_tod41","bnf":[["%%number_tod4142+"]],"fragment":true},{"name":"number_tod","bnf":[["%number_tod39","\":\"","%number_tod41"]]},{"name":"time_period_const","bnf":[["\"today\""]]},{"name":"time_period","bnf":[["time_period_const"],["between"]]},{"name":"%%string4344","bnf":[[{}],[{}],[{}]],"fragment":true},{"name":"%%string4345","bnf":[[{}],[{}],[{}],[{}],[{}],[{}],[{}],[{}],[{},"HEXDIG","HEXDIG","HEXDIG","HEXDIG"]],"fragment":true},{"name":"%string43","bnf":[["%%string4344"],[{},"%%string4345"]],"fragment":true},{"name":"string","bnf":[["'\"'","%string43*","'\"'"]]},{"name":"HEXDIG","bnf":[[{}]]}] \ No newline at end of file diff --git a/src/RuleParser.js b/src/RuleParser.js index 084bd5c..4b98515 100644 --- a/src/RuleParser.js +++ b/src/RuleParser.js @@ -1,85 +1,8 @@ -const { Grammars, Parser } = require('ebnf'), +const { Parser } = require('ebnf'), assert = require('assert') - -const grammar = ` -statement_main ::= statement EOF -logical_operator ::= AND | OR -statement ::= expression (logical_operator expression)* -expression ::= not_expression | standard_expression | parenthesis_expression -parenthesis_expression::= BEGIN_PARENTHESIS WS* statement WS* END_PARENTHESIS -not_expression ::= NOT (result | parenthesis_expression) -standard_expression ::= result ((WS* eq_approx) | (WS* basic_rhs) | ((WS+ IS)? WS+ between))? -basic_rhs ::= operator WS* result -eq_approx ::= eq_operator WS* "~" WS* result - -PLUS ::= "+" -MINUS ::= "-" -MULTIPLY ::= "*" -DIVIDE ::= "/" -MODULUS ::= "%" -DEFAULT_VAL ::= "??" -arithmetic_operator ::= PLUS | MINUS | MULTIPLY | DIVIDE | MODULUS | DEFAULT_VAL -arithmetic_result ::= simple_result WS* arithmetic_operator WS* ( arithmetic_result | simple_result ) - -simple_result ::= fcall | value -result ::= arithmetic_result | simple_result -value ::= false | true | array | number_time | number | number_tod | time_period | string -BEGIN_ARRAY ::= WS* #x5B WS* /* [ left square bracket */ -BEGIN_OBJECT ::= WS* #x7B WS* /* { left curly bracket */ -END_ARRAY ::= WS* #x5D WS* /* ] right square bracket */ -END_OBJECT ::= WS* #x7D WS* /* } right curly bracket */ -NAME_SEPARATOR ::= WS* #x3A WS* /* : colon */ -VALUE_SEPARATOR ::= WS* #x2C WS* /* , comma */ -WS ::= [#x20#x09#x0A#x0D]+ /* Space | Tab | \n | \r */ - -operator ::= GTE | LTE | GT | LT | EQ | NEQ -eq_operator ::= EQ | NEQ - -BEGIN_ARGUMENT ::= "(" -END_ARGUMENT ::= ")" - -BEGIN_PARENTHESIS ::= "(" -END_PARENTHESIS ::= ")" - -argument ::= statement WS* ("," WS*)? -arguments ::= argument* -fname ::= [a-zA-z0-9]+ -fcall ::= fname WS* BEGIN_ARGUMENT arguments? END_ARGUMENT - -between_number ::= number ((WS+ ("and" | "AND") WS+) | (WS* "-" WS*)) number -between_tod ::= number_tod ((WS+ ("and" | "AND") WS+)) number_tod -between ::= ("between" | "BETWEEN") WS+ (between_number | between_tod) - -AND ::= (WS* "&&" WS*) | (WS+ ("AND"|"and") WS+) -OR ::= (WS* "||" WS*) | (WS+ ("OR"|"or") WS+) -GT ::= ">" -LT ::= "<" -GTE ::= ">=" -LTE ::= "<=" -IS ::= "is" | "IS" -EQ ::= "==" | "=" -NEQ ::= "!=" -NOT ::= ("!" WS*) | ("not" WS+) -false ::= "false" | "FALSE" -null ::= "null" | "NULL" -true ::= "true" | "TRUE" -array ::= BEGIN_ARRAY (value (VALUE_SEPARATOR value)*)? END_ARRAY - -unit ::= "seconds" | "second" | "minutes" | "minute" | "min" | "mins" | "min" | "hours" | "hour" | "days" | "day" | "weeks" | "week" -number ::= "-"? ([0-9]+) ("." [0-9]+)? (("e" | "E") ( "-" | "+" )? ("0" | [1-9] [0-9]*))? -number_time ::= number WS+ unit -number_tod ::= ([0-9]+) ":" ([0-9]+) - -time_period_const ::= "today" -time_period ::= time_period_const | between - -string ::= '"' (([#x20-#x21] | [#x23-#x5B] | [#x5D-#xFFFF]) | #x5C (#x22 | #x5C | #x2F | #x62 | #x66 | #x6E | #x72 | #x74 | #x75 HEXDIG HEXDIG HEXDIG HEXDIG))* '"' -HEXDIG ::= [a-fA-F0-9] -` -let RULES = Grammars.W3C.getRules(grammar); -let parser = new Parser(RULES, {debug: false}); -const target = 'statement_main' +let ParserRules = require('./RuleParser.ebnf.js') +let ParserCache; const ArithmeticOperators = { "+": 'MathAdd', @@ -114,10 +37,12 @@ const Epsilon = 0.01 class RuleParser { static toAst(txt){ let ret - //if(process.env.NODE_ENV === 'test') { - // parser.debug = true - //} - ret = parser.getAST(txt, target); + + if(!ParserCache){ + ParserCache = new Parser(ParserRules, {debug: false}) + } + + ret = ParserCache.getAST(txt, 'statement_main'); if(ret){ return ret.children[0]