restructure + syntactic sugar implementation
This commit is contained in:
@ -25,7 +25,7 @@ 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()
|
||||
|
@ -6,26 +6,29 @@ primitive_number_of_arguments = []
|
||||
def primitive(name, unwrap_spec, wrap_spec):
|
||||
assert '$' + name not in registry, '${name} already defined'.format(name=name)
|
||||
primitive_number_of_arguments.append(len(unwrap_spec) - 1) # first argument is the receiver
|
||||
|
||||
def expose(func):
|
||||
def unwrapper(w_receiver, args_w, space):
|
||||
args = [w_receiver] + args_w
|
||||
if len(args) != len(unwrap_spec):
|
||||
raise TypeError(
|
||||
"Expected {ex} arguments, received {re}.".format(ex=len(unwrap_spec), re=len(args)))
|
||||
"Expected {ex} arguments, received {re}.".format(ex=len(unwrap_spec), re=len(args)))
|
||||
unwrapped_args = ()
|
||||
for t, arg in zip(unwrap_spec, args):
|
||||
if t is int:
|
||||
unwrapped_args += (arg.value, )
|
||||
unwrapped_args += (arg.value,)
|
||||
else:
|
||||
unwrapped_args += (arg, )
|
||||
unwrapped_args += (arg,)
|
||||
result = func(*unwrapped_args)
|
||||
if wrap_spec is int:
|
||||
return space.newint(result)
|
||||
return result
|
||||
|
||||
unwrapper.__qualname__ = name
|
||||
all_primitives.append(unwrapper)
|
||||
registry['$' + name] = len(all_primitives) - 1
|
||||
return None
|
||||
|
||||
return expose
|
||||
|
||||
|
||||
@ -45,3 +48,29 @@ def simple_int_add(a, b):
|
||||
@primitive('int_eq', [int, int], int)
|
||||
def simple_int_eq(a, b):
|
||||
return a == b
|
||||
|
||||
|
||||
# Syntactic Sugar Primitives
|
||||
@primitive("int_sub", [int, int], int)
|
||||
def simple_int_subtract(a, b):
|
||||
return a - b
|
||||
|
||||
|
||||
@primitive("int_mul", [int, int], int)
|
||||
def simple_int_multiply(a, b):
|
||||
return a * b
|
||||
|
||||
|
||||
@primitive("int_div", [int, int], int)
|
||||
def simple_int_divide(a, b):
|
||||
return a // b
|
||||
|
||||
|
||||
@primitive("int_mod", [int, int], int)
|
||||
def simple_int_modulo(a, b):
|
||||
return a % b
|
||||
|
||||
|
||||
@primitive("int_inc", [int], int)
|
||||
def simple_int_increment(a):
|
||||
return a + 1
|
@ -158,6 +158,16 @@ Assign = r'\='
|
||||
OpenBracket = r'[\[\(\{]'
|
||||
CloseBracket = r'[\]\)\}]'
|
||||
|
||||
# ____________________________________________________________
|
||||
# Syntactic Sugar Operators
|
||||
|
||||
Plus = r'\+'
|
||||
Minus = r'-'
|
||||
Multiply = r'\*'
|
||||
Divide = r'/'
|
||||
Increment = r'\+\+'
|
||||
Modulo = r'%'
|
||||
|
||||
# ____________________________________________________________
|
||||
# Keywords
|
||||
|
||||
@ -168,8 +178,9 @@ Def = r'def'
|
||||
Object = r'object'
|
||||
|
||||
tokens = ["If", "Else", "While", "Def", "Object", "Number", "String", "Ignore",
|
||||
"NewlineAndWhitespace", "OpenBracket", "CloseBracket", "Comma", "Assign",
|
||||
"Colon", "Name", "PrimitiveName"]
|
||||
"NewlineAndWhitespace", "OpenBracket", "CloseBracket", "Comma", "Assign", "Colon",
|
||||
"Increment", "Plus", "Minus", "Multiply", "Divide", "Modulo", # Sugar
|
||||
"Name", "PrimitiveName"]
|
||||
|
||||
|
||||
def make_lexer():
|
@ -1,35 +1,40 @@
|
||||
"""
|
||||
A 'simple' parser. Don't look into this file :-)
|
||||
"""
|
||||
import py
|
||||
import simpleast
|
||||
from rply import ParserGenerator, Token
|
||||
from simplelexer import lex
|
||||
from rply.token import Token
|
||||
|
||||
from rply import ParserGenerator
|
||||
import simpleast
|
||||
|
||||
pg = ParserGenerator(["If", "Else", "While", "Def", "Object", "Number",
|
||||
"String", "Name", "Indent", "Dedent", "Newline", "OpenBracket",
|
||||
"CloseBracket", "Comma", "Assign", "Colon", "PrimitiveName", "EOF"])
|
||||
"CloseBracket", "Comma", "Assign", "Colon",
|
||||
"Increment", "Plus", "Minus", "Multiply", "Divide", "Modulo", # Sugar
|
||||
"PrimitiveName", "EOF"],
|
||||
# Operator precedence for ambiguous rules, ascending
|
||||
precedence=[("left", ["Plus", "Minus"]),
|
||||
("left", ["Multiply", "Divide", "Modulo"]),
|
||||
("left", ["Increment"])])
|
||||
|
||||
|
||||
def build_methodcall(call, cls):
|
||||
def build_methodcall(call, cls, receiver=None):
|
||||
if len(call) == 1:
|
||||
args = []
|
||||
else:
|
||||
args = call[1]
|
||||
name = call[0]
|
||||
return cls(None, name, args)
|
||||
|
||||
return cls(receiver, name, args)
|
||||
|
||||
|
||||
@pg.production("program : statements EOF")
|
||||
@pg.production("program : newlines statements EOF")
|
||||
def program(prog):
|
||||
# import pdb; pdb.set_trace()
|
||||
if prog[0] is None:
|
||||
if prog[0] is None: # source starts with newlines
|
||||
prog = prog[1]
|
||||
else:
|
||||
prog = prog[0]
|
||||
|
||||
return prog
|
||||
|
||||
|
||||
@ -37,16 +42,17 @@ def program(prog):
|
||||
@pg.production("statements : statement statements")
|
||||
@pg.production("statements : statement newlines statements")
|
||||
def statements(stmts):
|
||||
if len(stmts) == 1:
|
||||
if len(stmts) == 1: # single statement
|
||||
stmt = stmts[0]
|
||||
return simpleast.Program([stmt])
|
||||
elif stmts[0] is None:
|
||||
elif stmts[0] is None: # ?
|
||||
assert len(stmts) == 2
|
||||
return stmts[1]
|
||||
elif len(stmts) == 2:
|
||||
elif len(stmts) == 2: # merge programs (simpleast.Program)
|
||||
stmt = stmts[0]
|
||||
result = stmts[1]
|
||||
result.statements.insert(0, stmt)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@ -71,6 +77,7 @@ def ifstatement(ifstmt):
|
||||
elseblock = None
|
||||
if len(ifstmt) > 3:
|
||||
elseblock = ifstmt[-1]
|
||||
|
||||
return simpleast.IfStatement(ifstmt[1], ifstmt[2], elseblock)
|
||||
|
||||
|
||||
@ -88,10 +95,12 @@ def objectstatement(obj):
|
||||
if len(obj) == 3:
|
||||
blk = obj[2]
|
||||
else:
|
||||
parents = obj[2]
|
||||
print(obj)
|
||||
parents = obj[2] # list of assignments (simpleast.Assignment)
|
||||
names = [p.attrname for p in parents]
|
||||
expressions = [p.expression for p in parents]
|
||||
blk = obj[3]
|
||||
|
||||
return simpleast.ObjectDefinition(name, blk, names, expressions)
|
||||
|
||||
|
||||
@ -105,6 +114,7 @@ def defstatement(defn):
|
||||
else:
|
||||
args = []
|
||||
blk = defn[2]
|
||||
|
||||
return simpleast.FunctionDefinition(name, args, blk)
|
||||
|
||||
|
||||
@ -118,37 +128,63 @@ def block(blk):
|
||||
def simplestatement(stmts):
|
||||
if len(stmts) == 2:
|
||||
return simpleast.ExprStatement(stmts[0])
|
||||
# assignement
|
||||
|
||||
# assignment
|
||||
result = stmts[0]
|
||||
assign = stmts[2]
|
||||
if (isinstance(result, simpleast.MethodCall) and
|
||||
result.arguments == []):
|
||||
return simpleast.Assignment(
|
||||
result.receiver, result.methodname, assign)
|
||||
else:
|
||||
source_pos = stmts[1].source_pos
|
||||
raise ParseError(source_pos,
|
||||
ErrorInformation(source_pos.idx,
|
||||
customerror="can only assign to attribute")) # , self.source)
|
||||
if isinstance(result, simpleast.MethodCall) and result.arguments == []: # assign to attribute
|
||||
return simpleast.Assignment(result.receiver, result.methodname, assign)
|
||||
|
||||
source_pos = stmts[1].source_pos
|
||||
raise ParseError(source_pos,
|
||||
ErrorInformation(source_pos.idx,
|
||||
customerror="can only assign to attribute")) # , self.source)
|
||||
|
||||
|
||||
@pg.production("expression : basic_expression")
|
||||
@pg.production("expression : basic_expression msg-chain")
|
||||
def expression(expr):
|
||||
if len(expr) > 1:
|
||||
if len(expr) > 1: # expression has messages
|
||||
prev = expr[0]
|
||||
for i in expr[1]:
|
||||
for i in expr[1]: # reverse message order
|
||||
i.receiver = prev
|
||||
prev = i
|
||||
return expr[1][-1]
|
||||
return expr[1][-1] # return last message
|
||||
|
||||
return expr[0]
|
||||
|
||||
|
||||
# TODO: Parenthesis
|
||||
# Syntactic Sugar: Plus, Minus, Multiply, Divide, Modulo, Increment
|
||||
@pg.production("expression : expression Plus expression")
|
||||
@pg.production("expression : expression Minus expression")
|
||||
@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):
|
||||
op = {
|
||||
"Plus": "$int_add",
|
||||
"Minus": "$int_sub",
|
||||
"Multiply": "$int_mul",
|
||||
"Divide": "$int_div",
|
||||
"Modulo": "$int_mod",
|
||||
"Increment": "$int_inc"
|
||||
}[expr[1].name]
|
||||
|
||||
if len(expr) == 2:
|
||||
return build_methodcall([op, []], simpleast.PrimitiveMethodCall, expr[0]) # ([name, arg], class, receiver)
|
||||
|
||||
return build_methodcall([op, [expr[2]]], simpleast.PrimitiveMethodCall, expr[0]) # ([name, arg], class, receiver)
|
||||
|
||||
|
||||
@pg.production("msg-chain : methodcall")
|
||||
@pg.production("msg-chain : methodcall msg-chain")
|
||||
def msg_chain(cc):
|
||||
if len(cc) > 1:
|
||||
return [cc[0]] + cc[1]
|
||||
return [cc[0]] + cc[1] # merge message lists for chain
|
||||
|
||||
return cc
|
||||
|
||||
|
||||
@ -166,6 +202,7 @@ def string_expression(stmt):
|
||||
def implicitselfmethodcall(call):
|
||||
methodcall = call[0]
|
||||
methodcall.receiver = simpleast.ImplicitSelf()
|
||||
|
||||
return methodcall
|
||||
|
||||
|
||||
@ -210,7 +247,8 @@ def argumentslist(args):
|
||||
@pg.production("parentdefinitions : assignment Comma parentdefinitions")
|
||||
def arguments(args):
|
||||
if len(args) == 3:
|
||||
return [args[0]] + args[2]
|
||||
return [args[0]] + args[2] # merge argument lists for chain
|
||||
|
||||
return [args[0]]
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
import py
|
||||
|
||||
from simpleparser import parse
|
||||
from objmodel import W_NormalObject, W_Integer
|
||||
from objmodel import W_Integer
|
||||
from interpreter import Interpreter
|
||||
|
||||
|
||||
|
@ -1,7 +1,4 @@
|
||||
import py
|
||||
|
||||
from simpleparser import parse
|
||||
from objmodel import W_NormalObject, W_Integer
|
||||
from interpreter import Interpreter
|
||||
|
||||
|
||||
|
13
mytest/test_sugar_lex.py
Normal file
13
mytest/test_sugar_lex.py
Normal file
@ -0,0 +1,13 @@
|
||||
from rply import Token
|
||||
from simplelexer import lex
|
||||
|
||||
|
||||
def test_lexing():
|
||||
token = lex("+ - * / ++ %")
|
||||
|
||||
assert Token("Plus", "+") == token[0]
|
||||
assert Token("Minus", "-") == token[1]
|
||||
assert Token("Multiply", "*") == token[2]
|
||||
assert Token("Divide", "/") == token[3]
|
||||
assert Token("Increment", "++") == token[4]
|
||||
assert Token("Modulo", "%") == token[5]
|
14
mytest/test_sugar_parse.py
Normal file
14
mytest/test_sugar_parse.py
Normal file
@ -0,0 +1,14 @@
|
||||
from simpleparser import parse
|
||||
|
||||
|
||||
def test_parsing():
|
||||
assert parse("1 + 2") == parse("1 $int_add(2)")
|
||||
assert parse("1 + 2 + 3") == parse("1 $int_add(2) $int_add(3)")
|
||||
assert parse("1 + 2 * 3") == parse("1 $int_add(2 $int_mul(3))")
|
||||
assert parse("1 - 2 - 3") == parse("1 $int_sub(2) $int_sub(3)")
|
||||
assert parse("1 + 2 % 3 / 4 - 5 * 6 + 7++") \
|
||||
== parse("1 $int_add(2 $int_mod(3) $int_div(4)) $int_sub(5 $int_mul(6)) $int_add(7 $int_inc)")
|
||||
|
||||
|
||||
def test_parsing_parenthesis():
|
||||
pass
|
Binary file not shown.
35
shell.nix
35
shell.nix
@ -1,35 +0,0 @@
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
with pkgs;
|
||||
|
||||
let myPython = python39.buildEnv.override {
|
||||
extraLibs = with python39Packages; [
|
||||
# Common Libs
|
||||
rich
|
||||
# numpy
|
||||
# matplotlib
|
||||
# scipy
|
||||
# pytorch
|
||||
# notbook
|
||||
|
||||
# Doom Emacs Libs
|
||||
black
|
||||
pyflakes
|
||||
isort
|
||||
nose
|
||||
pytest
|
||||
|
||||
# DynLang
|
||||
rply
|
||||
];
|
||||
};
|
||||
in
|
||||
|
||||
mkShell {
|
||||
buildInputs = [
|
||||
myPython
|
||||
nodePackages.pyright # LSP
|
||||
pipenv # Doom
|
||||
jetbrains.pycharm-professional
|
||||
];
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
import py
|
||||
|
||||
from simpleparser import parse
|
||||
from objmodel import W_NormalObject
|
||||
from interpreter import Interpreter
|
||||
|
||||
|
||||
def test_builtin_simple():
|
||||
builtincode = """
|
||||
x = 1
|
||||
@ -30,6 +28,7 @@ ax = a x
|
||||
assert w_module.getvalue("ax").value == 1
|
||||
assert w_module.getvalue("tx").value == 1
|
||||
|
||||
|
||||
def test_inttrait():
|
||||
builtincode = """
|
||||
object inttrait:
|
||||
@ -66,7 +65,7 @@ tr = inttrait
|
||||
assert w_module.getvalue("m1").value == 42
|
||||
assert w_module.getvalue("m2").value == 2
|
||||
|
||||
|
||||
|
||||
def test_builtin_default():
|
||||
ast = parse("""
|
||||
def sumupto(x):
|
||||
@ -80,7 +79,7 @@ x = sumupto(100)
|
||||
# the constructor is called without arguments, so the default builtins are
|
||||
# used
|
||||
interpreter = Interpreter()
|
||||
# test that the default inttrait defines a method ``add``
|
||||
# tests that the default inttrait defines a method ``add``
|
||||
w_module = interpreter.make_module()
|
||||
interpreter.eval(ast, w_module)
|
||||
assert w_module.getvalue("x").value == 5050
|
@ -1,5 +1,3 @@
|
||||
import py
|
||||
|
||||
from simpleparser import parse
|
||||
from objmodel import W_NormalObject
|
||||
from interpreter import Interpreter
|
@ -1,9 +1,8 @@
|
||||
import py
|
||||
|
||||
from simpleparser import parse
|
||||
from objmodel import W_NormalObject
|
||||
from interpreter import Interpreter
|
||||
|
||||
|
||||
def test_assign():
|
||||
ast = parse("""
|
||||
object a:
|
@ -1,7 +1,6 @@
|
||||
import py
|
||||
|
||||
from simpleparser import parse
|
||||
from objmodel import W_NormalObject
|
||||
from interpreter import Interpreter
|
||||
|
||||
|
Reference in New Issue
Block a user