mirror of
https://github.com/kanaka/mal.git
synced 2024-10-05 18:08:55 +03:00
ruby.2 step 3
This commit is contained in:
parent
918f370924
commit
229f30fdf1
40
impls/ruby.2/env.rb
Normal file
40
impls/ruby.2/env.rb
Normal file
@ -0,0 +1,40 @@
|
||||
require_relative "errors"
|
||||
require_relative "types"
|
||||
|
||||
module Mal
|
||||
class Env
|
||||
def initialize(outer = nil)
|
||||
@outer = outer
|
||||
@data = {}
|
||||
end
|
||||
|
||||
def set(k, v)
|
||||
@data[k] = v
|
||||
end
|
||||
|
||||
def find(k)
|
||||
if @data.key?(k)
|
||||
self
|
||||
elsif !@outer.nil?
|
||||
@outer.find(k)
|
||||
else
|
||||
Types::Nil.instance
|
||||
end
|
||||
end
|
||||
|
||||
def get(k)
|
||||
environment = find(k)
|
||||
|
||||
case environment
|
||||
when self.class
|
||||
environment.get_value(k)
|
||||
when Types::Nil
|
||||
raise SymbolNotFoundError, "Error! Symbol #{k.value} not found."
|
||||
end
|
||||
end
|
||||
|
||||
def get_value(k)
|
||||
@data[k]
|
||||
end
|
||||
end
|
||||
end
|
@ -3,6 +3,7 @@ module Mal
|
||||
class TypeError < ::TypeError; end
|
||||
|
||||
class InvalidHashmapKeyError < TypeError; end
|
||||
class InvalidLetBindingsError < TypeError; end
|
||||
class InvalidReaderPositionError < Error; end
|
||||
class InvalidTypeError < TypeError; end
|
||||
|
||||
|
@ -27,13 +27,14 @@ module Mal
|
||||
|
||||
def read_deref(reader)
|
||||
list = Types::List.new
|
||||
list << Types::Symbol.new("deref")
|
||||
list << Types::Symbol.for("deref")
|
||||
list << read_form(reader)
|
||||
list
|
||||
end
|
||||
|
||||
def read_false(reader)
|
||||
Types::False.new(reader.next)
|
||||
reader.advance!
|
||||
Types::False.instance
|
||||
end
|
||||
|
||||
def read_form(reader)
|
||||
@ -95,7 +96,7 @@ module Mal
|
||||
value = reader.next.dup[1...]
|
||||
substitute_escaped_chars!(value)
|
||||
|
||||
Types::Keyword.new(value)
|
||||
Types::Keyword.for(value)
|
||||
end
|
||||
|
||||
def read_list(reader)
|
||||
@ -117,7 +118,8 @@ module Mal
|
||||
end
|
||||
|
||||
def read_nil(reader)
|
||||
Types::Nil.new(reader.next)
|
||||
reader.advance!
|
||||
Types::Nil.instance
|
||||
end
|
||||
|
||||
def read_number(reader)
|
||||
@ -133,21 +135,21 @@ module Mal
|
||||
|
||||
def read_quasiquote(reader)
|
||||
list = Types::List.new
|
||||
list << Types::Symbol.new("quasiquote")
|
||||
list << Types::Symbol.for("quasiquote")
|
||||
list << read_form(reader)
|
||||
list
|
||||
end
|
||||
|
||||
def read_quote(reader)
|
||||
list = Types::List.new
|
||||
list << Types::Symbol.new("quote")
|
||||
list << Types::Symbol.for("quote")
|
||||
list << read_form(reader)
|
||||
list
|
||||
end
|
||||
|
||||
def read_splice_unquote(reader)
|
||||
list = Types::List.new
|
||||
list << Types::Symbol.new("splice-unquote")
|
||||
list << Types::Symbol.for("splice-unquote")
|
||||
list << read_form(reader)
|
||||
list
|
||||
end
|
||||
@ -170,16 +172,17 @@ module Mal
|
||||
end
|
||||
|
||||
def read_symbol(reader)
|
||||
Types::Symbol.new(reader.next)
|
||||
Types::Symbol.for(reader.next)
|
||||
end
|
||||
|
||||
def read_true(reader)
|
||||
Types::True.new(reader.next)
|
||||
reader.advance!
|
||||
Types::True.instance
|
||||
end
|
||||
|
||||
def read_unquote(reader)
|
||||
list = Types::List.new
|
||||
list << Types::Symbol.new("unquote")
|
||||
list << Types::Symbol.for("unquote")
|
||||
list << read_form(reader)
|
||||
list
|
||||
end
|
||||
@ -204,7 +207,7 @@ module Mal
|
||||
|
||||
def read_with_metadata(reader)
|
||||
list = Types::List.new
|
||||
list << Types::Symbol.new("with-meta")
|
||||
list << Types::Symbol.for("with-meta")
|
||||
|
||||
first = read_form(reader)
|
||||
second = read_form(reader)
|
||||
|
118
impls/ruby.2/step3_env.rb
Normal file
118
impls/ruby.2/step3_env.rb
Normal file
@ -0,0 +1,118 @@
|
||||
require "readline"
|
||||
|
||||
require_relative "env"
|
||||
require_relative "errors"
|
||||
require_relative "printer"
|
||||
require_relative "reader"
|
||||
|
||||
module Mal
|
||||
extend self
|
||||
|
||||
@repl_env = Env.new
|
||||
@repl_env.set(Types::Symbol.for('+'), -> (a, b) { a + b })
|
||||
@repl_env.set(Types::Symbol.for('-'), -> (a, b) { a - b })
|
||||
@repl_env.set(Types::Symbol.for('*'), -> (a, b) { a * b })
|
||||
@repl_env.set(Types::Symbol.for('/'), -> (a, b) { a / b })
|
||||
|
||||
def READ(input)
|
||||
read_str(input)
|
||||
end
|
||||
|
||||
def EVAL(ast, environment)
|
||||
if Types::List === ast && ast.size > 0
|
||||
case ast.first
|
||||
when Types::Symbol.for("def!")
|
||||
_, sym, val = ast
|
||||
environment.set(sym, EVAL(val, environment))
|
||||
when Types::Symbol.for("let*")
|
||||
e = Env.new(environment)
|
||||
_, bindings, val = ast
|
||||
|
||||
unless Types::List === bindings || Types::Vector === bindings
|
||||
raise InvalidLetBindingsError
|
||||
end
|
||||
|
||||
until bindings.empty?
|
||||
k, v = bindings.shift(2)
|
||||
|
||||
raise InvalidLetBindingsError if k.nil?
|
||||
v = Types::Nil.instance if v.nil?
|
||||
|
||||
e.set(k, EVAL(v, e))
|
||||
end
|
||||
|
||||
if !val.nil?
|
||||
EVAL(val, e)
|
||||
else
|
||||
Types::Nil.instance
|
||||
end
|
||||
else
|
||||
evaluated = eval_ast(ast, environment)
|
||||
maybe_callable = evaluated.first
|
||||
|
||||
if maybe_callable.respond_to?(:call)
|
||||
maybe_callable.call(*evaluated[1..])
|
||||
else
|
||||
raise NotCallableError, "Error! #{PRINT(maybe_callable)} is not callable."
|
||||
end
|
||||
end
|
||||
elsif Types::List === ast && ast.size == 0
|
||||
ast
|
||||
else
|
||||
eval_ast(ast, environment)
|
||||
end
|
||||
end
|
||||
|
||||
def PRINT(input)
|
||||
pr_str(input, true)
|
||||
end
|
||||
|
||||
def rep(input)
|
||||
PRINT(EVAL(READ(input), @repl_env))
|
||||
rescue InvalidHashmapKeyError => e
|
||||
"Error! Hashmap keys can only be strings or keywords."
|
||||
rescue NotCallableError => e
|
||||
e.message
|
||||
rescue SymbolNotFoundError => e
|
||||
e.message
|
||||
rescue UnbalancedEscapingError => e
|
||||
"Error! Detected unbalanced escaping. Check for matching '\\'."
|
||||
rescue UnbalancedHashmapError => e
|
||||
"Error! Detected unbalanced list. Check for matching '}'."
|
||||
rescue UnbalancedListError => e
|
||||
"Error! Detected unbalanced list. Check for matching ')'."
|
||||
rescue UnbalancedStringError => e
|
||||
"Error! Detected unbalanced string. Check for matching '\"'."
|
||||
rescue UnbalancedVectorError => e
|
||||
"Error! Detected unbalanced list. Check for matching ']'."
|
||||
end
|
||||
|
||||
def eval_ast(mal, environment)
|
||||
case mal
|
||||
when Types::Symbol
|
||||
environment.get(mal)
|
||||
when Types::List
|
||||
list = Types::List.new
|
||||
mal.each { |i| list << EVAL(i, environment) }
|
||||
list
|
||||
when Types::Vector
|
||||
vec = Types::Vector.new
|
||||
mal.each { |i| vec << EVAL(i, environment) }
|
||||
vec
|
||||
when Types::Hashmap
|
||||
hashmap = Types::Hashmap.new
|
||||
mal.each { |k, v| hashmap[k] = EVAL(v, environment) }
|
||||
hashmap
|
||||
else
|
||||
mal
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
while input = Readline.readline("user> ")
|
||||
puts Mal.rep(input)
|
||||
end
|
||||
|
||||
puts
|
||||
|
||||
|
@ -5,7 +5,18 @@ module Mal
|
||||
class Hashmap < ::Hash; end
|
||||
|
||||
class Atom < ::Struct.new(:value); end
|
||||
class Keyword < Atom; end
|
||||
|
||||
class Keyword < Atom
|
||||
def self.for(value)
|
||||
@_keywords ||= {}
|
||||
|
||||
if @_keywords.key?(value)
|
||||
@_keywords[value]
|
||||
else
|
||||
@_keywords[value] = new(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Number < Atom
|
||||
def +(other)
|
||||
@ -26,22 +37,45 @@ module Mal
|
||||
end
|
||||
|
||||
class String < Atom; end
|
||||
class Symbol < Atom; end
|
||||
|
||||
class Symbol < Atom
|
||||
def self.for(value)
|
||||
@_symbols ||= {}
|
||||
|
||||
if @_symbols.key?(value)
|
||||
@_symbols[value]
|
||||
else
|
||||
@_symbols[value] = new(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Nil < Atom
|
||||
def initialize(_value)
|
||||
def self.instance
|
||||
@_instance ||= new
|
||||
end
|
||||
|
||||
def initialize
|
||||
@value = nil
|
||||
end
|
||||
end
|
||||
|
||||
class True < Atom
|
||||
def initialize(_value)
|
||||
def self.instance
|
||||
@_instance ||= new
|
||||
end
|
||||
|
||||
def initialize
|
||||
@value = true
|
||||
end
|
||||
end
|
||||
|
||||
class False < Atom
|
||||
def initialize(_value)
|
||||
def self.instance
|
||||
@_instance ||= new
|
||||
end
|
||||
|
||||
def initialize
|
||||
@value = false
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user