skeem 0.0.14 → 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +15 -1
- data/lib/skeem/interpreter.rb +16 -0
- data/lib/skeem/s_expr_builder.rb +0 -2
- data/lib/skeem/s_expr_nodes.rb +8 -17
- data/lib/skeem/standard/base.skm +25 -0
- data/lib/skeem/version.rb +1 -1
- data/skeem.gemspec +1 -0
- data/spec/skeem/interpreter_spec.rb +90 -13
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17793fa7c6e636874b0d7fe8515d745b7ff664af
|
4
|
+
data.tar.gz: f24f83ad056e1af1ecac866e2daa649c94e544e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64d9feba80f8ac945161c326a8783cdd34babf4f46bbec94020192ad7643a408be059c72e922bf452387dff253708d2b855d7adbac93b9bb9fff0cf4f354bbce
|
7
|
+
data.tar.gz: 00f745fc5abeaa2f1f6d83007b21bc0cb7a4fbc3974d79f71be89c19324857a543339328d406a04ca5dd130e36c789c163e3b982c3be87b879b1978fbb277492
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.0.15] - 2018-09-30
|
2
|
+
Recursive functions are now supported.
|
3
|
+
Interpreter pre-loads a Scheme file with standard procedures (zero?, positive?, negative?, abs)
|
4
|
+
|
5
|
+
### Added
|
6
|
+
- File `base.skm` with standard Scheme procedures `zero?`, `positive?`, `negative?`, `abs`
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
- Class `Interpreter#initialize` now execute a Scheme file containing a number of standard procedures.
|
10
|
+
- File `README.md` Added third demo snippet showing example of recursive lambda function.
|
11
|
+
|
12
|
+
### Fixed
|
13
|
+
- Method `SkmLambda#bind_locals` now execute any procedure call in argument list before executing the lambda itself.
|
14
|
+
|
1
15
|
## [0.0.14] - 2018-09-23
|
2
16
|
Added `lambda` (anonymous function). This is an initial implementation that doesn't support recursion yet.
|
3
17
|
|
data/README.md
CHANGED
@@ -55,7 +55,6 @@ At this stage, the gem consists of a bare-bones interpreter.
|
|
55
55
|
```
|
56
56
|
|
57
57
|
### Example 2 (Defining a function)
|
58
|
-
Remark: Skeem 0.0.14 doesn't support recursive functions yet.
|
59
58
|
|
60
59
|
```ruby
|
61
60
|
require 'skeem'
|
@@ -79,6 +78,21 @@ Remark: Skeem 0.0.14 doesn't support recursive functions yet.
|
|
79
78
|
result = schemer.run(scheme_code)
|
80
79
|
puts result.value # => 3
|
81
80
|
```
|
81
|
+
### Example 3 (Defining a recursive function)
|
82
|
+
```ruby
|
83
|
+
require 'skeem'
|
84
|
+
|
85
|
+
schemer = Skeem::Interpreter.new
|
86
|
+
scheme_code = <<-SKEEM
|
87
|
+
; Compute the factorial of 100
|
88
|
+
(define fact (lambda (n)
|
89
|
+
(if (<= n 1) 1 (* n (fact (- n 1))))))
|
90
|
+
(fact 100)
|
91
|
+
SKEEM
|
92
|
+
|
93
|
+
result = schemer.run(scheme_code)
|
94
|
+
puts result.value # => 9332621544394415268169923885626670049071596826438162146859296389521759999322991560894146397615651828625369792082722375825118521091686400000000000000000000000
|
95
|
+
```
|
82
96
|
|
83
97
|
Roadmap:
|
84
98
|
- Extend language support
|
data/lib/skeem/interpreter.rb
CHANGED
@@ -12,6 +12,7 @@ module Skeem
|
|
12
12
|
def initialize()
|
13
13
|
@runtime = Runtime.new(Environment.new)
|
14
14
|
add_primitives(runtime)
|
15
|
+
add_standard(runtime)
|
15
16
|
end
|
16
17
|
|
17
18
|
def run(source)
|
@@ -19,5 +20,20 @@ module Skeem
|
|
19
20
|
@ptree = parser.parse(source)
|
20
21
|
return @ptree.root.evaluate(runtime)
|
21
22
|
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def add_standard(aRuntime)
|
27
|
+
std_pathname = File.dirname(__FILE__) + '/standard/base.skm'
|
28
|
+
load_lib(std_pathname)
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_lib(aPathname)
|
32
|
+
lib_source = nil
|
33
|
+
File.open(aPathname, 'r') do |lib|
|
34
|
+
lib_source = lib.read
|
35
|
+
run(lib_source)
|
36
|
+
end
|
37
|
+
end
|
22
38
|
end # class
|
23
39
|
end # module
|
data/lib/skeem/s_expr_builder.rb
CHANGED
@@ -85,8 +85,6 @@ module Skeem
|
|
85
85
|
# rule('lambda_expression' => 'LPAREN LAMBDA formals body RPAREN').as 'lambda_expression'
|
86
86
|
def reduce_lambda_expression(_production, _range, _tokens, theChildren)
|
87
87
|
lmbd = SkmLambda.new(_range, theChildren[2], theChildren[3])
|
88
|
-
# puts lmbd.inspect
|
89
|
-
lmbd
|
90
88
|
end
|
91
89
|
|
92
90
|
# rule('formals' => 'LPAREN identifier_star RPAREN').as 'identifiers_as_formals'
|
data/lib/skeem/s_expr_nodes.rb
CHANGED
@@ -325,7 +325,11 @@ module Skeem
|
|
325
325
|
raise err, err_msg
|
326
326
|
end
|
327
327
|
procedure = aRuntime.environment.fetch(var_key.value)
|
328
|
+
# puts "## CALL(#{var_key.value}) ###################"
|
329
|
+
# puts operands.inspect
|
328
330
|
result = procedure.call(aRuntime, self)
|
331
|
+
# puts "## RETURN #{result.inspect}"
|
332
|
+
result
|
329
333
|
end
|
330
334
|
|
331
335
|
def inspect
|
@@ -367,21 +371,6 @@ module Skeem
|
|
367
371
|
result
|
368
372
|
end
|
369
373
|
end # class
|
370
|
-
|
371
|
-
=begin
|
372
|
-
<Skeem::SkmLambda:
|
373
|
-
@formals [<Skeem::SkmIdentifier: x>]
|
374
|
-
@definitions nil
|
375
|
-
@sequence <Skeem::SkmList:
|
376
|
-
<Skeem::SkmCondition:
|
377
|
-
@test <Skeem::ProcedureCall: <Skeem::SkmIdentifier: <>,
|
378
|
-
<Skeem::SkmList: <Skeem::SkmVariableReference: <Skeem::SkmIdentifier: x>>,
|
379
|
-
<Skeem::SkmInteger: 0>>>,
|
380
|
-
@consequent <Skeem::ProcedureCall:
|
381
|
-
<Skeem::SkmIdentifier: ->,
|
382
|
-
<Skeem::SkmList: <Skeem::SkmVariableReference: <Skeem::SkmIdentifier: x>>>>,
|
383
|
-
@alternate <Skeem::SkmVariableReference: <Skeem::SkmIdentifier: x>>>>>
|
384
|
-
=end
|
385
374
|
|
386
375
|
class SkmLambda < SkmElement
|
387
376
|
# @!attribute [r] formals
|
@@ -427,12 +416,14 @@ module Skeem
|
|
427
416
|
formals.each_with_index do |arg_name, index|
|
428
417
|
arg = arguments[index]
|
429
418
|
if arg.nil?
|
430
|
-
p aProcedureCall.operands.members
|
431
419
|
raise StandardError, "Unbound variable: '#{arg_name.value}'"
|
432
420
|
end
|
433
|
-
|
421
|
+
|
422
|
+
# IMPORTANT: execute procedure call in argument list now
|
423
|
+
arg = arg.evaluate(aRuntime) if arg.kind_of?(ProcedureCall)
|
434
424
|
a_def = SkmDefinition.new(position, arg_name, arg)
|
435
425
|
a_def.evaluate(aRuntime)
|
426
|
+
# puts "LOCAL #{a_def.inspect}"
|
436
427
|
end
|
437
428
|
end
|
438
429
|
|
@@ -0,0 +1,25 @@
|
|
1
|
+
; Standard R7RS procedures from section 6.2.6
|
2
|
+
(define zero?
|
3
|
+
(lambda (z)
|
4
|
+
(if (= z 0)
|
5
|
+
#t
|
6
|
+
#f)))
|
7
|
+
|
8
|
+
; Return true if x greater or equal to zero; false otherwise
|
9
|
+
(define positive?
|
10
|
+
(lambda (x)
|
11
|
+
(if (>= x 0)
|
12
|
+
#t
|
13
|
+
#f)))
|
14
|
+
|
15
|
+
(define negative?
|
16
|
+
(lambda (x)
|
17
|
+
(if (< x 0)
|
18
|
+
#t
|
19
|
+
#f)))
|
20
|
+
|
21
|
+
(define abs
|
22
|
+
(lambda (x)
|
23
|
+
(if (>= x 0)
|
24
|
+
x
|
25
|
+
(- x))))
|
data/lib/skeem/version.rb
CHANGED
data/skeem.gemspec
CHANGED
@@ -8,8 +8,8 @@ module Skeem
|
|
8
8
|
expect { Interpreter.new() }.not_to raise_error
|
9
9
|
end
|
10
10
|
|
11
|
-
it 'should
|
12
|
-
expect(subject.parser).
|
11
|
+
it 'should have a parser' do
|
12
|
+
expect(subject.parser).not_to be_nil
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'should have a runtime object' do
|
@@ -154,17 +154,17 @@ SKEEM
|
|
154
154
|
result = subject.run('(min 2 2)')
|
155
155
|
expect(result.value).to eq(2)
|
156
156
|
end
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
157
|
+
|
158
|
+
it 'should implement recursive functions' do
|
159
|
+
source = <<-SKEEM
|
160
|
+
; Example from R7RS section 4.1.5
|
161
|
+
(define fact (lambda (n)
|
162
|
+
(if (<= n 1) 1 (* n (fact (- n 1))))))
|
163
|
+
(fact 10)
|
164
|
+
SKEEM
|
165
|
+
result = subject.run(source)
|
166
|
+
expect(result.value).to eq(3628800)
|
167
|
+
end
|
168
168
|
end # context
|
169
169
|
|
170
170
|
context 'Built-in primitive procedures' do
|
@@ -377,5 +377,82 @@ SKEEM
|
|
377
377
|
end
|
378
378
|
end
|
379
379
|
end # context
|
380
|
+
|
381
|
+
context 'Built-in standard procedures' do
|
382
|
+
it 'should implement the zero? predicate' do
|
383
|
+
checks = [
|
384
|
+
['(zero? 3.1)', false],
|
385
|
+
['(zero? -3.1)', false],
|
386
|
+
['(zero? 0)', true],
|
387
|
+
['(zero? 0.0)', true],
|
388
|
+
['(zero? 3)', false],
|
389
|
+
['(zero? -3)', false]
|
390
|
+
]
|
391
|
+
checks.each do |(skeem_expr, expectation)|
|
392
|
+
result = subject.run(skeem_expr)
|
393
|
+
expect(result.value).to eq(expectation)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
it 'should implement the positive? predicate' do
|
398
|
+
checks = [
|
399
|
+
['(positive? 3.1)', true],
|
400
|
+
['(positive? -3.1)', false],
|
401
|
+
['(positive? 0)', true],
|
402
|
+
['(positive? 0.0)', true],
|
403
|
+
['(positive? 3)', true],
|
404
|
+
['(positive? -3)', false]
|
405
|
+
]
|
406
|
+
checks.each do |(skeem_expr, expectation)|
|
407
|
+
result = subject.run(skeem_expr)
|
408
|
+
expect(result.value).to eq(expectation)
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
it 'should implement the positive? predicate' do
|
413
|
+
checks = [
|
414
|
+
['(positive? 3.1)', true],
|
415
|
+
['(positive? -3.1)', false],
|
416
|
+
['(positive? 0)', true],
|
417
|
+
['(positive? 0.0)', true],
|
418
|
+
['(positive? 3)', true],
|
419
|
+
['(positive? -3)', false]
|
420
|
+
]
|
421
|
+
checks.each do |(skeem_expr, expectation)|
|
422
|
+
result = subject.run(skeem_expr)
|
423
|
+
expect(result.value).to eq(expectation)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
it 'should implement the negative? predicate' do
|
428
|
+
checks = [
|
429
|
+
['(negative? 3.1)', false],
|
430
|
+
['(negative? -3.1)', true],
|
431
|
+
['(negative? 0)', false],
|
432
|
+
['(negative? 0.0)', false],
|
433
|
+
['(negative? 3)', false],
|
434
|
+
['(negative? -3)', true]
|
435
|
+
]
|
436
|
+
checks.each do |(skeem_expr, expectation)|
|
437
|
+
result = subject.run(skeem_expr)
|
438
|
+
expect(result.value).to eq(expectation)
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
it 'should implement the abs function' do
|
443
|
+
checks = [
|
444
|
+
['(abs 3.1)', 3.1],
|
445
|
+
['(abs -3.1)', 3.1],
|
446
|
+
['(abs 0)', 0],
|
447
|
+
['(abs 0.0)', 0],
|
448
|
+
['(abs 3)', 3],
|
449
|
+
['(abs -7)', 7]
|
450
|
+
]
|
451
|
+
checks.each do |(skeem_expr, expectation)|
|
452
|
+
result = subject.run(skeem_expr)
|
453
|
+
expect(result.value).to eq(expectation)
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end # context
|
380
457
|
end # describe
|
381
458
|
end # module
|
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.0.
|
4
|
+
version: 0.0.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-09-
|
11
|
+
date: 2018-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|
@@ -95,6 +95,7 @@ files:
|
|
95
95
|
- lib/skeem/runtime.rb
|
96
96
|
- lib/skeem/s_expr_builder.rb
|
97
97
|
- lib/skeem/s_expr_nodes.rb
|
98
|
+
- lib/skeem/standard/base.skm
|
98
99
|
- lib/skeem/stoken.rb
|
99
100
|
- lib/skeem/tokenizer.rb
|
100
101
|
- lib/skeem/version.rb
|