skeem 0.2.09 → 0.2.10
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/CHANGELOG.md +21 -0
- data/README.md +4 -2
- data/lib/skeem/datum_dsl.rb +17 -0
- data/lib/skeem/grammar.rb +3 -2
- data/lib/skeem/primitive/primitive_builder.rb +92 -13
- data/lib/skeem/s_expr_builder.rb +1 -0
- data/lib/skeem/skm_element.rb +8 -0
- data/lib/skeem/skm_simple_datum.rb +9 -3
- data/lib/skeem/standard/base.skm +28 -2
- data/lib/skeem/tokenizer.rb +19 -3
- data/lib/skeem/version.rb +1 -1
- data/spec/skeem/datum_dsl_spec.rb +21 -0
- data/spec/skeem/interpreter_spec.rb +76 -0
- data/spec/skeem/primitive/primitive_builder_spec.rb +75 -11
- data/spec/skeem/tokenizer_spec.rb +25 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 482a0c8f936db1cc3099e133dda1d42d685d5e3c7fe244bf98f0062ad1470bc1
|
4
|
+
data.tar.gz: e950b89cf3a4bc7971a5263e96f7a8dbb670d926b03601d2c4116ccc03b613ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8a6cde2cd049ccdf7ac4256b2313a462990f355790e6a23ab78febaa9f2377a39914f169c213e5ec1b2342e6bc611eeddae6f8f806ac3ecff713a356765403c
|
7
|
+
data.tar.gz: 6b9ea76b548deb643631515a755c431a30c915ad1db1dee609ceec1c03e17380d99f0fe33d1ab80a4eda4eec9de3a4acc55e03d6f8d4ebac800f650ad9695554
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
## [0.2.10] - 2019-06-15
|
2
|
+
- Skeem now supports rational numbers (fractions)
|
3
|
+
- Added procedures: `max`, `min`, `floor/`, `floor-quotient`, `floor-remainder`, `truncate/`, `truncate-quotient`,
|
4
|
+
`truncate-remainder`, `quotient`, `remainder`, `modulo`
|
5
|
+
|
6
|
+
### Added
|
7
|
+
- `DatumDSL#rational` conversion method
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
- `DatumDSL#to_datum(aLiteral): added conversion of literal rational into rational value.
|
11
|
+
- File `grammar.rb` Added new terminal RATIONAL and rule deriving number from rational
|
12
|
+
- File `primitive_builder.rb` Implemented primitive procedures floor/ and truncate/
|
13
|
+
- Class `SkmInteger` now inherits from `SkmRational` class
|
14
|
+
- File `base.skm` added implementation of `floor-quotient`, `floor-remainder`, `truncate-quotient`,
|
15
|
+
`truncate-remainder`, `quotient`, `remainder`, `modulo`
|
16
|
+
- Class Tokenizer updated to recognize rational numbers.
|
17
|
+
- Test suite file `base_tests.scm` expanded.
|
18
|
+
|
19
|
+
### Fixed
|
20
|
+
- File `primitive_builder.rb` Fixed and extended implementation of `/`procedure
|
21
|
+
|
1
22
|
## [0.2.09] - 2019-06-10
|
2
23
|
- New procedures: `complex?`, `exact-integer?`
|
3
24
|
- Support for `#| ... |#` block comments (including nesting)
|
data/README.md
CHANGED
@@ -174,6 +174,7 @@ Here are a few pointers for the Scheme programming language:
|
|
174
174
|
- Booleans: `#t`, `#true`, `#f`, `#false`
|
175
175
|
- Of the number hierarchy:
|
176
176
|
`real` (e.g. 2.718, 6.671e-11),
|
177
|
+
`rational` (e.g. 22/7, 1/137, -13/41)
|
177
178
|
`integer` (42, -3)
|
178
179
|
- Lists (quoted) : '(1 two "three")
|
179
180
|
- Strings: `"Hello, world."`
|
@@ -250,9 +251,10 @@ This section lists the implemented standard procedures
|
|
250
251
|
* `boolean?`, `and`, `or`, `not`
|
251
252
|
|
252
253
|
#### Numerical operations
|
253
|
-
* Number-level: `number?`, `complex?`, `real?`, `integer?`, `zero?`, `exact?`, `inexact?`, `exact-integer?` , `+`, `-`, `*`, `/`,
|
254
|
+
* Number-level: `number?`, `complex?`, `real?`, `rational?`, `integer?`, `zero?`, `exact?`, `inexact?`, `exact-integer?` , `+`, `-`, `*`, `/`,
|
254
255
|
`=`, `square`, `number->string`
|
255
|
-
* Real-level: `positive?`, `negative?`, `<`, `>`, `<=`, `>=`, `abs`, `floor-remainder`
|
256
|
+
* Real-level: `positive?`, `negative?`, `<`, `>`, `<=`, `>=`, `abs`, `max`, `min`, `floor/`, `floor-quotient`, `floor-remainder`, `truncate/`, `truncate-quotient`,
|
257
|
+
`truncate-remainder`, `quotient`, `remainder`, `modulo`
|
256
258
|
* Integer-level: `even?`, `odd?`
|
257
259
|
|
258
260
|
#### List procedures
|
data/lib/skeem/datum_dsl.rb
CHANGED
@@ -33,6 +33,19 @@ module Skeem
|
|
33
33
|
raise StandardError, aLiteral.inspect
|
34
34
|
end
|
35
35
|
end
|
36
|
+
|
37
|
+
def rational(aLiteral)
|
38
|
+
return aLiteral if aLiteral.kind_of?(SkmRational)
|
39
|
+
|
40
|
+
result = case aLiteral
|
41
|
+
when Rational
|
42
|
+
SkmRational.create(aLiteral)
|
43
|
+
when /^[+-]?\d+\/\d+$/
|
44
|
+
SkmRational.create(Rational(aLiteral))
|
45
|
+
else
|
46
|
+
raise StandardError, aLiteral.inspect
|
47
|
+
end
|
48
|
+
end
|
36
49
|
|
37
50
|
def real(aLiteral)
|
38
51
|
return aLiteral if aLiteral.kind_of?(SkmReal)
|
@@ -112,6 +125,8 @@ module Skeem
|
|
112
125
|
aLiteral.map { |elem| to_datum(elem) }
|
113
126
|
when Integer
|
114
127
|
SkmInteger.create(aLiteral)
|
128
|
+
when Rational
|
129
|
+
SkmRational.create(aLiteral)
|
115
130
|
when Float
|
116
131
|
SkmReal.create(aLiteral)
|
117
132
|
when TrueClass, FalseClass
|
@@ -133,6 +148,8 @@ module Skeem
|
|
133
148
|
boolean(aLiteral)
|
134
149
|
elsif aLiteral =~ /^#f(?:alse)?|false$/
|
135
150
|
boolean(aLiteral)
|
151
|
+
elsif aLiteral =~ /^[+-]?\d+\/\d+$/
|
152
|
+
rational(aLiteral)
|
136
153
|
elsif aLiteral =~ /^[+-]?\d+$/
|
137
154
|
integer(aLiteral)
|
138
155
|
elsif aLiteral =~ /^[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?$/
|
data/lib/skeem/grammar.rb
CHANGED
@@ -15,8 +15,8 @@ module Skeem
|
|
15
15
|
add_terminals('VECTOR_BEGIN')
|
16
16
|
|
17
17
|
# Literal values...
|
18
|
-
add_terminals('BOOLEAN', 'INTEGER', '
|
19
|
-
add_terminals('STRING_LIT', 'IDENTIFIER')
|
18
|
+
add_terminals('BOOLEAN', 'INTEGER', 'RATIONAL')
|
19
|
+
add_terminals('REAL', 'STRING_LIT', 'IDENTIFIER')
|
20
20
|
|
21
21
|
# Keywords...
|
22
22
|
add_terminals('BEGIN', 'COND', 'DEFINE', 'ELSE')
|
@@ -93,6 +93,7 @@ module Skeem
|
|
93
93
|
rule 'alternate' => 'expression'
|
94
94
|
rule 'alternate' => []
|
95
95
|
rule 'number' => 'INTEGER'
|
96
|
+
rule 'number' => 'RATIONAL'
|
96
97
|
rule 'number' => 'REAL'
|
97
98
|
rule('assignment' => 'LPAREN SET! IDENTIFIER expression RPAREN').as 'assignment'
|
98
99
|
rule('derived_expression' => 'LPAREN COND cond_clause_plus RPAREN').as 'cond_form'
|
@@ -51,7 +51,8 @@ module Skeem
|
|
51
51
|
create_minus(aRuntime)
|
52
52
|
create_multiply(aRuntime)
|
53
53
|
create_divide(aRuntime)
|
54
|
-
|
54
|
+
create_floor_slash(aRuntime)
|
55
|
+
create_truncate_slash(aRuntime)
|
55
56
|
end
|
56
57
|
|
57
58
|
def add_comparison(aRuntime)
|
@@ -63,12 +64,15 @@ module Skeem
|
|
63
64
|
create_gt(aRuntime)
|
64
65
|
create_lte(aRuntime)
|
65
66
|
create_gte(aRuntime)
|
67
|
+
create_max(aRuntime)
|
68
|
+
create_min(aRuntime)
|
66
69
|
end
|
67
70
|
|
68
71
|
def add_number_procedures(aRuntime)
|
69
72
|
create_object_predicate(aRuntime, 'number?')
|
70
73
|
create_object_predicate(aRuntime, 'complex?')
|
71
74
|
create_object_predicate(aRuntime, 'real?')
|
75
|
+
create_object_predicate(aRuntime, 'rational?')
|
72
76
|
create_object_predicate(aRuntime, 'integer?')
|
73
77
|
create_object_predicate(aRuntime, 'exact?')
|
74
78
|
create_number2string(aRuntime)
|
@@ -178,19 +182,47 @@ module Skeem
|
|
178
182
|
define_primitive_proc(aRuntime, '*', zero_or_more, primitive)
|
179
183
|
end
|
180
184
|
|
185
|
+
def reciprocal(aLiteral)
|
186
|
+
|
187
|
+
case aLiteral
|
188
|
+
when Integer
|
189
|
+
result = Rational(1, aLiteral)
|
190
|
+
when Rational
|
191
|
+
result = Rational(aLiteral.denominator, aLiteral.numerator)
|
192
|
+
else
|
193
|
+
result = 1 / aLiteral.to_f
|
194
|
+
end
|
195
|
+
|
196
|
+
result
|
197
|
+
end
|
181
198
|
|
182
199
|
def create_divide(aRuntime)
|
183
200
|
primitive = ->(_runtime, first_operand, arglist) do
|
184
201
|
raw_result = first_operand.value
|
185
202
|
if arglist.empty?
|
186
|
-
raw_result =
|
203
|
+
raw_result = reciprocal(raw_result)
|
187
204
|
else
|
205
|
+
# Ugly: Ruby version dependency: Rubies older than 2.4 have class Fixnum instead of Integer
|
206
|
+
int_class = (RUBY_VERSION[0..2] < "2.4") ? Fixnum : Integer
|
207
|
+
|
188
208
|
arglist.each do |elem|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
209
|
+
elem_value = elem.value
|
210
|
+
case [raw_result.class, elem_value.class]
|
211
|
+
when [int_class, int_class]
|
212
|
+
if raw_result.modulo(elem_value).zero?
|
213
|
+
raw_result = raw_result / elem_value
|
214
|
+
else
|
215
|
+
raw_result = Rational(raw_result, elem_value)
|
216
|
+
end
|
217
|
+
|
218
|
+
when [int_class, Rational]
|
219
|
+
raw_result = raw_result * reciprocal(elem_value)
|
220
|
+
|
221
|
+
when [Rational, Rational]
|
222
|
+
raw_result = raw_result * reciprocal(elem_value)
|
223
|
+
else
|
224
|
+
raw_result = raw_result.to_f
|
225
|
+
raw_result /= elem_value
|
194
226
|
end
|
195
227
|
end
|
196
228
|
end
|
@@ -200,13 +232,24 @@ module Skeem
|
|
200
232
|
define_primitive_proc(aRuntime, '/', one_or_more, primitive)
|
201
233
|
end
|
202
234
|
|
203
|
-
def
|
235
|
+
def create_floor_slash(aRuntime)
|
204
236
|
primitive = ->(_runtime, operand_1, operand_2) do
|
205
|
-
|
206
|
-
to_datum(
|
237
|
+
(quotient, modulus) = operand_1.value.divmod(operand_2.value)
|
238
|
+
SkmPair.new(to_datum(quotient), to_datum(modulus)) # improper list!
|
239
|
+
end
|
240
|
+
|
241
|
+
define_primitive_proc(aRuntime, 'floor/', binary, primitive)
|
242
|
+
end
|
243
|
+
|
244
|
+
def create_truncate_slash(aRuntime)
|
245
|
+
primitive = ->(_runtime, operand_1, operand_2) do
|
246
|
+
modulo_ = operand_1.value / operand_2.value
|
247
|
+
modulo_ += 1 if modulo_ < 0
|
248
|
+
remainder_ = operand_1.value.remainder(operand_2.value)
|
249
|
+
SkmPair.new(to_datum(modulo_), to_datum(remainder_)) # improper list!
|
207
250
|
end
|
208
251
|
|
209
|
-
define_primitive_proc(aRuntime, '
|
252
|
+
define_primitive_proc(aRuntime, 'truncate/', binary, primitive)
|
210
253
|
end
|
211
254
|
|
212
255
|
def core_eqv?(eval_arg1, eval_arg2)
|
@@ -309,7 +352,7 @@ module Skeem
|
|
309
352
|
|
310
353
|
define_primitive_proc(aRuntime, '>=', one_or_more, primitive)
|
311
354
|
end
|
312
|
-
|
355
|
+
|
313
356
|
def primitive_comparison(operator, _runtime, first_operand, arglist)
|
314
357
|
operands = [first_operand].concat(arglist)
|
315
358
|
result = true
|
@@ -317,7 +360,43 @@ module Skeem
|
|
317
360
|
result &&= elem1.value.send(operator, elem2.value)
|
318
361
|
end
|
319
362
|
|
320
|
-
boolean(result)
|
363
|
+
boolean(result)
|
364
|
+
end
|
365
|
+
|
366
|
+
def create_max(aRuntime)
|
367
|
+
primitive = ->(_runtime, first_operand, arglist) do
|
368
|
+
if arglist.empty?
|
369
|
+
result = first_operand
|
370
|
+
else
|
371
|
+
arr = arglist.to_a
|
372
|
+
arr.unshift(first_operand)
|
373
|
+
result = arr.max do |a, b|
|
374
|
+
a.value <=> b.value if a.real? && b.real?
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
result
|
379
|
+
end
|
380
|
+
|
381
|
+
define_primitive_proc(aRuntime, 'max', one_or_more, primitive)
|
382
|
+
end
|
383
|
+
|
384
|
+
def create_min(aRuntime)
|
385
|
+
primitive = ->(_runtime, first_operand, arglist) do
|
386
|
+
if arglist.empty?
|
387
|
+
result = first_operand
|
388
|
+
else
|
389
|
+
arr = arglist.to_a
|
390
|
+
arr.unshift(first_operand)
|
391
|
+
result = arr.min do |a, b|
|
392
|
+
a.value <=> b.value if a.real? && b.real?
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
result
|
397
|
+
end
|
398
|
+
|
399
|
+
define_primitive_proc(aRuntime, 'min', one_or_more, primitive)
|
321
400
|
end
|
322
401
|
|
323
402
|
def create_number2string(aRuntime)
|
data/lib/skeem/s_expr_builder.rb
CHANGED
data/lib/skeem/skm_element.rb
CHANGED
@@ -132,15 +132,21 @@ module Skeem
|
|
132
132
|
false
|
133
133
|
end
|
134
134
|
end # class
|
135
|
-
|
136
|
-
class
|
137
|
-
def
|
135
|
+
|
136
|
+
class SkmRational < SkmReal
|
137
|
+
def rational?
|
138
138
|
true
|
139
139
|
end
|
140
140
|
|
141
141
|
def exact?
|
142
142
|
true
|
143
143
|
end
|
144
|
+
end # class
|
145
|
+
|
146
|
+
class SkmInteger < SkmRational
|
147
|
+
def integer?
|
148
|
+
true
|
149
|
+
end
|
144
150
|
end # class
|
145
151
|
|
146
152
|
class SkmString < SkmSimpleDatum
|
data/lib/skeem/standard/base.skm
CHANGED
@@ -36,8 +36,34 @@
|
|
36
36
|
(define inexact?
|
37
37
|
(lambda (z)
|
38
38
|
(not (exact? z))))
|
39
|
-
|
40
|
-
|
39
|
+
|
40
|
+
(define floor-quotient
|
41
|
+
(lambda (n1 n2)
|
42
|
+
(car (floor/ n1 n2))))
|
43
|
+
|
44
|
+
(define floor-remainder
|
45
|
+
(lambda (n1 n2)
|
46
|
+
(cdr (floor/ n1 n2))))
|
47
|
+
|
48
|
+
(define truncate-quotient
|
49
|
+
(lambda (n1 n2)
|
50
|
+
(car (truncate/ n1 n2))))
|
51
|
+
|
52
|
+
(define truncate-remainder
|
53
|
+
(lambda (n1 n2)
|
54
|
+
(cdr (truncate/ n1 n2))))
|
55
|
+
|
56
|
+
; For R5RS compatibility
|
57
|
+
(define quotient
|
58
|
+
(lambda (x y)
|
59
|
+
(truncate-quotient x y)))
|
60
|
+
|
61
|
+
; For R5RS compatibility
|
62
|
+
(define remainder
|
63
|
+
(lambda (x y)
|
64
|
+
(truncate-remainder x y)))
|
65
|
+
|
66
|
+
; For R5RS compatibility
|
41
67
|
(define modulo
|
42
68
|
(lambda (x y)
|
43
69
|
(floor-remainder x y)))
|
data/lib/skeem/tokenizer.rb
CHANGED
@@ -93,6 +93,8 @@ module Skeem
|
|
93
93
|
token = build_token(@@lexeme2name[lexeme], lexeme)
|
94
94
|
elsif (lexeme = scanner.scan(/#(?:(?:true)|(?:false)|(?:u8)|[\\\(tfeiodx]|(?:\d+[=#]))/))
|
95
95
|
token = cardinal_token(lexeme)
|
96
|
+
elsif (lexeme = scanner.scan(/[+-]?[0-9]+\/[0-9]+(?=\s|[|()";]|$)/))
|
97
|
+
token = build_token('RATIONAL', lexeme) # Decimal radix
|
96
98
|
elsif (lexeme = scanner.scan(/[+-]?[0-9]+(?:.0+)?(?=\s|[|()";]|$)/))
|
97
99
|
token = build_token('INTEGER', lexeme) # Decimal radix
|
98
100
|
elsif (lexeme = scanner.scan(/[+-]?[0-9]+(?:\.[0-9]*)?(?:(?:e|E)[+-]?[0-9]+)?/))
|
@@ -152,10 +154,10 @@ other literal data (section 2.4).
|
|
152
154
|
|
153
155
|
def build_token(aSymbolName, aLexeme, aFormat = :default)
|
154
156
|
begin
|
155
|
-
value = convert_to(aLexeme, aSymbolName, aFormat)
|
157
|
+
(value, symb) = convert_to(aLexeme, aSymbolName, aFormat)
|
156
158
|
col = scanner.pos - aLexeme.size - @line_start + 1
|
157
159
|
pos = Rley::Lexical::Position.new(@lineno, col)
|
158
|
-
token = Rley::Lexical::Token.new(value,
|
160
|
+
token = Rley::Lexical::Token.new(value, symb, pos)
|
159
161
|
rescue StandardError => exc
|
160
162
|
puts "Failing with '#{aSymbolName}' and '#{aLexeme}'"
|
161
163
|
raise exc
|
@@ -165,11 +167,15 @@ other literal data (section 2.4).
|
|
165
167
|
end
|
166
168
|
|
167
169
|
def convert_to(aLexeme, aSymbolName, aFormat)
|
170
|
+
symb = aSymbolName
|
168
171
|
case aSymbolName
|
169
172
|
when 'BOOLEAN'
|
170
173
|
value = to_boolean(aLexeme, aFormat)
|
171
174
|
when 'INTEGER'
|
172
175
|
value = to_integer(aLexeme, aFormat)
|
176
|
+
when 'RATIONAL'
|
177
|
+
value = to_rational(aLexeme, aFormat)
|
178
|
+
symb = 'INTEGER' if value.kind_of?(Integer)
|
173
179
|
when 'REAL'
|
174
180
|
value = to_real(aLexeme, aFormat)
|
175
181
|
when 'STRING_LIT'
|
@@ -180,7 +186,7 @@ other literal data (section 2.4).
|
|
180
186
|
value = aLexeme
|
181
187
|
end
|
182
188
|
|
183
|
-
return value
|
189
|
+
return [value, symb]
|
184
190
|
end
|
185
191
|
|
186
192
|
def to_boolean(aLexeme, aFormat)
|
@@ -196,6 +202,16 @@ other literal data (section 2.4).
|
|
196
202
|
return value
|
197
203
|
end
|
198
204
|
|
205
|
+
def to_rational(aLexeme, aFormat)
|
206
|
+
case aFormat
|
207
|
+
when :default
|
208
|
+
value = Rational(aLexeme)
|
209
|
+
value = value.numerator if value.denominator == 1
|
210
|
+
end
|
211
|
+
|
212
|
+
return value
|
213
|
+
end
|
214
|
+
|
199
215
|
def to_real(aLexeme, aFormat)
|
200
216
|
case aFormat
|
201
217
|
when :default
|
data/lib/skeem/version.rb
CHANGED
@@ -35,6 +35,15 @@ module Skeem
|
|
35
35
|
['+456', 456]
|
36
36
|
]
|
37
37
|
end
|
38
|
+
|
39
|
+
let(:rational_tests) do
|
40
|
+
[
|
41
|
+
[-Rational(2,3), -Rational(2, 3)],
|
42
|
+
[Rational(22, 7), Rational(22, 7)],
|
43
|
+
['-2/3', -Rational(2, 3)],
|
44
|
+
['+22/7', Rational(22, 7)]
|
45
|
+
]
|
46
|
+
end
|
38
47
|
|
39
48
|
let(:real_tests) do
|
40
49
|
[
|
@@ -83,6 +92,12 @@ module Skeem
|
|
83
92
|
expect(subject.integer(literal)).to eq(predicted)
|
84
93
|
end
|
85
94
|
end
|
95
|
+
|
96
|
+
it 'should convert rational literals' do
|
97
|
+
rational_tests.each do |(literal, predicted)|
|
98
|
+
expect(subject.rational(literal)).to eq(predicted)
|
99
|
+
end
|
100
|
+
end
|
86
101
|
|
87
102
|
it 'should convert real number literals' do
|
88
103
|
real_tests.each do |(literal, predicted)|
|
@@ -163,6 +178,12 @@ module Skeem
|
|
163
178
|
expect(subject.to_datum(literal)).to eq(predicted)
|
164
179
|
end
|
165
180
|
end
|
181
|
+
|
182
|
+
it 'should recognize & convert rational literals' do
|
183
|
+
rational_tests.each do |(literal, predicted)|
|
184
|
+
expect(subject.to_datum(literal)).to eq(predicted)
|
185
|
+
end
|
186
|
+
end
|
166
187
|
|
167
188
|
it 'should recognize & convert real number literals' do
|
168
189
|
real_tests.each do |(literal, predicted)|
|
@@ -825,6 +825,82 @@ SKEEM
|
|
825
825
|
expect(result).to eq(expectation)
|
826
826
|
end
|
827
827
|
end
|
828
|
+
|
829
|
+
it 'should implement the floor-quotient procedure' do
|
830
|
+
checks = [
|
831
|
+
['(floor-quotient 5 2)', 2],
|
832
|
+
['(floor-quotient -5 2)', -3],
|
833
|
+
['(floor-quotient 5 -2)', -3],
|
834
|
+
['(floor-quotient -5 -2)', 2]
|
835
|
+
]
|
836
|
+
checks.each do |(skeem_expr, expectation)|
|
837
|
+
result = subject.run(skeem_expr)
|
838
|
+
expect(result).to eq(expectation)
|
839
|
+
end
|
840
|
+
end
|
841
|
+
|
842
|
+
it 'should implement the floor-remainder (modulo) procedure' do
|
843
|
+
checks = [
|
844
|
+
['(floor-remainder 16 4)', 0],
|
845
|
+
['(floor-remainder 5 2)', 1],
|
846
|
+
['(floor-remainder -45.0 7)', 4.0],
|
847
|
+
['(floor-remainder 10.0 -3.0)', -2.0],
|
848
|
+
['(floor-remainder -17 -9)', -8],
|
849
|
+
['(modulo 16 4)', 0],
|
850
|
+
['(modulo 5 2)', 1],
|
851
|
+
['(modulo -45.0 7)', 4.0],
|
852
|
+
['(modulo 10.0 -3.0)', -2.0],
|
853
|
+
['(modulo -17 -9)', -8]
|
854
|
+
]
|
855
|
+
checks.each do |(skeem_expr, expectation)|
|
856
|
+
result = subject.run(skeem_expr)
|
857
|
+
expect(result).to eq(expectation)
|
858
|
+
end
|
859
|
+
end
|
860
|
+
|
861
|
+
it 'should implement the truncate-quotient procedure' do
|
862
|
+
checks = [
|
863
|
+
['(truncate-quotient 5 2)', 2],
|
864
|
+
['(truncate-quotient -5 2)', -2],
|
865
|
+
['(truncate-quotient 5 -2)', -2],
|
866
|
+
['(truncate-quotient -5 -2)', 2],
|
867
|
+
['(quotient 5 2)', 2],
|
868
|
+
['(quotient -5 2)', -2],
|
869
|
+
['(quotient 5 -2)', -2],
|
870
|
+
['(quotient -5 -2)', 2]
|
871
|
+
]
|
872
|
+
checks.each do |(skeem_expr, expectation)|
|
873
|
+
result = subject.run(skeem_expr)
|
874
|
+
expect(result).to eq(expectation)
|
875
|
+
end
|
876
|
+
end
|
877
|
+
|
878
|
+
it 'should implement the truncate-remainder procedure' do
|
879
|
+
checks = [
|
880
|
+
['(truncate-remainder 5 2)', 1],
|
881
|
+
['(truncate-remainder -5 2)', -1],
|
882
|
+
['(truncate-remainder 5 -2)', 1],
|
883
|
+
['(truncate-remainder -5 -2)', -1],
|
884
|
+
['(remainder 5 2)', 1],
|
885
|
+
['(remainder -5 2)', -1],
|
886
|
+
['(remainder 5 -2)', 1],
|
887
|
+
['(remainder -5 -2)', -1]
|
888
|
+
]
|
889
|
+
checks.each do |(skeem_expr, expectation)|
|
890
|
+
result = subject.run(skeem_expr)
|
891
|
+
expect(result).to eq(expectation)
|
892
|
+
end
|
893
|
+
end
|
894
|
+
|
895
|
+
it 'should implement the test-equal procedure' do
|
896
|
+
checks = [
|
897
|
+
["(test-equal (cons 1 2) (cons 1 2))", true]
|
898
|
+
]
|
899
|
+
checks.each do |(skeem_expr, expectation)|
|
900
|
+
result = subject.run(skeem_expr)
|
901
|
+
expect(result).to eq(expectation)
|
902
|
+
end
|
903
|
+
end
|
828
904
|
end # context
|
829
905
|
|
830
906
|
context 'Second-order functions' do
|
@@ -35,6 +35,8 @@ SKEEM
|
|
35
35
|
['(+)', 0], # '+' as nullary operator. Example from section 6.2.6
|
36
36
|
['(+ -3)', -3], # '+' as unary operator
|
37
37
|
['(+ 3 4)', 7], # '+' as binary operator. Example from section 4.1.3
|
38
|
+
['(+ 1/2 2/3)', Rational(7,6)],
|
39
|
+
['(+ 1/2 3)', Rational(7,2)],
|
38
40
|
['(+ 2 2.34)', 4.34]
|
39
41
|
].each do |(expr, predicted)|
|
40
42
|
result = subject.run(expr)
|
@@ -45,6 +47,7 @@ SKEEM
|
|
45
47
|
it 'should implement the minus operator' do
|
46
48
|
[
|
47
49
|
['(- 3)', -3], # '-' as unary operator (= sign change)
|
50
|
+
['(- -2/3)', Rational(2, 3)],
|
48
51
|
['(- 3 4)', -1], # '-' as binary operator. Example from section 6.2.6
|
49
52
|
['(- 3 4 5)', -6] # '-' as variadic operator. Example from section 6.2.6
|
50
53
|
].each do |(expr, predicted)|
|
@@ -58,6 +61,7 @@ SKEEM
|
|
58
61
|
['(*)', 1], # '*' as nullary operator. Example from section 6.2.6
|
59
62
|
['(* 4)', 4], # '*' as unary operator. Example from section 6.2.6
|
60
63
|
['(* 5 8)', 40], # '*' as binary operator.
|
64
|
+
['(* 2/3 5/7)', Rational(10, 21)],
|
61
65
|
['(* 2 3 4 5)', 120] # '*' as variadic operator.
|
62
66
|
].each do |(expr, predicted)|
|
63
67
|
result = subject.run(expr)
|
@@ -67,28 +71,45 @@ SKEEM
|
|
67
71
|
|
68
72
|
it 'should implement the division operator' do
|
69
73
|
[
|
70
|
-
['(/ 3)', 1
|
71
|
-
['(/ 3
|
72
|
-
['(/ 3 4
|
74
|
+
['(/ 3)', Rational(1, 3)], # '/' as unary operator (= inverse of argument)
|
75
|
+
['(/ 3/4)', Rational(4, 3)],
|
76
|
+
['(/ 3 4)', Rational(3, 4)], # '/' as binary operator.
|
77
|
+
['(/ 2/3 5/7)', Rational(14, 15)],
|
78
|
+
['(/ 3 4 5)', Rational(3, 20)] # '/' as variadic operator. Example from section 6.2.6
|
73
79
|
].each do |(expr, predicted)|
|
74
80
|
result = subject.run(expr)
|
75
81
|
expect(result).to eq(predicted)
|
76
82
|
end
|
83
|
+
|
84
|
+
result = subject.run('(/ 3 4.5)')
|
85
|
+
expect(result.value).to be_within(0.000001).of(0.66666667)
|
77
86
|
end
|
78
87
|
|
79
|
-
it 'should implement the floor
|
88
|
+
it 'should implement the floor/ procedure' do
|
80
89
|
checks = [
|
81
|
-
['(floor
|
82
|
-
['(floor-
|
83
|
-
['(floor
|
84
|
-
['(floor-
|
85
|
-
['(floor-remainder -17 -9)', -8]
|
90
|
+
['(floor/ 5 2)', [2, 1]], # Binary procedure.
|
91
|
+
['(floor/ -5 2)', [-3, 1]],
|
92
|
+
['(floor/ 5 -2)', [-3, -1]],
|
93
|
+
['(floor/ -5 -2)', [2, -1]]
|
86
94
|
]
|
87
95
|
checks.each do |(skeem_expr, expectation)|
|
88
96
|
result = subject.run(skeem_expr)
|
89
|
-
expect(result).to eq(expectation)
|
97
|
+
expect([result.car, result.cdr]).to eq(expectation)
|
90
98
|
end
|
91
99
|
end
|
100
|
+
|
101
|
+
it 'should implement the truncate/ procedure' do
|
102
|
+
checks = [
|
103
|
+
['(truncate/ 5 2)', [2, 1]], # Binary procedure.
|
104
|
+
['(truncate/ -5 2)', [-2, -1]],
|
105
|
+
['(truncate/ 5 -2)', [-2, 1]],
|
106
|
+
['(truncate/ -5 -2)', [2, -1]]
|
107
|
+
]
|
108
|
+
checks.each do |(skeem_expr, expectation)|
|
109
|
+
result = subject.run(skeem_expr)
|
110
|
+
expect([result.car, result.cdr]).to eq(expectation)
|
111
|
+
end
|
112
|
+
end
|
92
113
|
end # context
|
93
114
|
|
94
115
|
context 'Comparison operators' do
|
@@ -228,12 +249,37 @@ SKEEM
|
|
228
249
|
expect(result).to eq(expectation)
|
229
250
|
end
|
230
251
|
end
|
252
|
+
|
253
|
+
it 'should implement the max procedure' do
|
254
|
+
checks = [
|
255
|
+
['(max 3 4)', 4],
|
256
|
+
['(max 3.9 4)', 4],
|
257
|
+
['(max 4 -7 2 0 -6)', 4]
|
258
|
+
]
|
259
|
+
checks.each do |(skeem_expr, expectation)|
|
260
|
+
result = subject.run(skeem_expr)
|
261
|
+
expect(result).to eq(expectation)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
it 'should implement the min procedure' do
|
266
|
+
checks = [
|
267
|
+
['(min 3 4)', 3],
|
268
|
+
['(min 3.9 4)', 3.9],
|
269
|
+
['(min 4 -7 2 0 -6)', -7]
|
270
|
+
]
|
271
|
+
checks.each do |(skeem_expr, expectation)|
|
272
|
+
result = subject.run(skeem_expr)
|
273
|
+
expect(result).to eq(expectation)
|
274
|
+
end
|
275
|
+
end
|
231
276
|
end # context
|
232
277
|
|
233
278
|
context 'Number procedures:' do
|
234
279
|
it 'should implement the number? predicate' do
|
235
280
|
checks = [
|
236
281
|
['(number? 3.1)', true],
|
282
|
+
['(number? 22/7)', true],
|
237
283
|
['(number? 3)', true],
|
238
284
|
['(number? "3")', false],
|
239
285
|
['(number? #t)', false]
|
@@ -247,6 +293,7 @@ SKEEM
|
|
247
293
|
it 'should implement the real? predicate' do
|
248
294
|
checks = [
|
249
295
|
['(real? 3.1)', true],
|
296
|
+
['(real? 22/7)', true],
|
250
297
|
['(real? 3)', true],
|
251
298
|
['(real? "3")', false],
|
252
299
|
['(real? #t)', false]
|
@@ -257,10 +304,26 @@ SKEEM
|
|
257
304
|
end
|
258
305
|
end
|
259
306
|
|
307
|
+
it 'should implement the rational? predicate' do
|
308
|
+
checks = [
|
309
|
+
['(rational? 3.1)', false],
|
310
|
+
['(rational? 3.0)', true],
|
311
|
+
['(rational? 22/7)', true],
|
312
|
+
['(rational? 3)', true],
|
313
|
+
['(rational? "3")', false],
|
314
|
+
['(rational? #t)', false]
|
315
|
+
]
|
316
|
+
checks.each do |(skeem_expr, expectation)|
|
317
|
+
result = subject.run(skeem_expr)
|
318
|
+
expect(result).to eq(expectation)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
260
322
|
it 'should implement the integer? predicate' do
|
261
323
|
checks = [
|
262
324
|
['(integer? 3.1)', false],
|
263
|
-
|
325
|
+
['(integer? 3.0)', true],
|
326
|
+
['(integer? 22/7)', false],
|
264
327
|
['(integer? 3)', true],
|
265
328
|
['(integer? "3")', false],
|
266
329
|
['(integer? #t)', false]
|
@@ -274,6 +337,7 @@ SKEEM
|
|
274
337
|
it 'should implement the number->string procedure' do
|
275
338
|
checks = [
|
276
339
|
['(number->string 3.4)', '3.4'],
|
340
|
+
['(number->string 22/7)', '22/7'],
|
277
341
|
['(number->string 1e2)', '100.0'],
|
278
342
|
['(number->string 1e-23)', '1.0e-23'],
|
279
343
|
['(number->string -7)', '-7']
|
@@ -82,6 +82,29 @@ module Skeem
|
|
82
82
|
end
|
83
83
|
end # context
|
84
84
|
|
85
|
+
context 'Rational literals recognition:' do
|
86
|
+
it 'should tokenize rational in default radix 10' do
|
87
|
+
tests = [
|
88
|
+
# couple [raw input, expected]
|
89
|
+
['1/2', Rational(1, 2)],
|
90
|
+
['-22/7', -Rational(22, 7)],
|
91
|
+
]
|
92
|
+
|
93
|
+
tests.each do |(input, prediction)|
|
94
|
+
subject.reinitialize(input)
|
95
|
+
token = subject.tokens.first
|
96
|
+
expect(token.terminal).to eq('RATIONAL')
|
97
|
+
expect(token.lexeme).to eq(prediction)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Special case: implicit promotion to integer
|
101
|
+
subject.reinitialize('8/4')
|
102
|
+
token = subject.tokens.first
|
103
|
+
expect(token.terminal).to eq('INTEGER')
|
104
|
+
expect(token.lexeme).to eq(2)
|
105
|
+
end
|
106
|
+
end # context
|
107
|
+
|
85
108
|
context 'Real number recognition:' do
|
86
109
|
it 'should tokenize real numbers' do
|
87
110
|
tests = [
|
@@ -211,7 +234,7 @@ module Skeem
|
|
211
234
|
expect(token.terminal).to eq('STRING_LIT')
|
212
235
|
expect(token.lexeme).to eq('Second text')
|
213
236
|
end
|
214
|
-
|
237
|
+
|
215
238
|
it 'should cope with nested block comments' do
|
216
239
|
input = '"First text" #| One #| Two |# comment #| Three |# |# "Second text"'
|
217
240
|
subject.reinitialize(input)
|
@@ -223,7 +246,7 @@ module Skeem
|
|
223
246
|
token = tokens[1]
|
224
247
|
expect(token.terminal).to eq('STRING_LIT')
|
225
248
|
expect(token.lexeme).to eq('Second text')
|
226
|
-
end
|
249
|
+
end
|
227
250
|
end
|
228
251
|
|
229
252
|
context 'Scanning Scheme sample code' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: skeem
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-06-
|
11
|
+
date: 2019-06-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|