1. Ronny Pfannschmidt
  2. whenever-py

Commits

Ronny Pfannschmidt  committed 7268182

move parsing to extra file, begin evaluator

  • Participants
  • Parent commits b136aa1
  • Branches default

Comments (0)

Files changed (4)

File testing/test_parse_examples.py

View file
 import py
-from whenever import parse
+from whenever import parse, Runner
 examples = py.path.local(__file__).join('../../examples/')
 
 def pytest_generate_tests(metafunc):
 
 def test_parse_example(path):
     parse(str(path))
+
+
+def test_run_example(path):
+    statements = parse(str(path))
+    runner = Runner(statements)
+    runner.run()
+

File whenever/__init__.py

View file
-from pypy.rlib.streamio import open_file_as_stream
 
-from pypy.rlib.parsing.ebnfparse import parse_ebnf, make_parse_function
-from pypy.rlib.parsing.deterministic import LexerError
-from pypy.rlib.parsing.parsing import ParseError
+from .parse import parse, parse_command
+from .eval import Evaluator
+from pypy.rlib.rrandom import Random
 
-
-def parse(filename):
-    result = {}
-
-    fp = open_file_as_stream(filename)
-    while True:
-        line = fp.readline()
-        if len(line) == 0:
-            break
-        number, commands = parseline(line)
-        assert number not in result
-        result[number] = commands
-
-
-
-def parseline(line):
-    number_str, commands = line.split(' ', 1)
-    print commands
-    try:
-        return int(number_str), parse_command(commands[:-1])
-    except LexerError, e:
-        print e.args[0], e.nice_error_message()
-        print commands
-        print ' '*e.args[2].i + '^'
-        raise
-    except ParseError, e:
-        print e.args[0], e.nice_error_message()
-        print commands
-        print ' '*e.args[0].i + '^'
-        raise
-
-regexs, rules, ToAST = parse_ebnf(
-"""
-IGNORE: " ";
-DECIMAL: "0|[1-9][0-9]*";
-STRING: "\\"[^\\\\"]*\\"";
-
-RPAR: "\)";
-LPAR: "\(";
-PLUS: "\+";
-STAR: "\*";
-FUNC: "N";
-
-EQ: "==";
-LT: "<";
-GT: ">";
-LTE: "<=";
-GTE: ">=";
-
-SHARP: "#";
-COMMA: ",";
-SEMICOLON: ";";
-
-OR: "\|\|";
-AND: "&&";
-
-command:  op SEMICOLON;
-op: action+ statements | action+ | statements;
-
-
-
-integer: DECIMAL | "-" DECIMAL;
-number: integer| function | STRING;
-
-statement: integer [SHARP] expr | integer;
-statements: (statement [COMMA])* statement;
-
-
-
-function: "N" [LPAR] expr [RPAR];
-
-addition: (number [PLUS])* number;
-expr: addition|number;
-
-comparisation: EQ | LT | GT | LTE | GTE;
-compare: expr comparisation expr;
-
-
-bool: compare | expr;
-chain: AND |OR;
-boolean: (bool chain)* bool;
-
-
-ACTION: "defer|again|forget";
-PRINT: "print";
-action: ACTION [LPAR] boolean [RPAR] | PRINT [LPAR] expr [RPAR];
-
-""")
-
-
-parse_command = make_parse_function(regexs, rules)
-
-
-class Command(object):
-    def __init__(self, tree):
-        self.tree = tree
+random = Random()
 
 
 class Runner(object):
+    __slots__ = 'statements', 'todo'
     def __init__(self, statements):
+        assert isinstance(statements, dict)
         self.statements = statements
+        self.todo = list(statements.keys())
+
     def run(self):
-        pass
+
+        while len(self.todo):
+            rand = random.random()
+            l = len(self.todo)
+
+            number = int(l*rand)
+
+            command = self.todo.pop(number)
+
+            evaluator = Evaluator(self.todo)
+            evaluator.handle(self.statements[command])
+            if evaluator.keep:
+                self.todo.append(number)

File whenever/eval.py

