├── .gitignore ├── README.md ├── __init__.py ├── elm_lexer.py ├── elm_parser.py ├── elm_types.py ├── example.elm ├── llvm_gen.py ├── ls-lah.txt ├── main.py ├── parsetab.py ├── repl.py ├── requirements.txt ├── test_elm_lexer.py └── test_elm_parser.py /.gitignore: -------------------------------------------------------------------------------- 1 | python-elm/* 2 | __pycache__/* 3 | *.ll 4 | *.s 5 | *.output 6 | *.out 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | EDIT: There were some typos which someone kindly corrected in a pull request. 2 | As I had to run for the next train I had no time to properly document the process. 3 | I posted in this twitt some of the screenshots I casually took during the process screenshots: 4 | 5 | [Screenshots](https://twitter.com/nudpiedo/status/1599444651462733824?s=20&t=r4OSZwzNu9GOI4XYPqi8Wg) 6 | 7 | NOTE: Everything this repository with the exception of this own note has been 8 | generated entirely with chatGPT and asked gradually how to correct or refactor each one 9 | of the errors that showed up on the terminal. 10 | 11 | For that I followed as guidelines these both articles: 12 | [Building A Virtual Machine inside ChatGPT](https://www.engraved.blog/building-a-virtual-machine-inside/) 13 | [Building an interpreter for my own programming language in ChatGPT](https://6502.is-a.dev/posts/aoc-2022/) 14 | 15 | It took me around two hours, where ChatGPT was the main pilot and I became the copilot, trying to run 16 | the project and asking GPT to refactor the parts of the code that didn't run, showing the console output mistake and how would it fix the given function etc. 17 | 18 | I could ask to change the tests form unittest to pytest and similar tasks... and it did so, The files I ask it to "cat" them and just copied them on my filesystem. 19 | 20 | The language doesn't completely run, but it was just a two-hour experiment. 21 | 22 | The day GPT is the pilot and humans become the copilot in a pair programming role is almost here. 23 | 24 | 25 | Actual generated README code begins here: 26 | 27 | ---- 28 | 29 | 30 | Elm-python 31 | ========= 32 | 33 | Elm-python is an experimental programming language. It takes inspiration from 34 | Clojure and Elm, and is implemented in Python. It compiles to LLVM. 35 | 36 | Features 37 | -------- 38 | 39 | - Immutable data structures 40 | - First-class functions 41 | - Pattern matching 42 | - Algebraic data types 43 | 44 | Usage 45 | ----- 46 | 47 | Run the REPL: 48 | 49 | $ python3 -m elm_python.repl 50 | 51 | Copy code 52 | 53 | Compile a file: 54 | 55 | $ python3 -m elm_python.main my_program.elm 56 | 57 | Copy code 58 | 59 | Run tests: 60 | 61 | $ pytest 62 | 63 | Copy code 64 | 65 | Roadmap 66 | ------- 67 | 68 | - Type inference 69 | - Compiler optimizations 70 | - Standard library 71 | 72 | License 73 | ------- 74 | 75 | MIT 76 | 77 | 78 | 79 | All the python code lines are: 80 | 81 | ls -1 *.py|cat|wc -l 82 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | from .elm_parser import parse 2 | from .elm_lexer import lex 3 | from .elm_types import ( 4 | Program, 5 | Expr, 6 | Literal, 7 | Ident, 8 | Tuple, 9 | List_, 10 | Record, 11 | Field, 12 | BinOp, 13 | UnOp, 14 | Let, 15 | LetRec, 16 | Match, 17 | Lambda, 18 | FunctionCall, 19 | ) 20 | from .llvm_gen import compile_program 21 | -------------------------------------------------------------------------------- /elm_lexer.py: -------------------------------------------------------------------------------- 1 | import ply.lex as lex 2 | 3 | # List of token names. This is always required 4 | tokens = ( 5 | "ADD", 6 | "SUB", 7 | "MUL", 8 | "DIV", 9 | "MOD", 10 | "LPAREN", 11 | "RPAREN", 12 | "LBRACKET", 13 | "RBRACKET", 14 | "LBRACE", 15 | "RBRACE", 16 | "LT", 17 | "GT", 18 | "EQ", 19 | "INTEGER", 20 | "FLOAT", 21 | "STRING", 22 | "LET", 23 | "REC", 24 | "DEFINE", 25 | "IN", 26 | "MATCH", 27 | "WITH", 28 | "BAR", 29 | "ARROW", 30 | "LAMBDA", 31 | "NOT", 32 | "UMINUS", 33 | "IDENT", 34 | "COMMA", 35 | "COLON", 36 | ) 37 | 38 | # Regular expression rules for simple tokens 39 | t_ADD = r"\+" 40 | t_SUB = r"-" 41 | t_MUL = r"\*" 42 | t_DIV = r"/" 43 | t_MOD = r"%" 44 | t_LPAREN = r"\(" 45 | t_RPAREN = r"\)" 46 | t_LBRACKET = r"\[" 47 | t_RBRACKET = r"\]" 48 | t_LBRACE = r"\{" 49 | t_RBRACE = r"\}" 50 | t_LT = r"<" 51 | t_GT = r">" 52 | t_EQ = r"=" 53 | t_LET = r"let" 54 | t_REC = r"rec" 55 | t_DEFINE = r"=" 56 | t_IN = r"in" 57 | t_MATCH = r"match" 58 | t_WITH = r"with" 59 | t_BAR = r"\|" 60 | t_ARROW = r"->" 61 | t_LAMBDA = r"\\" 62 | 63 | 64 | 65 | # A string containing ignored characters (spaces and tabs) 66 | t_ignore = " \t" 67 | 68 | # Define a rule for the UMINUS token 69 | def t_UMINUS(t): 70 | r'-' 71 | # If the next token is a number, return a UMINUS token 72 | if t.lexer.peek() in "0123456789": 73 | return t 74 | 75 | def t_INTEGER(t): 76 | r"\d+" 77 | t.value = int(t.value) 78 | return t 79 | 80 | def t_NOT(t): 81 | r'not' 82 | return t 83 | 84 | def t_FLOAT(t): 85 | r"\d+\.\d+" 86 | t.value = float(t.value) 87 | return t 88 | 89 | 90 | def t_STRING(t): 91 | r'"[^"]*"' 92 | t.value = str(t.value) 93 | return t 94 | 95 | 96 | def t_IDENT(t): 97 | r"[a-zA-Z_][a-zA-Z0-9_]*" 98 | return t 99 | 100 | 101 | def t_COMMA(t): 102 | r"," 103 | return t 104 | 105 | 106 | def t_COLON(t): 107 | r":" 108 | return t 109 | 110 | 111 | # Define a rule so we can track line numbers 112 | def t_newline(t): 113 | r"\n+" 114 | t.lexer.lineno += len(t.value) 115 | 116 | 117 | # Error handling rule 118 | def t_error(t): 119 | print(f"Illegal character '{t.value[0]}'") 120 | t.lexer.skip(1) 121 | 122 | 123 | # Build the lexer 124 | lexer = lex.lex() 125 | 126 | -------------------------------------------------------------------------------- /elm_parser.py: -------------------------------------------------------------------------------- 1 | from elm_lexer import tokens 2 | from elm_types import ( 3 | Program, 4 | Expr, 5 | Literal, 6 | Ident, 7 | Tuple, 8 | List_, 9 | Record, 10 | Field, 11 | BinOp, 12 | UnOp, 13 | Let, 14 | LetRec, 15 | Match, 16 | Lambda, 17 | FunctionCall, 18 | ) 19 | 20 | # Grammar 21 | # List of operator precedence rules 22 | # (order matters) 23 | precedence = ( 24 | ('right', 'UMINUS'), 25 | ('right', 'NOT'), 26 | ) 27 | 28 | 29 | def p_program(p): 30 | "program : expressions" 31 | p[0] = Program(p[1]) 32 | 33 | 34 | def p_expressions(p): 35 | """ 36 | expressions : expressions expression 37 | | expression 38 | """ 39 | if len(p) == 2: 40 | p[0] = [p[1]] 41 | else: 42 | p[0] = p[1] + [p[2]] 43 | 44 | 45 | def p_expression_integer(p): 46 | "expression : INTEGER" 47 | p[0] = Literal(int(p[1])) 48 | 49 | 50 | def p_expression_float(p): 51 | "expression : FLOAT" 52 | p[0] = Literal(float(p[1])) 53 | 54 | 55 | def p_expression_string(p): 56 | "expression : STRING" 57 | p[0] = Literal(p[1][1:-1]) 58 | 59 | 60 | def p_expression_ident(p): 61 | "expression : IDENT" 62 | p[0] = Ident(p[1]) 63 | 64 | 65 | def p_expression_tuple(p): 66 | "expression : LPAREN tuple_expr RPAREN" 67 | p[0] = Tuple(p[2]) 68 | 69 | 70 | def p_tuple_expr(p): 71 | """ 72 | tuple_expr : tuple_expr COMMA expression 73 | | expression 74 | """ 75 | if len(p) == 2: 76 | p[0] = [p[1]] 77 | else: 78 | p[0] = p[1] + [p[3]] 79 | 80 | 81 | def p_expression_list(p): 82 | "expression : LBRACKET list_expr RBRACKET" 83 | p[0] = List_(p[2]) 84 | 85 | 86 | def p_list_expr(p): 87 | """ 88 | list_expr : list_expr COMMA expression 89 | | expression 90 | """ 91 | if len(p) == 2: 92 | p[0] = [p[1]] 93 | else: 94 | p[0] = p[1] + [p[3]] 95 | 96 | 97 | def p_expression_record(p): 98 | "expression : LBRACE record_expr RBRACE" 99 | p[0] = Record(p[2]) 100 | 101 | 102 | def p_record_expr(p): 103 | """ 104 | record_expr : record_expr COMMA record_field 105 | | record_field 106 | """ 107 | if len(p) == 2: 108 | p[0] = [p[1]] 109 | else: 110 | p[0] = p[1] + [p[3]] 111 | 112 | 113 | def p_record_field(p): 114 | "record_field : IDENT COLON expression" 115 | p[0] = Field(p[1], p[3]) 116 | 117 | 118 | def p_expression_binop(p): 119 | """ 120 | expression : expression ADD expression 121 | | expression SUB expression 122 | | expression MUL expression 123 | | expression DIV expression 124 | | expression MOD expression 125 | | expression LT expression 126 | | expression GT expression 127 | | expression EQ expression 128 | """ 129 | p[0] = BinOp(p[2], p[1], p[3]) 130 | 131 | def p_expression_not(p): 132 | 'expression : NOT expression' 133 | p[0] = UnOp('not', p[2]) 134 | 135 | def p_expression_unop(p): 136 | 'expression : UMINUS expression' 137 | p[0] = UnOp('-', p[2]) 138 | 139 | 140 | def p_expression_let(p): 141 | "expression : LET ident_list DEFINE expressions IN expression" 142 | p[0] = Let(p[2], p[4], p[6]) 143 | 144 | 145 | def p_ident_list(p): 146 | """ 147 | ident_list : ident_list COMMA IDENT 148 | | IDENT 149 | """ 150 | if len(p) == 2: 151 | p[0] = [p[1]] 152 | else: 153 | p[0] = p[1] + [p[3]] 154 | 155 | 156 | def p_expression_let_rec(p): 157 | "expression : LET REC ident_expr_list IN expressions" 158 | p[0] = LetRec(p[3], p[5]) 159 | 160 | 161 | def p_ident_expr_list(p): 162 | """ 163 | ident_expr_list : ident_expr_list COMMA ident_expr 164 | | ident_expr 165 | """ 166 | if len(p) == 2: 167 | p[0] = [p[1]] 168 | else: 169 | p[0] = p[1] + [p[3]] 170 | 171 | 172 | def p_ident_expr(p): 173 | "ident_expr : IDENT DEFINE expression" 174 | p[0] = (p[1], p[3]) 175 | 176 | 177 | def p_expression_match(p): 178 | "expression : MATCH expression WITH match_expr_list" 179 | p[0] = Match(p[2], p[4]) 180 | 181 | 182 | def p_match_expr_list_single(p): 183 | "match_expr_list : match_expr" 184 | p[0] = [p[1]] 185 | 186 | def p_match_expr_list_multiple(p): 187 | "match_expr_list : match_expr_list match_expr" 188 | p[0] = p[1] + [p[2]] 189 | 190 | def p_match_expr(p): 191 | "match_expr : BAR pattern ARROW expression" 192 | p[0] = (p[2], p[4]) 193 | 194 | 195 | def p_pattern_literal(p): 196 | """ 197 | pattern : INTEGER 198 | | FLOAT 199 | | STRING 200 | """ 201 | if p[1][0] == '"': 202 | p[0] = Literal(p[1][1:-1]) 203 | else: 204 | p[0] = Literal(int(p[1])) 205 | 206 | 207 | def p_pattern_ident(p): 208 | "pattern : IDENT" 209 | p[0] = Ident(p[1]) 210 | 211 | 212 | def p_pattern_tuple(p): 213 | "pattern : LPAREN pattern_list RPAREN" 214 | p[0] = Tuple(p[2]) 215 | 216 | 217 | def p_pattern_list(p): 218 | """ 219 | pattern_list : pattern_list COMMA pattern 220 | | pattern 221 | """ 222 | if len(p) == 2: 223 | p[0] = [p[1]] 224 | else: 225 | p[0] = p[1] + [p[3]] 226 | 227 | 228 | def p_expression_lambda(p): 229 | "expression : LAMBDA pattern_list ARROW expression" 230 | p[0] = Lambda(p[2], p[4]) 231 | 232 | 233 | def p_expression_function_call(p): 234 | "expression : expression LPAREN argument_list RPAREN" 235 | p[0] = FunctionCall(p[1], p[3]) 236 | 237 | 238 | def p_argument_list(p): 239 | """ 240 | argument_list : argument_list COMMA expression 241 | | expression 242 | """ 243 | if len(p) == 2: 244 | p[0] = [p[1]] 245 | else: 246 | p[0] = p[1] + [p[3]] 247 | 248 | 249 | # Error rule for syntax errors 250 | def p_error(p): 251 | print(f"Syntax error in input: {p}") 252 | 253 | 254 | # import yacc: yacc.yacc() 255 | from ply import yacc 256 | from elm_lexer import lexer 257 | 258 | def parse(text: str) -> Program: 259 | 260 | """Parse a string containing Elm-like code into an AST. 261 | 262 | Args: 263 | text (str): The string containing the code to parse. 264 | 265 | Returns: 266 | Program: The AST representation of the code. 267 | """ 268 | # implement the yacc parser with the tokens, lexer , precedence and grammar 269 | # defined above 270 | # use the yacc.parse() method to parse the text 271 | # return the result 272 | parser = yacc.yacc() 273 | return parser.parse(text, lexer=lexer) 274 | 275 | # parser = yacc.yacc() 276 | # return parser(text, 277 | # lexer=lexer, 278 | # tokenfunc=tokens, 279 | # precedence=precedence, 280 | # debug=False, 281 | # tracking=True) 282 | 283 | 284 | -------------------------------------------------------------------------------- /elm_types.py: -------------------------------------------------------------------------------- 1 | from collections.abc import MutableSequence 2 | 3 | 4 | class Program: 5 | def __init__(self, expressions): 6 | self.expressions = expressions 7 | 8 | 9 | class Expr: 10 | pass 11 | 12 | 13 | class Literal(Expr): 14 | def __init__(self, value): 15 | self.value = value 16 | 17 | 18 | class Ident(Expr): 19 | def __init__(self, name): 20 | self.name = name 21 | 22 | 23 | class Tuple(Expr, MutableSequence): 24 | def __init__(self, elements): 25 | self.elements = elements 26 | 27 | def __getitem__(self, index): 28 | return self.elements[index] 29 | 30 | def __setitem__(self, index, value): 31 | self.elements[index] = value 32 | 33 | def __delitem__(self, index): 34 | del self.elements[index] 35 | 36 | def __len__(self): 37 | return len(self.elements) 38 | 39 | def insert(self, index, value): 40 | self.elements.insert(index, value) 41 | 42 | 43 | class List_(Expr, MutableSequence): 44 | def __init__(self, elements): 45 | self.elements = elements 46 | 47 | def __getitem__(self, index): 48 | return self.elements[index] 49 | 50 | def __setitem__(self, index, value): 51 | self.elements[index] = value 52 | 53 | def __delitem__(self, index): 54 | del self.elements[index] 55 | 56 | def __len__(self): 57 | return len(self.elements) 58 | 59 | def insert(self, index, value): 60 | self.elements.insert(index, value) 61 | 62 | 63 | class Record(Expr): 64 | def __init__(self, fields): 65 | self.fields = fields 66 | 67 | 68 | class Field: 69 | def __init__(self, name, value): 70 | self.name = name 71 | self.value = value 72 | 73 | 74 | class BinOp(Expr): 75 | def __init__(self, op, left, right): 76 | self.op = op 77 | self.left = left 78 | self.right = right 79 | 80 | 81 | class UnOp(Expr): 82 | def __init__(self, op, operand): 83 | self.op = op 84 | self.operand = operand 85 | 86 | 87 | class Let(Expr): 88 | def __init__(self, names, expressions, body): 89 | self.names = names 90 | self.expressions = expressions 91 | self.body = body 92 | 93 | 94 | class LetRec(Expr): 95 | def __init__(self, functions, body): 96 | self.functions = functions 97 | self.body = body 98 | 99 | 100 | class Match(Expr): 101 | def __init__(self, expr, cases): 102 | self.expr = expr 103 | self.cases = cases 104 | 105 | 106 | class Lambda(Expr): 107 | def __init__(self, arguments, body): 108 | self.arguments = arguments 109 | self.body = body 110 | 111 | 112 | class FunctionCall(Expr): 113 | def __init__(self, function, arguments): 114 | self.function = function 115 | self.arguments = arguments 116 | -------------------------------------------------------------------------------- /example.elm: -------------------------------------------------------------------------------- 1 | # Define a function that takes a list and a value, and returns a new list 2 | # with the value appended to the end. 3 | def append(list, value): 4 | # Create a new list by concatenating the original list and a single-element 5 | # list containing the value. 6 | return list ++ [value] 7 | 8 | # Define a function that takes a list and a value, and returns a new list 9 | # with the value prepended to the beginning. 10 | def prepend(list, value): 11 | # Create a new list by concatenating a single-element list containing 12 | # the value, and the original list. 13 | return [value] ++ list 14 | 15 | # Define a main function that is called when the program is run. 16 | def main(): 17 | # Create a list of numbers from 1 to 10. 18 | numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 19 | 20 | # Print the original list. 21 | print(numbers) 22 | 23 | # Append the number 11 to the end of the list. 24 | numbers = append(numbers, 11) 25 | 26 | # Print the modified list. 27 | print(numbers) 28 | 29 | # Prepend the number 0 to the beginning of the list. 30 | numbers = prepend(numbers, 0) 31 | 32 | # Print the modified list. 33 | print(numbers) 34 | 35 | # Call the main function when the program is run. 36 | main() 37 | -------------------------------------------------------------------------------- /llvm_gen.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from llvmlite import ir 4 | import llvmlite.binding as lib 5 | from elm_types import BinOp, Literal, Program 6 | 7 | 8 | class LLVMGenerator: 9 | def __init__(self): 10 | self.llvm_ir = "" 11 | self.__initialize_llvm() 12 | 13 | def __initialize_llvm(self): 14 | lib.initialize_native_target() 15 | lib.initialize_native_asmprinter() 16 | 17 | 18 | def generate_llvm_ir(self, program: Any) -> str: 19 | self.__reset_llvm_state() 20 | self.__visit_program(program) 21 | return self.llvm_ir 22 | 23 | def __reset_llvm_state(self): 24 | self.llvm_ir = "" 25 | self.module = ir.Module() 26 | self.builder = None 27 | self.function_name_to_function = {} 28 | 29 | def __visit_program(self, program: Any): 30 | self.builder = ir.IRBuilder() 31 | for expression in program.expressions: 32 | self.__visit_expression(expression) 33 | 34 | def __visit_expression(self, expression: Any): 35 | method_name = f"__visit_expression_{expression.__class__.__name__}" 36 | method = getattr(self, method_name, self.__visit_expression_default) 37 | return method(expression) 38 | 39 | def __visit_expression_default(self, expression: Any): 40 | raise NotImplementedError(f"No visitor method for {expression}") 41 | 42 | def __visit_expression_Literal(self, expression: Any): 43 | if isinstance(expression.value, int): 44 | return self.builder.constant(ir.IntType(64), expression.value) 45 | elif isinstance(expression.value, float): 46 | return self.builder.constant(ir.DoubleType(), expression.value) 47 | elif isinstance(expression.value, str): 48 | value = expression.value 49 | string = ir.Constant(ir.ArrayType(ir.IntType(8), len( 50 | value)), bytearray(value.encode("utf-8"))) 51 | return self.builder.gep(string, [self.builder.constant(ir.IntType(64), 0), self.builder.constant(ir.IntType(64), 0)]) 52 | else: 53 | raise NotImplementedError(f"No visitor method for {expression}") 54 | 55 | def __visit_expression_Ident(self, expression: Any): 56 | name = expression.name 57 | if name in self.function_name_to_function: 58 | return self.function_name_to_function[name] 59 | else: 60 | return self.builder.alloca(ir.IntType(64), name=name) 61 | 62 | 63 | def __visit_expression_BinOp(self, expression: Any): 64 | lhs = self.__visit_expression(expression.left) 65 | rhs = self.__visit_expression(expression.right) 66 | if expression.op == "+": 67 | return self.builder.add(lhs, rhs) 68 | elif expression.op == "-": 69 | return self.builder.sub(lhs, rhs) 70 | elif expression.op == "*": 71 | return self.builder.mul(lhs, rhs) 72 | elif expression.op == "/": 73 | return self.builder.sdiv(lhs, rhs) 74 | elif expression.op == "<": 75 | return self.builder.icmp_signed("<", lhs, rhs) 76 | elif expression.op == ">": 77 | return self.builder.icmp_signed(">", lhs, rhs) 78 | elif expression.op == "=": 79 | return self.builder.icmp_signed("==", lhs, rhs) 80 | else: 81 | raise NotImplementedError(f"No visitor method for {expression}") 82 | 83 | 84 | def __visit_expression_UnOp(self, expression: Any): 85 | operand = self.__visit_expression(expression.operand) 86 | if expression.op == "-": 87 | return self.builder.neg(operand) 88 | elif expression.op == "not": 89 | return self.builder.not_(operand) 90 | else: 91 | raise NotImplementedError(f"No visitor method for {expression}") 92 | 93 | 94 | def __visit_expression_Let(self, expression: Any): 95 | names = expression.names 96 | values = [self.__visit_expression(expr) 97 | for expr in expression.expressions] 98 | for name, value in zip(names, values): 99 | self.builder.store(value, self.builder.alloca( 100 | ir.IntType(64), name=name)) 101 | return self.__visit_expression(expression.body) 102 | 103 | 104 | def __visit_expression_LetRec(self, expression: Any): 105 | for func in expression.functions: 106 | self.__visit_function(func) 107 | return self.__visit_expression(expression.body) 108 | 109 | 110 | def __visit_function(self, func: Any): 111 | function_type = ir.FunctionType(ir.IntType( 112 | 64), [ir.IntType(64)] * len(func.arguments)) 113 | function = ir.Function(self.module, function_type, func.name) 114 | self.function_name_to_function[func.name] = function 115 | 116 | bb_entry = function.append_basic_block(name="entry") 117 | self.builder = ir.IRBuilder(bb_entry) 118 | 119 | for arg, name in zip(function.args, func.arguments): 120 | self.builder.store(arg, self.builder.alloca( 121 | ir.IntType(64), name=name)) 122 | 123 | self.__visit_expression(func.body) 124 | 125 | if isinstance(function.terminator, ir.Terminator): 126 | return 127 | 128 | retval = self.__visit_expression(Literal(0)) 129 | self.builder.ret(retval) 130 | 131 | self.builder.ret(retval) 132 | 133 | def __visit_expression_FunctionCall(self, expression: Any): 134 | function = self.__visit_expression(expression.function) 135 | arguments = [self.__visit_expression( 136 | arg) for arg in expression.arguments] 137 | return self.builder.call(function, arguments) 138 | 139 | def __visit_expression_Match(self, expression: Any): 140 | match_value = self.__visit_expression(expression.expr) 141 | 142 | for case in expression.cases: 143 | with self.builder.if_then( 144 | self.__visit_expression( 145 | BinOp(case.pattern, "=", expression.expr) 146 | ) 147 | ) as (then, _): 148 | self.builder = then 149 | self.__visit_expression(case.body) 150 | return 151 | 152 | def __finalize_llvm(self): 153 | self.llvm_ir = str(self.module) 154 | 155 | 156 | def run_llvm_ir(llvm_ir: str) -> float: 157 | # compile the llvm ir 158 | llvm_ir = bytes(llvm_ir, "utf8") 159 | module = lib.add_global("main_module") 160 | lib.LLVMParseIR(module, llvm_ir, len(llvm_ir), lib.ffi.NULL) 161 | 162 | # verify the module 163 | error = lib.ffi.new("char **") 164 | if lib.LLVMVerifyModule(module, lib.LLVMReturnStatusAction, error): 165 | raise ValueError(lib.ffi.string(error[0]).decode("utf8")) 166 | 167 | # optimize the module 168 | pass_manager = lib.LLVMCreatePassManager() 169 | lib.LLVMAddConstantPropagationPass(pass_manager) 170 | lib.LLVMAddInstructionCombiningPass(pass_manager) 171 | lib.LLVMAddPromoteMemoryToRegisterPass(pass_manager) 172 | lib.LLVMAddGVNPass(pass_manager) 173 | lib.LLVMAddCFGSimplificationPass(pass_manager) 174 | lib.LLVMRunPassManager(pass_manager, module) 175 | 176 | # create a JIT engine 177 | engine = lib.LLVMCreateJITCompilerForModule(module, 2) 178 | 179 | # create a function pointer 180 | function_ptr = lib.LLVMGetPointerToGlobal( 181 | engine, lib.LLVMGetNamedFunction(module, b"main")) 182 | function_ptr = lib.ffi.cast("double (*)()", function_ptr) 183 | 184 | # run the function and return its result 185 | result = function_ptr() 186 | # dispose of the JIT engine 187 | lib.LLVMDisposeExecutionEngine(engine) 188 | # dispose of the module 189 | lib.LLVMDisposeModule(module) 190 | return result 191 | 192 | 193 | def compile_program(program: Program) -> str: 194 | generator = LLVMGenerator() 195 | generator.visit_program(program) 196 | return str(generator.module) 197 | -------------------------------------------------------------------------------- /ls-lah.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vrescobar/chatGPT-python-elm/6d7bb191d889bb25f6f39099f8ee414aaf51f814/ls-lah.txt -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import sys 3 | 4 | from elm_parser import parse 5 | from elm_types import Program 6 | from llvm_gen import compile_program 7 | 8 | 9 | def main(): 10 | parser = argparse.ArgumentParser() 11 | # parser.add_argument("input_file", type=argparse.FileType("r")) 12 | parser.add_argument("filename", type=str) 13 | args = parser.parse_args() 14 | 15 | with open(args.filename) as f: 16 | text = f.read() 17 | 18 | program = parse(text) 19 | assert isinstance(program, Program) 20 | llvm_ir = compile_program(program) 21 | print(llvm_ir) 22 | 23 | 24 | if __name__ == "__main__": 25 | sys.exit(main()) 26 | -------------------------------------------------------------------------------- /parsetab.py: -------------------------------------------------------------------------------- 1 | 2 | # parsetab.py 3 | # This file is automatically generated. Do not edit. 4 | # pylint: disable=W,C,R 5 | _tabversion = '3.10' 6 | 7 | _lr_method = 'LALR' 8 | 9 | _lr_signature = 'rightUMINUSrightNOTADD ARROW BAR COLON COMMA DEFINE DIV EQ FLOAT GT IDENT IN INTEGER LAMBDA LBRACE LBRACKET LET LPAREN LT MATCH MOD MUL NOT RBRACE RBRACKET REC RPAREN STRING SUB UMINUS WITHprogram : expressions\n expressions : expressions expression\n | expression\n expression : INTEGERexpression : FLOATexpression : STRINGexpression : IDENTexpression : LPAREN tuple_expr RPAREN\n tuple_expr : tuple_expr COMMA expression\n | expression\n expression : LBRACKET list_expr RBRACKET\n list_expr : list_expr COMMA expression\n | expression\n expression : LBRACE record_expr RBRACE\n record_expr : record_expr COMMA record_field\n | record_field\n record_field : IDENT COLON expression\n expression : expression ADD expression\n | expression SUB expression\n | expression MUL expression\n | expression DIV expression\n | expression MOD expression\n | expression LT expression\n | expression GT expression\n | expression EQ expression\n expression : NOT expressionexpression : UMINUS expressionexpression : LET ident_list DEFINE expressions IN expression\n ident_list : ident_list COMMA IDENT\n | IDENT\n expression : LET REC ident_expr_list IN expressions\n ident_expr_list : ident_expr_list COMMA ident_expr\n | ident_expr\n ident_expr : IDENT DEFINE expressionexpression : MATCH expression WITH match_expr_listmatch_expr_list : match_exprmatch_expr_list : match_expr_list match_exprmatch_expr : BAR pattern ARROW expression\n pattern : INTEGER\n | FLOAT\n | STRING\n pattern : IDENTpattern : LPAREN pattern_list RPAREN\n pattern_list : pattern_list COMMA pattern\n | pattern\n expression : LAMBDA pattern_list ARROW expressionexpression : expression LPAREN argument_list RPAREN\n argument_list : argument_list COMMA expression\n | expression\n ' 10 | 11 | _lr_action_items = {'INTEGER':([0,2,3,4,5,6,7,8,9,11,12,14,15,16,17,18,19,20,21,22,23,24,25,33,34,45,46,47,48,49,50,51,52,53,56,57,58,59,60,62,63,69,70,72,73,78,80,82,83,84,85,86,90,91,94,96,97,98,],[4,4,-3,-4,-5,-6,-7,4,4,4,4,4,41,-2,4,4,4,4,4,4,4,4,4,-26,-27,41,-18,-19,-20,-21,-22,-23,-24,-25,-8,4,-11,4,-14,4,4,4,41,-47,4,4,4,4,-35,-36,41,-46,4,4,-37,-28,4,-38,]),'FLOAT':([0,2,3,4,5,6,7,8,9,11,12,14,15,16,17,18,19,20,21,22,23,24,25,33,34,45,46,47,48,49,50,51,52,53,56,57,58,59,60,62,63,69,70,72,73,78,80,82,83,84,85,86,90,91,94,96,97,98,],[5,5,-3,-4,-5,-6,-7,5,5,5,5,5,42,-2,5,5,5,5,5,5,5,5,5,-26,-27,42,-18,-19,-20,-21,-22,-23,-24,-25,-8,5,-11,5,-14,5,5,5,42,-47,5,5,5,5,-35,-36,42,-46,5,5,-37,-28,5,-38,]),'STRING':([0,2,3,4,5,6,7,8,9,11,12,14,15,16,17,18,19,20,21,22,23,24,25,33,34,45,46,47,48,49,50,51,52,53,56,57,58,59,60,62,63,69,70,72,73,78,80,82,83,84,85,86,90,91,94,96,97,98,],[6,6,-3,-4,-5,-6,-7,6,6,6,6,6,43,-2,6,6,6,6,6,6,6,6,6,-26,-27,43,-18,-19,-20,-21,-22,-23,-24,-25,-8,6,-11,6,-14,6,6,6,43,-47,6,6,6,6,-35,-36,43,-46,6,6,-37,-28,6,-38,]),'IDENT':([0,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,33,34,36,45,46,47,48,49,50,51,52,53,56,57,58,59,60,61,62,63,64,69,70,72,73,78,80,81,82,83,84,85,86,90,91,94,96,97,98,],[7,7,-3,-4,-5,-6,-7,7,7,32,7,7,37,7,44,-2,7,7,7,7,7,7,7,7,7,-26,-27,67,44,-18,-19,-20,-21,-22,-23,-24,-25,-8,7,-11,7,-14,32,7,7,79,7,44,-47,7,7,7,67,7,-35,-36,44,-46,7,7,-37,-28,7,-38,]),'LPAREN':([0,2,3,4,5,6,7,8,9,11,12,14,15,16,17,18,19,20,21,22,23,24,25,27,29,33,34,38,45,46,47,48,49,50,51,52,53,54,56,57,58,59,60,62,63,69,70,72,73,74,75,77,78,80,82,83,84,85,86,89,90,91,93,94,96,97,98,],[8,8,25,-4,-5,-6,-7,8,8,8,8,8,45,25,8,8,8,8,8,8,8,8,8,25,25,-26,-27,25,45,25,25,25,25,25,25,25,25,25,-8,8,-11,8,-14,8,8,8,45,-47,8,25,25,25,8,8,8,-35,-36,45,25,25,8,8,25,-37,25,8,25,]),'LBRACKET':([0,2,3,4,5,6,7,8,9,11,12,14,16,17,18,19,20,21,22,23,24,25,33,34,46,47,48,49,50,51,52,53,56,57,58,59,60,62,63,69,72,73,78,80,82,83,84,86,90,91,94,96,97,98,],[9,9,-3,-4,-5,-6,-7,9,9,9,9,9,-2,9,9,9,9,9,9,9,9,9,-26,-27,-18,-19,-20,-21,-22,-23,-24,-25,-8,9,-11,9,-14,9,9,9,-47,9,9,9,9,-35,-36,-46,9,9,-37,-28,9,-38,]),'LBRACE':([0,2,3,4,5,6,7,8,9,11,12,14,16,17,18,19,20,21,22,23,24,25,33,34,46,47,48,49,50,51,52,53,56,57,58,59,60,62,63,69,72,73,78,80,82,83,84,86,90,91,94,96,97,98,],[10,10,-3,-4,-5,-6,-7,10,10,10,10,10,-2,10,10,10,10,10,10,10,10,10,-26,-27,-18,-19,-20,-21,-22,-23,-24,-25,-8,10,-11,10,-14,10,10,10,-47,10,10,10,10,-35,-36,-46,10,10,-37,-28,10,-38,]),'NOT':([0,2,3,4,5,6,7,8,9,11,12,14,16,17,18,19,20,21,22,23,24,25,33,34,46,47,48,49,50,51,52,53,56,57,58,59,60,62,63,69,72,73,78,80,82,83,84,86,90,91,94,96,97,98,],[11,11,-3,-4,-5,-6,-7,11,11,11,11,11,-2,11,11,11,11,11,11,11,11,11,-26,-27,-18,-19,-20,-21,-22,-23,-24,-25,-8,11,-11,11,-14,11,11,11,-47,11,11,11,11,-35,-36,-46,11,11,-37,-28,11,-38,]),'UMINUS':([0,2,3,4,5,6,7,8,9,11,12,14,16,17,18,19,20,21,22,23,24,25,33,34,46,47,48,49,50,51,52,53,56,57,58,59,60,62,63,69,72,73,78,80,82,83,84,86,90,91,94,96,97,98,],[12,12,-3,-4,-5,-6,-7,12,12,12,12,12,-2,12,12,12,12,12,12,12,12,12,-26,-27,-18,-19,-20,-21,-22,-23,-24,-25,-8,12,-11,12,-14,12,12,12,-47,12,12,12,12,-35,-36,-46,12,12,-37,-28,12,-38,]),'LET':([0,2,3,4,5,6,7,8,9,11,12,14,16,17,18,19,20,21,22,23,24,25,33,34,46,47,48,49,50,51,52,53,56,57,58,59,60,62,63,69,72,73,78,80,82,83,84,86,90,91,94,96,97,98,],[13,13,-3,-4,-5,-6,-7,13,13,13,13,13,-2,13,13,13,13,13,13,13,13,13,-26,-27,-18,-19,-20,-21,-22,-23,-24,-25,-8,13,-11,13,-14,13,13,13,-47,13,13,13,13,-35,-36,-46,13,13,-37,-28,13,-38,]),'MATCH':([0,2,3,4,5,6,7,8,9,11,12,14,16,17,18,19,20,21,22,23,24,25,33,34,46,47,48,49,50,51,52,53,56,57,58,59,60,62,63,69,72,73,78,80,82,83,84,86,90,91,94,96,97,98,],[14,14,-3,-4,-5,-6,-7,14,14,14,14,14,-2,14,14,14,14,14,14,14,14,14,-26,-27,-18,-19,-20,-21,-22,-23,-24,-25,-8,14,-11,14,-14,14,14,14,-47,14,14,14,14,-35,-36,-46,14,14,-37,-28,14,-38,]),'LAMBDA':([0,2,3,4,5,6,7,8,9,11,12,14,16,17,18,19,20,21,22,23,24,25,33,34,46,47,48,49,50,51,52,53,56,57,58,59,60,62,63,69,72,73,78,80,82,83,84,86,90,91,94,96,97,98,],[15,15,-3,-4,-5,-6,-7,15,15,15,15,15,-2,15,15,15,15,15,15,15,15,15,-26,-27,-18,-19,-20,-21,-22,-23,-24,-25,-8,15,-11,15,-14,15,15,15,-47,15,15,15,15,-35,-36,-46,15,15,-37,-28,15,-38,]),'$end':([1,2,3,4,5,6,7,16,33,34,46,47,48,49,50,51,52,53,56,58,60,72,83,84,86,91,94,96,98,],[0,-1,-3,-4,-5,-6,-7,-2,-26,-27,-18,-19,-20,-21,-22,-23,-24,-25,-8,-11,-14,-47,-35,-36,-46,-31,-37,-28,-38,]),'IN':([3,4,5,6,7,16,33,34,46,47,48,49,50,51,52,53,56,58,60,65,66,72,78,83,84,86,91,92,93,94,96,98,],[-3,-4,-5,-6,-7,-2,-26,-27,-18,-19,-20,-21,-22,-23,-24,-25,-8,-11,-14,80,-33,-47,90,-35,-36,-46,-31,-32,-34,-37,-28,-38,]),'ADD':([3,4,5,6,7,16,27,29,33,34,38,46,47,48,49,50,51,52,53,54,56,58,60,72,74,75,77,83,84,86,89,91,93,94,96,98,],[17,-4,-5,-6,-7,17,17,17,-26,-27,17,17,17,17,17,17,17,17,17,17,-8,-11,-14,-47,17,17,17,-35,-36,17,17,-31,17,-37,17,17,]),'SUB':([3,4,5,6,7,16,27,29,33,34,38,46,47,48,49,50,51,52,53,54,56,58,60,72,74,75,77,83,84,86,89,91,93,94,96,98,],[18,-4,-5,-6,-7,18,18,18,-26,-27,18,18,18,18,18,18,18,18,18,18,-8,-11,-14,-47,18,18,18,-35,-36,18,18,-31,18,-37,18,18,]),'MUL':([3,4,5,6,7,16,27,29,33,34,38,46,47,48,49,50,51,52,53,54,56,58,60,72,74,75,77,83,84,86,89,91,93,94,96,98,],[19,-4,-5,-6,-7,19,19,19,-26,-27,19,19,19,19,19,19,19,19,19,19,-8,-11,-14,-47,19,19,19,-35,-36,19,19,-31,19,-37,19,19,]),'DIV':([3,4,5,6,7,16,27,29,33,34,38,46,47,48,49,50,51,52,53,54,56,58,60,72,74,75,77,83,84,86,89,91,93,94,96,98,],[20,-4,-5,-6,-7,20,20,20,-26,-27,20,20,20,20,20,20,20,20,20,20,-8,-11,-14,-47,20,20,20,-35,-36,20,20,-31,20,-37,20,20,]),'MOD':([3,4,5,6,7,16,27,29,33,34,38,46,47,48,49,50,51,52,53,54,56,58,60,72,74,75,77,83,84,86,89,91,93,94,96,98,],[21,-4,-5,-6,-7,21,21,21,-26,-27,21,21,21,21,21,21,21,21,21,21,-8,-11,-14,-47,21,21,21,-35,-36,21,21,-31,21,-37,21,21,]),'LT':([3,4,5,6,7,16,27,29,33,34,38,46,47,48,49,50,51,52,53,54,56,58,60,72,74,75,77,83,84,86,89,91,93,94,96,98,],[22,-4,-5,-6,-7,22,22,22,-26,-27,22,22,22,22,22,22,22,22,22,22,-8,-11,-14,-47,22,22,22,-35,-36,22,22,-31,22,-37,22,22,]),'GT':([3,4,5,6,7,16,27,29,33,34,38,46,47,48,49,50,51,52,53,54,56,58,60,72,74,75,77,83,84,86,89,91,93,94,96,98,],[23,-4,-5,-6,-7,23,23,23,-26,-27,23,23,23,23,23,23,23,23,23,23,-8,-11,-14,-47,23,23,23,-35,-36,23,23,-31,23,-37,23,23,]),'EQ':([3,4,5,6,7,16,27,29,33,34,38,46,47,48,49,50,51,52,53,54,56,58,60,72,74,75,77,83,84,86,89,91,93,94,96,98,],[24,-4,-5,-6,-7,24,24,24,-26,-27,24,24,24,24,24,24,24,24,24,24,-8,-11,-14,-47,24,24,24,-35,-36,24,24,-31,24,-37,24,24,]),'RPAREN':([3,4,5,6,7,16,26,27,33,34,40,41,42,43,44,46,47,48,49,50,51,52,53,54,55,56,58,60,71,72,74,83,84,86,87,88,89,91,94,96,98,],[-3,-4,-5,-6,-7,-2,56,-10,-26,-27,-45,-39,-40,-41,-42,-18,-19,-20,-21,-22,-23,-24,-25,-49,72,-8,-11,-14,88,-47,-9,-35,-36,-46,-44,-43,-48,-31,-37,-28,-38,]),'COMMA':([3,4,5,6,7,16,26,27,28,29,30,31,33,34,35,37,39,40,41,42,43,44,46,47,48,49,50,51,52,53,54,55,56,58,60,65,66,71,72,74,75,76,77,79,83,84,86,87,88,89,91,92,93,94,96,98,],[-3,-4,-5,-6,-7,-2,57,-10,59,-13,61,-16,-26,-27,64,-30,70,-45,-39,-40,-41,-42,-18,-19,-20,-21,-22,-23,-24,-25,-49,73,-8,-11,-14,81,-33,70,-47,-9,-12,-15,-17,-29,-35,-36,-46,-44,-43,-48,-31,-32,-34,-37,-28,-38,]),'RBRACKET':([3,4,5,6,7,16,28,29,33,34,46,47,48,49,50,51,52,53,56,58,60,72,75,83,84,86,91,94,96,98,],[-3,-4,-5,-6,-7,-2,58,-13,-26,-27,-18,-19,-20,-21,-22,-23,-24,-25,-8,-11,-14,-47,-12,-35,-36,-46,-31,-37,-28,-38,]),'WITH':([3,4,5,6,7,16,33,34,38,46,47,48,49,50,51,52,53,56,58,60,72,83,84,86,91,94,96,98,],[-3,-4,-5,-6,-7,-2,-26,-27,68,-18,-19,-20,-21,-22,-23,-24,-25,-8,-11,-14,-47,-35,-36,-46,-31,-37,-28,-38,]),'RBRACE':([3,4,5,6,7,16,30,31,33,34,46,47,48,49,50,51,52,53,56,58,60,72,76,77,83,84,86,91,94,96,98,],[-3,-4,-5,-6,-7,-2,60,-16,-26,-27,-18,-19,-20,-21,-22,-23,-24,-25,-8,-11,-14,-47,-15,-17,-35,-36,-46,-31,-37,-28,-38,]),'BAR':([3,4,5,6,7,16,33,34,46,47,48,49,50,51,52,53,56,58,60,68,72,83,84,86,91,94,96,98,],[-3,-4,-5,-6,-7,-2,-26,-27,-18,-19,-20,-21,-22,-23,-24,-25,-8,-11,-14,85,-47,85,-36,-46,-31,-37,-28,-38,]),'REC':([13,],[36,]),'COLON':([32,],[62,]),'DEFINE':([35,37,67,79,],[63,-30,82,-29,]),'ARROW':([39,40,41,42,43,44,87,88,95,],[69,-45,-39,-40,-41,-42,-44,-43,97,]),} 12 | 13 | _lr_action = {} 14 | for _k, _v in _lr_action_items.items(): 15 | for _x,_y in zip(_v[0],_v[1]): 16 | if not _x in _lr_action: _lr_action[_x] = {} 17 | _lr_action[_x][_k] = _y 18 | del _lr_action_items 19 | 20 | _lr_goto_items = {'program':([0,],[1,]),'expressions':([0,63,80,],[2,78,91,]),'expression':([0,2,8,9,11,12,14,17,18,19,20,21,22,23,24,25,57,59,62,63,69,73,78,80,82,90,91,97,],[3,16,27,29,33,34,38,46,47,48,49,50,51,52,53,54,74,75,77,3,86,89,16,3,93,96,16,98,]),'tuple_expr':([8,],[26,]),'list_expr':([9,],[28,]),'record_expr':([10,],[30,]),'record_field':([10,61,],[31,76,]),'ident_list':([13,],[35,]),'pattern_list':([15,45,],[39,71,]),'pattern':([15,45,70,85,],[40,40,87,95,]),'argument_list':([25,],[55,]),'ident_expr_list':([36,],[65,]),'ident_expr':([36,81,],[66,92,]),'match_expr_list':([68,],[83,]),'match_expr':([68,83,],[84,94,]),} 21 | 22 | _lr_goto = {} 23 | for _k, _v in _lr_goto_items.items(): 24 | for _x, _y in zip(_v[0], _v[1]): 25 | if not _x in _lr_goto: _lr_goto[_x] = {} 26 | _lr_goto[_x][_k] = _y 27 | del _lr_goto_items 28 | _lr_productions = [ 29 | ("S' -> program","S'",1,None,None,None), 30 | ('program -> expressions','program',1,'p_program','elm_parser.py',30), 31 | ('expressions -> expressions expression','expressions',2,'p_expressions','elm_parser.py',36), 32 | ('expressions -> expression','expressions',1,'p_expressions','elm_parser.py',37), 33 | ('expression -> INTEGER','expression',1,'p_expression_integer','elm_parser.py',46), 34 | ('expression -> FLOAT','expression',1,'p_expression_float','elm_parser.py',51), 35 | ('expression -> STRING','expression',1,'p_expression_string','elm_parser.py',56), 36 | ('expression -> IDENT','expression',1,'p_expression_ident','elm_parser.py',61), 37 | ('expression -> LPAREN tuple_expr RPAREN','expression',3,'p_expression_tuple','elm_parser.py',66), 38 | ('tuple_expr -> tuple_expr COMMA expression','tuple_expr',3,'p_tuple_expr','elm_parser.py',72), 39 | ('tuple_expr -> expression','tuple_expr',1,'p_tuple_expr','elm_parser.py',73), 40 | ('expression -> LBRACKET list_expr RBRACKET','expression',3,'p_expression_list','elm_parser.py',82), 41 | ('list_expr -> list_expr COMMA expression','list_expr',3,'p_list_expr','elm_parser.py',88), 42 | ('list_expr -> expression','list_expr',1,'p_list_expr','elm_parser.py',89), 43 | ('expression -> LBRACE record_expr RBRACE','expression',3,'p_expression_record','elm_parser.py',98), 44 | ('record_expr -> record_expr COMMA record_field','record_expr',3,'p_record_expr','elm_parser.py',104), 45 | ('record_expr -> record_field','record_expr',1,'p_record_expr','elm_parser.py',105), 46 | ('record_field -> IDENT COLON expression','record_field',3,'p_record_field','elm_parser.py',114), 47 | ('expression -> expression ADD expression','expression',3,'p_expression_binop','elm_parser.py',120), 48 | ('expression -> expression SUB expression','expression',3,'p_expression_binop','elm_parser.py',121), 49 | ('expression -> expression MUL expression','expression',3,'p_expression_binop','elm_parser.py',122), 50 | ('expression -> expression DIV expression','expression',3,'p_expression_binop','elm_parser.py',123), 51 | ('expression -> expression MOD expression','expression',3,'p_expression_binop','elm_parser.py',124), 52 | ('expression -> expression LT expression','expression',3,'p_expression_binop','elm_parser.py',125), 53 | ('expression -> expression GT expression','expression',3,'p_expression_binop','elm_parser.py',126), 54 | ('expression -> expression EQ expression','expression',3,'p_expression_binop','elm_parser.py',127), 55 | ('expression -> NOT expression','expression',2,'p_expression_not','elm_parser.py',132), 56 | ('expression -> UMINUS expression','expression',2,'p_expression_unop','elm_parser.py',136), 57 | ('expression -> LET ident_list DEFINE expressions IN expression','expression',6,'p_expression_let','elm_parser.py',141), 58 | ('ident_list -> ident_list COMMA IDENT','ident_list',3,'p_ident_list','elm_parser.py',147), 59 | ('ident_list -> IDENT','ident_list',1,'p_ident_list','elm_parser.py',148), 60 | ('expression -> LET REC ident_expr_list IN expressions','expression',5,'p_expression_let_rec','elm_parser.py',157), 61 | ('ident_expr_list -> ident_expr_list COMMA ident_expr','ident_expr_list',3,'p_ident_expr_list','elm_parser.py',163), 62 | ('ident_expr_list -> ident_expr','ident_expr_list',1,'p_ident_expr_list','elm_parser.py',164), 63 | ('ident_expr -> IDENT DEFINE expression','ident_expr',3,'p_ident_expr','elm_parser.py',173), 64 | ('expression -> MATCH expression WITH match_expr_list','expression',4,'p_expression_match','elm_parser.py',178), 65 | ('match_expr_list -> match_expr','match_expr_list',1,'p_match_expr_list_single','elm_parser.py',183), 66 | ('match_expr_list -> match_expr_list match_expr','match_expr_list',2,'p_match_expr_list_multiple','elm_parser.py',187), 67 | ('match_expr -> BAR pattern ARROW expression','match_expr',4,'p_match_expr','elm_parser.py',191), 68 | ('pattern -> INTEGER','pattern',1,'p_pattern_literal','elm_parser.py',197), 69 | ('pattern -> FLOAT','pattern',1,'p_pattern_literal','elm_parser.py',198), 70 | ('pattern -> STRING','pattern',1,'p_pattern_literal','elm_parser.py',199), 71 | ('pattern -> IDENT','pattern',1,'p_pattern_ident','elm_parser.py',208), 72 | ('pattern -> LPAREN pattern_list RPAREN','pattern',3,'p_pattern_tuple','elm_parser.py',213), 73 | ('pattern_list -> pattern_list COMMA pattern','pattern_list',3,'p_pattern_list','elm_parser.py',219), 74 | ('pattern_list -> pattern','pattern_list',1,'p_pattern_list','elm_parser.py',220), 75 | ('expression -> LAMBDA pattern_list ARROW expression','expression',4,'p_expression_lambda','elm_parser.py',229), 76 | ('expression -> expression LPAREN argument_list RPAREN','expression',4,'p_expression_function_call','elm_parser.py',234), 77 | ('argument_list -> argument_list COMMA expression','argument_list',3,'p_argument_list','elm_parser.py',240), 78 | ('argument_list -> expression','argument_list',1,'p_argument_list','elm_parser.py',241), 79 | ] 80 | -------------------------------------------------------------------------------- /repl.py: -------------------------------------------------------------------------------- 1 | import code 2 | import sys 3 | 4 | from elm_parser import parse 5 | from elm_types import Program 6 | from llvm_gen import compile_program, run_llvm_ir 7 | 8 | 9 | def repl(): 10 | while True: 11 | try: 12 | input_text = input("> ") 13 | if not input_text: 14 | continue 15 | program = parse(input_text) 16 | assert isinstance(program, Program) 17 | llvm_ir = compile_program(program) 18 | result = run_llvm_ir(llvm_ir) 19 | print(result) 20 | except (EOFError, KeyboardInterrupt): 21 | break 22 | 23 | 24 | def main(): 25 | if len(sys.argv) == 2 and sys.argv[1] == "python": 26 | code.interact(local={"repl": repl}) 27 | else: 28 | repl() 29 | 30 | 31 | if __name__ == "__main__": 32 | sys.exit(main()) 33 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | llvmlite==0.39.1 2 | parsy==2.0 3 | ply==3.11 4 | -------------------------------------------------------------------------------- /test_elm_lexer.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from elm_lexer import lex 4 | 5 | 6 | @pytest.mark.parametrize("input_text,expected", [ 7 | ( 8 | "1 + 2", 9 | [ 10 | ("NUMBER", "1"), 11 | ("PLUS", "+"), 12 | ("NUMBER", "2"), 13 | ], 14 | ), 15 | ]) 16 | def test_lexer(input_text, expected): 17 | assert list(lex(input_text)) == expected 18 | -------------------------------------------------------------------------------- /test_elm_parser.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from elm_parser import parse 4 | from elm_types import ( 5 | Program, 6 | Expr, 7 | Literal, 8 | Ident, 9 | Tuple, 10 | List_, 11 | Record, 12 | Field, 13 | BinOp, 14 | UnOp, 15 | Let, 16 | LetRec, 17 | Match, 18 | Lambda, 19 | FunctionCall, 20 | ) 21 | 22 | 23 | @pytest.mark.parametrize("input_text,expected", [ 24 | ( 25 | "1 + 2", 26 | Program( 27 | [ 28 | Expr( 29 | BinOp( 30 | Literal(1), 31 | "+", 32 | Literal(2) 33 | ) 34 | ) 35 | ] 36 | ), 37 | ), 38 | ]) 39 | def test_parser(input_text, expected): 40 | assert parse(input_text) == expected 41 | --------------------------------------------------------------------------------