@halleyassist/rule-parser 1.0.19 → 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/rule-parser.browser.js +118 -41
- package/package.json +1 -1
- package/src/RuleParser.ebnf.js +46 -22
- package/src/RuleParser.js +114 -41
- package/src/RuleParser.production.ebnf.js +1 -1
- package/src/RuleParser.production.js +114 -41
- package/src/errors/ErrorAnalyzer.js +1 -1
- package/src/errors/RuleParseError.js +1 -1
|
@@ -1920,9 +1920,9 @@ exports.Grammars = require("./Grammars");
|
|
|
1920
1920
|
module.exports = require('./RuleParser.production')
|
|
1921
1921
|
|
|
1922
1922
|
},{"./RuleParser.production":12}],11:[function(require,module,exports){
|
|
1923
|
-
module.exports=[{"name":"statement_main","bnf":[["statement","EOF"]]},{"name":"logical_operator","bnf":[["AND"],["OR"]]},{"name":"%statement[2]","bnf":[["logical_operator","expression"]],"fragment":true},{"name":"statement","bnf":[["expression","%statement[2]*"]]},{"name":"expression","bnf":[["not_expression"],["standard_expression"],["parenthesis_expression"]]},{"name":"parenthesis_expression","bnf":[["BEGIN_PARENTHESIS","WS*","statement","WS*","END_PARENTHESIS"]]},{"name":"%not_expression[2]","bnf":[["result"],["parenthesis_expression"]],"fragment":true},{"name":"not_expression","bnf":[["NOT","%not_expression[2]"]]},{"name":"%standard_expression[2][1]","bnf":[["WS*","eq_approx"]],"fragment":true},{"name":"%standard_expression[2][2]","bnf":[["WS*","basic_rhs"]],"fragment":true},{"name":"%standard_expression[2][3][1]","bnf":[["WS+","IS"]],"fragment":true},{"name":"%standard_expression[2][3]","bnf":[["%standard_expression[2][3][1]?","WS+","between"]],"fragment":true},{"name":"%standard_expression[2][4]","bnf":[["WS+","in_expr"]],"fragment":true},{"name":"%standard_expression[2]","bnf":[["%standard_expression[2][1]"],["%standard_expression[2][2]"],["%standard_expression[2][3]"],["%standard_expression[2][4]"]],"fragment":true},{"name":"standard_expression","bnf":[["result","%standard_expression[2]?"]]},{"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_operand","bnf":[["fcall"],["
|
|
1923
|
+
module.exports=[{"name":"statement_main","bnf":[["statement","EOF"]]},{"name":"logical_operator","bnf":[["AND"],["OR"]]},{"name":"%statement[2]","bnf":[["logical_operator","expression"]],"fragment":true},{"name":"statement","bnf":[["expression","%statement[2]*"]]},{"name":"expression","bnf":[["not_expression"],["standard_expression"],["parenthesis_expression"]]},{"name":"parenthesis_expression","bnf":[["BEGIN_PARENTHESIS","WS*","statement","WS*","END_PARENTHESIS"]]},{"name":"%not_expression[2]","bnf":[["result"],["parenthesis_expression"]],"fragment":true},{"name":"not_expression","bnf":[["NOT","%not_expression[2]"]]},{"name":"%standard_expression[2][1]","bnf":[["WS*","eq_approx"]],"fragment":true},{"name":"%standard_expression[2][2]","bnf":[["WS*","basic_rhs"]],"fragment":true},{"name":"%standard_expression[2][3][1]","bnf":[["WS+","IS"]],"fragment":true},{"name":"%standard_expression[2][3]","bnf":[["%standard_expression[2][3][1]?","WS+","between"]],"fragment":true},{"name":"%standard_expression[2][4]","bnf":[["WS+","in_expr"]],"fragment":true},{"name":"%standard_expression[2]","bnf":[["%standard_expression[2][1]"],["%standard_expression[2][2]"],["%standard_expression[2][3]"],["%standard_expression[2][4]"]],"fragment":true},{"name":"standard_expression","bnf":[["result","%standard_expression[2]?"]]},{"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":"number_atom","bnf":[["number"]]},{"name":"number_time_atom","bnf":[["number_time"]]},{"name":"tod_atom","bnf":[["number_tod"]]},{"name":"dow_atom","bnf":[["dow"]]},{"name":"arithmetic_operand","bnf":[["fcall"],["number_time_atom"],["number_atom"]]},{"name":"%arithmetic_result[5]","bnf":[["arithmetic_result"],["arithmetic_operand"]],"fragment":true},{"name":"arithmetic_result","bnf":[["arithmetic_operand","WS*","arithmetic_operator","WS*","%arithmetic_result[5]"]]},{"name":"simple_result","bnf":[["fcall"],["value"]]},{"name":"result","bnf":[["arithmetic_result"],["simple_result"]]},{"name":"value_atom","bnf":[["false"],["true"],["array"],["time_period"],["number_time_atom"],["number_atom"],["tod_atom"],["string"]]},{"name":"value","bnf":[["value_atom"]]},{"name":"BEGIN_ARRAY","bnf":[["WS*",/\x5B/,"WS*"]]},{"name":"BEGIN_OBJECT","bnf":[["WS*",/\x7B/,"WS*"]]},{"name":"END_ARRAY","bnf":[["WS*",/\x5D/,"WS*"]]},{"name":"END_OBJECT","bnf":[["WS*",/\x7D/,"WS*"]]},{"name":"NAME_SEPARATOR","bnf":[["WS*",/\x3A/,"WS*"]]},{"name":"VALUE_SEPARATOR","bnf":[["WS*",/\x2C/,"WS*"]]},{"name":"WS","bnf":[[/[\x20\x09\x0A\x0D]/]]},{"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":"BEGIN_IN","bnf":[[/[Ii]/,/[Nn]/]]},{"name":"in_expr","bnf":[["BEGIN_IN","WS*","BEGIN_PARENTHESIS","WS*","arguments","END_PARENTHESIS"]]},{"name":"argument","bnf":[["statement","WS*"]]},{"name":"%arguments[2]","bnf":[["WS*","\",\"","WS*","argument"]],"fragment":true},{"name":"arguments","bnf":[["argument","%arguments[2]*"]]},{"name":"%fname[1]","bnf":[[/[A-Za-z0-9]/]]},{"name":"fname","bnf":[["%fname[1]+"]]},{"name":"fcall","bnf":[["fname","WS*","BEGIN_ARGUMENT","WS*","arguments?","END_ARGUMENT"]]},{"name":"%between_dash_or_and[1]","bnf":[["WS+",/[Aa]/,/[Nn]/,/[Dd]/,"WS+"]],"fragment":true},{"name":"%between_dash_or_and[2]","bnf":[["WS*",/\-/,"WS*"]],"fragment":true},{"name":"between_dash_or_and","bnf":[["%between_dash_or_and[1]"],["%between_dash_or_and[2]"]]},{"name":"between_number_inner","bnf":[["number_atom"],["number_time_atom"]]},{"name":"between_number","bnf":[["between_number_inner","between_dash_or_and","between_number_inner"]]},{"name":"between_number_time_inner","bnf":[["number_time_atom"]]},{"name":"%between_number_time[4]","bnf":[["WS+","dow_range"]],"fragment":true},{"name":"between_number_time","bnf":[["between_number_time_inner","between_dash_or_and","between_number_time_inner","%between_number_time[4]?"]]},{"name":"between_tod_inner","bnf":[["tod_atom"]]},{"name":"%between_tod[2]","bnf":[["WS+",/[Aa]/,/[Nn]/,/[Dd]/,"WS+"]],"fragment":true},{"name":"%between_tod[4]","bnf":[["WS+","dow_range"]],"fragment":true},{"name":"between_tod","bnf":[["between_tod_inner","%between_tod[2]","between_tod_inner","%between_tod[4]?"]]},{"name":"%between[3]","bnf":[["between_number"],["between_tod"]],"fragment":true},{"name":"between","bnf":[[/[Bb]/,/[Ee]/,/[Tt]/,/[Ww]/,/[Ee]/,/[Ee]/,/[Nn]/,"WS+","%between[3]"]]},{"name":"dow","bnf":[[/[Mm]/,/[Oo]/,/[Nn]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Mm]/,/[Oo]/,/[Nn]/],[/[Tt]/,/[Uu]/,/[Ee]/,/[Ss]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Tt]/,/[Uu]/,/[Ee]/],[/[Ww]/,/[Ee]/,/[Dd]/,/[Nn]/,/[Ee]/,/[Ss]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Ww]/,/[Ee]/,/[Dd]/],[/[Tt]/,/[Hh]/,/[Uu]/,/[Rr]/,/[Ss]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Tt]/,/[Hh]/,/[Uu]/],[/[Tt]/,/[Hh]/,/[Uu]/,/[Rr]/],[/[Ff]/,/[Rr]/,/[Ii]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Ff]/,/[Rr]/,/[Ii]/],[/[Ss]/,/[Aa]/,/[Tt]/,/[Uu]/,/[Rr]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Ss]/,/[Aa]/,/[Tt]/],[/[Ss]/,/[Uu]/,/[Nn]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Ss]/,/[Uu]/,/[Nn]/]]},{"name":"dow_range_inner","bnf":[["dow_atom"]]},{"name":"%dow_range[4]","bnf":[["WS+",/[Tt]/,/[Oo]/,"WS+","dow_range_inner"]],"fragment":true},{"name":"dow_range","bnf":[[/[Oo]/,/[Nn]/,"WS+","dow_range_inner","%dow_range[4]?"]]},{"name":"between_time_only","bnf":[[/[Bb]/,/[Ee]/,/[Tt]/,/[Ww]/,/[Ee]/,/[Ee]/,/[Nn]/,"WS+","between_number_time"]]},{"name":"between_tod_only","bnf":[[/[Bb]/,/[Ee]/,/[Tt]/,/[Ww]/,/[Ee]/,/[Ee]/,/[Nn]/,"WS+","between_tod"]]},{"name":"between_time_only_atom","bnf":[["between_time_only"]]},{"name":"between_tod_only_atom","bnf":[["between_tod_only"]]},{"name":"%AND[1]","bnf":[["WS*",/&/,/&/,"WS*"]],"fragment":true},{"name":"%AND[2]","bnf":[["WS+",/[Aa]/,/[Nn]/,/[Dd]/,"WS+"]],"fragment":true},{"name":"AND","bnf":[["%AND[1]"],["%AND[2]"]]},{"name":"%OR[1]","bnf":[["WS*",/\|/,/\|/,"WS*"]],"fragment":true},{"name":"%OR[2]","bnf":[["WS+",/[Oo]/,/[Rr]/,"WS+"]],"fragment":true},{"name":"OR","bnf":[["%OR[1]"],["%OR[2]"]]},{"name":"AGO","bnf":[[/[Aa]/,/[Gg]/,/[Oo]/]]},{"name":"GT","bnf":[["\">\""]]},{"name":"LT","bnf":[["\"<\""]]},{"name":"GTE","bnf":[["\">=\""]]},{"name":"LTE","bnf":[["\"<=\""]]},{"name":"IS","bnf":[[/[Ii]/,/[Ss]/]]},{"name":"EQ","bnf":[["\"==\""],["\"=\""]]},{"name":"NEQ","bnf":[["\"!=\""]]},{"name":"%NOT[1]","bnf":[[/!/,"WS*"]],"fragment":true},{"name":"%NOT[2]","bnf":[[/[Nn]/,/[Oo]/,/[Tt]/,"WS+"]],"fragment":true},{"name":"NOT","bnf":[["%NOT[1]"],["%NOT[2]"]]},{"name":"false","bnf":[[/[Ff]/,/[Aa]/,/[Ll]/,/[Ss]/,/[Ee]/]]},{"name":"null","bnf":[[/[Nn]/,/[Uu]/,/[Ll]/,/[Ll]/]]},{"name":"true","bnf":[[/[Tt]/,/[Rr]/,/[Uu]/,/[Ee]/]]},{"name":"%array[2][2]","bnf":[["VALUE_SEPARATOR","value"]],"fragment":true},{"name":"%array[2]","bnf":[["value","%array[2][2]*"]],"fragment":true},{"name":"array","bnf":[["BEGIN_ARRAY","%array[2]?","END_ARRAY"]]},{"name":"unit","bnf":[[/[Ss]/,/[Ee]/,/[Cc]/,/[Oo]/,/[Nn]/,/[Dd]/,/[Ss]/],[/[Mm]/,/[Ii]/,/[Nn]/,/[Uu]/,/[Tt]/,/[Ee]/,/[Ss]/],[/[Hh]/,/[Oo]/,/[Uu]/,/[Rr]/,/[Ss]/],[/[Ww]/,/[Ee]/,/[Ee]/,/[Kk]/,/[Ss]/],[/[Dd]/,/[Aa]/,/[Yy]/,/[Ss]/],[/[Ss]/,/[Ee]/,/[Cc]/,/[Oo]/,/[Nn]/,/[Dd]/],[/[Mm]/,/[Ii]/,/[Nn]/,/[Uu]/,/[Tt]/,/[Ee]/],[/[Ww]/,/[Ee]/,/[Ee]/,/[Kk]/],[/[Hh]/,/[Oo]/,/[Uu]/,/[Rr]/],[/[Dd]/,/[Aa]/,/[Yy]/],[/[Mm]/,/[Ii]/,/[Nn]/,/[Ss]/],[/[Mm]/,/[Ii]/,/[Nn]/]]},{"name":"%number[2][1]","bnf":[[/[0-9]/]]},{"name":"%number[2]","bnf":[["%number[2][1]+"]],"fragment":true},{"name":"%number[3][2]","bnf":[[/[0-9]/]]},{"name":"%number[3]","bnf":[["\".\"","%number[3][2]+"]],"fragment":true},{"name":"%number[4][2]","bnf":[["\"-\""],["\"+\""]],"fragment":true},{"name":"%number[4][3][2]","bnf":[[/[0-9]/]]},{"name":"%number[4][3]","bnf":[["\"0\""],[/[1-9]/,"%number[4][3][2]*"]],"fragment":true},{"name":"%number[4]","bnf":[["\"e\"","%number[4][2]?","%number[4][3]"]],"fragment":true},{"name":"number","bnf":[["\"-\"?","%number[2]","%number[3]?","%number[4]?"]]},{"name":"number_time","bnf":[["number","WS+","unit"]]},{"name":"%number_tod[1][1]","bnf":[[/[0-9]/]]},{"name":"%number_tod[1]","bnf":[["%number_tod[1][1]+"]],"fragment":true},{"name":"%number_tod[3][1]","bnf":[[/[0-9]/]]},{"name":"%number_tod[3]","bnf":[["%number_tod[3][1]+"]],"fragment":true},{"name":"number_tod","bnf":[["%number_tod[1]","\":\"","%number_tod[3]"]]},{"name":"%time_period_ago[2]","bnf":[["WS+","number_time_atom"]],"fragment":true},{"name":"time_period_ago","bnf":[["number_time_atom","%time_period_ago[2]*","WS+","AGO"]]},{"name":"%time_period_ago_between[2]","bnf":[["WS+","number_time_atom"]],"fragment":true},{"name":"%time_period_ago_between[6]","bnf":[["between_time_only_atom"],["between_tod_only_atom"]],"fragment":true},{"name":"time_period_ago_between","bnf":[["number_time_atom","%time_period_ago_between[2]*","WS+","AGO","WS+","%time_period_ago_between[6]"]]},{"name":"time_period_const","bnf":[[/[Tt]/,/[Oo]/,/[Dd]/,/[Aa]/,/[Yy]/],["time_period_ago"]]},{"name":"time_period","bnf":[["time_period_ago_between"],["time_period_const"],["between_tod_only"],["between_time_only"]]},{"name":"%string[2][1]","bnf":[[/[\x20-\x21]/],[/[\x23-\x5B]/],[/[\x5D-\uFFFF]/]],"fragment":true},{"name":"%string[2][2]","bnf":[[/\x22/],[/\x5C/],[/\x2F/],[/\x62/],[/\x66/],[/\x6E/],[/\x72/],[/\x74/],[/\x75/,"HEXDIG","HEXDIG","HEXDIG","HEXDIG"]],"fragment":true},{"name":"%string[2]","bnf":[["%string[2][1]"],[/\x5C/,"%string[2][2]"]],"fragment":true},{"name":"string","bnf":[["'\"'","%string[2]*","'\"'"]]},{"name":"HEXDIG","bnf":[[/[a-fA-F0-9]/]]}]
|
|
1924
1924
|
},{}],12:[function(require,module,exports){
|
|
1925
|
-
const {Parser} = require('ebnf/dist/Parser.js'), {ParsingError} = require('ebnf');
|
|
1925
|
+
const {Parser} = require('ebnf/dist/Parser.js'), {ParsingError} = require('ebnf'), RuleParseError = require('./errors/RuleParseError');
|
|
1926
1926
|
let ParserRules = require('./RuleParser.production.ebnf.js');
|
|
1927
1927
|
let ParserCache;
|
|
1928
1928
|
const {ErrorAnalyzer} = require('./errors/ErrorAnalyzer');
|
|
@@ -2015,14 +2015,17 @@ class RuleParser {
|
|
|
2015
2015
|
}
|
|
2016
2016
|
static _parseDowRange(dowRange) {
|
|
2017
2017
|
if (dowRange.children.length === 1) {
|
|
2018
|
+
const dayText = RuleParser.__parseValue(dowRange.children[0]);
|
|
2018
2019
|
return {
|
|
2019
|
-
start:
|
|
2020
|
-
end:
|
|
2020
|
+
start: dayText,
|
|
2021
|
+
end: dayText
|
|
2021
2022
|
};
|
|
2022
2023
|
} else if (dowRange.children.length === 2) {
|
|
2024
|
+
const startDay = RuleParser.__parseValue(dowRange.children[0]);
|
|
2025
|
+
const endDay = RuleParser.__parseValue(dowRange.children[1]);
|
|
2023
2026
|
return {
|
|
2024
|
-
start:
|
|
2025
|
-
end:
|
|
2027
|
+
start: startDay,
|
|
2028
|
+
end: endDay
|
|
2026
2029
|
};
|
|
2027
2030
|
} else {
|
|
2028
2031
|
throw new Error(`Invalid dow_range with ${ dowRange.children.length } children`);
|
|
@@ -2043,14 +2046,15 @@ class RuleParser {
|
|
|
2043
2046
|
let totalSeconds = 0;
|
|
2044
2047
|
const components = [];
|
|
2045
2048
|
for (const child of timePeriodAgo.children) {
|
|
2046
|
-
if (child.type === '
|
|
2047
|
-
const
|
|
2048
|
-
const
|
|
2049
|
+
if (child.type === 'number_time_atom') {
|
|
2050
|
+
const numberTime = child.children[0];
|
|
2051
|
+
const number = parseFloat(numberTime.children[0].text);
|
|
2052
|
+
const unit = numberTime.children[1].text.toUpperCase();
|
|
2049
2053
|
components.push([
|
|
2050
2054
|
number,
|
|
2051
2055
|
unit
|
|
2052
2056
|
]);
|
|
2053
|
-
totalSeconds += RuleParser.__parseValue(
|
|
2057
|
+
totalSeconds += RuleParser.__parseValue(numberTime);
|
|
2054
2058
|
}
|
|
2055
2059
|
}
|
|
2056
2060
|
if (components.length === 1) {
|
|
@@ -2073,29 +2077,67 @@ class RuleParser {
|
|
|
2073
2077
|
];
|
|
2074
2078
|
case 'time_period_ago_between': {
|
|
2075
2079
|
let totalSeconds = 0;
|
|
2076
|
-
let
|
|
2080
|
+
let betweenNode = null;
|
|
2081
|
+
let isBetweenTod = false;
|
|
2077
2082
|
for (let i = 0; i < tp.children.length; i++) {
|
|
2078
|
-
if (tp.children[i].type === '
|
|
2079
|
-
totalSeconds += RuleParser.__parseValue(tp.children[i]);
|
|
2080
|
-
} else if (tp.children[i].type === '
|
|
2081
|
-
|
|
2083
|
+
if (tp.children[i].type === 'number_time_atom') {
|
|
2084
|
+
totalSeconds += RuleParser.__parseValue(tp.children[i].children[0]);
|
|
2085
|
+
} else if (tp.children[i].type === 'between_time_only' || tp.children[i].type === 'between_time_only_atom') {
|
|
2086
|
+
betweenNode = tp.children[i].type === 'between_time_only_atom' ? tp.children[i].children[0] : tp.children[i];
|
|
2087
|
+
isBetweenTod = false;
|
|
2088
|
+
} else if (tp.children[i].type === 'between_tod_only' || tp.children[i].type === 'between_tod_only_atom') {
|
|
2089
|
+
betweenNode = tp.children[i].type === 'between_tod_only_atom' ? tp.children[i].children[0] : tp.children[i];
|
|
2090
|
+
isBetweenTod = true;
|
|
2082
2091
|
}
|
|
2083
2092
|
}
|
|
2084
|
-
if (!
|
|
2085
|
-
throw new Error('time_period_ago_between requires between_tod_only child');
|
|
2093
|
+
if (!betweenNode) {
|
|
2094
|
+
throw new Error('time_period_ago_between requires between_time_only or between_tod_only child');
|
|
2086
2095
|
}
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2096
|
+
if (isBetweenTod) {
|
|
2097
|
+
const betweenTod = betweenNode.children[0];
|
|
2098
|
+
let startTod = RuleParser.__parseValue(betweenTod.children[0]);
|
|
2099
|
+
let endTod = RuleParser.__parseValue(betweenTod.children[1]);
|
|
2100
|
+
if (betweenTod.children.length > 2) {
|
|
2101
|
+
RuleParser._addDowToTods(startTod, endTod, betweenTod.children[2]);
|
|
2102
|
+
}
|
|
2103
|
+
return [
|
|
2104
|
+
'TimePeriodBetweenAgo',
|
|
2105
|
+
totalSeconds,
|
|
2106
|
+
startTod,
|
|
2107
|
+
endTod
|
|
2108
|
+
];
|
|
2109
|
+
} else {
|
|
2110
|
+
const betweenNumberTime = betweenNode.children[0];
|
|
2111
|
+
const startValue = RuleParser.__parseValue(betweenNumberTime.children[0]);
|
|
2112
|
+
const endValue = RuleParser.__parseValue(betweenNumberTime.children[2]);
|
|
2113
|
+
if (betweenNumberTime.children.length > 3 && betweenNumberTime.children[3].type === 'dow_range') {
|
|
2114
|
+
const dow = RuleParser._parseDowRange(betweenNumberTime.children[3]);
|
|
2115
|
+
if (dow.start === dow.end) {
|
|
2116
|
+
return [
|
|
2117
|
+
'TimePeriodBetweenAgo',
|
|
2118
|
+
totalSeconds,
|
|
2119
|
+
startValue,
|
|
2120
|
+
endValue,
|
|
2121
|
+
dow.start
|
|
2122
|
+
];
|
|
2123
|
+
} else {
|
|
2124
|
+
return [
|
|
2125
|
+
'TimePeriodBetweenAgo',
|
|
2126
|
+
totalSeconds,
|
|
2127
|
+
startValue,
|
|
2128
|
+
endValue,
|
|
2129
|
+
dow.start,
|
|
2130
|
+
dow.end
|
|
2131
|
+
];
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
return [
|
|
2135
|
+
'TimePeriodBetweenAgo',
|
|
2136
|
+
totalSeconds,
|
|
2137
|
+
startValue,
|
|
2138
|
+
endValue
|
|
2139
|
+
];
|
|
2092
2140
|
}
|
|
2093
|
-
return [
|
|
2094
|
-
'TimePeriodBetweenAgo',
|
|
2095
|
-
totalSeconds,
|
|
2096
|
-
startTod,
|
|
2097
|
-
endTod
|
|
2098
|
-
];
|
|
2099
2141
|
}
|
|
2100
2142
|
case 'between_tod_only': {
|
|
2101
2143
|
const betweenTod = tp.children[0];
|
|
@@ -2123,9 +2165,9 @@ class RuleParser {
|
|
|
2123
2165
|
case 'between_time_only': {
|
|
2124
2166
|
const betweenNumberTime = tp.children[0];
|
|
2125
2167
|
const startValue = RuleParser.__parseValue(betweenNumberTime.children[0]);
|
|
2126
|
-
const endValue = RuleParser.__parseValue(betweenNumberTime.children[
|
|
2127
|
-
if (betweenNumberTime.children.length >
|
|
2128
|
-
const dow = RuleParser._parseDowRange(betweenNumberTime.children[
|
|
2168
|
+
const endValue = RuleParser.__parseValue(betweenNumberTime.children[2]);
|
|
2169
|
+
if (betweenNumberTime.children.length > 3 && betweenNumberTime.children[3].type === 'dow_range') {
|
|
2170
|
+
const dow = RuleParser._parseDowRange(betweenNumberTime.children[3]);
|
|
2129
2171
|
if (dow.start === dow.end) {
|
|
2130
2172
|
return [
|
|
2131
2173
|
'TimePeriodBetween',
|
|
@@ -2154,6 +2196,22 @@ class RuleParser {
|
|
|
2154
2196
|
static __parseValue(child) {
|
|
2155
2197
|
const type = child.type;
|
|
2156
2198
|
switch (type) {
|
|
2199
|
+
case 'value': {
|
|
2200
|
+
return RuleParser.__parseValue(child.children[0]);
|
|
2201
|
+
}
|
|
2202
|
+
case 'value_atom': {
|
|
2203
|
+
return RuleParser.__parseValue(child.children[0]);
|
|
2204
|
+
}
|
|
2205
|
+
case 'number_atom':
|
|
2206
|
+
case 'number_time_atom':
|
|
2207
|
+
case 'tod_atom':
|
|
2208
|
+
case 'dow_atom':
|
|
2209
|
+
case 'between_tod_inner':
|
|
2210
|
+
case 'between_number_inner':
|
|
2211
|
+
case 'between_number_time_inner':
|
|
2212
|
+
case 'dow_range_inner': {
|
|
2213
|
+
return RuleParser.__parseValue(child.children[0]);
|
|
2214
|
+
}
|
|
2157
2215
|
case 'string': {
|
|
2158
2216
|
const str = child.text;
|
|
2159
2217
|
return str.slice(1, -1);
|
|
@@ -2209,10 +2267,13 @@ class RuleParser {
|
|
|
2209
2267
|
case 'array': {
|
|
2210
2268
|
const ret = [];
|
|
2211
2269
|
for (const c of child.children) {
|
|
2212
|
-
ret.push(RuleParser.__parseValue(c
|
|
2270
|
+
ret.push(RuleParser.__parseValue(c));
|
|
2213
2271
|
}
|
|
2214
2272
|
return ret;
|
|
2215
2273
|
}
|
|
2274
|
+
case 'dow': {
|
|
2275
|
+
return normalizeDow(child.text);
|
|
2276
|
+
}
|
|
2216
2277
|
default:
|
|
2217
2278
|
throw new Error(`Unknown value type ${ type }`);
|
|
2218
2279
|
}
|
|
@@ -2221,6 +2282,17 @@ class RuleParser {
|
|
|
2221
2282
|
const child = value.children[0];
|
|
2222
2283
|
const type = child.type;
|
|
2223
2284
|
switch (type) {
|
|
2285
|
+
case 'value_atom': {
|
|
2286
|
+
const atomChild = child.children[0];
|
|
2287
|
+
if (atomChild.type === 'time_period') {
|
|
2288
|
+
const tp = atomChild.children[0];
|
|
2289
|
+
return RuleParser._parseTimePeriod(tp);
|
|
2290
|
+
}
|
|
2291
|
+
return [
|
|
2292
|
+
'Value',
|
|
2293
|
+
RuleParser.__parseValue(atomChild)
|
|
2294
|
+
];
|
|
2295
|
+
}
|
|
2224
2296
|
case 'time_period': {
|
|
2225
2297
|
const tp = child.children[0];
|
|
2226
2298
|
return RuleParser._parseTimePeriod(tp);
|
|
@@ -2249,6 +2321,13 @@ class RuleParser {
|
|
|
2249
2321
|
switch (type) {
|
|
2250
2322
|
case 'fcall':
|
|
2251
2323
|
return RuleParser._parseFcall(child);
|
|
2324
|
+
case 'number_atom':
|
|
2325
|
+
case 'number_time_atom': {
|
|
2326
|
+
return [
|
|
2327
|
+
'Value',
|
|
2328
|
+
RuleParser.__parseValue(child.children[0])
|
|
2329
|
+
];
|
|
2330
|
+
}
|
|
2252
2331
|
case 'number':
|
|
2253
2332
|
return [
|
|
2254
2333
|
'Value',
|
|
@@ -2384,7 +2463,7 @@ class RuleParser {
|
|
|
2384
2463
|
],
|
|
2385
2464
|
[
|
|
2386
2465
|
'Value',
|
|
2387
|
-
RuleParser.__parseValue(betweenChild.children[
|
|
2466
|
+
RuleParser.__parseValue(betweenChild.children[2])
|
|
2388
2467
|
]
|
|
2389
2468
|
];
|
|
2390
2469
|
}
|
|
@@ -2399,7 +2478,7 @@ class RuleParser {
|
|
|
2399
2478
|
],
|
|
2400
2479
|
[
|
|
2401
2480
|
'Value',
|
|
2402
|
-
RuleParser.__parseValue(rhs.children[0].children[
|
|
2481
|
+
RuleParser.__parseValue(rhs.children[0].children[2])
|
|
2403
2482
|
]
|
|
2404
2483
|
];
|
|
2405
2484
|
case 'basic_rhs':
|
|
@@ -2538,8 +2617,6 @@ class RuleParser {
|
|
|
2538
2617
|
if (e.message && e.message.includes('Invalid time of day')) {
|
|
2539
2618
|
const match = e.message.match(/Invalid time of day[,:]?\s*([0-9:]+)/);
|
|
2540
2619
|
const badTod = match ? match[1] : 'invalid';
|
|
2541
|
-
const {ParsingError} = require('ebnf');
|
|
2542
|
-
const {RuleParseError} = require('./errors/RuleParseError');
|
|
2543
2620
|
const lines = txt.trim().split('\n');
|
|
2544
2621
|
const position = {
|
|
2545
2622
|
line: lines.length,
|
|
@@ -2551,7 +2628,6 @@ class RuleParser {
|
|
|
2551
2628
|
if (e.message && e.message.includes('Invalid day of week')) {
|
|
2552
2629
|
const match = e.message.match(/Invalid day of week[,:]?\s*(\w+)/);
|
|
2553
2630
|
const badDow = match ? match[1] : 'invalid';
|
|
2554
|
-
const {RuleParseError} = require('./errors/RuleParseError');
|
|
2555
2631
|
const lines = txt.trim().split('\n');
|
|
2556
2632
|
const position = {
|
|
2557
2633
|
line: lines.length,
|
|
@@ -2573,11 +2649,12 @@ class RuleParser {
|
|
|
2573
2649
|
}
|
|
2574
2650
|
}
|
|
2575
2651
|
module.exports = RuleParser;
|
|
2576
|
-
module.exports.
|
|
2577
|
-
module.exports.
|
|
2652
|
+
module.exports.ParserRules = ParserRules;
|
|
2653
|
+
module.exports.ParsingError = ParsingError;
|
|
2654
|
+
module.exports.RuleParseError = RuleParseError;
|
|
2578
2655
|
},{"./RuleParser.production.ebnf.js":11,"./errors/ErrorAnalyzer":13,"./errors/RuleParseError":14,"ebnf":9,"ebnf/dist/Parser.js":5}],13:[function(require,module,exports){
|
|
2579
2656
|
const { ParsingError } = require('ebnf');
|
|
2580
|
-
const
|
|
2657
|
+
const RuleParseError = require('./RuleParseError');
|
|
2581
2658
|
|
|
2582
2659
|
/**
|
|
2583
2660
|
* Analyzes parsing errors and maps them to user-friendly error codes
|
|
@@ -3565,7 +3642,7 @@ class RuleParseError extends Error {
|
|
|
3565
3642
|
}
|
|
3566
3643
|
}
|
|
3567
3644
|
|
|
3568
|
-
module.exports =
|
|
3645
|
+
module.exports = RuleParseError;
|
|
3569
3646
|
|
|
3570
3647
|
},{}]},{},[10])(10)
|
|
3571
3648
|
});
|
package/package.json
CHANGED
package/src/RuleParser.ebnf.js
CHANGED
|
@@ -5,10 +5,10 @@ statement_main ::= statement EOF
|
|
|
5
5
|
logical_operator ||= AND | OR
|
|
6
6
|
statement ::= expression (logical_operator expression)*
|
|
7
7
|
expression ::= not_expression | standard_expression | parenthesis_expression
|
|
8
|
-
parenthesis_expression::= BEGIN_PARENTHESIS WS* statement WS* END_PARENTHESIS
|
|
8
|
+
parenthesis_expression ::= BEGIN_PARENTHESIS WS* statement WS* END_PARENTHESIS
|
|
9
9
|
not_expression ||= NOT (result | parenthesis_expression)
|
|
10
10
|
standard_expression ||= result ((WS* eq_approx) | (WS* basic_rhs) | ((WS+ IS)? WS+ between) | (WS+ in_expr))?
|
|
11
|
-
basic_rhs ::= operator WS*
|
|
11
|
+
basic_rhs ::= operator WS* result
|
|
12
12
|
eq_approx ::= eq_operator WS* "~" WS* result
|
|
13
13
|
|
|
14
14
|
PLUS ::= "+"
|
|
@@ -18,46 +18,67 @@ DIVIDE ::= "/"
|
|
|
18
18
|
MODULUS ::= "%"
|
|
19
19
|
DEFAULT_VAL ::= "??"
|
|
20
20
|
arithmetic_operator ::= PLUS | MINUS | MULTIPLY | DIVIDE | MODULUS | DEFAULT_VAL
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
|
|
22
|
+
number_atom ::= number
|
|
23
|
+
number_time_atom ::= number_time
|
|
24
|
+
tod_atom ::= number_tod
|
|
25
|
+
dow_atom ::= dow
|
|
26
|
+
|
|
27
|
+
arithmetic_operand ::= fcall | number_time_atom | number_atom
|
|
28
|
+
arithmetic_result ::= arithmetic_operand WS* arithmetic_operator WS* (arithmetic_result | arithmetic_operand)
|
|
23
29
|
|
|
24
30
|
simple_result ::= fcall | value
|
|
25
31
|
result ::= arithmetic_result | simple_result
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
|
|
33
|
+
value_atom ::= false | true | array | time_period | number_time_atom | number_atom | tod_atom | string
|
|
34
|
+
value ::= value_atom
|
|
35
|
+
|
|
36
|
+
BEGIN_ARRAY ::= WS* #x5B WS* /* [ */
|
|
37
|
+
BEGIN_OBJECT ::= WS* #x7B WS* /* { */
|
|
38
|
+
END_ARRAY ::= WS* #x5D WS* /* ] */
|
|
39
|
+
END_OBJECT ::= WS* #x7D WS* /* } */
|
|
40
|
+
NAME_SEPARATOR ::= WS* #x3A WS* /* : */
|
|
41
|
+
VALUE_SEPARATOR ::= WS* #x2C WS* /* , */
|
|
42
|
+
WS ::= [#x20#x09#x0A#x0D]
|
|
34
43
|
|
|
35
44
|
operator ::= GTE | LTE | GT | LT | EQ | NEQ
|
|
36
45
|
eq_operator ::= EQ | NEQ
|
|
37
46
|
|
|
38
47
|
BEGIN_ARGUMENT ::= "("
|
|
39
48
|
END_ARGUMENT ::= ")"
|
|
40
|
-
|
|
41
49
|
BEGIN_PARENTHESIS ::= "("
|
|
42
50
|
END_PARENTHESIS ::= ")"
|
|
43
51
|
|
|
44
52
|
BEGIN_IN ||= "IN"
|
|
45
|
-
|
|
46
53
|
in_expr ::= BEGIN_IN WS* BEGIN_PARENTHESIS WS* arguments END_PARENTHESIS
|
|
47
54
|
|
|
48
55
|
argument ::= statement WS*
|
|
49
56
|
arguments ::= argument (WS* "," WS* argument)*
|
|
50
|
-
fname ::= [
|
|
57
|
+
fname ::= [A-Za-z0-9]+
|
|
51
58
|
fcall ::= fname WS* BEGIN_ARGUMENT WS* arguments? END_ARGUMENT
|
|
52
59
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
between_dash_or_and ||= (WS+ "AND" WS+) | (WS* "-" WS*)
|
|
61
|
+
|
|
62
|
+
between_number_inner ::= number_atom | number_time_atom
|
|
63
|
+
between_number ||= between_number_inner between_dash_or_and between_number_inner
|
|
64
|
+
|
|
65
|
+
between_number_time_inner ::= number_time_atom
|
|
66
|
+
between_number_time ||= between_number_time_inner between_dash_or_and between_number_time_inner (WS+ dow_range)?
|
|
67
|
+
|
|
68
|
+
between_tod_inner ::= tod_atom
|
|
69
|
+
between_tod ||= between_tod_inner (WS+ "AND" WS+) between_tod_inner (WS+ dow_range)?
|
|
70
|
+
|
|
56
71
|
between ||= "BETWEEN" WS+ (between_number | between_tod)
|
|
72
|
+
|
|
57
73
|
dow ||= "MONDAY" | "MON" | "TUESDAY" | "TUE" | "WEDNESDAY" | "WED" | "THURSDAY" | "THU" | "THUR" | "FRIDAY" | "FRI" | "SATURDAY" | "SAT" | "SUNDAY" | "SUN"
|
|
58
|
-
|
|
74
|
+
|
|
75
|
+
dow_range_inner ::= dow_atom
|
|
76
|
+
dow_range ||= "ON" WS+ dow_range_inner (WS+ "TO" WS+ dow_range_inner)?
|
|
77
|
+
|
|
59
78
|
between_time_only ||= "BETWEEN" WS+ between_number_time
|
|
60
79
|
between_tod_only ||= "BETWEEN" WS+ between_tod
|
|
80
|
+
between_time_only_atom ::= between_time_only
|
|
81
|
+
between_tod_only_atom ::= between_tod_only
|
|
61
82
|
|
|
62
83
|
AND ||= (WS* "&&" WS*) | (WS+ "AND" WS+)
|
|
63
84
|
OR ||= (WS* "||" WS*) | (WS+ "OR" WS+)
|
|
@@ -70,18 +91,21 @@ IS ||= "is"
|
|
|
70
91
|
EQ ::= "==" | "="
|
|
71
92
|
NEQ ::= "!="
|
|
72
93
|
NOT ||= ("!" WS*) | ("not" WS+)
|
|
94
|
+
|
|
73
95
|
false ||= "FALSE"
|
|
74
96
|
null ||= "null"
|
|
75
97
|
true ||= "TRUE"
|
|
98
|
+
|
|
76
99
|
array ::= BEGIN_ARRAY (value (VALUE_SEPARATOR value)*)? END_ARRAY
|
|
77
100
|
|
|
78
101
|
unit ||= "seconds" | "minutes" | "hours" | "weeks" | "days" | "second" | "minute" | "week" | "hour" | "day" | "mins" | "min"
|
|
79
|
-
|
|
102
|
+
|
|
103
|
+
number ::= "-"? ([0-9]+) ("." [0-9]+)? ("e" ("-" | "+")? ("0" | [1-9] [0-9]*))?
|
|
80
104
|
number_time ::= number WS+ unit
|
|
81
105
|
number_tod ::= ([0-9]+) ":" ([0-9]+)
|
|
82
106
|
|
|
83
|
-
time_period_ago ||=
|
|
84
|
-
time_period_ago_between ||=
|
|
107
|
+
time_period_ago ||= number_time_atom (WS+ number_time_atom)* WS+ AGO
|
|
108
|
+
time_period_ago_between ||= number_time_atom (WS+ number_time_atom)* WS+ AGO WS+ (between_time_only_atom | between_tod_only_atom)
|
|
85
109
|
time_period_const ||= "today" | time_period_ago
|
|
86
110
|
time_period ::= time_period_ago_between | time_period_const | between_tod_only | between_time_only
|
|
87
111
|
|
package/src/RuleParser.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const {Parser} = require('ebnf/dist/Parser.js'),
|
|
2
2
|
{ParsingError} = require('ebnf'),
|
|
3
|
-
assert = require('assert')
|
|
3
|
+
assert = require('assert'),
|
|
4
|
+
RuleParseError = require('./errors/RuleParseError')
|
|
4
5
|
|
|
5
6
|
let ParserRules = require('./RuleParser.ebnf.js')
|
|
6
7
|
let ParserCache;
|
|
@@ -108,10 +109,14 @@ class RuleParser {
|
|
|
108
109
|
// dow_range can have 1 or 2 children (single day or range)
|
|
109
110
|
if (dowRange.children.length === 1) {
|
|
110
111
|
// Single day: ON MONDAY - return just the day string
|
|
111
|
-
|
|
112
|
+
// New structure: dow_range -> dow_range_inner -> dow_atom -> dow
|
|
113
|
+
const dayText = RuleParser.__parseValue(dowRange.children[0])
|
|
114
|
+
return { start: dayText, end: dayText };
|
|
112
115
|
} else if (dowRange.children.length === 2) {
|
|
113
116
|
// Range: ON MONDAY TO FRIDAY - return both start and end days
|
|
114
|
-
|
|
117
|
+
const startDay = RuleParser.__parseValue(dowRange.children[0])
|
|
118
|
+
const endDay = RuleParser.__parseValue(dowRange.children[1])
|
|
119
|
+
return { start: startDay, end: endDay };
|
|
115
120
|
} else {
|
|
116
121
|
throw new Error(`Invalid dow_range with ${dowRange.children.length} children`);
|
|
117
122
|
}
|
|
@@ -133,12 +138,14 @@ class RuleParser {
|
|
|
133
138
|
let totalSeconds = 0
|
|
134
139
|
const components = []
|
|
135
140
|
for (const child of timePeriodAgo.children) {
|
|
136
|
-
if (child.type === '
|
|
137
|
-
|
|
138
|
-
const
|
|
141
|
+
if (child.type === 'number_time_atom') {
|
|
142
|
+
// New structure: number_time_atom -> number_time -> number, unit
|
|
143
|
+
const numberTime = child.children[0]
|
|
144
|
+
const number = parseFloat(numberTime.children[0].text)
|
|
145
|
+
const unit = numberTime.children[1].text.toUpperCase()
|
|
139
146
|
components.push([number, unit])
|
|
140
147
|
// Parse the value to get seconds
|
|
141
|
-
totalSeconds += RuleParser.__parseValue(
|
|
148
|
+
totalSeconds += RuleParser.__parseValue(numberTime)
|
|
142
149
|
}
|
|
143
150
|
}
|
|
144
151
|
// If there's only one component, use its number and unit
|
|
@@ -151,40 +158,69 @@ class RuleParser {
|
|
|
151
158
|
}
|
|
152
159
|
return ["TimePeriodConst", tp.text]
|
|
153
160
|
case 'time_period_ago_between': {
|
|
154
|
-
// time_period_ago_between has:
|
|
161
|
+
// time_period_ago_between has: number_time_atom (WS+ number_time_atom)* WS+ AGO WS+ (between_time_only | between_tod_only)
|
|
155
162
|
// We need to extract all number_time children and sum them up, then return TimePeriodBetweenAgo
|
|
156
163
|
let totalSeconds = 0
|
|
157
|
-
let
|
|
164
|
+
let betweenNode = null
|
|
165
|
+
let isBetweenTod = false
|
|
158
166
|
|
|
159
|
-
// Find all
|
|
167
|
+
// Find all number_time_atom children and the between node (either between_time_only or between_tod_only)
|
|
160
168
|
for (let i = 0; i < tp.children.length; i++) {
|
|
161
|
-
if (tp.children[i].type === '
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
169
|
+
if (tp.children[i].type === 'number_time_atom') {
|
|
170
|
+
// New structure: number_time_atom -> number_time
|
|
171
|
+
totalSeconds += RuleParser.__parseValue(tp.children[i].children[0])
|
|
172
|
+
} else if (tp.children[i].type === 'between_time_only' || tp.children[i].type === 'between_time_only_atom') {
|
|
173
|
+
betweenNode = tp.children[i].type === 'between_time_only_atom' ? tp.children[i].children[0] : tp.children[i]
|
|
174
|
+
isBetweenTod = false
|
|
175
|
+
} else if (tp.children[i].type === 'between_tod_only' || tp.children[i].type === 'between_tod_only_atom') {
|
|
176
|
+
betweenNode = tp.children[i].type === 'between_tod_only_atom' ? tp.children[i].children[0] : tp.children[i]
|
|
177
|
+
isBetweenTod = true
|
|
165
178
|
}
|
|
166
179
|
}
|
|
167
180
|
|
|
168
181
|
// This should always be present based on the grammar, but check defensively
|
|
169
|
-
if (!
|
|
170
|
-
throw new Error('time_period_ago_between requires between_tod_only child')
|
|
182
|
+
if (!betweenNode) {
|
|
183
|
+
throw new Error('time_period_ago_between requires between_time_only or between_tod_only child')
|
|
171
184
|
}
|
|
172
185
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
186
|
+
if (isBetweenTod) {
|
|
187
|
+
const betweenTod = betweenNode.children[0]
|
|
188
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
189
|
+
let startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
190
|
+
let endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
191
|
+
|
|
192
|
+
// Check if there's a dow_range at betweenTod.children[2]
|
|
193
|
+
// Note: startTod and endTod should always be objects from number_tod parsing
|
|
194
|
+
if (betweenTod.children.length > 2) {
|
|
195
|
+
RuleParser._addDowToTods(startTod, endTod, betweenTod.children[2])
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return ["TimePeriodBetweenAgo", totalSeconds, startTod, endTod]
|
|
199
|
+
} else {
|
|
200
|
+
const betweenNumberTime = betweenNode.children[0]
|
|
201
|
+
// between_number_time has: children[0] = first time_inner, children[1] = separator, children[2] = second time_inner, children[3] = optional dow_range
|
|
202
|
+
const startValue = RuleParser.__parseValue(betweenNumberTime.children[0])
|
|
203
|
+
const endValue = RuleParser.__parseValue(betweenNumberTime.children[2])
|
|
204
|
+
|
|
205
|
+
// Check if there's a dow_range at betweenNumberTime.children[3]
|
|
206
|
+
if (betweenNumberTime.children.length > 3 && betweenNumberTime.children[3].type === 'dow_range') {
|
|
207
|
+
const dow = RuleParser._parseDowRange(betweenNumberTime.children[3])
|
|
208
|
+
if (dow.start === dow.end) {
|
|
209
|
+
// Single day: ["TimePeriodBetweenAgo", totalSeconds, start, end, "MONDAY"]
|
|
210
|
+
return ["TimePeriodBetweenAgo", totalSeconds, startValue, endValue, dow.start]
|
|
211
|
+
} else {
|
|
212
|
+
// Range: ["TimePeriodBetweenAgo", totalSeconds, start, end, "MONDAY", "FRIDAY"]
|
|
213
|
+
return ["TimePeriodBetweenAgo", totalSeconds, startValue, endValue, dow.start, dow.end]
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return ["TimePeriodBetweenAgo", totalSeconds, startValue, endValue]
|
|
181
218
|
}
|
|
182
|
-
|
|
183
|
-
return ["TimePeriodBetweenAgo", totalSeconds, startTod, endTod]
|
|
184
219
|
}
|
|
185
220
|
case 'between_tod_only': {
|
|
186
221
|
// between_tod_only has children[0] = between_tod node
|
|
187
222
|
const betweenTod = tp.children[0]
|
|
223
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
188
224
|
let startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
189
225
|
let endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
190
226
|
|
|
@@ -200,13 +236,14 @@ class RuleParser {
|
|
|
200
236
|
case 'between_time_only': {
|
|
201
237
|
// between_time_only has children[0] = between_number_time node
|
|
202
238
|
const betweenNumberTime = tp.children[0]
|
|
239
|
+
// between_number_time has: children[0] = first time_inner, children[1] = separator, children[2] = second time_inner, children[3] = optional dow_range
|
|
203
240
|
const startValue = RuleParser.__parseValue(betweenNumberTime.children[0])
|
|
204
|
-
const endValue = RuleParser.__parseValue(betweenNumberTime.children[
|
|
241
|
+
const endValue = RuleParser.__parseValue(betweenNumberTime.children[2])
|
|
205
242
|
|
|
206
|
-
// Check if there's a dow_range at betweenNumberTime.children[
|
|
243
|
+
// Check if there's a dow_range at betweenNumberTime.children[3]
|
|
207
244
|
// If DOW filters are provided, append them as additional parameters
|
|
208
|
-
if (betweenNumberTime.children.length >
|
|
209
|
-
const dow = RuleParser._parseDowRange(betweenNumberTime.children[
|
|
245
|
+
if (betweenNumberTime.children.length > 3 && betweenNumberTime.children[3].type === 'dow_range') {
|
|
246
|
+
const dow = RuleParser._parseDowRange(betweenNumberTime.children[3])
|
|
210
247
|
if (dow.start === dow.end) {
|
|
211
248
|
// Single day: ["TimePeriodBetween", start, end, "MONDAY"]
|
|
212
249
|
return ["TimePeriodBetween", startValue, endValue, dow.start]
|
|
@@ -223,6 +260,25 @@ class RuleParser {
|
|
|
223
260
|
static __parseValue(child){
|
|
224
261
|
const type = child.type
|
|
225
262
|
switch(type){
|
|
263
|
+
case 'value': {
|
|
264
|
+
// Arrays have value nodes as children - unwrap to get value_atom
|
|
265
|
+
return RuleParser.__parseValue(child.children[0])
|
|
266
|
+
}
|
|
267
|
+
case 'value_atom': {
|
|
268
|
+
// New layer: unwrap value_atom to get the actual atomic type
|
|
269
|
+
return RuleParser.__parseValue(child.children[0])
|
|
270
|
+
}
|
|
271
|
+
case 'number_atom':
|
|
272
|
+
case 'number_time_atom':
|
|
273
|
+
case 'tod_atom':
|
|
274
|
+
case 'dow_atom':
|
|
275
|
+
case 'between_tod_inner':
|
|
276
|
+
case 'between_number_inner':
|
|
277
|
+
case 'between_number_time_inner':
|
|
278
|
+
case 'dow_range_inner': {
|
|
279
|
+
// New layer: unwrap atom wrappers to get the actual leaf nodes
|
|
280
|
+
return RuleParser.__parseValue(child.children[0])
|
|
281
|
+
}
|
|
226
282
|
case 'string': {
|
|
227
283
|
const str = child.text
|
|
228
284
|
return str.slice(1, -1)
|
|
@@ -274,10 +330,13 @@ class RuleParser {
|
|
|
274
330
|
case 'array': {
|
|
275
331
|
const ret = []
|
|
276
332
|
for(const c of child.children){
|
|
277
|
-
ret.push(RuleParser.__parseValue(c
|
|
333
|
+
ret.push(RuleParser.__parseValue(c))
|
|
278
334
|
}
|
|
279
335
|
return ret;
|
|
280
336
|
}
|
|
337
|
+
case 'dow': {
|
|
338
|
+
return normalizeDow(child.text)
|
|
339
|
+
}
|
|
281
340
|
default:
|
|
282
341
|
throw new Error(`Unknown value type ${type}`)
|
|
283
342
|
}
|
|
@@ -287,7 +346,17 @@ class RuleParser {
|
|
|
287
346
|
|
|
288
347
|
const type = child.type
|
|
289
348
|
switch(type){
|
|
349
|
+
case 'value_atom': {
|
|
350
|
+
// New layer: unwrap value_atom to get the actual atomic type
|
|
351
|
+
const atomChild = child.children[0]
|
|
352
|
+
if (atomChild.type === 'time_period') {
|
|
353
|
+
const tp = atomChild.children[0]
|
|
354
|
+
return RuleParser._parseTimePeriod(tp)
|
|
355
|
+
}
|
|
356
|
+
return ['Value', RuleParser.__parseValue(atomChild)]
|
|
357
|
+
}
|
|
290
358
|
case 'time_period': {
|
|
359
|
+
// Old structure (shouldn't happen with new EBNF)
|
|
291
360
|
const tp = child.children[0]
|
|
292
361
|
return RuleParser._parseTimePeriod(tp)
|
|
293
362
|
}
|
|
@@ -314,6 +383,11 @@ class RuleParser {
|
|
|
314
383
|
switch(type){
|
|
315
384
|
case 'fcall':
|
|
316
385
|
return RuleParser._parseFcall(child)
|
|
386
|
+
case 'number_atom':
|
|
387
|
+
case 'number_time_atom': {
|
|
388
|
+
// New layer: unwrap atom wrappers to get the actual leaf nodes
|
|
389
|
+
return ['Value', RuleParser.__parseValue(child.children[0])]
|
|
390
|
+
}
|
|
317
391
|
case 'number':
|
|
318
392
|
return ['Value', parseFloat(child.text)]
|
|
319
393
|
case 'number_time':
|
|
@@ -406,7 +480,7 @@ class RuleParser {
|
|
|
406
480
|
switch(rhs.type){
|
|
407
481
|
case 'between_tod': {
|
|
408
482
|
// Direct between_tod (without wrapping between node)
|
|
409
|
-
// between_tod has: children[0] = first
|
|
483
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
410
484
|
const startTod = RuleParser.__parseValue(rhs.children[0])
|
|
411
485
|
const endTod = RuleParser.__parseValue(rhs.children[1])
|
|
412
486
|
|
|
@@ -421,7 +495,7 @@ class RuleParser {
|
|
|
421
495
|
// between wraps either between_number or between_tod
|
|
422
496
|
const betweenChild = rhs.children[0]
|
|
423
497
|
if (betweenChild.type === 'between_tod') {
|
|
424
|
-
// between_tod has: children[0] = first
|
|
498
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
425
499
|
const startTod = RuleParser.__parseValue(betweenChild.children[0])
|
|
426
500
|
const endTod = RuleParser.__parseValue(betweenChild.children[1])
|
|
427
501
|
|
|
@@ -432,12 +506,13 @@ class RuleParser {
|
|
|
432
506
|
|
|
433
507
|
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', startTod], ['Value', endTod]]
|
|
434
508
|
} else {
|
|
435
|
-
// between_number
|
|
436
|
-
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(betweenChild.children[0])], ['Value', RuleParser.__parseValue(betweenChild.children[
|
|
509
|
+
// between_number has: children[0] = first number_inner, children[1] = separator, children[2] = second number_inner
|
|
510
|
+
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(betweenChild.children[0])], ['Value', RuleParser.__parseValue(betweenChild.children[2])]]
|
|
437
511
|
}
|
|
438
512
|
}
|
|
439
513
|
case 'between_number':
|
|
440
|
-
|
|
514
|
+
// between_number has: children[0] = first number_inner, children[1] = separator, children[2] = second number_inner
|
|
515
|
+
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(rhs.children[0].children[0])], ['Value', RuleParser.__parseValue(rhs.children[0].children[2])]]
|
|
441
516
|
case 'basic_rhs':
|
|
442
517
|
return [OperatorFn[rhs.children[0].text], RuleParser._parseResult(expr.children[0]), RuleParser._parseResult(rhs.children[1])]
|
|
443
518
|
case 'eq_approx': {
|
|
@@ -559,8 +634,6 @@ class RuleParser {
|
|
|
559
634
|
// Extract the invalid time from the error message
|
|
560
635
|
const match = e.message.match(/Invalid time of day[,:]?\s*([0-9:]+)/);
|
|
561
636
|
const badTod = match ? match[1] : 'invalid';
|
|
562
|
-
const { ParsingError } = require('ebnf');
|
|
563
|
-
const { RuleParseError } = require('./errors/RuleParseError');
|
|
564
637
|
|
|
565
638
|
// Calculate position (simplified - at end of input)
|
|
566
639
|
const lines = txt.trim().split('\n');
|
|
@@ -585,7 +658,6 @@ class RuleParser {
|
|
|
585
658
|
if (e.message && e.message.includes('Invalid day of week')) {
|
|
586
659
|
const match = e.message.match(/Invalid day of week[,:]?\s*(\w+)/);
|
|
587
660
|
const badDow = match ? match[1] : 'invalid';
|
|
588
|
-
const { RuleParseError } = require('./errors/RuleParseError');
|
|
589
661
|
|
|
590
662
|
const lines = txt.trim().split('\n');
|
|
591
663
|
const position = {
|
|
@@ -611,5 +683,6 @@ class RuleParser {
|
|
|
611
683
|
}
|
|
612
684
|
}
|
|
613
685
|
module.exports = RuleParser
|
|
614
|
-
module.exports.
|
|
615
|
-
module.exports.
|
|
686
|
+
module.exports.ParserRules = ParserRules
|
|
687
|
+
module.exports.ParsingError = ParsingError
|
|
688
|
+
module.exports.RuleParseError = RuleParseError
|
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports=[{"name":"statement_main","bnf":[["statement","EOF"]]},{"name":"logical_operator","bnf":[["AND"],["OR"]]},{"name":"%statement[2]","bnf":[["logical_operator","expression"]],"fragment":true},{"name":"statement","bnf":[["expression","%statement[2]*"]]},{"name":"expression","bnf":[["not_expression"],["standard_expression"],["parenthesis_expression"]]},{"name":"parenthesis_expression","bnf":[["BEGIN_PARENTHESIS","WS*","statement","WS*","END_PARENTHESIS"]]},{"name":"%not_expression[2]","bnf":[["result"],["parenthesis_expression"]],"fragment":true},{"name":"not_expression","bnf":[["NOT","%not_expression[2]"]]},{"name":"%standard_expression[2][1]","bnf":[["WS*","eq_approx"]],"fragment":true},{"name":"%standard_expression[2][2]","bnf":[["WS*","basic_rhs"]],"fragment":true},{"name":"%standard_expression[2][3][1]","bnf":[["WS+","IS"]],"fragment":true},{"name":"%standard_expression[2][3]","bnf":[["%standard_expression[2][3][1]?","WS+","between"]],"fragment":true},{"name":"%standard_expression[2][4]","bnf":[["WS+","in_expr"]],"fragment":true},{"name":"%standard_expression[2]","bnf":[["%standard_expression[2][1]"],["%standard_expression[2][2]"],["%standard_expression[2][3]"],["%standard_expression[2][4]"]],"fragment":true},{"name":"standard_expression","bnf":[["result","%standard_expression[2]?"]]},{"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_operand","bnf":[["fcall"],["
|
|
1
|
+
module.exports=[{"name":"statement_main","bnf":[["statement","EOF"]]},{"name":"logical_operator","bnf":[["AND"],["OR"]]},{"name":"%statement[2]","bnf":[["logical_operator","expression"]],"fragment":true},{"name":"statement","bnf":[["expression","%statement[2]*"]]},{"name":"expression","bnf":[["not_expression"],["standard_expression"],["parenthesis_expression"]]},{"name":"parenthesis_expression","bnf":[["BEGIN_PARENTHESIS","WS*","statement","WS*","END_PARENTHESIS"]]},{"name":"%not_expression[2]","bnf":[["result"],["parenthesis_expression"]],"fragment":true},{"name":"not_expression","bnf":[["NOT","%not_expression[2]"]]},{"name":"%standard_expression[2][1]","bnf":[["WS*","eq_approx"]],"fragment":true},{"name":"%standard_expression[2][2]","bnf":[["WS*","basic_rhs"]],"fragment":true},{"name":"%standard_expression[2][3][1]","bnf":[["WS+","IS"]],"fragment":true},{"name":"%standard_expression[2][3]","bnf":[["%standard_expression[2][3][1]?","WS+","between"]],"fragment":true},{"name":"%standard_expression[2][4]","bnf":[["WS+","in_expr"]],"fragment":true},{"name":"%standard_expression[2]","bnf":[["%standard_expression[2][1]"],["%standard_expression[2][2]"],["%standard_expression[2][3]"],["%standard_expression[2][4]"]],"fragment":true},{"name":"standard_expression","bnf":[["result","%standard_expression[2]?"]]},{"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":"number_atom","bnf":[["number"]]},{"name":"number_time_atom","bnf":[["number_time"]]},{"name":"tod_atom","bnf":[["number_tod"]]},{"name":"dow_atom","bnf":[["dow"]]},{"name":"arithmetic_operand","bnf":[["fcall"],["number_time_atom"],["number_atom"]]},{"name":"%arithmetic_result[5]","bnf":[["arithmetic_result"],["arithmetic_operand"]],"fragment":true},{"name":"arithmetic_result","bnf":[["arithmetic_operand","WS*","arithmetic_operator","WS*","%arithmetic_result[5]"]]},{"name":"simple_result","bnf":[["fcall"],["value"]]},{"name":"result","bnf":[["arithmetic_result"],["simple_result"]]},{"name":"value_atom","bnf":[["false"],["true"],["array"],["time_period"],["number_time_atom"],["number_atom"],["tod_atom"],["string"]]},{"name":"value","bnf":[["value_atom"]]},{"name":"BEGIN_ARRAY","bnf":[["WS*",/\x5B/,"WS*"]]},{"name":"BEGIN_OBJECT","bnf":[["WS*",/\x7B/,"WS*"]]},{"name":"END_ARRAY","bnf":[["WS*",/\x5D/,"WS*"]]},{"name":"END_OBJECT","bnf":[["WS*",/\x7D/,"WS*"]]},{"name":"NAME_SEPARATOR","bnf":[["WS*",/\x3A/,"WS*"]]},{"name":"VALUE_SEPARATOR","bnf":[["WS*",/\x2C/,"WS*"]]},{"name":"WS","bnf":[[/[\x20\x09\x0A\x0D]/]]},{"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":"BEGIN_IN","bnf":[[/[Ii]/,/[Nn]/]]},{"name":"in_expr","bnf":[["BEGIN_IN","WS*","BEGIN_PARENTHESIS","WS*","arguments","END_PARENTHESIS"]]},{"name":"argument","bnf":[["statement","WS*"]]},{"name":"%arguments[2]","bnf":[["WS*","\",\"","WS*","argument"]],"fragment":true},{"name":"arguments","bnf":[["argument","%arguments[2]*"]]},{"name":"%fname[1]","bnf":[[/[A-Za-z0-9]/]]},{"name":"fname","bnf":[["%fname[1]+"]]},{"name":"fcall","bnf":[["fname","WS*","BEGIN_ARGUMENT","WS*","arguments?","END_ARGUMENT"]]},{"name":"%between_dash_or_and[1]","bnf":[["WS+",/[Aa]/,/[Nn]/,/[Dd]/,"WS+"]],"fragment":true},{"name":"%between_dash_or_and[2]","bnf":[["WS*",/\-/,"WS*"]],"fragment":true},{"name":"between_dash_or_and","bnf":[["%between_dash_or_and[1]"],["%between_dash_or_and[2]"]]},{"name":"between_number_inner","bnf":[["number_atom"],["number_time_atom"]]},{"name":"between_number","bnf":[["between_number_inner","between_dash_or_and","between_number_inner"]]},{"name":"between_number_time_inner","bnf":[["number_time_atom"]]},{"name":"%between_number_time[4]","bnf":[["WS+","dow_range"]],"fragment":true},{"name":"between_number_time","bnf":[["between_number_time_inner","between_dash_or_and","between_number_time_inner","%between_number_time[4]?"]]},{"name":"between_tod_inner","bnf":[["tod_atom"]]},{"name":"%between_tod[2]","bnf":[["WS+",/[Aa]/,/[Nn]/,/[Dd]/,"WS+"]],"fragment":true},{"name":"%between_tod[4]","bnf":[["WS+","dow_range"]],"fragment":true},{"name":"between_tod","bnf":[["between_tod_inner","%between_tod[2]","between_tod_inner","%between_tod[4]?"]]},{"name":"%between[3]","bnf":[["between_number"],["between_tod"]],"fragment":true},{"name":"between","bnf":[[/[Bb]/,/[Ee]/,/[Tt]/,/[Ww]/,/[Ee]/,/[Ee]/,/[Nn]/,"WS+","%between[3]"]]},{"name":"dow","bnf":[[/[Mm]/,/[Oo]/,/[Nn]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Mm]/,/[Oo]/,/[Nn]/],[/[Tt]/,/[Uu]/,/[Ee]/,/[Ss]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Tt]/,/[Uu]/,/[Ee]/],[/[Ww]/,/[Ee]/,/[Dd]/,/[Nn]/,/[Ee]/,/[Ss]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Ww]/,/[Ee]/,/[Dd]/],[/[Tt]/,/[Hh]/,/[Uu]/,/[Rr]/,/[Ss]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Tt]/,/[Hh]/,/[Uu]/],[/[Tt]/,/[Hh]/,/[Uu]/,/[Rr]/],[/[Ff]/,/[Rr]/,/[Ii]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Ff]/,/[Rr]/,/[Ii]/],[/[Ss]/,/[Aa]/,/[Tt]/,/[Uu]/,/[Rr]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Ss]/,/[Aa]/,/[Tt]/],[/[Ss]/,/[Uu]/,/[Nn]/,/[Dd]/,/[Aa]/,/[Yy]/],[/[Ss]/,/[Uu]/,/[Nn]/]]},{"name":"dow_range_inner","bnf":[["dow_atom"]]},{"name":"%dow_range[4]","bnf":[["WS+",/[Tt]/,/[Oo]/,"WS+","dow_range_inner"]],"fragment":true},{"name":"dow_range","bnf":[[/[Oo]/,/[Nn]/,"WS+","dow_range_inner","%dow_range[4]?"]]},{"name":"between_time_only","bnf":[[/[Bb]/,/[Ee]/,/[Tt]/,/[Ww]/,/[Ee]/,/[Ee]/,/[Nn]/,"WS+","between_number_time"]]},{"name":"between_tod_only","bnf":[[/[Bb]/,/[Ee]/,/[Tt]/,/[Ww]/,/[Ee]/,/[Ee]/,/[Nn]/,"WS+","between_tod"]]},{"name":"between_time_only_atom","bnf":[["between_time_only"]]},{"name":"between_tod_only_atom","bnf":[["between_tod_only"]]},{"name":"%AND[1]","bnf":[["WS*",/&/,/&/,"WS*"]],"fragment":true},{"name":"%AND[2]","bnf":[["WS+",/[Aa]/,/[Nn]/,/[Dd]/,"WS+"]],"fragment":true},{"name":"AND","bnf":[["%AND[1]"],["%AND[2]"]]},{"name":"%OR[1]","bnf":[["WS*",/\|/,/\|/,"WS*"]],"fragment":true},{"name":"%OR[2]","bnf":[["WS+",/[Oo]/,/[Rr]/,"WS+"]],"fragment":true},{"name":"OR","bnf":[["%OR[1]"],["%OR[2]"]]},{"name":"AGO","bnf":[[/[Aa]/,/[Gg]/,/[Oo]/]]},{"name":"GT","bnf":[["\">\""]]},{"name":"LT","bnf":[["\"<\""]]},{"name":"GTE","bnf":[["\">=\""]]},{"name":"LTE","bnf":[["\"<=\""]]},{"name":"IS","bnf":[[/[Ii]/,/[Ss]/]]},{"name":"EQ","bnf":[["\"==\""],["\"=\""]]},{"name":"NEQ","bnf":[["\"!=\""]]},{"name":"%NOT[1]","bnf":[[/!/,"WS*"]],"fragment":true},{"name":"%NOT[2]","bnf":[[/[Nn]/,/[Oo]/,/[Tt]/,"WS+"]],"fragment":true},{"name":"NOT","bnf":[["%NOT[1]"],["%NOT[2]"]]},{"name":"false","bnf":[[/[Ff]/,/[Aa]/,/[Ll]/,/[Ss]/,/[Ee]/]]},{"name":"null","bnf":[[/[Nn]/,/[Uu]/,/[Ll]/,/[Ll]/]]},{"name":"true","bnf":[[/[Tt]/,/[Rr]/,/[Uu]/,/[Ee]/]]},{"name":"%array[2][2]","bnf":[["VALUE_SEPARATOR","value"]],"fragment":true},{"name":"%array[2]","bnf":[["value","%array[2][2]*"]],"fragment":true},{"name":"array","bnf":[["BEGIN_ARRAY","%array[2]?","END_ARRAY"]]},{"name":"unit","bnf":[[/[Ss]/,/[Ee]/,/[Cc]/,/[Oo]/,/[Nn]/,/[Dd]/,/[Ss]/],[/[Mm]/,/[Ii]/,/[Nn]/,/[Uu]/,/[Tt]/,/[Ee]/,/[Ss]/],[/[Hh]/,/[Oo]/,/[Uu]/,/[Rr]/,/[Ss]/],[/[Ww]/,/[Ee]/,/[Ee]/,/[Kk]/,/[Ss]/],[/[Dd]/,/[Aa]/,/[Yy]/,/[Ss]/],[/[Ss]/,/[Ee]/,/[Cc]/,/[Oo]/,/[Nn]/,/[Dd]/],[/[Mm]/,/[Ii]/,/[Nn]/,/[Uu]/,/[Tt]/,/[Ee]/],[/[Ww]/,/[Ee]/,/[Ee]/,/[Kk]/],[/[Hh]/,/[Oo]/,/[Uu]/,/[Rr]/],[/[Dd]/,/[Aa]/,/[Yy]/],[/[Mm]/,/[Ii]/,/[Nn]/,/[Ss]/],[/[Mm]/,/[Ii]/,/[Nn]/]]},{"name":"%number[2][1]","bnf":[[/[0-9]/]]},{"name":"%number[2]","bnf":[["%number[2][1]+"]],"fragment":true},{"name":"%number[3][2]","bnf":[[/[0-9]/]]},{"name":"%number[3]","bnf":[["\".\"","%number[3][2]+"]],"fragment":true},{"name":"%number[4][2]","bnf":[["\"-\""],["\"+\""]],"fragment":true},{"name":"%number[4][3][2]","bnf":[[/[0-9]/]]},{"name":"%number[4][3]","bnf":[["\"0\""],[/[1-9]/,"%number[4][3][2]*"]],"fragment":true},{"name":"%number[4]","bnf":[["\"e\"","%number[4][2]?","%number[4][3]"]],"fragment":true},{"name":"number","bnf":[["\"-\"?","%number[2]","%number[3]?","%number[4]?"]]},{"name":"number_time","bnf":[["number","WS+","unit"]]},{"name":"%number_tod[1][1]","bnf":[[/[0-9]/]]},{"name":"%number_tod[1]","bnf":[["%number_tod[1][1]+"]],"fragment":true},{"name":"%number_tod[3][1]","bnf":[[/[0-9]/]]},{"name":"%number_tod[3]","bnf":[["%number_tod[3][1]+"]],"fragment":true},{"name":"number_tod","bnf":[["%number_tod[1]","\":\"","%number_tod[3]"]]},{"name":"%time_period_ago[2]","bnf":[["WS+","number_time_atom"]],"fragment":true},{"name":"time_period_ago","bnf":[["number_time_atom","%time_period_ago[2]*","WS+","AGO"]]},{"name":"%time_period_ago_between[2]","bnf":[["WS+","number_time_atom"]],"fragment":true},{"name":"%time_period_ago_between[6]","bnf":[["between_time_only_atom"],["between_tod_only_atom"]],"fragment":true},{"name":"time_period_ago_between","bnf":[["number_time_atom","%time_period_ago_between[2]*","WS+","AGO","WS+","%time_period_ago_between[6]"]]},{"name":"time_period_const","bnf":[[/[Tt]/,/[Oo]/,/[Dd]/,/[Aa]/,/[Yy]/],["time_period_ago"]]},{"name":"time_period","bnf":[["time_period_ago_between"],["time_period_const"],["between_tod_only"],["between_time_only"]]},{"name":"%string[2][1]","bnf":[[/[\x20-\x21]/],[/[\x23-\x5B]/],[/[\x5D-\uFFFF]/]],"fragment":true},{"name":"%string[2][2]","bnf":[[/\x22/],[/\x5C/],[/\x2F/],[/\x62/],[/\x66/],[/\x6E/],[/\x72/],[/\x74/],[/\x75/,"HEXDIG","HEXDIG","HEXDIG","HEXDIG"]],"fragment":true},{"name":"%string[2]","bnf":[["%string[2][1]"],[/\x5C/,"%string[2][2]"]],"fragment":true},{"name":"string","bnf":[["'\"'","%string[2]*","'\"'"]]},{"name":"HEXDIG","bnf":[[/[a-fA-F0-9]/]]}]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const {Parser} = require('ebnf/dist/Parser.js'),
|
|
2
2
|
{ParsingError} = require('ebnf'),
|
|
3
|
-
assert = require('assert')
|
|
3
|
+
assert = require('assert'),
|
|
4
|
+
RuleParseError = require('./errors/RuleParseError')
|
|
4
5
|
|
|
5
6
|
let ParserRules = require('./RuleParser.production.ebnf.js')
|
|
6
7
|
let ParserCache;
|
|
@@ -108,10 +109,14 @@ class RuleParser {
|
|
|
108
109
|
// dow_range can have 1 or 2 children (single day or range)
|
|
109
110
|
if (dowRange.children.length === 1) {
|
|
110
111
|
// Single day: ON MONDAY - return just the day string
|
|
111
|
-
|
|
112
|
+
// New structure: dow_range -> dow_range_inner -> dow_atom -> dow
|
|
113
|
+
const dayText = RuleParser.__parseValue(dowRange.children[0])
|
|
114
|
+
return { start: dayText, end: dayText };
|
|
112
115
|
} else if (dowRange.children.length === 2) {
|
|
113
116
|
// Range: ON MONDAY TO FRIDAY - return both start and end days
|
|
114
|
-
|
|
117
|
+
const startDay = RuleParser.__parseValue(dowRange.children[0])
|
|
118
|
+
const endDay = RuleParser.__parseValue(dowRange.children[1])
|
|
119
|
+
return { start: startDay, end: endDay };
|
|
115
120
|
} else {
|
|
116
121
|
throw new Error(`Invalid dow_range with ${dowRange.children.length} children`);
|
|
117
122
|
}
|
|
@@ -133,12 +138,14 @@ class RuleParser {
|
|
|
133
138
|
let totalSeconds = 0
|
|
134
139
|
const components = []
|
|
135
140
|
for (const child of timePeriodAgo.children) {
|
|
136
|
-
if (child.type === '
|
|
137
|
-
|
|
138
|
-
const
|
|
141
|
+
if (child.type === 'number_time_atom') {
|
|
142
|
+
// New structure: number_time_atom -> number_time -> number, unit
|
|
143
|
+
const numberTime = child.children[0]
|
|
144
|
+
const number = parseFloat(numberTime.children[0].text)
|
|
145
|
+
const unit = numberTime.children[1].text.toUpperCase()
|
|
139
146
|
components.push([number, unit])
|
|
140
147
|
// Parse the value to get seconds
|
|
141
|
-
totalSeconds += RuleParser.__parseValue(
|
|
148
|
+
totalSeconds += RuleParser.__parseValue(numberTime)
|
|
142
149
|
}
|
|
143
150
|
}
|
|
144
151
|
// If there's only one component, use its number and unit
|
|
@@ -151,40 +158,69 @@ class RuleParser {
|
|
|
151
158
|
}
|
|
152
159
|
return ["TimePeriodConst", tp.text]
|
|
153
160
|
case 'time_period_ago_between': {
|
|
154
|
-
// time_period_ago_between has:
|
|
161
|
+
// time_period_ago_between has: number_time_atom (WS+ number_time_atom)* WS+ AGO WS+ (between_time_only | between_tod_only)
|
|
155
162
|
// We need to extract all number_time children and sum them up, then return TimePeriodBetweenAgo
|
|
156
163
|
let totalSeconds = 0
|
|
157
|
-
let
|
|
164
|
+
let betweenNode = null
|
|
165
|
+
let isBetweenTod = false
|
|
158
166
|
|
|
159
|
-
// Find all
|
|
167
|
+
// Find all number_time_atom children and the between node (either between_time_only or between_tod_only)
|
|
160
168
|
for (let i = 0; i < tp.children.length; i++) {
|
|
161
|
-
if (tp.children[i].type === '
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
169
|
+
if (tp.children[i].type === 'number_time_atom') {
|
|
170
|
+
// New structure: number_time_atom -> number_time
|
|
171
|
+
totalSeconds += RuleParser.__parseValue(tp.children[i].children[0])
|
|
172
|
+
} else if (tp.children[i].type === 'between_time_only' || tp.children[i].type === 'between_time_only_atom') {
|
|
173
|
+
betweenNode = tp.children[i].type === 'between_time_only_atom' ? tp.children[i].children[0] : tp.children[i]
|
|
174
|
+
isBetweenTod = false
|
|
175
|
+
} else if (tp.children[i].type === 'between_tod_only' || tp.children[i].type === 'between_tod_only_atom') {
|
|
176
|
+
betweenNode = tp.children[i].type === 'between_tod_only_atom' ? tp.children[i].children[0] : tp.children[i]
|
|
177
|
+
isBetweenTod = true
|
|
165
178
|
}
|
|
166
179
|
}
|
|
167
180
|
|
|
168
181
|
// This should always be present based on the grammar, but check defensively
|
|
169
|
-
if (!
|
|
170
|
-
throw new Error('time_period_ago_between requires between_tod_only child')
|
|
182
|
+
if (!betweenNode) {
|
|
183
|
+
throw new Error('time_period_ago_between requires between_time_only or between_tod_only child')
|
|
171
184
|
}
|
|
172
185
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
186
|
+
if (isBetweenTod) {
|
|
187
|
+
const betweenTod = betweenNode.children[0]
|
|
188
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
189
|
+
let startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
190
|
+
let endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
191
|
+
|
|
192
|
+
// Check if there's a dow_range at betweenTod.children[2]
|
|
193
|
+
// Note: startTod and endTod should always be objects from number_tod parsing
|
|
194
|
+
if (betweenTod.children.length > 2) {
|
|
195
|
+
RuleParser._addDowToTods(startTod, endTod, betweenTod.children[2])
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return ["TimePeriodBetweenAgo", totalSeconds, startTod, endTod]
|
|
199
|
+
} else {
|
|
200
|
+
const betweenNumberTime = betweenNode.children[0]
|
|
201
|
+
// between_number_time has: children[0] = first time_inner, children[1] = separator, children[2] = second time_inner, children[3] = optional dow_range
|
|
202
|
+
const startValue = RuleParser.__parseValue(betweenNumberTime.children[0])
|
|
203
|
+
const endValue = RuleParser.__parseValue(betweenNumberTime.children[2])
|
|
204
|
+
|
|
205
|
+
// Check if there's a dow_range at betweenNumberTime.children[3]
|
|
206
|
+
if (betweenNumberTime.children.length > 3 && betweenNumberTime.children[3].type === 'dow_range') {
|
|
207
|
+
const dow = RuleParser._parseDowRange(betweenNumberTime.children[3])
|
|
208
|
+
if (dow.start === dow.end) {
|
|
209
|
+
// Single day: ["TimePeriodBetweenAgo", totalSeconds, start, end, "MONDAY"]
|
|
210
|
+
return ["TimePeriodBetweenAgo", totalSeconds, startValue, endValue, dow.start]
|
|
211
|
+
} else {
|
|
212
|
+
// Range: ["TimePeriodBetweenAgo", totalSeconds, start, end, "MONDAY", "FRIDAY"]
|
|
213
|
+
return ["TimePeriodBetweenAgo", totalSeconds, startValue, endValue, dow.start, dow.end]
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return ["TimePeriodBetweenAgo", totalSeconds, startValue, endValue]
|
|
181
218
|
}
|
|
182
|
-
|
|
183
|
-
return ["TimePeriodBetweenAgo", totalSeconds, startTod, endTod]
|
|
184
219
|
}
|
|
185
220
|
case 'between_tod_only': {
|
|
186
221
|
// between_tod_only has children[0] = between_tod node
|
|
187
222
|
const betweenTod = tp.children[0]
|
|
223
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
188
224
|
let startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
189
225
|
let endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
190
226
|
|
|
@@ -200,13 +236,14 @@ class RuleParser {
|
|
|
200
236
|
case 'between_time_only': {
|
|
201
237
|
// between_time_only has children[0] = between_number_time node
|
|
202
238
|
const betweenNumberTime = tp.children[0]
|
|
239
|
+
// between_number_time has: children[0] = first time_inner, children[1] = separator, children[2] = second time_inner, children[3] = optional dow_range
|
|
203
240
|
const startValue = RuleParser.__parseValue(betweenNumberTime.children[0])
|
|
204
|
-
const endValue = RuleParser.__parseValue(betweenNumberTime.children[
|
|
241
|
+
const endValue = RuleParser.__parseValue(betweenNumberTime.children[2])
|
|
205
242
|
|
|
206
|
-
// Check if there's a dow_range at betweenNumberTime.children[
|
|
243
|
+
// Check if there's a dow_range at betweenNumberTime.children[3]
|
|
207
244
|
// If DOW filters are provided, append them as additional parameters
|
|
208
|
-
if (betweenNumberTime.children.length >
|
|
209
|
-
const dow = RuleParser._parseDowRange(betweenNumberTime.children[
|
|
245
|
+
if (betweenNumberTime.children.length > 3 && betweenNumberTime.children[3].type === 'dow_range') {
|
|
246
|
+
const dow = RuleParser._parseDowRange(betweenNumberTime.children[3])
|
|
210
247
|
if (dow.start === dow.end) {
|
|
211
248
|
// Single day: ["TimePeriodBetween", start, end, "MONDAY"]
|
|
212
249
|
return ["TimePeriodBetween", startValue, endValue, dow.start]
|
|
@@ -223,6 +260,25 @@ class RuleParser {
|
|
|
223
260
|
static __parseValue(child){
|
|
224
261
|
const type = child.type
|
|
225
262
|
switch(type){
|
|
263
|
+
case 'value': {
|
|
264
|
+
// Arrays have value nodes as children - unwrap to get value_atom
|
|
265
|
+
return RuleParser.__parseValue(child.children[0])
|
|
266
|
+
}
|
|
267
|
+
case 'value_atom': {
|
|
268
|
+
// New layer: unwrap value_atom to get the actual atomic type
|
|
269
|
+
return RuleParser.__parseValue(child.children[0])
|
|
270
|
+
}
|
|
271
|
+
case 'number_atom':
|
|
272
|
+
case 'number_time_atom':
|
|
273
|
+
case 'tod_atom':
|
|
274
|
+
case 'dow_atom':
|
|
275
|
+
case 'between_tod_inner':
|
|
276
|
+
case 'between_number_inner':
|
|
277
|
+
case 'between_number_time_inner':
|
|
278
|
+
case 'dow_range_inner': {
|
|
279
|
+
// New layer: unwrap atom wrappers to get the actual leaf nodes
|
|
280
|
+
return RuleParser.__parseValue(child.children[0])
|
|
281
|
+
}
|
|
226
282
|
case 'string': {
|
|
227
283
|
const str = child.text
|
|
228
284
|
return str.slice(1, -1)
|
|
@@ -274,10 +330,13 @@ class RuleParser {
|
|
|
274
330
|
case 'array': {
|
|
275
331
|
const ret = []
|
|
276
332
|
for(const c of child.children){
|
|
277
|
-
ret.push(RuleParser.__parseValue(c
|
|
333
|
+
ret.push(RuleParser.__parseValue(c))
|
|
278
334
|
}
|
|
279
335
|
return ret;
|
|
280
336
|
}
|
|
337
|
+
case 'dow': {
|
|
338
|
+
return normalizeDow(child.text)
|
|
339
|
+
}
|
|
281
340
|
default:
|
|
282
341
|
throw new Error(`Unknown value type ${type}`)
|
|
283
342
|
}
|
|
@@ -287,7 +346,17 @@ class RuleParser {
|
|
|
287
346
|
|
|
288
347
|
const type = child.type
|
|
289
348
|
switch(type){
|
|
349
|
+
case 'value_atom': {
|
|
350
|
+
// New layer: unwrap value_atom to get the actual atomic type
|
|
351
|
+
const atomChild = child.children[0]
|
|
352
|
+
if (atomChild.type === 'time_period') {
|
|
353
|
+
const tp = atomChild.children[0]
|
|
354
|
+
return RuleParser._parseTimePeriod(tp)
|
|
355
|
+
}
|
|
356
|
+
return ['Value', RuleParser.__parseValue(atomChild)]
|
|
357
|
+
}
|
|
290
358
|
case 'time_period': {
|
|
359
|
+
// Old structure (shouldn't happen with new EBNF)
|
|
291
360
|
const tp = child.children[0]
|
|
292
361
|
return RuleParser._parseTimePeriod(tp)
|
|
293
362
|
}
|
|
@@ -314,6 +383,11 @@ class RuleParser {
|
|
|
314
383
|
switch(type){
|
|
315
384
|
case 'fcall':
|
|
316
385
|
return RuleParser._parseFcall(child)
|
|
386
|
+
case 'number_atom':
|
|
387
|
+
case 'number_time_atom': {
|
|
388
|
+
// New layer: unwrap atom wrappers to get the actual leaf nodes
|
|
389
|
+
return ['Value', RuleParser.__parseValue(child.children[0])]
|
|
390
|
+
}
|
|
317
391
|
case 'number':
|
|
318
392
|
return ['Value', parseFloat(child.text)]
|
|
319
393
|
case 'number_time':
|
|
@@ -406,7 +480,7 @@ class RuleParser {
|
|
|
406
480
|
switch(rhs.type){
|
|
407
481
|
case 'between_tod': {
|
|
408
482
|
// Direct between_tod (without wrapping between node)
|
|
409
|
-
// between_tod has: children[0] = first
|
|
483
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
410
484
|
const startTod = RuleParser.__parseValue(rhs.children[0])
|
|
411
485
|
const endTod = RuleParser.__parseValue(rhs.children[1])
|
|
412
486
|
|
|
@@ -421,7 +495,7 @@ class RuleParser {
|
|
|
421
495
|
// between wraps either between_number or between_tod
|
|
422
496
|
const betweenChild = rhs.children[0]
|
|
423
497
|
if (betweenChild.type === 'between_tod') {
|
|
424
|
-
// between_tod has: children[0] = first
|
|
498
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
425
499
|
const startTod = RuleParser.__parseValue(betweenChild.children[0])
|
|
426
500
|
const endTod = RuleParser.__parseValue(betweenChild.children[1])
|
|
427
501
|
|
|
@@ -432,12 +506,13 @@ class RuleParser {
|
|
|
432
506
|
|
|
433
507
|
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', startTod], ['Value', endTod]]
|
|
434
508
|
} else {
|
|
435
|
-
// between_number
|
|
436
|
-
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(betweenChild.children[0])], ['Value', RuleParser.__parseValue(betweenChild.children[
|
|
509
|
+
// between_number has: children[0] = first number_inner, children[1] = separator, children[2] = second number_inner
|
|
510
|
+
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(betweenChild.children[0])], ['Value', RuleParser.__parseValue(betweenChild.children[2])]]
|
|
437
511
|
}
|
|
438
512
|
}
|
|
439
513
|
case 'between_number':
|
|
440
|
-
|
|
514
|
+
// between_number has: children[0] = first number_inner, children[1] = separator, children[2] = second number_inner
|
|
515
|
+
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(rhs.children[0].children[0])], ['Value', RuleParser.__parseValue(rhs.children[0].children[2])]]
|
|
441
516
|
case 'basic_rhs':
|
|
442
517
|
return [OperatorFn[rhs.children[0].text], RuleParser._parseResult(expr.children[0]), RuleParser._parseResult(rhs.children[1])]
|
|
443
518
|
case 'eq_approx': {
|
|
@@ -559,8 +634,6 @@ class RuleParser {
|
|
|
559
634
|
// Extract the invalid time from the error message
|
|
560
635
|
const match = e.message.match(/Invalid time of day[,:]?\s*([0-9:]+)/);
|
|
561
636
|
const badTod = match ? match[1] : 'invalid';
|
|
562
|
-
const { ParsingError } = require('ebnf');
|
|
563
|
-
const { RuleParseError } = require('./errors/RuleParseError');
|
|
564
637
|
|
|
565
638
|
// Calculate position (simplified - at end of input)
|
|
566
639
|
const lines = txt.trim().split('\n');
|
|
@@ -585,7 +658,6 @@ class RuleParser {
|
|
|
585
658
|
if (e.message && e.message.includes('Invalid day of week')) {
|
|
586
659
|
const match = e.message.match(/Invalid day of week[,:]?\s*(\w+)/);
|
|
587
660
|
const badDow = match ? match[1] : 'invalid';
|
|
588
|
-
const { RuleParseError } = require('./errors/RuleParseError');
|
|
589
661
|
|
|
590
662
|
const lines = txt.trim().split('\n');
|
|
591
663
|
const position = {
|
|
@@ -611,5 +683,6 @@ class RuleParser {
|
|
|
611
683
|
}
|
|
612
684
|
}
|
|
613
685
|
module.exports = RuleParser
|
|
614
|
-
module.exports.
|
|
615
|
-
module.exports.
|
|
686
|
+
module.exports.ParserRules = ParserRules
|
|
687
|
+
module.exports.ParsingError = ParsingError
|
|
688
|
+
module.exports.RuleParseError = RuleParseError
|