View file
+from pypy.rlib.parsing.tree import Nonterminal
+
+
+
+class W_Object(object):
+    __slots__ = ()
+
+
+class W_Int(W_Object):
+    __slots__ = 'intval',
+    def __init__(w_self, intval):
+        w_self.intval = intval
+
+    def __repr__(self):
+        return '(int %s)' % self.intval
+
+class W_String(W_Object):
+    __slots__ = 'strval',
+    def __init__(w_self, strval):
+        w_self.strval = strval
+
+class W_Action(W_Object):
+    __slots__ = 'name',
+    def __init__(w_self, name):
+        w_self.name = name
+
+
+
+class W_Compare(W_Object):
+    __slots__ = 'op',
+    def __init__(w_self, op):
+        w_self.op = op
+
+
+class W_Bool(W_Object):
+    __slots__ = 'value',
+    def __init__(self, value):
+        self.value = value
+
+class Evaluator(object):
+    __slots__ = 'todo', 'resultstate', 'stack', 'node_stack', 'keep'
+
+    def __init__(self, todo):
+        self.todo = todo
+        self.stack = []
+        self.node_stack = []
+        self.keep = False
+
+    def handle(self, node):
+        #print ' '*len(self.node_stack), node.symbol
+        self.node_stack.append(node)
+        if isinstance(node, Nonterminal):
+            #print node.children
+            for child in node.children:
+                self.handle(child)
+
+
+        #print self.stack
+        #print [x.symbol for x in self.node_stack]
+        if not node.symbol[0] == '_':
+            handler = self.dispatch[node.symbol]
+            handler(self, node)
+        self.node_stack.pop()
+
+    def ignore(self, node):
+        pass
+
+    handle_COMMA = handle_LPAR = handle_RPAR = \
+            handle_SEMICOLON = handle_PRINT = handle_PLUS = \
+            handle_SHARP = ignore
+
+    def handle_ACTION(self, node):
+        self.stack.append(W_Action(node.token.source))
+
+    def handle_DECIMAL(self, node):
+        self.stack.append(W_Int(int(node.token.source)))
+
+    def handle_STRING(self, node):
+        self.stack.append(W_String(node.token.source[1:-1])) # unescape?
+
+    def handle_LT(self, node): self.stack.append(W_Compare(lambda a,b: a<b))
+    def handle_GT(self, node): self.stack.append(W_Compare(lambda a,b: a>b))
+    def handle_LTE(self, node): self.stack.append(W_Compare(lambda a,b: a<=b))
+
+    def handle_OR(self, node): self.stack.append(W_Compare(lambda a,b: a | b))
+
+    def handle_integer(self, node):
+        if len(node.children) == 2:
+            w_int = self.stack[-1]
+            assert isinstance(w_int, W_Int)
+            w_int.intval = -w_int.intval
+
+    handle_comparisation = handle_chain = ignore
+    def handle_compare(self, node):
+        a = self.stack.pop()
+        comp = self.stack.pop()
+        b = self.stack.pop()
+
+        assert isinstance(a, W_Int)
+        assert isinstance(b, W_Int)
+        assert isinstance(comp, W_Compare)
+        self.stack.append(W_Bool(comp.op(a.intval, b.intval)))
+
+    def handle_statement(self, node):
+        def remove(n):
+            i = self.todo.index(n)
+            if i >= 0:
+                del self.todo[i]
+
+        if len(node.children) == 1:
+            number = self.stack.pop()
+            n = number.intval
+            if n < 0:
+                remove(-n)
+            else:
+                self.todo.append(n)
+        else:
+            b_count = self.stack.pop()
+            b_number = self.stack.pop()
+            count = b_count.intval
+            number = b_number.intval
+            if number < 0:
+                count = -count
+                number = -number
+            if count < 0:
+                for i in range(count):
+                    remove(number)
+            else:
+                for i in range(count):
+                    self.todo.append(number)
+
+
+
+    def handle_addition(self, node):
+        if len(node.children) == 1:
+            return
+        #XXX STRINGS
+        a = self.stack.pop()
+        b = self.stack.pop()
+        assert isinstance(a, W_Int)
+        assert isinstance(b, W_Int)
+        self.stack.append(W_Int(a.intval+b.intval))
+
+
+    def handle_function(self, node):
+        #XXX: its n
+        w_int = self.stack.pop()
+        assert isinstance(w_int, W_Int)
+        count = 0
+        for i in range(len(self.todo)):
+            if self.todo[i] == w_int.intval:
+                count += 1
+        self.stack.append(W_Int(count))
+
+    def handle_expr(self, node):
+        pass
+
+    def handle_bool(self, node):
+        val = self.stack.pop()
+        if isinstance(val, W_Int):
+            self.stack.append(W_Bool(bool(val.intval)))
+        else:
+            self.stack.append(val)
+
+    def handle_boolean(self, node):
+        pass
+
+    def handle_statements(self, node):
+        pass
+
+    handle_number = handle_op = ignore
+
+    def handle_action(self, node):
+        if len(self.stack) >=2 and isinstance(self.stack[-2], W_Action):
+            truth = self.stack.pop()
+            assert isinstance(truth, W_Bool)
+            action = self.stack.pop()
+            if not truth.value:
+                return
+
+            if  action == 'defer':
+                self.keep = True
+                raise StopIteration
+            elif action=='forget':
+                self.keep = False
+                raise StopIteration
+        else:
+            maybe_text = self.stack.pop()
+            if isinstance(maybe_text, W_Int):
+                print maybe_text.intval
+            elif isinstance(maybe_text, W_String):
+                print maybe_text.strval
+
+
+
+    def handle_command(self, node):
+        pass
+
+    dispatch = {}
+    for name, function in locals().items():
+        if name.startswith('handle_'):
+            dispatch[name[7:]] = function
+
+    del name, function
+

