├── README.md ├── alpha ├── README.md ├── alpha.rb └── input.txt ├── beta ├── Gemfile ├── Gemfile.lock ├── README.md ├── ast.rb ├── compiler.rb ├── grammar.y ├── interpreter.rb ├── lexer.rb ├── parser.rb ├── test │ ├── conditionals.txt │ ├── sample.txt │ └── variables.txt └── vm.rb └── gamma ├── Gemfile ├── Gemfile.lock ├── README.md ├── ast.rb ├── compiler.rb ├── grammar.y ├── interpreter.rb ├── lexer.rb ├── parser.rb ├── test ├── fib.txt ├── operators.txt ├── simple_fn.txt └── while.txt └── vm.rb /README.md: -------------------------------------------------------------------------------- 1 | A collection of small languages I've created while learning the basics of compilers and language design. 2 | 3 | I'm planning on making a series of languages to practice building each component separately, hooking them together, and playing with them. 4 | -------------------------------------------------------------------------------- /alpha/README.md: -------------------------------------------------------------------------------- 1 | # alpha 2 | 3 | ## Introduction 4 | Pure Ruby implementation of a very simple programming language. Actually, it probably doesn't meet the definition of a programming language, as it lacks variables, flow control and other necessities. However, it is processed in a similar manner as a programming language during compilation. 5 | 6 | Nevertheless, it does illustrate many of the moving parts of a compiler. 7 | 8 | This is part of a series of tiny languages I'm creating to learn programming language design and theory. Subsequent languages will contain more features. 9 | 10 | ## Running 11 | Pass the input file as the first argument: 12 | 13 | $ ruby alpha.rb input.txt 14 | 15 | To switch between interpreted and compiled execution, uncomment the appropriate line in `alpha.rb`. 16 | 17 | ## Structure 18 | 19 | 1. `Lexer` splits up the input file into a series of tokens 20 | 2. `Parser` consumes these tokens to build an Abstract Syntax Tree. In this language, it is represented by an Array of `PrintNode`'s. 21 | 3. `Interpreter` traverses the AST, pulls out the data from each node, and evaluates it 22 | 4. `Compiler` emits bytecode (represented as an Array) that the `VM` consumes. The bytecode presupposes use of a stack. 23 | 24 | ## Semantics 25 | In this language, the only valid statement is a print statement: 26 | 27 | print "Hello" 28 | print "This will get printed out upon execution" 29 | 30 | The compiler accepts input files containing 0-n lines, where each line contains a print statement, a comment (starting with `#`), or is left blank. Syntax errors are reported with a corresponding line number. 31 | 32 | -------------------------------------------------------------------------------- /alpha/alpha.rb: -------------------------------------------------------------------------------- 1 | # Dirt-simple lexer that parses statements of the form: 2 | # print "" 3 | class Lexer 4 | def lex(code) 5 | tokens = [] 6 | 7 | i = 0 8 | line = 1 9 | 10 | while i < code.length 11 | chunk = code[i..code.length] 12 | 13 | if print_statement = chunk[/\Aprint \"(.*)\"/] 14 | tokens << [:PRINT, $1] 15 | i += print_statement.length 16 | 17 | elsif comment = chunk[/\A#.*/] 18 | i += chunk.index("\n") || comment.length 19 | 20 | elsif chunk[0] == "\n" 21 | line += 1 22 | i += 1 23 | 24 | else 25 | raise "Syntax error on line #{line}: \"#{chunk[0..chunk.index("\n")-1]}\"" 26 | 27 | end 28 | end 29 | 30 | tokens 31 | end 32 | end 33 | 34 | # An AST with only one node :) 35 | class PrintNode < Struct.new(:str) 36 | end 37 | 38 | # Converts token stream produced by lexer into AST nodes 39 | class Parser 40 | def initialize 41 | @lexer = Lexer.new 42 | end 43 | 44 | def parse(code) 45 | nodes = [] 46 | 47 | tokens = @lexer.lex(code) 48 | tokens.each do |token| 49 | if token[0] == :PRINT 50 | nodes << PrintNode.new(token[1]) 51 | else 52 | raise "Unexpected token: #{token}" 53 | end 54 | end 55 | 56 | nodes 57 | end 58 | end 59 | 60 | # Walks the specified AST, executing it 61 | class Interpreter 62 | def evaluate(code) 63 | nodes = Parser.new.parse(code) 64 | 65 | nodes.each do |node| 66 | method = "evaluate" + node.class.name.gsub(/([A-Z])/) { "_#{$1.downcase}" } 67 | 68 | __send__(method, node) 69 | end 70 | end 71 | 72 | def evaluate_print_node(node) 73 | print node.str + "\n" 74 | end 75 | end 76 | 77 | # Simple bytecode format used by the compiler and the VM 78 | # 79 | # Opcode Operands Stack before Stack after 80 | # --------------------------------------------------------- 81 | PUSH_STR = 0 # String to push [] [str] 82 | PRINT = 1 # [str] [] 83 | RETURN = 2 # 84 | 85 | class Compiler 86 | def initialize 87 | @bytecode = [] 88 | end 89 | 90 | def compile(code) 91 | nodes = Parser.new.parse(code) 92 | 93 | nodes.each do |node| 94 | method = "compile" + node.class.name.gsub(/([A-Z])/) { "_#{$1.downcase}" } 95 | 96 | __send__(method, node) 97 | end 98 | 99 | emit(RETURN) 100 | 101 | @bytecode 102 | end 103 | 104 | def compile_print_node(node) 105 | emit(PUSH_STR, node.str) 106 | emit(PRINT) 107 | end 108 | 109 | def emit(opcode, *operands) 110 | @bytecode << opcode 111 | @bytecode.concat(operands) 112 | end 113 | end 114 | 115 | class VM 116 | def execute(bytecode) 117 | ip = 0 118 | stack = [] 119 | 120 | loop do 121 | opcode = bytecode[ip] 122 | ip += 1 123 | 124 | case opcode 125 | when PUSH_STR 126 | stack.push(bytecode[ip]) 127 | ip += 1 128 | 129 | when PRINT 130 | s = stack.pop 131 | 132 | print s + "\n" 133 | 134 | when RETURN 135 | break 136 | 137 | else 138 | raise "Unexpected opcode #{opcode}" 139 | 140 | end 141 | end 142 | end 143 | end 144 | 145 | code = File.read(ARGV[0]) 146 | #Interpreter.new.evaluate(File.read(ARGV[0])) 147 | VM.new.execute(Compiler.new.compile(code)) 148 | -------------------------------------------------------------------------------- /alpha/input.txt: -------------------------------------------------------------------------------- 1 | # This is a comment 2 | # print "Shouldn't see this" 3 | 4 | print "Hello" 5 | print "Several words" 6 | print "Last line" 7 | 8 | # Blank strings should work 9 | print "" 10 | 11 | # Comment on last line 12 | -------------------------------------------------------------------------------- /beta/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'racc' 4 | -------------------------------------------------------------------------------- /beta/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | racc (1.4.11) 5 | 6 | PLATFORMS 7 | ruby 8 | 9 | DEPENDENCIES 10 | racc 11 | -------------------------------------------------------------------------------- /beta/README.md: -------------------------------------------------------------------------------- 1 | # beta 2 | 3 | Pure Ruby implementation of a simple programming language. Unlike alpha, we support several more concepts, such as mutable variables and flow control. This requires a real parser, so we use RACC as the parser generator. 4 | 5 | Beta supports: 6 | 7 | * Dynamic typing 8 | * Assigning values to variables (`var = 42`) 9 | * Basic operators: `+`, `-`, `*`, `/` 10 | * Boolean operators: `==` and `!=` 11 | * Flow control via `if` and `else` 12 | * Three basic data types: integers, strings, and booleans (represented by `true`/`false`) 13 | * 'Function' invocation (only `print` is supported) 14 | 15 | ## Example 16 | print("Hello, I am ") 17 | 18 | age = 1 + 4 19 | print(age) 20 | 21 | ## Installation 22 | Install dependencies: 23 | 24 | $ cd beta 25 | $ bundle 26 | 27 | ## Running 28 | To use the interpreter: 29 | 30 | $ bundle exec ruby interpreter.rb test/variables.txt 31 | 32 | To try out the bytecode compiler + VM version: 33 | 34 | $ bundle exec ruby vm.rb test/variables.txt 35 | 36 | -------------------------------------------------------------------------------- /beta/ast.rb: -------------------------------------------------------------------------------- 1 | class AST < Struct.new(:nodes) 2 | def <<(node) 3 | nodes << node 4 | 5 | self 6 | end 7 | end 8 | 9 | class LiteralNode < Struct.new(:value) 10 | end 11 | 12 | class NumberNode < LiteralNode 13 | end 14 | 15 | class StringNode < LiteralNode 16 | end 17 | 18 | class TrueNode < LiteralNode 19 | def initialize 20 | super(true) 21 | end 22 | end 23 | 24 | class FalseNode < LiteralNode 25 | def initialize 26 | super(false) 27 | end 28 | end 29 | 30 | class CallNode < Struct.new(:name, :args) 31 | end 32 | 33 | class OpNode < Struct.new(:left, :right) 34 | end 35 | 36 | class AddNode < OpNode 37 | end 38 | 39 | class SubNode < OpNode 40 | end 41 | 42 | class MulNode < OpNode 43 | end 44 | 45 | class DivNode < OpNode 46 | end 47 | 48 | class EqNode < OpNode 49 | end 50 | 51 | class NotEqNode < OpNode 52 | end 53 | 54 | class GetLocalNode < Struct.new(:name) 55 | end 56 | 57 | class SetLocalNode < Struct.new(:name, :value) 58 | end 59 | 60 | class IfNode < Struct.new(:condition, :if_block, :else_block) 61 | end 62 | -------------------------------------------------------------------------------- /beta/compiler.rb: -------------------------------------------------------------------------------- 1 | require 'pp' 2 | require_relative 'parser' 3 | 4 | class Instruction < Struct.new(:opcode, :operands) 5 | end 6 | 7 | class Compiler 8 | def initialize 9 | @bytecode = [] 10 | end 11 | 12 | def compile(code) 13 | ast = Parser.new.parse(code) 14 | 15 | ast.nodes.each { |n| visit(n) } 16 | emit(:return) 17 | 18 | @bytecode 19 | end 20 | 21 | private 22 | 23 | def emit(op, *operands) 24 | inst = Instruction.new(op, operands) 25 | @bytecode << inst 26 | 27 | inst 28 | end 29 | 30 | def visit(node) 31 | if node.is_a?(LiteralNode) 32 | visit_literal(node) 33 | return 34 | end 35 | 36 | method = "visit" + node.class.name.gsub(/([A-Z])/) { "_#{$1.downcase}" }.gsub("_node", "") 37 | __send__(method, node) 38 | end 39 | 40 | def visit_literal(node) 41 | emit(:push, node.value) 42 | end 43 | 44 | def visit_op(node, &block) 45 | visit(node.left) 46 | visit(node.right) 47 | 48 | yield 49 | end 50 | 51 | def visit_add(node) 52 | visit_op(node) { emit(:add) } 53 | end 54 | 55 | def visit_sub(node) 56 | visit_op(node) { emit(:sub) } 57 | end 58 | 59 | def visit_mul(node) 60 | visit_op(node) { emit(:mul) } 61 | end 62 | 63 | def visit_div(node) 64 | visit_op(node) { emit(:div) } 65 | end 66 | 67 | def visit_eq(node) 68 | visit_op(node) { emit(:eq) } 69 | end 70 | 71 | def visit_not_eq(node) 72 | visit_op(node) { emit(:not_eq) } 73 | end 74 | 75 | def visit_call(node) 76 | visit(node.args.first) 77 | 78 | emit(:call, node.name) 79 | end 80 | 81 | def visit_get_local(node) 82 | emit(:get_local, node.name) 83 | end 84 | 85 | def visit_set_local(node) 86 | visit(node.value) 87 | 88 | emit(:set_local, node.name) 89 | end 90 | 91 | def visit_if(node) 92 | visit(node.condition) 93 | 94 | branch = emit(:if, 0, 0) 95 | 96 | branch.operands[0] = @bytecode.length 97 | node.if_block.each { |n| visit(n) } 98 | jump = emit(:jump, 0) 99 | 100 | branch.operands[1] = @bytecode.length 101 | node.else_block.each { |n| visit(n) } 102 | 103 | jump.operands[0] = @bytecode.length 104 | end 105 | end 106 | 107 | if $0 == __FILE__ 108 | pp Compiler.new.compile(File.read(ARGV[0])) 109 | end 110 | -------------------------------------------------------------------------------- /beta/grammar.y: -------------------------------------------------------------------------------- 1 | class Parser 2 | 3 | token NUMBER 4 | token STRING 5 | token NEWLINE 6 | token TRUE FALSE 7 | token IDENTIFIER 8 | token IF ELSE END 9 | 10 | prechigh 11 | left '*' '/' 12 | left '+' '-' 13 | preclow 14 | 15 | rule 16 | Program: 17 | { result = AST.new([]) } 18 | | Statements { result = AST.new(val[0]) } 19 | ; 20 | 21 | Statements: 22 | Statement { result = val } 23 | | Statements Terminator Statement { result = val[0] << val[2] } 24 | | Statements Terminator { result = val[0] } 25 | | Terminator Statements { result = val[1] } 26 | | Terminator { result = [] } 27 | ; 28 | 29 | Statement: 30 | Expression 31 | | If 32 | | Call 33 | | SetLocal 34 | 35 | Expression: 36 | Literal 37 | | Operator 38 | | GetLocal 39 | | '(' Expression ')' { result = val[1] } 40 | ; 41 | 42 | Literal: 43 | NUMBER { result = NumberNode.new(val[0]) } 44 | | STRING { result = StringNode.new(val[0]) } 45 | | TRUE { result = TrueNode.new } 46 | | FALSE { result = FalseNode.new } 47 | ; 48 | 49 | Operator: 50 | Expression '+' Expression { result = AddNode.new(val[0], val[2]) } 51 | | Expression '-' Expression { result = SubNode.new(val[0], val[2]) } 52 | | Expression '*' Expression { result = MulNode.new(val[0], val[2]) } 53 | | Expression '/' Expression { result = DivNode.new(val[0], val[2]) } 54 | | Expression '==' Expression { result = EqNode.new(val[0], val[2]) } 55 | | Expression '!=' Expression { result = NotEqNode.new(val[0], val[2]) } 56 | ; 57 | 58 | Call: 59 | IDENTIFIER '(' Expression ')' { result = CallNode.new(val[0], [val[2]]) } 60 | ; 61 | 62 | GetLocal: 63 | IDENTIFIER { result = GetLocalNode.new(val[0]) } 64 | ; 65 | 66 | SetLocal: 67 | IDENTIFIER '=' Expression { result = SetLocalNode.new(val[0], val[2]) } 68 | ; 69 | 70 | If: 71 | IF Expression Statements END { result = IfNode.new(val[1], val[2], []) } 72 | | IF Expression Statements ELSE Statements END { result = IfNode.new(val[1], val[2], val[4]) } 73 | ; 74 | 75 | Terminator: 76 | NEWLINE 77 | | ";" 78 | ; 79 | 80 | ---- header 81 | require_relative 'lexer' 82 | require_relative 'ast' 83 | 84 | ---- inner 85 | def parse(code, show_tokens = false) 86 | @tokens = Lexer.new.tokenize(code) 87 | puts @tokens.inspect if show_tokens 88 | do_parse 89 | end 90 | 91 | def next_token 92 | @tokens.shift 93 | end 94 | 95 | ---- footer 96 | if __FILE__ == $0 97 | puts Parser.new.parse(File.read(ARGV[0])).inspect 98 | end 99 | -------------------------------------------------------------------------------- /beta/interpreter.rb: -------------------------------------------------------------------------------- 1 | require_relative 'parser' 2 | 3 | class Interpreter 4 | def initialize 5 | @locals = {} 6 | end 7 | 8 | def evaluate(code) 9 | ast = Parser.new.parse(code) 10 | 11 | ast.nodes.each { |n| evaluate_node(n) } 12 | end 13 | 14 | private 15 | 16 | def evaluate_node(node) 17 | if node.is_a?(LiteralNode) 18 | return node.value 19 | end 20 | 21 | m = "evaluate" + node.class.name.gsub(/([A-Z])/) { "_#{$1.downcase}" }.gsub("_node", "") 22 | __send__(m, node) 23 | end 24 | 25 | def evaluate_add(node) 26 | evaluate_node(node.left) + evaluate_node(node.right) 27 | end 28 | 29 | def evaluate_sub(node) 30 | evaluate_node(node.left) - evaluate_node(node.right) 31 | end 32 | 33 | def evaluate_mul(node) 34 | evaluate_node(node.left) * evaluate_node(node.right) 35 | end 36 | 37 | def evaluate_div(node) 38 | evaluate_node(node.left) / evaluate_node(node.right) 39 | end 40 | 41 | def evaluate_eq(node) 42 | evaluate_node(node.left) == evaluate_node(node.right) 43 | end 44 | 45 | def evaluate_not_eq(node) 46 | evaluate_node(node.left) != evaluate_node(node.right) 47 | end 48 | 49 | def evaluate_call(node) 50 | if node.name == "print" 51 | puts evaluate_node(node.args[0]) 52 | else 53 | raise "unknown function: #{node.name}" 54 | end 55 | end 56 | 57 | def evaluate_get_local(node) 58 | @locals[node.name] or raise "undefined variable '#{node.name}'" 59 | end 60 | 61 | def evaluate_set_local(node) 62 | @locals[node.name] = evaluate_node(node.value) 63 | end 64 | 65 | def evaluate_if(node) 66 | if evaluate_node(node.condition) == false 67 | node.else_block.each { |n| evaluate_node(n) } 68 | else 69 | node.if_block.each { |n| evaluate_node(n) } 70 | end 71 | 72 | 73 | 74 | end 75 | end 76 | 77 | if __FILE__ == $0 78 | Interpreter.new.evaluate(File.read(ARGV[0])) 79 | end 80 | -------------------------------------------------------------------------------- /beta/lexer.rb: -------------------------------------------------------------------------------- 1 | class Lexer 2 | KEYWORDS = ["if", "else", "end", "true", "false"] 3 | 4 | def tokenize(code) 5 | code.chomp! 6 | 7 | tokens = [] 8 | 9 | i = 0 10 | while i < code.length 11 | chunk = code[i..-1] 12 | 13 | if identifier = chunk[/\A([a-z]\w*)/, 1] 14 | if KEYWORDS.include?(identifier) 15 | tokens << [identifier.upcase.to_sym, identifier] 16 | else 17 | tokens << [:IDENTIFIER, identifier] 18 | end 19 | 20 | i += identifier.length 21 | 22 | elsif number = chunk[/\A([0-9]+)/, 1] 23 | tokens << [:NUMBER, number.to_i] 24 | i += number.length 25 | 26 | elsif string = chunk[/\A"([^"]*)"/, 1] 27 | tokens << [:STRING, string] 28 | i += string.length + 2 29 | 30 | elsif operator = chunk[/\A(==|!=)/, 1] 31 | tokens << [operator, operator] 32 | i += operator.length 33 | 34 | elsif chunk[0] == " " 35 | i += 1 36 | 37 | elsif chunk[0] == "\n" 38 | tokens << [:NEWLINE, "\n"] 39 | i += 1 40 | 41 | else 42 | value = chunk[0,1] 43 | tokens << [value, value] 44 | i += 1 45 | end 46 | end 47 | 48 | tokens 49 | end 50 | end 51 | 52 | if __FILE__ == $0 53 | puts Lexer.new.tokenize(File.read(ARGV[0])).inspect 54 | end 55 | -------------------------------------------------------------------------------- /beta/parser.rb: -------------------------------------------------------------------------------- 1 | # 2 | # DO NOT MODIFY!!!! 3 | # This file is automatically generated by Racc 1.4.11 4 | # from Racc grammer file "". 5 | # 6 | 7 | require 'racc/parser.rb' 8 | 9 | require_relative 'lexer' 10 | require_relative 'ast' 11 | 12 | class Parser < Racc::Parser 13 | 14 | module_eval(<<'...end grammar.y/module_eval...', 'grammar.y', 85) 15 | def parse(code, show_tokens = false) 16 | @tokens = Lexer.new.tokenize(code) 17 | puts @tokens.inspect if show_tokens 18 | do_parse 19 | end 20 | 21 | def next_token 22 | @tokens.shift 23 | end 24 | 25 | ...end grammar.y/module_eval... 26 | ##### State transition tables begin ### 27 | 28 | racc_action_table = [ 29 | 13, 14, 19, 15, 16, 17, 18, 28, 29, 21, 30 | 19, 19, 19, 12, 35, 49, 48, nil, 20, 13, 31 | 14, 19, 15, 16, 17, 18, 20, 20, 20, 19, 32 | 32, nil, 12, nil, 33, 51, nil, 20, 13, 14, 33 | 19, 15, 16, 17, 18, 20, nil, 26, 27, 24, 34 | 25, 12, nil, 28, 29, nil, 20, 13, 14, 19, 35 | 15, 16, 17, 18, 13, 14, nil, 15, 16, 31, 36 | 12, 28, 29, nil, nil, 20, nil, 12, 13, 14, 37 | nil, 15, 16, 17, 18, 13, 14, nil, 15, 16, 38 | 31, 12, 13, 14, nil, 15, 16, 31, 12, 13, 39 | 14, nil, 15, 16, 31, 12, 13, 14, nil, 15, 40 | 16, 31, 12, 13, 14, nil, 15, 16, 31, 12, 41 | 13, 14, nil, 15, 16, 31, 12, 13, 14, nil, 42 | 15, 16, 31, 12, 13, 14, nil, 15, 16, 31, 43 | 12, 13, 14, nil, 15, 16, 31, 12, nil, nil, 44 | 26, 27, 24, 25, 12, 43, 28, 29, 26, 27, 45 | 24, 25, 26, 27, 28, 29, 26, 27, 28, 29, 46 | nil, nil, 28, 29, 26, 27, 24, 25, nil, nil, 47 | 28, 29, 26, 27, 24, 25, nil, 47, 28, 29, 48 | 26, 27, 24, 25, nil, nil, 28, 29, 26, 27, 49 | 24, 25, nil, nil, 28, 29 ] 50 | 51 | racc_action_check = [ 52 | 0, 0, 0, 0, 0, 0, 0, 40, 40, 1, 53 | 46, 2, 23, 0, 21, 46, 46, nil, 0, 49, 54 | 49, 49, 49, 49, 49, 49, 46, 2, 23, 50, 55 | 17, nil, 49, nil, 17, 50, nil, 49, 34, 34, 56 | 34, 34, 34, 34, 34, 50, nil, 34, 34, 34, 57 | 34, 34, nil, 34, 34, nil, 34, 4, 4, 4, 58 | 4, 4, 4, 4, 29, 29, nil, 29, 29, 29, 59 | 4, 39, 39, nil, nil, 4, nil, 29, 22, 22, 60 | nil, 22, 22, 22, 22, 18, 18, nil, 18, 18, 61 | 18, 22, 24, 24, nil, 24, 24, 24, 18, 25, 62 | 25, nil, 25, 25, 25, 24, 26, 26, nil, 26, 63 | 26, 26, 25, 27, 27, nil, 27, 27, 27, 26, 64 | 28, 28, nil, 28, 28, 28, 27, 32, 32, nil, 65 | 32, 32, 32, 28, 33, 33, nil, 33, 33, 33, 66 | 32, 12, 12, nil, 12, 12, 12, 33, nil, nil, 67 | 30, 30, 30, 30, 12, 30, 30, 30, 5, 5, 68 | 5, 5, 37, 37, 5, 5, 38, 38, 37, 37, 69 | nil, nil, 38, 38, 45, 45, 45, 45, nil, nil, 70 | 45, 45, 44, 44, 44, 44, nil, 44, 44, 44, 71 | 42, 42, 42, 42, nil, nil, 42, 42, 41, 41, 72 | 41, 41, nil, nil, 41, 41 ] 73 | 74 | racc_action_pointer = [ 75 | -2, 9, 7, nil, 55, 147, nil, nil, nil, nil, 76 | nil, nil, 139, nil, nil, nil, nil, 15, 83, nil, 77 | nil, 14, 76, 8, 90, 97, 104, 111, 118, 62, 78 | 139, nil, 125, 132, 36, nil, nil, 151, 155, 54, 79 | -10, 187, 179, nil, 171, 163, 6, nil, nil, 17, 80 | 25, nil ] 81 | 82 | racc_action_default = [ 83 | -1, -33, -2, -3, -7, -8, -9, -10, -11, -12, 84 | -13, -14, -33, -16, -17, -18, -19, -27, -33, -31, 85 | -32, -33, -5, -6, -33, -33, -33, -33, -33, -33, 86 | -33, -27, -33, -33, -33, 52, -4, -20, -21, -22, 87 | -23, -24, -25, -15, -33, -28, -33, -26, -29, -33, 88 | -33, -30 ] 89 | 90 | racc_goto_table = [ 91 | 2, 30, 22, 36, 23, 1, nil, 34, nil, nil, 92 | nil, nil, nil, 37, 38, 39, 40, 41, 42, nil, 93 | nil, 44, 45, 22, nil, nil, nil, nil, nil, nil, 94 | nil, nil, nil, nil, 46, nil, nil, nil, nil, nil, 95 | nil, nil, nil, nil, nil, nil, 22, nil, nil, 50, 96 | 22 ] 97 | 98 | racc_goto_check = [ 99 | 2, 5, 4, 3, 2, 1, nil, 5, nil, nil, 100 | nil, nil, nil, 5, 5, 5, 5, 5, 5, nil, 101 | nil, 5, 5, 4, nil, nil, nil, nil, nil, nil, 102 | nil, nil, nil, nil, 2, nil, nil, nil, nil, nil, 103 | nil, nil, nil, nil, nil, nil, 4, nil, nil, 2, 104 | 4 ] 105 | 106 | racc_goto_pointer = [ 107 | nil, 5, 0, -19, 0, -11, nil, nil, nil, nil, 108 | nil, nil ] 109 | 110 | racc_goto_default = [ 111 | nil, nil, nil, 3, 4, 5, 6, 7, 8, 9, 112 | 10, 11 ] 113 | 114 | racc_reduce_table = [ 115 | 0, 0, :racc_error, 116 | 0, 22, :_reduce_1, 117 | 1, 22, :_reduce_2, 118 | 1, 23, :_reduce_3, 119 | 3, 23, :_reduce_4, 120 | 2, 23, :_reduce_5, 121 | 2, 23, :_reduce_6, 122 | 1, 23, :_reduce_7, 123 | 1, 24, :_reduce_none, 124 | 1, 24, :_reduce_none, 125 | 1, 24, :_reduce_none, 126 | 1, 24, :_reduce_none, 127 | 1, 26, :_reduce_none, 128 | 1, 26, :_reduce_none, 129 | 1, 26, :_reduce_none, 130 | 3, 26, :_reduce_15, 131 | 1, 30, :_reduce_16, 132 | 1, 30, :_reduce_17, 133 | 1, 30, :_reduce_18, 134 | 1, 30, :_reduce_19, 135 | 3, 31, :_reduce_20, 136 | 3, 31, :_reduce_21, 137 | 3, 31, :_reduce_22, 138 | 3, 31, :_reduce_23, 139 | 3, 31, :_reduce_24, 140 | 3, 31, :_reduce_25, 141 | 4, 28, :_reduce_26, 142 | 1, 32, :_reduce_27, 143 | 3, 29, :_reduce_28, 144 | 4, 27, :_reduce_29, 145 | 6, 27, :_reduce_30, 146 | 1, 25, :_reduce_none, 147 | 1, 25, :_reduce_none ] 148 | 149 | racc_reduce_n = 33 150 | 151 | racc_shift_n = 52 152 | 153 | racc_token_table = { 154 | false => 0, 155 | :error => 1, 156 | :NUMBER => 2, 157 | :STRING => 3, 158 | :NEWLINE => 4, 159 | :TRUE => 5, 160 | :FALSE => 6, 161 | :IDENTIFIER => 7, 162 | :IF => 8, 163 | :ELSE => 9, 164 | :END => 10, 165 | "*" => 11, 166 | "/" => 12, 167 | "+" => 13, 168 | "-" => 14, 169 | "(" => 15, 170 | ")" => 16, 171 | "==" => 17, 172 | "!=" => 18, 173 | "=" => 19, 174 | ";" => 20 } 175 | 176 | racc_nt_base = 21 177 | 178 | racc_use_result_var = true 179 | 180 | Racc_arg = [ 181 | racc_action_table, 182 | racc_action_check, 183 | racc_action_default, 184 | racc_action_pointer, 185 | racc_goto_table, 186 | racc_goto_check, 187 | racc_goto_default, 188 | racc_goto_pointer, 189 | racc_nt_base, 190 | racc_reduce_table, 191 | racc_token_table, 192 | racc_shift_n, 193 | racc_reduce_n, 194 | racc_use_result_var ] 195 | 196 | Racc_token_to_s_table = [ 197 | "$end", 198 | "error", 199 | "NUMBER", 200 | "STRING", 201 | "NEWLINE", 202 | "TRUE", 203 | "FALSE", 204 | "IDENTIFIER", 205 | "IF", 206 | "ELSE", 207 | "END", 208 | "\"*\"", 209 | "\"/\"", 210 | "\"+\"", 211 | "\"-\"", 212 | "\"(\"", 213 | "\")\"", 214 | "\"==\"", 215 | "\"!=\"", 216 | "\"=\"", 217 | "\";\"", 218 | "$start", 219 | "Program", 220 | "Statements", 221 | "Statement", 222 | "Terminator", 223 | "Expression", 224 | "If", 225 | "Call", 226 | "SetLocal", 227 | "Literal", 228 | "Operator", 229 | "GetLocal" ] 230 | 231 | Racc_debug_parser = false 232 | 233 | ##### State transition tables end ##### 234 | 235 | # reduce 0 omitted 236 | 237 | module_eval(<<'.,.,', 'grammar.y', 16) 238 | def _reduce_1(val, _values, result) 239 | result = AST.new([]) 240 | result 241 | end 242 | .,., 243 | 244 | module_eval(<<'.,.,', 'grammar.y', 17) 245 | def _reduce_2(val, _values, result) 246 | result = AST.new(val[0]) 247 | result 248 | end 249 | .,., 250 | 251 | module_eval(<<'.,.,', 'grammar.y', 21) 252 | def _reduce_3(val, _values, result) 253 | result = val 254 | result 255 | end 256 | .,., 257 | 258 | module_eval(<<'.,.,', 'grammar.y', 22) 259 | def _reduce_4(val, _values, result) 260 | result = val[0] << val[2] 261 | result 262 | end 263 | .,., 264 | 265 | module_eval(<<'.,.,', 'grammar.y', 23) 266 | def _reduce_5(val, _values, result) 267 | result = val[0] 268 | result 269 | end 270 | .,., 271 | 272 | module_eval(<<'.,.,', 'grammar.y', 24) 273 | def _reduce_6(val, _values, result) 274 | result = val[1] 275 | result 276 | end 277 | .,., 278 | 279 | module_eval(<<'.,.,', 'grammar.y', 25) 280 | def _reduce_7(val, _values, result) 281 | result = [] 282 | result 283 | end 284 | .,., 285 | 286 | # reduce 8 omitted 287 | 288 | # reduce 9 omitted 289 | 290 | # reduce 10 omitted 291 | 292 | # reduce 11 omitted 293 | 294 | # reduce 12 omitted 295 | 296 | # reduce 13 omitted 297 | 298 | # reduce 14 omitted 299 | 300 | module_eval(<<'.,.,', 'grammar.y', 38) 301 | def _reduce_15(val, _values, result) 302 | result = val[1] 303 | result 304 | end 305 | .,., 306 | 307 | module_eval(<<'.,.,', 'grammar.y', 42) 308 | def _reduce_16(val, _values, result) 309 | result = NumberNode.new(val[0]) 310 | result 311 | end 312 | .,., 313 | 314 | module_eval(<<'.,.,', 'grammar.y', 43) 315 | def _reduce_17(val, _values, result) 316 | result = StringNode.new(val[0]) 317 | result 318 | end 319 | .,., 320 | 321 | module_eval(<<'.,.,', 'grammar.y', 44) 322 | def _reduce_18(val, _values, result) 323 | result = TrueNode.new 324 | result 325 | end 326 | .,., 327 | 328 | module_eval(<<'.,.,', 'grammar.y', 45) 329 | def _reduce_19(val, _values, result) 330 | result = FalseNode.new 331 | result 332 | end 333 | .,., 334 | 335 | module_eval(<<'.,.,', 'grammar.y', 49) 336 | def _reduce_20(val, _values, result) 337 | result = AddNode.new(val[0], val[2]) 338 | result 339 | end 340 | .,., 341 | 342 | module_eval(<<'.,.,', 'grammar.y', 50) 343 | def _reduce_21(val, _values, result) 344 | result = SubNode.new(val[0], val[2]) 345 | result 346 | end 347 | .,., 348 | 349 | module_eval(<<'.,.,', 'grammar.y', 51) 350 | def _reduce_22(val, _values, result) 351 | result = MulNode.new(val[0], val[2]) 352 | result 353 | end 354 | .,., 355 | 356 | module_eval(<<'.,.,', 'grammar.y', 52) 357 | def _reduce_23(val, _values, result) 358 | result = DivNode.new(val[0], val[2]) 359 | result 360 | end 361 | .,., 362 | 363 | module_eval(<<'.,.,', 'grammar.y', 53) 364 | def _reduce_24(val, _values, result) 365 | result = EqNode.new(val[0], val[2]) 366 | result 367 | end 368 | .,., 369 | 370 | module_eval(<<'.,.,', 'grammar.y', 54) 371 | def _reduce_25(val, _values, result) 372 | result = NotEqNode.new(val[0], val[2]) 373 | result 374 | end 375 | .,., 376 | 377 | module_eval(<<'.,.,', 'grammar.y', 58) 378 | def _reduce_26(val, _values, result) 379 | result = CallNode.new(val[0], [val[2]]) 380 | result 381 | end 382 | .,., 383 | 384 | module_eval(<<'.,.,', 'grammar.y', 62) 385 | def _reduce_27(val, _values, result) 386 | result = GetLocalNode.new(val[0]) 387 | result 388 | end 389 | .,., 390 | 391 | module_eval(<<'.,.,', 'grammar.y', 66) 392 | def _reduce_28(val, _values, result) 393 | result = SetLocalNode.new(val[0], val[2]) 394 | result 395 | end 396 | .,., 397 | 398 | module_eval(<<'.,.,', 'grammar.y', 70) 399 | def _reduce_29(val, _values, result) 400 | result = IfNode.new(val[1], val[2], []) 401 | result 402 | end 403 | .,., 404 | 405 | module_eval(<<'.,.,', 'grammar.y', 71) 406 | def _reduce_30(val, _values, result) 407 | result = IfNode.new(val[1], val[2], val[4]) 408 | result 409 | end 410 | .,., 411 | 412 | # reduce 31 omitted 413 | 414 | # reduce 32 omitted 415 | 416 | def _reduce_none(val, _values, result) 417 | val[0] 418 | end 419 | 420 | end # class Parser 421 | 422 | if __FILE__ == $0 423 | puts Parser.new.parse(File.read(ARGV[0])).inspect 424 | end 425 | -------------------------------------------------------------------------------- /beta/test/conditionals.txt: -------------------------------------------------------------------------------- 1 | a = 4 2 | 3 | if a == 4 4 | print("a is 4") 5 | print("a is true") 6 | else 7 | print("a is false") 8 | end 9 | -------------------------------------------------------------------------------- /beta/test/sample.txt: -------------------------------------------------------------------------------- 1 | print(1+1+1) 2 | print(4-1) 3 | print("hello") 4 | print(4*4/2) 5 | print(true) 6 | print(false) 7 | print(1); print(2); print(3); 8 | -------------------------------------------------------------------------------- /beta/test/variables.txt: -------------------------------------------------------------------------------- 1 | a = 5 + 1 2 | b = "hey" 3 | print(a) 4 | 5 | a = 1 6 | print(a) 7 | 8 | print(b) 9 | -------------------------------------------------------------------------------- /beta/vm.rb: -------------------------------------------------------------------------------- 1 | require_relative 'compiler' 2 | 3 | class VM 4 | def execute(bytecode) 5 | ip = 0 6 | stack = [] 7 | locals = {} 8 | 9 | loop do 10 | instruction = bytecode[ip] 11 | ip += 1 12 | 13 | case instruction.opcode 14 | when :push 15 | stack.push(instruction.operands[0]) 16 | 17 | when :add 18 | right = stack.pop 19 | left = stack.pop 20 | stack.push(left + right) 21 | 22 | when :sub 23 | right = stack.pop 24 | left = stack.pop 25 | stack.push(left - right) 26 | 27 | when :mul 28 | right = stack.pop 29 | left = stack.pop 30 | stack.push(left * right) 31 | 32 | when :div 33 | right = stack.pop 34 | left = stack.pop 35 | stack.push(left / right) 36 | 37 | when :eq 38 | right = stack.pop 39 | left = stack.pop 40 | stack.push(left == right) 41 | 42 | when :not_eq 43 | right = stack.pop 44 | left = stack.pop 45 | stack.push(left != right) 46 | 47 | when :call 48 | arg = stack.pop 49 | name = instruction.operands[0] 50 | 51 | if name == "print" 52 | puts arg 53 | else 54 | raise "unknown function: #{name}" 55 | end 56 | 57 | when :get_local 58 | name = instruction.operands[0] 59 | val = locals[name] or raise "undefined variable '#{name}'" 60 | 61 | stack.push(val) 62 | 63 | when :set_local 64 | name = instruction.operands[0] 65 | 66 | locals[name] = stack.pop 67 | 68 | when :if 69 | true_jump = instruction.operands[0] 70 | false_jump = instruction.operands[1] 71 | 72 | condition = stack.pop 73 | if condition == false 74 | ip = false_jump 75 | else 76 | ip = true_jump 77 | end 78 | 79 | when :jump 80 | ip = instruction.operands[0] 81 | 82 | when :return 83 | break 84 | end 85 | end 86 | end 87 | end 88 | 89 | VM.new.execute(Compiler.new.compile(File.read(ARGV[0]))) 90 | -------------------------------------------------------------------------------- /gamma/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'racc' 4 | -------------------------------------------------------------------------------- /gamma/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | racc (1.4.11) 5 | 6 | PLATFORMS 7 | ruby 8 | 9 | DEPENDENCIES 10 | racc 11 | -------------------------------------------------------------------------------- /gamma/README.md: -------------------------------------------------------------------------------- 1 | #gamma 2 | 3 | A small, Ruby-like language implemented in Ruby. Unlike beta, we support more advanced control flow (loops), as well as functions. Both of these constructs result in significantly more complexity, particularly in the virtual machine implementation. 4 | 5 | Gamma supports: 6 | 7 | * Dynamic typing 8 | * Mutable variables (`name = "Matt"`) 9 | * Flow control via `if` and `else` 10 | * Functions with 0-n arguments 11 | * While loops, including `break` 12 | * Three data types: integers, strings, and booleans 13 | * Operators: `+`, `-`, `*`, `/` 14 | * Comparison operators: `<`, `>`, `<=`, `>=`, `==`, `!=` 15 | * Boolean operators: `&&`, `||`, `!` (parenthesis may be used to elevate precedence) 16 | * Built-in `print` function for outputting results 17 | 18 | The syntax is kept close to Ruby's. One noteworthy difference is that function definitions must include parenthesis. 19 | 20 | ## Examples 21 | 22 | Fibonacci sequence: 23 | ````ruby 24 | def fib(n) 25 | if n == 1 26 | return 1 27 | end 28 | 29 | if n == 2 30 | return 1 31 | end 32 | 33 | fib(n - 1) + fib(n - 2) 34 | end 35 | 36 | print(fib(20)) 37 | ```` 38 | 39 | A simple countdown program: 40 | ````ruby 41 | a = 10 42 | 43 | while a >= 0 44 | print(a) 45 | 46 | if a == 4 47 | break 48 | end 49 | 50 | a = a - 1 51 | end 52 | ```` 53 | 54 | ## Installation 55 | ```` 56 | cd gamma 57 | bundle 58 | ```` 59 | 60 | ## Running 61 | Run a program using the interpreter: 62 | 63 | $ bundle exec ruby interpreter.rb test/fib.txt 64 | 65 | Run a program using the VM: 66 | 67 | $ bundle exec ruby vm.rb test/fib.txt 68 | -------------------------------------------------------------------------------- /gamma/ast.rb: -------------------------------------------------------------------------------- 1 | class AST < Struct.new(:nodes) 2 | def <<(node) 3 | nodes << node 4 | 5 | self 6 | end 7 | end 8 | 9 | class LiteralNode < Struct.new(:value) 10 | end 11 | 12 | class NumberNode < LiteralNode 13 | end 14 | 15 | class StringNode < LiteralNode 16 | end 17 | 18 | class TrueNode < LiteralNode 19 | def initialize 20 | super(true) 21 | end 22 | end 23 | 24 | class FalseNode < LiteralNode 25 | def initialize 26 | super(false) 27 | end 28 | end 29 | 30 | class CallNode < Struct.new(:name, :args) 31 | end 32 | 33 | class OpNode < Struct.new(:left, :right) 34 | end 35 | 36 | class AddNode < OpNode 37 | end 38 | 39 | class SubNode < OpNode 40 | end 41 | 42 | class MulNode < OpNode 43 | end 44 | 45 | class DivNode < OpNode 46 | end 47 | 48 | class EqNode < OpNode 49 | end 50 | 51 | class NotEqNode < OpNode 52 | end 53 | 54 | class LessThanNode < OpNode 55 | end 56 | 57 | class LessThanEqNode < OpNode 58 | end 59 | 60 | class GreaterThanNode < OpNode 61 | end 62 | 63 | class GreaterThanEqNode < OpNode 64 | end 65 | 66 | class AndNode < OpNode 67 | end 68 | 69 | class OrNode < OpNode 70 | end 71 | 72 | class NotNode < Struct.new(:op) 73 | end 74 | 75 | class GetLocalNode < Struct.new(:name) 76 | end 77 | 78 | class SetLocalNode < Struct.new(:name, :value) 79 | end 80 | 81 | class IfNode < Struct.new(:condition, :if_block, :else_block) 82 | end 83 | 84 | class WhileNode < Struct.new(:condition, :block) 85 | end 86 | 87 | class DefNode < Struct.new(:name, :args, :body) 88 | end 89 | 90 | class ReturnNode < Struct.new(:value) 91 | end 92 | 93 | class BreakNode 94 | end 95 | -------------------------------------------------------------------------------- /gamma/compiler.rb: -------------------------------------------------------------------------------- 1 | require_relative 'parser' 2 | 3 | class Compiler 4 | def initialize 5 | @bytecode = [] 6 | @functions = [] 7 | 8 | @in_loop = false 9 | @breaks = [] 10 | 11 | @in_function = false 12 | @returns = [] 13 | end 14 | 15 | def compile(code) 16 | ast = Parser.new.parse(code) 17 | ast.nodes.each { |n| visit(n) } 18 | emit(:end) 19 | 20 | program = @bytecode 21 | 22 | @functions.each do |node| 23 | @bytecode = [] 24 | 25 | emit(:fn_begin, node.name) 26 | emit(:frame_setup, node.args) 27 | 28 | @in_function = true 29 | node.body.each { |n| visit(n) } 30 | @in_function = false 31 | 32 | teardown_ip = @bytecode.length 33 | emit(:frame_teardown) 34 | emit(:fn_end) 35 | 36 | @returns.each do |ret| 37 | ret.operands[0] = teardown_ip - ret.ip - 1 38 | end 39 | @returns.clear 40 | 41 | program = @bytecode + program 42 | end 43 | 44 | program 45 | end 46 | 47 | private 48 | 49 | def emit(opcode, *operands) 50 | inst = Instruction.new(@bytecode.length, opcode, operands) 51 | @bytecode << inst 52 | 53 | inst 54 | end 55 | 56 | def visit(node) 57 | if node.is_a?(LiteralNode) 58 | return visit_literal(node) 59 | end 60 | 61 | method = "visit" + node.class.name.gsub(/([A-Z])/) { "_#{$1.downcase}" }.gsub("_node", "") 62 | __send__(method, node) 63 | end 64 | 65 | def visit_literal(node) 66 | emit(:push, node.value) 67 | end 68 | 69 | def visit_get_local(node) 70 | emit(:get_local, node.name) 71 | end 72 | 73 | def visit_set_local(node) 74 | visit(node.value) 75 | 76 | emit(:set_local, node.name) 77 | end 78 | 79 | def visit_op(node, &block) 80 | visit(node.left) 81 | visit(node.right) 82 | 83 | yield 84 | end 85 | 86 | def visit_add(node) 87 | visit_op(node) { emit(:add) } 88 | end 89 | 90 | def visit_sub(node) 91 | visit_op(node) { emit(:sub) } 92 | end 93 | 94 | def visit_mul(node) 95 | visit_op(node) { emit(:mul) } 96 | end 97 | 98 | def visit_div(node) 99 | visit_op(node) { emit(:div) } 100 | end 101 | 102 | def visit_eq(node) 103 | visit_op(node) { emit(:eq) } 104 | end 105 | 106 | def visit_not_eq(node) 107 | visit_op(node) { emit(:not_eq) } 108 | end 109 | 110 | def visit_less_than(node) 111 | visit_op(node) { emit(:lt) } 112 | end 113 | 114 | def visit_less_than_eq(node) 115 | visit_op(node) { emit(:lte) } 116 | end 117 | 118 | def visit_greater_than(node) 119 | visit_op(node) { emit(:gt) } 120 | end 121 | 122 | def visit_greater_than_eq(node) 123 | visit_op(node) { emit(:gte) } 124 | end 125 | 126 | def visit_and(node) 127 | visit_op(node) { emit(:and) } 128 | end 129 | 130 | def visit_or(node) 131 | visit_op(node) { emit(:or) } 132 | end 133 | 134 | def visit_not(node) 135 | visit(node.op) 136 | 137 | emit(:not) 138 | end 139 | 140 | def visit_call(node) 141 | node.args.each { |a| visit(a) } 142 | emit(:push, node.args.size) 143 | emit(:call, node.name) 144 | end 145 | 146 | def visit_def(node) 147 | @functions << node 148 | end 149 | 150 | def visit_if(node) 151 | visit(node.condition) 152 | 153 | branch = emit(:creljump, 0, 0) 154 | branch_ip = @bytecode.length 155 | 156 | node.if_block.each { |n| visit(n) } 157 | 158 | jump = emit(:reljump, 0) 159 | jump_ip = @bytecode.length 160 | 161 | branch.operands[1] = offset(branch_ip) 162 | node.else_block.each { |n| visit(n) } 163 | 164 | jump.operands[0] = offset(jump_ip) 165 | end 166 | 167 | def visit_while(node) 168 | condition_ip = @bytecode.length 169 | visit(node.condition) 170 | 171 | branch = emit(:creljump, 0, 0) 172 | loop_end = @bytecode.length 173 | 174 | @in_loop = true 175 | node.block.each { |n| visit(n) } 176 | @in_loop = false 177 | 178 | emit(:reljump, condition_ip - @bytecode.length - 1) 179 | 180 | branch.operands[1] = offset(loop_end) 181 | 182 | @breaks.each do |jump| 183 | jump.operands[0] = jump.ip - loop_end - 1 184 | end 185 | 186 | @breaks.clear 187 | end 188 | 189 | def visit_break(node) 190 | raise "can't break outside of a loop" unless @in_loop 191 | 192 | @breaks << emit(:reljump, 0) 193 | end 194 | 195 | def visit_return(node) 196 | raise "can't return outside of function" unless @in_function 197 | 198 | visit(node.value) 199 | @returns << emit(:reljump, 0) 200 | end 201 | 202 | def offset(to) 203 | @bytecode.length - to 204 | end 205 | end 206 | 207 | class Instruction < Struct.new(:ip, :opcode, :operands) 208 | end 209 | 210 | if $0 == __FILE__ 211 | require 'pp' 212 | pp Compiler.new.compile(File.read(ARGV[0])) 213 | end 214 | -------------------------------------------------------------------------------- /gamma/grammar.y: -------------------------------------------------------------------------------- 1 | class Parser 2 | 3 | token NUMBER 4 | token STRING 5 | token NEWLINE 6 | token TRUE FALSE 7 | token IDENTIFIER 8 | token IF ELSE END 9 | token DEF 10 | token RETURN 11 | token WHILE 12 | token BREAK 13 | 14 | prechigh 15 | right '!' 16 | left '*' '/' 17 | left '+' '-' 18 | left '>' '>=' '<' '<=' 19 | left '==' '!=' 20 | left '&&' 21 | left '||' 22 | right '=' 23 | left ',' 24 | preclow 25 | 26 | rule 27 | Program: 28 | { result = AST.new([]) } 29 | | Statements { result = AST.new(val[0]) } 30 | ; 31 | 32 | Statements: 33 | Statement { result = val } 34 | | Statements Terminator Statement { result = val[0] << val[2] } 35 | | Statements Terminator { result = val[0] } 36 | | Terminator Statements { result = val[1] } 37 | | Terminator { result = [] } 38 | ; 39 | 40 | Statement: 41 | Expression 42 | | If 43 | | SetLocal 44 | | Function 45 | | While 46 | | Break 47 | | Return 48 | ; 49 | 50 | Expression: 51 | Literal 52 | | Operator 53 | | Call 54 | | GetLocal 55 | | '(' Expression ')' { result = val[1] } 56 | ; 57 | 58 | Literal: 59 | NUMBER { result = NumberNode.new(val[0]) } 60 | | STRING { result = StringNode.new(val[0]) } 61 | | TRUE { result = TrueNode.new } 62 | | FALSE { result = FalseNode.new } 63 | ; 64 | 65 | Operator: 66 | Expression '+' Expression { result = AddNode.new(val[0], val[2]) } 67 | | Expression '-' Expression { result = SubNode.new(val[0], val[2]) } 68 | | Expression '*' Expression { result = MulNode.new(val[0], val[2]) } 69 | | Expression '/' Expression { result = DivNode.new(val[0], val[2]) } 70 | | Expression '<' Expression { result = LessThanNode.new(val[0], val[2]) } 71 | | Expression '<=' Expression { result = LessThanEqNode.new(val[0], val[2]) } 72 | | Expression '>' Expression { result = GreaterThanNode.new(val[0], val[2]) } 73 | | Expression '>=' Expression { result = GreaterThanEqNode.new(val[0], val[2]) } 74 | | Expression '==' Expression { result = EqNode.new(val[0], val[2]) } 75 | | Expression '!=' Expression { result = NotEqNode.new(val[0], val[2]) } 76 | | Expression '&&' Expression { result = AndNode.new(val[0], val[2]) } 77 | | Expression '||' Expression { result = OrNode.new(val[0], val[2]) } 78 | | '!' Expression { result = NotNode.new(val[1]) } 79 | ; 80 | 81 | Call: 82 | IDENTIFIER Arguments { result = CallNode.new(val[0], val[1]) } 83 | ; 84 | 85 | Arguments: 86 | '(' ')' { result = [] } 87 | | '(' Args ')' { result = val[1] } 88 | ; 89 | 90 | Args: 91 | Expression { result = [val[0]] } 92 | | Args ',' Expression { result = val[0] << val[2] } 93 | ; 94 | 95 | 96 | GetLocal: 97 | IDENTIFIER { result = GetLocalNode.new(val[0]) } 98 | ; 99 | 100 | SetLocal: 101 | IDENTIFIER '=' Expression { result = SetLocalNode.new(val[0], val[2]) } 102 | ; 103 | 104 | If: 105 | IF Expression Statements END { result = IfNode.new(val[1], val[2], []) } 106 | | IF Expression Statements ELSE Statements END { result = IfNode.new(val[1], val[2], val[4]) } 107 | ; 108 | 109 | Function: 110 | DEF IDENTIFIER '(' ')' Statements END { result = DefNode.new(val[1], [], val[4]) } 111 | | DEF IDENTIFIER '(' Params ')' Statements END { result = DefNode.new(val[1], val[3], val[5]) } 112 | ; 113 | 114 | Params: 115 | IDENTIFIER { result = [val[0]] } 116 | | Params ',' IDENTIFIER { result = val[0] << val[2] } 117 | ; 118 | 119 | Return: 120 | RETURN { result = ReturnNode.new(nil) } 121 | | RETURN Expression { result = ReturnNode.new(val[1]) } 122 | 123 | While: 124 | WHILE Expression Statements END { result = WhileNode.new(val[1], val[2]) } 125 | ; 126 | 127 | Break: 128 | BREAK { result = BreakNode.new } 129 | ; 130 | 131 | Terminator: 132 | NEWLINE 133 | | ";" 134 | ; 135 | 136 | ---- header 137 | require 'pp' 138 | require_relative 'lexer' 139 | require_relative 'ast' 140 | 141 | ---- inner 142 | def parse(code, show_tokens = false) 143 | @tokens = Lexer.new.tokenize(code) 144 | puts @tokens.inspect if show_tokens 145 | do_parse 146 | end 147 | 148 | def next_token 149 | @tokens.shift 150 | end 151 | 152 | ---- footer 153 | if __FILE__ == $0 154 | pp Parser.new.parse(File.read(ARGV[0])) 155 | end 156 | -------------------------------------------------------------------------------- /gamma/interpreter.rb: -------------------------------------------------------------------------------- 1 | require_relative 'parser' 2 | 3 | class Interpreter 4 | def initialize 5 | @globals = {} 6 | @frames = [] 7 | @functions = {} 8 | @builtins = {} 9 | 10 | @returning = false 11 | @breaking = false 12 | 13 | @builtins["print"] = ->(args) { puts args[0] } 14 | end 15 | 16 | def evaluate(code) 17 | ast = Parser.new.parse(code) 18 | 19 | ast.nodes.each { |n| evaluate_node(n) } 20 | end 21 | 22 | private 23 | 24 | def evaluate_node(node) 25 | raise "non-node type passed to evaluate_node: #{node.inspect}" if node.class.name !~ /Node/ 26 | 27 | if node.is_a?(LiteralNode) 28 | return node.value 29 | end 30 | 31 | m = "evaluate" + node.class.name.gsub(/([A-Z])/) { "_#{$1.downcase}" }.gsub("_node", "") 32 | __send__(m, node) 33 | end 34 | 35 | def evaluate_add(node) 36 | evaluate_binary_operand(node) { |l, r| l + r } 37 | end 38 | 39 | def evaluate_sub(node) 40 | evaluate_binary_operand(node) { |l, r| l - r } 41 | end 42 | 43 | def evaluate_mul(node) 44 | evaluate_binary_operand(node) { |l, r| l * r } 45 | end 46 | 47 | def evaluate_div(node) 48 | evaluate_binary_operand(node) { |l, r| l / r } 49 | end 50 | 51 | def evaluate_eq(node) 52 | evaluate_binary_operand(node) { |l, r| l == r } 53 | end 54 | 55 | def evaluate_not_eq(node) 56 | evaluate_binary_operand(node) { |l, r| l != r } 57 | end 58 | 59 | def evaluate_and(node) 60 | evaluate_binary_operand(node) { |l, r| l && r } 61 | end 62 | 63 | def evaluate_or(node) 64 | evaluate_binary_operand(node) { |l, r| l || r } 65 | end 66 | 67 | def evaluate_not(node) 68 | ! evaluate_node(node.op) 69 | end 70 | 71 | def evaluate_greater_than(node) 72 | evaluate_binary_operand(node) { |l, r| l > r } 73 | end 74 | 75 | def evaluate_greater_than_eq(node) 76 | evaluate_binary_operand(node) { |l, r| l >= r } 77 | end 78 | 79 | def evaluate_less_than(node) 80 | evaluate_binary_operand(node) { |l, r| l < r } 81 | end 82 | 83 | def evaluate_less_than_eq(node) 84 | evaluate_binary_operand(node) { |l, r| l <= r } 85 | end 86 | 87 | def evaluate_binary_operand(node, &block) 88 | yield evaluate_node(node.left), evaluate_node(node.right) 89 | end 90 | 91 | def evaluate_call(node) 92 | if fn = @builtins[node.name] 93 | args = node.args.map { |a| evaluate_node(a) } 94 | fn.call(args) 95 | elsif fn = @functions[node.name] 96 | raise "bad arity, expected #{fn.args.size} args, got #{node.args.size}" if node.args.size != fn.args.size 97 | 98 | parameters = {} 99 | fn.args.zip(node.args) { |name, arg| parameters[name] = evaluate_node(arg) } 100 | 101 | @frames.push(parameters) 102 | result = evaluate_nodes(fn.body) 103 | @returning = false 104 | @frames.pop 105 | 106 | result 107 | else 108 | raise "unknown function: #{node.name}" 109 | end 110 | end 111 | 112 | def evaluate_get_local(node) 113 | locals[node.name] || @globals[node.name] or raise "undefined variable '#{node.name}'" 114 | end 115 | 116 | def evaluate_set_local(node) 117 | locals[node.name] = evaluate_node(node.value) 118 | end 119 | 120 | def evaluate_if(node) 121 | if evaluate_node(node.condition) == false 122 | evaluate_nodes(node.else_block) 123 | else 124 | evaluate_nodes(node.if_block) 125 | end 126 | end 127 | 128 | def evaluate_nodes(nodes) 129 | result = nil 130 | 131 | nodes.each do |n| 132 | result = evaluate_node(n) 133 | raise "leaking AST nodes" if result.class.name =~ /Node/ 134 | result = nil if result.class.name =~ /Node/ 135 | 136 | break if @returning 137 | end 138 | 139 | result 140 | end 141 | 142 | def evaluate_def(node) 143 | @functions[node.name] = Function.new(node.name, node.args, node.body) 144 | end 145 | 146 | def evaluate_return(node) 147 | raise "cannot return outside of a function" if @frames.empty? 148 | 149 | @returning = true 150 | evaluate_node(node.value) 151 | end 152 | 153 | def evaluate_while(node) 154 | while evaluate_node(node.condition) != false && ! @returning && ! @breaking 155 | evaluate_nodes(node.block) 156 | end 157 | end 158 | 159 | def evaluate_break(node) 160 | @breaking = true 161 | 162 | nil 163 | end 164 | 165 | def locals 166 | return @globals if @frames.empty? 167 | 168 | @frames.last 169 | end 170 | end 171 | 172 | class Function < Struct.new(:name, :args, :body) 173 | end 174 | 175 | if __FILE__ == $0 176 | Interpreter.new.evaluate(File.read(ARGV[0])) 177 | end 178 | -------------------------------------------------------------------------------- /gamma/lexer.rb: -------------------------------------------------------------------------------- 1 | class Lexer 2 | KEYWORDS = ["def", "if", "else", "end", "true", "false", "return", "while", "break"] 3 | 4 | def tokenize(code) 5 | code.chomp! 6 | 7 | tokens = [] 8 | 9 | i = 0 10 | while i < code.length 11 | chunk = code[i..-1] 12 | 13 | if identifier = chunk[/\A([a-z]\w*)/, 1] 14 | if KEYWORDS.include?(identifier) 15 | tokens << [identifier.upcase.to_sym, identifier] 16 | else 17 | tokens << [:IDENTIFIER, identifier] 18 | end 19 | 20 | i += identifier.length 21 | 22 | elsif number = chunk[/\A([0-9]+)/, 1] 23 | tokens << [:NUMBER, number.to_i] 24 | i += number.length 25 | 26 | elsif string = chunk[/\A"([^"]*)"/, 1] 27 | tokens << [:STRING, string] 28 | i += string.length + 2 29 | 30 | elsif operator = chunk[/\A(==|!=|>=|<=|\|\||&&)/, 1] 31 | tokens << [operator, operator] 32 | i += operator.length 33 | 34 | elsif chunk[0] == " " 35 | i += 1 36 | 37 | elsif chunk[0] == "\n" 38 | tokens << [:NEWLINE, "\n"] 39 | i += 1 40 | 41 | else 42 | value = chunk[0,1] 43 | tokens << [value, value] 44 | i += 1 45 | end 46 | end 47 | 48 | tokens 49 | end 50 | end 51 | 52 | if __FILE__ == $0 53 | require 'pp' 54 | pp Lexer.new.tokenize(File.read(ARGV[0])) 55 | end 56 | -------------------------------------------------------------------------------- /gamma/parser.rb: -------------------------------------------------------------------------------- 1 | # 2 | # DO NOT MODIFY!!!! 3 | # This file is automatically generated by Racc 1.4.11 4 | # from Racc grammer file "". 5 | # 6 | 7 | require 'racc/parser.rb' 8 | 9 | require 'pp' 10 | require_relative 'lexer' 11 | require_relative 'ast' 12 | 13 | class Parser < Racc::Parser 14 | 15 | module_eval(<<'...end grammar.y/module_eval...', 'grammar.y', 142) 16 | def parse(code, show_tokens = false) 17 | @tokens = Lexer.new.tokenize(code) 18 | puts @tokens.inspect if show_tokens 19 | do_parse 20 | end 21 | 22 | def next_token 23 | @tokens.shift 24 | end 25 | 26 | ...end grammar.y/module_eval... 27 | ##### State transition tables begin ### 28 | 29 | racc_action_table = [ 30 | 17, 18, 28, 19, 20, 22, 23, 28, 28, 24, 31 | 25, 26, 27, 21, 17, 18, 28, 19, 20, 22, 32 | 23, 35, 36, 24, 25, 26, 27, 21, 16, 83, 33 | 29, 35, 36, 33, 34, 29, 29, 35, 36, 33, 34 | 34, 78, 16, 77, 29, 17, 18, 28, 19, 20, 35 | 22, 23, 75, 81, 24, 25, 26, 27, 21, 17, 36 | 18, 28, 19, 20, 22, 23, 35, 36, 24, 25, 37 | 26, 27, 21, 16, 49, 29, 35, 36, 33, 34, 38 | 39, 40, 37, 38, 41, 42, 43, 16, 55, 29, 39 | 17, 18, 28, 19, 20, 22, 23, 30, 93, 24, 40 | 25, 26, 27, 21, 35, 36, 33, 34, 39, 40, 41 | 37, 38, 41, 42, 43, 44, 52, 50, 16, 49, 42 | 29, 17, 18, 28, 19, 20, 22, 23, nil, nil, 43 | 24, 25, 26, 27, 21, 35, 36, 33, 34, 39, 44 | 40, 37, 38, 41, 42, 43, 44, nil, 89, 16, 45 | 88, 29, 17, 18, 28, 19, 20, 22, 23, nil, 46 | nil, 24, 25, 26, 27, 21, 17, 18, 28, 19, 47 | 20, 46, 17, 18, 94, 19, 20, 46, nil, 21, 48 | 16, nil, 29, nil, nil, 21, 17, 18, nil, 19, 49 | 20, 46, 28, nil, 16, 71, 29, nil, 91, 21, 50 | 16, nil, 17, 18, nil, 19, 20, 46, 17, 18, 51 | nil, 19, 20, 46, 16, 21, nil, nil, 28, nil, 52 | 29, 21, 17, 18, 90, 19, 20, 22, 23, nil, 53 | 16, 24, 25, 26, 27, 21, 16, nil, 17, 18, 54 | nil, 19, 20, 46, 17, 18, 29, 19, 20, 46, 55 | 16, 21, nil, nil, nil, 17, 18, 21, 19, 20, 56 | 46, nil, nil, nil, 28, nil, 16, nil, 21, nil, 57 | 84, nil, 16, 17, 18, nil, 19, 20, 46, 35, 58 | 36, 33, 34, 16, 17, 18, 21, 19, 20, 46, 59 | 17, 18, 29, 19, 20, 46, nil, 21, nil, nil, 60 | nil, 16, nil, 21, 17, 18, nil, 19, 20, 46, 61 | 17, 18, 16, 19, 20, 46, nil, 21, 16, nil, 62 | nil, 17, 18, 21, 19, 20, 46, nil, nil, nil, 63 | 28, nil, 16, nil, 21, 80, 79, nil, 16, 17, 64 | 18, nil, 19, 20, 46, 35, 36, 33, 34, 16, 65 | 17, 18, 21, 19, 20, 46, 17, 18, 29, 19, 66 | 20, 46, nil, 21, nil, nil, nil, 16, nil, 21, 67 | 17, 18, nil, 19, 20, 46, 17, 18, 16, 19, 68 | 20, 46, nil, 21, 16, nil, nil, 17, 18, 21, 69 | 19, 20, 46, nil, nil, nil, nil, nil, 16, nil, 70 | 21, nil, nil, nil, 16, 35, 36, 33, 34, 39, 71 | 40, 37, 38, 41, 42, 16, 35, 36, 33, 34, 72 | 39, 40, 37, 38, 41, 42, 43, 44, nil, nil, 73 | nil, 69, 35, 36, 33, 34, 39, 40, 37, 38, 74 | 41, 42, 43, 44, 35, 36, 33, 34, 39, 40, 75 | 37, 38, 41, 42, 43, 44, 35, 36, 33, 34, 76 | 39, 40, 37, 38, 41, 42, 43, 44, 35, 36, 77 | 33, 34, 39, 40, 37, 38, 41, 42, 43, 44, 78 | 35, 36, 33, 34, 39, 40, 37, 38, 41, 42, 79 | 43, 44, 35, 36, 33, 34, 39, 40, 37, 38, 80 | 35, 36, 33, 34, 39, 40, 37, 38 ] 81 | 82 | racc_action_check = [ 83 | 0, 0, 0, 0, 0, 0, 0, 32, 2, 0, 84 | 0, 0, 0, 0, 88, 88, 88, 88, 88, 88, 85 | 88, 58, 58, 88, 88, 88, 88, 88, 0, 75, 86 | 0, 61, 61, 61, 61, 32, 2, 64, 64, 64, 87 | 64, 72, 88, 72, 88, 81, 81, 81, 81, 81, 88 | 81, 81, 52, 75, 81, 81, 81, 81, 81, 4, 89 | 4, 4, 4, 4, 4, 4, 57, 57, 4, 4, 90 | 4, 4, 4, 81, 46, 81, 68, 68, 68, 68, 91 | 68, 68, 68, 68, 68, 68, 68, 4, 30, 4, 92 | 51, 51, 51, 51, 51, 51, 51, 1, 89, 51, 93 | 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 94 | 51, 51, 51, 51, 51, 51, 24, 22, 51, 22, 95 | 51, 54, 54, 54, 54, 54, 54, 54, nil, nil, 96 | 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 97 | 54, 54, 54, 54, 54, 54, 54, nil, 82, 54, 98 | 82, 54, 80, 80, 80, 80, 80, 80, 80, nil, 99 | nil, 80, 80, 80, 80, 80, 49, 49, 92, 49, 100 | 49, 49, 33, 33, 92, 33, 33, 33, nil, 49, 101 | 80, nil, 80, nil, nil, 33, 25, 25, nil, 25, 102 | 25, 25, 87, nil, 49, 49, 92, nil, 87, 25, 103 | 33, nil, 26, 26, nil, 26, 26, 26, 23, 23, 104 | nil, 23, 23, 23, 25, 26, nil, nil, 86, nil, 105 | 87, 23, 31, 31, 86, 31, 31, 31, 31, nil, 106 | 26, 31, 31, 31, 31, 31, 23, nil, 34, 34, 107 | nil, 34, 34, 34, 35, 35, 86, 35, 35, 35, 108 | 31, 34, nil, nil, nil, 36, 36, 35, 36, 36, 109 | 36, nil, nil, nil, 76, nil, 34, nil, 36, nil, 110 | 76, nil, 35, 37, 37, nil, 37, 37, 37, 62, 111 | 62, 62, 62, 36, 38, 38, 37, 38, 38, 38, 112 | 39, 39, 76, 39, 39, 39, nil, 38, nil, nil, 113 | nil, 37, nil, 39, 40, 40, nil, 40, 40, 40, 114 | 41, 41, 38, 41, 41, 41, nil, 40, 39, nil, 115 | nil, 42, 42, 41, 42, 42, 42, nil, nil, nil, 116 | 74, nil, 40, nil, 42, 74, 74, nil, 41, 43, 117 | 43, nil, 43, 43, 43, 63, 63, 63, 63, 42, 118 | 44, 44, 43, 44, 44, 44, 78, 78, 74, 78, 119 | 78, 78, nil, 44, nil, nil, nil, 43, nil, 78, 120 | 50, 50, nil, 50, 50, 50, 16, 16, 44, 16, 121 | 16, 16, nil, 50, 78, nil, nil, 21, 21, 16, 122 | 21, 21, 21, nil, nil, nil, nil, nil, 50, nil, 123 | 21, nil, nil, nil, 16, 67, 67, 67, 67, 67, 124 | 67, 67, 67, 67, 67, 21, 45, 45, 45, 45, 125 | 45, 45, 45, 45, 45, 45, 45, 45, nil, nil, 126 | nil, 45, 85, 85, 85, 85, 85, 85, 85, 85, 127 | 85, 85, 85, 85, 5, 5, 5, 5, 5, 5, 128 | 5, 5, 5, 5, 5, 5, 53, 53, 53, 53, 129 | 53, 53, 53, 53, 53, 53, 53, 53, 73, 73, 130 | 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 131 | 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 132 | 70, 70, 66, 66, 66, 66, 66, 66, 66, 66, 133 | 65, 65, 65, 65, 65, 65, 65, 65 ] 134 | 135 | racc_action_pointer = [ 136 | -2, 97, 4, nil, 57, 428, nil, nil, nil, nil, 137 | nil, nil, nil, nil, nil, nil, 374, nil, nil, nil, 138 | nil, 385, 89, 206, 109, 184, 200, nil, nil, nil, 139 | 88, 220, 3, 170, 236, 242, 253, 271, 282, 288, 140 | 302, 308, 319, 337, 348, 400, 44, nil, nil, 164, 141 | 368, 88, 22, 440, 119, nil, nil, 50, 5, nil, 142 | nil, 15, 263, 329, 21, 484, 476, 389, 60, nil, 143 | 464, nil, 12, 452, 326, 22, 260, nil, 354, nil, 144 | 150, 43, 119, nil, nil, 416, 214, 188, 12, 91, 145 | nil, nil, 164, nil, nil ] 146 | 147 | racc_action_default = [ 148 | -1, -56, -2, -3, -7, -8, -9, -10, -11, -12, 149 | -13, -14, -15, -16, -17, -18, -56, -20, -21, -22, 150 | -23, -56, -42, -56, -56, -50, -56, -53, -54, -55, 151 | -56, -5, -6, -56, -56, -56, -56, -56, -56, -56, 152 | -56, -56, -56, -56, -56, -56, -42, -36, -37, -56, 153 | -56, -56, -56, -51, -56, 95, -4, -24, -25, -26, 154 | -27, -28, -29, -30, -31, -32, -33, -34, -35, -19, 155 | -40, -38, -56, -43, -56, -56, -56, -39, -56, -44, 156 | -56, -56, -56, -48, -52, -41, -56, -56, -56, -56, 157 | -45, -46, -56, -49, -47 ] 158 | 159 | racc_goto_table = [ 160 | 31, 2, 1, 56, 72, 32, 82, nil, nil, nil, 161 | nil, nil, nil, nil, 45, nil, nil, nil, nil, 47, 162 | nil, 51, nil, 53, 54, nil, nil, nil, nil, nil, 163 | 31, 57, 58, 59, 60, 61, 62, 63, 64, 65, 164 | 66, 67, 68, nil, nil, nil, nil, 70, 73, nil, 165 | nil, nil, 74, nil, nil, 76, nil, nil, nil, nil, 166 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 167 | nil, nil, 31, nil, 31, nil, 85, nil, nil, nil, 168 | nil, 86, 87, nil, 31, 31, nil, nil, nil, 92, 169 | 31 ] 170 | 171 | racc_goto_check = [ 172 | 4, 2, 1, 3, 17, 2, 18, nil, nil, nil, 173 | nil, nil, nil, nil, 5, nil, nil, nil, nil, 5, 174 | nil, 5, nil, 5, 5, nil, nil, nil, nil, nil, 175 | 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 176 | 5, 5, 5, nil, nil, nil, nil, 5, 5, nil, 177 | nil, nil, 2, nil, nil, 2, nil, nil, nil, nil, 178 | nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 179 | nil, nil, 4, nil, 4, nil, 5, nil, nil, nil, 180 | nil, 2, 2, nil, 4, 4, nil, nil, nil, 2, 181 | 4 ] 182 | 183 | racc_goto_pointer = [ 184 | nil, 2, 1, -28, -2, -2, nil, nil, nil, nil, 185 | nil, nil, nil, nil, nil, nil, nil, -45, -69 ] 186 | 187 | racc_goto_default = [ 188 | nil, nil, nil, 3, 4, 5, 6, 7, 8, 9, 189 | 10, 11, 12, 13, 14, 15, 48, nil, nil ] 190 | 191 | racc_reduce_table = [ 192 | 0, 0, :racc_error, 193 | 0, 34, :_reduce_1, 194 | 1, 34, :_reduce_2, 195 | 1, 35, :_reduce_3, 196 | 3, 35, :_reduce_4, 197 | 2, 35, :_reduce_5, 198 | 2, 35, :_reduce_6, 199 | 1, 35, :_reduce_7, 200 | 1, 36, :_reduce_none, 201 | 1, 36, :_reduce_none, 202 | 1, 36, :_reduce_none, 203 | 1, 36, :_reduce_none, 204 | 1, 36, :_reduce_none, 205 | 1, 36, :_reduce_none, 206 | 1, 36, :_reduce_none, 207 | 1, 38, :_reduce_none, 208 | 1, 38, :_reduce_none, 209 | 1, 38, :_reduce_none, 210 | 1, 38, :_reduce_none, 211 | 3, 38, :_reduce_19, 212 | 1, 45, :_reduce_20, 213 | 1, 45, :_reduce_21, 214 | 1, 45, :_reduce_22, 215 | 1, 45, :_reduce_23, 216 | 3, 46, :_reduce_24, 217 | 3, 46, :_reduce_25, 218 | 3, 46, :_reduce_26, 219 | 3, 46, :_reduce_27, 220 | 3, 46, :_reduce_28, 221 | 3, 46, :_reduce_29, 222 | 3, 46, :_reduce_30, 223 | 3, 46, :_reduce_31, 224 | 3, 46, :_reduce_32, 225 | 3, 46, :_reduce_33, 226 | 3, 46, :_reduce_34, 227 | 3, 46, :_reduce_35, 228 | 2, 46, :_reduce_36, 229 | 2, 47, :_reduce_37, 230 | 2, 49, :_reduce_38, 231 | 3, 49, :_reduce_39, 232 | 1, 50, :_reduce_40, 233 | 3, 50, :_reduce_41, 234 | 1, 48, :_reduce_42, 235 | 3, 40, :_reduce_43, 236 | 4, 39, :_reduce_44, 237 | 6, 39, :_reduce_45, 238 | 6, 41, :_reduce_46, 239 | 7, 41, :_reduce_47, 240 | 1, 51, :_reduce_48, 241 | 3, 51, :_reduce_49, 242 | 1, 44, :_reduce_50, 243 | 2, 44, :_reduce_51, 244 | 4, 42, :_reduce_52, 245 | 1, 43, :_reduce_53, 246 | 1, 37, :_reduce_none, 247 | 1, 37, :_reduce_none ] 248 | 249 | racc_reduce_n = 56 250 | 251 | racc_shift_n = 95 252 | 253 | racc_token_table = { 254 | false => 0, 255 | :error => 1, 256 | :NUMBER => 2, 257 | :STRING => 3, 258 | :NEWLINE => 4, 259 | :TRUE => 5, 260 | :FALSE => 6, 261 | :IDENTIFIER => 7, 262 | :IF => 8, 263 | :ELSE => 9, 264 | :END => 10, 265 | :DEF => 11, 266 | :RETURN => 12, 267 | :WHILE => 13, 268 | :BREAK => 14, 269 | "!" => 15, 270 | "*" => 16, 271 | "/" => 17, 272 | "+" => 18, 273 | "-" => 19, 274 | ">" => 20, 275 | ">=" => 21, 276 | "<" => 22, 277 | "<=" => 23, 278 | "==" => 24, 279 | "!=" => 25, 280 | "&&" => 26, 281 | "||" => 27, 282 | "=" => 28, 283 | "," => 29, 284 | "(" => 30, 285 | ")" => 31, 286 | ";" => 32 } 287 | 288 | racc_nt_base = 33 289 | 290 | racc_use_result_var = true 291 | 292 | Racc_arg = [ 293 | racc_action_table, 294 | racc_action_check, 295 | racc_action_default, 296 | racc_action_pointer, 297 | racc_goto_table, 298 | racc_goto_check, 299 | racc_goto_default, 300 | racc_goto_pointer, 301 | racc_nt_base, 302 | racc_reduce_table, 303 | racc_token_table, 304 | racc_shift_n, 305 | racc_reduce_n, 306 | racc_use_result_var ] 307 | 308 | Racc_token_to_s_table = [ 309 | "$end", 310 | "error", 311 | "NUMBER", 312 | "STRING", 313 | "NEWLINE", 314 | "TRUE", 315 | "FALSE", 316 | "IDENTIFIER", 317 | "IF", 318 | "ELSE", 319 | "END", 320 | "DEF", 321 | "RETURN", 322 | "WHILE", 323 | "BREAK", 324 | "\"!\"", 325 | "\"*\"", 326 | "\"/\"", 327 | "\"+\"", 328 | "\"-\"", 329 | "\">\"", 330 | "\">=\"", 331 | "\"<\"", 332 | "\"<=\"", 333 | "\"==\"", 334 | "\"!=\"", 335 | "\"&&\"", 336 | "\"||\"", 337 | "\"=\"", 338 | "\",\"", 339 | "\"(\"", 340 | "\")\"", 341 | "\";\"", 342 | "$start", 343 | "Program", 344 | "Statements", 345 | "Statement", 346 | "Terminator", 347 | "Expression", 348 | "If", 349 | "SetLocal", 350 | "Function", 351 | "While", 352 | "Break", 353 | "Return", 354 | "Literal", 355 | "Operator", 356 | "Call", 357 | "GetLocal", 358 | "Arguments", 359 | "Args", 360 | "Params" ] 361 | 362 | Racc_debug_parser = false 363 | 364 | ##### State transition tables end ##### 365 | 366 | # reduce 0 omitted 367 | 368 | module_eval(<<'.,.,', 'grammar.y', 27) 369 | def _reduce_1(val, _values, result) 370 | result = AST.new([]) 371 | result 372 | end 373 | .,., 374 | 375 | module_eval(<<'.,.,', 'grammar.y', 28) 376 | def _reduce_2(val, _values, result) 377 | result = AST.new(val[0]) 378 | result 379 | end 380 | .,., 381 | 382 | module_eval(<<'.,.,', 'grammar.y', 32) 383 | def _reduce_3(val, _values, result) 384 | result = val 385 | result 386 | end 387 | .,., 388 | 389 | module_eval(<<'.,.,', 'grammar.y', 33) 390 | def _reduce_4(val, _values, result) 391 | result = val[0] << val[2] 392 | result 393 | end 394 | .,., 395 | 396 | module_eval(<<'.,.,', 'grammar.y', 34) 397 | def _reduce_5(val, _values, result) 398 | result = val[0] 399 | result 400 | end 401 | .,., 402 | 403 | module_eval(<<'.,.,', 'grammar.y', 35) 404 | def _reduce_6(val, _values, result) 405 | result = val[1] 406 | result 407 | end 408 | .,., 409 | 410 | module_eval(<<'.,.,', 'grammar.y', 36) 411 | def _reduce_7(val, _values, result) 412 | result = [] 413 | result 414 | end 415 | .,., 416 | 417 | # reduce 8 omitted 418 | 419 | # reduce 9 omitted 420 | 421 | # reduce 10 omitted 422 | 423 | # reduce 11 omitted 424 | 425 | # reduce 12 omitted 426 | 427 | # reduce 13 omitted 428 | 429 | # reduce 14 omitted 430 | 431 | # reduce 15 omitted 432 | 433 | # reduce 16 omitted 434 | 435 | # reduce 17 omitted 436 | 437 | # reduce 18 omitted 438 | 439 | module_eval(<<'.,.,', 'grammar.y', 54) 440 | def _reduce_19(val, _values, result) 441 | result = val[1] 442 | result 443 | end 444 | .,., 445 | 446 | module_eval(<<'.,.,', 'grammar.y', 58) 447 | def _reduce_20(val, _values, result) 448 | result = NumberNode.new(val[0]) 449 | result 450 | end 451 | .,., 452 | 453 | module_eval(<<'.,.,', 'grammar.y', 59) 454 | def _reduce_21(val, _values, result) 455 | result = StringNode.new(val[0]) 456 | result 457 | end 458 | .,., 459 | 460 | module_eval(<<'.,.,', 'grammar.y', 60) 461 | def _reduce_22(val, _values, result) 462 | result = TrueNode.new 463 | result 464 | end 465 | .,., 466 | 467 | module_eval(<<'.,.,', 'grammar.y', 61) 468 | def _reduce_23(val, _values, result) 469 | result = FalseNode.new 470 | result 471 | end 472 | .,., 473 | 474 | module_eval(<<'.,.,', 'grammar.y', 65) 475 | def _reduce_24(val, _values, result) 476 | result = AddNode.new(val[0], val[2]) 477 | result 478 | end 479 | .,., 480 | 481 | module_eval(<<'.,.,', 'grammar.y', 66) 482 | def _reduce_25(val, _values, result) 483 | result = SubNode.new(val[0], val[2]) 484 | result 485 | end 486 | .,., 487 | 488 | module_eval(<<'.,.,', 'grammar.y', 67) 489 | def _reduce_26(val, _values, result) 490 | result = MulNode.new(val[0], val[2]) 491 | result 492 | end 493 | .,., 494 | 495 | module_eval(<<'.,.,', 'grammar.y', 68) 496 | def _reduce_27(val, _values, result) 497 | result = DivNode.new(val[0], val[2]) 498 | result 499 | end 500 | .,., 501 | 502 | module_eval(<<'.,.,', 'grammar.y', 69) 503 | def _reduce_28(val, _values, result) 504 | result = LessThanNode.new(val[0], val[2]) 505 | result 506 | end 507 | .,., 508 | 509 | module_eval(<<'.,.,', 'grammar.y', 70) 510 | def _reduce_29(val, _values, result) 511 | result = LessThanEqNode.new(val[0], val[2]) 512 | result 513 | end 514 | .,., 515 | 516 | module_eval(<<'.,.,', 'grammar.y', 71) 517 | def _reduce_30(val, _values, result) 518 | result = GreaterThanNode.new(val[0], val[2]) 519 | result 520 | end 521 | .,., 522 | 523 | module_eval(<<'.,.,', 'grammar.y', 72) 524 | def _reduce_31(val, _values, result) 525 | result = GreaterThanEqNode.new(val[0], val[2]) 526 | result 527 | end 528 | .,., 529 | 530 | module_eval(<<'.,.,', 'grammar.y', 73) 531 | def _reduce_32(val, _values, result) 532 | result = EqNode.new(val[0], val[2]) 533 | result 534 | end 535 | .,., 536 | 537 | module_eval(<<'.,.,', 'grammar.y', 74) 538 | def _reduce_33(val, _values, result) 539 | result = NotEqNode.new(val[0], val[2]) 540 | result 541 | end 542 | .,., 543 | 544 | module_eval(<<'.,.,', 'grammar.y', 75) 545 | def _reduce_34(val, _values, result) 546 | result = AndNode.new(val[0], val[2]) 547 | result 548 | end 549 | .,., 550 | 551 | module_eval(<<'.,.,', 'grammar.y', 76) 552 | def _reduce_35(val, _values, result) 553 | result = OrNode.new(val[0], val[2]) 554 | result 555 | end 556 | .,., 557 | 558 | module_eval(<<'.,.,', 'grammar.y', 77) 559 | def _reduce_36(val, _values, result) 560 | result = NotNode.new(val[1]) 561 | result 562 | end 563 | .,., 564 | 565 | module_eval(<<'.,.,', 'grammar.y', 81) 566 | def _reduce_37(val, _values, result) 567 | result = CallNode.new(val[0], val[1]) 568 | result 569 | end 570 | .,., 571 | 572 | module_eval(<<'.,.,', 'grammar.y', 85) 573 | def _reduce_38(val, _values, result) 574 | result = [] 575 | result 576 | end 577 | .,., 578 | 579 | module_eval(<<'.,.,', 'grammar.y', 86) 580 | def _reduce_39(val, _values, result) 581 | result = val[1] 582 | result 583 | end 584 | .,., 585 | 586 | module_eval(<<'.,.,', 'grammar.y', 90) 587 | def _reduce_40(val, _values, result) 588 | result = [val[0]] 589 | result 590 | end 591 | .,., 592 | 593 | module_eval(<<'.,.,', 'grammar.y', 91) 594 | def _reduce_41(val, _values, result) 595 | result = val[0] << val[2] 596 | result 597 | end 598 | .,., 599 | 600 | module_eval(<<'.,.,', 'grammar.y', 96) 601 | def _reduce_42(val, _values, result) 602 | result = GetLocalNode.new(val[0]) 603 | result 604 | end 605 | .,., 606 | 607 | module_eval(<<'.,.,', 'grammar.y', 100) 608 | def _reduce_43(val, _values, result) 609 | result = SetLocalNode.new(val[0], val[2]) 610 | result 611 | end 612 | .,., 613 | 614 | module_eval(<<'.,.,', 'grammar.y', 104) 615 | def _reduce_44(val, _values, result) 616 | result = IfNode.new(val[1], val[2], []) 617 | result 618 | end 619 | .,., 620 | 621 | module_eval(<<'.,.,', 'grammar.y', 105) 622 | def _reduce_45(val, _values, result) 623 | result = IfNode.new(val[1], val[2], val[4]) 624 | result 625 | end 626 | .,., 627 | 628 | module_eval(<<'.,.,', 'grammar.y', 109) 629 | def _reduce_46(val, _values, result) 630 | result = DefNode.new(val[1], [], val[4]) 631 | result 632 | end 633 | .,., 634 | 635 | module_eval(<<'.,.,', 'grammar.y', 110) 636 | def _reduce_47(val, _values, result) 637 | result = DefNode.new(val[1], val[3], val[5]) 638 | result 639 | end 640 | .,., 641 | 642 | module_eval(<<'.,.,', 'grammar.y', 114) 643 | def _reduce_48(val, _values, result) 644 | result = [val[0]] 645 | result 646 | end 647 | .,., 648 | 649 | module_eval(<<'.,.,', 'grammar.y', 115) 650 | def _reduce_49(val, _values, result) 651 | result = val[0] << val[2] 652 | result 653 | end 654 | .,., 655 | 656 | module_eval(<<'.,.,', 'grammar.y', 119) 657 | def _reduce_50(val, _values, result) 658 | result = ReturnNode.new(nil) 659 | result 660 | end 661 | .,., 662 | 663 | module_eval(<<'.,.,', 'grammar.y', 120) 664 | def _reduce_51(val, _values, result) 665 | result = ReturnNode.new(val[1]) 666 | result 667 | end 668 | .,., 669 | 670 | module_eval(<<'.,.,', 'grammar.y', 123) 671 | def _reduce_52(val, _values, result) 672 | result = WhileNode.new(val[1], val[2]) 673 | result 674 | end 675 | .,., 676 | 677 | module_eval(<<'.,.,', 'grammar.y', 127) 678 | def _reduce_53(val, _values, result) 679 | result = BreakNode.new 680 | result 681 | end 682 | .,., 683 | 684 | # reduce 54 omitted 685 | 686 | # reduce 55 omitted 687 | 688 | def _reduce_none(val, _values, result) 689 | val[0] 690 | end 691 | 692 | end # class Parser 693 | 694 | if __FILE__ == $0 695 | pp Parser.new.parse(File.read(ARGV[0])) 696 | end 697 | -------------------------------------------------------------------------------- /gamma/test/fib.txt: -------------------------------------------------------------------------------- 1 | def fib(n) 2 | if n == 1 3 | return 1 4 | end 5 | 6 | if n == 2 7 | return 1 8 | end 9 | 10 | fib(n - 1) + fib(n - 2) 11 | end 12 | 13 | print(fib(20)) 14 | -------------------------------------------------------------------------------- /gamma/test/operators.txt: -------------------------------------------------------------------------------- 1 | a = 4 2 | b = 6 3 | 4 | if a >= 4 5 | print("a is greater than or equal to 4") 6 | end 7 | 8 | if a <= 4 9 | print("a is less than or equal to 4") 10 | end 11 | 12 | if a == 1 || a == 2 13 | print("shouldn't see this") 14 | end 15 | 16 | if a == 4 && b == 6 17 | print("a is 4 and b is 6") 18 | end 19 | 20 | if ! (a == 5 && b == 7) 21 | print("Convoluted test is false") 22 | end 23 | -------------------------------------------------------------------------------- /gamma/test/simple_fn.txt: -------------------------------------------------------------------------------- 1 | def test(arg) 2 | if arg == 42 3 | print("Arg was 42") 4 | else 5 | print("Arg was not 42") 6 | end 7 | end 8 | 9 | def test2() 10 | "test" 11 | end 12 | 13 | def greet(age) 14 | if age == 32 15 | print("You are as old as Matt!") 16 | return "wow" 17 | end 18 | 19 | if age == 42 20 | print("Wow, you are old") 21 | true 22 | else 23 | print("You are not so old after all") 24 | false 25 | end 26 | end 27 | 28 | print(greet(22)) 29 | print(greet(32)) 30 | print(test2()) 31 | test(43) 32 | -------------------------------------------------------------------------------- /gamma/test/while.txt: -------------------------------------------------------------------------------- 1 | a = 10 2 | 3 | while a >= 0 4 | print(a) 5 | 6 | if a == 4 7 | break 8 | end 9 | 10 | a = a - 1 11 | end 12 | -------------------------------------------------------------------------------- /gamma/vm.rb: -------------------------------------------------------------------------------- 1 | require_relative 'compiler' 2 | 3 | class VM 4 | def execute(bytecode) 5 | ip = 0 6 | functions = {} 7 | stack = [] 8 | frames = [{}] 9 | locals = frames.first 10 | return_ips = [] 11 | 12 | loop do 13 | instruction = bytecode[ip] 14 | ip += 1 15 | 16 | case instruction.opcode 17 | when :push 18 | stack.push(instruction.operands[0]) 19 | 20 | when :get_local 21 | name = instruction.operands[0] 22 | val = locals[name] or raise "undefined variable #{name}" 23 | stack.push(val) 24 | 25 | when :set_local 26 | name = instruction.operands[0] 27 | locals[name] = stack.pop 28 | 29 | when :fn_begin 30 | name = instruction.operands[0] 31 | functions[name] = ip 32 | 33 | while bytecode[ip].opcode != :fn_end 34 | ip += 1 35 | end 36 | 37 | ip += 1 38 | 39 | when :frame_setup 40 | return_ips.push(stack.pop) 41 | 42 | arg_names = instruction.operands[0].dup 43 | 44 | args = {} 45 | arg_count = stack.pop 46 | while ! arg_names.empty? 47 | arg_name = arg_names.pop 48 | args[arg_name] = stack.pop 49 | end 50 | 51 | frames.push(args) 52 | locals = frames.last 53 | 54 | when :frame_teardown 55 | frames.pop 56 | locals = frames.last 57 | 58 | ip = return_ips.pop 59 | 60 | when :eq 61 | right = stack.pop 62 | left = stack.pop 63 | stack.push(left == right) 64 | 65 | when :not_eq 66 | right = stack.pop 67 | left = stack.pop 68 | stack.push(left != right) 69 | 70 | when :and 71 | right = stack.pop 72 | left = stack.pop 73 | stack.push(left && right) 74 | 75 | when :or 76 | right = stack.pop 77 | left = stack.pop 78 | stack.push(left || right) 79 | 80 | when :not 81 | val = stack.pop 82 | stack.push(! val) 83 | 84 | when :lt 85 | right = stack.pop 86 | left = stack.pop 87 | stack.push(left < right) 88 | 89 | when :lte 90 | right = stack.pop 91 | left = stack.pop 92 | stack.push(left <= right) 93 | 94 | when :gt 95 | right = stack.pop 96 | left = stack.pop 97 | stack.push(left > right) 98 | 99 | when :gte 100 | right = stack.pop 101 | left = stack.pop 102 | stack.push(left >= right) 103 | 104 | when :add 105 | right = stack.pop 106 | left = stack.pop 107 | stack.push(left + right) 108 | 109 | when :sub 110 | right = stack.pop 111 | left = stack.pop 112 | stack.push(left - right) 113 | 114 | when :mul 115 | right = stack.pop 116 | left = stack.pop 117 | stack.push(left * right) 118 | 119 | when :div 120 | right = stack.pop 121 | left = stack.pop 122 | stack.push(left / right) 123 | 124 | when :creljump 125 | condition = stack.pop 126 | 127 | if condition == false 128 | ip += instruction.operands[1] 129 | else 130 | ip += instruction.operands[0] 131 | end 132 | 133 | when :reljump 134 | ip += instruction.operands[0] 135 | 136 | when :call 137 | name = instruction.operands[0] 138 | 139 | if name == "print" 140 | args = [] 141 | arg_count = stack.pop 142 | arg_count.times { args << stack.pop } 143 | 144 | puts args[0] 145 | elsif fn_ip = functions[name] 146 | stack.push(ip) 147 | ip = fn_ip 148 | else 149 | raise "unknown function '#{name}'" 150 | end 151 | 152 | when :end 153 | break 154 | 155 | else 156 | raise "unknown opcode '#{instruction.opcode}' (#{instruction.inspect})" 157 | end 158 | end 159 | end 160 | end 161 | 162 | VM.new.execute(Compiler.new.compile(File.read(ARGV[0]))) 163 | --------------------------------------------------------------------------------