├── .gitignore ├── LICENSE ├── README.md ├── intcode.py ├── intscript.lark ├── intscript.py ├── intscript_tests.py ├── requirements.txt ├── samples ├── fibonacci.is └── intcode.is └── tests ├── arrays.is ├── booleans.is └── comparisons.is /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # Intcode programs 107 | *.ic 108 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alex Gulliver 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # intscript 2 | Compiles programs which run on the [intcode computer](https://adventofcode.com/2019/day/5) used by Advent of Code. 3 | 4 | Install requirements using: 5 | ``` 6 | pip install -r requirements.txt 7 | ``` 8 | 9 | To compile a program: 10 | ``` 11 | python intscript.py samples/fibonacci.is > samples/fibonacci.ic 12 | ``` 13 | 14 | To run a program pass the intcode program followed by the inputs: 15 | ``` 16 | python intcode.py samples/fibonacci.ic 15 17 | ``` 18 | 19 | It will print the outputs generated by the program: 20 | ``` 21 | [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610] 22 | ``` 23 | 24 | Basic syntax: 25 | ``` 26 | # Read an input into variable `n` 27 | input n; 28 | 29 | # Output the value of variable `n` 30 | output n; 31 | 32 | # Basic expressions 33 | a = 1; 34 | b = 2; 35 | c = a + b; 36 | 37 | # `if` statements 38 | if(6 < 7) 39 | { 40 | output c: 41 | } 42 | 43 | # `while` loops 44 | i = 0; 45 | n = 10; 46 | while(i < n) 47 | { 48 | i += 1; 49 | } 50 | 51 | # Arrays 52 | array a[3]; 53 | a[0] = 1; 54 | a[1] = 2; 55 | a[2] = a[0] + a[1]; 56 | ``` 57 | -------------------------------------------------------------------------------- /intcode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Advent of Code 2019, Intcode computer""" 4 | 5 | from argparse import ArgumentParser 6 | from inspect import signature 7 | import sys 8 | 9 | 10 | class OPCODE: 11 | ADD = 1 12 | MULTIPLY = 2 13 | INPUT = 3 14 | OUTPUT = 4 15 | JUMP_IF_TRUE = 5 16 | JUMP_IF_FALSE = 6 17 | LESS_THAN = 7 18 | EQUALS = 8 19 | STOP = 99 20 | 21 | 22 | class MODE: 23 | POSITION = 0 24 | IMMEDIATE = 1 25 | 26 | 27 | def intcode_add(cpu, left, right): 28 | return left + right 29 | 30 | 31 | def intcode_multiply(cpu, left, right): 32 | return left * right 33 | 34 | 35 | def intcode_input(cpu): 36 | if len(cpu.inputs) > 0: 37 | cpu.waiting_for_input = False 38 | return cpu.inputs.pop() 39 | else: 40 | cpu.pc -= 1 41 | cpu.waiting_for_input = True 42 | 43 | 44 | def intcode_output(cpu, value): 45 | cpu.outputs.append(value) 46 | 47 | 48 | def intcode_jump_if_true(cpu, cond, pc): 49 | if cond != 0: 50 | cpu.pc = pc 51 | 52 | 53 | def intcode_jump_if_false(cpu, cond, pc): 54 | if cond == 0: 55 | cpu.pc = pc 56 | 57 | 58 | def intcode_less_than(cpu, left, right): 59 | return 1 if left < right else 0 60 | 61 | 62 | def intcode_equals(cpu, left, right): 63 | return 1 if left == right else 0 64 | 65 | 66 | class IntcodeOp: 67 | def __init__(slf, op): 68 | slf.op = op 69 | slf.num_inputs = len(signature(op).parameters) - 1 70 | 71 | def __call__(slf, *args): 72 | return slf.op(*args) 73 | 74 | 75 | ops = { 76 | OPCODE.ADD: IntcodeOp(intcode_add), 77 | OPCODE.MULTIPLY: IntcodeOp(intcode_multiply), 78 | OPCODE.INPUT: IntcodeOp(intcode_input), 79 | OPCODE.OUTPUT: IntcodeOp(intcode_output), 80 | OPCODE.JUMP_IF_TRUE: IntcodeOp(intcode_jump_if_true), 81 | OPCODE.JUMP_IF_FALSE: IntcodeOp(intcode_jump_if_false), 82 | OPCODE.LESS_THAN: IntcodeOp(intcode_less_than), 83 | OPCODE.EQUALS: IntcodeOp(intcode_equals) 84 | } 85 | 86 | 87 | class IntcodeCPU: 88 | def __init__(slf, memory, *inputs): 89 | slf.memory = list(memory) 90 | slf.inputs = list(reversed(inputs)) 91 | slf.outputs = [] 92 | slf.pc = 0 93 | slf.waiting_for_input = False 94 | 95 | def execute(slf): 96 | while slf.memory[slf.pc] != OPCODE.STOP: 97 | # Reading opcode. 98 | head = slf.memory[slf.pc] 99 | slf.pc += 1 100 | op = ops[head % 100] 101 | head //= 100 102 | 103 | # Loading parameters. 104 | params = [] 105 | for i in range(op.num_inputs): 106 | mode = head % 10 107 | if mode == MODE.POSITION: 108 | params.append(slf.memory[slf.memory[slf.pc+i]]) 109 | elif mode == MODE.IMMEDIATE: 110 | params.append(slf.memory[slf.pc+i]) 111 | head //= 10 112 | slf.pc += op.num_inputs 113 | 114 | # Executing op. 115 | out = op(slf, *params) 116 | if slf.waiting_for_input: 117 | break 118 | 119 | # Writing output. 120 | if out is not None: 121 | slf.memory[slf.memory[slf.pc]] = out 122 | slf.pc += 1 123 | 124 | 125 | def intcode(memory, *inputs): 126 | cpu = IntcodeCPU(memory, *inputs) 127 | cpu.execute() 128 | return cpu 129 | 130 | 131 | def main(argv): 132 | arg_parser = ArgumentParser(description='intcode computer') 133 | arg_parser.add_argument( 134 | 'file', 135 | help='intcode program' 136 | ) 137 | arg_parser.add_argument( 138 | 'inputs', 139 | nargs='*', 140 | default=[], 141 | help='intcode inputs' 142 | ) 143 | args = arg_parser.parse_args(argv) 144 | 145 | with open(args.file) as f: 146 | program = list(map(int, f.read().split(','))) 147 | cpu = intcode(program, *[int(i) for i in args.inputs]) 148 | print(cpu.outputs) 149 | 150 | 151 | if __name__ == "__main__": 152 | main(sys.argv[1:]) 153 | -------------------------------------------------------------------------------- /intscript.lark: -------------------------------------------------------------------------------- 1 | program: stmt* 2 | 3 | // Statements 4 | ?stmt: block_stmt | atom_stmt 5 | block_stmt: "{" stmt* "}" 6 | ?atom_stmt: expr OP_ASSIGN expr ";" -> assign_stmt 7 | | "input" expr ";" -> input_stmt 8 | | "output" expr ";" -> output_stmt 9 | | "if" "(" expr ")" stmt ["else" stmt] -> if_stmt 10 | | "while" "(" expr ")" stmt -> while_stmt 11 | | "continue" ";" -> continue_stmt 12 | | "break" ";" -> break_stmt 13 | | "array" identifier "[" literal "]" ";" -> array_stmt 14 | 15 | // Expressions 16 | ?expr: or_expr 17 | ?or_expr: and_expr 18 | | or_expr OP_OR and_expr -> binary_expr 19 | ?and_expr: eql_expr 20 | | and_expr OP_AND eql_expr -> binary_expr 21 | ?eql_expr: rel_expr 22 | | eql_expr OP_EQL rel_expr -> binary_expr 23 | ?rel_expr: add_expr 24 | | rel_expr OP_REL add_expr -> binary_expr 25 | ?add_expr: mul_expr 26 | | add_expr OP_ADD mul_expr -> binary_expr 27 | ?mul_expr: unary_expr 28 | | mul_expr OP_MUL unary_expr -> binary_expr 29 | ?unary_expr: postfix_expr 30 | | (OP_NOT | OP_ADD) unary_expr 31 | ?postfix_expr: primary_expr 32 | | postfix_expr "[" expr "]" -> subscript_expr 33 | ?primary_expr: "(" expr ")" 34 | | literal 35 | | identifier 36 | 37 | // Values 38 | literal: INT 39 | identifier: NAME 40 | 41 | // Operator terminals 42 | OP_OR: "or" 43 | OP_AND: "and" 44 | OP_NOT: "not" 45 | OP_REL: "<=" | ">=" | "<" | ">" 46 | OP_EQL: "==" | "!=" 47 | OP_ADD: "+" | "-" 48 | OP_MUL: "*" 49 | OP_ASSIGN: "=" | "+=" | "-=" | "*=" 50 | 51 | COMMENT: /#[^\n]*/ 52 | 53 | %import common.FLOAT 54 | %import common.INT 55 | %import common.CNAME -> NAME 56 | %import common.WS 57 | 58 | %ignore WS 59 | %ignore COMMENT 60 | -------------------------------------------------------------------------------- /intscript.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """a tiny language that compiles to intcode""" 4 | 5 | from argparse import ArgumentParser 6 | from contextlib import contextmanager 7 | from itertools import chain, repeat 8 | from lark import Lark, Token, Transformer, v_args 9 | from pathlib import Path 10 | import sys 11 | 12 | from intcode import OPCODE 13 | 14 | 15 | class OP: 16 | OR = 'or' 17 | AND = 'and' 18 | NOT = 'not' 19 | LT = '<' 20 | LE = '<=' 21 | GT = '>' 22 | GE = '>=' 23 | EQ = '==' 24 | NE = '!=' 25 | ADD = '+' 26 | SUB = '-' 27 | MUL = '*' 28 | ASSIGN = '=' 29 | ASSIGN_ADD = '+=' 30 | ASSIGN_SUB = '-=' 31 | ASSIGN_MUL = '*=' 32 | 33 | 34 | assign_ops = { 35 | OP.ASSIGN_ADD: OP.ADD, 36 | OP.ASSIGN_SUB: OP.SUB, 37 | OP.ASSIGN_MUL: OP.MUL 38 | } 39 | 40 | 41 | class ASTNode: 42 | _id = '' 43 | _props = [] 44 | 45 | def __init__(slf, **kwargs): 46 | for prop, val in kwargs.items(): 47 | if prop not in slf._props: 48 | raise Exception( 49 | f"'{prop}' is not a property of '{type(slf).__name__}'" 50 | ) 51 | setattr(slf, prop, val) 52 | 53 | for prop in slf._props: 54 | if getattr(slf, prop) is None: 55 | setattr(slf, prop, None) 56 | 57 | 58 | class ASTProgram(ASTNode): 59 | _id = 'program' 60 | _props = ['stmts'] 61 | 62 | 63 | class ASTBlockStatement(ASTNode): 64 | _id = 'block_stmt' 65 | _props = ['body'] 66 | 67 | 68 | class ASTAssignStatement(ASTNode): 69 | _id = 'assign_stmt' 70 | _props = ['target', 'op', 'expr'] 71 | 72 | 73 | class ASTInputStatement(ASTNode): 74 | _id = 'input_stmt' 75 | _props = ['dest'] 76 | 77 | 78 | class ASTOutputStatement(ASTNode): 79 | _id = 'output_stmt' 80 | _props = ['src'] 81 | 82 | 83 | class ASTIfStatement(ASTNode): 84 | _id = 'if_stmt' 85 | _props = ['cond', 'true', 'false'] 86 | 87 | 88 | class ASTWhileStatement(ASTNode): 89 | _id = 'while_stmt' 90 | _props = ['cond', 'body'] 91 | 92 | 93 | class ASTContinueStatement(ASTNode): 94 | _id = 'continue_stmt' 95 | 96 | 97 | class ASTBreakStatement(ASTNode): 98 | _id = 'break_stmt' 99 | 100 | 101 | class ASTArrayStatement(ASTNode): 102 | _id = 'array_stmt' 103 | _props = ['target', 'size'] 104 | 105 | 106 | class ASTBinaryExpression(ASTNode): 107 | _id = 'binary_expr' 108 | _props = ['left', 'op', 'right'] 109 | 110 | 111 | class ASTUnaryExpression(ASTNode): 112 | _id = 'unary_expr' 113 | _props = ['op', 'right'] 114 | 115 | 116 | class ASTSubscriptExpression(ASTNode): 117 | _id = 'subscript_expr' 118 | _props = ['addr', 'index'] 119 | 120 | 121 | class ASTLiteral(ASTNode): 122 | _id = 'literal' 123 | _props = ['value'] 124 | 125 | 126 | class ASTIdentifer(ASTNode): 127 | _id = 'identifier' 128 | _props = ['value'] 129 | 130 | 131 | @v_args(inline=True) 132 | class ASTTransformer(Transformer): 133 | def program(slf, *stmts): 134 | return ASTProgram(stmts=stmts) 135 | 136 | def block_stmt(slf, *body): 137 | return ASTBlockStatement(body=list(body)) 138 | 139 | def assign_stmt(slf, target, op, expr): 140 | return ASTAssignStatement(target=target, op=op, expr=expr) 141 | 142 | def input_stmt(slf, dest): 143 | return ASTInputStatement(dest=dest) 144 | 145 | def output_stmt(slf, src): 146 | return ASTOutputStatement(src=src) 147 | 148 | def if_stmt(slf, cond, true, false=None): 149 | return ASTIfStatement(cond=cond, true=true, false=false) 150 | 151 | def while_stmt(slf, cond, body): 152 | return ASTWhileStatement(cond=cond, body=body) 153 | 154 | def continue_stmt(slf): 155 | return ASTContinueStatement() 156 | 157 | def break_stmt(slf): 158 | return ASTBreakStatement() 159 | 160 | def array_stmt(slf, target, size): 161 | return ASTArrayStatement(target=target, size=size) 162 | 163 | def binary_expr(slf, left, op, right): 164 | return ASTBinaryExpression(left=left, op=op, right=right) 165 | 166 | def unary_expr(slf, op, right): 167 | return ASTUnaryExpression(op=op, right=right) 168 | 169 | def subscript_expr(slf, addr, index): 170 | return ASTSubscriptExpression(addr=addr, index=index) 171 | 172 | def literal(slf, token): 173 | return ASTLiteral(value=int(token)) 174 | 175 | def identifier(slf, token): 176 | return ASTIdentifer(value=str(token)) 177 | 178 | 179 | class ASTVisitor: 180 | def visit(slf, node): 181 | if node: 182 | visit = f'visit_{node._id}' 183 | if hasattr(slf, visit): 184 | return getattr(slf, visit)(node) 185 | 186 | 187 | class IRLiteral: 188 | immediate = True 189 | 190 | def __init__(slf, value): 191 | slf.value = value 192 | 193 | def __hash__(slf): 194 | return hash(('IRLiteral', slf.value)) 195 | 196 | def __eq__(slf, otr): 197 | return isinstance(otr, IRLiteral) and slf.value == otr.value 198 | 199 | def __str__(slf): 200 | return str(slf.value) 201 | 202 | def __repr__(slf): 203 | return f'IRLiteral({slf.value})' 204 | 205 | 206 | class IRAddress: 207 | immediate = False 208 | 209 | def __init__(slf, value): 210 | slf.value = value 211 | 212 | def __str__(slf): 213 | return f'#{slf.value}' 214 | 215 | def __repr__(slf): 216 | return f'IRAddress({slf.value})' 217 | 218 | 219 | class IRStorage: 220 | pass 221 | 222 | 223 | class IRVariable(IRStorage): 224 | immediate = False 225 | size = 1 226 | 227 | def __init__(slf, name): 228 | slf.name = name 229 | 230 | def __hash__(slf): 231 | return hash(('IRVariable', slf.name)) 232 | 233 | def __eq__(slf, otr): 234 | return isinstance(otr, IRVariable) and slf.name == otr.name 235 | 236 | def __str__(slf): 237 | return str(slf.name) 238 | 239 | def __repr__(slf): 240 | return f'IRVariable({slf.name})' 241 | 242 | 243 | class IRArray(IRStorage): 244 | immediate = True 245 | 246 | def __init__(slf, size): 247 | if size < 1: 248 | raise Exception('Arrays must have at least one element!') 249 | slf.size = size 250 | 251 | def __str__(slf): 252 | return f'array[{slf.size}]' 253 | 254 | def __repr__(slf): 255 | return f'IRArray({slf.size})' 256 | 257 | 258 | class IRElement: 259 | immediate = False 260 | 261 | def __init__(slf, addr, index): 262 | slf.addr = addr 263 | slf.index = index 264 | 265 | def __str__(slf): 266 | return f'{slf.addr}[{slf.index}]' 267 | 268 | def __repr__(slf): 269 | return f'IRElement({slf.addr}, {slf.index})' 270 | 271 | 272 | class IRInstruction: 273 | _lhs = [] 274 | _rhs = [] 275 | 276 | def __init__(slf, *args): 277 | ln = len(slf._lhs) 278 | rn = len(slf._rhs) 279 | n = ln + rn 280 | if len(args) != n: 281 | raise Exception( 282 | f"'{type(slf).__name__}' expects {n} parameters" 283 | ) 284 | 285 | slf.lhs = list(args[:ln]) 286 | slf.rhs = list(args[ln:]) 287 | 288 | 289 | class IRLabel(IRInstruction): 290 | immediate = True 291 | _id = 'label' 292 | _rhs = ['name'] 293 | 294 | def __str__(slf): 295 | return f'label {slf.rhs[0]}' 296 | 297 | 298 | class IRInputAssignment(IRInstruction): 299 | _id = 'input' 300 | _lhs = ['dest'] 301 | 302 | def __str__(slf): 303 | return f'{slf.lhs[0]} << input' 304 | 305 | 306 | class IROutput(IRInstruction): 307 | _id = 'output' 308 | _rhs = ['src'] 309 | 310 | def __str__(slf): 311 | return f'output << {slf.rhs[0]}' 312 | 313 | 314 | class IRCopyAssignment(IRInstruction): 315 | _id = 'copy' 316 | _lhs = ['result'] 317 | _rhs = ['right'] 318 | 319 | def __str__(slf): 320 | return f'{slf.lhs[0]} := {slf.rhs[0]}' 321 | 322 | 323 | class IRUnaryExprAssignment(IRInstruction): 324 | _id = 'unary' 325 | _lhs = ['result'] 326 | _rhs = ['op', 'right'] 327 | 328 | def __str__(slf): 329 | return f'{slf.lhs[0]} := {slf.rhs[0]} {slf.rhs[1]}' 330 | 331 | 332 | class IRBinaryExprAssignment(IRInstruction): 333 | _id = 'binary' 334 | _lhs = ['result'] 335 | _rhs = ['left', 'op', 'right'] 336 | 337 | def __str__(slf): 338 | return f'{slf.lhs[0]} := {slf.rhs[0]} {slf.rhs[1]} {slf.rhs[2]}' 339 | 340 | 341 | class IRGoto(IRInstruction): 342 | _id = 'goto' 343 | _rhs = ['label'] 344 | 345 | def __str__(slf): 346 | return f'goto {slf.rhs[0].rhs[0]}' 347 | 348 | 349 | class IRGotoIfFalse(IRInstruction): 350 | _id = 'goto_if_false' 351 | _rhs = ['cond', 'label'] 352 | 353 | def __str__(slf): 354 | return f'if(!{slf.rhs[0]}) goto {slf.rhs[1].rhs[0]}' 355 | 356 | 357 | class IRArrayAssignment(IRInstruction): 358 | _id = 'array' 359 | _lhs = ['result'] 360 | _rhs = ['array'] 361 | 362 | def __str__(slf): 363 | return f'{slf.lhs[0]} = {slf.rhs[0]}' 364 | 365 | 366 | class IRScope: 367 | CONTINUE = 'continue' 368 | BREAK = 'break' 369 | 370 | def __init__(slf): 371 | slf.variables = {} 372 | slf.labels = {} 373 | 374 | 375 | class IRScopeManager: 376 | def __init__(slf): 377 | slf.scopes = [] 378 | 379 | def __enter__(slf): 380 | scope = IRScope() 381 | slf.scopes.append(scope) 382 | return scope 383 | 384 | def __exit__(slf, *args): 385 | slf.scopes.pop() 386 | 387 | def map_variable(slf, v): 388 | scope = slf.scopes[-1] 389 | if v.name in scope.variables: 390 | raise Exception( 391 | f"There is already a variable named '{v.name}' in the scope" 392 | ) 393 | else: 394 | scope.variables[v.name] = v 395 | 396 | def map_label(slf, name, label): 397 | slf.scopes[-1].labels[name] = label 398 | 399 | def find_variable(slf, name): 400 | for scope in reversed(slf.scopes): 401 | if name in scope.variables: 402 | return scope.variables[name] 403 | 404 | raise KeyError( 405 | f"There is no variable named '{name}' in the current scope" 406 | ) 407 | 408 | def find_label(slf, name): 409 | for scope in reversed(slf.scopes): 410 | if name in scope.labels: 411 | return scope.labels[name] 412 | raise KeyError() 413 | 414 | 415 | class IRGenerator(ASTVisitor): 416 | def __init__(slf): 417 | slf.scope = IRScopeManager() 418 | slf.next_label = 0 419 | slf.next_variable = 0 420 | slf.instrs = [] 421 | 422 | def emit(slf, instr): 423 | slf.instrs.append(instr) 424 | 425 | def label(slf): 426 | label = IRLabel(f'L{slf.next_label}') 427 | slf.next_label += 1 428 | return label 429 | 430 | def temp(slf): 431 | temp = IRVariable(f't{slf.next_variable}') 432 | slf.next_variable += 1 433 | return temp 434 | 435 | def visit_program(slf, node): 436 | with slf.scope: 437 | for stmt in node.stmts: 438 | slf.visit(stmt) 439 | return slf.instrs 440 | 441 | def visit_block_stmt(slf, node): 442 | with slf.scope: 443 | for stmt in node.body: 444 | slf.visit(stmt) 445 | 446 | def find_or_create_variable(slf, name): 447 | try: 448 | return slf.scope.find_variable(name) 449 | except KeyError: 450 | variable = IRVariable(name) 451 | slf.scope.map_variable(variable) 452 | return variable 453 | 454 | def visit_assign_stmt(slf, node): 455 | # Assignments to a variable automatically instantiates that variable, 456 | # if it doesn't already exist. 457 | if isinstance(node.target, ASTIdentifer): 458 | target = slf.find_or_create_variable(node.target.value) 459 | else: 460 | target = slf.visit(node.target) 461 | expr = slf.visit(node.expr) 462 | 463 | if node.op == OP.ASSIGN: 464 | slf.emit(IRCopyAssignment(target, expr)) 465 | else: 466 | op = assign_ops[node.op] 467 | slf.emit(IRBinaryExprAssignment(target, target, op, expr)) 468 | 469 | def visit_input_stmt(slf, node): 470 | if isinstance(node.dest, ASTIdentifer): 471 | dest = slf.find_or_create_variable(node.dest.value) 472 | else: 473 | dest = slf.visit(node.dest) 474 | slf.emit(IRInputAssignment(dest)) 475 | 476 | def visit_output_stmt(slf, node): 477 | src = slf.visit(node.src) 478 | slf.emit(IROutput(src)) 479 | 480 | def visit_if_stmt(slf, node): 481 | label_false = slf.label() 482 | label_end = slf.label() 483 | 484 | # Condition 485 | cond = slf.visit(node.cond) 486 | slf.emit(IRGotoIfFalse(cond, label_false)) 487 | 488 | # True 489 | slf.visit(node.true) 490 | slf.emit(IRGoto(label_end)) 491 | 492 | # False 493 | slf.emit(label_false) 494 | if node.false: 495 | slf.visit(node.false) 496 | 497 | # End 498 | slf.emit(label_end) 499 | 500 | def visit_while_stmt(slf, node): 501 | label_loop = slf.label() 502 | label_end = slf.label() 503 | 504 | # Condition 505 | slf.emit(label_loop) 506 | cond = slf.visit(node.cond) 507 | slf.emit(IRGotoIfFalse(cond, label_end)) 508 | 509 | # Body 510 | with slf.scope: 511 | slf.scope.map_label(IRScope.CONTINUE, label_loop) 512 | slf.scope.map_label(IRScope.BREAK, label_end) 513 | slf.visit(node.body) 514 | slf.emit(IRGoto(label_loop)) 515 | 516 | # End 517 | slf.emit(label_end) 518 | 519 | def visit_continue_stmt(slf, node): 520 | try: 521 | label = slf.scope.find_label(IRScope.CONTINUE) 522 | except KeyError: 523 | raise Exception('Continue statement outside loop') 524 | slf.emit(IRGoto(label)) 525 | 526 | def visit_break_stmt(slf, node): 527 | try: 528 | label = slf.scope.find_label(IRScope.BREAK) 529 | except KeyError: 530 | raise SyntaxError('Break statement outside loop') 531 | slf.emit(IRGoto(label)) 532 | 533 | def visit_array_stmt(slf, node): 534 | target = slf.find_or_create_variable(node.target.value) 535 | array = IRArray(node.size.value) 536 | slf.emit(IRArrayAssignment(target, array)) 537 | 538 | def visit_binary_expr(slf, node): 539 | left = slf.visit(node.left) 540 | right = slf.visit(node.right) 541 | result = slf.temp() 542 | slf.emit(IRBinaryExprAssignment(result, left, node.op.value, right)) 543 | return result 544 | 545 | def visit_unary_expr(slf, node): 546 | right = slf.visit(node.right) 547 | result = slf.temp() 548 | slf.emit(IRUnaryExprAssignment(result, node.op.value, right)) 549 | return result 550 | 551 | def visit_subscript_expr(slf, node): 552 | addr = slf.visit(node.addr) 553 | index = slf.visit(node.index) 554 | return IRElement(addr, index) 555 | 556 | def visit_identifier(slf, node): 557 | return slf.scope.find_variable(node.value) 558 | 559 | def visit_literal(slf, node): 560 | return IRLiteral(node.value) 561 | 562 | 563 | class IntcodeGenerator: 564 | def generate(slf, ir): 565 | slf.memory = [] 566 | slf.labels = {} 567 | slf.free_scratches = set() 568 | slf.used_scratches = set() 569 | 570 | # Process each instruction. 571 | for instr in ir: 572 | visit = f'visit_{instr._id}' 573 | assert hasattr(slf, visit) 574 | getattr(slf, visit)(*chain(instr.lhs, instr.rhs)) 575 | 576 | # Insert a stop instruction at the end of the program. 577 | slf.memory.extend([OPCODE.STOP]) 578 | 579 | # Allocate storage for variables and arrays. 580 | offset = len(slf.memory) 581 | storage = {} 582 | for v in slf.memory: 583 | if isinstance(v, IRStorage) and v not in storage: 584 | storage[v] = len(slf.memory) 585 | slf.memory.extend(repeat(0, v.size)) 586 | 587 | # Replace label and storage placeholders. 588 | for i, cell in enumerate(slf.memory): 589 | if isinstance(cell, IRStorage): 590 | slf.memory[i] = storage[cell] 591 | elif isinstance(cell, IRLabel): 592 | slf.memory[i] = slf.labels[cell.rhs[0]] 593 | 594 | return slf.memory 595 | 596 | @contextmanager 597 | def scratch(slf): 598 | if len(slf.free_scratches) == 0: 599 | n = len(slf.used_scratches) 600 | slf.free_scratches.add(IRVariable(f'__scratch{n}__')) 601 | 602 | scratch = slf.free_scratches.pop() 603 | slf.used_scratches.add(scratch) 604 | 605 | yield scratch 606 | 607 | slf.used_scratches.remove(scratch) 608 | slf.free_scratches.add(scratch) 609 | 610 | def emit(slf, opcode, *params): 611 | # To handle indexing, we emit a prelude which modifies the instruction 612 | # with the address of the element we are reading or writing. 613 | elements = [e for e in params if isinstance(e, IRElement)] 614 | preludes = {} 615 | for element in elements: 616 | slf.emit(OPCODE.ADD, element.addr, element.index, IRAddress(0)) 617 | preludes[element] = len(slf.memory) - 1 618 | 619 | # Now emit the actual instruction. 620 | flags = ['1' if p.immediate else '0' for p in params] 621 | head = ''.join(reversed(flags)) + f'{opcode:02d}' 622 | slf.memory.append(int(head)) 623 | for param in params: 624 | if isinstance(param, IRLiteral) or isinstance(param, IRAddress): 625 | slf.memory.append(param.value) 626 | elif isinstance(param, IRElement): 627 | # Write the address of this parameter into the prelude 628 | # instruction which will modify it. 629 | slf.memory[preludes[param]] = len(slf.memory) 630 | slf.memory.append(0) 631 | else: 632 | slf.memory.append(param) 633 | 634 | def visit_label(slf, name): 635 | slf.labels[name] = len(slf.memory) 636 | 637 | def visit_input(slf, dest): 638 | slf.emit(OPCODE.INPUT, dest) 639 | 640 | def visit_output(slf, src): 641 | slf.emit(OPCODE.OUTPUT, src) 642 | 643 | def visit_copy(slf, result, right): 644 | slf.visit_binary(result, IRLiteral(0), OP.ADD, right) 645 | 646 | def visit_unary(slf, result, op, right): 647 | if op == OP.NOT: 648 | slf.visit_binary(result, IRLiteral(0), OP.EQ, right) 649 | elif op == OP.SUB: 650 | slf.visit_binary(result, IRLiteral(-1), OP.MUL, right) 651 | elif op == OP.ADD: 652 | slf.visit_copy(result, right) 653 | else: 654 | raise Exception(f"Unknown unary op '{op}'") 655 | 656 | def visit_binary(slf, result, left, op, right): 657 | if op == OP.OR: 658 | with slf.scratch() as scratch0, slf.scratch() as scratch1: 659 | slf.visit_binary(scratch0, left, OP.EQ, IRLiteral(0)) 660 | slf.visit_binary(scratch1, right, OP.EQ, IRLiteral(0)) 661 | slf.visit_binary(scratch0, scratch0, OP.ADD, scratch1) 662 | slf.visit_binary(result, scratch0, OP.LT, IRLiteral(2)) 663 | elif op == OP.AND: 664 | with slf.scratch() as scratch0, slf.scratch() as scratch1: 665 | slf.visit_binary(scratch0, left, OP.EQ, IRLiteral(0)) 666 | slf.visit_binary(scratch1, right, OP.EQ, IRLiteral(0)) 667 | slf.visit_binary(scratch0, scratch0, OP.ADD, scratch1) 668 | slf.visit_binary(result, scratch0, OP.EQ, IRLiteral(0)) 669 | elif op == OP.LT: 670 | slf.emit(OPCODE.LESS_THAN, left, right, result) 671 | elif op == OP.LE: 672 | with slf.scratch() as scratch0, slf.scratch() as scratch1: 673 | slf.visit_binary(scratch0, left, OP.LT, right) 674 | slf.visit_binary(scratch1, left, OP.EQ, right) 675 | slf.visit_binary(scratch0, scratch0, OP.ADD, scratch1) 676 | slf.visit_binary(result, scratch0, OP.EQ, IRLiteral(1)) 677 | elif op == OP.GT: 678 | with slf.scratch() as scratch: 679 | slf.visit_binary(scratch, left, OP.LE, right) 680 | slf.visit_unary(result, OP.NOT, scratch) 681 | elif op == OP.GE: 682 | with slf.scratch() as scratch: 683 | slf.visit_binary(scratch, left, OP.LT, right) 684 | slf.visit_unary(result, OP.NOT, scratch) 685 | elif op == OP.EQ: 686 | slf.emit(OPCODE.EQUALS, left, right, result) 687 | elif op == OP.NE: 688 | with slf.scratch() as scratch: 689 | slf.emit(OPCODE.EQUALS, left, right, scratch) 690 | slf.visit_unary(result, OP.NOT, scratch) 691 | elif op == OP.ADD: 692 | slf.emit(OPCODE.ADD, left, right, result) 693 | elif op == OP.SUB: 694 | with slf.scratch() as scratch: 695 | slf.visit_unary(scratch, OP.SUB, right) 696 | slf.emit(OPCODE.ADD, left, scratch, result) 697 | elif op == OP.MUL: 698 | slf.emit(OPCODE.MULTIPLY, left, right, result) 699 | else: 700 | raise Exception(f"Unknown binary op '{op}'") 701 | 702 | def visit_goto(slf, label): 703 | slf.emit(OPCODE.JUMP_IF_TRUE, IRLiteral(1), label) 704 | 705 | def visit_goto_if_false(slf, cond, label): 706 | slf.emit(OPCODE.JUMP_IF_FALSE, cond, label) 707 | 708 | def visit_array(slf, result, array): 709 | slf.emit(OPCODE.ADD, IRLiteral(0), array, result) 710 | 711 | 712 | def intscript(file): 713 | # Load grammar. 714 | grammar_path = Path(__file__).parent / 'intscript.lark' 715 | with open(grammar_path, mode='rt', encoding='UTF-8') as f: 716 | parser = Lark(f.read(), start='program', propagate_positions=True) 717 | 718 | # Parse tree. 719 | with open(file, mode='rt', encoding='UTF-8') as f: 720 | parse_tree = parser.parse(f.read()) 721 | 722 | # Abstract syntax tree. 723 | ast = ASTTransformer().transform(parse_tree) 724 | 725 | # Intermediate representation. 726 | ir = IRGenerator().visit(ast) 727 | 728 | # Intcode. 729 | return IntcodeGenerator().generate(ir) 730 | 731 | 732 | def main(argv): 733 | arg_parser = ArgumentParser(description='intscript compiler') 734 | arg_parser.add_argument( 735 | 'file', 736 | help='intscript file' 737 | ) 738 | args = arg_parser.parse_args(argv) 739 | intcode = intscript(args.file) 740 | print(','.join(map(str, intcode))) 741 | 742 | 743 | if __name__ == "__main__": 744 | main(sys.argv[1:]) 745 | -------------------------------------------------------------------------------- /intscript_tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """intscript unit tests""" 4 | 5 | import unittest 6 | 7 | from intscript import intscript 8 | from intcode import intcode 9 | 10 | 11 | def run(program, *inputs): 12 | return intcode(program, *inputs).outputs 13 | 14 | 15 | def compile_and_run(file, *inputs): 16 | return run(intscript(file, *inputs), *inputs) 17 | 18 | 19 | class IntscriptTests(unittest.TestCase): 20 | def test_booleans(slf): 21 | slf.assertEqual( 22 | compile_and_run('tests/booleans.is'), 23 | [0, 1, 1, 1, 0, 0, 0, 1, 1, 0] 24 | ) 25 | 26 | def test_comparisons(slf): 27 | slf.assertEqual( 28 | compile_and_run('tests/comparisons.is'), 29 | [1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1] 30 | ) 31 | 32 | def test_arrays(slf): 33 | slf.assertEqual( 34 | compile_and_run('tests/arrays.is'), 35 | [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 24] 36 | ) 37 | 38 | 39 | class FibonacciTests(unittest.TestCase): 40 | def setUp(slf): 41 | slf.fibonacci = intscript('samples/fibonacci.is') 42 | 43 | def test_fibonacci_0(slf): 44 | slf.assertEqual(run(slf.fibonacci, 0), []) 45 | 46 | def test_fibonacci_1(slf): 47 | slf.assertEqual(run(slf.fibonacci, 1), [1]) 48 | 49 | def test_fibonacci_5(slf): 50 | slf.assertEqual(run(slf.fibonacci, 5), [1, 1, 2, 3, 5]) 51 | 52 | def test_fibonacci_9(slf): 53 | slf.assertEqual(run(slf.fibonacci, 9), [1, 1, 2, 3, 5, 8, 13, 21, 34]) 54 | 55 | def test_fibonacci_20(slf): 56 | slf.assertEqual(run(slf.fibonacci, 20)[19], 6765) 57 | 58 | 59 | class IntcodeTests(unittest.TestCase): 60 | @classmethod 61 | def setUpClass(cls): 62 | cls.intcode = intscript('samples/intcode.is') 63 | 64 | # Find the index where memory starts in the program. 65 | for i in reversed(range(len(cls.intcode))): 66 | if cls.intcode[i] == 99: 67 | cls.memstart = i + 1 68 | break 69 | 70 | def execution_test(slf, program, *inputs): 71 | """Check that a program runs identically on a real intcode computer.""" 72 | actual = intcode(slf.intcode, len(program), *program, *inputs) 73 | expected = intcode(program, *inputs) 74 | slf.assertEqual(actual.outputs, expected.outputs) 75 | memory = actual.memory[slf.memstart:slf.memstart+len(program)] 76 | slf.assertEqual(memory, expected.memory) 77 | 78 | def test_fibonacci(slf): 79 | fibonacci = intscript('samples/fibonacci.is') 80 | slf.execution_test(fibonacci, 0) 81 | slf.execution_test(fibonacci, 1) 82 | slf.execution_test(fibonacci, 5) 83 | slf.execution_test(fibonacci, 10) 84 | 85 | def test_d02_example1(slf): 86 | slf.execution_test([1, 0, 0, 0, 99]) 87 | 88 | def test_d02_example2(slf): 89 | slf.execution_test([2, 3, 0, 3, 99]) 90 | 91 | def test_d02_example3(slf): 92 | slf.execution_test([2, 4, 4, 5, 99, 0]) 93 | 94 | def test_d02_example4(slf): 95 | slf.execution_test([1, 1, 1, 4, 99, 5, 6, 0, 99]) 96 | 97 | def test_d02_example5(slf): 98 | slf.execution_test([1, 9, 10, 3, 2, 3, 11, 0, 99, 30, 40, 50]) 99 | 100 | def test_d02_puzzle(slf): 101 | puzzle = "1,0,0,3,1,1,2,3,1,3,4,3,1,5,0,3,2,1,6,19,2,19,6,23,1,23,5,27,1,9,27,31,1,31,10,35,2,35,9,39,1,5,39,43,2,43,9,47,1,5,47,51,2,51,13,55,1,55,10,59,1,59,10,63,2,9,63,67,1,67,5,71,2,13,71,75,1,75,10,79,1,79,6,83,2,13,83,87,1,87,6,91,1,6,91,95,1,10,95,99,2,99,6,103,1,103,5,107,2,6,107,111,1,10,111,115,1,115,5,119,2,6,119,123,1,123,5,127,2,127,6,131,1,131,5,135,1,2,135,139,1,139,13,0,99,2,0,14,0" 102 | program = list(map(int, puzzle.split(','))) 103 | program[1] = 89 104 | program[2] = 76 105 | cpu = intcode(slf.intcode, len(program), *program) 106 | slf.assertEqual(cpu.memory[slf.memstart], 19690720) 107 | 108 | def test_d05_example1(slf): 109 | example1 = [3, 9, 8, 9, 10, 9, 4, 9, 99, -1, 8] 110 | slf.execution_test(example1, 8) 111 | slf.execution_test(example1, 13) 112 | 113 | def test_d05_example2(slf): 114 | example2 = [3, 9, 7, 9, 10, 9, 4, 9, 99, -1, 8] 115 | slf.execution_test(example2, 7) 116 | slf.execution_test(example2, 13) 117 | 118 | def test_d05_example3(slf): 119 | example3 = [3, 3, 1108, -1, 8, 3, 4, 3, 99] 120 | slf.execution_test(example3, 8) 121 | slf.execution_test(example3, 13) 122 | 123 | def test_d05_example4(slf): 124 | example4 = [3, 3, 1107, -1, 8, 3, 4, 3, 99] 125 | slf.execution_test(example4, 7) 126 | slf.execution_test(example4, 13) 127 | 128 | def test_d05_example5(slf): 129 | example5 = [3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, -1, 0, 1, 9] 130 | slf.execution_test(example5, -1) 131 | slf.execution_test(example5, 0) 132 | slf.execution_test(example5, 1) 133 | 134 | def test_d05_example6(slf): 135 | example6 = [3, 3, 1105, -1, 9, 1101, 0, 0, 12, 4, 12, 99, 1] 136 | slf.execution_test(example6, -1) 137 | slf.execution_test(example6, 0) 138 | slf.execution_test(example6, 1) 139 | 140 | def test_d05_example7(slf): 141 | example7 = [3, 21, 1008, 21, 8, 20, 1005, 20, 22, 107, 8, 21, 20, 1006, 20, 31, 1106, 0, 36, 98, 0, 0, 1002, 21, 125, 20, 4, 20, 1105, 1, 46, 104, 999, 1105, 1, 46, 1101, 1000, 1, 20, 4, 20, 1105, 1, 46, 98, 99] 142 | slf.execution_test(example7, 7) 143 | slf.execution_test(example7, 8) 144 | slf.execution_test(example7, 9) 145 | 146 | def test_d05_puzzle(slf): 147 | puzzle = "3,225,1,225,6,6,1100,1,238,225,104,0,1102,59,58,224,1001,224,-3422,224,4,224,102,8,223,223,101,3,224,224,1,224,223,223,1101,59,30,225,1101,53,84,224,101,-137,224,224,4,224,1002,223,8,223,101,3,224,224,1,223,224,223,1102,42,83,225,2,140,88,224,1001,224,-4891,224,4,224,1002,223,8,223,1001,224,5,224,1,223,224,223,1101,61,67,225,101,46,62,224,1001,224,-129,224,4,224,1002,223,8,223,101,5,224,224,1,223,224,223,1102,53,40,225,1001,35,35,224,1001,224,-94,224,4,224,102,8,223,223,101,6,224,224,1,223,224,223,1101,5,73,225,1002,191,52,224,1001,224,-1872,224,4,224,1002,223,8,223,1001,224,5,224,1,223,224,223,102,82,195,224,101,-738,224,224,4,224,1002,223,8,223,1001,224,2,224,1,224,223,223,1101,83,52,225,1101,36,77,225,1101,9,10,225,1,113,187,224,1001,224,-136,224,4,224,1002,223,8,223,101,2,224,224,1,224,223,223,4,223,99,0,0,0,677,0,0,0,0,0,0,0,0,0,0,0,1105,0,99999,1105,227,247,1105,1,99999,1005,227,99999,1005,0,256,1105,1,99999,1106,227,99999,1106,0,265,1105,1,99999,1006,0,99999,1006,227,274,1105,1,99999,1105,1,280,1105,1,99999,1,225,225,225,1101,294,0,0,105,1,0,1105,1,99999,1106,0,300,1105,1,99999,1,225,225,225,1101,314,0,0,106,0,0,1105,1,99999,1007,226,226,224,1002,223,2,223,1006,224,329,1001,223,1,223,1108,226,226,224,102,2,223,223,1006,224,344,101,1,223,223,1007,677,677,224,102,2,223,223,1006,224,359,101,1,223,223,1108,677,226,224,1002,223,2,223,1005,224,374,1001,223,1,223,7,677,226,224,102,2,223,223,1005,224,389,1001,223,1,223,1008,677,677,224,1002,223,2,223,1005,224,404,101,1,223,223,108,226,226,224,1002,223,2,223,1006,224,419,101,1,223,223,1008,226,677,224,1002,223,2,223,1006,224,434,1001,223,1,223,1107,677,226,224,1002,223,2,223,1005,224,449,101,1,223,223,1008,226,226,224,102,2,223,223,1005,224,464,1001,223,1,223,8,226,226,224,1002,223,2,223,1006,224,479,1001,223,1,223,107,226,677,224,102,2,223,223,1005,224,494,1001,223,1,223,7,226,226,224,102,2,223,223,1005,224,509,1001,223,1,223,107,226,226,224,102,2,223,223,1005,224,524,101,1,223,223,107,677,677,224,1002,223,2,223,1006,224,539,101,1,223,223,8,677,226,224,1002,223,2,223,1006,224,554,101,1,223,223,1107,677,677,224,1002,223,2,223,1005,224,569,101,1,223,223,108,226,677,224,1002,223,2,223,1006,224,584,101,1,223,223,7,226,677,224,1002,223,2,223,1005,224,599,1001,223,1,223,8,226,677,224,102,2,223,223,1006,224,614,1001,223,1,223,108,677,677,224,1002,223,2,223,1006,224,629,1001,223,1,223,1007,226,677,224,1002,223,2,223,1006,224,644,101,1,223,223,1108,226,677,224,102,2,223,223,1005,224,659,1001,223,1,223,1107,226,677,224,102,2,223,223,1006,224,674,1001,223,1,223,4,223,99,226" 148 | program = list(map(int, puzzle.split(','))) 149 | slf.execution_test(program, 1) 150 | slf.execution_test(program, 5) 151 | 152 | if __name__ == '__main__': 153 | unittest.main() 154 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | lark-parser==0.7.3 2 | -------------------------------------------------------------------------------- /samples/fibonacci.is: -------------------------------------------------------------------------------- 1 | input n; 2 | 3 | i = 0; 4 | a = 0; 5 | b = 1; 6 | 7 | while(i < n) 8 | { 9 | t = a + b; 10 | a = b; 11 | b = t; 12 | i += 1; 13 | output a; 14 | } 15 | -------------------------------------------------------------------------------- /samples/intcode.is: -------------------------------------------------------------------------------- 1 | # An intcode computer that can run on an intcode computer! 2 | 3 | # Read the program into memory. The computer has 1k of memory. 4 | array memory[1024]; 5 | input n; 6 | i = 0; 7 | while(i < n) 8 | { 9 | input memory[i]; 10 | i += 1; 11 | } 12 | 13 | # Run the computer. 14 | pc = 0; 15 | while(memory[pc] != 99) 16 | { 17 | opcode = memory[pc]; 18 | pc += 1; 19 | 20 | # Instead of implementing a modulo operator to extract the opcode and the 21 | # various parameter modes, we implement each possibility separately. 22 | 23 | # ADD 24 | if(opcode == 1) 25 | { 26 | memory[memory[pc+2]] = memory[memory[pc]] + memory[memory[pc+1]]; 27 | pc += 3; 28 | } 29 | else if(opcode == 101) 30 | { 31 | memory[memory[pc+2]] = memory[pc] + memory[memory[pc+1]]; 32 | pc += 3; 33 | } 34 | else if(opcode == 1001) 35 | { 36 | memory[memory[pc+2]] = memory[memory[pc]] + memory[pc+1]; 37 | pc += 3; 38 | } 39 | else if(opcode == 1101) 40 | { 41 | memory[memory[pc+2]] = memory[pc] + memory[pc+1]; 42 | pc += 3; 43 | } 44 | 45 | # MULTIPLY 46 | else if(opcode == 2) 47 | { 48 | memory[memory[pc+2]] = memory[memory[pc]] * memory[memory[pc+1]]; 49 | pc += 3; 50 | } 51 | else if(opcode == 102) 52 | { 53 | memory[memory[pc+2]] = memory[pc] * memory[memory[pc+1]]; 54 | pc += 3; 55 | } 56 | else if(opcode == 1002) 57 | { 58 | memory[memory[pc+2]] = memory[memory[pc]] * memory[pc+1]; 59 | pc += 3; 60 | } 61 | else if(opcode == 1102) 62 | { 63 | memory[memory[pc+2]] = memory[pc] * memory[pc+1]; 64 | pc += 3; 65 | } 66 | 67 | # INPUT 68 | else if(opcode == 3) 69 | { 70 | input memory[memory[pc]]; 71 | pc += 1; 72 | } 73 | 74 | # OUTPUT 75 | else if(opcode == 4) 76 | { 77 | output memory[memory[pc]]; 78 | pc += 1; 79 | } 80 | else if(opcode == 104) 81 | { 82 | output memory[pc]; 83 | pc += 1; 84 | } 85 | 86 | # JUMP_IF_TRUE 87 | else if(opcode == 5) 88 | { 89 | if(memory[memory[pc]] != 0) 90 | { 91 | pc = memory[memory[pc+1]]; 92 | } 93 | else 94 | { 95 | pc += 2; 96 | } 97 | } 98 | else if(opcode == 105) 99 | { 100 | if(memory[pc] != 0) 101 | { 102 | pc = memory[memory[pc+1]]; 103 | } 104 | else 105 | { 106 | pc += 2; 107 | } 108 | } 109 | else if(opcode == 1005) 110 | { 111 | if(memory[memory[pc]] != 0) 112 | { 113 | pc = memory[pc+1]; 114 | } 115 | else 116 | { 117 | pc += 2; 118 | } 119 | } 120 | else if(opcode == 1105) 121 | { 122 | if(memory[pc] != 0) 123 | { 124 | pc = memory[pc+1]; 125 | } 126 | else 127 | { 128 | pc += 2; 129 | } 130 | } 131 | 132 | # JUMP_IF_FALSE 133 | else if(opcode == 6) 134 | { 135 | if(memory[memory[pc]] == 0) 136 | { 137 | pc = memory[memory[pc+1]]; 138 | } 139 | else 140 | { 141 | pc += 2; 142 | } 143 | } 144 | else if(opcode == 106) 145 | { 146 | if(memory[pc] == 0) 147 | { 148 | pc = memory[memory[pc+1]]; 149 | } 150 | else 151 | { 152 | pc += 2; 153 | } 154 | } 155 | else if(opcode == 1006) 156 | { 157 | if(memory[memory[pc]] == 0) 158 | { 159 | pc = memory[pc+1]; 160 | } 161 | else 162 | { 163 | pc += 2; 164 | } 165 | } 166 | else if(opcode == 1106) 167 | { 168 | if(memory[pc] == 0) 169 | { 170 | pc = memory[pc+1]; 171 | } 172 | else 173 | { 174 | pc += 2; 175 | } 176 | } 177 | 178 | # LESS_THAN 179 | else if(opcode == 7) 180 | { 181 | memory[memory[pc+2]] = memory[memory[pc]] < memory[memory[pc+1]]; 182 | pc += 3; 183 | } 184 | else if(opcode == 107) 185 | { 186 | memory[memory[pc+2]] = memory[pc] < memory[memory[pc+1]]; 187 | pc += 3; 188 | } 189 | else if(opcode == 1007) 190 | { 191 | memory[memory[pc+2]] = memory[memory[pc]] < memory[pc+1]; 192 | pc += 3; 193 | } 194 | else if(opcode == 1107) 195 | { 196 | memory[memory[pc+2]] = memory[pc] < memory[pc+1]; 197 | pc += 3; 198 | } 199 | 200 | # EQUALS 201 | else if(opcode == 8) 202 | { 203 | memory[memory[pc+2]] = memory[memory[pc]] == memory[memory[pc+1]]; 204 | pc += 3; 205 | } 206 | else if(opcode == 108) 207 | { 208 | memory[memory[pc+2]] = memory[pc] == memory[memory[pc+1]]; 209 | pc += 3; 210 | } 211 | else if(opcode == 1008) 212 | { 213 | memory[memory[pc+2]] = memory[memory[pc]] == memory[pc+1]; 214 | pc += 3; 215 | } 216 | else if(opcode == 1108) 217 | { 218 | memory[memory[pc+2]] = memory[pc] == memory[pc+1]; 219 | pc += 3; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /tests/arrays.is: -------------------------------------------------------------------------------- 1 | array a[10]; 2 | n = 10; 3 | i = 0; 4 | 5 | while(i < n) 6 | { 7 | if(i < 2) 8 | { 9 | a[i] = 1; 10 | } 11 | else 12 | { 13 | a[i] = a[i-1] + a[i-2]; 14 | } 15 | output a[i]; 16 | i += 1; 17 | } 18 | 19 | array b[5]; 20 | b[0] = 12; 21 | b[1] = b[0] * 2; 22 | b[2] = (7 - b[1]) + 35; 23 | b[3] = 2 * b[2]; 24 | b[4] = b[3] - b[0]; 25 | output b[4]; 26 | -------------------------------------------------------------------------------- /tests/booleans.is: -------------------------------------------------------------------------------- 1 | # Or 2 | output 0 or 0; 3 | output 0 or 1; 4 | output 1 or 0; 5 | output 1 or 1; 6 | 7 | # And 8 | output 0 and 0; 9 | output 0 and 1; 10 | output 1 and 0; 11 | output 1 and 1; 12 | 13 | # Not 14 | output not 0; 15 | output not 1; 16 | -------------------------------------------------------------------------------- /tests/comparisons.is: -------------------------------------------------------------------------------- 1 | # Less-than 2 | output 1 < 2; 3 | output 2 < 2; 4 | output 3 < 2; 5 | 6 | # Less-than-or-equal 7 | output 1 <= 2; 8 | output 2 <= 2; 9 | output 3 <= 2; 10 | 11 | # Greater-than 12 | output 1 > 2; 13 | output 2 > 2; 14 | output 3 > 2; 15 | 16 | # Greater-than-or-equal 17 | output 1 >= 2; 18 | output 2 >= 2; 19 | output 3 >= 2; 20 | 21 | # Equal 22 | output 1 == 2; 23 | output 2 == 2; 24 | output 3 == 2; 25 | 26 | # Not-equal 27 | output 1 != 2; 28 | output 2 != 2; 29 | output 3 != 2; 30 | --------------------------------------------------------------------------------