├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── bin └── .keep ├── samples ├── arithmetic.cr ├── boolean.cr ├── each.cr ├── literals │ ├── array.cr │ ├── bool.cr │ ├── chars.cr │ ├── floats.cr │ ├── hash.cr │ ├── integers.cr │ ├── nil.cr │ ├── proc.cr │ ├── range.cr │ ├── regex.cr │ ├── strings.cr │ ├── symbols.cr │ └── tuple.cr ├── methods.cr ├── recursive_fibonacci.cr └── saluton_mondo.cr ├── shard.yml ├── spec ├── magma_spec.cr └── spec_helper.cr └── src ├── magma.cr └── magma ├── interpreter.cr ├── mtype_wrapper.cr ├── processor.cr ├── processors ├── and_processor.cr ├── array_literal_processor.cr ├── assign_processor.cr ├── bool_literal_processor.cr ├── call_processor.cr ├── char_literal_processor.cr ├── def_processor.cr ├── expressions_processor.cr ├── fun_literal_processor.cr ├── if_processor.cr ├── nil_processor.cr ├── node_processor.cr ├── nop_processor.cr ├── number_literal_processor.cr ├── or_processor.cr ├── range_processor.cr ├── regex_literal_processor.cr ├── string_interpolation_processor.cr ├── string_literal_processor.cr ├── symbol_literal_processor.cr ├── tuple_literal_processor.cr └── var_processor.cr ├── types ├── m_array.cr ├── m_bool.cr ├── m_char.cr ├── m_nil.cr ├── m_number.cr ├── m_object.cr ├── m_range.cr ├── m_regex.cr ├── m_string.cr ├── m_symbol.cr └── m_tuple.cr └── version.cr /.gitignore: -------------------------------------------------------------------------------- 1 | /doc/ 2 | /libs/ 3 | /.crystal/ 4 | /.shards/ 5 | 6 | /bin/magma 7 | 8 | # This file is often used to run test examples 9 | /test.cr 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: crystal 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Potapov Sergey 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CRYSTAL_BIN ?= $(shell which crystal) 2 | ICR_BIN ?= $(shell which icr) 3 | 4 | build: 5 | $(CRYSTAL_BIN) build --release -o bin/magma src/magma.cr $(CRFLAGS) 6 | clean: 7 | rm -f ./bin/magma 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Magma 2 | 3 | An attempt to implement Crystal interpreter. 4 | 5 | Currently under hard development. However you can try it:) 6 | 7 | ## Installation 8 | 9 | It requires Crystal v0.11. 10 | 11 | Clone the repo: 12 | ``` 13 | git clone https://github.com/greyblake/crystal-magma.git 14 | cd crystal-magma 15 | ``` 16 | Run make to build binary 17 | ``` 18 | make 19 | ``` 20 | 21 | ## Usages 22 | 23 | Now you can try to run some samples, e.g: 24 | ```sh 25 | # aka Hello World 26 | ./bin/magma ./samples/saluton_mondo.cr 27 | Saluton Mondo! 28 | 29 | # Recursive fibonacci 30 | /bin/magma ./samples/recursive_fibonacci.cr 31 | ``` 32 | 33 | There are a lot to implement, likely you'll experience a bug, if you'll try to run something more complecated. 34 | 35 | ## Contributors 36 | 37 | - [greyblake](https://github.com/greyblake) Potapov Sergey - creator, maintainer 38 | -------------------------------------------------------------------------------- /bin/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyblake/crystal-magma/c89e9dbdc5a56c371d284b4d82f45a713a39ee28/bin/.keep -------------------------------------------------------------------------------- /samples/arithmetic.cr: -------------------------------------------------------------------------------- 1 | a = 20 2 | b = 2.5 3 | c = a**b - 10 + 4.3 / (4/2) 4 | 5 | puts c 6 | -------------------------------------------------------------------------------- /samples/boolean.cr: -------------------------------------------------------------------------------- 1 | a = true # true 2 | b = !a # false 3 | c = a || b # true 4 | d = a && b && c # false 5 | e = a ^ false # true 6 | 7 | puts a, b, c, d, e 8 | -------------------------------------------------------------------------------- /samples/each.cr: -------------------------------------------------------------------------------- 1 | arr = [1, 2, 3] 2 | 3 | total = 0 4 | 5 | arr.each do |item| 6 | total += item 7 | end 8 | 9 | puts total 10 | -------------------------------------------------------------------------------- /samples/literals/array.cr: -------------------------------------------------------------------------------- 1 | arr = [1, 2, 3 + 10] # Array(Int32) 2 | puts arr 3 | 4 | [1, "hello", 'x'] # Array(Int32 | String | Char) 5 | 6 | [] of Int32 # same as Array(Int32).new 7 | 8 | %w(one two three) # ["one", "two", "three"] 9 | 10 | %i(one two three) # [:one, :two, :three] 11 | -------------------------------------------------------------------------------- /samples/literals/bool.cr: -------------------------------------------------------------------------------- 1 | true # A Bool that is true 2 | false # A Bool that is false 3 | -------------------------------------------------------------------------------- /samples/literals/chars.cr: -------------------------------------------------------------------------------- 1 | 'a' 2 | 'z' 3 | '0' 4 | '_' 5 | 'あ' 6 | 7 | '\'' # single quote 8 | '\\' # backslash 9 | '\e' # escape 10 | '\f' # form feed 11 | '\n' # newline 12 | '\r' # carriage return 13 | '\t' # tab 14 | '\v' # vertical tab 15 | 16 | '\101' # == 'A' 17 | '\123' # == 'S' 18 | '\12' # == '\n' 19 | '\1' # code point 1 20 | 21 | '\u0041' # == 'A' 22 | 23 | '\u{41}' # == 'A' 24 | '\u{1F52E}' # == '🔮' 25 | -------------------------------------------------------------------------------- /samples/literals/floats.cr: -------------------------------------------------------------------------------- 1 | 1.0 # Float64 2 | 1.0_f32 # Float32 3 | 1_f32 # Float32 4 | 5 | 1e10 # Float64 6 | 1.5e10 # Float64 7 | 1.5e-7 # Float64 8 | 9 | +1.3 # Float64 10 | -0.5 # Float64 11 | 12 | 1_000_000.111_111 # better than 1000000.111111 13 | -------------------------------------------------------------------------------- /samples/literals/hash.cr: -------------------------------------------------------------------------------- 1 | {1 => 2, 3 => 4} # Hash(Int32, Int32) 2 | #{1 => 2, 'a' => 3} # Hash(Int32 | Char, Int32) 3 | -------------------------------------------------------------------------------- /samples/literals/integers.cr: -------------------------------------------------------------------------------- 1 | 1 # Int32 2 | 3 | 1_i8 # Int8 4 | 1_i16 # Int16 5 | 1_i32 # Int32 6 | 1_i64 # Int64 7 | 8 | 1_u8 # UInt8 9 | 1_u16 # UInt16 10 | 1_u32 # UInt32 11 | 1_u64 # UInt64 12 | 13 | +10 # Int32 14 | -20 # Int32 15 | 16 | 2147483648 # Int64 17 | 9223372036854775808 # UInt64 18 | 19 | 1_000_000 # better than 1000000 20 | 21 | 0o123 # == 83 22 | 23 | 0xFE012D # == 16646445 24 | 0xfe012d # == 16646445 25 | -------------------------------------------------------------------------------- /samples/literals/nil.cr: -------------------------------------------------------------------------------- 1 | puts nil 2 | -------------------------------------------------------------------------------- /samples/literals/proc.cr: -------------------------------------------------------------------------------- 1 | # A proc without arguments 2 | ->{ 1 } # Proc(Int32) 3 | 4 | # A proc with one argument 5 | ->(x : Int32) { x.to_s } # Proc(Int32, String) 6 | 7 | # A proc with two arguments: 8 | ->(x : Int32, y : Int32) { x + y } # Proc(Int32, Int32, Int32) 9 | 10 | Proc(Int32, String).new { |x| x.to_s } # Proc(Int32, String) 11 | 12 | proc = ->(x : Int32, y : Int32) { x + y } 13 | proc.call(1, 2) #=> 3 14 | -------------------------------------------------------------------------------- /samples/literals/range.cr: -------------------------------------------------------------------------------- 1 | 10..20 2 | 10...14.3 3 | -------------------------------------------------------------------------------- /samples/literals/regex.cr: -------------------------------------------------------------------------------- 1 | foo_or_bar = /foo|bar/ 2 | heeello = /h(e+)llo/ 3 | integer = /\d+/ 4 | 5 | r = /foo/imx 6 | 7 | slash = /\// 8 | 9 | r = %r(regex with slash: /) 10 | -------------------------------------------------------------------------------- /samples/literals/strings.cr: -------------------------------------------------------------------------------- 1 | "Saluton mondo!" 2 | 3 | "\"" # double quote 4 | "\\" # backslash 5 | "\e" # escape 6 | "\f" # form feed 7 | "\n" # newline 8 | "\r" # carriage return 9 | "\t" # tab 10 | "\v" # vertical tab 11 | 12 | "\101" # == "A" 13 | "\123" # == "S" 14 | "\12" # == "\n" 15 | "\1" # string with one character with code point 1 16 | 17 | "\u0041" # == "A" 18 | 19 | "\u{41}" # == "A" 20 | "\u{1F52E}" # == "🔮" 21 | 22 | "hello 23 | world" # same as "hello\n world" 24 | 25 | "hello " \ 26 | "world, " \ 27 | "no newlines" # same as "hello world, no newlines" 28 | 29 | "hello \ 30 | world, \ 31 | no newlines" # same as "hello world, no newlines" 32 | 33 | # Supports double quotes and nested parenthesis 34 | %(hello ("world")) # same as "hello (\"world\")" 35 | 36 | # Supports double quotes and nested brackets 37 | %[hello ["world"]] # same as "hello [\"world\"]" 38 | 39 | # Supports double quotes and nested curlies 40 | %{hello {"world"}} # same as "hello {\"world\"}" 41 | 42 | # Supports double quotes and nested angles 43 | %> # same as "hello <\"world\">" 44 | 45 | <<-XML 46 | 47 | 48 | 49 | XML 50 | 51 | # Same as "Hello\n world" 52 | <<-STRING 53 | Hello 54 | world 55 | STRING 56 | 57 | # Same as " Hello\n world" 58 | <<-STRING 59 | Hello 60 | world 61 | STRING 62 | 63 | a = 1 64 | b = 2 65 | "sum = #{a + b}" # "sum = 3" 66 | -------------------------------------------------------------------------------- /samples/literals/symbols.cr: -------------------------------------------------------------------------------- 1 | :hello 2 | :good_bye 3 | 4 | # With spaces and symbols 5 | :"symbol with spaces" 6 | 7 | # Ending with question and exclamation marks 8 | :question? 9 | :exclamation! 10 | 11 | # For the operators 12 | :+ 13 | :- 14 | :* 15 | :/ 16 | :== 17 | :< 18 | :<= 19 | :> 20 | :>= 21 | :! 22 | :!= 23 | :=~ 24 | :!~ 25 | :& 26 | :| 27 | :^ 28 | :~ 29 | :** 30 | :>> 31 | :<< 32 | :% 33 | :[] 34 | :[]? 35 | :[]= 36 | :<=> 37 | :=== 38 | -------------------------------------------------------------------------------- /samples/literals/tuple.cr: -------------------------------------------------------------------------------- 1 | tuple = {1, "hello", 'x'} # Tuple(Int32, String, Char) 2 | puts tuple 3 | puts tuple[0] #=> 1 (Int32) 4 | puts tuple[1] #=> "hello" (String) 5 | puts tuple[2] #=> 'x' (Char) 6 | -------------------------------------------------------------------------------- /samples/methods.cr: -------------------------------------------------------------------------------- 1 | 2 | def plus(a, b) 3 | a + b 4 | end 5 | 6 | puts plus(2, 11) 7 | -------------------------------------------------------------------------------- /samples/recursive_fibonacci.cr: -------------------------------------------------------------------------------- 1 | def fib(n) 2 | if n <= 1 3 | 1 4 | else 5 | fib(n-1) + fib(n-2) 6 | end 7 | end 8 | 9 | puts fib(5) 10 | -------------------------------------------------------------------------------- /samples/saluton_mondo.cr: -------------------------------------------------------------------------------- 1 | puts "Saluton Mondo!" 2 | -------------------------------------------------------------------------------- /shard.yml: -------------------------------------------------------------------------------- 1 | name: magma 2 | version: 0.1.0 3 | 4 | authors: 5 | - Potapov Sergey 6 | 7 | license: MIT 8 | -------------------------------------------------------------------------------- /spec/magma_spec.cr: -------------------------------------------------------------------------------- 1 | require "./spec_helper" 2 | 3 | describe Magma do 4 | # TODO: Write tests 5 | 6 | it "works" do 7 | false.should eq(true) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | require "spec" 2 | require "../src/magma" 3 | -------------------------------------------------------------------------------- /src/magma.cr: -------------------------------------------------------------------------------- 1 | #require "compiler/crystal/**" 2 | 3 | # Require minimal to use Parser 4 | require "compiler/crystal/program" 5 | require "compiler/crystal/compiler" 6 | require "compiler/crystal/syntax/**" 7 | require "compiler/crystal/semantic/**" 8 | 9 | 10 | alias Any = Int32 | Int64 | UInt64 | Float64 | Nil | Bool | String | Char 11 | 12 | require "./magma/mtype_wrapper" 13 | require "./magma/processor" 14 | require "./magma/interpreter" 15 | require "./magma/processors/node_processor" 16 | require "./magma/processors/*" 17 | 18 | require "./magma/types/m_object" 19 | require "./magma/types/*" 20 | 21 | 22 | module Magma 23 | end 24 | 25 | 26 | code = File.read(ARGV[0]) 27 | 28 | interpreter = Magma::Interpreter.new 29 | interpreter.execute(code) 30 | -------------------------------------------------------------------------------- /src/magma/interpreter.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class Interpreter < Processor 3 | def execute(code : String) 4 | node = Crystal::Parser.parse(code) 5 | gprocess(node) 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/magma/mtype_wrapper.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | module MTypeWrapper 3 | def mtype_wrap(arg) : MObject 4 | case arg 5 | when MObject 6 | arg 7 | when Bool 8 | MBool.new(arg) 9 | when Number 10 | MNumber.new(arg) 11 | when String 12 | MString.new(arg) 13 | else 14 | raise "#mtype_wrap: unknown type: #{arg.class}" 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /src/magma/processor.cr: -------------------------------------------------------------------------------- 1 | 2 | module Magma 3 | class Processor 4 | # Generic #gprocess method to process any possible node 5 | # Example: 6 | # def gprocess(node : Crystal::ASTNode) 7 | # case node 8 | # dispatch_ast_nodes [Assign, StringLiteral, Expressions] 9 | # when Crystal::Assign 10 | # AssignProcessor.new(self).process(node as Crystal::Assign) 11 | # else 12 | # abort "Processor#gprocess: can't process node #{node.class}" 13 | # end 14 | # end 15 | macro define_gprocess(nodes) 16 | def gprocess(node : Crystal::ASTNode|Nil) 17 | case node 18 | {% for node, index in nodes %} 19 | when Crystal::{{node}} 20 | {{node}}Processor.new(self).process(node as Crystal::{{node}}) 21 | {% end %} 22 | else 23 | node_name = node.class.to_s.split("::").last 24 | puts "Processor#gprocess: can't process node #{node.class}" 25 | puts <<-MSG 26 | Implement Magma::#{node_name}Processor class: 27 | 28 | module Magma 29 | class #{node_name}Processor < NodeProcessor 30 | def process(node : Crystal::#{node_name}) : MObject 31 | end 32 | end 33 | end 34 | 35 | And register in processor.cr 36 | 37 | define_gprocess [Assign, StringLiteral, ... , #{node_name}] 38 | 39 | MSG 40 | exit 1 41 | end 42 | end 43 | end 44 | 45 | include MTypeWrapper 46 | 47 | define_gprocess [ 48 | Assign, StringLiteral, NumberLiteral, Call, Expressions, BoolLiteral, Var, Or, And, NilLiteral, CharLiteral, 49 | StringInterpolation, SymbolLiteral, ArrayLiteral, RangeLiteral, RegexLiteral, TupleLiteral, 50 | FunLiteral, Def, 51 | Nop, 52 | If 53 | ] 54 | 55 | property :var_context, :method_context 56 | 57 | def initialize 58 | @var_context = Hash(String, MObject).new 59 | @method_context = Hash(String, Crystal::Def).new 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /src/magma/processors/and_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class AndProcessor < NodeProcessor 3 | def process(node : Crystal::And) : MObject 4 | left = gprocess(node.left) 5 | right = gprocess(node.right) 6 | mtype_wrap(left.value && right.value) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /src/magma/processors/array_literal_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class ArrayLiteralProcessor < NodeProcessor 3 | def process(node : Crystal::ArrayLiteral) : MObject 4 | # Other properties for the node 5 | # - of 6 | # - name 7 | arr = node.elements.map { |node| gprocess(node) } 8 | 9 | # TODO: determine type of array 10 | 11 | MArray.new(arr) 12 | end 13 | end 14 | end 15 | 16 | -------------------------------------------------------------------------------- /src/magma/processors/assign_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class AssignProcessor < NodeProcessor 3 | def process(node : Crystal::Assign) : MObject 4 | case node.target 5 | when Crystal::Var 6 | var = node.target as Crystal::Var 7 | value = gprocess(node.value) 8 | var_context[var.name] = value 9 | else 10 | abort "AssignProcessor: unknown node: #{node.class}" 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /src/magma/processors/bool_literal_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class BoolLiteralProcessor < NodeProcessor 3 | def process(node : Crystal::BoolLiteral) : MBool 4 | value = node.true_literal? ? true : false 5 | MBool.new(value) 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/magma/processors/call_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | # This one has to be refactored to delegate calls to the types. 3 | class CallProcessor < NodeProcessor 4 | def process(node : Crystal::Call) : MObject 5 | if node.obj 6 | receiver = gprocess(node.obj) 7 | args = node.args.map { |arg_node| gprocess(arg_node) } 8 | receiver.call(node.name, args) 9 | else 10 | process_global(node) 11 | end 12 | end 13 | 14 | #class BlockExecuter < ::Magma::Processor 15 | # def initialize(@processor, @block : Crystal::Block, @args : Array) 16 | # @context = @processor.context 17 | # end 18 | 19 | # def execute 20 | # # Set context variables 21 | # @block.args.each_with_index do |var_node, index| 22 | # name = var_node.name 23 | # @context[name] = @args[index] 24 | # end 25 | 26 | # process_node(@block.body) 27 | # end 28 | #end 29 | 30 | #private def process_array(obj) 31 | # case node.name 32 | # when "each" 33 | # if node.block 34 | # obj.each do |item| 35 | # BlockExecuter.new(processor, node.block as Crystal::Block, [item]).execute 36 | # end 37 | # end 38 | # when "<<" 39 | # obj << processor.process_node(node.args.first) 40 | # else 41 | # puts "#{__FILE__}:#{__LINE__}" 42 | # raise("Unknown method for #{obj.class}: #{node.name}") 43 | # end 44 | #end 45 | 46 | #private def process_bool(obj : Bool) 47 | # case node.name 48 | # when "!" 49 | # !obj 50 | # when "^" 51 | # arg = processor.process_node(node.args.first) 52 | # obj ^ arg 53 | # else 54 | # raise("Unknown method for #{obj.inspect}: #{node.name}") 55 | # end 56 | #end 57 | 58 | #private def process_number(obj) 59 | # val = processor.process_node(node.args.first) 60 | 61 | # case node.name 62 | # when "+" 63 | # obj + val 64 | # when "-" 65 | # obj - val 66 | # when "*" 67 | # obj * val 68 | # when "/" 69 | # obj / val 70 | # when "**" 71 | # obj **(val) 72 | # when "<=>" 73 | # obj <=> val 74 | # when "==" 75 | # obj == val 76 | # when ">" 77 | # obj > val 78 | # when "<" 79 | # obj < val 80 | # when "abs" 81 | # obj.abs 82 | # when "abs2" 83 | # obj.abs2 84 | # else 85 | # raise("Unknown method for #{obj.inspect}: `#{node.name}`") 86 | # end 87 | #end 88 | 89 | private def process_global(node) 90 | if method_context[node.name]? 91 | def_node = method_context[node.name] 92 | # get actual value of arguments 93 | args = node.args.map { |arg_node| gprocess(arg_node) } 94 | MethodCaller.new(self).call(def_node, args) 95 | else 96 | args = node.args.map {|node| gprocess(node) } 97 | MObject.new(nil).call(node.name, args) 98 | end 99 | end 100 | 101 | # TODO: hacky, it needs to manage context, but should not inherit form NodeProcessor. 102 | class MethodCaller < NodeProcessor 103 | def initialize(*args) 104 | super(*args) 105 | 106 | # Method should have its own var context. 107 | @var_context = Hash(String, MObject).new 108 | end 109 | 110 | # It's necessary, because NodeProcessor redefines +var_context+ 111 | def var_context 112 | @var_context 113 | end 114 | 115 | def call(def_node : Crystal::Def, args : Array(MObject)) 116 | # Pass arguments into the method context 117 | def_node.args.each_with_index do |var_node, index| 118 | name = var_node.name 119 | var_context[name] = args[index] 120 | end 121 | gprocess(def_node.body) 122 | end 123 | end 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /src/magma/processors/char_literal_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class CharLiteralProcessor < NodeProcessor 3 | def process(node : Crystal::CharLiteral) : MObject 4 | MChar.new(node.value) 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/magma/processors/def_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | # class Def < ASTNode 3 | # property :receiver 4 | # property :name 5 | # property :args 6 | # property :body 7 | # property :block_arg 8 | # property? :macro_def 9 | # property :return_type 10 | # property :yields 11 | # property :instance_vars 12 | # property :calls_super 13 | # property :calls_initialize 14 | # property :uses_block_arg 15 | # property :assigns_special_var 16 | # property :name_column_number 17 | # property :abstract 18 | # property :attributes 19 | # property :splat_index 20 | # property :doc 21 | 22 | class DefProcessor < NodeProcessor 23 | def process(node : Crystal::Def) : MObject 24 | #method = MDef.new( 25 | # node.receiver, 26 | # node.name, 27 | # node.args, 28 | # node.body 29 | #) 30 | unless node.name 31 | raise("DefProcessor: no method name") 32 | end 33 | name = node.name as String 34 | 35 | method_context[name] = node 36 | MSymbol.new(node.name) 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /src/magma/processors/expressions_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class ExpressionsProcessor < NodeProcessor 3 | def process(node : Crystal::Expressions) : MObject 4 | result = MNil.new 5 | node.expressions.each do |node| 6 | result = gprocess(node) 7 | end 8 | result 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /src/magma/processors/fun_literal_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class FunLiteralProcessor < NodeProcessor 3 | def process(node : Crystal::FunLiteral) : MObject 4 | MNil.new 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/magma/processors/if_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class IfProcessor < NodeProcessor 3 | # property :cond 4 | # property :then 5 | # property :else 6 | # property :binary 7 | def process(node : Crystal::If) : MObject 8 | cond = gprocess(node.cond) 9 | if cond.value 10 | gprocess(node.then) 11 | else 12 | gprocess(node.else) 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /src/magma/processors/nil_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class NilLiteralProcessor < NodeProcessor 3 | def process(node : Crystal::NilLiteral) : MObject 4 | MNil.new 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/magma/processors/node_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | # Basic class for the all node processors 3 | abstract class NodeProcessor < Processor 4 | getter :parent 5 | 6 | def initialize(@parent) 7 | super() 8 | end 9 | 10 | def var_context 11 | parent.var_context 12 | end 13 | 14 | def method_context 15 | parent.method_context 16 | end 17 | 18 | # Does not work really work 19 | #abstract def process(node : Crystal::ASTNode) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /src/magma/processors/nop_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class NopProcessor < NodeProcessor 3 | def process(node : Crystal::Nop) : MObject 4 | MNil.new 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/magma/processors/number_literal_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class NumberLiteralProcessor < NodeProcessor 3 | def process(node : Crystal::NumberLiteral) : MNumber 4 | case node.kind 5 | when :i8, :i16, :i32, :i64 6 | MNumber.new(node.value.to_i64) 7 | when :u8, :u16, :u32, :u64 8 | MNumber.new(node.value.to_u64) 9 | when :f32, :f64 10 | MNumber.new(node.value.to_f64) 11 | else 12 | raise "NumberLiteralProcessor:: unknown kind: #{node.kind.inspect}" 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /src/magma/processors/or_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class OrProcessor < NodeProcessor 3 | def process(node : Crystal::Or) : MObject 4 | left = gprocess(node.left) 5 | right = gprocess(node.right) 6 | mtype_wrap(left.value || right.value) 7 | end 8 | end 9 | end 10 | 11 | -------------------------------------------------------------------------------- /src/magma/processors/range_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class RangeLiteralProcessor < NodeProcessor 3 | def process(node : Crystal::RangeLiteral) : MObject 4 | from = gprocess(node.from) 5 | to = gprocess(node.to) 6 | 7 | exclusive = node.exclusive 8 | MRange.new(from, to, exclusive) 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /src/magma/processors/regex_literal_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class RegexLiteralProcessor < NodeProcessor 3 | def process(node : Crystal::RegexLiteral) : MObject 4 | source = gprocess(node.value).value as String 5 | regex = Regex.new(source, node.options) 6 | MRegex.new(regex) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /src/magma/processors/string_interpolation_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class StringInterpolationProcessor < NodeProcessor 3 | def process(node : Crystal::StringInterpolation) : MObject 4 | strs = node.expressions.map do |exp| 5 | (gprocess(exp).call("to_s", [] of MObject).value) as String 6 | end 7 | mtype_wrap(strs.join("")) 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /src/magma/processors/string_literal_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class StringLiteralProcessor < NodeProcessor 3 | def process(node : Crystal::StringLiteral) : MObject 4 | MString.new(node.value) 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/magma/processors/symbol_literal_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class SymbolLiteralProcessor < NodeProcessor 3 | def process(node : Crystal::SymbolLiteral) : MObject 4 | MSymbol.new(node.value) 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/magma/processors/tuple_literal_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class TupleLiteralProcessor < NodeProcessor 3 | def process(node : Crystal::TupleLiteral) : MObject 4 | arr = node.elements.map { |el| gprocess(el) } 5 | MTuple.new(arr) 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/magma/processors/var_processor.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class VarProcessor < NodeProcessor 3 | def process(node : Crystal::Var) : MObject 4 | var_context[node.name] 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/magma/types/m_array.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class MArray < MObject 3 | def initialize(@value : Array(MObject)) 4 | end 5 | 6 | # TODO: hack, could it be done without casting? 7 | def value 8 | @value as Array(MObject) 9 | end 10 | 11 | def call(method, args = [] of MObject : Array(MObject)) 12 | case method 13 | when "first" 14 | value[0] 15 | when "to_s" 16 | call("inspect", args) 17 | when "inspect" 18 | list = value.map { |item| item.call("inspect", [] of MObject).value }.join(", ") 19 | MString.new("[#{list}]") 20 | when "<<" 21 | # TODO: raise exception if number of arguments does not match 22 | 23 | new_item = args.first 24 | value << new_item 25 | self 26 | else 27 | super 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /src/magma/types/m_bool.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class MBool < MObject 3 | def call(method, args = [] of MObject : Array(MObject)) 4 | case method 5 | when "!" 6 | MBool.new(!@value) 7 | when "^" 8 | if args.size != 1 9 | raise("Wrong number of arguments for 'Bool#^' (#{args.size} for 1)") 10 | end 11 | arg = mtype_wrap(args.first) 12 | result = (@value as Bool) ^ (arg.value as Bool) 13 | mtype_wrap(result) 14 | else 15 | super(method, args) 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /src/magma/types/m_char.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class MChar < MObject 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /src/magma/types/m_nil.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class MNil < MObject 3 | def initialize 4 | @value = nil 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/magma/types/m_number.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class MNumber < MObject 3 | # TODO: refactor duplications, using macro 4 | def call(method : String, args : Array(MObject)) 5 | case method 6 | when "**" 7 | result = (@value as Number) ** (args.first.value as Number) 8 | mtype_wrap(result) 9 | when "-" 10 | result = (@value as Number) - (args.first.value as Number) 11 | mtype_wrap(result) 12 | when "/" 13 | result = (@value as Number) / (args.first.value as Number) 14 | mtype_wrap(result) 15 | when "+" 16 | result = (@value as Number) + (args.first.value as Number) 17 | mtype_wrap(result) 18 | when "%" 19 | result = (@value as Number) % (args.first.value as Int) 20 | mtype_wrap(result) 21 | 22 | # TODO: refactor, clean up with macro 23 | when "==" 24 | result = (@value as Number) == (args.first.value as Number) 25 | mtype_wrap(result) 26 | when ">" 27 | result = (@value as Number) > (args.first.value as Number) 28 | mtype_wrap(result) 29 | when "<" 30 | result = (@value as Number) < (args.first.value as Number) 31 | mtype_wrap(result) 32 | when "<=" 33 | result = (@value as Number) <= (args.first.value as Number) 34 | mtype_wrap(result) 35 | when ">=" 36 | result = (@value as Number) >= (args.first.value as Number) 37 | mtype_wrap(result) 38 | 39 | when "inspect" 40 | MString.new(@value.inspect) 41 | when "to_s" 42 | MString.new(@value.to_s) 43 | else 44 | super(method, args) 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /src/magma/types/m_object.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class MObject 3 | include MTypeWrapper 4 | 5 | getter :value 6 | 7 | def initialize(@value) 8 | end 9 | 10 | def call(method : String, args : Array(MObject)) 11 | case method 12 | when "puts" 13 | args.each { |arg| puts arg.call("to_s", [] of MObject).value } 14 | MNil.new 15 | when "to_s" 16 | MString.new(@value.to_s) 17 | when "inspect" 18 | MString.new(@value.inspect) 19 | else 20 | abort("Not implement method #{self.class}##{method}") 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /src/magma/types/m_range.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class MRange < MObject 3 | def initialize(@from : MObject, @to : MObject, @exclusive : Bool) 4 | end 5 | 6 | def call(method, args = [] of MObject : Array(MObject)) 7 | case method 8 | when "to_s" 9 | call("inspect", args) 10 | when "inspect" 11 | from_str = @from.call("inspect", [] of MObject).value 12 | to_str = @to.call("inspect", [] of MObject).value 13 | interval = @exclusive ? "..." : ".." 14 | MString.new("#{from_str}#{interval}#{to_str}") 15 | else 16 | super 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /src/magma/types/m_regex.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class MRegex < MObject 3 | def call(method, args = [] of MObject : Array(MObject)) 4 | case method 5 | when "to_s" 6 | MString.new(value.to_s) 7 | when "inspect" 8 | MString.new(value.inspect) 9 | else 10 | super 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /src/magma/types/m_string.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class MString < MObject 3 | def call(method, args = [] of MObject : Array(MObject)) 4 | case method 5 | when "to_s" 6 | self 7 | when "=~" 8 | regex = args.first.value as Regex 9 | result = value =~ regex 10 | mtype_wrap(result) 11 | else 12 | super 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /src/magma/types/m_symbol.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class MSymbol < MObject 3 | def call(method, args : Array(MObject)) 4 | case method 5 | when "to_s" 6 | MString.new(value) 7 | when "inspect" 8 | MString.new(":#{value}") 9 | else 10 | super 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /src/magma/types/m_tuple.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | class MTuple < MObject 3 | def initialize(@value : Array(MObject)) 4 | end 5 | 6 | # TODO: hack, could it be done without casting? 7 | def value 8 | @value as Array(MObject) 9 | end 10 | 11 | def call(method, args : Array(MObject)) 12 | case method 13 | when "to_s" 14 | call("inspect", args) 15 | when "inspect" 16 | list = value.map { |item| item.call("inspect", [] of MObject).value }.join(", ") 17 | MString.new("{#{list}}") 18 | when "[]" 19 | index = args.first.value as Int64 20 | value[index] 21 | else 22 | super 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /src/magma/version.cr: -------------------------------------------------------------------------------- 1 | module Magma 2 | VERSION = "0.1.0" 3 | end 4 | --------------------------------------------------------------------------------