skeem 0.2.17 → 0.2.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +241 -13
- data/CHANGELOG.md +9 -0
- data/bin/skeem +4 -4
- data/lib/skeem/datum_dsl.rb +36 -35
- data/lib/skeem/grammar.rb +3 -3
- data/lib/skeem/interpreter.rb +1 -1
- data/lib/skeem/primitive/primitive_builder.rb +7 -12
- data/lib/skeem/primitive/primitive_procedure.rb +6 -5
- data/lib/skeem/runtime.rb +8 -10
- data/lib/skeem/s_expr_builder.rb +3 -13
- data/lib/skeem/s_expr_nodes.rb +34 -44
- data/lib/skeem/skeem_exception.rb +1 -0
- data/lib/skeem/skm_binding.rb +4 -5
- data/lib/skeem/skm_compound_datum.rb +2 -3
- data/lib/skeem/skm_element.rb +1 -1
- data/lib/skeem/skm_pair.rb +5 -2
- data/lib/skeem/skm_procedure_exec.rb +3 -0
- data/lib/skeem/skm_simple_datum.rb +12 -10
- data/lib/skeem/skm_unary_expression.rb +25 -26
- data/lib/skeem/tokenizer.rb +8 -4
- data/lib/skeem/version.rb +1 -1
- data/skeem.gemspec +4 -3
- data/spec/skeem/element_visitor_spec.rb +3 -1
- data/spec/skeem/interpreter_spec.rb +3 -1
- data/spec/skeem/lambda_spec.rb +4 -4
- data/spec/skeem/parser_spec.rb +2 -0
- data/spec/skeem/primitive/primitive_builder_spec.rb +5 -5
- data/spec/skeem/primitive/primitive_procedure_spec.rb +1 -1
- data/spec/skeem/skm_compound_datum_spec.rb +1 -1
- data/spec/skeem/skm_pair_spec.rb +5 -5
- data/spec/skeem/tokenizer_spec.rb +4 -2
- data/spec/spec_helper.rb +13 -10
- metadata +9 -9
data/lib/skeem/grammar.rb
CHANGED
@@ -9,7 +9,7 @@ module Skeem
|
|
9
9
|
# Official Small Scheme grammar is available at:
|
10
10
|
# https://bitbucket.org/cowan/r7rs/src/draft-10/rnrs/r7rs.pdf
|
11
11
|
# Names of grammar elements are based on the R7RS documentation
|
12
|
-
builder = Rley::
|
12
|
+
builder = Rley::grammar_builder do
|
13
13
|
# Delimiters, separators...
|
14
14
|
add_terminals('APOSTROPHE', 'COMMA', 'COMMA_AT_SIGN')
|
15
15
|
add_terminals('GRAVE_ACCENT', 'LPAREN', 'RPAREN')
|
@@ -22,7 +22,7 @@ module Skeem
|
|
22
22
|
|
23
23
|
# Keywords...
|
24
24
|
add_terminals('BEGIN', 'COND', 'DEFINE', 'DEFINE-SYNTAX', 'DO')
|
25
|
-
add_terminals('ELSE', 'IF', 'INCLUDE', 'LAMBDA', 'LET', '
|
25
|
+
add_terminals('ELSE', 'IF', 'INCLUDE', 'LAMBDA', 'LET', 'LET_STAR')
|
26
26
|
add_terminals('QUOTE', 'QUASIQUOTE', 'SET!', 'SYNTAX-RULES')
|
27
27
|
add_terminals('UNQUOTE', 'UNQUOTE-SPLICING')
|
28
28
|
|
@@ -108,7 +108,7 @@ module Skeem
|
|
108
108
|
rule('derived_expression' => 'LPAREN LET LPAREN binding_spec_star RPAREN body RPAREN').as 'short_let_form'
|
109
109
|
# TODO: implement "named let"
|
110
110
|
rule('derived_expression' => 'LPAREN LET IDENTIFIER LPAREN binding_spec_star RPAREN body RPAREN') # .as 'named_form'
|
111
|
-
rule('derived_expression' => 'LPAREN
|
111
|
+
rule('derived_expression' => 'LPAREN LET_STAR LPAREN binding_spec_star RPAREN body RPAREN').as 'let_star_form'
|
112
112
|
|
113
113
|
# As the R7RS grammar is too restrictive,
|
114
114
|
# the next rule was made more general than its standard counterpart
|
data/lib/skeem/interpreter.rb
CHANGED
@@ -234,20 +234,17 @@ module Skeem
|
|
234
234
|
if arglist.empty?
|
235
235
|
raw_result = reciprocal(raw_result)
|
236
236
|
else
|
237
|
-
# Ugly: Ruby version dependency: Rubies older than 2.4 have class Fixnum instead of Integer
|
238
|
-
int_class = (RUBY_VERSION[0..2] < '2.4') ? Fixnum : Integer
|
239
|
-
|
240
237
|
arglist.each do |elem|
|
241
238
|
elem_value = elem.value
|
242
239
|
case [raw_result.class, elem_value.class]
|
243
|
-
when [
|
240
|
+
when [Integer, Integer]
|
244
241
|
if raw_result.modulo(elem_value).zero?
|
245
242
|
raw_result /= elem_value
|
246
243
|
else
|
247
244
|
raw_result = Rational(raw_result, elem_value)
|
248
245
|
end
|
249
246
|
|
250
|
-
when [
|
247
|
+
when [Integer, Rational]
|
251
248
|
raw_result *= reciprocal(elem_value)
|
252
249
|
|
253
250
|
when [Rational, Rational]
|
@@ -827,8 +824,6 @@ module Skeem
|
|
827
824
|
cloned = arg.klone
|
828
825
|
if result.kind_of?(SkmEmptyList)
|
829
826
|
result = cloned
|
830
|
-
elsif result.kind_of?(SkmEmptyList)
|
831
|
-
result = SkmPair.new(arg, SkmEmptyList.instance)
|
832
827
|
else
|
833
828
|
result.append_list(cloned)
|
834
829
|
end
|
@@ -945,7 +940,7 @@ module Skeem
|
|
945
940
|
break
|
946
941
|
end
|
947
942
|
pair = pair.cdr
|
948
|
-
break unless pair
|
943
|
+
break unless pair.kind_of?(SkmPair)
|
949
944
|
end
|
950
945
|
end
|
951
946
|
|
@@ -967,7 +962,7 @@ module Skeem
|
|
967
962
|
break
|
968
963
|
end
|
969
964
|
pair = pair.cdr
|
970
|
-
break unless pair
|
965
|
+
break unless pair.kind_of?(SkmPair)
|
971
966
|
end
|
972
967
|
end
|
973
968
|
|
@@ -1142,7 +1137,7 @@ module Skeem
|
|
1142
1137
|
# Error: assertion failed: (> 1 2)
|
1143
1138
|
msg1 = "assertion failed on line #{pos.line}, column #{pos.column}"
|
1144
1139
|
msg2 = ", with #{arg_evaluated.inspect}"
|
1145
|
-
raise StandardError,
|
1140
|
+
raise StandardError, "Error: #{msg1}#{msg2}"
|
1146
1141
|
else
|
1147
1142
|
boolean(true)
|
1148
1143
|
end
|
@@ -1165,7 +1160,7 @@ module Skeem
|
|
1165
1160
|
# Non-standard procedure reserved for internal testing/debugging purposes.
|
1166
1161
|
def create_inspect(aRuntime)
|
1167
1162
|
primitive = lambda do |_runtime, arg_evaluated|
|
1168
|
-
$stderr.puts
|
1163
|
+
$stderr.puts "INSPECT>#{arg_evaluated.inspect}"
|
1169
1164
|
Skeem::SkmUndefined.instance
|
1170
1165
|
end
|
1171
1166
|
define_primitive_proc(aRuntime, '_inspect', unary, primitive)
|
@@ -1233,7 +1228,7 @@ module Skeem
|
|
1233
1228
|
else
|
1234
1229
|
msg2 = "but got #{argument.class}"
|
1235
1230
|
end
|
1236
|
-
raise StandardError, msg1
|
1231
|
+
raise StandardError, "#{msg1} #{msg2}"
|
1237
1232
|
end
|
1238
1233
|
|
1239
1234
|
def remaining_args(arglist, aRuntime)
|
@@ -42,10 +42,11 @@ module Skeem
|
|
42
42
|
check_actual_count(actuals)
|
43
43
|
# TODO: check that next line became useless
|
44
44
|
# aProcedureCall.operands_consumed = true
|
45
|
-
result = do_call(aRuntime, actuals)
|
45
|
+
# result = do_call(aRuntime, actuals)
|
46
46
|
# $stderr.puts " Result: #{result.inspect}"
|
47
47
|
# $stderr.puts "--- End of procedure #{identifier}"
|
48
|
-
result
|
48
|
+
# result
|
49
|
+
do_call(aRuntime, actuals)
|
49
50
|
end
|
50
51
|
|
51
52
|
def skm_equal?(other)
|
@@ -146,20 +147,20 @@ module Skeem
|
|
146
147
|
|
147
148
|
def error_lambda(message_suffix)
|
148
149
|
msg1 = "Primitive procedure '#{identifier.value}'"
|
149
|
-
raise StandardError, msg1
|
150
|
+
raise StandardError, "#{msg1} #{message_suffix}"
|
150
151
|
end
|
151
152
|
|
152
153
|
def discrepancy_arity_argument_count(arity_required, count_param, delta)
|
153
154
|
msg1 = "Discrepancy in primitive procedure '#{identifier.value}'"
|
154
155
|
msg2 = "between arity (#{arity_required}) + #{delta}"
|
155
156
|
msg3 = "and parameter count of lambda #{count_param}."
|
156
|
-
raise StandardError, msg1
|
157
|
+
raise StandardError, "#{msg1} #{msg2} #{msg3}"
|
157
158
|
end
|
158
159
|
|
159
160
|
def wrong_number_arguments(required, actual)
|
160
161
|
msg1 = "Wrong number of arguments for #<Procedure #{identifier.value}>"
|
161
162
|
msg2 = "(required at least #{required}, got #{actual})"
|
162
|
-
raise StandardError, msg1
|
163
|
+
raise StandardError, "#{msg1} #{msg2}"
|
163
164
|
end
|
164
165
|
end # class
|
165
166
|
end # module
|
data/lib/skeem/runtime.rb
CHANGED
@@ -124,16 +124,14 @@ module Skeem
|
|
124
124
|
private
|
125
125
|
|
126
126
|
def normalize_key(aKey)
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
result
|
127
|
+
case aKey
|
128
|
+
when String
|
129
|
+
aKey
|
130
|
+
when SkmVariableReference
|
131
|
+
aKey.child.value
|
132
|
+
else
|
133
|
+
aKey.evaluate(self).value
|
134
|
+
end
|
137
135
|
end
|
138
136
|
end # class
|
139
137
|
end # module
|
data/lib/skeem/s_expr_builder.rb
CHANGED
@@ -24,17 +24,6 @@ module Skeem
|
|
24
24
|
'STRING_LIT' => SkmString
|
25
25
|
}.freeze
|
26
26
|
|
27
|
-
# Create a new AST builder instance.
|
28
|
-
# @param theTokens [Array<Token>] The sequence of input tokens.
|
29
|
-
def initialize(theTokens)
|
30
|
-
super(theTokens)
|
31
|
-
end
|
32
|
-
|
33
|
-
# Notification that the parse tree construction is complete.
|
34
|
-
def done!
|
35
|
-
super
|
36
|
-
end
|
37
|
-
|
38
27
|
protected
|
39
28
|
|
40
29
|
def terminal2node
|
@@ -191,9 +180,10 @@ module Skeem
|
|
191
180
|
|
192
181
|
# rule('lambda_expression' => 'LPAREN LAMBDA formals body RPAREN').as 'lambda_expression'
|
193
182
|
def reduce_lambda_expression(_production, aRange, _tokens, theChildren)
|
194
|
-
lmbd = SkmLambdaRep.new(aRange, theChildren[2], theChildren[3])
|
183
|
+
# lmbd = SkmLambdaRep.new(aRange, theChildren[2], theChildren[3])
|
195
184
|
# $stderr.puts lmbd.inspect
|
196
|
-
lmbd
|
185
|
+
# lmbd
|
186
|
+
SkmLambdaRep.new(aRange, theChildren[2], theChildren[3])
|
197
187
|
end
|
198
188
|
|
199
189
|
# rule('formals' => 'LPAREN identifier_star RPAREN').as 'fixed_arity_formals'
|
data/lib/skeem/s_expr_nodes.rb
CHANGED
@@ -103,8 +103,8 @@ module Skeem
|
|
103
103
|
end
|
104
104
|
|
105
105
|
def inspect
|
106
|
-
result = inspect_prefix
|
107
|
-
result <<
|
106
|
+
result = +"#{inspect_prefix}#{operator.inspect}, "
|
107
|
+
result << "@operands #{operands.inspect}#{inspect_suffix}"
|
108
108
|
result
|
109
109
|
end
|
110
110
|
|
@@ -127,12 +127,10 @@ module Skeem
|
|
127
127
|
|
128
128
|
callee = result
|
129
129
|
# callee = fetch_callee(aRuntime, result)
|
130
|
-
when Primitive::PrimitiveProcedure
|
130
|
+
when Primitive::PrimitiveProcedure, SkmLambda
|
131
131
|
callee = operator
|
132
132
|
when SkmLambdaRep
|
133
133
|
callee = operator.evaluate(aRuntime)
|
134
|
-
when SkmLambda
|
135
|
-
callee = operator
|
136
134
|
else
|
137
135
|
result = operator.evaluate(aRuntime)
|
138
136
|
if result.kind_of?(Primitive::PrimitiveProcedure)
|
@@ -225,9 +223,9 @@ module Skeem
|
|
225
223
|
end
|
226
224
|
|
227
225
|
def inspect
|
228
|
-
result = inspect_prefix
|
229
|
-
result <<
|
230
|
-
result <<
|
226
|
+
result = +"#{inspect_prefix}@test #{test.inspect}, "
|
227
|
+
result << "@consequent #{consequent.inspect}, "
|
228
|
+
result << "@alternate #{alternate.inspect}#{inspect_suffix}"
|
231
229
|
result
|
232
230
|
end
|
233
231
|
|
@@ -236,7 +234,6 @@ module Skeem
|
|
236
234
|
end
|
237
235
|
end # class
|
238
236
|
|
239
|
-
|
240
237
|
class SkmConditional < SkmMultiExpression
|
241
238
|
# An array of couples [test, sequence]
|
242
239
|
attr_reader :clauses
|
@@ -280,20 +277,23 @@ module Skeem
|
|
280
277
|
end
|
281
278
|
|
282
279
|
def inspect
|
283
|
-
result = inspect_prefix
|
280
|
+
result = "#{inspect_prefix}@test #{test.inspect} , "
|
284
281
|
result << "@clauses \n"
|
285
282
|
clauses.each do |(test, consequent)|
|
286
283
|
result << ' ' << test.inspect << ' ' << consequent.inspect << "\n"
|
287
284
|
end
|
288
|
-
result <<
|
285
|
+
result << "@alternate #{alternate.inspect}#{inspect_suffix}"
|
289
286
|
result
|
290
287
|
end
|
291
288
|
end # class
|
292
289
|
|
293
290
|
SkmArity = Struct.new(:low, :high) do
|
291
|
+
# rubocop: disable Style/NumericPredicate
|
292
|
+
|
294
293
|
def nullary?
|
295
294
|
low.zero? && high == 0
|
296
295
|
end
|
296
|
+
# rubocop: enable Style/NumericPredicate
|
297
297
|
|
298
298
|
def variadic?
|
299
299
|
high == '*'
|
@@ -446,6 +446,7 @@ module Skeem
|
|
446
446
|
attr_reader :update_steps
|
447
447
|
|
448
448
|
def initialize(aTest, doResult, theCommands, theUpdates)
|
449
|
+
super(nil)
|
449
450
|
@test = aTest
|
450
451
|
@do_result = doResult
|
451
452
|
@commands = theCommands
|
@@ -546,7 +547,6 @@ module Skeem
|
|
546
547
|
end
|
547
548
|
end # class
|
548
549
|
|
549
|
-
|
550
550
|
# Parse tree representation of a Lambda
|
551
551
|
# - Not bound to a frame (aka environment)
|
552
552
|
# - Knows the parse representation of its embedded definitions
|
@@ -629,9 +629,7 @@ module Skeem
|
|
629
629
|
variadic_part_raw = actuals.drop(required_arity)
|
630
630
|
variadic_part = variadic_part_raw.map do |actual|
|
631
631
|
case actual
|
632
|
-
when ProcedureCall
|
633
|
-
actual.evaluate(aRuntime)
|
634
|
-
when SkmQuotation
|
632
|
+
when ProcedureCall, SkmQuotation
|
635
633
|
actual.evaluate(aRuntime)
|
636
634
|
else
|
637
635
|
to_datum(actual)
|
@@ -697,9 +695,9 @@ module Skeem
|
|
697
695
|
|
698
696
|
def inspect_specific
|
699
697
|
result = +''
|
700
|
-
result <<
|
701
|
-
result <<
|
702
|
-
result <<
|
698
|
+
result << "@formals #{formals.inspect}, "
|
699
|
+
result << "@definitions #{definitions.inspect}, "
|
700
|
+
result << "@sequence #{sequence.inspect}#{inspect_suffix}"
|
703
701
|
|
704
702
|
result
|
705
703
|
end
|
@@ -715,6 +713,7 @@ module Skeem
|
|
715
713
|
def_delegators(:@representation, :formals, :definitions, :sequence)
|
716
714
|
|
717
715
|
def initialize(aRepresentation, aRuntime)
|
716
|
+
super(nil)
|
718
717
|
@representation = aRepresentation
|
719
718
|
@environment = aRuntime.environment
|
720
719
|
end
|
@@ -790,9 +789,7 @@ module Skeem
|
|
790
789
|
variadic_part_raw = actuals.drop(required_arity)
|
791
790
|
variadic_part = variadic_part_raw.map do |actual|
|
792
791
|
case actual
|
793
|
-
when ProcedureCall
|
794
|
-
actual.evaluate(aRuntime)
|
795
|
-
when SkmQuotation
|
792
|
+
when ProcedureCall, SkmQuotation
|
796
793
|
actual.evaluate(aRuntime)
|
797
794
|
else
|
798
795
|
to_datum(actual)
|
@@ -814,20 +811,17 @@ module Skeem
|
|
814
811
|
|
815
812
|
def evaluate_sequence(aRuntime)
|
816
813
|
result = nil
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
result = cmd.evaluate(aRuntime)
|
823
|
-
end
|
824
|
-
rescue NoMethodError => e
|
825
|
-
$stderr.puts inspect
|
826
|
-
$stderr.puts sequence.inspect
|
827
|
-
$stderr.puts cmd.inspect
|
828
|
-
raise e
|
829
|
-
end
|
814
|
+
sequence&.each do |cmd|
|
815
|
+
if cmd.kind_of?(SkmLambda)
|
816
|
+
result = cmd.dup_cond(aRuntime)
|
817
|
+
else
|
818
|
+
result = cmd.evaluate(aRuntime)
|
830
819
|
end
|
820
|
+
rescue NoMethodError => e
|
821
|
+
$stderr.puts inspect
|
822
|
+
$stderr.puts sequence.inspect
|
823
|
+
$stderr.puts cmd.inspect
|
824
|
+
raise e
|
831
825
|
end
|
832
826
|
|
833
827
|
result
|
@@ -835,22 +829,18 @@ module Skeem
|
|
835
829
|
|
836
830
|
def dup_cond(aRuntime)
|
837
831
|
if environment
|
838
|
-
|
832
|
+
self
|
839
833
|
else
|
840
834
|
twin = dup
|
841
835
|
twin.set_cond_environment(aRuntime.environment)
|
842
|
-
|
836
|
+
twin
|
843
837
|
end
|
844
|
-
|
845
|
-
result
|
846
838
|
end
|
847
839
|
|
848
840
|
def doppelganger(aRuntime)
|
849
841
|
twin = dup
|
850
842
|
twin.set_cond_environment(aRuntime.environment.dup)
|
851
|
-
|
852
|
-
|
853
|
-
result
|
843
|
+
twin
|
854
844
|
end
|
855
845
|
|
856
846
|
def set_cond_environment(theFrame)
|
@@ -917,9 +907,9 @@ module Skeem
|
|
917
907
|
result << "Parent environment #{environment.parent.object_id.to_s(16)}, "
|
918
908
|
result << environment.inspect
|
919
909
|
end
|
920
|
-
result <<
|
921
|
-
result <<
|
922
|
-
result <<
|
910
|
+
result << "@formals #{formals.inspect}, "
|
911
|
+
result << "@definitions #{definitions.inspect}, "
|
912
|
+
result << "@sequence #{sequence.inspect}#{inspect_suffix}"
|
923
913
|
|
924
914
|
result
|
925
915
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
# frozen_string_literal: true
|
data/lib/skeem/skm_binding.rb
CHANGED
@@ -17,10 +17,13 @@ module Skeem
|
|
17
17
|
# @param anIdentifier [SkmIdentifier] The variable name
|
18
18
|
# @param aValue [SkmElement] The value to bind to the variable.
|
19
19
|
def initialize(anIdentifier, aValue)
|
20
|
+
super(nil)
|
20
21
|
@variable = anIdentifier
|
21
22
|
@value = aValue
|
22
23
|
end
|
23
24
|
|
25
|
+
# rubocop: disable Style/NegatedIfElseCondition
|
26
|
+
|
24
27
|
def evaluate(aRuntime)
|
25
28
|
name = variable.evaluate(aRuntime)
|
26
29
|
|
@@ -42,6 +45,7 @@ module Skeem
|
|
42
45
|
binding_action(aRuntime, name, result)
|
43
46
|
result
|
44
47
|
end
|
48
|
+
# rubocop: enable Style/NegatedIfElseCondition
|
45
49
|
|
46
50
|
protected
|
47
51
|
|
@@ -59,7 +63,6 @@ module Skeem
|
|
59
63
|
end
|
60
64
|
end # class
|
61
65
|
|
62
|
-
|
63
66
|
class SkmUpdateBinding < SkmBinding
|
64
67
|
protected
|
65
68
|
|
@@ -71,10 +74,6 @@ module Skeem
|
|
71
74
|
class SkmDelayedUpdateBinding < SkmBinding
|
72
75
|
attr_reader :new_val
|
73
76
|
|
74
|
-
def initialize(anIdentifier, aValue)
|
75
|
-
super(anIdentifier, aValue)
|
76
|
-
end
|
77
|
-
|
78
77
|
def do_it!(aRuntime)
|
79
78
|
aRuntime.update_binding(variable, new_val)
|
80
79
|
end
|
@@ -22,13 +22,12 @@ module Skeem
|
|
22
22
|
def ==(other)
|
23
23
|
return true if equal?(other)
|
24
24
|
|
25
|
-
|
25
|
+
case other
|
26
26
|
when SkmCompoundDatum
|
27
27
|
self.class == other.class && members == other.members
|
28
28
|
when Array
|
29
29
|
members == other
|
30
30
|
end
|
31
|
-
result
|
32
31
|
end
|
33
32
|
|
34
33
|
alias eqv? equal?
|
@@ -70,7 +69,7 @@ module Skeem
|
|
70
69
|
|
71
70
|
def inspect_specific
|
72
71
|
result = +''
|
73
|
-
members.each { |elem| result << elem.inspect
|
72
|
+
members.each { |elem| result << "#{elem.inspect}, " }
|
74
73
|
result.sub!(/, $/, '')
|
75
74
|
result
|
76
75
|
end
|