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/skm_element.rb
CHANGED
data/lib/skeem/skm_pair.rb
CHANGED
@@ -4,7 +4,10 @@ require_relative 'skm_empty_list'
|
|
4
4
|
|
5
5
|
module Skeem
|
6
6
|
class SkmPair < SkmElement
|
7
|
+
# @return [SkmElement]
|
7
8
|
attr_accessor :car
|
9
|
+
|
10
|
+
# @return [SkmElement]
|
8
11
|
attr_accessor :cdr
|
9
12
|
|
10
13
|
alias first car
|
@@ -168,8 +171,8 @@ module Skeem
|
|
168
171
|
begin
|
169
172
|
result = clone_evaluate(aRuntime)
|
170
173
|
rescue NoMethodError => e
|
171
|
-
$stderr.puts
|
172
|
-
$stderr.puts
|
174
|
+
$stderr.puts "SkmPair#evaluate: #{inspect}"
|
175
|
+
$stderr.puts "SkmPair as Array: #{to_a.inspect}"
|
173
176
|
raise e
|
174
177
|
end
|
175
178
|
end
|
@@ -6,7 +6,10 @@ module Skeem
|
|
6
6
|
# Abstract class. Root of class hierarchy needed for Interpreter
|
7
7
|
# design pattern
|
8
8
|
class SkmSimpleDatum < SkmElement
|
9
|
+
# @return [Rley::Syntax::Token] token object corresponding to Skeem element
|
9
10
|
attr_reader :token
|
11
|
+
|
12
|
+
# @return [Object]
|
10
13
|
attr_reader :value
|
11
14
|
|
12
15
|
def initialize(aToken, aPosition)
|
@@ -37,13 +40,11 @@ module Skeem
|
|
37
40
|
def ==(other)
|
38
41
|
return true if equal?(other)
|
39
42
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
result
|
43
|
+
if other.kind_of?(SkmSimpleDatum)
|
44
|
+
value == other.value
|
45
|
+
else
|
46
|
+
value == other
|
47
|
+
end
|
47
48
|
end
|
48
49
|
|
49
50
|
alias eqv? ==
|
@@ -104,10 +105,12 @@ module Skeem
|
|
104
105
|
false
|
105
106
|
end
|
106
107
|
|
108
|
+
# rubocop: disable Style/NegatedIfElseCondition
|
109
|
+
|
107
110
|
def eqv?(other)
|
108
111
|
return true if equal?(other)
|
109
112
|
|
110
|
-
|
113
|
+
if other.kind_of?(SkmNumber)
|
111
114
|
if exact? != other.exact?
|
112
115
|
false
|
113
116
|
else
|
@@ -116,9 +119,8 @@ module Skeem
|
|
116
119
|
else
|
117
120
|
value == other
|
118
121
|
end
|
119
|
-
|
120
|
-
result
|
121
122
|
end
|
123
|
+
# rubocop: enable Style/NegatedIfElseCondition
|
122
124
|
end # class
|
123
125
|
|
124
126
|
class SkmReal < SkmNumber
|
@@ -76,8 +76,7 @@ module Skeem
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def quasiquote(aRuntime)
|
79
|
-
|
80
|
-
result
|
79
|
+
evaluate(aRuntime)
|
81
80
|
end
|
82
81
|
|
83
82
|
protected
|
@@ -115,7 +114,10 @@ module Skeem
|
|
115
114
|
class SkmBindingBlock < SkmUnaryExpression
|
116
115
|
alias body child
|
117
116
|
|
117
|
+
# @return [Symbol] One of: :let, :let_star
|
118
118
|
attr_reader :kind
|
119
|
+
|
120
|
+
# @return [Array<SkmBinding>]
|
119
121
|
attr_reader :bindings
|
120
122
|
|
121
123
|
def initialize(theKind, theBindings, aBody)
|
@@ -126,14 +128,15 @@ module Skeem
|
|
126
128
|
|
127
129
|
def evaluate(aRuntime)
|
128
130
|
aRuntime.push(SkmFrame.new(aRuntime.environment))
|
129
|
-
|
131
|
+
case kind
|
132
|
+
when :let
|
130
133
|
locals = bindings.map do |bnd|
|
131
134
|
SkmBinding.new(bnd.variable, bnd.value.evaluate(aRuntime))
|
132
135
|
end
|
133
136
|
locals.each do |bnd|
|
134
137
|
aRuntime.add_binding(bnd.variable.evaluate(aRuntime), bnd.value)
|
135
138
|
end
|
136
|
-
|
139
|
+
when :let_star
|
137
140
|
bindings.each do |bnd|
|
138
141
|
val = bnd.value.evaluate(aRuntime)
|
139
142
|
aRuntime.add_binding(bnd.variable.evaluate(aRuntime), val)
|
@@ -186,18 +189,16 @@ module Skeem
|
|
186
189
|
def eval_pair(aRuntime)
|
187
190
|
result = nil
|
188
191
|
sequence.to_a.each do |cmd|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
result = cmd.evaluate(aRuntime)
|
194
|
-
end
|
195
|
-
rescue NoMethodError => e
|
196
|
-
$stderr.puts inspect
|
197
|
-
$stderr.puts sequence.inspect
|
198
|
-
$stderr.puts cmd.inspect
|
199
|
-
raise e
|
192
|
+
if cmd.kind_of?(SkmLambda)
|
193
|
+
result = cmd.dup_cond(aRuntime)
|
194
|
+
else
|
195
|
+
result = cmd.evaluate(aRuntime)
|
200
196
|
end
|
197
|
+
rescue NoMethodError => e
|
198
|
+
$stderr.puts inspect
|
199
|
+
$stderr.puts sequence.inspect
|
200
|
+
$stderr.puts cmd.inspect
|
201
|
+
raise e
|
201
202
|
end
|
202
203
|
|
203
204
|
result
|
@@ -215,18 +216,16 @@ module Skeem
|
|
215
216
|
|
216
217
|
if sequence[:sequence].kind_of?(SkmPair)
|
217
218
|
sequence[:sequence].to_a.each do |cmd|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
result = cmd.evaluate(aRuntime)
|
223
|
-
end
|
224
|
-
rescue NoMethodError => e
|
225
|
-
$stderr.puts inspect
|
226
|
-
$stderr.puts sequence[:sequence].inspect
|
227
|
-
$stderr.puts cmd.inspect
|
228
|
-
raise e
|
219
|
+
if cmd.kind_of?(SkmLambda)
|
220
|
+
result = cmd.dup_cond(aRuntime)
|
221
|
+
else
|
222
|
+
result = cmd.evaluate(aRuntime)
|
229
223
|
end
|
224
|
+
rescue NoMethodError => e
|
225
|
+
$stderr.puts inspect
|
226
|
+
$stderr.puts sequence[:sequence].inspect
|
227
|
+
$stderr.puts cmd.inspect
|
228
|
+
raise e
|
230
229
|
end
|
231
230
|
else
|
232
231
|
result = sequence.evaluate(aRuntime)
|
data/lib/skeem/tokenizer.rb
CHANGED
@@ -16,8 +16,13 @@ module Skeem
|
|
16
16
|
# Delimiters: parentheses '(', ')'
|
17
17
|
# Separators: comma
|
18
18
|
class Tokenizer
|
19
|
+
# @return [StringScanner]
|
19
20
|
attr_reader(:scanner)
|
21
|
+
|
22
|
+
# @return [Integer] Current line number
|
20
23
|
attr_reader(:lineno)
|
24
|
+
|
25
|
+
# @return [Integer] Offset of start of current line
|
21
26
|
attr_reader(:line_start)
|
22
27
|
|
23
28
|
@@lexeme2name = {
|
@@ -53,7 +58,7 @@ module Skeem
|
|
53
58
|
SYNTAX-RULES
|
54
59
|
UNQUOTE
|
55
60
|
UNQUOTE-SPLICING
|
56
|
-
].map { |x| [x, x] }.to_h
|
61
|
+
].map { |x| [x, x.sub(/\*$/, '_STAR')] }.to_h
|
57
62
|
|
58
63
|
class ScanError < StandardError; end
|
59
64
|
|
@@ -162,11 +167,10 @@ other literal data (section 2.4).
|
|
162
167
|
if (lexeme = scanner.scan(/#\\/))
|
163
168
|
if (lexeme = scanner.scan(/(?:alarm|backspace|delete|escape|newline|null|return|space|tab)/))
|
164
169
|
token = build_token('CHAR', lexeme, :name)
|
165
|
-
elsif (lexeme = scanner.scan(/[^x]/))
|
166
|
-
token = build_token('CHAR', lexeme, :escaped)
|
167
170
|
elsif (lexeme = scanner.scan(/x[0-9a-fA-F]+/))
|
168
171
|
token = build_token('CHAR', lexeme, :hex_value)
|
169
|
-
|
172
|
+
else
|
173
|
+
lexeme = scanner.getch
|
170
174
|
token = build_token('CHAR', lexeme, :escaped)
|
171
175
|
end
|
172
176
|
end
|
data/lib/skeem/version.rb
CHANGED
data/skeem.gemspec
CHANGED
@@ -24,7 +24,8 @@ module PkgExtending
|
|
24
24
|
'lib/**/*.rb',
|
25
25
|
'lib/**/*.skm',
|
26
26
|
'spec/**/*.rb',
|
27
|
-
'spec/**/*.skm'
|
27
|
+
'spec/**/*.skm',
|
28
|
+
'spec/**/*.yml'
|
28
29
|
]
|
29
30
|
aPackage.files = file_list
|
30
31
|
aPackage.test_files = Dir['spec/**/*_spec.rb']
|
@@ -53,7 +54,7 @@ DESCR
|
|
53
54
|
SUMMARY
|
54
55
|
spec.homepage = 'https://github.com/famished-tiger/Skeem'
|
55
56
|
spec.license = 'MIT'
|
56
|
-
spec.required_ruby_version = '>= 2.
|
57
|
+
spec.required_ruby_version = '>= 2.5.0'
|
57
58
|
|
58
59
|
spec.bindir = 'bin'
|
59
60
|
spec.executables << 'skeem'
|
@@ -61,7 +62,7 @@ SUMMARY
|
|
61
62
|
PkgExtending.pkg_files(spec)
|
62
63
|
PkgExtending.pkg_documentation(spec)
|
63
64
|
# Runtime dependencies
|
64
|
-
spec.add_dependency 'rley', '~> 0.
|
65
|
+
spec.add_dependency 'rley', '~> 0.8.03'
|
65
66
|
|
66
67
|
# Development dependencies
|
67
68
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
@@ -16,7 +16,7 @@ module Skeem
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'could be initialized with a block argument' do
|
19
|
-
expect { Interpreter.new
|
19
|
+
expect { Interpreter.new(&:runtime) }.not_to raise_error
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'should have a parser' do
|
@@ -67,6 +67,7 @@ module Skeem
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
+
# rubocop: disable Style/ExponentialNotation
|
70
71
|
it 'should evaluate isolated real numbers' do
|
71
72
|
samples = [
|
72
73
|
['0.0', 0.0],
|
@@ -80,6 +81,7 @@ module Skeem
|
|
80
81
|
expect(result).to eq(predicted)
|
81
82
|
end
|
82
83
|
end
|
84
|
+
# rubocop: enable Style/ExponentialNotation
|
83
85
|
|
84
86
|
it 'should evaluate isolated strings' do
|
85
87
|
samples = [
|
data/spec/skeem/lambda_spec.rb
CHANGED
@@ -31,7 +31,7 @@ SKEEM
|
|
31
31
|
|
32
32
|
context 'Defining compound procedures:' do
|
33
33
|
it 'should accept the definition of simple procedure with arity 1' do
|
34
|
-
source = definition_set
|
34
|
+
source = "#{definition_set}\nsquare"
|
35
35
|
result = subject.run(source)
|
36
36
|
|
37
37
|
square = result.last
|
@@ -41,7 +41,7 @@ SKEEM
|
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'should accept the definition of simple procedure with arity 2' do
|
44
|
-
source = definition_set
|
44
|
+
source = "#{definition_set}\nsum-of-squares"
|
45
45
|
result = subject.run(source)
|
46
46
|
|
47
47
|
square = result.last
|
@@ -65,14 +65,14 @@ SKEEM
|
|
65
65
|
end
|
66
66
|
|
67
67
|
it 'should support the call to a simple procedure with arity 2' do
|
68
|
-
source = definition_set
|
68
|
+
source = "#{definition_set}\n(sum-of-squares 3 4)"
|
69
69
|
result = subject.run(source)
|
70
70
|
|
71
71
|
expect(result.last).to eq(25)
|
72
72
|
end
|
73
73
|
|
74
74
|
it 'should support the call to a nested lambda procedure' do
|
75
|
-
source = definition_set
|
75
|
+
source = "#{definition_set}\n(f 5)"
|
76
76
|
result = subject.run(source)
|
77
77
|
|
78
78
|
expect(result.last).to eq(136)
|
data/spec/skeem/parser_spec.rb
CHANGED
@@ -45,6 +45,7 @@ module Skeem
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
# rubocop: disable Style/ExponentialNotation
|
48
49
|
it 'should parse isolated real numbers' do
|
49
50
|
samples = [
|
50
51
|
['0.0', 0.0],
|
@@ -59,6 +60,7 @@ module Skeem
|
|
59
60
|
expect(ptree.root.value).to eq(predicted)
|
60
61
|
end
|
61
62
|
end
|
63
|
+
# rubocop: enable Style/ExponentialNotation
|
62
64
|
|
63
65
|
it 'should parse isolated strings' do
|
64
66
|
samples = [
|
@@ -654,8 +654,8 @@ SKEEM
|
|
654
654
|
["(append '(a b) '(c d))", array2list_ids(%w[a b c d])],
|
655
655
|
["(append '(a b) '(c) 'd)", array2list_ids(%w[a b c d])],
|
656
656
|
["(append '(a (b)) '((c)))", [SkmIdentifier.create('a'),
|
657
|
-
|
658
|
-
|
657
|
+
SkmPair.create_from_a(array2list_ids(['b'])),
|
658
|
+
SkmPair.create_from_a(array2list_ids(['c']))]],
|
659
659
|
["(append '() 'a)", SkmIdentifier.create('a')]
|
660
660
|
]
|
661
661
|
compare_to_predicted(checks) do |result, expectation|
|
@@ -919,8 +919,8 @@ SKEEM
|
|
919
919
|
result = subject.run(source)
|
920
920
|
expect(result).to be_kind_of(SkmVector)
|
921
921
|
expectation = [SkmInteger.create(0),
|
922
|
-
|
923
|
-
|
922
|
+
SkmPair.new(SkmString.create('Sue'), SkmPair.new(SkmString.create('Sue'), SkmEmptyList.instance)),
|
923
|
+
SkmString.create('Anna')]
|
924
924
|
expect(result).to eq(expectation)
|
925
925
|
|
926
926
|
source = <<-SKEEM
|
@@ -1010,7 +1010,7 @@ SKEEM
|
|
1010
1010
|
err = StandardError
|
1011
1011
|
msg1 = 'Error: assertion failed on line 3, column 4'
|
1012
1012
|
msg2 = 'with <Skeem::SkmBoolean: false>'
|
1013
|
-
expect { subject.run(source) }.to raise_error(err, msg1
|
1013
|
+
expect { subject.run(source) }.to raise_error(err, "#{msg1}, #{msg2}")
|
1014
1014
|
end
|
1015
1015
|
end # context
|
1016
1016
|
end # describe
|
@@ -149,7 +149,7 @@ module Skeem
|
|
149
149
|
expect(pproc.call(rtime, no_arg)).to eq(0)
|
150
150
|
|
151
151
|
many = [SkmString.create('foo'), SkmString.create('bar'),
|
152
|
-
|
152
|
+
SkmString.create('quux')]
|
153
153
|
expect(pproc.call(rtime, many)).to eq(3)
|
154
154
|
end
|
155
155
|
end # context
|
@@ -50,7 +50,7 @@ module Skeem
|
|
50
50
|
it 'should return its text representation' do
|
51
51
|
txt1 = '<Skeem::SkmCompoundDatum: <Skeem::SkmInteger: 1>,'
|
52
52
|
txt2 = '<Skeem::SkmInteger: 2>, <Skeem::SkmInteger: 3>>'
|
53
|
-
expect(subject.inspect).to eq(txt1
|
53
|
+
expect(subject.inspect).to eq("#{txt1} #{txt2}")
|
54
54
|
end
|
55
55
|
end # context
|
56
56
|
|
data/spec/skeem/skm_pair_spec.rb
CHANGED
@@ -37,7 +37,7 @@ module Skeem
|
|
37
37
|
|
38
38
|
context 'Provided services:' do
|
39
39
|
let(:runtime) { Runtime.new(SkmFrame.new) }
|
40
|
-
let(:
|
40
|
+
let(:list_length2) { SkmPair.new(integer(10), subject) }
|
41
41
|
let(:quirk_element) { double('three') }
|
42
42
|
let(:quirk_members) { [integer(10), quirk_element] }
|
43
43
|
|
@@ -71,7 +71,7 @@ module Skeem
|
|
71
71
|
expect(subject.length).to eq(1)
|
72
72
|
|
73
73
|
# Use a list of length 2
|
74
|
-
expect(
|
74
|
+
expect(list_length2.length).to eq(2)
|
75
75
|
end
|
76
76
|
|
77
77
|
it 'should respond false to `eqv?` message' do
|
@@ -113,7 +113,7 @@ module Skeem
|
|
113
113
|
expect(subject.to_a).to eq([sample_car])
|
114
114
|
|
115
115
|
# Use a list of length 2
|
116
|
-
expect(
|
116
|
+
expect(list_length2.to_a).to eq([integer(10), sample_car])
|
117
117
|
end
|
118
118
|
|
119
119
|
it 'should return the last pair of a proper list' do
|
@@ -133,7 +133,7 @@ module Skeem
|
|
133
133
|
|
134
134
|
it 'should return the last element of a list' do
|
135
135
|
expect(subject.last).to eq(sample_car)
|
136
|
-
expect(
|
136
|
+
expect(list_length2.last).to eq(sample_car)
|
137
137
|
end
|
138
138
|
|
139
139
|
it 'should append a new element to a list' do
|
@@ -227,7 +227,7 @@ module Skeem
|
|
227
227
|
expect(subject.inspect).to eq(predicted)
|
228
228
|
|
229
229
|
predicted = '<Skeem::SkmPair: <Skeem::SkmInteger: 10>, <Skeem::SkmInteger: 3>>'
|
230
|
-
expect(
|
230
|
+
expect(list_length2.inspect).to eq(predicted)
|
231
231
|
end
|
232
232
|
end # context
|
233
233
|
end # describe
|