aboutsummaryrefslogtreecommitdiff
path: root/ruby
diff options
context:
space:
mode:
Diffstat (limited to 'ruby')
-rw-r--r--ruby/printer.rb23
-rw-r--r--ruby/reader.rb74
-rw-r--r--ruby/step0_repl.rb25
-rw-r--r--ruby/step1_read_print.rb32
-rw-r--r--ruby/types.rb5
5 files changed, 159 insertions, 0 deletions
diff --git a/ruby/printer.rb b/ruby/printer.rb
new file mode 100644
index 0000000..2d46e07
--- /dev/null
+++ b/ruby/printer.rb
@@ -0,0 +1,23 @@
+require "types"
+
+def _pr_str(obj, print_readably=true)
+ _r = print_readably
+ return case obj
+ when List
+ "(" + obj.map{|x| _pr_str(x, _r)}.join(" ") + ")"
+ when Vector
+ "[" + obj.map{|x| _pr_str(x, _r)}.join(" ") + "]"
+ when String
+ if _r
+ "\"" + obj.gsub(/\\/, "\\\\") \
+ .gsub(/"/, "\\\\\"") \
+ .gsub(/\n/, "\\\\n") + "\""
+ else
+ obj
+ end
+ when nil
+ "nil"
+ else
+ obj.to_s
+ end
+end
diff --git a/ruby/reader.rb b/ruby/reader.rb
new file mode 100644
index 0000000..d224e9c
--- /dev/null
+++ b/ruby/reader.rb
@@ -0,0 +1,74 @@
+require "types"
+
+class Reader
+ def initialize(tokens)
+ @position = 0
+ @tokens = tokens
+ end
+ def peek
+ return @tokens[@position]
+ end
+ def next
+ @position += 1
+ return @tokens[@position-1]
+ end
+end
+
+
+def tokenize(str)
+ re = /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)/
+ return str.scan(re).map{|m| m[0]}.select{|m| m != ""}
+end
+
+def parse_str(t)
+ return t[1..-2].gsub(/\\"/, "\"").gsub(/\\n/, "\n")
+end
+
+def read_atom(rdr)
+ token = rdr.next
+ return case token
+ when /^-?[0-9]+$/ then token.to_i # integer
+ when /^-?[0-9][0-9.]*$/ then token.to_f # float
+ when /^"/ then parse_str(token) # string
+ when "nil" then nil
+ when "true" then true
+ when "false" then false
+ else token.to_sym # symbol
+ end
+end
+
+def read_list(rdr, klass, start="(", last =")")
+ ast = klass.new
+ token = rdr.next()
+ if token != start
+ raise "expected '" + start + "'"
+ end
+ while (token = rdr.peek) != last
+ if not token
+ raise "expected '" + last + "', got EOF"
+ end
+ ast.push(read_form(rdr))
+ end
+ rdr.next
+ return ast
+end
+
+def read_form(rdr)
+ token = rdr.peek
+ return case rdr.peek
+ when ";" then nil
+ when "(" then read_list(rdr, List, "(", ")")
+ when ")" then raise "unexpected ')'"
+ when "[" then read_list(rdr, Vector, "[", "]")
+ when "]" then raise "unexpected ']'"
+ when "{" then raise "unexpected '{'"
+ when "}" then raise "unexpected '}'"
+ else read_atom(rdr)
+ end
+end
+
+def read_str(str)
+ tokens = tokenize(str)
+ return read_form(Reader.new(tokens))
+end
+
diff --git a/ruby/step0_repl.rb b/ruby/step0_repl.rb
new file mode 100644
index 0000000..dd32b27
--- /dev/null
+++ b/ruby/step0_repl.rb
@@ -0,0 +1,25 @@
+require "readline"
+
+# read
+def READ(str)
+ return str
+end
+
+# eval
+def EVAL(ast, env)
+ return ast
+end
+
+# print
+def PRINT(exp)
+ return exp
+end
+
+# repl
+def REP(str)
+ return PRINT(EVAL(READ(str), {}))
+end
+
+while line = Readline.readline("user> ", true)
+ puts REP(line)
+end
diff --git a/ruby/step1_read_print.rb b/ruby/step1_read_print.rb
new file mode 100644
index 0000000..20dd450
--- /dev/null
+++ b/ruby/step1_read_print.rb
@@ -0,0 +1,32 @@
+require "readline"
+require "types"
+require "reader"
+require "printer"
+
+# read
+def READ(str)
+ return read_str(str)
+end
+
+# eval
+def EVAL(ast, env)
+ return ast
+end
+
+# print
+def PRINT(exp)
+ return _pr_str(exp, true)
+end
+
+# repl
+def REP(str)
+ return PRINT(EVAL(READ(str), {}))
+end
+
+while line = Readline.readline("user> ", true)
+ begin
+ puts REP(line)
+ rescue Exception => e
+ puts "Error: %{e}"
+ end
+end
diff --git a/ruby/types.rb b/ruby/types.rb
new file mode 100644
index 0000000..a7ce285
--- /dev/null
+++ b/ruby/types.rb
@@ -0,0 +1,5 @@
+class List < Array
+end
+
+class Vector < Array
+end