diff --git a/lua/parser-smoke.sh b/lua/parser-smoke.sh index d6882ec7..2f9ee69f 100755 --- a/lua/parser-smoke.sh +++ b/lua/parser-smoke.sh @@ -15,12 +15,12 @@ function error_exit_bad function good { - lua test.lua "$1" || error_exit_good "$1" + lua test.lua "$1" 2> /dev/null || error_exit_good "$1" } function bad { - lua test.lua "$1" && error_exit_bad "$1" + lua test.lua "$1" 2> /dev/null && error_exit_bad "$1" } # Filters @@ -47,7 +47,6 @@ good "a.b = 'bla'" good "a.b = not" good "a.b contains bla" good "a.b icontains 'bla'" -good "a.g in ()" good "a.g in (1, 'a', b)" good "a.g in ( 1 ,, , b)" good "evt.dir=> and fd.name=*.log" @@ -55,6 +54,7 @@ good "evt.dir=> and fd.name=/var/log/httpd.log" good "a.g in (1, 'a', b.c)" good "a.b = a.a" +bad "a.g in ()" bad "(a.b = 1" # Macros @@ -66,4 +66,6 @@ good "a : evt.dir=>" good "inbound: (syscall.type=listen and evt.dir='>') or (syscall.type=accept and evt.dir='<')" bad "a:" +echo +echo "All tests passed." exit 0 diff --git a/lua/sysdig-parser.lua b/lua/sysdig-parser.lua index 55b09366..2a279b13 100644 --- a/lua/sysdig-parser.lua +++ b/lua/sysdig-parser.lua @@ -237,6 +237,74 @@ local G = { OneWord = V"Name" + V"Number" + V"String" + P(1); } +function map(f, arr) + local res = {} + for i,v in ipairs(arr) do + res[i] = f(v) + end + return res +end + +function foldr(f, acc, arr) + for i,v in pairs(arr) do + acc = f(acc, v) + end + return acc +end + +--[[ + Traverses the AST and replaces `in` relational expressions with a sequence of ORs. + + For example, `a.b in [1, 2]` is expanded to `a.b = 1 or a.b = 2` (in ASTs) +]]-- +function expand_in(node) + local t = node.type + + if t == "Filter" then + expand_in(node.value) + + elseif t == "UnaryBoolOp" then + expand_in(node.argument) + + elseif t == "BinaryBoolOp" then + expand_in(node.left) + expand_in(node.right) + + elseif t == "BinaryRelOp" and node.operator == "in" then + if (table.maxn(node.right.elements) == 0) then + error ("In list with zero elements") + end + + local mapper = function(element) + return { + type = "BinaryRelOp", + operator = "eq", + left = node.left, + right = element + } + end + + local equalities = map(mapper, node.right.elements) + local lasteq = equalities[table.maxn(equalities)] + equalities[table.maxn(equalities)] = nil + + local folder = function(left, right) + return { + type = "BinaryBoolOp", + operator = "or", + left = left, + right = right + } + end + lasteq = foldr(folder, lasteq, equalities) + + node.type=lasteq.type + node.operator=lasteq.operator + node.left=lasteq.left + node.right=lasteq.right + end +end + function print_ast(node, level) local t = node.type local prefix = string.rep(" ", level*2) @@ -276,6 +344,10 @@ function parser.parse (subject) local errorinfo = { subject = subject } lpeg.setmaxstack(1000) local ast, error_msg = lpeg.match(G, subject, nil, errorinfo) + if (error_msg) then + return ast, error_msg + end + expand_in(ast) return ast, error_msg end