shen-ruby 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.rspec +0 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +20 -0
- data/MIT_LICENSE.txt +26 -0
- data/README.md +94 -0
- data/bin/shen_test_suite.rb +9 -0
- data/bin/srrepl +23 -0
- data/lib/kl.rb +7 -0
- data/lib/kl/absvector.rb +12 -0
- data/lib/kl/compiler.rb +253 -0
- data/lib/kl/cons.rb +51 -0
- data/lib/kl/empty_list.rb +12 -0
- data/lib/kl/environment.rb +123 -0
- data/lib/kl/error.rb +4 -0
- data/lib/kl/internal_error.rb +7 -0
- data/lib/kl/lexer.rb +186 -0
- data/lib/kl/primitives/arithmetic.rb +60 -0
- data/lib/kl/primitives/assignments.rb +18 -0
- data/lib/kl/primitives/booleans.rb +17 -0
- data/lib/kl/primitives/error_handling.rb +13 -0
- data/lib/kl/primitives/generic_functions.rb +22 -0
- data/lib/kl/primitives/lists.rb +21 -0
- data/lib/kl/primitives/streams.rb +38 -0
- data/lib/kl/primitives/strings.rb +55 -0
- data/lib/kl/primitives/symbols.rb +17 -0
- data/lib/kl/primitives/time.rb +17 -0
- data/lib/kl/primitives/vectors.rb +30 -0
- data/lib/kl/reader.rb +40 -0
- data/lib/kl/trampoline.rb +14 -0
- data/lib/shen_ruby.rb +7 -0
- data/lib/shen_ruby/version.rb +3 -0
- data/shen-ruby.gemspec +26 -0
- data/shen/README.txt +17 -0
- data/shen/lib/shen_ruby/shen.rb +124 -0
- data/shen/license.txt +34 -0
- data/shen/release/benchmarks/N_queens.shen +45 -0
- data/shen/release/benchmarks/README.shen +14 -0
- data/shen/release/benchmarks/benchmarks.shen +56 -0
- data/shen/release/benchmarks/bigprog +2173 -0
- data/shen/release/benchmarks/br.shen +13 -0
- data/shen/release/benchmarks/einstein.shen +33 -0
- data/shen/release/benchmarks/heatwave.gif +0 -0
- data/shen/release/benchmarks/interpreter.shen +219 -0
- data/shen/release/benchmarks/picture.jpg +0 -0
- data/shen/release/benchmarks/plato.jpg +0 -0
- data/shen/release/benchmarks/powerset.shen +10 -0
- data/shen/release/benchmarks/prime.shen +10 -0
- data/shen/release/benchmarks/short.shen +129 -0
- data/shen/release/benchmarks/text.txt +68 -0
- data/shen/release/k_lambda/core.kl +1002 -0
- data/shen/release/k_lambda/declarations.kl +1021 -0
- data/shen/release/k_lambda/load.kl +94 -0
- data/shen/release/k_lambda/macros.kl +479 -0
- data/shen/release/k_lambda/prolog.kl +1309 -0
- data/shen/release/k_lambda/reader.kl +1058 -0
- data/shen/release/k_lambda/sequent.kl +556 -0
- data/shen/release/k_lambda/sys.kl +582 -0
- data/shen/release/k_lambda/t-star.kl +3493 -0
- data/shen/release/k_lambda/toplevel.kl +223 -0
- data/shen/release/k_lambda/track.kl +208 -0
- data/shen/release/k_lambda/types.kl +455 -0
- data/shen/release/k_lambda/writer.kl +108 -0
- data/shen/release/k_lambda/yacc.kl +280 -0
- data/shen/release/test_programs/Chap13/problems.txt +26 -0
- data/shen/release/test_programs/README.shen +53 -0
- data/shen/release/test_programs/TinyLispFunctions.txt +16 -0
- data/shen/release/test_programs/TinyTypes.shen +55 -0
- data/shen/release/test_programs/binary.shen +24 -0
- data/shen/release/test_programs/bubble_version_1.shen +28 -0
- data/shen/release/test_programs/bubble_version_2.shen +22 -0
- data/shen/release/test_programs/calculator.shen +21 -0
- data/shen/release/test_programs/cartprod.shen +23 -0
- data/shen/release/test_programs/change.shen +25 -0
- data/shen/release/test_programs/classes-defaults.shen +94 -0
- data/shen/release/test_programs/classes-inheritance.shen +100 -0
- data/shen/release/test_programs/classes-typed.shen +74 -0
- data/shen/release/test_programs/classes-untyped.shen +46 -0
- data/shen/release/test_programs/depth_.shen +14 -0
- data/shen/release/test_programs/einstein.shen +33 -0
- data/shen/release/test_programs/fruit_machine.shen +46 -0
- data/shen/release/test_programs/interpreter.shen +219 -0
- data/shen/release/test_programs/metaprog.shen +85 -0
- data/shen/release/test_programs/minim.shen +193 -0
- data/shen/release/test_programs/mutual.shen +11 -0
- data/shen/release/test_programs/n_queens.shen +45 -0
- data/shen/release/test_programs/newton_version_1.shen +33 -0
- data/shen/release/test_programs/newton_version_2.shen +24 -0
- data/shen/release/test_programs/parse.prl +14 -0
- data/shen/release/test_programs/parser.shen +52 -0
- data/shen/release/test_programs/powerset.shen +10 -0
- data/shen/release/test_programs/prime.shen +10 -0
- data/shen/release/test_programs/proof_assistant.shen +81 -0
- data/shen/release/test_programs/proplog_version_1.shen +25 -0
- data/shen/release/test_programs/proplog_version_2.shen +27 -0
- data/shen/release/test_programs/qmachine.shen +67 -0
- data/shen/release/test_programs/red-black.shen +55 -0
- data/shen/release/test_programs/search.shen +56 -0
- data/shen/release/test_programs/semantic_net.shen +44 -0
- data/shen/release/test_programs/spreadsheet.shen +35 -0
- data/shen/release/test_programs/stack.shen +27 -0
- data/shen/release/test_programs/streams.shen +20 -0
- data/shen/release/test_programs/strings.shen +59 -0
- data/shen/release/test_programs/structures-typed.shen +71 -0
- data/shen/release/test_programs/structures-untyped.shen +42 -0
- data/shen/release/test_programs/tests.shen +294 -0
- data/shen/release/test_programs/types.shen +11 -0
- data/shen/release/test_programs/whist.shen +240 -0
- data/shen/release/test_programs/yacc.shen +136 -0
- data/spec/kl/cons_spec.rb +12 -0
- data/spec/kl/environment_spec.rb +306 -0
- data/spec/kl/lexer_spec.rb +149 -0
- data/spec/kl/primitives/generic_functions_spec.rb +29 -0
- data/spec/kl/primitives/symbols_spec.rb +21 -0
- data/spec/kl/reader_spec.rb +36 -0
- data/spec/spec_helper.rb +2 -0
- metadata +189 -0
data/lib/kl/cons.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'kl/empty_list'
|
2
|
+
|
3
|
+
module Kl
|
4
|
+
class Cons
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
attr_reader :hd, :tl
|
8
|
+
|
9
|
+
def initialize(hd, tl)
|
10
|
+
@hd = hd
|
11
|
+
@tl = tl
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
other.kind_of?(Kl::Cons) && hd == other.hd && tl == other.tl
|
16
|
+
end
|
17
|
+
|
18
|
+
def each
|
19
|
+
cell = self
|
20
|
+
while !cell.kind_of? Kl::EmptyList
|
21
|
+
yield cell.hd
|
22
|
+
cell = cell.tl
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class << self
|
27
|
+
def list(array)
|
28
|
+
if array.empty?
|
29
|
+
Kl::EmptyList.instance
|
30
|
+
else
|
31
|
+
index = array.size - 1
|
32
|
+
head = new(array[index], Kl::EmptyList.instance)
|
33
|
+
index = index - 1
|
34
|
+
while index >= 0
|
35
|
+
head = new(array[index], head)
|
36
|
+
index = index - 1
|
37
|
+
end
|
38
|
+
head
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def list_to_string(f)
|
43
|
+
if f.kind_of? Kl::Cons
|
44
|
+
'(' + f.map {|x| list_to_string(x)}.join(' ') + ')'
|
45
|
+
else
|
46
|
+
f.to_s
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'kl/compiler'
|
2
|
+
require 'kl/primitives/booleans'
|
3
|
+
require 'kl/primitives/symbols'
|
4
|
+
require 'kl/primitives/strings'
|
5
|
+
require 'kl/primitives/assignments'
|
6
|
+
require 'kl/primitives/error_handling'
|
7
|
+
require 'kl/primitives/lists'
|
8
|
+
require 'kl/primitives/generic_functions'
|
9
|
+
require 'kl/primitives/vectors'
|
10
|
+
require 'kl/primitives/streams'
|
11
|
+
require 'kl/primitives/time'
|
12
|
+
require 'kl/primitives/arithmetic'
|
13
|
+
|
14
|
+
module Kl
|
15
|
+
class Environment
|
16
|
+
include ::Kl::Primitives::Booleans
|
17
|
+
include ::Kl::Primitives::Symbols
|
18
|
+
include ::Kl::Primitives::Strings
|
19
|
+
include ::Kl::Primitives::Assignments
|
20
|
+
include ::Kl::Primitives::ErrorHandling
|
21
|
+
include ::Kl::Primitives::Lists
|
22
|
+
include ::Kl::Primitives::GenericFunctions
|
23
|
+
include ::Kl::Primitives::Vectors
|
24
|
+
include ::Kl::Primitives::Streams
|
25
|
+
include ::Kl::Primitives::Time
|
26
|
+
include ::Kl::Primitives::Arithmetic
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@dump_code = false
|
30
|
+
@tramp_fn = @tramp_args = nil
|
31
|
+
@variables = {}
|
32
|
+
@eigenklass = class << self; self; end
|
33
|
+
@arity_cache = Hash.new { |h, k| h[k] = method(k).arity }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Trampoline-aware function application
|
37
|
+
def __apply(fn, args)
|
38
|
+
while fn
|
39
|
+
@tramp_fn = nil
|
40
|
+
if fn.kind_of? Symbol
|
41
|
+
if respond_to? fn
|
42
|
+
arity = @arity_cache[fn]
|
43
|
+
if arity == args.size || arity == -1
|
44
|
+
result = send(fn, *args)
|
45
|
+
elsif arity > args.size
|
46
|
+
# Partial application
|
47
|
+
result = method(fn).to_proc.curry.call(*args)
|
48
|
+
else
|
49
|
+
# Uncurrying. Apply fn to its expected number of arguments
|
50
|
+
# and hope that the result is a function that can be applied
|
51
|
+
# to the remainder.
|
52
|
+
fn = __apply(fn, args[0, arity])
|
53
|
+
args = args[arity..-1]
|
54
|
+
next
|
55
|
+
end
|
56
|
+
else
|
57
|
+
raise Kl::Error, "The function #{fn} is undefined"
|
58
|
+
end
|
59
|
+
else
|
60
|
+
arity = fn.arity
|
61
|
+
if arity == args.size || arity == -1
|
62
|
+
result = fn.call(*args)
|
63
|
+
elsif arity > args.size
|
64
|
+
result = fn.curry.call(*args)
|
65
|
+
else
|
66
|
+
# Uncurrying. Apply fn to its expected number of arguments
|
67
|
+
# and hope that the result is a function that can be applied
|
68
|
+
# to the remainder.
|
69
|
+
fn = __apply(fn, args[0, arity])
|
70
|
+
args = args[arity..-1]
|
71
|
+
next
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
if fn = @tramp_fn
|
76
|
+
# Bounce on the trampoline
|
77
|
+
args = @tramp_args
|
78
|
+
@tramp_args = nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
result
|
82
|
+
rescue SystemStackError
|
83
|
+
raise ::Kl::Error, 'maximum stack depth exceeded'
|
84
|
+
end
|
85
|
+
|
86
|
+
def __eval(form)
|
87
|
+
if @dump_code
|
88
|
+
puts "=" * 70
|
89
|
+
puts "Compiling:"
|
90
|
+
puts Kl::Cons.list_to_string(form)
|
91
|
+
puts '-----'
|
92
|
+
end
|
93
|
+
code = ::Kl::Compiler.compile(form, {}, true)
|
94
|
+
if @dump_code
|
95
|
+
puts code
|
96
|
+
puts "=" * 70
|
97
|
+
end
|
98
|
+
@tramp_fn = nil
|
99
|
+
result = instance_eval(code)
|
100
|
+
# Handle top-level trampolines
|
101
|
+
if @tramp_fn
|
102
|
+
fn = @tramp_fn
|
103
|
+
args = @tramp_args
|
104
|
+
@tramp_fn = nil
|
105
|
+
@tramp_args = nil
|
106
|
+
__apply(fn, args)
|
107
|
+
else
|
108
|
+
result
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class << self
|
113
|
+
def load_file(env, path)
|
114
|
+
File.open(path, 'r') do |file|
|
115
|
+
reader = Kl::Reader.new(file)
|
116
|
+
while form = reader.next
|
117
|
+
env.__eval(form)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/lib/kl/error.rb
ADDED
data/lib/kl/lexer.rb
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'kl/error'
|
3
|
+
|
4
|
+
module Kl
|
5
|
+
class Lexer
|
6
|
+
SYMBOL_CHARS = /[-=*\/+_?$!\@~><&%'#`;:{}a-zA-Z0-9.]/
|
7
|
+
|
8
|
+
# Syntax tokens
|
9
|
+
class OpenParen
|
10
|
+
include Singleton
|
11
|
+
end
|
12
|
+
class CloseParen
|
13
|
+
include Singleton
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(stream)
|
17
|
+
@stream = stream
|
18
|
+
@buffer = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def eof?
|
22
|
+
@buffer.empty? && @stream.eof?
|
23
|
+
end
|
24
|
+
|
25
|
+
def getc
|
26
|
+
if @buffer.empty?
|
27
|
+
@stream.getc
|
28
|
+
else
|
29
|
+
@buffer.pop
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def ungetc(c)
|
34
|
+
@buffer.push(c)
|
35
|
+
end
|
36
|
+
|
37
|
+
def next
|
38
|
+
drain_whitespace
|
39
|
+
unless eof?
|
40
|
+
c = getc
|
41
|
+
case c
|
42
|
+
when '('
|
43
|
+
OpenParen.instance
|
44
|
+
when ')'
|
45
|
+
CloseParen.instance
|
46
|
+
when '"'
|
47
|
+
consume_string
|
48
|
+
when SYMBOL_CHARS
|
49
|
+
ungetc(c)
|
50
|
+
consume_number_or_symbol
|
51
|
+
else
|
52
|
+
raise Error, "illegal character: #{c}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def drain_whitespace
|
59
|
+
until eof?
|
60
|
+
c = getc
|
61
|
+
if c =~ /\S/
|
62
|
+
ungetc(c)
|
63
|
+
break
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def consume_string
|
69
|
+
chars = []
|
70
|
+
loop do
|
71
|
+
raise Error, "unterminated string" if eof?
|
72
|
+
c = getc
|
73
|
+
break if c == '"'
|
74
|
+
chars << c
|
75
|
+
end
|
76
|
+
chars.join
|
77
|
+
end
|
78
|
+
|
79
|
+
def consume_number
|
80
|
+
# Shen allows multiple leading plusses and minuses. The plusses
|
81
|
+
# are ignored and an even number of minuses cancel each other.
|
82
|
+
# Thus '------+-7' is read as 7.
|
83
|
+
#
|
84
|
+
# The Shen reader parses "7." as the integer 7 and the symbol '.'
|
85
|
+
decimal_seen = false
|
86
|
+
negative = false
|
87
|
+
past_sign = false
|
88
|
+
chars = []
|
89
|
+
loop do
|
90
|
+
break if eof?
|
91
|
+
c = getc
|
92
|
+
if c =~ /\d/
|
93
|
+
past_sign = true
|
94
|
+
chars << c
|
95
|
+
elsif c == '.' && !decimal_seen
|
96
|
+
past_sign = true
|
97
|
+
decimal_seen = true
|
98
|
+
chars << c
|
99
|
+
elsif c == '+' && !past_sign
|
100
|
+
# ignore
|
101
|
+
elsif c == '-' && !past_sign
|
102
|
+
negative = !negative
|
103
|
+
else
|
104
|
+
ungetc c
|
105
|
+
break
|
106
|
+
end
|
107
|
+
end
|
108
|
+
chars.unshift('-') if negative
|
109
|
+
if chars.last == '.'
|
110
|
+
# A trailing decimal point is treated as part of the next
|
111
|
+
# token. Forget we saw it.
|
112
|
+
ungetc(chars.pop)
|
113
|
+
decimal_seen = false
|
114
|
+
end
|
115
|
+
str = chars.join
|
116
|
+
decimal_seen ? str.to_f : str.to_i
|
117
|
+
end
|
118
|
+
|
119
|
+
def consume_symbol
|
120
|
+
chars = []
|
121
|
+
loop do
|
122
|
+
break if eof?
|
123
|
+
c = getc
|
124
|
+
unless c =~ SYMBOL_CHARS
|
125
|
+
ungetc c
|
126
|
+
break
|
127
|
+
end
|
128
|
+
chars << c
|
129
|
+
end
|
130
|
+
str = chars.join
|
131
|
+
|
132
|
+
case str
|
133
|
+
when 'true'
|
134
|
+
true
|
135
|
+
when 'false'
|
136
|
+
false
|
137
|
+
else
|
138
|
+
str.to_sym
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def consume_number_or_symbol
|
143
|
+
# First drain optional leading signs
|
144
|
+
# Then drain optional decimal point
|
145
|
+
# If there is another character and it is a digit, then it
|
146
|
+
# is a number. Otherwise it is a symbol.
|
147
|
+
chars = []
|
148
|
+
loop do
|
149
|
+
break if eof?
|
150
|
+
c = getc
|
151
|
+
unless c =~ /[-+]/
|
152
|
+
ungetc c
|
153
|
+
break
|
154
|
+
end
|
155
|
+
chars << c
|
156
|
+
end
|
157
|
+
if eof?
|
158
|
+
chars.reverse.each {|x| ungetc x}
|
159
|
+
return consume_symbol
|
160
|
+
end
|
161
|
+
|
162
|
+
c = getc
|
163
|
+
chars << c
|
164
|
+
if c == '.'
|
165
|
+
if eof?
|
166
|
+
chars.reverse.each {|x| ungetc x}
|
167
|
+
return consume_symbol
|
168
|
+
end
|
169
|
+
c = getc
|
170
|
+
chars << c
|
171
|
+
chars.reverse.each {|x| ungetc x}
|
172
|
+
if c =~ /\d/
|
173
|
+
return consume_number
|
174
|
+
else
|
175
|
+
return consume_symbol
|
176
|
+
end
|
177
|
+
elsif c =~ /\d/
|
178
|
+
chars.reverse.each {|x| ungetc x}
|
179
|
+
return consume_number
|
180
|
+
else
|
181
|
+
chars.reverse.each {|x| ungetc x}
|
182
|
+
return consume_symbol
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Kl
|
2
|
+
module Primitives
|
3
|
+
module Arithmetic
|
4
|
+
def +(a, b)
|
5
|
+
raise ::Kl::Error, "#{a} is not a number" unless a.kind_of? Numeric
|
6
|
+
raise ::Kl::Error, "#{b} is not a number" unless b.kind_of? Numeric
|
7
|
+
a + b
|
8
|
+
end
|
9
|
+
|
10
|
+
def -(a, b)
|
11
|
+
raise ::Kl::Error, "#{a} is not a number" unless a.kind_of? Numeric
|
12
|
+
raise ::Kl::Error, "#{b} is not a number" unless b.kind_of? Numeric
|
13
|
+
a - b
|
14
|
+
end
|
15
|
+
|
16
|
+
def *(a, b)
|
17
|
+
raise ::Kl::Error, "#{a} is not a number" unless a.kind_of? Numeric
|
18
|
+
raise ::Kl::Error, "#{b} is not a number" unless b.kind_of? Numeric
|
19
|
+
a * b
|
20
|
+
end
|
21
|
+
|
22
|
+
def /(a, b)
|
23
|
+
raise ::Kl::Error, "#{a} is not a number" unless a.kind_of? Numeric
|
24
|
+
raise ::Kl::Error, "#{b} is not a number" unless b.kind_of? Numeric
|
25
|
+
if a.kind_of?(Fixnum) && b.kind_of?(Fixnum) && a % b != 0
|
26
|
+
a = a.to_f
|
27
|
+
end
|
28
|
+
a / b
|
29
|
+
end
|
30
|
+
|
31
|
+
def >(a, b)
|
32
|
+
raise ::Kl::Error, "#{a} is not a number" unless a.kind_of? Numeric
|
33
|
+
raise ::Kl::Error, "#{b} is not a number" unless b.kind_of? Numeric
|
34
|
+
a > b
|
35
|
+
end
|
36
|
+
|
37
|
+
def <(a, b)
|
38
|
+
raise ::Kl::Error, "#{a} is not a number" unless a.kind_of? Numeric
|
39
|
+
raise ::Kl::Error, "#{b} is not a number" unless b.kind_of? Numeric
|
40
|
+
a < b
|
41
|
+
end
|
42
|
+
|
43
|
+
def >=(a, b)
|
44
|
+
raise ::Kl::Error, "#{a} is not a number" unless a.kind_of? Numeric
|
45
|
+
raise ::Kl::Error, "#{b} is not a number" unless b.kind_of? Numeric
|
46
|
+
a >= b
|
47
|
+
end
|
48
|
+
|
49
|
+
def <=(a, b)
|
50
|
+
raise ::Kl::Error, "#{a} is not a number" unless a.kind_of? Numeric
|
51
|
+
raise ::Kl::Error, "#{b} is not a number" unless b.kind_of? Numeric
|
52
|
+
a <= b
|
53
|
+
end
|
54
|
+
|
55
|
+
def number?(a)
|
56
|
+
a.kind_of?(Numeric)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|