diff options
Diffstat (limited to 'ruby')
| -rw-r--r-- | ruby/printer.rb | 23 | ||||
| -rw-r--r-- | ruby/reader.rb | 74 | ||||
| -rw-r--r-- | ruby/step0_repl.rb | 25 | ||||
| -rw-r--r-- | ruby/step1_read_print.rb | 32 | ||||
| -rw-r--r-- | ruby/types.rb | 5 |
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 |
