Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F33100260
rules.py
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
4 KB
Referenced Files
None
Subscribers
None
rules.py
View Options
import
re
# XXX: Since we are stuck with Python 3.9, we can't disable backtrack yet.
# https://docs.python.org/3/library/re.html
patterns
=
{
'OP'
:
re
.
compile
(
r'\s*(==|\|\||&&|=~|\!=|\!~|\!)'
),
'PAREN_LEFT'
:
re
.
compile
(
r'\s*\('
),
'PAREN_RIGHT'
:
re
.
compile
(
r'\s*\)'
),
'STR_DOUBLE'
:
re
.
compile
(
r'''\s*"((?:[^\\"]|\\.)*)"'''
),
'STR_SINGLE'
:
re
.
compile
(
r"""\s*'((?:[^\\']|\\.)*)'"""
),
'REGEX'
:
re
.
compile
(
r'''\s*/((?:[^\\/]|\\.)*)/'''
),
'VAR'
:
re
.
compile
(
r'\s*\$([A-Za-z_][A-Za-z0-9_]*)'
),
'NULL'
:
re
.
compile
(
r'\s*null'
),
'END'
:
re
.
compile
(
r'\s*$'
),
}
ops
=
{
'=='
:
(
2
,
lambda
a
,
b
:
a
==
b
),
'!='
:
(
2
,
lambda
a
,
b
:
a
!=
b
),
'=~'
:
(
2
,
lambda
a
,
b
:
not
not
re
.
search
(
b
,
a
)),
'!~'
:
(
2
,
lambda
a
,
b
:
not
re
.
search
(
b
,
a
)),
'!'
:
(
1
,
lambda
a
:
not
a
),
'&&'
:
(
2
,
lambda
a
,
b
:
a
and
b
),
'||'
:
(
2
,
lambda
a
,
b
:
a
or
b
),
}
TERM_PRECEDENCE
=
9
PAREN_PRECEDENCE
=
8
def
is_term
(
token
):
return
token
[
0
]
in
[
'STR_DOUBLE'
,
'STR_SINGLE'
,
'REGEX'
,
'VAR'
,
'NULL'
]
def
get_precedence
(
token
):
if
is_term
(
token
):
return
TERM_PRECEDENCE
elif
token
[
0
]
in
[
'PAREN_LEFT'
,
'PAREN_RIGHT'
]:
return
PAREN_PRECEDENCE
elif
token
[
0
]
==
'OP'
and
(
token
[
1
][
0
]
in
[
'=='
,
'!='
,
'=~'
,
'!~'
]):
return
6
elif
token
[
0
]
==
'OP'
and
(
token
[
1
][
0
]
in
[
'!'
]):
return
5
elif
token
[
0
]
==
'OP'
and
(
token
[
1
][
0
]
in
[
'&&'
]):
return
4
elif
token
[
0
]
==
'OP'
and
(
token
[
1
][
0
]
in
[
'||'
]):
return
3
raise
SyntaxError
(
'Unknown token'
)
def
tokenize_rule
(
rule_str
):
pos
=
0
tokenized
=
[]
while
not
(
len
(
tokenized
)
and
tokenized
[
-
1
][
0
]
==
'END'
):
match
=
None
for
t
in
patterns
:
regex
=
patterns
[
t
]
match
=
regex
.
match
(
rule_str
,
pos
)
if
match
:
tokenized
.
append
((
t
,
match
.
groups
()))
pos
=
match
.
end
()
break
if
not
match
:
raise
SyntaxError
(
f
'Bad rule "{rule_str}", at pos {pos}'
)
return
tokenized
[:
-
1
]
def
make_tree
(
tokenized
):
# https://en.wikipedia.org/wiki/Shunting_yard_algorithm
stack
=
[]
res
=
[]
for
t
in
tokenized
:
if
is_term
(
t
):
res
.
append
(
t
)
elif
t
[
0
]
==
'OP'
:
if
len
(
stack
)
and
stack
[
-
1
][
0
]
==
'OP'
and
get_precedence
(
stack
[
-
1
])
>=
get_precedence
(
t
):
res
.
append
(
stack
.
pop
())
stack
.
append
(
t
)
elif
t
[
0
]
==
'PAREN_LEFT'
:
stack
.
append
(
t
)
elif
t
[
0
]
==
'PAREN_RIGHT'
:
while
len
(
stack
)
and
stack
[
-
1
][
0
]
!=
'PAREN_LEFT'
:
res
.
append
(
stack
.
pop
())
if
not
len
(
stack
):
raise
SyntaxError
(
'Mismatched parentheses'
)
stack
.
pop
()
while
len
(
stack
):
t
=
stack
.
pop
()
if
t
[
0
]
==
'PAREN_LEFT'
:
raise
SyntaxError
(
'Mismatched parentheses'
)
res
.
append
(
t
)
stack
=
[]
for
t
in
res
:
if
t
[
0
]
==
'OP'
:
opname
=
t
[
1
][
0
]
arity
=
ops
[
opname
][
0
]
if
len
(
stack
)
<
arity
:
raise
SyntaxError
(
'Missing operands'
)
operands
=
tuple
(
reversed
([
stack
.
pop
()
for
i
in
range
(
arity
)]))
stack
.
append
((
t
[
0
],
(
opname
,)
+
operands
))
else
:
stack
.
append
(
t
)
if
len
(
stack
)
!=
1
:
raise
SyntaxError
(
'Too many operands'
)
return
stack
[
0
]
def
parse_rule
(
rule_str
):
return
make_tree
(
tokenize_rule
(
rule_str
))
backslash_re
=
re
.
compile
(
r'\\(.)'
)
def
replace_backslash
(
match
):
c
=
match
.
groups
()[
0
]
return
c
def
replace_backslash_in_regex
(
match
):
c
=
match
.
groups
()[
0
]
if
c
==
'/'
:
return
c
# Treat everything except \/ as is
return
'
\\
'
+
c
def
evaluate_rule
(
expr
,
variables
):
if
expr
[
0
]
==
'VAR'
:
return
variables
.
get
(
expr
[
1
][
0
])
elif
expr
[
0
]
==
'STR_DOUBLE'
or
expr
[
0
]
==
'STR_SINGLE'
:
return
backslash_re
.
sub
(
replace_backslash
,
expr
[
1
][
0
])
elif
expr
[
0
]
==
'REGEX'
:
return
backslash_re
.
sub
(
replace_backslash_in_regex
,
expr
[
1
][
0
])
elif
expr
[
0
]
==
'NULL'
:
return
None
elif
expr
[
0
]
==
'OP'
:
opname
=
expr
[
1
][
0
]
operands
=
[
evaluate_rule
(
o
,
variables
)
for
o
in
expr
[
1
][
1
:]]
return
ops
[
opname
][
1
](
*
operands
)
raise
SyntaxError
(
f
'Cannot evaluate expression {expr}'
)
File Metadata
Details
Attached
Mime Type
text/x-python
Expires
Tue, Jan 20, 12:08 PM (2 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
971961
Default Alt Text
rules.py (4 KB)
Attached To
Mode
rB lilybuild
Attached
Detach File
Event Timeline
Log In to Comment