skeem 0.2.09 → 0.2.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|