Skip to content

Commit

Permalink
Resolve some grammar ambiguities (#2280)
Browse files Browse the repository at this point in the history
* Resolve ambiguities in storm parser et al

Resolve ambiguity with yield statement and dollar expressions inside
filter expression.
  • Loading branch information
Nic Watson committed Jul 13, 2021
1 parent 2eeaebe commit a857825
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 14 deletions.
3 changes: 2 additions & 1 deletion synapse/lib/parser.py
Expand Up @@ -365,7 +365,8 @@ def switchcase(self, kids):
with s_datfile.openDatFile('synapse.lib/storm.lark') as larkf:
_grammar = larkf.read().decode()

LarkParser = lark.Lark(_grammar, regex=True, start=['query', 'lookup', 'cmdrargs', 'evalvalu'], propagate_positions=True)
LarkParser = lark.Lark(_grammar, regex=True, start=['query', 'lookup', 'cmdrargs', 'evalvalu'],
propagate_positions=True)

class Parser:
'''
Expand Down
11 changes: 6 additions & 5 deletions synapse/lib/storm.lark
Expand Up @@ -32,13 +32,13 @@ _morenons: ( _WSCOMM? "|" _WSCOMM? | _WSCOMM) (_querystartcommand | _querystartn
_editblock: "[" _WS? _editopers _WS? "]"
_editopers: _editoper (_WS _editoper)*

///A single edit operation
// A single edit operation
_editoper: editnodeadd
| editpropset | editunivset | edittagpropset | edittagadd
| editpropdel | editunivdel | edittagpropdel | edittagdel
| editparens | edgeaddn1 | edgedeln1 | edgeaddn2 | edgedeln2

///Parenthesis in an edit block don't have incoming nodes
// Parenthesis in an edit block don't have incoming nodes
editparens: "(" _WS? editnodeadd [ _WS _editopers ] _WS? ")"
edittagadd: "+" [SETTAGOPER] tagname [_WS? "=" _WS? _valu]
editunivdel: "-" univprop
Expand Down Expand Up @@ -82,7 +82,7 @@ switchcase: "switch" _WS _varvalu _WS? "{" (_WSCOMM? (DEFAULTCASE | DOUBLEQUOTED
DEFAULTCASE: "*"
CASEBARE: /(?!\*)([^:\s"']+)/

yieldvalu: YIELD _WS _valu
yieldvalu: YIELD _WS _argvalu

initblock: "init" _WSCOMM? "{" query "}"
finiblock: "fini" _WSCOMM? "{" query "}"
Expand Down Expand Up @@ -202,7 +202,6 @@ CMPR: /(?!<-)[@?!<>^~=][@!<>^~=]*/

_safe_cmpr: BYNAME | CMPR

// Common subset of values allowed
_rootvalu: _varvalu | relpropvalu | univpropvalu | tagvalu | tagpropvalu | DOUBLEQUOTEDSTRING
| SINGLEQUOTEDSTRING | dollarexpr

Expand Down Expand Up @@ -240,6 +239,7 @@ _EMBEDQUERYSTART: "${"
NONQUOTEWORD: /(?!\/\/)[\w\-\+\?\*\/\\][^\s\),=\]}\|]*/

// An unquoted string within a storm command argument list
// TODO: revisit this doesn't make sense
WORDTOKN: /(?!\/[\/\*])[\w\+\-\?\*\/\\][^\s\=\|\}\)]*/

// An unquoted string within a list syntax
Expand Down Expand Up @@ -270,6 +270,7 @@ filtoper: FILTPREFIX _cond
FILTPREFIX: "+" | "-"

// Condition used for filters
// TODO: unify cond and dollarexpr
_cond: notcond | "(" _WS? _condexpr _WS? ")"
| hasrelpropcond | relpropcond
| abspropcond | hasabspropcond
Expand Down Expand Up @@ -361,4 +362,4 @@ AND: "and"
?exprcmp: exprsum | exprcmp _WSCOMM? EXPRCMPR _WSCOMM? exprsum
?exprsum: exprproduct | exprsum _WSCOMM? (EXPRPLUS | EXPRMINUS) _WSCOMM? exprproduct
?exprproduct: _expratom | exprproduct _WSCOMM? (EXPRTIMES | EXPRDIVIDE) _WSCOMM? _expratom
_expratom: _exprvalu | "(" _WSCOMM? expror _WSCOMM? ")"
_expratom: _exprvalu
30 changes: 24 additions & 6 deletions synapse/tests/test_lib_grammar.py
Expand Up @@ -13,7 +13,8 @@

# flake8: noqa: E501

_Queries = [
Queries = [
'test:array*[=1.2.3.4]',
'macro.set hehe ${ inet:ipv4 }',
'$q=${#foo.bar}',
'metrics.edits.byprop inet:fqdn:domain --newv $lib.null',
Expand Down Expand Up @@ -581,10 +582,18 @@
'inet:ipv4=1.2.3.4 -+> #*',
'inet:ipv4=1.2.3.4 -+> #biz.*',
'inet:ipv4=1.2.3.4 -+> #bar.baz',
'function middlechild(arg2) { yield $rockbottom($arg2) }',
'[test:comp=(10,bar)] yield { -> test:int}',
'test:arrayprop +:ints*[ range=(50,100) ]',
'inet:ipv4 +(($foo and $bar))',
'inet:ipv4 +($(0 and 1))',
'$x=$($x-1)',
'inet:ipv4=1.2.3.4 +(:asn + 20 >= 42)',
]

# Generated with print_parse_list below
_ParseResults = [
'Query: [LiftByArray: [Const: test:array, Const: =, Const: 1.2.3.4]]',
'Query: [CmdOper: [Const: macro.set, List: [Const: hehe, EmbedQuery: inet:ipv4 ]]]',
'Query: [SetVarOper: [Const: q, EmbedQuery: #foo.bar]]',
'Query: [CmdOper: [Const: metrics.edits.byprop, List: [Const: inet:fqdn:domain, Const: --newv, VarDeref: [VarValue: [Const: lib], Const: null]]]]',
Expand Down Expand Up @@ -1011,7 +1020,7 @@
'Query: [SetVarOper: [Const: x, DollarExpr: [ExprNode: [ExprNode: [Const: 1, Const: *, Const: 3], Const: +, Const: 2]]]]',
'Query: [SetVarOper: [Const: x, DollarExpr: [ExprNode: [Const: 1, Const: -, ExprNode: [Const: 3.2, Const: /, Const: -3.2]]]]]',
'Query: [SetVarOper: [Const: x, DollarExpr: [ExprNode: [Const: 1, Const: +, ExprNode: [Const: 3, Const: /, Const: 2]]]]]',
'Query: [SetVarOper: [Const: x, DollarExpr: [ExprNode: [ExprNode: [Const: 1, Const: +, Const: 3], Const: /, Const: 2]]]]',
'Query: [SetVarOper: [Const: x, DollarExpr: [ExprNode: [DollarExpr: [ExprNode: [Const: 1, Const: +, Const: 3]], Const: /, Const: 2]]]]',
'Query: [SetVarOper: [Const: foo, Const: 42], SetVarOper: [Const: foo2, Const: 43], SetVarOper: [Const: x, DollarExpr: [ExprNode: [VarValue: [Const: foo], Const: *, VarValue: [Const: foo2]]]]]',
'Query: [SetVarOper: [Const: yep, DollarExpr: [ExprNode: [Const: 42, Const: <, Const: 43]]]]',
'Query: [SetVarOper: [Const: yep, DollarExpr: [ExprNode: [Const: 42, Const: >, Const: 43]]]]',
Expand Down Expand Up @@ -1069,7 +1078,13 @@
'Query: [LiftPropBy: [Const: inet:ipv4, Const: =, Const: 1.2.3.4], PivotToTags: [TagMatch: [Const: *]], isjoin=True]',
'Query: [LiftPropBy: [Const: inet:ipv4, Const: =, Const: 1.2.3.4], PivotToTags: [TagMatch: [Const: biz.*]], isjoin=True]',
'Query: [LiftPropBy: [Const: inet:ipv4, Const: =, Const: 1.2.3.4], PivotToTags: [TagMatch: [Const: bar.baz]], isjoin=True]',

'Query: [Function: [Const: middlechild, FuncArgs: [Const: arg2], Query: [YieldValu: [FuncCall: [VarValue: [Const: rockbottom], CallArgs: [VarValue: [Const: arg2]], CallKwargs: []]]]]]',
'Query: [EditNodeAdd: [FormName: [Const: test:comp], Const: =, List: [Const: 10, Const: bar]], SubQuery: [Query: [FormPivot: [AbsProp: test:int], isjoin=False]]]',
'Query: [LiftProp: [Const: test:arrayprop], FiltOper: [Const: +, ArrayCond: [RelProp: [Const: ints], Const: range=, List: [Const: 50, Const: 100]]]]',
'Query: [LiftProp: [Const: inet:ipv4], FiltOper: [Const: +, AndCond: [VarValue: [Const: foo], VarValue: [Const: bar]]]]',
'Query: [LiftProp: [Const: inet:ipv4], FiltOper: [Const: +, DollarExpr: [ExprAndNode: [Const: 0, Const: and, Const: 1]]]]',
'Query: [SetVarOper: [Const: x, DollarExpr: [ExprNode: [VarValue: [Const: x], Const: -, Const: 1]]]]',
'Query: [LiftPropBy: [Const: inet:ipv4, Const: =, Const: 1.2.3.4], FiltOper: [Const: +, DollarExpr: [ExprNode: [ExprNode: [RelPropValue: [Const: asn], Const: +, Const: 20], Const: >=, Const: 42]]]]',
]

class GrammarTest(s_t_utils.SynTest):
Expand All @@ -1084,7 +1099,10 @@ def test_grammar(self):
parser = lark.Lark(grammar, start='query', debug=True, ambiguity='explicit', keep_all_tokens=True,
propagate_positions=True)

for i, query in enumerate(_Queries):
for i, query in enumerate(Queries):
if i in (12, 13):
# For now, accept an ambiguity in _cond between _condexpr and dollarexpr
continue
try:
tree = parser.parse(query)
# print(f'#{i}: {query}')
Expand All @@ -1097,7 +1115,7 @@ def test_grammar(self):

async def test_parser(self):
self.maxDiff = None
for i, query in enumerate(_Queries):
for i, query in enumerate(Queries):
parser = s_parser.Parser(query)
tree = parser.query()
self.eq(str(tree), _ParseResults[i])
Expand Down Expand Up @@ -1245,7 +1263,7 @@ def gen_parse_list():
Prints out the Asts for a list of queries in order to compare ASTs between versions of parsers
'''
retn = []
for i, query in enumerate(_Queries):
for i, query in enumerate(Queries):
parser = s_parser.Parser(query)
tree = parser.query()
retn.append(str(tree))
Expand Down
4 changes: 2 additions & 2 deletions synapse/tests/test_lib_storm_format.py
@@ -1,9 +1,9 @@
import synapse.lib.storm_format as s_storm_format
import synapse.lib.parser as s_parser

from synapse.tests.test_lib_grammar import _Queries
from synapse.tests.test_lib_grammar import Queries

def test_highlight_storm():

for _, query in enumerate(_Queries):
for _, query in enumerate(Queries):
s_storm_format.highlight_storm(s_parser.LarkParser, query)

0 comments on commit a857825

Please sign in to comment.