File whenever/parse.py

View file
+
+from pypy.rlib.streamio import open_file_as_stream
+
+from pypy.rlib.parsing.ebnfparse import parse_ebnf, make_parse_function
+from pypy.rlib.parsing.deterministic import LexerError
+from pypy.rlib.parsing.parsing import ParseError
+
+
+def parse(filename):
+    result = {}
+
+    fp = open_file_as_stream(filename)
+    while True:
+        line = fp.readline()
+        if len(line) == 0:
+            break
+        number, commands = parseline(line)
+        assert number not in result
+        result[number] = commands
+    return result
+
+
+
+def parseline(line):
+    number_str, commands = line.split(' ', 1)
+    try:
+        return int(number_str), parse_command(commands[:-1])
+    except LexerError, e:
+        print e.args[0], e.nice_error_message()
+        print commands
+        print ' '*e.args[2].i + '^'
+        raise
+    except ParseError, e:
+        print e.args[0], e.nice_error_message()
+        print commands
+        print ' '*e.args[0].i + '^'
+        raise
+
+regexs, rules, ToAST = parse_ebnf(
+"""
+IGNORE: " ";
+DECIMAL: "0|[1-9][0-9]*";
+STRING: "\\"[^\\\\"]*\\"";
+
+RPAR: "\)";
+LPAR: "\(";
+PLUS: "\+";
+STAR: "\*";
+FUNC: "N";
+
+EQ: "==";
+LT: "<";
+GT: ">";
+LTE: "<=";
+GTE: ">=";
+
+SHARP: "#";
+COMMA: ",";
+SEMICOLON: ";";
+
+OR: "\|\|";
+AND: "&&";
+
+command:  <op> [ SEMICOLON ];
+op: action+ statements | action+ | statements;
+
+
+
+integer: DECIMAL | "-" DECIMAL;
+number: integer| function | STRING;
+
+statement: integer [SHARP] expr | integer;
+statements: (statement COMMA)* statement;
+
+
+
+function: "N" [LPAR] expr [RPAR];
+
+addition: (number [PLUS])* number;
+expr: addition|number;
+
+comparisation: EQ | LT | GT | LTE | GTE;
+compare: expr comparisation expr;
+
+
+bool: compare | expr;
+chain: AND |OR;
+boolean: (bool chain)* bool;
+
+
+ACTION: "defer|again|forget";
+PRINT: "print";
+action: ACTION [LPAR] boolean [RPAR] | PRINT [LPAR] expr [RPAR];
+
+""")
+
+
+parse_command = make_parse_function(regexs, rules)