@halleyassist/rule-parser 1.0.19 → 1.0.20
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 +64 -25
- package/package.json +1 -1
- package/src/RuleParser.ebnf.js +44 -22
- package/src/RuleParser.js +73 -26
- package/src/RuleParser.production.ebnf.js +1 -1
- package/src/RuleParser.production.js +73 -26
- 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":"%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","bnf":[["number_time_atom","%time_period_ago_between[2]*","WS+","AGO","WS+","between_tod_only"]]},{"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) {
|
|
@@ -2075,8 +2079,8 @@ class RuleParser {
|
|
|
2075
2079
|
let totalSeconds = 0;
|
|
2076
2080
|
let betweenTodOnly = null;
|
|
2077
2081
|
for (let i = 0; i < tp.children.length; i++) {
|
|
2078
|
-
if (tp.children[i].type === '
|
|
2079
|
-
totalSeconds += RuleParser.__parseValue(tp.children[i]);
|
|
2082
|
+
if (tp.children[i].type === 'number_time_atom') {
|
|
2083
|
+
totalSeconds += RuleParser.__parseValue(tp.children[i].children[0]);
|
|
2080
2084
|
} else if (tp.children[i].type === 'between_tod_only') {
|
|
2081
2085
|
betweenTodOnly = tp.children[i];
|
|
2082
2086
|
}
|
|
@@ -2123,9 +2127,9 @@ class RuleParser {
|
|
|
2123
2127
|
case 'between_time_only': {
|
|
2124
2128
|
const betweenNumberTime = tp.children[0];
|
|
2125
2129
|
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[
|
|
2130
|
+
const endValue = RuleParser.__parseValue(betweenNumberTime.children[2]);
|
|
2131
|
+
if (betweenNumberTime.children.length > 3 && betweenNumberTime.children[3].type === 'dow_range') {
|
|
2132
|
+
const dow = RuleParser._parseDowRange(betweenNumberTime.children[3]);
|
|
2129
2133
|
if (dow.start === dow.end) {
|
|
2130
2134
|
return [
|
|
2131
2135
|
'TimePeriodBetween',
|
|
@@ -2154,6 +2158,22 @@ class RuleParser {
|
|
|
2154
2158
|
static __parseValue(child) {
|
|
2155
2159
|
const type = child.type;
|
|
2156
2160
|
switch (type) {
|
|
2161
|
+
case 'value': {
|
|
2162
|
+
return RuleParser.__parseValue(child.children[0]);
|
|
2163
|
+
}
|
|
2164
|
+
case 'value_atom': {
|
|
2165
|
+
return RuleParser.__parseValue(child.children[0]);
|
|
2166
|
+
}
|
|
2167
|
+
case 'number_atom':
|
|
2168
|
+
case 'number_time_atom':
|
|
2169
|
+
case 'tod_atom':
|
|
2170
|
+
case 'dow_atom':
|
|
2171
|
+
case 'between_tod_inner':
|
|
2172
|
+
case 'between_number_inner':
|
|
2173
|
+
case 'between_number_time_inner':
|
|
2174
|
+
case 'dow_range_inner': {
|
|
2175
|
+
return RuleParser.__parseValue(child.children[0]);
|
|
2176
|
+
}
|
|
2157
2177
|
case 'string': {
|
|
2158
2178
|
const str = child.text;
|
|
2159
2179
|
return str.slice(1, -1);
|
|
@@ -2209,10 +2229,13 @@ class RuleParser {
|
|
|
2209
2229
|
case 'array': {
|
|
2210
2230
|
const ret = [];
|
|
2211
2231
|
for (const c of child.children) {
|
|
2212
|
-
ret.push(RuleParser.__parseValue(c
|
|
2232
|
+
ret.push(RuleParser.__parseValue(c));
|
|
2213
2233
|
}
|
|
2214
2234
|
return ret;
|
|
2215
2235
|
}
|
|
2236
|
+
case 'dow': {
|
|
2237
|
+
return normalizeDow(child.text);
|
|
2238
|
+
}
|
|
2216
2239
|
default:
|
|
2217
2240
|
throw new Error(`Unknown value type ${ type }`);
|
|
2218
2241
|
}
|
|
@@ -2221,6 +2244,17 @@ class RuleParser {
|
|
|
2221
2244
|
const child = value.children[0];
|
|
2222
2245
|
const type = child.type;
|
|
2223
2246
|
switch (type) {
|
|
2247
|
+
case 'value_atom': {
|
|
2248
|
+
const atomChild = child.children[0];
|
|
2249
|
+
if (atomChild.type === 'time_period') {
|
|
2250
|
+
const tp = atomChild.children[0];
|
|
2251
|
+
return RuleParser._parseTimePeriod(tp);
|
|
2252
|
+
}
|
|
2253
|
+
return [
|
|
2254
|
+
'Value',
|
|
2255
|
+
RuleParser.__parseValue(atomChild)
|
|
2256
|
+
];
|
|
2257
|
+
}
|
|
2224
2258
|
case 'time_period': {
|
|
2225
2259
|
const tp = child.children[0];
|
|
2226
2260
|
return RuleParser._parseTimePeriod(tp);
|
|
@@ -2249,6 +2283,13 @@ class RuleParser {
|
|
|
2249
2283
|
switch (type) {
|
|
2250
2284
|
case 'fcall':
|
|
2251
2285
|
return RuleParser._parseFcall(child);
|
|
2286
|
+
case 'number_atom':
|
|
2287
|
+
case 'number_time_atom': {
|
|
2288
|
+
return [
|
|
2289
|
+
'Value',
|
|
2290
|
+
RuleParser.__parseValue(child.children[0])
|
|
2291
|
+
];
|
|
2292
|
+
}
|
|
2252
2293
|
case 'number':
|
|
2253
2294
|
return [
|
|
2254
2295
|
'Value',
|
|
@@ -2384,7 +2425,7 @@ class RuleParser {
|
|
|
2384
2425
|
],
|
|
2385
2426
|
[
|
|
2386
2427
|
'Value',
|
|
2387
|
-
RuleParser.__parseValue(betweenChild.children[
|
|
2428
|
+
RuleParser.__parseValue(betweenChild.children[2])
|
|
2388
2429
|
]
|
|
2389
2430
|
];
|
|
2390
2431
|
}
|
|
@@ -2399,7 +2440,7 @@ class RuleParser {
|
|
|
2399
2440
|
],
|
|
2400
2441
|
[
|
|
2401
2442
|
'Value',
|
|
2402
|
-
RuleParser.__parseValue(rhs.children[0].children[
|
|
2443
|
+
RuleParser.__parseValue(rhs.children[0].children[2])
|
|
2403
2444
|
]
|
|
2404
2445
|
];
|
|
2405
2446
|
case 'basic_rhs':
|
|
@@ -2538,8 +2579,6 @@ class RuleParser {
|
|
|
2538
2579
|
if (e.message && e.message.includes('Invalid time of day')) {
|
|
2539
2580
|
const match = e.message.match(/Invalid time of day[,:]?\s*([0-9:]+)/);
|
|
2540
2581
|
const badTod = match ? match[1] : 'invalid';
|
|
2541
|
-
const {ParsingError} = require('ebnf');
|
|
2542
|
-
const {RuleParseError} = require('./errors/RuleParseError');
|
|
2543
2582
|
const lines = txt.trim().split('\n');
|
|
2544
2583
|
const position = {
|
|
2545
2584
|
line: lines.length,
|
|
@@ -2551,7 +2590,6 @@ class RuleParser {
|
|
|
2551
2590
|
if (e.message && e.message.includes('Invalid day of week')) {
|
|
2552
2591
|
const match = e.message.match(/Invalid day of week[,:]?\s*(\w+)/);
|
|
2553
2592
|
const badDow = match ? match[1] : 'invalid';
|
|
2554
|
-
const {RuleParseError} = require('./errors/RuleParseError');
|
|
2555
2593
|
const lines = txt.trim().split('\n');
|
|
2556
2594
|
const position = {
|
|
2557
2595
|
line: lines.length,
|
|
@@ -2573,11 +2611,12 @@ class RuleParser {
|
|
|
2573
2611
|
}
|
|
2574
2612
|
}
|
|
2575
2613
|
module.exports = RuleParser;
|
|
2576
|
-
module.exports.
|
|
2577
|
-
module.exports.
|
|
2614
|
+
module.exports.ParserRules = ParserRules;
|
|
2615
|
+
module.exports.ParsingError = ParsingError;
|
|
2616
|
+
module.exports.RuleParseError = RuleParseError;
|
|
2578
2617
|
},{"./RuleParser.production.ebnf.js":11,"./errors/ErrorAnalyzer":13,"./errors/RuleParseError":14,"ebnf":9,"ebnf/dist/Parser.js":5}],13:[function(require,module,exports){
|
|
2579
2618
|
const { ParsingError } = require('ebnf');
|
|
2580
|
-
const
|
|
2619
|
+
const RuleParseError = require('./RuleParseError');
|
|
2581
2620
|
|
|
2582
2621
|
/**
|
|
2583
2622
|
* Analyzes parsing errors and maps them to user-friendly error codes
|
|
@@ -3565,7 +3604,7 @@ class RuleParseError extends Error {
|
|
|
3565
3604
|
}
|
|
3566
3605
|
}
|
|
3567
3606
|
|
|
3568
|
-
module.exports =
|
|
3607
|
+
module.exports = RuleParseError;
|
|
3569
3608
|
|
|
3570
3609
|
},{}]},{},[10])(10)
|
|
3571
3610
|
});
|
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,44 +18,63 @@ 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
|
|
61
80
|
|
|
@@ -70,18 +89,21 @@ IS ||= "is"
|
|
|
70
89
|
EQ ::= "==" | "="
|
|
71
90
|
NEQ ::= "!="
|
|
72
91
|
NOT ||= ("!" WS*) | ("not" WS+)
|
|
92
|
+
|
|
73
93
|
false ||= "FALSE"
|
|
74
94
|
null ||= "null"
|
|
75
95
|
true ||= "TRUE"
|
|
96
|
+
|
|
76
97
|
array ::= BEGIN_ARRAY (value (VALUE_SEPARATOR value)*)? END_ARRAY
|
|
77
98
|
|
|
78
99
|
unit ||= "seconds" | "minutes" | "hours" | "weeks" | "days" | "second" | "minute" | "week" | "hour" | "day" | "mins" | "min"
|
|
79
|
-
|
|
100
|
+
|
|
101
|
+
number ::= "-"? ([0-9]+) ("." [0-9]+)? ("e" ("-" | "+")? ("0" | [1-9] [0-9]*))?
|
|
80
102
|
number_time ::= number WS+ unit
|
|
81
103
|
number_tod ::= ([0-9]+) ":" ([0-9]+)
|
|
82
104
|
|
|
83
|
-
time_period_ago ||=
|
|
84
|
-
time_period_ago_between ||=
|
|
105
|
+
time_period_ago ||= number_time_atom (WS+ number_time_atom)* WS+ AGO
|
|
106
|
+
time_period_ago_between ||= number_time_atom (WS+ number_time_atom)* WS+ AGO WS+ between_tod_only
|
|
85
107
|
time_period_const ||= "today" | time_period_ago
|
|
86
108
|
time_period ::= time_period_ago_between | time_period_const | between_tod_only | between_time_only
|
|
87
109
|
|
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,15 +158,16 @@ 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_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
164
|
let betweenTodOnly = null
|
|
158
165
|
|
|
159
|
-
// Find all
|
|
166
|
+
// Find all number_time_atom children and the between_tod_only child
|
|
160
167
|
for (let i = 0; i < tp.children.length; i++) {
|
|
161
|
-
if (tp.children[i].type === '
|
|
162
|
-
|
|
168
|
+
if (tp.children[i].type === 'number_time_atom') {
|
|
169
|
+
// New structure: number_time_atom -> number_time
|
|
170
|
+
totalSeconds += RuleParser.__parseValue(tp.children[i].children[0])
|
|
163
171
|
} else if (tp.children[i].type === 'between_tod_only') {
|
|
164
172
|
betweenTodOnly = tp.children[i]
|
|
165
173
|
}
|
|
@@ -171,6 +179,7 @@ class RuleParser {
|
|
|
171
179
|
}
|
|
172
180
|
|
|
173
181
|
const betweenTod = betweenTodOnly.children[0]
|
|
182
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
174
183
|
let startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
175
184
|
let endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
176
185
|
|
|
@@ -185,6 +194,7 @@ class RuleParser {
|
|
|
185
194
|
case 'between_tod_only': {
|
|
186
195
|
// between_tod_only has children[0] = between_tod node
|
|
187
196
|
const betweenTod = tp.children[0]
|
|
197
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
188
198
|
let startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
189
199
|
let endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
190
200
|
|
|
@@ -200,13 +210,14 @@ class RuleParser {
|
|
|
200
210
|
case 'between_time_only': {
|
|
201
211
|
// between_time_only has children[0] = between_number_time node
|
|
202
212
|
const betweenNumberTime = tp.children[0]
|
|
213
|
+
// between_number_time has: children[0] = first time_inner, children[1] = separator, children[2] = second time_inner, children[3] = optional dow_range
|
|
203
214
|
const startValue = RuleParser.__parseValue(betweenNumberTime.children[0])
|
|
204
|
-
const endValue = RuleParser.__parseValue(betweenNumberTime.children[
|
|
215
|
+
const endValue = RuleParser.__parseValue(betweenNumberTime.children[2])
|
|
205
216
|
|
|
206
|
-
// Check if there's a dow_range at betweenNumberTime.children[
|
|
217
|
+
// Check if there's a dow_range at betweenNumberTime.children[3]
|
|
207
218
|
// If DOW filters are provided, append them as additional parameters
|
|
208
|
-
if (betweenNumberTime.children.length >
|
|
209
|
-
const dow = RuleParser._parseDowRange(betweenNumberTime.children[
|
|
219
|
+
if (betweenNumberTime.children.length > 3 && betweenNumberTime.children[3].type === 'dow_range') {
|
|
220
|
+
const dow = RuleParser._parseDowRange(betweenNumberTime.children[3])
|
|
210
221
|
if (dow.start === dow.end) {
|
|
211
222
|
// Single day: ["TimePeriodBetween", start, end, "MONDAY"]
|
|
212
223
|
return ["TimePeriodBetween", startValue, endValue, dow.start]
|
|
@@ -223,6 +234,25 @@ class RuleParser {
|
|
|
223
234
|
static __parseValue(child){
|
|
224
235
|
const type = child.type
|
|
225
236
|
switch(type){
|
|
237
|
+
case 'value': {
|
|
238
|
+
// Arrays have value nodes as children - unwrap to get value_atom
|
|
239
|
+
return RuleParser.__parseValue(child.children[0])
|
|
240
|
+
}
|
|
241
|
+
case 'value_atom': {
|
|
242
|
+
// New layer: unwrap value_atom to get the actual atomic type
|
|
243
|
+
return RuleParser.__parseValue(child.children[0])
|
|
244
|
+
}
|
|
245
|
+
case 'number_atom':
|
|
246
|
+
case 'number_time_atom':
|
|
247
|
+
case 'tod_atom':
|
|
248
|
+
case 'dow_atom':
|
|
249
|
+
case 'between_tod_inner':
|
|
250
|
+
case 'between_number_inner':
|
|
251
|
+
case 'between_number_time_inner':
|
|
252
|
+
case 'dow_range_inner': {
|
|
253
|
+
// New layer: unwrap atom wrappers to get the actual leaf nodes
|
|
254
|
+
return RuleParser.__parseValue(child.children[0])
|
|
255
|
+
}
|
|
226
256
|
case 'string': {
|
|
227
257
|
const str = child.text
|
|
228
258
|
return str.slice(1, -1)
|
|
@@ -274,10 +304,13 @@ class RuleParser {
|
|
|
274
304
|
case 'array': {
|
|
275
305
|
const ret = []
|
|
276
306
|
for(const c of child.children){
|
|
277
|
-
ret.push(RuleParser.__parseValue(c
|
|
307
|
+
ret.push(RuleParser.__parseValue(c))
|
|
278
308
|
}
|
|
279
309
|
return ret;
|
|
280
310
|
}
|
|
311
|
+
case 'dow': {
|
|
312
|
+
return normalizeDow(child.text)
|
|
313
|
+
}
|
|
281
314
|
default:
|
|
282
315
|
throw new Error(`Unknown value type ${type}`)
|
|
283
316
|
}
|
|
@@ -287,7 +320,17 @@ class RuleParser {
|
|
|
287
320
|
|
|
288
321
|
const type = child.type
|
|
289
322
|
switch(type){
|
|
323
|
+
case 'value_atom': {
|
|
324
|
+
// New layer: unwrap value_atom to get the actual atomic type
|
|
325
|
+
const atomChild = child.children[0]
|
|
326
|
+
if (atomChild.type === 'time_period') {
|
|
327
|
+
const tp = atomChild.children[0]
|
|
328
|
+
return RuleParser._parseTimePeriod(tp)
|
|
329
|
+
}
|
|
330
|
+
return ['Value', RuleParser.__parseValue(atomChild)]
|
|
331
|
+
}
|
|
290
332
|
case 'time_period': {
|
|
333
|
+
// Old structure (shouldn't happen with new EBNF)
|
|
291
334
|
const tp = child.children[0]
|
|
292
335
|
return RuleParser._parseTimePeriod(tp)
|
|
293
336
|
}
|
|
@@ -314,6 +357,11 @@ class RuleParser {
|
|
|
314
357
|
switch(type){
|
|
315
358
|
case 'fcall':
|
|
316
359
|
return RuleParser._parseFcall(child)
|
|
360
|
+
case 'number_atom':
|
|
361
|
+
case 'number_time_atom': {
|
|
362
|
+
// New layer: unwrap atom wrappers to get the actual leaf nodes
|
|
363
|
+
return ['Value', RuleParser.__parseValue(child.children[0])]
|
|
364
|
+
}
|
|
317
365
|
case 'number':
|
|
318
366
|
return ['Value', parseFloat(child.text)]
|
|
319
367
|
case 'number_time':
|
|
@@ -406,7 +454,7 @@ class RuleParser {
|
|
|
406
454
|
switch(rhs.type){
|
|
407
455
|
case 'between_tod': {
|
|
408
456
|
// Direct between_tod (without wrapping between node)
|
|
409
|
-
// between_tod has: children[0] = first
|
|
457
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
410
458
|
const startTod = RuleParser.__parseValue(rhs.children[0])
|
|
411
459
|
const endTod = RuleParser.__parseValue(rhs.children[1])
|
|
412
460
|
|
|
@@ -421,7 +469,7 @@ class RuleParser {
|
|
|
421
469
|
// between wraps either between_number or between_tod
|
|
422
470
|
const betweenChild = rhs.children[0]
|
|
423
471
|
if (betweenChild.type === 'between_tod') {
|
|
424
|
-
// between_tod has: children[0] = first
|
|
472
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
425
473
|
const startTod = RuleParser.__parseValue(betweenChild.children[0])
|
|
426
474
|
const endTod = RuleParser.__parseValue(betweenChild.children[1])
|
|
427
475
|
|
|
@@ -432,12 +480,13 @@ class RuleParser {
|
|
|
432
480
|
|
|
433
481
|
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', startTod], ['Value', endTod]]
|
|
434
482
|
} else {
|
|
435
|
-
// between_number
|
|
436
|
-
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(betweenChild.children[0])], ['Value', RuleParser.__parseValue(betweenChild.children[
|
|
483
|
+
// between_number has: children[0] = first number_inner, children[1] = separator, children[2] = second number_inner
|
|
484
|
+
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(betweenChild.children[0])], ['Value', RuleParser.__parseValue(betweenChild.children[2])]]
|
|
437
485
|
}
|
|
438
486
|
}
|
|
439
487
|
case 'between_number':
|
|
440
|
-
|
|
488
|
+
// between_number has: children[0] = first number_inner, children[1] = separator, children[2] = second number_inner
|
|
489
|
+
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(rhs.children[0].children[0])], ['Value', RuleParser.__parseValue(rhs.children[0].children[2])]]
|
|
441
490
|
case 'basic_rhs':
|
|
442
491
|
return [OperatorFn[rhs.children[0].text], RuleParser._parseResult(expr.children[0]), RuleParser._parseResult(rhs.children[1])]
|
|
443
492
|
case 'eq_approx': {
|
|
@@ -559,8 +608,6 @@ class RuleParser {
|
|
|
559
608
|
// Extract the invalid time from the error message
|
|
560
609
|
const match = e.message.match(/Invalid time of day[,:]?\s*([0-9:]+)/);
|
|
561
610
|
const badTod = match ? match[1] : 'invalid';
|
|
562
|
-
const { ParsingError } = require('ebnf');
|
|
563
|
-
const { RuleParseError } = require('./errors/RuleParseError');
|
|
564
611
|
|
|
565
612
|
// Calculate position (simplified - at end of input)
|
|
566
613
|
const lines = txt.trim().split('\n');
|
|
@@ -585,7 +632,6 @@ class RuleParser {
|
|
|
585
632
|
if (e.message && e.message.includes('Invalid day of week')) {
|
|
586
633
|
const match = e.message.match(/Invalid day of week[,:]?\s*(\w+)/);
|
|
587
634
|
const badDow = match ? match[1] : 'invalid';
|
|
588
|
-
const { RuleParseError } = require('./errors/RuleParseError');
|
|
589
635
|
|
|
590
636
|
const lines = txt.trim().split('\n');
|
|
591
637
|
const position = {
|
|
@@ -611,5 +657,6 @@ class RuleParser {
|
|
|
611
657
|
}
|
|
612
658
|
}
|
|
613
659
|
module.exports = RuleParser
|
|
614
|
-
module.exports.
|
|
615
|
-
module.exports.
|
|
660
|
+
module.exports.ParserRules = ParserRules
|
|
661
|
+
module.exports.ParsingError = ParsingError
|
|
662
|
+
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":"%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","bnf":[["number_time_atom","%time_period_ago_between[2]*","WS+","AGO","WS+","between_tod_only"]]},{"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,15 +158,16 @@ 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_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
164
|
let betweenTodOnly = null
|
|
158
165
|
|
|
159
|
-
// Find all
|
|
166
|
+
// Find all number_time_atom children and the between_tod_only child
|
|
160
167
|
for (let i = 0; i < tp.children.length; i++) {
|
|
161
|
-
if (tp.children[i].type === '
|
|
162
|
-
|
|
168
|
+
if (tp.children[i].type === 'number_time_atom') {
|
|
169
|
+
// New structure: number_time_atom -> number_time
|
|
170
|
+
totalSeconds += RuleParser.__parseValue(tp.children[i].children[0])
|
|
163
171
|
} else if (tp.children[i].type === 'between_tod_only') {
|
|
164
172
|
betweenTodOnly = tp.children[i]
|
|
165
173
|
}
|
|
@@ -171,6 +179,7 @@ class RuleParser {
|
|
|
171
179
|
}
|
|
172
180
|
|
|
173
181
|
const betweenTod = betweenTodOnly.children[0]
|
|
182
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
174
183
|
let startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
175
184
|
let endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
176
185
|
|
|
@@ -185,6 +194,7 @@ class RuleParser {
|
|
|
185
194
|
case 'between_tod_only': {
|
|
186
195
|
// between_tod_only has children[0] = between_tod node
|
|
187
196
|
const betweenTod = tp.children[0]
|
|
197
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
188
198
|
let startTod = RuleParser.__parseValue(betweenTod.children[0])
|
|
189
199
|
let endTod = RuleParser.__parseValue(betweenTod.children[1])
|
|
190
200
|
|
|
@@ -200,13 +210,14 @@ class RuleParser {
|
|
|
200
210
|
case 'between_time_only': {
|
|
201
211
|
// between_time_only has children[0] = between_number_time node
|
|
202
212
|
const betweenNumberTime = tp.children[0]
|
|
213
|
+
// between_number_time has: children[0] = first time_inner, children[1] = separator, children[2] = second time_inner, children[3] = optional dow_range
|
|
203
214
|
const startValue = RuleParser.__parseValue(betweenNumberTime.children[0])
|
|
204
|
-
const endValue = RuleParser.__parseValue(betweenNumberTime.children[
|
|
215
|
+
const endValue = RuleParser.__parseValue(betweenNumberTime.children[2])
|
|
205
216
|
|
|
206
|
-
// Check if there's a dow_range at betweenNumberTime.children[
|
|
217
|
+
// Check if there's a dow_range at betweenNumberTime.children[3]
|
|
207
218
|
// If DOW filters are provided, append them as additional parameters
|
|
208
|
-
if (betweenNumberTime.children.length >
|
|
209
|
-
const dow = RuleParser._parseDowRange(betweenNumberTime.children[
|
|
219
|
+
if (betweenNumberTime.children.length > 3 && betweenNumberTime.children[3].type === 'dow_range') {
|
|
220
|
+
const dow = RuleParser._parseDowRange(betweenNumberTime.children[3])
|
|
210
221
|
if (dow.start === dow.end) {
|
|
211
222
|
// Single day: ["TimePeriodBetween", start, end, "MONDAY"]
|
|
212
223
|
return ["TimePeriodBetween", startValue, endValue, dow.start]
|
|
@@ -223,6 +234,25 @@ class RuleParser {
|
|
|
223
234
|
static __parseValue(child){
|
|
224
235
|
const type = child.type
|
|
225
236
|
switch(type){
|
|
237
|
+
case 'value': {
|
|
238
|
+
// Arrays have value nodes as children - unwrap to get value_atom
|
|
239
|
+
return RuleParser.__parseValue(child.children[0])
|
|
240
|
+
}
|
|
241
|
+
case 'value_atom': {
|
|
242
|
+
// New layer: unwrap value_atom to get the actual atomic type
|
|
243
|
+
return RuleParser.__parseValue(child.children[0])
|
|
244
|
+
}
|
|
245
|
+
case 'number_atom':
|
|
246
|
+
case 'number_time_atom':
|
|
247
|
+
case 'tod_atom':
|
|
248
|
+
case 'dow_atom':
|
|
249
|
+
case 'between_tod_inner':
|
|
250
|
+
case 'between_number_inner':
|
|
251
|
+
case 'between_number_time_inner':
|
|
252
|
+
case 'dow_range_inner': {
|
|
253
|
+
// New layer: unwrap atom wrappers to get the actual leaf nodes
|
|
254
|
+
return RuleParser.__parseValue(child.children[0])
|
|
255
|
+
}
|
|
226
256
|
case 'string': {
|
|
227
257
|
const str = child.text
|
|
228
258
|
return str.slice(1, -1)
|
|
@@ -274,10 +304,13 @@ class RuleParser {
|
|
|
274
304
|
case 'array': {
|
|
275
305
|
const ret = []
|
|
276
306
|
for(const c of child.children){
|
|
277
|
-
ret.push(RuleParser.__parseValue(c
|
|
307
|
+
ret.push(RuleParser.__parseValue(c))
|
|
278
308
|
}
|
|
279
309
|
return ret;
|
|
280
310
|
}
|
|
311
|
+
case 'dow': {
|
|
312
|
+
return normalizeDow(child.text)
|
|
313
|
+
}
|
|
281
314
|
default:
|
|
282
315
|
throw new Error(`Unknown value type ${type}`)
|
|
283
316
|
}
|
|
@@ -287,7 +320,17 @@ class RuleParser {
|
|
|
287
320
|
|
|
288
321
|
const type = child.type
|
|
289
322
|
switch(type){
|
|
323
|
+
case 'value_atom': {
|
|
324
|
+
// New layer: unwrap value_atom to get the actual atomic type
|
|
325
|
+
const atomChild = child.children[0]
|
|
326
|
+
if (atomChild.type === 'time_period') {
|
|
327
|
+
const tp = atomChild.children[0]
|
|
328
|
+
return RuleParser._parseTimePeriod(tp)
|
|
329
|
+
}
|
|
330
|
+
return ['Value', RuleParser.__parseValue(atomChild)]
|
|
331
|
+
}
|
|
290
332
|
case 'time_period': {
|
|
333
|
+
// Old structure (shouldn't happen with new EBNF)
|
|
291
334
|
const tp = child.children[0]
|
|
292
335
|
return RuleParser._parseTimePeriod(tp)
|
|
293
336
|
}
|
|
@@ -314,6 +357,11 @@ class RuleParser {
|
|
|
314
357
|
switch(type){
|
|
315
358
|
case 'fcall':
|
|
316
359
|
return RuleParser._parseFcall(child)
|
|
360
|
+
case 'number_atom':
|
|
361
|
+
case 'number_time_atom': {
|
|
362
|
+
// New layer: unwrap atom wrappers to get the actual leaf nodes
|
|
363
|
+
return ['Value', RuleParser.__parseValue(child.children[0])]
|
|
364
|
+
}
|
|
317
365
|
case 'number':
|
|
318
366
|
return ['Value', parseFloat(child.text)]
|
|
319
367
|
case 'number_time':
|
|
@@ -406,7 +454,7 @@ class RuleParser {
|
|
|
406
454
|
switch(rhs.type){
|
|
407
455
|
case 'between_tod': {
|
|
408
456
|
// Direct between_tod (without wrapping between node)
|
|
409
|
-
// between_tod has: children[0] = first
|
|
457
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
410
458
|
const startTod = RuleParser.__parseValue(rhs.children[0])
|
|
411
459
|
const endTod = RuleParser.__parseValue(rhs.children[1])
|
|
412
460
|
|
|
@@ -421,7 +469,7 @@ class RuleParser {
|
|
|
421
469
|
// between wraps either between_number or between_tod
|
|
422
470
|
const betweenChild = rhs.children[0]
|
|
423
471
|
if (betweenChild.type === 'between_tod') {
|
|
424
|
-
// between_tod has: children[0] = first
|
|
472
|
+
// between_tod has inline separator, so: children[0] = first tod_inner, children[1] = second tod_inner, children[2] = optional dow_range
|
|
425
473
|
const startTod = RuleParser.__parseValue(betweenChild.children[0])
|
|
426
474
|
const endTod = RuleParser.__parseValue(betweenChild.children[1])
|
|
427
475
|
|
|
@@ -432,12 +480,13 @@ class RuleParser {
|
|
|
432
480
|
|
|
433
481
|
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', startTod], ['Value', endTod]]
|
|
434
482
|
} else {
|
|
435
|
-
// between_number
|
|
436
|
-
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(betweenChild.children[0])], ['Value', RuleParser.__parseValue(betweenChild.children[
|
|
483
|
+
// between_number has: children[0] = first number_inner, children[1] = separator, children[2] = second number_inner
|
|
484
|
+
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(betweenChild.children[0])], ['Value', RuleParser.__parseValue(betweenChild.children[2])]]
|
|
437
485
|
}
|
|
438
486
|
}
|
|
439
487
|
case 'between_number':
|
|
440
|
-
|
|
488
|
+
// between_number has: children[0] = first number_inner, children[1] = separator, children[2] = second number_inner
|
|
489
|
+
return ['Between', RuleParser._parseResult(expr.children[0]), ['Value', RuleParser.__parseValue(rhs.children[0].children[0])], ['Value', RuleParser.__parseValue(rhs.children[0].children[2])]]
|
|
441
490
|
case 'basic_rhs':
|
|
442
491
|
return [OperatorFn[rhs.children[0].text], RuleParser._parseResult(expr.children[0]), RuleParser._parseResult(rhs.children[1])]
|
|
443
492
|
case 'eq_approx': {
|
|
@@ -559,8 +608,6 @@ class RuleParser {
|
|
|
559
608
|
// Extract the invalid time from the error message
|
|
560
609
|
const match = e.message.match(/Invalid time of day[,:]?\s*([0-9:]+)/);
|
|
561
610
|
const badTod = match ? match[1] : 'invalid';
|
|
562
|
-
const { ParsingError } = require('ebnf');
|
|
563
|
-
const { RuleParseError } = require('./errors/RuleParseError');
|
|
564
611
|
|
|
565
612
|
// Calculate position (simplified - at end of input)
|
|
566
613
|
const lines = txt.trim().split('\n');
|
|
@@ -585,7 +632,6 @@ class RuleParser {
|
|
|
585
632
|
if (e.message && e.message.includes('Invalid day of week')) {
|
|
586
633
|
const match = e.message.match(/Invalid day of week[,:]?\s*(\w+)/);
|
|
587
634
|
const badDow = match ? match[1] : 'invalid';
|
|
588
|
-
const { RuleParseError } = require('./errors/RuleParseError');
|
|
589
635
|
|
|
590
636
|
const lines = txt.trim().split('\n');
|
|
591
637
|
const position = {
|
|
@@ -611,5 +657,6 @@ class RuleParser {
|
|
|
611
657
|
}
|
|
612
658
|
}
|
|
613
659
|
module.exports = RuleParser
|
|
614
|
-
module.exports.
|
|
615
|
-
module.exports.
|
|
660
|
+
module.exports.ParserRules = ParserRules
|
|
661
|
+
module.exports.ParsingError = ParsingError
|
|
662
|
+
module.exports.RuleParseError = RuleParseError
|