Implemented interpreter, almost fully documented

This commit is contained in:
hellerve
2015-07-08 16:38:49 +02:00
parent a96a5fe1f7
commit 9d4406907a
6 changed files with 137 additions and 22 deletions

View File

@@ -142,7 +142,7 @@ class BinArithmeticExp(ArithmeticExp):
left_value = self.left.eval(env) left_value = self.left.eval(env)
right_value = self.right.eval(env) right_value = self.right.eval(env)
try: try:
return operators[self.op](left_value, right_value) return self.operators[self.op](left_value, right_value)
except KeyError: except KeyError:
raise RuntimeError('unknown operator: ' + self.op) raise RuntimeError('unknown operator: ' + self.op)
@@ -150,10 +150,10 @@ class RelationExp(BooleanExp):
"""The AST node for relational boolean expressions.""" """The AST node for relational boolean expressions."""
operators = { operators = {
'<': operator.gt, '<': operator.lt,
'<=': operator.ge, '<=': operator.le,
'>': operator.lt, '>': operator.gt,
'>=': operator.le, '>=': operator.ge,
'=': operator.eq, '=': operator.eq,
'!=': operator.ne, '!=': operator.ne,
} }
@@ -191,7 +191,7 @@ class RelationExp(BooleanExp):
left_value = self.left.eval(env) left_value = self.left.eval(env)
right_value = self.right.eval(env) right_value = self.right.eval(env)
try: try:
return operators[self.op](left_value, right_value) return self.operators[self.op](left_value, right_value)
except KeyError: except KeyError:
raise RuntimeError('unknown operator: ' + self.op) raise RuntimeError('unknown operator: ' + self.op)

View File

@@ -1,2 +1,27 @@
def callIMP(chars): """The interpreter"""
from .parser import parse
from .tokenize import tokenize
class ParseError(Exception):
pass pass
def callIMP(chars):
"""
The entry point for everyone who wants to call
IMP from within Python. The function either
returns the value or None, if the call did not
succeed.
chars -- the string to evaluate
returns -- the IMP environment | None
"""
if not chars: return None
tokens = tokenize(chars)
if not tokens: raise ParseError("tokens could not be generated")
parsed = parse(tokens)
if not parsed or not parsed.value: raise ParseError("tokens could not be parsed")
ast = parsed.value
env = {}
ast.eval(env)
return env

View File

@@ -26,7 +26,8 @@ def lex(characters, token_exprs):
tokens.append(token) tokens.append(token)
break break
if not match: if not match:
sys.stderr.write('[Parser] Illegal character: %s\\n' % characters[pos]) sys.stderr.write('[Lexer] Illegal character at %d: %s(%d)\n'
% (pos, characters[pos], ord(characters[pos])))
raise ValueError(characters[pos]) raise ValueError(characters[pos])
else: else:
pos = match.end(0) pos = match.end(0)

View File

