diff --git a/builtins.simple b/builtins.simple index e92a489..95d3387 100644 --- a/builtins.simple +++ b/builtins.simple @@ -9,6 +9,8 @@ def pass: object inttrait: def eq(other): self $int_eq(other) + def noteq(other): + self $int_eq(other) not def leq(other): self $int_leq(other) @@ -31,6 +33,8 @@ object inttrait: self $int_mod(other) def inc: self $int_inc + def dec: + self $int_dec def tobool: self $int_tobool diff --git a/de.churl.simple/bytecodeinterpreter.py b/de.churl.simple/bytecodeinterpreter.py index 7e13102..9b6a472 100644 --- a/de.churl.simple/bytecodeinterpreter.py +++ b/de.churl.simple/bytecodeinterpreter.py @@ -33,7 +33,7 @@ class Interpreter(object): pc = 0 stack = [] code = bytecode.code - print(disassemble(bytecode)) + # print(disassemble(bytecode)) while pc < len(code): opcode = ord(code[pc]) # convert unicode to number pc += 1 diff --git a/de.churl.simple/objspace.py b/de.churl.simple/objspace.py index ccbfd04..5c4015c 100644 --- a/de.churl.simple/objspace.py +++ b/de.churl.simple/objspace.py @@ -27,7 +27,8 @@ class ObjectSpace(object): import os builtins = os.path.join( os.path.dirname(__file__), - '../builtins.simple') + '..', + 'builtins.simple') with open(builtins, 'r') as f: return f.read() diff --git a/de.churl.simple/primitives.py b/de.churl.simple/primitives.py index f1a1f37..3bac0da 100644 --- a/de.churl.simple/primitives.py +++ b/de.churl.simple/primitives.py @@ -106,6 +106,11 @@ def simple_int_increment(a): return a + 1 +@primitive("int_dec", [int], int) +def simple_int_decrement(a): + return a - 1 + + @primitive("int_tobool", [int], bool) def simple_int_tobool(a): return a diff --git a/de.churl.simple/simplelexer.py b/de.churl.simple/simplelexer.py index 3bfd504..4620e99 100644 --- a/de.churl.simple/simplelexer.py +++ b/de.churl.simple/simplelexer.py @@ -174,8 +174,25 @@ Minus = r'-' Multiply = r'\*' Divide = r'/' Increment = r'\+\+' +Decrement = r'--' Modulo = r'%' +PlusInplace = r'\+=' +MinusInplace = r'-=' +MultiplyInplace = r'\*=' +DivideInplace = r'/=' + +Less = r'<' +LessEqual = r'<=' +Greater = r'>' +GreaterEqual = r'>=' +Equal = r'==' +NotEqual = r'!=' + +And = r'&&' +Or = r'\|\|' +Not = r'!' + GC = r'gc' # ____________________________________________________________ @@ -191,9 +208,13 @@ tokens = ["If", "Else", "While", "Def", "Object", "Ignore", "String", "Boolean", "Double", "Number", # after Double "GC", - "NewlineAndWhitespace", "OpenBracket", "CloseBracket", "Comma", "Assign", "Colon", + "NewlineAndWhitespace", "OpenBracket", "CloseBracket", "Comma", "Colon", + "And", "Or", "LessEqual", "Less", "GreaterEqual", "Greater", "Equal", "NotEqual", + "Decrement", "PlusInplace", "MinusInplace", "MultiplyInplace", "DivideInplace", "Increment", "Plus", "Minus", "Multiply", "Divide", "Modulo", - "Name", "PrimitiveName"] + "Assign", "Not", + "Name", "PrimitiveName", + ] def make_lexer(): diff --git a/de.churl.simple/simpleparser.py b/de.churl.simple/simpleparser.py index 025ee03..d197b54 100644 --- a/de.churl.simple/simpleparser.py +++ b/de.churl.simple/simpleparser.py @@ -9,13 +9,23 @@ pg = ParserGenerator(["If", "Else", "While", "Def", "Object", "Number", "String", "Boolean", "Double", "GC", "Name", "Indent", "Dedent", "Newline", "OpenBracket", - "CloseBracket", "Comma", "Assign", "Colon", + "CloseBracket", "Comma", "Colon", "Increment", "Plus", "Minus", "Multiply", "Divide", "Modulo", - "PrimitiveName", "EOF"], + "Decrement", "PlusInplace", "MinusInplace", "MultiplyInplace", "DivideInplace", + "Less", "LessEqual", "Greater", "GreaterEqual", "Equal", "NotEqual", + "Assign", + "And", "Or", + "PrimitiveName", "EOF", + "Not"], # Operator precedence for ambiguous rules, ascending - precedence=[("left", ["Plus", "Minus"]), + precedence=[("left", ["Or"]), + ("left", ["And"]), + ("left", ["Equal", "NotEqual"]), + ("left", ["Less", "LessEqual", "Greater", "GreaterEqual"]), + ("left", ["Plus", "Minus"]), ("left", ["Multiply", "Divide", "Modulo"]), - ("left", ["Increment"])]) + ("left", ["Not"]), + ("left", ["Increment", "Decrement"])]) def build_methodcall(call, cls, receiver=None): @@ -103,7 +113,6 @@ def objectstatement(obj): if len(obj) == 3: blk = obj[2] else: - print(obj) parents = obj[2] # list of assignments (simpleast.Assignment) names = [p.attrname for p in parents] expressions = [p.expression for p in parents] @@ -168,22 +177,76 @@ def expression(expr): @pg.production("expression : expression Multiply expression") @pg.production("expression : expression Divide expression") @pg.production("expression : expression Modulo expression") -@pg.production("expression : expression Increment expression") -@pg.production("expression : expression Increment") -def sugar(expr): +def sugar_binary_arithmetic(expr): op = { - "Plus": "$int_add", - "Minus": "$int_sub", - "Multiply": "$int_mul", - "Divide": "$int_div", - "Modulo": "$int_mod", - "Increment": "$int_inc" + "Plus": "add", + "Minus": "sub", + "Multiply": "mul", + "Divide": "div", + "Modulo": "mod", }[expr[1].name] - if len(expr) == 2: # unary operator - return build_methodcall([op, []], simpleast.PrimitiveMethodCall, expr[0]) # ([name, arg], class, receiver) + return build_methodcall([op, [expr[2]]], simpleast.MethodCall, expr[0]) # ([name, arg], class, receiver) - return build_methodcall([op, [expr[2]]], simpleast.PrimitiveMethodCall, expr[0]) + +@pg.production("expression : expression Decrement expression") +@pg.production("expression : expression Decrement") +@pg.production("expression : expression Increment expression") +@pg.production("expression : expression Increment") +@pg.production("expression : expression PlusInplace expression") +@pg.production("expression : expression MinusInplace expression") +@pg.production("expression : expression MultiplyInplace expression") +@pg.production("expression : expression DivideInplace expression") +def sugar_inplace_arithmetic(expr): + op = { + "Increment": "inc", + "Decrement": "dec", + "PlusInplace": "add", + "MinusInplace": "sub", + "MultiplyInplace": "mul", + "DivideInplace": "div", + }[expr[1].name] + args = [] if len(expr) == 2 else [expr[2]] # unary/binary + methcall = build_methodcall([op, args], simpleast.MethodCall, expr[0]) + + if isinstance(expr[0], simpleast.MethodCall) and expr[0].arguments == []: # attribute + return simpleast.Assignment(expr[0].receiver, expr[0].methodname, methcall) # modify inplace + + return build_methodcall([op, args], simpleast.MethodCall, expr[0]) # just increment + + +@pg.production("expression : expression Less expression") +@pg.production("expression : expression LessEqual expression") +@pg.production("expression : expression Greater expression") +@pg.production("expression : expression GreaterEqual expression") +@pg.production("expression : expression Equal expression") +@pg.production("expression : expression NotEqual expression") +def sugar_comparison(expr): + op = { + "Less": "less", + "LessEqual": "leq", + "Greater": "greater", + "GreaterEqual": "geq", + "Equal": "eq", + "NotEqual": "noteq", + }[expr[1].name] + + return build_methodcall([op, [expr[2]]], simpleast.MethodCall, expr[0]) # ([name, arg], class, receiver) + + +@pg.production("expression : expression And expression") +@pg.production("expression : expression Or expression") +@pg.production("expression : Not expression") +def sugar_logical(expr): + if len(expr) == 2: # unary + return build_methodcall(["not", []], simpleast.MethodCall, expr[1]) + + op = { + "And": "and", + "Or": "or", + }[expr[1].name] + + return build_methodcall([op, [expr[2]]], simpleast.MethodCall, expr[0]) # ([name, arg], class, receiver) @pg.production("expression : OpenBracket expression CloseBracket")