skeem 0.2.17 → 0.2.18
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 +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
|