@@ -17,22 +17,52 @@ BOOLEAN_PRECEDENCE = [
] ]
#Helper #Helper
def precedence(value_parser, precedence_levels, combine): def precedence(value_parser, precedences, combine):
def op_parser(precedence_level): def op_parser(precedence):
return any_op_in_list(precedence_level) ^ combine return any_op_in_list(precedence) ^ combine
parser = value_parser * op_parser(precedence_levels[0]) parser = value_parser * op_parser(precedences[0])
for precedence_level in precedence_levels[1:]: for precedence in precedences[1:]:
parser = parser * op_parser(precedence_level) parser = parser * op_parser(precedence)
return parser return parser
def process_binop(op): def process_binop(op):
"""
Takes a binary operation as input and
returns a function that build AST nodes
given the operands.
op -- the operation
returns -- a function that takes two operands
and returns a Binary Arithmetic AST
node
"""
return lambda l, r: BinArithmeticExp(op, l, r) return lambda l, r: BinArithmeticExp(op, l, r)
def process_relop(parsed): def process_relop(parsed):
""""
Takes a parsed relational operation and returns
an AST node.
parsed -- a tuple of the form ((left, operation), right)
returns -- an AST node representing the expression
"""
((left, op), right) = parsed ((left, op), right) = parsed
return RelationExp(op, left, right) return RelationExp(op, left, right)
def process_logic(op): def process_logic(op):
"""
Takes a logical operation and returns a function
consuming two operands, building an AST node from them.
op -- the operation
returns -- a function that builds Logical Expression
AST nodes
throws -- RuntimeError on unknown operator
"""
if op == 'and': if op == 'and':
return lambda l, r: AndExp(l, r) return lambda l, r: AndExp(l, r)
elif op == 'or': elif op == 'or':
@@ -45,46 +75,105 @@ def process_group(parsed):
return p return p
def any_op_in_list(ops): def any_op_in_list(ops):
"""
Builds a keyword parser from all given operations.
ops -- a list of operators
returns -- a parser matching all operators
"""
op_parsers = [keyword(op) for op in ops] op_parsers = [keyword(op) for op in ops]
parser = reduce(lambda l, r: l | r, op_parsers) return reduce(lambda l, r: l | r, op_parsers)
return parser
#Parser #Parser
num = Tag(INT) ^ (lambda i: int(i)) num = Tag(INT) ^ (lambda i: int(i))
imp_id = Tag(ID) imp_id = Tag(ID)
def keyword(kw): def keyword(kw):
"""
The keyword parser. Takes a keyword
and returns a Reserved AST node.
kw -- the operation as string
returns -- a corresponding Reserved AST node
"""
return Reserved(kw, RESERVED) return Reserved(kw, RESERVED)
def arithmetic_group(): def arithmetic_group():
"""
Matches groups of arithmetic operations.
returns -- a parser matching grouped arithmetic operations
"""
return keyword('(') + Lazy(arithmetic_exp) + keyword(')') ^ process_group return keyword('(') + Lazy(arithmetic_exp) + keyword(')') ^ process_group
def arithmetic_value(): def arithmetic_value():
"""
Matches numbers and variables.
returns -- a parser matching all kinds of values
"""
return ((num ^ (lambda i: IntArithmeticExp(i))) | return ((num ^ (lambda i: IntArithmeticExp(i))) |
(id ^ (lambda v: VarArithmeticExp(v)))) (imp_id ^ (lambda v: VarArithmeticExp(v))))
def arithmetic_term(): def arithmetic_term():
"""
Matches an arithmetic term.
returns -- a parser matching an arithmetic value or group
"""
return arithmetic_value() | arithmetic_group() return arithmetic_value() | arithmetic_group()
def arithmetic_exp(): def arithmetic_exp():
"""
Matches arithmetic expressions, valuing precedence.
returns -- a parser correctly matching arithmetic expressions
"""
return precedence(arithmetic_term(), return precedence(arithmetic_term(),
ARITHMETIC_PRECEDENCE, ARITHMETIC_PRECEDENCE,
process_binop) process_binop)
def boolean_not(): def boolean_not():
"""
Matches the boolean not with its arguments.
returns -- a parser matching the boolean not
"""
return keyword('not') + Lazy(boolean_term) ^ (lambda parsed: NotExp(parsed[1])) return keyword('not') + Lazy(boolean_term) ^ (lambda parsed: NotExp(parsed[1]))
def boolean_relop(): def boolean_relop():
"""
Matches boolean relational operations.
returns -- a parser matching all relations
"""
relops = ['<', '<=', '>', '>=', '=', '!='] relops = ['<', '<=', '>', '>=', '=', '!=']
return arithmetic_exp() + any_op_in_list(relops) + arithmetic_exp() ^ process_relop return arithmetic_exp() + any_op_in_list(relops) + arithmetic_exp() ^ process_relop
def boolean_group(): def boolean_group():
"""
Matches grouped boolean expressions
returns -- a parser matching groups of boolean expressions
"""
return keyword('(') + Lazy(boolean_exp) + keyword(')') ^ process_group return keyword('(') + Lazy(boolean_exp) + keyword(')') ^ process_group
def boolean_term(): def boolean_term():
"""
Matches boolean terms.
returns -- a parser matching boolean terms
"""
return boolean_not() | boolean_relop() | boolean_group() return boolean_not() | boolean_relop() | boolean_group()
def boolean_exp(): def boolean_exp():
"""
Matches boolean expressing, valuing precedence.
returns -- a parser correctly matching arithmetic expressions
"""
return precedence(boolean_term(), return precedence(boolean_term(),
BOOLEAN_PRECEDENCE, BOOLEAN_PRECEDENCE,
process_logic) process_logic)
@@ -126,5 +215,5 @@ def statements():
def parser(): def parser():
return Phrase(statements()) return Phrase(statements())
def imp_parser(tokens): def parse(tokens):
return parser()(tokens, 0) return parser()(tokens, 0)

View File

@@ -91,7 +91,7 @@ class Reserved(Parser):
returns -- a Result object | None returns -- a Result object | None
""" """
if pos < len(tokens) and tokens[pos] == [self.value, self.tag]: if pos < len(tokens) and tokens[pos][0] == self.value and tokens[pos][1] is self.tag:
return Result(tokens[pos][0], pos + 1) return Result(tokens[pos][0], pos + 1)
else: else:
return None return None
@@ -170,7 +170,7 @@ class Alternate(Parser):
def __call__(self, tokens, pos): def __call__(self, tokens, pos):
""" """
Calls the parser. Returns a result if either Calls the parser. Returns a result if either
parser matches, otherwisu returns None. parser matches, otherwise returns None.
tokens -- the token list tokens -- the token list
pos -- the position to check pos -- the position to check

View File

@@ -6,8 +6,8 @@ INT = 'INT'
ID = 'ID' ID = 'ID'
TOKENS = [ TOKENS = [
(r'[ \\n\\t]+', None), (r'[ \n\t]+', None),
(r'#[^\\n]*', None), (r'#[^\n]*', None),
(r'\:=', RESERVED), (r'\:=', RESERVED),
(r'\(', RESERVED), (r'\(', RESERVED),
(r'\)', RESERVED), (r'\)', RESERVED),