diff --git a/.gitignore b/.gitignore index 488c18a..15c110c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,8 @@ test* parser.out -/__pycache__ \ No newline at end of file +/__pycache__ + +output* +*.ll +*.dt \ No newline at end of file diff --git a/compiler/__init__.py b/compiler/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/compiler/__pycache__/__init__.cpython-37.pyc b/compiler/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000..3b3d6a0 Binary files /dev/null and b/compiler/__pycache__/__init__.cpython-37.pyc differ diff --git a/compiler/__pycache__/lexical.cpython-37.pyc b/compiler/__pycache__/lexical.cpython-37.pyc new file mode 100644 index 0000000..7b3e5fc Binary files /dev/null and b/compiler/__pycache__/lexical.cpython-37.pyc differ diff --git a/compiler/__pycache__/parser.cpython-37.pyc b/compiler/__pycache__/parser.cpython-37.pyc new file mode 100644 index 0000000..bc5902d Binary files /dev/null and b/compiler/__pycache__/parser.cpython-37.pyc differ diff --git a/lexical.py b/compiler/lexical.py similarity index 77% rename from lexical.py rename to compiler/lexical.py index e7b405a..3924f13 100644 --- a/lexical.py +++ b/compiler/lexical.py @@ -47,28 +47,10 @@ class DothLexer(Lexer): # RETURN = r'(EZAT)' # PRINT = r'(FREDRIK)' - NUMBER = r'\d+' - # @_(r'\d+') - # def NUMBER(self, t): - # t.value = int(t.value) - # return t - FLOAT = r'\d*\.?\d+' - # @_(r'\d*\.?\d+') - # def FLOAT(self, t): - # t.value = float(t.value) - # return t + NUMBER = r'\d+' STRING = r'''("[^"\\]*(\\.[^"\\]*)*"|'[^'\\]*(\\.[^'\\]*)*')''' - # @_(r'''("[^"\\]*(\\.[^"\\]*)*"|'[^'\\]*(\\.[^'\\]*)*')''') - # def STRING(self, t): - # t.value = self.remove_quotes(t.value) - # return t - - # def remove_quotes(self, text: str): - # if text.startswith('\"') or text.startswith('\''): - # return text[1:-1] - # return text # Tokens NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' diff --git a/parser.py b/compiler/parser.py similarity index 79% rename from parser.py rename to compiler/parser.py index 55a4dc8..01763f0 100644 --- a/parser.py +++ b/compiler/parser.py @@ -1,6 +1,9 @@ +import sys +sys.path.append('../') from sly import Parser -from lexical import DothLexer -from nodes import Block, DefFuncOp, IfOp, LoopOp, DeclarationOp, AssignmentOp, ReturnOp, CallFunOp, PrintOp, BinOp, UnOp, IntVal, FloatVal, StringVal, BoolVal, IdentifierVal, NoOp +from compiler.lexical import DothLexer +from helpers.nodes import Node, Block, DefFuncOp, IfOp, LoopOp, DeclarationOp, AssignmentOp, ReturnOp, CallFunOp, PrintOp, BinOp, UnOp, IntVal, FloatVal, StringVal, BoolVal, IdentifierVal, NoOp +from llvmlite import ir class DothParser(Parser): @@ -11,10 +14,29 @@ class DothParser(Parser): ('left', TIMES, DIVIDE), ('right', UMINUS, UNOT, UPLUS), ) - debugfile = 'parser.out' + debugfile = './out/parser.out' - def __init__(self, debug=True): + def __init__(self, module, builder, printf, debug=True): + self.module = module + self.builder = builder + self.printf = printf self.variables = {} + Node.module = self.module + Node.builder = self.builder + Node.printf = self.printf + Node.printfFmtArg = self._configurePrintf() + + def _configurePrintf(self): + # Declare argument list + voidptrY = ir.IntType(8).as_pointer() + fmt = "%i \n\0" + cFmt = ir.Constant(ir.ArrayType(ir.IntType(8), len(fmt)), + bytearray(fmt.encode("utf8"))) + globalFmt = ir.GlobalVariable(self.module, cFmt.type, name="fstr") + globalFmt.linkage = 'internal' + globalFmt.global_constant = True + globalFmt.initializer = cFmt + return globalFmt, voidptrY @_('{ statement }') def code(self, p): @@ -62,14 +84,19 @@ def command(self, p): return values[0] @_( - 'TSTRING NAME LPAREN param { param } RPAREN block EOL', - 'TINT NAME LPAREN param { param } RPAREN block EOL', - 'TFLOAT NAME LPAREN param { param } RPAREN block EOL', - 'BOOL NAME LPAREN param { param } RPAREN block EOL', + 'TSTRING NAME LPAREN { param } RPAREN block EOL', + 'TINT NAME LPAREN { param } RPAREN block EOL', + 'TFLOAT NAME LPAREN { param } RPAREN block EOL', + 'BOOL NAME LPAREN { param } RPAREN block EOL', + 'VOID NAME LPAREN { param } RPAREN block EOL', ) def def_function(self, p): values = p._slice - return DefFuncOp(values[1]) + func = DefFuncOp(values[1], values[0]) + for prm in p.param: + func.addParam(prm) + func.setBlock(p.block) + return func @_( 'IF LPAREN or_expr RPAREN block elseif', @@ -83,7 +110,7 @@ def conditional(self, p): elif values[-1].type == 'elses': root = IfOp(values[0], p.or_expr, p.block) elses = p.elses - root.setElse(elses[3]) + root.setElse(elses[2]) return root elif values[-1].type == 'elseif': root = IfOp(values[0], p.or_expr, p.block) @@ -150,15 +177,15 @@ def assignment(self, p): values = p._slice return AssignmentOp(values[0], p.or_expr) - @_('RETURN or_expr') + @_('RETURN or_expr EOL') def returns(self, p): return ReturnOp(p.RETURN, p.or_expr) @_( - 'NAME LPAREN { param } RPAREN', + 'NAME LPAREN { factor } RPAREN', ) def call_function(self, p): - return CallFunOp(p.NAME) + return CallFunOp(p.NAME, p.factor) @_('PRINT LPAREN or_expr RPAREN EOL') def println(self, p): @@ -172,7 +199,7 @@ def println(self, p): ) def param(self, p): values = p._slice - return (values[0], values[1]) + return values[0], values[1] @_('BOPEN { command } BCLOSE') def block(self, p): @@ -269,7 +296,7 @@ def factor(self, p): if values[0].type == 'NUMBER': return IntVal(values[0]) elif values[0].type in ['PLUS', 'MINUS', 'NOT']: - return UnOp(values[0], values[1]) + return UnOp(values[0], values.factor) elif values[0].type == 'NAME': return IdentifierVal(values[0]) elif values[0].type == 'STRING': diff --git a/helpers/__init__.py b/helpers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/helpers/__pycache__/__init__.cpython-37.pyc b/helpers/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000..315d555 Binary files /dev/null and b/helpers/__pycache__/__init__.cpython-37.pyc differ diff --git a/helpers/__pycache__/nodes.cpython-37.pyc b/helpers/__pycache__/nodes.cpython-37.pyc new file mode 100644 index 0000000..9de3f80 Binary files /dev/null and b/helpers/__pycache__/nodes.cpython-37.pyc differ diff --git a/helpers/__pycache__/symbolTable.cpython-37.pyc b/helpers/__pycache__/symbolTable.cpython-37.pyc new file mode 100644 index 0000000..e23df4e Binary files /dev/null and b/helpers/__pycache__/symbolTable.cpython-37.pyc differ diff --git a/helpers/nodes.py b/helpers/nodes.py new file mode 100644 index 0000000..5327764 --- /dev/null +++ b/helpers/nodes.py @@ -0,0 +1,369 @@ +import sys +sys.path.append('../') +from typing import List, NoReturn, Union, Set +from sly.lex import Token +from helpers.symbolTable import SymbolTable +from llvmlite import ir +import uuid +from copy import deepcopy + + +class Node: + + module = None + builder = None + printf = None + printfFmtArg = None + + def __init__(self, value: Token): + self.value = value + self.children: List[Node] = [] + self.id = str(uuid.uuid4()) + + def evaluate(self, table: SymbolTable) -> int: + return 0 + + +class Block(Node): + + def __init__(self, value: Token): + super().__init__(value) + + def addChild(self, child: Node) -> NoReturn: + self.children.append(child) + + def evaluate(self, table) -> NoReturn: + for child in self.children: + child.evaluate(table) + + +class DefFuncOp(Node): + + type_dict = { + 'TINT': ir.IntType(32), + 'TFLOAT': ir.FloatType(), + 'TSTRING': ir.ArrayType(ir.IntType(8), 64), + 'BOOL': ir.IntType(1), + 'VOID': ir.VoidType(), + } + var_dict = { + 'TINT': 'int', + 'TFLOAT': 'float', + 'TSTRING': 'string', + 'BOOL': 'bool', + 'VOID': 'void', + } + + def __init__(self, value: Token, funcType: Token): + super().__init__(value) + self.params = [] + self.block = None + self.funcType = self.type_dict[funcType.type] + + def addParam(self, param: List): + param = [param[0].type, param[1].value] + self.params.append(param) + + def setBlock(self, block: Node): + self.block = block + + def evaluate(self, table: SymbolTable): + fnty = ir.FunctionType( + self.funcType, [self.type_dict[p[0]] for p in self.params]) + func = ir.Function(self.module, fnty, name=self.value.value) + + funcBlock = func.append_basic_block(f'{self.value.value}_entry') + previousBuilder = self.builder + + Node.builder = ir.IRBuilder(funcBlock) + + paramsPtrs = [] + for i, typ in enumerate([self.type_dict[p[0]] for p in self.params]): + ptr = self.builder.alloca(typ) + self.builder.store(func.args[i], ptr) + paramsPtrs.append(ptr) + + scopeTable = SymbolTable() + for i, (typ, name) in enumerate(self.params): + scopeTable.declareVariable( + name, paramsPtrs[i]) + + for var, val in table.table.items(): + if val['type'] == 'function': + scopeTable.declareVariable(var, val['pointer'], val['type']) + + scopeTable.declareVariable(self.value.value, func) + self.block.evaluate(scopeTable) + if self.funcType == ir.VoidType(): + self.builder.ret_void() + + table.declareVariable(self.value.value, func, "function") + Node.builder = previousBuilder + return table + + +class IfOp(Node): + + condition: Node + commandTrue: Node + commandElse: Node + elifClauses: List[List[Node]] + + def __init__(self, value: Token, condition: Node, commandTrue: Node): + super().__init__(value) + self.condition = condition + self.commandTrue = commandTrue + self.commandElse = None + self.elifClauses = [] + + def setElse(self, commandElse: Node) -> NoReturn: + self.commandElse = commandElse + + def addElif(self, clause: Node, command: Node) -> NoReturn: + self.elifClauses.append([clause, command]) + + def evaluate(self, table: SymbolTable) -> NoReturn: + cond = self.condition.evaluate(table) + with self.builder.if_else(cond) as (then, otherwise): + with then: + self.commandTrue.evaluate(table) + with otherwise: + # for ind, elif_ in enumerate(self.elifClauses): + # elifEntry = self.builder.append_basic_block( + # name=f"elif_{self.id}_{ind}") + # ifOut = self.builder.append_basic_block( + # name=f"elif_out_{self.id}_{ind}") + # typ, value, cond = elif_[0].evaluate(table) + # self.builder.cbranch(cond, elifEntry, ifOut) + # pos = self.builder.position_at_start(elifEntry) + # com = elif_[1].evaluate(table) + # typ, value, cond = self.condition.evaluate(table) + # self.builder.cbranch(cond, elifEntry, ifOut) + # self.builder.position_at_start(ifOut) + if self.commandElse: + self.commandElse.evaluate(table) + + +class LoopOp(Node): + + condition: Node + command: Node + + def __init__(self, value: Token, condition: Node, command: Node): + super().__init__(value) + self.condition = condition + self.command = command + + def evaluate(self, table: SymbolTable) -> NoReturn: + loopEntry = self.builder.append_basic_block(name=f"while_{self.id}") + loopOut = self.builder.append_basic_block(name=f"while_out_{self.id}") + cond = self.condition.evaluate(table) + self.builder.cbranch(cond, loopEntry, loopOut) + pos = self.builder.position_at_start(loopEntry) + com = self.command.evaluate(table) + cond = self.condition.evaluate(table) + self.builder.cbranch(cond, loopEntry, loopOut) + self.builder.position_at_start(loopOut) + + +class DeclarationOp(Node): + + var_type: Token + expr: Node + var_dict = { + 'TINT': ir.IntType(32), + 'TFLOAT': ir.FloatType(), + 'TSTRING': ir.ArrayType(ir.IntType(8), 64), + 'BOOL': ir.IntType(1), + 'VOID': ir.VoidType(), + } + + def __init__(self, value: Token, var_type: Token, expr: Node): + super().__init__(value) + self.var_type = self.var_dict[var_type.type] + self.expr = expr + + def evaluate(self, table: SymbolTable): + expr = self.expr.evaluate(table) + irAloc = self.builder.alloca(self.var_type, name=self.value.value) + self.builder.store(expr, irAloc) + table.declareVariable(self.value.value, irAloc) + return table + + +class AssignmentOp(Node): + + expr: Node + + def __init__(self, value: Token, expr: Node): + super().__init__(value) + self.expr = expr + + def evaluate(self, table: SymbolTable): + pointer, tmp = table.getVariable(self.value.value) + expr = self.expr.evaluate(table) + self.builder.store(expr, pointer) + table.setVariable(self.value.value, pointer) + return table + + +class ReturnOp(Node): + + expr: Node + + def __init__(self, value: Token, expr: Node): + super().__init__(value) + self.expr = expr + + def evaluate(self, table): + return self.builder.ret(self.expr.evaluate(table)) + + +class CallFunOp(Node): + + expr: Node + params: List[Node] + + def __init__(self, value: Token, params: List[Node]): + super().__init__(value) + self.params = params + + def evaluate(self, table): + args = [] + for param in self.params: + value = param.evaluate(table) + args.append(value) + + func, tmp = table.getVariable(self.value) + ret = self.builder.call(func, args) + return ret + + +class PrintOp(Node): + + expr: Node + + def __init__(self, value: Token, expr: Node): + super().__init__(value) + self.expr = expr + + def evaluate(self, table): + value = self.expr.evaluate(table) + # value = self.builder.trunc(value, ir.IntType(8)) + globalFmt, voidptrY = self.printfFmtArg + + strFmt = self.builder.bitcast(globalFmt, voidptrY) + # Call Print Function + self.builder.call(self.printf, [strFmt, value]) + + +class BinOp(Node): + + def __init__(self, value: Token, left: Node, right: Node): + super().__init__(value) + self.children = [left, right] + + def evaluate(self, table: SymbolTable): + children0 = self.children[0].evaluate(table) + children1 = self.children[1].evaluate(table) + if self.value.type == 'PLUS': + i = self.builder.add(children0, children1) + elif self.value.type == 'MINUS': + i = self.builder.sub(children0, children1) + elif self.value.type == 'TIMES': + i = self.builder.mul(children0, children1) + elif self.value.type == 'DIVIDE': + i = self.builder.sdiv(children0, children1) + elif self.value.type == 'EQ': + i = self.builder.icmp_signed('==', children0, children1) + elif self.value.type == 'OR': + i = self.builder.or_(children0, children1) + elif self.value.type == 'AND': + i = self.builder.and_(children0, children1) + elif self.value.type == 'GT': + i = self.builder.icmp_signed('>', children0, children1) + elif self.value.type == 'LT': + i = self.builder.icmp_signed('<', children0, children1) + else: + raise BufferError(f"Invalid operation of type {self.value.type}") + return i + + +class UnOp(Node): + + expr: Node + + def __init__(self, value: Token, expr: Node): + super().__init__(value) + self.expr = expr + + def evaluate(self, table): + ret = self.expr.evaluate(table) + if self.value.type == 'UPLUS' or self.value.type == 'PLUS': + return ret + elif self.value.type == 'UMINUS' or self.value.type == 'MINUS': + return self.builder.neg(ret) + elif self.value.type == 'UNOT' or self.value.type == 'NOT': + return self.builder.not_(ret) + else: + raise BufferError(f"Invalid operation of type {self.value.type}") + + +class IntVal(Node): + + def __init__(self, value: Token): + super().__init__(value) + + def evaluate(self, table: SymbolTable): + return ir.Constant(ir.IntType(32), self.value.value) + + +class FloatVal(Node): + + def __init__(self, value: Token): + super().__init__(value) + + def evaluate(self, table: SymbolTable): + if '.' in self.value.value: + return ir.Constant(ir.FloatType(), self.value.value) + return ir.Constant(ir.IntType(32), self.value.value) + + +class StringVal(Node): + + def __init__(self, value: Token): + super().__init__(value) + + def evaluate(self, table: SymbolTable): + i = ir.Constant(ir.ArrayType(ir.IntType(8), len(self.value.value)), + bytearray(self.value.value.encode("utf8"))) + return i + + +class BoolVal(Node): + + def __init__(self, value: Token): + super().__init__(value) + + def evaluate(self, table: SymbolTable): + return ir.Constant(ir.IntType(1), self.value.value) + + +class IdentifierVal(Node): + + def __init__(self, value: Token): + super().__init__(value) + + def evaluate(self, table: SymbolTable): + variable, tmp = table.getVariable(self.value.value) + i = self.builder.load(variable, name=self.value.value) + return i + + +class NoOp(Node): + + def __init__(self, value: Token): + super().__init__(value) + + def evaluate(self, table: SymbolTable): + return ir.Constant(ir.IntType(32), 0) diff --git a/helpers/symbolTable.py b/helpers/symbolTable.py new file mode 100644 index 0000000..aa02538 --- /dev/null +++ b/helpers/symbolTable.py @@ -0,0 +1,29 @@ +from typing import NoReturn, Dict, Union +from sly.lex import Token + + +class SymbolTable: + + table: Dict[str, Token] + + def __init__(self): + self.table = {} + + def getVariable(self, variable: str) -> Token: + if variable in self.table: + return self.table[variable]["pointer"], self.table[variable]["type"] + raise KeyError(f"No variable named {variable}") + + def declareVariable( + self, variable: str, ptr, var_type: str = "null" + ) -> NoReturn: + if variable in self.table: + raise NameError(f"Variable {variable} already declared") + self.table[variable] = {"pointer": ptr, "type": var_type} + + def setVariable( + self, variable: str, ptr + ) -> NoReturn: + if variable not in self.table: + raise NameError(f"Variable {variable} not declared") + self.table[variable]["pointer"] = ptr diff --git a/llvm/__init__.py b/llvm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/llvm/__pycache__/__init__.cpython-37.pyc b/llvm/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000..114cde0 Binary files /dev/null and b/llvm/__pycache__/__init__.cpython-37.pyc differ diff --git a/llvm/__pycache__/codegen.cpython-37.pyc b/llvm/__pycache__/codegen.cpython-37.pyc new file mode 100644 index 0000000..00b19f8 Binary files /dev/null and b/llvm/__pycache__/codegen.cpython-37.pyc differ diff --git a/llvm/codegen.py b/llvm/codegen.py new file mode 100644 index 0000000..bc55027 --- /dev/null +++ b/llvm/codegen.py @@ -0,0 +1,58 @@ +""" +Code based on tutorial found on medium +https://blog.usejournal.com/writing-your-own-programming-language-and-compiler-with-python-a468970ae6df + +Author: Marcelo Andrade +""" + +from llvmlite import ir, binding + + +class CodeGen(): + + def __init__(self): + self.binding = binding + self.binding.initialize() + self.binding.initialize_native_target() + self.binding.initialize_native_asmprinter() + self._baseConfig() + self._createEngine() + self._declarePrint() + + def _baseConfig(self): + self.module = ir.Module(name=__file__) + self.module.triple = self.binding.get_default_triple() + func_type = ir.FunctionType(ir.VoidType(), [], False) + base_func = ir.Function(self.module, func_type, name="main") + block = base_func.append_basic_block(name="entry") + self.builder = ir.IRBuilder(block) + + def _createEngine(self): + target = self.binding.Target.from_default_triple() + target_machine = target.create_target_machine() + backing_mod = binding.parse_assembly("") + engine = binding.create_mcjit_compiler(backing_mod, target_machine) + self.engine = engine + + def _declarePrint(self): + voidptr_ty = ir.IntType(8).as_pointer() + printf_ty = ir.FunctionType(ir.IntType(32), [voidptr_ty], var_arg=True) + printf = ir.Function(self.module, printf_ty, name="printf") + self.printf = printf + + def _compile(self): + self.builder.ret_void() + llvm_ir = str(self.module) + mod = self.binding.parse_assembly(llvm_ir) + mod.verify() + self.engine.add_module(mod) + self.engine.finalize_object() + self.engine.run_static_constructors() + return mod + + def createIr(self): + self._compile() + + def saveIr(self, filename): + with open(filename, 'w') as f: + f.write(str(self.module)) diff --git a/main.py b/main.py index b01aa01..cfe1419 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,7 @@ -from parser import DothParser -from lexical import DothLexer -from symbolTable import SymbolTable +from compiler.parser import DothParser +from compiler.lexical import DothLexer +from helpers.symbolTable import SymbolTable +from llvm.codegen import CodeGen import sys if __name__ == '__main__': @@ -9,13 +10,17 @@ f = sys.argv[1] with open(f, "r") as tmp: code = tmp.read() + + codegen = CodeGen() lexer = DothLexer() - parser = DothParser() + parser = DothParser(codegen.module, codegen.builder, codegen.printf) table = SymbolTable() if code: tokens = lexer.tokenize(code) nodes = parser.parse(tokens) nodes.evaluate(table) + codegen.createIr() + codegen.saveIr('./out/output.ll') else: while True: try: diff --git a/makefile b/makefile new file mode 100644 index 0000000..eb71361 --- /dev/null +++ b/makefile @@ -0,0 +1,16 @@ +all: generate_llvm parse_llvm compile_gcc run + +generate_llvm: + python3 main.py ./examples/test.dt + +parse_llvm: + /usr/local/Cellar/llvm/12.0.0_1/bin/llc -filetype=obj ./out/output.ll + +compile_gcc: + gcc ./out/output.o -o ./out/output + +run: + ./out/output + +config_env: + pip3 install -r requirements.txt diff --git a/nodes.py b/nodes.py deleted file mode 100644 index 32f0eef..0000000 --- a/nodes.py +++ /dev/null @@ -1,280 +0,0 @@ -from typing import List, NoReturn, Union, Set -from sly.lex import Token -from symbolTable import SymbolTable - - -class Node: - def __init__(self, value: Token): - self.value = value - self.children: List[Node] = [] - - def evaluate(self, table: SymbolTable) -> int: - return 0 - - -class Block(Node): - - def __init__(self, value: Token): - super().__init__(value) - - def addChild(self, child: Node) -> NoReturn: - self.children.append(child) - - def evaluate(self, table) -> NoReturn: - for child in self.children: - child.evaluate(table) - - -class DefFuncOp(Node): - - def __init__(self, value: Token): - super().__init__(value) - - def evaluate(self, table): - return 0 - - -class IfOp(Node): - - condition: Node - commandTrue: Node - commandElse: Node - elifClauses: List[List[Node]] - - def __init__(self, value: Token, condition: Node, commandTrue: Node): - super().__init__(value) - self.condition = condition - self.commandTrue = commandTrue - self.commandElse = None - self.elifClauses = [] - - def setElse(self, commandElse: Node) -> NoReturn: - self.commandElse = commandElse - - def addElif(self, clause: Node, command: Node) -> NoReturn: - self.elifClauses.append([clause, command]) - - def evaluate(self, table: SymbolTable) -> NoReturn: - if self.condition.evaluate(table)[0]: - self.commandTrue.evaluate(table) - else: - for clause in self.elifClauses: - if clause[0].evaluate(table)[0]: - clause[1].evaluate(table) - return - if self.commandElse != None: - self.commandElse.evaluate(table) - - -class LoopOp(Node): - - condition: Node - command: Node - - def __init__(self, value: Token, condition: Node, command: Node): - super().__init__(value) - self.condition = condition - self.command = command - - def evaluate(self, table: SymbolTable) -> NoReturn: - while self.condition.evaluate(table)[0]: - self.command.evaluate(table) - - -class DeclarationOp(Node): - - var_type: Token - expr: Node - var_dict = { - 'TINT': 'int', - 'TFLOAT': 'float', - 'TSTRING': 'string', - 'BOOL': 'bool', - } - - def __init__(self, value: Token, var_type: Token, expr: Node): - super().__init__(value) - self.var_type = self.var_dict[var_type.type] - self.expr = expr - - def evaluate(self, table: SymbolTable): - table.declareVariable( - self.value.value, self.expr.evaluate(table)[0], self.var_type) - return table - - -class AssignmentOp(Node): - - expr: Node - - def __init__(self, value: Token, expr: Node): - super().__init__(value) - self.expr = expr - - def evaluate(self, table: SymbolTable): - expr = self.expr.evaluate(table) - table.setVariable(self.value.value, - expr[0], expr[1]) - return table - - -class ReturnOp(Node): - - expr: Node - - def __init__(self, value: Token, expr: Node): - super().__init__(value) - self.expr = expr - - def evaluate(self, table): - return self.expr.evaluate(table) - - -class CallFunOp(Node): - - expr: Node - params: List[Node] - - def __init__(self, value: Token, params: List[Node]): - super().__init__(value) - self.params = params - - def evaluate(self, table): - return super().evaluate(table) - - -class PrintOp(Node): - - expr: Node - - def __init__(self, value: Token, expr: Node): - super().__init__(value) - self.expr = expr - - def evaluate(self, table): - print(self.expr.evaluate(table)[0]) - - -class BinOp(Node): - - def __init__(self, value: Token, left: Node, right: Node): - super().__init__(value) - self.children = [left, right] - - def evaluate(self, table: SymbolTable): - children0 = self.children[0].evaluate(table) - children1 = self.children[1].evaluate(table) - operation_result = None - var_type = None - if self.value.type == 'PLUS': - operation_result = children0[0] + children1[0] - if children0[1] == 'int' and children1[1] == 'int': - var_type = 'int' - elif 'float' in [children0[1], children1[1]]: - var_type = 'float' - elif self.value.type == 'MINUS': - operation_result = children0[0] - children1[0] - if children0[1] == 'int' and children1[1] == 'int': - var_type = 'int' - elif 'float' in [children0[1], children1[1]]: - var_type = 'float' - elif self.value.type == 'TIMES': - operation_result = children0[0] * children1[0] - if children0[1] == 'int' and children1[1] == 'int': - var_type = 'int' - elif 'float' in [children0[1], children1[1]]: - var_type = 'float' - elif self.value.type == 'DIVIDE': - operation_result = children0[0] / children1[0] - var_type = "float" - elif self.value.type == 'EQ': - operation_result = children0[0] == children1[0] - var_type = "bool" - elif self.value.type == 'OR': - operation_result = children0[0] or children1[0] - var_type = "bool" - elif self.value.type == 'AND': - operation_result = children0[0] and children1[0] - var_type = "bool" - elif self.value.type == 'GT': - operation_result = children0[0] > children1[0] - var_type = "bool" - elif self.value.type == 'LT': - operation_result = children0[0] < children1[0] - var_type = "bool" - else: - raise BufferError(f"Invalid operation of type {self.value.type}") - return operation_result, var_type - - -class UnOp(Node): - - expr: Node - - def __init__(self, value: Token, expr: Node): - super().__init__(value) - self.expr = expr - - def evaluate(self, table): - if self.value.type == 'UPLUS' or self.value.type == 'PLUS': - return self.expr.evaluate(table) - elif self.value.type == 'UMINUS' or self.value.type == 'MINUS': - return -self.expr.evaluate(table) - elif self.value.type == 'UNOT' or self.value.type == 'NOT': - return not self.expr.evaluate(table) - else: - raise BufferError(f"Invalid operation of type {self.value.type}") - - -class IntVal(Node): - - def __init__(self, value: Token): - super().__init__(value) - - def evaluate(self, table: SymbolTable): - return int(self.value.value), "int" - - -class FloatVal(Node): - - def __init__(self, value: Token): - super().__init__(value) - - def evaluate(self, table: SymbolTable): - return float(self.value.value), "float" - - -class StringVal(Node): - - def __init__(self, value: Token): - super().__init__(value) - - def evaluate(self, table: SymbolTable): - return self.value.value, "string" - - -class BoolVal(Node): - - def __init__(self, value: Token): - super().__init__(value) - - def evaluate(self, table: SymbolTable): - return bool(self.value.value), "bool" - - -class IdentifierVal(Node): - - def __init__(self, value: Token): - super().__init__(value) - - def evaluate(self, table: SymbolTable): - return table.getVariable(self.value.value) - - -class NoOp(Node): - - def __init__(self, value: Token): - super().__init__(value) - - def evaluate(self, table: SymbolTable): - return 0, "void" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f4d0df4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +autopep8==1.5.7 +llvmlite==0.36.0 +pycodestyle==2.7.0 +sly==0.4 +toml==0.10.2 diff --git a/symbolTable.py b/symbolTable.py deleted file mode 100644 index 18836a2..0000000 --- a/symbolTable.py +++ /dev/null @@ -1,38 +0,0 @@ -from typing import NoReturn, Dict, Union -from sly.lex import Token - - -class SymbolTable: - - table: Dict[str, Token] - - def __init__(self): - self.table = {} - - def getVariable(self, variable: str) -> Token: - if variable in self.table: - return self.table[variable]["value"], self.table[variable]["varType"] - raise KeyError(f"No variable named {variable}") - - def declareVariable( - self, variable: str, value: Union[int, str, bool], varType: str - ) -> NoReturn: - if variable in self.table: - raise NameError(f"Variable {variable} already declared") - self.table[variable] = {"value": value, "varType": varType} - - def setVariable( - self, variable: str, value: Union[int, str, bool], varType: str - ) -> NoReturn: - if variable not in self.table: - raise NameError(f"Variable {variable} not declared") - elif varType != self.table[variable]["varType"]: - if varType in ['int', 'float'] and self.table[variable]["varType"] == 'bool': - value = bool(value) - elif varType in ['bool', 'float'] and self.table[variable]["varType"] == 'int': - value = int(value) - elif varType in ['int', 'bool'] and self.table[variable]["varType"] == 'float': - value = float(value) - else: - raise TypeError() - self.table[variable]["value"] = value