Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 276 lines (227 sloc) 9.49 KB
#! /usr/bin/env python3
import sys
import lexer
from tree import Node, CompositeNode
class OperatorDesc:
def __init__(self, oper, lprio, rprio, evaluator):
self.oper = oper
self.lprio = lprio
self.rprio = rprio
self.evaluator = evaluator
def __repr__(self):
return '<Oper {} {}/{}>'.format(self.oper, self.lprio, self.rprio)
def prefix_unary_evaluator(parser):
oper = parser.operators_stack.pop()
parser.values_stack.append(CompositeNode(oper.oper, [parser.values_stack.pop()]))
def postfix_unary_evaluator(parser):
oper = parser.operators_stack.pop()
parser.values_stack.append(CompositeNode('post'+oper.oper, [parser.values_stack.pop()]))
def binary_evaluator(parser):
oper = parser.operators_stack.pop()
val2 = parser.values_stack.pop()
val1 = parser.values_stack.pop()
parser.values_stack.append(CompositeNode(oper.oper, [val1, val2]))
class Parser:
def __init__(self):
self.prefix_actions = {}
self.prefix_operators = {}
self.infix_operators = {}
self.postfix_actions = {}
self.postfix_operators = {}
self.reset()
def reset(self):
self.waiting_value = True
self.values_stack = []
self.operators_stack = []
def register_prefix_action(self, oper, action):
if type(oper) is str:
self.prefix_actions[oper] = action
else:
for op in oper:
self.prefix_actions[op] = action
def register_prefix_operator(self, oper, rprio, evaluator=None):
if evaluator is None:
evaluator = prefix_unary_evaluator
if type(oper) is str:
self.prefix_operators[oper] = OperatorDesc(oper, -1, rprio, evaluator)
else:
for op in oper:
self.prefix_operators[op] = OperatorDesc(op, -1, rprio, evaluator)
def register_infix_operator(self, oper, lprio, rprio, evaluator=None):
if evaluator is None:
evaluator = binary_evaluator
if type(oper) is str:
self.infix_operators[oper] = OperatorDesc(oper, lprio, rprio, evaluator)
else:
for op in oper:
self.infix_operators[op] = OperatorDesc(op, lprio, rprio, evaluator)
def register_postfix_action(self, oper, action):
if type(oper) is str:
self.postfix_actions[oper] = action
else:
for op in oper:
self.postfix_actions[op] = action
def register_postfix_operator(self, oper, lprio, evaluator=None):
if evaluator is None:
evaluator = postfix_unary_evaluator
if type(oper) is str:
self.postfix_operators[oper] = OperatorDesc(oper, lprio, -1, evaluator)
else:
for op in oper:
self.postfix_operators[op] = OperatorDesc(op, lprio, -1, evaluator)
def evaluate_operator(self):
self.operators_stack[-1].evaluator(self)
def push_operator(self, oper):
# prefix operators (marked with oper.lprio == -1) never trigger the evaluation of pending operators
if oper.lprio >= 0:
while len(self.operators_stack) > 0 and self.operators_stack[-1].rprio >= oper.lprio:
self.evaluate_operator()
self.operators_stack.append(oper)
# postfix operators (marked with oper.rprio == -1) are never put in pending state
if oper.rprio < 0:
self.evaluate_operator()
def parse_for_value(self, tk):
if tk.kind == 'NUMBER' or tk.kind == 'ID':
self.values_stack.append(Node(tk))
self.waiting_value = False
elif tk.lexem in self.prefix_actions:
self.prefix_actions[tk.lexem](self)
elif tk.lexem in self.prefix_operators:
self.push_operator(self.prefix_operators[tk.lexem])
else:
self.values_stack.append(Node(lexer.Token('', 'ERROR')))
self.parse_for_operator(tk)
def parse_for_operator(self, tk):
if tk.lexem in self.postfix_actions:
self.postfix_actions[tk.lexem](self)
elif tk.lexem in self.postfix_operators:
self.push_operator(self.postfix_operators[tk.lexem])
elif tk.lexem in self.infix_operators:
self.waiting_value = True
self.push_operator(self.infix_operators[tk.lexem])
else:
val = self.values_stack.pop()
self.values_stack.append(CompositeNode('MISSING OPERATOR', [val, Node(tk)]))
def parse(self, s):
self.reset()
for tk in lexer.tokenize(s):
if self.waiting_value:
self.parse_for_value(tk)
else:
self.parse_for_operator(tk)
if self.waiting_value:
self.dump()
raise RuntimeError('missing value')
while len(self.operators_stack) > 0:
self.evaluate_operator()
if len(self.values_stack) != 1:
raise RuntimeError('Internal error: value left on stack')
return self.values_stack.pop()
def dump(self):
print('Operator stack')
for oper in self.operators_stack:
print(' {}'.format(oper))
print('Value stack')
for val in self.values_stack:
print(' {}'.format(val))
def prefix_open_parenthesis(parser):
parser.dump()
raise RuntimeError('Unclosed open parenthesis')
def infix_open_parenthesis(parser):
parser.dump()
raise RuntimeError('Unclosed open parenthesis')
def prefix_close_parenthesis(parser):
op1 = parser.operators_stack.pop()
if op1.oper != '(' or op1.evaluator != infix_open_parenthesis:
parser.dump()
raise RuntimeError('Empty parenthesis')
val1 = parser.values_stack.pop()
parser.values_stack.append(CompositeNode('call', [val1]))
parser.waiting_value = False
def postfix_close_parenthesis(parser):
op1 = parser.operators_stack.pop()
op2 = parser.operators_stack.pop()
if op2.oper != '(':
parser.dump()
raise RuntimeError('Unopened close parenthesis')
if op2.evaluator == infix_open_parenthesis:
val1 = parser.values_stack.pop()
val2 = parser.values_stack.pop()
if type(val1) == CompositeNode and val1.token == ',':
args = [val2] + val1.children
else:
args = [val2, val1]
parser.values_stack.append(CompositeNode('call', args))
else:
parser.values_stack[-1].parenthesis = True
def infix_question(parser):
parser.dump()
raise RuntimeError('? without :')
def infix_colon(parser):
op1 = parser.operators_stack.pop()
op2 = parser.operators_stack.pop()
if op2.oper != '?':
parser.dump()
raise RuntimeError(': wihout ?')
val1 = parser.values_stack.pop()
val2 = parser.values_stack.pop()
val3 = parser.values_stack.pop()
parser.values_stack.append(CompositeNode('?', [val3, val2, val1]))
def infix_open_brackets(parser):
parser.dump()
raise RuntimeError('Unclosed open bracket')
def postfix_close_brackets(parser):
op1 = parser.operators_stack.pop()
op2 = parser.operators_stack.pop()
if op2.oper != '[' or op2.evaluator != infix_open_brackets:
parser.dump()
raise RuntimeError('Unopened close bracket')
val1 = parser.values_stack.pop()
val2 = parser.values_stack.pop()
parser.values_stack.append(CompositeNode('get', [val2, val1]))
def coma_evaluator(parser):
oper = parser.operators_stack.pop()
val2 = parser.values_stack.pop()
val1 = parser.values_stack.pop()
if type(val1) is CompositeNode and val1.token == ',' and not val1.parenthesis:
val1.children.append(val2)
parser.values_stack.append(val1)
else:
parser.values_stack.append(CompositeNode(oper.oper, [val1, val2]))
def cexp_parser():
parser = Parser()
parser.register_prefix_operator('(', 0, prefix_open_parenthesis)
parser.register_postfix_operator(')', 1, postfix_close_parenthesis)
parser.register_prefix_action(')', prefix_close_parenthesis)
parser.register_postfix_operator(']', 1, postfix_close_brackets)
parser.register_infix_operator(',', 2, 2, coma_evaluator)
parser.register_infix_operator(['=', '*=', '/=', '%=', '+=', '-=', '<<=', '>>=', '&=', '|=', '^='], 4, 3)
parser.register_infix_operator('?', 6, 0, infix_question)
parser.register_infix_operator(':', 1, 5, infix_colon)
parser.register_infix_operator('||', 8, 8)
parser.register_infix_operator('&&', 10, 10)
parser.register_infix_operator('|', 12, 12)
parser.register_infix_operator('^', 14, 14)
parser.register_infix_operator('&', 16, 16)
parser.register_infix_operator(['==', '!='], 18, 18)
parser.register_infix_operator(['<', '>', '<=', '>='], 20, 20)
parser.register_infix_operator(['<<', '>>'], 22, 22)
parser.register_infix_operator(['+', '-'], 24, 24)
parser.register_infix_operator(['*', '/', '%'], 26, 26)
parser.register_infix_operator('**', 28, 27)
parser.register_prefix_operator(['+', '-', '++', '--', '&', '*', '~', '!'], 30)
parser.register_postfix_operator(['++', '--'], 32)
parser.register_infix_operator(['.', '->'], 32, 32)
parser.register_infix_operator('(', 32, 0, infix_open_parenthesis)
parser.register_infix_operator('[', 32, 0, infix_open_brackets)
return parser
def main(args):
parser = cexp_parser()
for s in args[1:]:
try:
exp = parser.parse(s)
print('{} -> {}'.format(s, exp))
except RuntimeError as run_error:
print('Unable to parse {}: {}'.format(s, run_error))
if __name__ == "__main__":
main(sys.argv)