testing all of this in python
This commit is contained in:
3
Dockerfile-python
Normal file
3
Dockerfile-python
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
FROM python:3.9
|
||||||
|
|
||||||
|
RUN pip install lark
|
||||||
5
boring-test.bl
Normal file
5
boring-test.bl
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
fn add(a, b) { a + b }
|
||||||
|
fn subtract(a, b) { a - b }
|
||||||
|
fn main() {
|
||||||
|
add(10, subtract(9, 1))
|
||||||
|
}
|
||||||
75
boring/interpret.py
Normal file
75
boring/interpret.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import sys
|
||||||
|
import copy
|
||||||
|
from boring import parse
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Environment:
|
||||||
|
identifiers: dict
|
||||||
|
|
||||||
|
|
||||||
|
class Interpreter:
|
||||||
|
def handle_identifier(self, env, identifier):
|
||||||
|
return env.identifiers[identifier.name]
|
||||||
|
|
||||||
|
def handle_literal_int(self, env, literal_int):
|
||||||
|
return literal_int.value
|
||||||
|
|
||||||
|
def handle_function_call(self, env, function_call):
|
||||||
|
new_env = copy.deepcopy(env)
|
||||||
|
function_definition = new_env.identifiers[function_call.name.name]
|
||||||
|
assert len(function_definition.arguments) == len(function_call.arguments)
|
||||||
|
|
||||||
|
for i, argument in enumerate(function_definition.arguments):
|
||||||
|
new_env.identifiers[argument.name.name] = self.handle_expression(env, function_call.arguments[i])
|
||||||
|
return self.handle_block(new_env, function_definition.block)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_operation(self, env, operation):
|
||||||
|
if operation.op == parse.Operator.plus:
|
||||||
|
return self.handle_expression(env, operation.left) + self.handle_expression(env, operation.right)
|
||||||
|
elif operation.op == parse.Operator.minus:
|
||||||
|
return self.handle_expression(env, operation.left) - self.handle_expression(env, operation.right)
|
||||||
|
elif operation.op == parse.Operator.mult:
|
||||||
|
return self.handle_expression(env, operation.left) * self.handle_expression(env, operation.right)
|
||||||
|
elif operation.op == parse.Operator.div:
|
||||||
|
return self.handle_expression(env, operation.left) / self.handle_expression(env, operation.right)
|
||||||
|
|
||||||
|
def handle_expression(self, env, expression):
|
||||||
|
if type(expression.expression) == parse.LiteralInt:
|
||||||
|
return self.handle_literal_int(env, expression.expression)
|
||||||
|
elif type(expression.expression) == parse.FunctionCall:
|
||||||
|
return self.handle_function_call(env, expression.expression)
|
||||||
|
elif type(expression.expression) == parse.Identifier:
|
||||||
|
return self.handle_identifier(env, expression.expression)
|
||||||
|
elif type(expression.expression) == parse.Operation:
|
||||||
|
return self.handle_operation(env, expression.expression)
|
||||||
|
elif type(expression.expression) == parse.Expression:
|
||||||
|
return self.handle_expression(env, expression.expression)
|
||||||
|
else:
|
||||||
|
raise Exception(f"unexpected type: {type(expression.expression)}")
|
||||||
|
|
||||||
|
def handle_block(self, env, block):
|
||||||
|
return self.handle_expression(env, block.expression)
|
||||||
|
|
||||||
|
def run(self, module):
|
||||||
|
env = Environment(identifiers={})
|
||||||
|
for function in module.functions:
|
||||||
|
env.identifiers[function.name.name] = function
|
||||||
|
|
||||||
|
if 'main' not in env.identifiers:
|
||||||
|
raise Exception("must have main function")
|
||||||
|
|
||||||
|
return self.handle_function_call(env, parse.FunctionCall(name=parse.Identifier("main"), arguments=[]))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
with open(sys.argv[1]) as f:
|
||||||
|
tree = parse.boring_parser.parse(f.read())
|
||||||
|
# print(tree)
|
||||||
|
ast = parse.TreeToBoring().transform(tree)
|
||||||
|
print(ast)
|
||||||
|
result = Interpreter().run(ast)
|
||||||
|
print(result)
|
||||||
|
exit(result)
|
||||||
177
boring/parse.py
Normal file
177
boring/parse.py
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
import sys
|
||||||
|
import enum
|
||||||
|
from typing import Union
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from lark import Lark, Transformer
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Identifier:
|
||||||
|
name: str
|
||||||
|
|
||||||
|
|
||||||
|
class Operator(enum.Enum):
|
||||||
|
mult = "mult"
|
||||||
|
div = "div"
|
||||||
|
plus = "plus"
|
||||||
|
minus = "minus"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LiteralInt:
|
||||||
|
value: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FunctionCall:
|
||||||
|
name: Identifier
|
||||||
|
arguments: list['Expression'] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Operation:
|
||||||
|
left: 'Expression'
|
||||||
|
op: Operator
|
||||||
|
right: 'Expression'
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Expression:
|
||||||
|
expression: Union[LiteralInt,FunctionCall,Identifier,Operation,'Expression']
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Block:
|
||||||
|
expression: Expression
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class VariableDeclaration:
|
||||||
|
name: Identifier
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Function:
|
||||||
|
name: Identifier
|
||||||
|
arguments: list[VariableDeclaration]
|
||||||
|
block: Block
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Module:
|
||||||
|
functions: Function
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
boring_grammar = r"""
|
||||||
|
plus : "+"
|
||||||
|
minus : "-"
|
||||||
|
mult : "*"
|
||||||
|
div : "/"
|
||||||
|
|
||||||
|
literal_int: SIGNED_NUMBER
|
||||||
|
identifier : NAME
|
||||||
|
function_call : identifier "(" [expression ("," expression)*] ")"
|
||||||
|
|
||||||
|
add_expression : expression plus factor
|
||||||
|
sub_expression : expression minus factor
|
||||||
|
mult_expression : expression mult term
|
||||||
|
div_expression : expression div term
|
||||||
|
|
||||||
|
expression : add_expression
|
||||||
|
| sub_expression
|
||||||
|
| factor
|
||||||
|
|
||||||
|
factor : mult_expression
|
||||||
|
| div_expression
|
||||||
|
| term
|
||||||
|
|
||||||
|
term : literal_int
|
||||||
|
| identifier
|
||||||
|
| function_call
|
||||||
|
| "(" expression ")"
|
||||||
|
|
||||||
|
block : "{" expression "}"
|
||||||
|
|
||||||
|
variable_declaration : identifier
|
||||||
|
|
||||||
|
function : "fn" identifier "(" [variable_declaration ("," variable_declaration)*] ")" block
|
||||||
|
|
||||||
|
module : (function)*
|
||||||
|
|
||||||
|
%import common.CNAME -> NAME
|
||||||
|
%import common.SIGNED_NUMBER
|
||||||
|
%import common.WS
|
||||||
|
%ignore WS
|
||||||
|
"""
|
||||||
|
|
||||||
|
class TreeToBoring(Transformer):
|
||||||
|
def plus(self, p):
|
||||||
|
return Operator.plus
|
||||||
|
|
||||||
|
def minus(self, m):
|
||||||
|
return Operator.minus
|
||||||
|
|
||||||
|
def mult(self, m):
|
||||||
|
return Operator.mult
|
||||||
|
|
||||||
|
def div(self, d):
|
||||||
|
return Operator.div
|
||||||
|
|
||||||
|
def literal_int(self, n):
|
||||||
|
(n,) = n
|
||||||
|
return LiteralInt(value=int(n))
|
||||||
|
|
||||||
|
def identifier(self, i):
|
||||||
|
(i,) = i
|
||||||
|
return Identifier(name=str(i))
|
||||||
|
|
||||||
|
def function_call(self, call):
|
||||||
|
return FunctionCall(name=call[0], arguments=call[1:])
|
||||||
|
|
||||||
|
def add_expression(self, ae):
|
||||||
|
return Operation(left=ae[0], op=ae[1], right=ae[2])
|
||||||
|
|
||||||
|
def sub_expression(self, se):
|
||||||
|
return Operation(left=se[0], op=se[1], right=se[2])
|
||||||
|
|
||||||
|
def mult_expression(self, se):
|
||||||
|
return Operation(left=se[0], op=se[1], right=se[2])
|
||||||
|
|
||||||
|
def div_expression(self, se):
|
||||||
|
return Operation(left=se[0], op=se[1], right=se[2])
|
||||||
|
|
||||||
|
def expression(self, exp):
|
||||||
|
(exp,) = exp
|
||||||
|
return Expression(expression=exp)
|
||||||
|
|
||||||
|
def factor(self, factor):
|
||||||
|
(factor,) = factor
|
||||||
|
return Expression(factor)
|
||||||
|
|
||||||
|
def term(self, term):
|
||||||
|
(term,) = term
|
||||||
|
return Expression(term)
|
||||||
|
|
||||||
|
def block(self, block):
|
||||||
|
(block,) = block
|
||||||
|
return Block(expression=block)
|
||||||
|
|
||||||
|
def variable_declaration(self, identifier):
|
||||||
|
(identifier,) = identifier
|
||||||
|
return VariableDeclaration(name=identifier)
|
||||||
|
|
||||||
|
def function(self, function):
|
||||||
|
return Function(name=function[0], arguments=function[1:-1], block=function[-1])
|
||||||
|
|
||||||
|
def module(self, functions):
|
||||||
|
return Module(functions=functions)
|
||||||
|
|
||||||
|
|
||||||
|
boring_parser = Lark(boring_grammar, start='module', lexer='standard')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
with open(sys.argv[1]) as f:
|
||||||
|
tree = boring_parser.parse(f.read())
|
||||||
|
print(tree)
|
||||||
|
print(TreeToBoring().transform(tree))
|
||||||
@@ -4,3 +4,9 @@ services:
|
|||||||
build: .
|
build: .
|
||||||
volumes:
|
volumes:
|
||||||
- .:/code/
|
- .:/code/
|
||||||
|
boring-python:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile-python
|
||||||
|
volumes:
|
||||||
|
- .:/code/
|
||||||
|
|||||||
12
setup.py
Normal file
12
setup.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from distutils.core import setup
|
||||||
|
from setuptools import find_packages
|
||||||
|
|
||||||
|
setup(name='boring-lang',
|
||||||
|
version='0.0.1',
|
||||||
|
description='Boring programming language',
|
||||||
|
author='Andrew Segavac',
|
||||||
|
author_email='andrew@eunomia.io',
|
||||||
|
packages=find_packages(),
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user