@halleyassist/rule-templater 0.0.18 → 0.0.19

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.
@@ -1,5 +1,5 @@
1
1
  (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.RuleTemplate = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
2
- 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]/]]}]
2
+ 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"],["parenthesis_expression"],["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":"time_period_atom","bnf":[["between_tod_only"],["between_time_only"]]},{"name":"time_period_ago_atom","bnf":[["time_period_ago_between"]]},{"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_atom"],["time_period_const"],["time_period_atom"]]},{"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]/]]}]
3
3
  },{}],2:[function(require,module,exports){
4
4
  const {Parser} = require('ebnf/dist/Parser.js'), {ParsingError} = require('ebnf'), RuleParseError = require('./errors/RuleParseError');
5
5
  let ParserRules = require('./RuleParser.production.ebnf.js');
@@ -28,6 +28,31 @@ const LogicalOperators = {
28
28
  '||': 'Or',
29
29
  'OR': 'Or'
30
30
  };
31
+ const ILReservedNames = new Set([
32
+ 'Value',
33
+ 'Array',
34
+ 'ArrayIn',
35
+ 'Between',
36
+ 'Not',
37
+ 'And',
38
+ 'Or',
39
+ 'Gt',
40
+ 'Lt',
41
+ 'Gte',
42
+ 'Lte',
43
+ 'Eq',
44
+ 'Neq',
45
+ 'MathAdd',
46
+ 'MathSub',
47
+ 'MathDiv',
48
+ 'MathMul',
49
+ 'MathMod',
50
+ 'Default',
51
+ 'TimePeriodConst',
52
+ 'TimePeriodConstAgo',
53
+ 'TimePeriodBetween',
54
+ 'TimePeriodBetweenAgo'
55
+ ]);
31
56
  const DOW_MAP = {
32
57
  'MON': 'MONDAY',
33
58
  'TUE': 'TUESDAY',
@@ -122,6 +147,9 @@ class RuleParser {
122
147
  }
123
148
  static _parseTimePeriod(tp) {
124
149
  switch (tp.type) {
150
+ case 'time_period_atom':
151
+ case 'time_period_ago_atom':
152
+ return RuleParser._parseTimePeriod(tp.children[0]);
125
153
  case 'time_period_const':
126
154
  if (tp.children && tp.children.length > 0 && tp.children[0].type === 'time_period_ago') {
127
155
  const timePeriodAgo = tp.children[0];
@@ -366,7 +394,7 @@ class RuleParser {
366
394
  switch (type) {
367
395
  case 'value_atom': {
368
396
  const atomChild = child.children[0];
369
- if (atomChild.type === 'time_period') {
397
+ if (atomChild.type === 'time_period' || atomChild.type === 'time_period_atom' || atomChild.type === 'time_period_ago_atom') {
370
398
  const tp = atomChild.children[0];
371
399
  return RuleParser._parseTimePeriod(tp);
372
400
  }
@@ -379,6 +407,9 @@ class RuleParser {
379
407
  const tp = child.children[0];
380
408
  return RuleParser._parseTimePeriod(tp);
381
409
  }
410
+ case 'time_period_atom':
411
+ case 'time_period_ago_atom':
412
+ return RuleParser._parseTimePeriod(child);
382
413
  default:
383
414
  return [
384
415
  'Value',
@@ -403,6 +434,8 @@ class RuleParser {
403
434
  switch (type) {
404
435
  case 'fcall':
405
436
  return RuleParser._parseFcall(child);
437
+ case 'parenthesis_expression':
438
+ return RuleParser._parseParenthesisExpression(child);
406
439
  case 'number_atom':
407
440
  case 'number_time_atom': {
408
441
  return [
@@ -686,6 +719,29 @@ class RuleParser {
686
719
  throw new Error(`unknown type of expression ${ eInner.type }`);
687
720
  }
688
721
  }
722
+ static _collectFunctions(il, names) {
723
+ if (!Array.isArray(il) || il.length === 0) {
724
+ return;
725
+ }
726
+ const [head, ...tail] = il;
727
+ if (typeof head === 'string') {
728
+ if (!ILReservedNames.has(head)) {
729
+ names.add(head);
730
+ }
731
+ for (const child of tail) {
732
+ RuleParser._collectFunctions(child, names);
733
+ }
734
+ return;
735
+ }
736
+ for (const child of il) {
737
+ RuleParser._collectFunctions(child, names);
738
+ }
739
+ }
740
+ static getFunctions(il) {
741
+ const names = new Set();
742
+ RuleParser._collectFunctions(il, names);
743
+ return [...names];
744
+ }
689
745
  static toIL(txt) {
690
746
  try {
691
747
  const ast = RuleParser.toAst(txt);
@@ -781,10 +837,14 @@ class ErrorAnalyzer {
781
837
 
782
838
  // Get snippet (last 50 chars or the whole input if shorter)
783
839
  const snippetStart = Math.max(0, trimmedInput.length - 50);
784
- const snippet = (snippetStart > 0 ? '...' : '') + trimmedInput.substring(snippetStart);
840
+ const snippetEnd = trimmedInput.length;
841
+ let snippet = this._buildSnippet(trimmedInput, snippetStart, snippetEnd);
785
842
 
786
843
  // Analyze the error pattern
787
844
  const errorInfo = this._detectErrorPattern(trimmedInput, position, snippet);
845
+ this._enrichBadFunctionCallError(trimmedInput, position, errorInfo, (value) => {
846
+ snippet = value;
847
+ }, snippetStart, snippetEnd);
788
848
 
789
849
  return new RuleParseError(
790
850
  errorInfo.code,
@@ -810,12 +870,13 @@ class ErrorAnalyzer {
810
870
  // Get snippet around error position
811
871
  const snippetStart = Math.max(0, position.offset - 20);
812
872
  const snippetEnd = Math.min(input.length, position.offset + 30);
813
- const snippet = (snippetStart > 0 ? '...' : '') +
814
- input.substring(snippetStart, snippetEnd) +
815
- (snippetEnd < input.length ? '...' : '');
873
+ let snippet = this._buildSnippet(input, snippetStart, snippetEnd);
816
874
 
817
875
  // Analyze what was expected to determine error type using failureTree
818
876
  const errorInfo = this._detectErrorFromFailureTree(input, position, expected, found, failureTree);
877
+ this._enrichBadFunctionCallError(input, position, errorInfo, (value) => {
878
+ snippet = value;
879
+ }, snippetStart, snippetEnd);
819
880
 
820
881
  return new RuleParseError(
821
882
  errorInfo.code,
@@ -1329,6 +1390,109 @@ class ErrorAnalyzer {
1329
1390
  };
1330
1391
  }
1331
1392
 
1393
+ static _enrichBadFunctionCallError(input, position, errorInfo, setSnippet, snippetStart, snippetEnd) {
1394
+ if (!errorInfo || errorInfo.code !== "BAD_FUNCTION_CALL") {
1395
+ return;
1396
+ }
1397
+
1398
+ const functionContext = this._findFunctionContext(input, position.offset);
1399
+ errorInfo.hint = this._getBadFunctionCallHint(input, position, functionContext);
1400
+
1401
+ if (!functionContext) {
1402
+ return;
1403
+ }
1404
+
1405
+ errorInfo.message = `Invalid function call syntax for ${functionContext.name}.`;
1406
+ setSnippet(this._buildBadFunctionCallSnippet(input, functionContext, snippetStart, snippetEnd));
1407
+ }
1408
+
1409
+ static _buildSnippet(input, start, end) {
1410
+ return (start > 0 ? '...' : '') +
1411
+ input.substring(start, end) +
1412
+ (end < input.length ? '...' : '');
1413
+ }
1414
+
1415
+ static _getBadFunctionCallHint(input, position, functionContext) {
1416
+ const genericHint = "Function calls must have matching parentheses, e.g. func() or func(arg1, arg2).";
1417
+
1418
+ if (!functionContext || !this._isArgumentRelatedFunctionCallError(input, position, functionContext)) {
1419
+ return genericHint;
1420
+ }
1421
+
1422
+ return "Function calls must have matching parentheses, e.g. func() or func(arg1, arg2). Please check function arguments for invalid syntax.";
1423
+ }
1424
+
1425
+ static _isArgumentRelatedFunctionCallError(input, position, functionContext) {
1426
+ const endOffset = Math.max(functionContext.openParenIndex + 1, Math.min(position.offset, input.length));
1427
+ const argumentText = input.substring(functionContext.openParenIndex + 1, endOffset).trim();
1428
+ return argumentText.length > 0;
1429
+ }
1430
+
1431
+ static _buildBadFunctionCallSnippet(input, functionContext, snippetStart, snippetEnd) {
1432
+ if (snippetStart <= functionContext.start) {
1433
+ return this._buildSnippet(input, snippetStart, snippetEnd);
1434
+ }
1435
+
1436
+ const functionPrefix = input.substring(functionContext.start, functionContext.openParenIndex + 1);
1437
+ const suffix = input.substring(snippetStart, snippetEnd);
1438
+ return functionPrefix + '...' + suffix + (snippetEnd < input.length ? '...' : '');
1439
+ }
1440
+
1441
+ static _findFunctionContext(input, offset) {
1442
+ const scanLimit = Math.max(0, Math.min(typeof offset === 'number' ? offset : input.length, input.length));
1443
+ const stack = [];
1444
+ let inString = false;
1445
+
1446
+ for (let i = 0; i < scanLimit; i++) {
1447
+ const char = input[i];
1448
+
1449
+ if (char === '"') {
1450
+ if (!inString) {
1451
+ inString = true;
1452
+ } else {
1453
+ let backslashCount = 0;
1454
+ let j = i - 1;
1455
+ while (j >= 0 && input[j] === '\\') {
1456
+ backslashCount++;
1457
+ j--;
1458
+ }
1459
+ if (backslashCount % 2 === 0) {
1460
+ inString = false;
1461
+ }
1462
+ }
1463
+ continue;
1464
+ }
1465
+
1466
+ if (inString) {
1467
+ continue;
1468
+ }
1469
+
1470
+ if (char === '(') {
1471
+ const prefix = input.substring(0, i);
1472
+ const match = /([a-zA-Z_][a-zA-Z0-9_]*)\s*$/.exec(prefix);
1473
+ if (match) {
1474
+ stack.push({
1475
+ name: match[1],
1476
+ start: i - match[1].length,
1477
+ openParenIndex: i
1478
+ });
1479
+ } else {
1480
+ stack.push(null);
1481
+ }
1482
+ } else if (char === ')' && stack.length > 0) {
1483
+ stack.pop();
1484
+ }
1485
+ }
1486
+
1487
+ for (let i = stack.length - 1; i >= 0; i--) {
1488
+ if (stack[i]) {
1489
+ return stack[i];
1490
+ }
1491
+ }
1492
+
1493
+ return null;
1494
+ }
1495
+
1332
1496
  /**
1333
1497
  * Check if string is properly terminated
1334
1498
  * @private
@@ -1700,12 +1864,6 @@ class RuleParseError extends Error {
1700
1864
  if (this.hint) {
1701
1865
  msg += ` Hint: ${this.hint}\n`;
1702
1866
  }
1703
- if (this.found) {
1704
- msg += ` Found: ${this.found}\n`;
1705
- }
1706
- if (this.expected && this.expected.length) {
1707
- msg += ` Expected: ${this.expected.join(', ')}`;
1708
- }
1709
1867
  return msg;
1710
1868
  }
1711
1869
 
@@ -3671,18 +3829,9 @@ const grammar = `
3671
3829
 
3672
3830
  template_filter_arg ::= value | template_value
3673
3831
 
3674
- number_atom ::= number | template_value
3675
- number_time_atom ::= number_time | template_value WS+ unit | template_value
3676
- tod_atom ::= number_tod | template_value
3677
- dow_atom ::= dow | template_value
3678
- between_time_only_atom ::= between_time_only | template_value
3679
- between_tod_only_atom ::= between_tod_only | template_value
3680
-
3681
3832
  string_atom ::= string
3682
3833
  boolean_atom ::= false | true
3683
3834
  time_value_atom ::= number_tod
3684
- time_period_atom ::= time_value_atom WS* "TO" WS* time_value_atom
3685
- time_period_ago_atom ::= time_value_atom WS* "TO" WS* time_value_atom WS+ AGO WS+ number WS+ unit
3686
3835
 
3687
3836
  object_atom ::= json_object
3688
3837
  json_value ::= string | number | false | true | null | json_array | json_object
@@ -3699,7 +3848,7 @@ const grammar = `
3699
3848
  module.exports = Grammars.W3C.getRules(grammar);
3700
3849
 
3701
3850
  },{"ebnf":13}],16:[function(require,module,exports){
3702
- module.exports=[{"name":"TEMPLATE_BEGIN","bnf":[["\"${\""]]},{"name":"TEMPLATE_END","bnf":[["\"}\""]]},{"name":"PIPE","bnf":[["\"|\""]]},{"name":"%IDENT[2]","bnf":[[/[A-Za-z0-9_]/]]},{"name":"IDENT","bnf":[[/[A-Za-z_]/,"%IDENT[2]*"]]},{"name":"DOT","bnf":[["\".\""]]},{"name":"template_value","bnf":[["TEMPLATE_BEGIN","WS*","template_expr","WS*","TEMPLATE_END"]]},{"name":"%template_expr[2]","bnf":[["WS*","template_pipe","WS*","template_filter_call"]],"fragment":true},{"name":"template_expr","bnf":[["template_path","%template_expr[2]*"]]},{"name":"template_pipe","bnf":[["PIPE"]]},{"name":"%template_path[2]","bnf":[["WS*","DOT","WS*","IDENT"]],"fragment":true},{"name":"template_path","bnf":[["IDENT","%template_path[2]*"]]},{"name":"%template_filter_call[2]","bnf":[["WS*","BEGIN_ARGUMENT","WS*","template_filter_args?","WS*","END_ARGUMENT"]],"fragment":true},{"name":"template_filter_call","bnf":[["template_filter_name","%template_filter_call[2]?"]]},{"name":"template_filter_name","bnf":[["IDENT"]]},{"name":"%template_filter_args[2]","bnf":[["WS*","\",\"","WS*","template_filter_arg"]],"fragment":true},{"name":"template_filter_args","bnf":[["template_filter_arg","%template_filter_args[2]*"]]},{"name":"template_filter_arg","bnf":[["value"],["template_value"]]},{"name":"number_atom","bnf":[["number"],["template_value"]]},{"name":"number_time_atom","bnf":[["number_time"],["template_value","WS+","unit"],["template_value"]]},{"name":"tod_atom","bnf":[["number_tod"],["template_value"]]},{"name":"dow_atom","bnf":[["dow"],["template_value"]]},{"name":"between_time_only_atom","bnf":[["between_time_only"],["template_value"]]},{"name":"between_tod_only_atom","bnf":[["between_tod_only"],["template_value"]]},{"name":"string_atom","bnf":[["string"]]},{"name":"boolean_atom","bnf":[["false"],["true"]]},{"name":"time_value_atom","bnf":[["number_tod"]]},{"name":"time_period_atom","bnf":[["time_value_atom","WS*","\"TO\"","WS*","time_value_atom"]]},{"name":"time_period_ago_atom","bnf":[["time_value_atom","WS*","\"TO\"","WS*","time_value_atom","WS+","AGO","WS+","number","WS+","unit"]]},{"name":"object_atom","bnf":[["json_object"]]},{"name":"json_value","bnf":[["string"],["number"],["false"],["true"],["null"],["json_array"],["json_object"]]},{"name":"json_member","bnf":[["string","NAME_SEPARATOR","json_value"]]},{"name":"%json_object[2][2]","bnf":[["VALUE_SEPARATOR","json_member"]],"fragment":true},{"name":"%json_object[2]","bnf":[["json_member","%json_object[2][2]*"]],"fragment":true},{"name":"json_object","bnf":[["BEGIN_OBJECT","%json_object[2]?","END_OBJECT"]]},{"name":"%json_array[2][2]","bnf":[["VALUE_SEPARATOR","json_value"]],"fragment":true},{"name":"%json_array[2]","bnf":[["json_value","%json_array[2][2]*"]],"fragment":true},{"name":"json_array","bnf":[["BEGIN_ARRAY","%json_array[2]?","END_ARRAY"]]},{"name":"%string_array[2][2]","bnf":[["VALUE_SEPARATOR","string"]],"fragment":true},{"name":"%string_array[2]","bnf":[["string","%string_array[2][2]*"]],"fragment":true},{"name":"string_array","bnf":[["BEGIN_ARRAY","%string_array[2]?","END_ARRAY"]]},{"name":"%number_array[2][2]","bnf":[["VALUE_SEPARATOR","number"]],"fragment":true},{"name":"%number_array[2]","bnf":[["number","%number_array[2][2]*"]],"fragment":true},{"name":"number_array","bnf":[["BEGIN_ARRAY","%number_array[2]?","END_ARRAY"]]},{"name":"%boolean_array[2][2]","bnf":[["VALUE_SEPARATOR","boolean_atom"]],"fragment":true},{"name":"%boolean_array[2]","bnf":[["boolean_atom","%boolean_array[2][2]*"]],"fragment":true},{"name":"boolean_array","bnf":[["BEGIN_ARRAY","%boolean_array[2]?","END_ARRAY"]]},{"name":"%object_array[2][2]","bnf":[["VALUE_SEPARATOR","json_object"]],"fragment":true},{"name":"%object_array[2]","bnf":[["json_object","%object_array[2][2]*"]],"fragment":true},{"name":"object_array","bnf":[["BEGIN_ARRAY","%object_array[2]?","END_ARRAY"]]}]
3851
+ module.exports=[{"name":"TEMPLATE_BEGIN","bnf":[["\"${\""]]},{"name":"TEMPLATE_END","bnf":[["\"}\""]]},{"name":"PIPE","bnf":[["\"|\""]]},{"name":"%IDENT[2]","bnf":[[/[A-Za-z0-9_]/]]},{"name":"IDENT","bnf":[[/[A-Za-z_]/,"%IDENT[2]*"]]},{"name":"DOT","bnf":[["\".\""]]},{"name":"template_value","bnf":[["TEMPLATE_BEGIN","WS*","template_expr","WS*","TEMPLATE_END"]]},{"name":"%template_expr[2]","bnf":[["WS*","template_pipe","WS*","template_filter_call"]],"fragment":true},{"name":"template_expr","bnf":[["template_path","%template_expr[2]*"]]},{"name":"template_pipe","bnf":[["PIPE"]]},{"name":"%template_path[2]","bnf":[["WS*","DOT","WS*","IDENT"]],"fragment":true},{"name":"template_path","bnf":[["IDENT","%template_path[2]*"]]},{"name":"%template_filter_call[2]","bnf":[["WS*","BEGIN_ARGUMENT","WS*","template_filter_args?","WS*","END_ARGUMENT"]],"fragment":true},{"name":"template_filter_call","bnf":[["template_filter_name","%template_filter_call[2]?"]]},{"name":"template_filter_name","bnf":[["IDENT"]]},{"name":"%template_filter_args[2]","bnf":[["WS*","\",\"","WS*","template_filter_arg"]],"fragment":true},{"name":"template_filter_args","bnf":[["template_filter_arg","%template_filter_args[2]*"]]},{"name":"template_filter_arg","bnf":[["value"],["template_value"]]},{"name":"string_atom","bnf":[["string"]]},{"name":"boolean_atom","bnf":[["false"],["true"]]},{"name":"time_value_atom","bnf":[["number_tod"]]},{"name":"object_atom","bnf":[["json_object"]]},{"name":"json_value","bnf":[["string"],["number"],["false"],["true"],["null"],["json_array"],["json_object"]]},{"name":"json_member","bnf":[["string","NAME_SEPARATOR","json_value"]]},{"name":"%json_object[2][2]","bnf":[["VALUE_SEPARATOR","json_member"]],"fragment":true},{"name":"%json_object[2]","bnf":[["json_member","%json_object[2][2]*"]],"fragment":true},{"name":"json_object","bnf":[["BEGIN_OBJECT","%json_object[2]?","END_OBJECT"]]},{"name":"%json_array[2][2]","bnf":[["VALUE_SEPARATOR","json_value"]],"fragment":true},{"name":"%json_array[2]","bnf":[["json_value","%json_array[2][2]*"]],"fragment":true},{"name":"json_array","bnf":[["BEGIN_ARRAY","%json_array[2]?","END_ARRAY"]]},{"name":"%string_array[2][2]","bnf":[["VALUE_SEPARATOR","string"]],"fragment":true},{"name":"%string_array[2]","bnf":[["string","%string_array[2][2]*"]],"fragment":true},{"name":"string_array","bnf":[["BEGIN_ARRAY","%string_array[2]?","END_ARRAY"]]},{"name":"%number_array[2][2]","bnf":[["VALUE_SEPARATOR","number"]],"fragment":true},{"name":"%number_array[2]","bnf":[["number","%number_array[2][2]*"]],"fragment":true},{"name":"number_array","bnf":[["BEGIN_ARRAY","%number_array[2]?","END_ARRAY"]]},{"name":"%boolean_array[2][2]","bnf":[["VALUE_SEPARATOR","boolean_atom"]],"fragment":true},{"name":"%boolean_array[2]","bnf":[["boolean_atom","%boolean_array[2][2]*"]],"fragment":true},{"name":"boolean_array","bnf":[["BEGIN_ARRAY","%boolean_array[2]?","END_ARRAY"]]},{"name":"%object_array[2][2]","bnf":[["VALUE_SEPARATOR","json_object"]],"fragment":true},{"name":"%object_array[2]","bnf":[["json_object","%object_array[2][2]*"]],"fragment":true},{"name":"object_array","bnf":[["BEGIN_ARRAY","%object_array[2]?","END_ARRAY"]]}]
3703
3852
  },{}],17:[function(require,module,exports){
3704
3853
  // Note: We are coupled closely with the ebnf grammar structure of rule-parser
3705
3854
  const TemplateGrammar = require('./RuleTemplate.production.ebnf.js'),
@@ -3731,7 +3880,7 @@ const AllowedTypeMapping = {
3731
3880
  'number': ['number_atom', 'math_expr'],
3732
3881
  'boolean': ['boolean_atom', 'boolean_expr'],
3733
3882
  'time period': ['time_period_atom'],
3734
- 'time period ago': ['time_period_atom'],
3883
+ 'time period ago': ['time_period_ago_atom'],
3735
3884
  'time value': ['time_value_atom', 'tod_atom'],
3736
3885
  'number time': ['number_atom'],
3737
3886
  'string array': ['string_array'],
@@ -3752,12 +3901,71 @@ for(const rule of TemplateGrammar){
3752
3901
  }
3753
3902
  }
3754
3903
 
3755
- // Add template_value as an alternative to value_atom so templates can be parsed
3756
- const valueAtomIdx = extendedGrammar.findIndex(r => r.name === 'value_atom');
3757
- if (valueAtomIdx !== -1) {
3758
- extendedGrammar[valueAtomIdx] = Object.assign({}, extendedGrammar[valueAtomIdx]);
3759
- extendedGrammar[valueAtomIdx].bnf = extendedGrammar[valueAtomIdx].bnf.concat([['template_value']]);
3760
- }
3904
+ const cloneRule = (ruleName) => {
3905
+ const idx = extendedGrammar.findIndex(rule => rule.name === ruleName);
3906
+ if (idx === -1) {
3907
+ return null;
3908
+ }
3909
+
3910
+ extendedGrammar[idx] = Object.assign({}, extendedGrammar[idx], {
3911
+ bnf: extendedGrammar[idx].bnf.map(alt => Array.isArray(alt) ? alt.slice() : alt)
3912
+ });
3913
+
3914
+ return extendedGrammar[idx];
3915
+ };
3916
+
3917
+ const appendAlternative = (ruleName, alternative) => {
3918
+ const rule = cloneRule(ruleName);
3919
+ if (!rule) {
3920
+ return;
3921
+ }
3922
+
3923
+ const exists = rule.bnf.some(existing => JSON.stringify(existing) === JSON.stringify(alternative));
3924
+ if (!exists) {
3925
+ rule.bnf.push(alternative);
3926
+ }
3927
+ };
3928
+
3929
+ const replaceRule = (ruleName, bnf) => {
3930
+ const idx = extendedGrammar.findIndex(rule => rule.name === ruleName);
3931
+ if (idx === -1) {
3932
+ extendedGrammar.push({name: ruleName, bnf});
3933
+ return;
3934
+ }
3935
+
3936
+ extendedGrammar[idx] = Object.assign({}, extendedGrammar[idx], {
3937
+ bnf: bnf.map(alt => alt.slice())
3938
+ });
3939
+ };
3940
+
3941
+ appendAlternative('number_atom', ['template_value']);
3942
+ appendAlternative('number_time_atom', ['template_value', 'WS+', 'unit']);
3943
+ appendAlternative('number_time_atom', ['template_value']);
3944
+ appendAlternative('tod_atom', ['template_value']);
3945
+ appendAlternative('dow_atom', ['template_value']);
3946
+ appendAlternative('between_time_only_atom', ['template_value']);
3947
+ appendAlternative('between_tod_only_atom', ['template_value']);
3948
+ appendAlternative('string_atom', ['template_value']);
3949
+ appendAlternative('boolean_atom', ['template_value']);
3950
+ appendAlternative('time_value_atom', ['template_value']);
3951
+ appendAlternative('time_period_atom', ['template_value']);
3952
+ appendAlternative('time_period_ago_atom', ['template_value']);
3953
+ appendAlternative('object_atom', ['template_value']);
3954
+ appendAlternative('string_array', ['template_value']);
3955
+ appendAlternative('number_array', ['template_value']);
3956
+ appendAlternative('boolean_array', ['template_value']);
3957
+ appendAlternative('object_array', ['template_value']);
3958
+
3959
+ replaceRule('argument', [
3960
+ ['number_time_atom', 'WS*'],
3961
+ ['statement', 'WS*']
3962
+ ]);
3963
+
3964
+ replaceRule('simple_result', [
3965
+ ['fcall'],
3966
+ ['number_time_atom'],
3967
+ ['value']
3968
+ ]);
3761
3969
 
3762
3970
  // Export the parser rules for potential external use
3763
3971
  const ParserRules = extendedGrammar;
@@ -4054,6 +4262,23 @@ class RuleTemplate {
4054
4262
  }
4055
4263
  }
4056
4264
 
4265
+ const canValidatePreparedRule = errors.length === 0
4266
+ && Array.from(seenVariables).every(varName => {
4267
+ const varData = variables[varName];
4268
+ return varData
4269
+ && typeof varData === 'object'
4270
+ && Object.prototype.hasOwnProperty.call(varData, 'type')
4271
+ && Object.prototype.hasOwnProperty.call(varData, 'value');
4272
+ });
4273
+
4274
+ if (canValidatePreparedRule) {
4275
+ try {
4276
+ RuleParser.toAst(this.prepare(variables));
4277
+ } catch (error) {
4278
+ errors.push(`Prepared rule is invalid: ${error.message}`);
4279
+ }
4280
+ }
4281
+
4057
4282
  if (functionBlob && typeof functionBlob.validate === 'function') {
4058
4283
  for (const functionCall of this._extractFunctionCalls()) {
4059
4284
  warnings.push(...functionBlob.validate(functionCall.name, functionCall.arguments));
@@ -4262,12 +4487,12 @@ class RuleTemplate {
4262
4487
  return value ? 'true' : 'false';
4263
4488
  }
4264
4489
 
4265
- if (type === 'time period' || type === 'time period ago') {
4266
- let ret = `${value.from} TO ${value.to}`;
4267
- if(value.ago) {
4268
- ret += ` AGO ${value.ago[0]} ${value.ago[1]}`;
4269
- }
4270
- return ret;
4490
+ if (type === 'time period') {
4491
+ return `BETWEEN ${value.from} AND ${value.to}`;
4492
+ }
4493
+
4494
+ if (type === 'time period ago') {
4495
+ return `${value.ago[0]} ${value.ago[1]} AGO BETWEEN ${value.from} AND ${value.to}`;
4271
4496
  }
4272
4497
 
4273
4498
  if (type === 'object' || type === 'string array' || type === 'number array' || type === 'boolean array' || type === 'object array') {
@@ -4762,7 +4987,7 @@ class VariableValidate {
4762
4987
  return null;
4763
4988
  }
4764
4989
 
4765
- return `${value.from.trim()} TO ${value.to.trim()}`;
4990
+ return `BETWEEN ${value.from.trim()} AND ${value.to.trim()}`;
4766
4991
  }
4767
4992
 
4768
4993
  static _serializeTimePeriodAgo(value) {
@@ -4779,7 +5004,7 @@ class VariableValidate {
4779
5004
  return null;
4780
5005
  }
4781
5006
 
4782
- return `${value.from.trim()} TO ${value.to.trim()} AGO ${amount} ${unit.trim()}`;
5007
+ return `${amount} ${unit.trim()} AGO BETWEEN ${value.from.trim()} AND ${value.to.trim()}`;
4783
5008
  }
4784
5009
 
4785
5010
  static _serializeNumberTime(value) {
@@ -4896,7 +5121,7 @@ VariableValidate.validators = Object.freeze({
4896
5121
  'time period': (value) => VariableValidate._validateWithRule(value, 'time_period_atom', {
4897
5122
  normalize: VariableValidate._serializeTimePeriod,
4898
5123
  emptyMessage: 'Time period variables must be objects with string from/to properties',
4899
- parseMessage: 'Time period variables must serialize to FROM TO TO syntax',
5124
+ parseMessage: 'Time period variables must serialize to BETWEEN FROM AND TO syntax',
4900
5125
  semanticCheck: (rawValue) => {
4901
5126
  const fromError = VariableValidate._validateTimeOfDay(rawValue?.from);
4902
5127
  if (fromError) return `Invalid time period from value: ${fromError}`;
@@ -4910,7 +5135,7 @@ VariableValidate.validators = Object.freeze({
4910
5135
  'time period ago': (value) => VariableValidate._validateWithRule(value, 'time_period_ago_atom', {
4911
5136
  normalize: VariableValidate._serializeTimePeriodAgo,
4912
5137
  emptyMessage: 'Time period ago variables must be objects with from, to, and ago properties',
4913
- parseMessage: 'Time period ago variables must serialize to FROM TO TO AGO AMOUNT UNIT syntax',
5138
+ parseMessage: 'Time period ago variables must serialize to AMOUNT UNIT AGO BETWEEN FROM AND TO syntax',
4914
5139
  semanticCheck: (rawValue) => {
4915
5140
  const fromError = VariableValidate._validateTimeOfDay(rawValue?.from);
4916
5141
  if (fromError) return `Invalid time period ago from value: ${fromError}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@halleyassist/rule-templater",
3
- "version": "0.0.18",
3
+ "version": "0.0.19",
4
4
  "description": "The grammar for HalleyAssist rules",
5
5
  "main": "src/RuleTemplate.production.js",
6
6
  "browser": "./dist/rule-templater.browser.js",
@@ -190,12 +190,12 @@ class GeneralTemplate {
190
190
  return '';
191
191
  }
192
192
 
193
- if (varData.type === 'time period' || varData.type === 'time period ago') {
194
- let ret = `${varData.value.from} TO ${varData.value.to}`;
195
- if (varData.value.ago) {
196
- ret += ` AGO ${varData.value.ago[0]} ${varData.value.ago[1]}`;
197
- }
198
- return ret;
193
+ if (varData.type === 'time period') {
194
+ return `BETWEEN ${varData.value.from} AND ${varData.value.to}`;
195
+ }
196
+
197
+ if (varData.type === 'time period ago') {
198
+ return `${varData.value.ago[0]} ${varData.value.ago[1]} AGO BETWEEN ${varData.value.from} AND ${varData.value.to}`;
199
199
  }
200
200
 
201
201
  return String(varData.value);
@@ -23,18 +23,9 @@ const grammar = `
23
23
 
24
24
  template_filter_arg ::= value | template_value
25
25
 
26
- number_atom ::= number | template_value
27
- number_time_atom ::= number_time | template_value WS+ unit | template_value
28
- tod_atom ::= number_tod | template_value
29
- dow_atom ::= dow | template_value
30
- between_time_only_atom ::= between_time_only | template_value
31
- between_tod_only_atom ::= between_tod_only | template_value
32
-
33
26
  string_atom ::= string
34
27
  boolean_atom ::= false | true
35
28
  time_value_atom ::= number_tod
36
- time_period_atom ::= time_value_atom WS* "TO" WS* time_value_atom
37
- time_period_ago_atom ::= time_value_atom WS* "TO" WS* time_value_atom WS+ AGO WS+ number WS+ unit
38
29
 
39
30
  object_atom ::= json_object
40
31
  json_value ::= string | number | false | true | null | json_array | json_object
@@ -28,7 +28,7 @@ const AllowedTypeMapping = {
28
28
  'number': ['number_atom', 'math_expr'],
29
29
  'boolean': ['boolean_atom', 'boolean_expr'],
30
30
  'time period': ['time_period_atom'],
31
- 'time period ago': ['time_period_atom'],
31
+ 'time period ago': ['time_period_ago_atom'],
32
32
  'time value': ['time_value_atom', 'tod_atom'],
33
33
  'number time': ['number_atom'],
34
34
  'string array': ['string_array'],
@@ -49,12 +49,71 @@ for(const rule of TemplateGrammar){
49
49
  }
50
50
  }
51
51
 
52
- // Add template_value as an alternative to value_atom so templates can be parsed
53
- const valueAtomIdx = extendedGrammar.findIndex(r => r.name === 'value_atom');
54
- if (valueAtomIdx !== -1) {
55
- extendedGrammar[valueAtomIdx] = Object.assign({}, extendedGrammar[valueAtomIdx]);
56
- extendedGrammar[valueAtomIdx].bnf = extendedGrammar[valueAtomIdx].bnf.concat([['template_value']]);
57
- }
52
+ const cloneRule = (ruleName) => {
53
+ const idx = extendedGrammar.findIndex(rule => rule.name === ruleName);
54
+ if (idx === -1) {
55
+ return null;
56
+ }
57
+
58
+ extendedGrammar[idx] = Object.assign({}, extendedGrammar[idx], {
59
+ bnf: extendedGrammar[idx].bnf.map(alt => Array.isArray(alt) ? alt.slice() : alt)
60
+ });
61
+
62
+ return extendedGrammar[idx];
63
+ };
64
+
65
+ const appendAlternative = (ruleName, alternative) => {
66
+ const rule = cloneRule(ruleName);
67
+ if (!rule) {
68
+ return;
69
+ }
70
+
71
+ const exists = rule.bnf.some(existing => JSON.stringify(existing) === JSON.stringify(alternative));
72
+ if (!exists) {
73
+ rule.bnf.push(alternative);
74
+ }
75
+ };
76
+
77
+ const replaceRule = (ruleName, bnf) => {
78
+ const idx = extendedGrammar.findIndex(rule => rule.name === ruleName);
79
+ if (idx === -1) {
80
+ extendedGrammar.push({name: ruleName, bnf});
81
+ return;
82
+ }
83
+
84
+ extendedGrammar[idx] = Object.assign({}, extendedGrammar[idx], {
85
+ bnf: bnf.map(alt => alt.slice())
86
+ });
87
+ };
88
+
89
+ appendAlternative('number_atom', ['template_value']);
90
+ appendAlternative('number_time_atom', ['template_value', 'WS+', 'unit']);
91
+ appendAlternative('number_time_atom', ['template_value']);
92
+ appendAlternative('tod_atom', ['template_value']);
93
+ appendAlternative('dow_atom', ['template_value']);
94
+ appendAlternative('between_time_only_atom', ['template_value']);
95
+ appendAlternative('between_tod_only_atom', ['template_value']);
96
+ appendAlternative('string_atom', ['template_value']);
97
+ appendAlternative('boolean_atom', ['template_value']);
98
+ appendAlternative('time_value_atom', ['template_value']);
99
+ appendAlternative('time_period_atom', ['template_value']);
100
+ appendAlternative('time_period_ago_atom', ['template_value']);
101
+ appendAlternative('object_atom', ['template_value']);
102
+ appendAlternative('string_array', ['template_value']);
103
+ appendAlternative('number_array', ['template_value']);
104
+ appendAlternative('boolean_array', ['template_value']);
105
+ appendAlternative('object_array', ['template_value']);
106
+
107
+ replaceRule('argument', [
108
+ ['number_time_atom', 'WS*'],
109
+ ['statement', 'WS*']
110
+ ]);
111
+
112
+ replaceRule('simple_result', [
113
+ ['fcall'],
114
+ ['number_time_atom'],
115
+ ['value']
116
+ ]);
58
117
 
59
118
  // Export the parser rules for potential external use
60
119
  const ParserRules = extendedGrammar;
@@ -351,6 +410,23 @@ class RuleTemplate {
351
410
  }
352
411
  }
353
412
 
413
+ const canValidatePreparedRule = errors.length === 0
414
+ && Array.from(seenVariables).every(varName => {
415
+ const varData = variables[varName];
416
+ return varData
417
+ && typeof varData === 'object'
418
+ && Object.prototype.hasOwnProperty.call(varData, 'type')
419
+ && Object.prototype.hasOwnProperty.call(varData, 'value');
420
+ });
421
+
422
+ if (canValidatePreparedRule) {
423
+ try {
424
+ RuleParser.toAst(this.prepare(variables));
425
+ } catch (error) {
426
+ errors.push(`Prepared rule is invalid: ${error.message}`);
427
+ }
428
+ }
429
+
354
430
  if (functionBlob && typeof functionBlob.validate === 'function') {
355
431
  for (const functionCall of this._extractFunctionCalls()) {
356
432
  warnings.push(...functionBlob.validate(functionCall.name, functionCall.arguments));
@@ -559,12 +635,12 @@ class RuleTemplate {
559
635
  return value ? 'true' : 'false';
560
636
  }
561
637
 
562
- if (type === 'time period' || type === 'time period ago') {
563
- let ret = `${value.from} TO ${value.to}`;
564
- if(value.ago) {
565
- ret += ` AGO ${value.ago[0]} ${value.ago[1]}`;
566
- }
567
- return ret;
638
+ if (type === 'time period') {
639
+ return `BETWEEN ${value.from} AND ${value.to}`;
640
+ }
641
+
642
+ if (type === 'time period ago') {
643
+ return `${value.ago[0]} ${value.ago[1]} AGO BETWEEN ${value.from} AND ${value.to}`;
568
644
  }
569
645
 
570
646
  if (type === 'object' || type === 'string array' || type === 'number array' || type === 'boolean array' || type === 'object array') {
@@ -1 +1 @@
1
- module.exports=[{"name":"TEMPLATE_BEGIN","bnf":[["\"${\""]]},{"name":"TEMPLATE_END","bnf":[["\"}\""]]},{"name":"PIPE","bnf":[["\"|\""]]},{"name":"%IDENT[2]","bnf":[[/[A-Za-z0-9_]/]]},{"name":"IDENT","bnf":[[/[A-Za-z_]/,"%IDENT[2]*"]]},{"name":"DOT","bnf":[["\".\""]]},{"name":"template_value","bnf":[["TEMPLATE_BEGIN","WS*","template_expr","WS*","TEMPLATE_END"]]},{"name":"%template_expr[2]","bnf":[["WS*","template_pipe","WS*","template_filter_call"]],"fragment":true},{"name":"template_expr","bnf":[["template_path","%template_expr[2]*"]]},{"name":"template_pipe","bnf":[["PIPE"]]},{"name":"%template_path[2]","bnf":[["WS*","DOT","WS*","IDENT"]],"fragment":true},{"name":"template_path","bnf":[["IDENT","%template_path[2]*"]]},{"name":"%template_filter_call[2]","bnf":[["WS*","BEGIN_ARGUMENT","WS*","template_filter_args?","WS*","END_ARGUMENT"]],"fragment":true},{"name":"template_filter_call","bnf":[["template_filter_name","%template_filter_call[2]?"]]},{"name":"template_filter_name","bnf":[["IDENT"]]},{"name":"%template_filter_args[2]","bnf":[["WS*","\",\"","WS*","template_filter_arg"]],"fragment":true},{"name":"template_filter_args","bnf":[["template_filter_arg","%template_filter_args[2]*"]]},{"name":"template_filter_arg","bnf":[["value"],["template_value"]]},{"name":"number_atom","bnf":[["number"],["template_value"]]},{"name":"number_time_atom","bnf":[["number_time"],["template_value","WS+","unit"],["template_value"]]},{"name":"tod_atom","bnf":[["number_tod"],["template_value"]]},{"name":"dow_atom","bnf":[["dow"],["template_value"]]},{"name":"between_time_only_atom","bnf":[["between_time_only"],["template_value"]]},{"name":"between_tod_only_atom","bnf":[["between_tod_only"],["template_value"]]},{"name":"string_atom","bnf":[["string"]]},{"name":"boolean_atom","bnf":[["false"],["true"]]},{"name":"time_value_atom","bnf":[["number_tod"]]},{"name":"time_period_atom","bnf":[["time_value_atom","WS*","\"TO\"","WS*","time_value_atom"]]},{"name":"time_period_ago_atom","bnf":[["time_value_atom","WS*","\"TO\"","WS*","time_value_atom","WS+","AGO","WS+","number","WS+","unit"]]},{"name":"object_atom","bnf":[["json_object"]]},{"name":"json_value","bnf":[["string"],["number"],["false"],["true"],["null"],["json_array"],["json_object"]]},{"name":"json_member","bnf":[["string","NAME_SEPARATOR","json_value"]]},{"name":"%json_object[2][2]","bnf":[["VALUE_SEPARATOR","json_member"]],"fragment":true},{"name":"%json_object[2]","bnf":[["json_member","%json_object[2][2]*"]],"fragment":true},{"name":"json_object","bnf":[["BEGIN_OBJECT","%json_object[2]?","END_OBJECT"]]},{"name":"%json_array[2][2]","bnf":[["VALUE_SEPARATOR","json_value"]],"fragment":true},{"name":"%json_array[2]","bnf":[["json_value","%json_array[2][2]*"]],"fragment":true},{"name":"json_array","bnf":[["BEGIN_ARRAY","%json_array[2]?","END_ARRAY"]]},{"name":"%string_array[2][2]","bnf":[["VALUE_SEPARATOR","string"]],"fragment":true},{"name":"%string_array[2]","bnf":[["string","%string_array[2][2]*"]],"fragment":true},{"name":"string_array","bnf":[["BEGIN_ARRAY","%string_array[2]?","END_ARRAY"]]},{"name":"%number_array[2][2]","bnf":[["VALUE_SEPARATOR","number"]],"fragment":true},{"name":"%number_array[2]","bnf":[["number","%number_array[2][2]*"]],"fragment":true},{"name":"number_array","bnf":[["BEGIN_ARRAY","%number_array[2]?","END_ARRAY"]]},{"name":"%boolean_array[2][2]","bnf":[["VALUE_SEPARATOR","boolean_atom"]],"fragment":true},{"name":"%boolean_array[2]","bnf":[["boolean_atom","%boolean_array[2][2]*"]],"fragment":true},{"name":"boolean_array","bnf":[["BEGIN_ARRAY","%boolean_array[2]?","END_ARRAY"]]},{"name":"%object_array[2][2]","bnf":[["VALUE_SEPARATOR","json_object"]],"fragment":true},{"name":"%object_array[2]","bnf":[["json_object","%object_array[2][2]*"]],"fragment":true},{"name":"object_array","bnf":[["BEGIN_ARRAY","%object_array[2]?","END_ARRAY"]]}]
1
+ module.exports=[{"name":"TEMPLATE_BEGIN","bnf":[["\"${\""]]},{"name":"TEMPLATE_END","bnf":[["\"}\""]]},{"name":"PIPE","bnf":[["\"|\""]]},{"name":"%IDENT[2]","bnf":[[/[A-Za-z0-9_]/]]},{"name":"IDENT","bnf":[[/[A-Za-z_]/,"%IDENT[2]*"]]},{"name":"DOT","bnf":[["\".\""]]},{"name":"template_value","bnf":[["TEMPLATE_BEGIN","WS*","template_expr","WS*","TEMPLATE_END"]]},{"name":"%template_expr[2]","bnf":[["WS*","template_pipe","WS*","template_filter_call"]],"fragment":true},{"name":"template_expr","bnf":[["template_path","%template_expr[2]*"]]},{"name":"template_pipe","bnf":[["PIPE"]]},{"name":"%template_path[2]","bnf":[["WS*","DOT","WS*","IDENT"]],"fragment":true},{"name":"template_path","bnf":[["IDENT","%template_path[2]*"]]},{"name":"%template_filter_call[2]","bnf":[["WS*","BEGIN_ARGUMENT","WS*","template_filter_args?","WS*","END_ARGUMENT"]],"fragment":true},{"name":"template_filter_call","bnf":[["template_filter_name","%template_filter_call[2]?"]]},{"name":"template_filter_name","bnf":[["IDENT"]]},{"name":"%template_filter_args[2]","bnf":[["WS*","\",\"","WS*","template_filter_arg"]],"fragment":true},{"name":"template_filter_args","bnf":[["template_filter_arg","%template_filter_args[2]*"]]},{"name":"template_filter_arg","bnf":[["value"],["template_value"]]},{"name":"string_atom","bnf":[["string"]]},{"name":"boolean_atom","bnf":[["false"],["true"]]},{"name":"time_value_atom","bnf":[["number_tod"]]},{"name":"object_atom","bnf":[["json_object"]]},{"name":"json_value","bnf":[["string"],["number"],["false"],["true"],["null"],["json_array"],["json_object"]]},{"name":"json_member","bnf":[["string","NAME_SEPARATOR","json_value"]]},{"name":"%json_object[2][2]","bnf":[["VALUE_SEPARATOR","json_member"]],"fragment":true},{"name":"%json_object[2]","bnf":[["json_member","%json_object[2][2]*"]],"fragment":true},{"name":"json_object","bnf":[["BEGIN_OBJECT","%json_object[2]?","END_OBJECT"]]},{"name":"%json_array[2][2]","bnf":[["VALUE_SEPARATOR","json_value"]],"fragment":true},{"name":"%json_array[2]","bnf":[["json_value","%json_array[2][2]*"]],"fragment":true},{"name":"json_array","bnf":[["BEGIN_ARRAY","%json_array[2]?","END_ARRAY"]]},{"name":"%string_array[2][2]","bnf":[["VALUE_SEPARATOR","string"]],"fragment":true},{"name":"%string_array[2]","bnf":[["string","%string_array[2][2]*"]],"fragment":true},{"name":"string_array","bnf":[["BEGIN_ARRAY","%string_array[2]?","END_ARRAY"]]},{"name":"%number_array[2][2]","bnf":[["VALUE_SEPARATOR","number"]],"fragment":true},{"name":"%number_array[2]","bnf":[["number","%number_array[2][2]*"]],"fragment":true},{"name":"number_array","bnf":[["BEGIN_ARRAY","%number_array[2]?","END_ARRAY"]]},{"name":"%boolean_array[2][2]","bnf":[["VALUE_SEPARATOR","boolean_atom"]],"fragment":true},{"name":"%boolean_array[2]","bnf":[["boolean_atom","%boolean_array[2][2]*"]],"fragment":true},{"name":"boolean_array","bnf":[["BEGIN_ARRAY","%boolean_array[2]?","END_ARRAY"]]},{"name":"%object_array[2][2]","bnf":[["VALUE_SEPARATOR","json_object"]],"fragment":true},{"name":"%object_array[2]","bnf":[["json_object","%object_array[2][2]*"]],"fragment":true},{"name":"object_array","bnf":[["BEGIN_ARRAY","%object_array[2]?","END_ARRAY"]]}]
@@ -28,7 +28,7 @@ const AllowedTypeMapping = {
28
28
  'number': ['number_atom', 'math_expr'],
29
29
  'boolean': ['boolean_atom', 'boolean_expr'],
30
30
  'time period': ['time_period_atom'],
31
- 'time period ago': ['time_period_atom'],
31
+ 'time period ago': ['time_period_ago_atom'],
32
32
  'time value': ['time_value_atom', 'tod_atom'],
33
33
  'number time': ['number_atom'],
34
34
  'string array': ['string_array'],
@@ -49,12 +49,71 @@ for(const rule of TemplateGrammar){
49
49
  }
50
50
  }
51
51
 
52
- // Add template_value as an alternative to value_atom so templates can be parsed
53
- const valueAtomIdx = extendedGrammar.findIndex(r => r.name === 'value_atom');
54
- if (valueAtomIdx !== -1) {
55
- extendedGrammar[valueAtomIdx] = Object.assign({}, extendedGrammar[valueAtomIdx]);
56
- extendedGrammar[valueAtomIdx].bnf = extendedGrammar[valueAtomIdx].bnf.concat([['template_value']]);
57
- }
52
+ const cloneRule = (ruleName) => {
53
+ const idx = extendedGrammar.findIndex(rule => rule.name === ruleName);
54
+ if (idx === -1) {
55
+ return null;
56
+ }
57
+
58
+ extendedGrammar[idx] = Object.assign({}, extendedGrammar[idx], {
59
+ bnf: extendedGrammar[idx].bnf.map(alt => Array.isArray(alt) ? alt.slice() : alt)
60
+ });
61
+
62
+ return extendedGrammar[idx];
63
+ };
64
+
65
+ const appendAlternative = (ruleName, alternative) => {
66
+ const rule = cloneRule(ruleName);
67
+ if (!rule) {
68
+ return;
69
+ }
70
+
71
+ const exists = rule.bnf.some(existing => JSON.stringify(existing) === JSON.stringify(alternative));
72
+ if (!exists) {
73
+ rule.bnf.push(alternative);
74
+ }
75
+ };
76
+
77
+ const replaceRule = (ruleName, bnf) => {
78
+ const idx = extendedGrammar.findIndex(rule => rule.name === ruleName);
79
+ if (idx === -1) {
80
+ extendedGrammar.push({name: ruleName, bnf});
81
+ return;
82
+ }
83
+
84
+ extendedGrammar[idx] = Object.assign({}, extendedGrammar[idx], {
85
+ bnf: bnf.map(alt => alt.slice())
86
+ });
87
+ };
88
+
89
+ appendAlternative('number_atom', ['template_value']);
90
+ appendAlternative('number_time_atom', ['template_value', 'WS+', 'unit']);
91
+ appendAlternative('number_time_atom', ['template_value']);
92
+ appendAlternative('tod_atom', ['template_value']);
93
+ appendAlternative('dow_atom', ['template_value']);
94
+ appendAlternative('between_time_only_atom', ['template_value']);
95
+ appendAlternative('between_tod_only_atom', ['template_value']);
96
+ appendAlternative('string_atom', ['template_value']);
97
+ appendAlternative('boolean_atom', ['template_value']);
98
+ appendAlternative('time_value_atom', ['template_value']);
99
+ appendAlternative('time_period_atom', ['template_value']);
100
+ appendAlternative('time_period_ago_atom', ['template_value']);
101
+ appendAlternative('object_atom', ['template_value']);
102
+ appendAlternative('string_array', ['template_value']);
103
+ appendAlternative('number_array', ['template_value']);
104
+ appendAlternative('boolean_array', ['template_value']);
105
+ appendAlternative('object_array', ['template_value']);
106
+
107
+ replaceRule('argument', [
108
+ ['number_time_atom', 'WS*'],
109
+ ['statement', 'WS*']
110
+ ]);
111
+
112
+ replaceRule('simple_result', [
113
+ ['fcall'],
114
+ ['number_time_atom'],
115
+ ['value']
116
+ ]);
58
117
 
59
118
  // Export the parser rules for potential external use
60
119
  const ParserRules = extendedGrammar;
@@ -351,6 +410,23 @@ class RuleTemplate {
351
410
  }
352
411
  }
353
412
 
413
+ const canValidatePreparedRule = errors.length === 0
414
+ && Array.from(seenVariables).every(varName => {
415
+ const varData = variables[varName];
416
+ return varData
417
+ && typeof varData === 'object'
418
+ && Object.prototype.hasOwnProperty.call(varData, 'type')
419
+ && Object.prototype.hasOwnProperty.call(varData, 'value');
420
+ });
421
+
422
+ if (canValidatePreparedRule) {
423
+ try {
424
+ RuleParser.toAst(this.prepare(variables));
425
+ } catch (error) {
426
+ errors.push(`Prepared rule is invalid: ${error.message}`);
427
+ }
428
+ }
429
+
354
430
  if (functionBlob && typeof functionBlob.validate === 'function') {
355
431
  for (const functionCall of this._extractFunctionCalls()) {
356
432
  warnings.push(...functionBlob.validate(functionCall.name, functionCall.arguments));
@@ -559,12 +635,12 @@ class RuleTemplate {
559
635
  return value ? 'true' : 'false';
560
636
  }
561
637
 
562
- if (type === 'time period' || type === 'time period ago') {
563
- let ret = `${value.from} TO ${value.to}`;
564
- if(value.ago) {
565
- ret += ` AGO ${value.ago[0]} ${value.ago[1]}`;
566
- }
567
- return ret;
638
+ if (type === 'time period') {
639
+ return `BETWEEN ${value.from} AND ${value.to}`;
640
+ }
641
+
642
+ if (type === 'time period ago') {
643
+ return `${value.ago[0]} ${value.ago[1]} AGO BETWEEN ${value.from} AND ${value.to}`;
568
644
  }
569
645
 
570
646
  if (type === 'object' || type === 'string array' || type === 'number array' || type === 'boolean array' || type === 'object array') {
@@ -13,6 +13,7 @@ const VariableTypes = [
13
13
  'boolean',
14
14
  'object',
15
15
  'time period',
16
+ 'time period ago',
16
17
  'time value',
17
18
  'string array',
18
19
  'number array',
@@ -25,6 +26,7 @@ const AllowedTypeMapping = {
25
26
  'number': ['number_atom', 'math_expr'],
26
27
  'boolean': ['boolean_atom', 'boolean_expr'],
27
28
  'time period': ['time_period_atom'],
29
+ 'time period ago': ['time_period_ago_atom'],
28
30
  'time value': ['time_value_atom', 'tod_atom'],
29
31
  'string array': ['string_array'],
30
32
  'number array': ['number_array'],
@@ -44,11 +46,71 @@ for(const rule of TemplateGrammar){
44
46
  }
45
47
  }
46
48
 
47
- // Add template_value as an alternative to value_atom so templates can be parsed
48
- const valueAtomIdx = extendedGrammar.findIndex(r => r.name === 'value_atom');
49
- if (valueAtomIdx !== -1) {
50
- extendedGrammar[valueAtomIdx].bnf.push(['template_value']);
51
- }
49
+ const cloneRule = (ruleName) => {
50
+ const idx = extendedGrammar.findIndex(rule => rule.name === ruleName);
51
+ if (idx === -1) {
52
+ return null;
53
+ }
54
+
55
+ extendedGrammar[idx] = Object.assign({}, extendedGrammar[idx], {
56
+ bnf: extendedGrammar[idx].bnf.map(alt => Array.isArray(alt) ? alt.slice() : alt)
57
+ });
58
+
59
+ return extendedGrammar[idx];
60
+ };
61
+
62
+ const appendAlternative = (ruleName, alternative) => {
63
+ const rule = cloneRule(ruleName);
64
+ if (!rule) {
65
+ return;
66
+ }
67
+
68
+ const exists = rule.bnf.some(existing => JSON.stringify(existing) === JSON.stringify(alternative));
69
+ if (!exists) {
70
+ rule.bnf.push(alternative);
71
+ }
72
+ };
73
+
74
+ const replaceRule = (ruleName, bnf) => {
75
+ const idx = extendedGrammar.findIndex(rule => rule.name === ruleName);
76
+ if (idx === -1) {
77
+ extendedGrammar.push({name: ruleName, bnf});
78
+ return;
79
+ }
80
+
81
+ extendedGrammar[idx] = Object.assign({}, extendedGrammar[idx], {
82
+ bnf: bnf.map(alt => alt.slice())
83
+ });
84
+ };
85
+
86
+ appendAlternative('number_atom', ['template_value']);
87
+ appendAlternative('number_time_atom', ['template_value', 'WS+', 'unit']);
88
+ appendAlternative('number_time_atom', ['template_value']);
89
+ appendAlternative('tod_atom', ['template_value']);
90
+ appendAlternative('dow_atom', ['template_value']);
91
+ appendAlternative('between_time_only_atom', ['template_value']);
92
+ appendAlternative('between_tod_only_atom', ['template_value']);
93
+ appendAlternative('string_atom', ['template_value']);
94
+ appendAlternative('boolean_atom', ['template_value']);
95
+ appendAlternative('time_value_atom', ['template_value']);
96
+ appendAlternative('time_period_atom', ['template_value']);
97
+ appendAlternative('time_period_ago_atom', ['template_value']);
98
+ appendAlternative('object_atom', ['template_value']);
99
+ appendAlternative('string_array', ['template_value']);
100
+ appendAlternative('number_array', ['template_value']);
101
+ appendAlternative('boolean_array', ['template_value']);
102
+ appendAlternative('object_array', ['template_value']);
103
+
104
+ replaceRule('argument', [
105
+ ['number_time_atom', 'WS*'],
106
+ ['statement', 'WS*']
107
+ ]);
108
+
109
+ replaceRule('simple_result', [
110
+ ['fcall'],
111
+ ['number_time_atom'],
112
+ ['value']
113
+ ]);
52
114
 
53
115
  // Export the parser rules for potential external use
54
116
  const ParserRules = extendedGrammar;
@@ -374,6 +436,10 @@ class RuleTemplate {
374
436
  return String(value);
375
437
  } else if (type === 'boolean') {
376
438
  return value ? 'true' : 'false';
439
+ } else if (type === 'time period') {
440
+ return `BETWEEN ${value.from} AND ${value.to}`;
441
+ } else if (type === 'time period ago') {
442
+ return `${value.ago[0]} ${value.ago[1]} AGO BETWEEN ${value.from} AND ${value.to}`;
377
443
  } else {
378
444
  // Default behavior - just insert the value as-is
379
445
  return String(value);
@@ -207,7 +207,7 @@ class VariableValidate {
207
207
  return null;
208
208
  }
209
209
 
210
- return `${value.from.trim()} TO ${value.to.trim()}`;
210
+ return `BETWEEN ${value.from.trim()} AND ${value.to.trim()}`;
211
211
  }
212
212
 
213
213
  static _serializeTimePeriodAgo(value) {
@@ -224,7 +224,7 @@ class VariableValidate {
224
224
  return null;
225
225
  }
226
226
 
227
- return `${value.from.trim()} TO ${value.to.trim()} AGO ${amount} ${unit.trim()}`;
227
+ return `${amount} ${unit.trim()} AGO BETWEEN ${value.from.trim()} AND ${value.to.trim()}`;
228
228
  }
229
229
 
230
230
  static _serializeNumberTime(value) {
@@ -341,7 +341,7 @@ VariableValidate.validators = Object.freeze({
341
341
  'time period': (value) => VariableValidate._validateWithRule(value, 'time_period_atom', {
342
342
  normalize: VariableValidate._serializeTimePeriod,
343
343
  emptyMessage: 'Time period variables must be objects with string from/to properties',
344
- parseMessage: 'Time period variables must serialize to FROM TO TO syntax',
344
+ parseMessage: 'Time period variables must serialize to BETWEEN FROM AND TO syntax',
345
345
  semanticCheck: (rawValue) => {
346
346
  const fromError = VariableValidate._validateTimeOfDay(rawValue?.from);
347
347
  if (fromError) return `Invalid time period from value: ${fromError}`;
@@ -355,7 +355,7 @@ VariableValidate.validators = Object.freeze({
355
355
  'time period ago': (value) => VariableValidate._validateWithRule(value, 'time_period_ago_atom', {
356
356
  normalize: VariableValidate._serializeTimePeriodAgo,
357
357
  emptyMessage: 'Time period ago variables must be objects with from, to, and ago properties',
358
- parseMessage: 'Time period ago variables must serialize to FROM TO TO AGO AMOUNT UNIT syntax',
358
+ parseMessage: 'Time period ago variables must serialize to AMOUNT UNIT AGO BETWEEN FROM AND TO syntax',
359
359
  semanticCheck: (rawValue) => {
360
360
  const fromError = VariableValidate._validateTimeOfDay(rawValue?.from);
361
361
  if (fromError) return `Invalid time period ago from value: ${fromError}`;