rb-scheme 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +37 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +65 -0
- data/Rakefile +11 -0
- data/circle.yml +6 -0
- data/examples/nqueen.scm +120 -0
- data/examples/y_combinator.scm +12 -0
- data/exe/rb-scheme +8 -0
- data/lib/rb-scheme.rb +18 -0
- data/lib/rb-scheme/compiler.rb +280 -0
- data/lib/rb-scheme/evaluator.rb +26 -0
- data/lib/rb-scheme/executer.rb +55 -0
- data/lib/rb-scheme/extension.rb +17 -0
- data/lib/rb-scheme/extension/procedures.scm +7 -0
- data/lib/rb-scheme/global.rb +25 -0
- data/lib/rb-scheme/helpers.rb +42 -0
- data/lib/rb-scheme/lisp-objects.rb +105 -0
- data/lib/rb-scheme/parser.rb +146 -0
- data/lib/rb-scheme/primitive.rb +111 -0
- data/lib/rb-scheme/primitive/procedure.rb +42 -0
- data/lib/rb-scheme/printer.rb +108 -0
- data/lib/rb-scheme/symbol.rb +14 -0
- data/lib/rb-scheme/version.rb +3 -0
- data/lib/rb-scheme/vm.rb +281 -0
- data/lib/rb-scheme/vm/box.rb +17 -0
- data/lib/rb-scheme/vm/stack.rb +45 -0
- data/rb-scheme.gemspec +27 -0
- metadata +118 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
module RbScheme
|
2
|
+
class Evaluator
|
3
|
+
extend Forwardable
|
4
|
+
include Helpers
|
5
|
+
include Symbol
|
6
|
+
|
7
|
+
def_delegator :@compiler, :compile
|
8
|
+
def_delegator :@vm, :exec, :vm_exec
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@compiler = Compiler.new
|
12
|
+
@vm = VM.new
|
13
|
+
Primitive.new.initialize_vm_primitive!
|
14
|
+
Extension.initialize_compound!(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def vm_eval(obj)
|
18
|
+
c = compile(obj, list, Set.new, list(intern("halt")))
|
19
|
+
vm_exec(list,
|
20
|
+
c,
|
21
|
+
0,
|
22
|
+
list,
|
23
|
+
0)
|
24
|
+
end
|
25
|
+
end # Evaluator
|
26
|
+
end # RbScheme
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RbScheme
|
2
|
+
class Executer
|
3
|
+
extend Forwardable
|
4
|
+
include Helpers
|
5
|
+
|
6
|
+
def_delegator :@parser, :read_expr
|
7
|
+
def_delegator :@evaluator, :vm_eval
|
8
|
+
def_delegator :@printer, :puts_lisp_object
|
9
|
+
|
10
|
+
def self.run(source)
|
11
|
+
new(source).exec
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(source)
|
15
|
+
set_source!(source)
|
16
|
+
@evaluator = Evaluator.new
|
17
|
+
@printer = Printer.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_source!(source)
|
21
|
+
@source = source
|
22
|
+
@parser = Parser.new(source)
|
23
|
+
end
|
24
|
+
|
25
|
+
def exit?(expr)
|
26
|
+
expr.is_a?(LSymbol) && expr.name == "exit"
|
27
|
+
end
|
28
|
+
|
29
|
+
def exec
|
30
|
+
if File.file?(@source)
|
31
|
+
exec_file
|
32
|
+
else
|
33
|
+
exec_repl
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def exec_file
|
38
|
+
loop do
|
39
|
+
expr = read_expr
|
40
|
+
break if expr.nil?
|
41
|
+
vm_eval(expr)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def exec_repl
|
46
|
+
loop do
|
47
|
+
print "> "
|
48
|
+
expr = read_expr
|
49
|
+
return if expr.nil?
|
50
|
+
return if exit?(expr)
|
51
|
+
puts_lisp_object(vm_eval(expr))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end # Executer
|
55
|
+
end # RbScheme
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RbScheme
|
2
|
+
module Extension
|
3
|
+
def self.initialize_compound!(evaluator)
|
4
|
+
base = File.dirname(File.expand_path(__FILE__))
|
5
|
+
definitions = File.join(base, "extension/procedures.scm")
|
6
|
+
|
7
|
+
File.open(definitions) do |io|
|
8
|
+
parser = Parser.new(io)
|
9
|
+
loop do
|
10
|
+
expr = parser.read_expr
|
11
|
+
break if expr.nil?
|
12
|
+
evaluator.vm_eval(expr)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end # Extension
|
17
|
+
end # RbScheme
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RbScheme
|
2
|
+
module Global
|
3
|
+
@@global_table = {}
|
4
|
+
|
5
|
+
def self.defined?(key)
|
6
|
+
raise unless key.is_a? LSymbol
|
7
|
+
@@global_table.member?(key)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.put(key, value)
|
11
|
+
raise unless key.is_a? LSymbol
|
12
|
+
@@global_table[key] = value
|
13
|
+
value
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.get(key)
|
17
|
+
raise unless key.is_a? LSymbol
|
18
|
+
@@global_table[key]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.variables
|
22
|
+
@@global_table.keys
|
23
|
+
end
|
24
|
+
end # Global
|
25
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module RbScheme
|
2
|
+
module Helpers
|
3
|
+
# Constructor
|
4
|
+
def cons(car, cdr)
|
5
|
+
LCell.new(car, cdr)
|
6
|
+
end
|
7
|
+
|
8
|
+
def acons(key, val, cdr)
|
9
|
+
cons(cons(key, val), cdr)
|
10
|
+
end
|
11
|
+
|
12
|
+
def list(*args)
|
13
|
+
args.any? ? convert_to_list(args) : LCell.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def convert_to_list(array)
|
17
|
+
result = list
|
18
|
+
array.reverse_each do |e|
|
19
|
+
result = cons(e, result)
|
20
|
+
end
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
def boolean(value)
|
25
|
+
value ? LTrue.instance : LFalse.instance
|
26
|
+
end
|
27
|
+
|
28
|
+
def check_length!(lst, n, name)
|
29
|
+
c = lst.count
|
30
|
+
unless c == n
|
31
|
+
raise ArgumentError, "#{name}: wrong number of arguments(given #{c}, expected #{n})"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def check_min_length!(lst, min, name)
|
36
|
+
c = lst.count
|
37
|
+
unless c >= min
|
38
|
+
raise ArgumentError, "#{name}: wrong number of arguments(given #{c}, expected #{min}..)"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end # RbScheme
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module RbScheme
|
4
|
+
class LInt
|
5
|
+
attr_accessor :value
|
6
|
+
|
7
|
+
def initialize(value)
|
8
|
+
@value = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(another)
|
12
|
+
return false unless another.is_a? LInt
|
13
|
+
value == another.value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class LCell
|
18
|
+
include Enumerable
|
19
|
+
|
20
|
+
attr_accessor :car, :cdr
|
21
|
+
|
22
|
+
def initialize(car = nil, cdr = nil)
|
23
|
+
@car = car
|
24
|
+
@cdr = cdr
|
25
|
+
end
|
26
|
+
|
27
|
+
def each
|
28
|
+
list = self
|
29
|
+
until list.null?
|
30
|
+
yield(list.car)
|
31
|
+
list = list.cdr
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def null?
|
36
|
+
car == nil && cdr == nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def cadr
|
40
|
+
@cdr.car
|
41
|
+
end
|
42
|
+
|
43
|
+
def cddr
|
44
|
+
@cdr.cdr
|
45
|
+
end
|
46
|
+
|
47
|
+
def caddr
|
48
|
+
@cdr.cdr.car
|
49
|
+
end
|
50
|
+
|
51
|
+
def cadddr
|
52
|
+
@cdr.cdr.cdr.car
|
53
|
+
end
|
54
|
+
|
55
|
+
def list?
|
56
|
+
cdr = @cdr
|
57
|
+
loop do
|
58
|
+
return false unless LCell === cdr
|
59
|
+
return true if cdr.null?
|
60
|
+
cdr = cdr.cdr
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def ==(another)
|
65
|
+
l1 = self
|
66
|
+
l2 = another
|
67
|
+
loop do
|
68
|
+
if l1.is_a?(LCell) && l2.is_a?(LCell)
|
69
|
+
return false unless l1.car == l2.car
|
70
|
+
l1 = l1.cdr
|
71
|
+
l2 = l2.cdr
|
72
|
+
elsif !l1.is_a?(LCell) && !l2.is_a?(LCell)
|
73
|
+
return l1 == l2
|
74
|
+
else
|
75
|
+
return false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
class LSymbol
|
83
|
+
attr_accessor :name
|
84
|
+
|
85
|
+
def initialize(name)
|
86
|
+
@name = name
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class LDot
|
91
|
+
include Singleton
|
92
|
+
end
|
93
|
+
|
94
|
+
class LCloseParen
|
95
|
+
include Singleton
|
96
|
+
end
|
97
|
+
|
98
|
+
class LTrue
|
99
|
+
include Singleton
|
100
|
+
end
|
101
|
+
|
102
|
+
class LFalse
|
103
|
+
include Singleton
|
104
|
+
end
|
105
|
+
end # RbScheme
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module RbScheme
|
2
|
+
class Parser
|
3
|
+
extend Forwardable
|
4
|
+
include Helpers
|
5
|
+
include Symbol
|
6
|
+
|
7
|
+
EOF = nil
|
8
|
+
|
9
|
+
def_delegator :@input, :getc
|
10
|
+
|
11
|
+
def self.read_expr(input)
|
12
|
+
new(input).read_expr
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(input)
|
16
|
+
@input = input
|
17
|
+
end
|
18
|
+
|
19
|
+
def read_expr
|
20
|
+
loop do
|
21
|
+
c = getc
|
22
|
+
case c
|
23
|
+
when /\s/
|
24
|
+
next
|
25
|
+
when EOF
|
26
|
+
return nil
|
27
|
+
when ';'
|
28
|
+
skip_line
|
29
|
+
next
|
30
|
+
when '('
|
31
|
+
return read_list
|
32
|
+
when ')'
|
33
|
+
return LCloseParen.instance
|
34
|
+
when '.'
|
35
|
+
return LDot.instance
|
36
|
+
when '\''
|
37
|
+
return read_quote
|
38
|
+
when /\d/
|
39
|
+
return LInt.new(read_number(c.to_i))
|
40
|
+
when negative_number_pred
|
41
|
+
return LInt.new(-read_number(c.to_i))
|
42
|
+
when '#'
|
43
|
+
return read_hash
|
44
|
+
when symbol_rp
|
45
|
+
return read_symbol(c)
|
46
|
+
else
|
47
|
+
raise "Unexpected character - #{c}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def peek
|
55
|
+
c = getc
|
56
|
+
@input.ungetc(c)
|
57
|
+
c
|
58
|
+
end
|
59
|
+
|
60
|
+
def reverse_list(lst)
|
61
|
+
return lst if lst.null?
|
62
|
+
lst.reduce(list) { |res, e| cons(e, res) }
|
63
|
+
end
|
64
|
+
|
65
|
+
def skip_line
|
66
|
+
loop do
|
67
|
+
c = getc
|
68
|
+
case c
|
69
|
+
when EOF, "\n"
|
70
|
+
return
|
71
|
+
when "\r"
|
72
|
+
getc if "\n" == peek
|
73
|
+
return
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def read_list
|
79
|
+
acc = list
|
80
|
+
loop do
|
81
|
+
obj = read_expr
|
82
|
+
raise "read_list: Unclosed parenthesis" if obj.nil?
|
83
|
+
|
84
|
+
case obj
|
85
|
+
when LCloseParen
|
86
|
+
return reverse_list(acc)
|
87
|
+
when LDot
|
88
|
+
last = read_expr
|
89
|
+
close = read_expr
|
90
|
+
if close.nil? || !(LCloseParen === close)
|
91
|
+
raise "read_list: Unclosed parenthesis"
|
92
|
+
end
|
93
|
+
if acc.null?
|
94
|
+
raise "read_list: dotted list must have car"
|
95
|
+
end
|
96
|
+
|
97
|
+
return acc.reduce(last) { |res, e| cons(e, res) }
|
98
|
+
else
|
99
|
+
acc = cons(obj, acc)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def read_quote
|
105
|
+
sym = intern("quote")
|
106
|
+
list(sym, read_expr)
|
107
|
+
end
|
108
|
+
|
109
|
+
def read_number(value)
|
110
|
+
result = value
|
111
|
+
while /\d/ === peek
|
112
|
+
result = result * 10 + getc.to_i
|
113
|
+
end
|
114
|
+
result
|
115
|
+
end
|
116
|
+
|
117
|
+
def read_hash
|
118
|
+
c = getc
|
119
|
+
case c
|
120
|
+
when 't'
|
121
|
+
LTrue.instance
|
122
|
+
when 'f'
|
123
|
+
LFalse.instance
|
124
|
+
else
|
125
|
+
raise "Unexpected hash literal #{c}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def read_symbol(first_char)
|
130
|
+
result = first_char
|
131
|
+
while symbol_rp === peek
|
132
|
+
result += getc
|
133
|
+
end
|
134
|
+
intern(result)
|
135
|
+
end
|
136
|
+
|
137
|
+
def negative_number_pred
|
138
|
+
Proc.new {|c| '-' == c && /\d/ === peek}
|
139
|
+
end
|
140
|
+
|
141
|
+
def symbol_rp
|
142
|
+
allowed = '~!@$%^&*-_=+:/?<>'
|
143
|
+
Regexp.new("[A-Za-z0-9#{Regexp.escape(allowed)}]")
|
144
|
+
end
|
145
|
+
end # Parser
|
146
|
+
end # RbScheme
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module RbScheme
|
2
|
+
class Primitive
|
3
|
+
extend Forwardable
|
4
|
+
include Helpers
|
5
|
+
include Symbol
|
6
|
+
|
7
|
+
def_delegators :@printer, :print_lisp_object, :puts_lisp_object
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@printer = Printer.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize_vm_primitive!
|
14
|
+
put_primitive_proc("+", lambda do |*nums|
|
15
|
+
sum = 0
|
16
|
+
nums.each do |n|
|
17
|
+
sum += n.value
|
18
|
+
end
|
19
|
+
LInt.new(sum)
|
20
|
+
end)
|
21
|
+
|
22
|
+
put_primitive_proc("-", lambda do |first, *rest|
|
23
|
+
result = first.value
|
24
|
+
if rest.any?
|
25
|
+
rest.each do |n|
|
26
|
+
result -= n.value
|
27
|
+
end
|
28
|
+
else
|
29
|
+
result = -result
|
30
|
+
end
|
31
|
+
LInt.new(result)
|
32
|
+
end)
|
33
|
+
|
34
|
+
put_primitive_proc("*", lambda do |*nums|
|
35
|
+
result = 1
|
36
|
+
nums.each do |n|
|
37
|
+
result *= n.value
|
38
|
+
end
|
39
|
+
LInt.new(result)
|
40
|
+
end)
|
41
|
+
|
42
|
+
put_primitive_proc("/", lambda do |first, *rest|
|
43
|
+
result = first.value
|
44
|
+
if rest.any?
|
45
|
+
rest.each do |n|
|
46
|
+
result /= n.value
|
47
|
+
end
|
48
|
+
else
|
49
|
+
result = 1 / result
|
50
|
+
end
|
51
|
+
LInt.new(result)
|
52
|
+
end)
|
53
|
+
|
54
|
+
put_primitive_proc("=", lambda do |n1, n2|
|
55
|
+
boolean(n1.value == n2.value)
|
56
|
+
end)
|
57
|
+
|
58
|
+
put_primitive_proc("<", lambda do |n1, n2|
|
59
|
+
boolean(n1.value < n2.value)
|
60
|
+
end)
|
61
|
+
|
62
|
+
put_primitive_proc(">", lambda do |n1, n2|
|
63
|
+
boolean(n1.value > n2.value)
|
64
|
+
end)
|
65
|
+
|
66
|
+
put_primitive_proc("null?", lambda do |lst|
|
67
|
+
boolean(lst.is_a?(LCell) && lst.null?)
|
68
|
+
end)
|
69
|
+
|
70
|
+
put_primitive_proc("cons", lambda do |e1, e2|
|
71
|
+
cons(e1, e2)
|
72
|
+
end)
|
73
|
+
|
74
|
+
put_primitive_proc("car", lambda do |c|
|
75
|
+
unless c.is_a?(LCell)
|
76
|
+
raise ArgumentError, "pair required, but got #{c}"
|
77
|
+
end
|
78
|
+
c.car
|
79
|
+
end)
|
80
|
+
|
81
|
+
put_primitive_proc("cdr", lambda do |c|
|
82
|
+
unless c.is_a?(LCell)
|
83
|
+
raise ArgumentError, "pair required, but got #{c}"
|
84
|
+
end
|
85
|
+
c.cdr
|
86
|
+
end)
|
87
|
+
|
88
|
+
put_primitive_proc("list", lambda do |*lst|
|
89
|
+
list(*lst)
|
90
|
+
end)
|
91
|
+
|
92
|
+
put_primitive_proc("display", lambda do |obj|
|
93
|
+
print_lisp_object(obj)
|
94
|
+
end)
|
95
|
+
|
96
|
+
put_primitive_proc("newline", lambda do
|
97
|
+
print("\n")
|
98
|
+
end)
|
99
|
+
|
100
|
+
put_primitive_proc("print", lambda do |obj|
|
101
|
+
puts_lisp_object(obj)
|
102
|
+
end)
|
103
|
+
# todo...
|
104
|
+
end
|
105
|
+
|
106
|
+
def put_primitive_proc(name, func)
|
107
|
+
prim = Procedure.new(name: name, func: func)
|
108
|
+
Global.put(intern(name), prim)
|
109
|
+
end
|
110
|
+
end # Primitive
|
111
|
+
end # RbScheme
|