skeem 0.0.24 → 0.0.25

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e1f6bf0ae4407a85eb404c02c2497964744c26b2
4
- data.tar.gz: f66450bf98cb129a7e40499bf589ef6454760c12
3
+ metadata.gz: bb979d01736d3b23101b56c2b7c21dff003eed46
4
+ data.tar.gz: 91dddadb24f3e6cc1aa146f7c5420d4d88708d56
5
5
  SHA512:
6
- metadata.gz: f07d638db941e45c54717ba99addfdb627ba9c76ace292905bb3fa3687888a42a633f51cc9dc6975d058f6995782e56f9a7439cc8e0a0086c1a4ba93be84040a
7
- data.tar.gz: 03b924927a3cf1e08780de16743403ab17ca68646e17f1f1b36bcfc58f3478cf403d09c2cf0626306f83dc60e6b06c4a9df57502500ca246361bbcc641833bc7
6
+ metadata.gz: 1a3db261dbca2bf57909b90de1db6897026eea3baa739e80a52c2ee045eee1be23d685d903757ed1fb1ca92daac80cbbcdc9fb015d7f6daeb7d275262f5952fe
7
+ data.tar.gz: 27f38b7d92bbe22f339de58945fd7dad7efc9b57ffc1257f626fd3d97b5bd063c3432eb124e28e75d67ff35f7bf75470af15ea8ddb633850d4b21b76e122012f
data/README.md CHANGED
@@ -129,6 +129,7 @@ Here are a few pointers for the Scheme programming language:
129
129
  - Lambda expressions
130
130
  - If conditionals
131
131
  - Definitions
132
+ - Assignments
132
133
 
133
134
  ### Standard syntactic forms
134
135
  #### define
@@ -155,6 +156,13 @@ __Syntax:__
155
156
  * (quote <datum\>)
156
157
  * '<datum\>
157
158
 
159
+ #### set!
160
+ __Purpose:__ Assign to an existing variable an expression/value to it.
161
+ __Syntax:__
162
+ * (set! <identifier\> <expression\>)
163
+
164
+
165
+
158
166
  ### Standard library
159
167
  This section lists the implemented standard procedures
160
168
 
@@ -170,10 +178,10 @@ This section lists the implemented standard procedures
170
178
  * `list?`, `null?`, `list`, `length`
171
179
 
172
180
  #### String procedures
173
- * `string?`, `string-append`, `string-length`, `string->symbol`,
181
+ * `string?`, `string=?`, `string-append`, `string-length`, `string->symbol`,
174
182
 
175
183
  #### Symbol procedures
176
- * `symbol?`
184
+ * `symbol?`, `symbol=?`
177
185
 
178
186
  #### Vector procedures
179
187
  * `vector?`, `vector`, `vector-length`, `vector-set!`
@@ -1,11 +1,13 @@
1
1
  require_relative 'primitive_procedure'
2
2
  require_relative '../datum_dsl'
3
+ # require_relative '../s_expr_nodes'
3
4
 
4
5
  module Skeem
5
6
  module Primitive
6
7
  module PrimitiveBuilder
7
8
  include DatumDSL
8
9
  def add_primitives(aRuntime)
10
+ add_binding(aRuntime)
9
11
  add_arithmetic(aRuntime)
10
12
  add_comparison(aRuntime)
11
13
  add_number_procedures(aRuntime)
@@ -40,6 +42,10 @@ module Skeem
40
42
  SkmArity.new(1, '*')
41
43
  end
42
44
 
45
+ def add_binding(aRuntime)
46
+ create_set!(aRuntime)
47
+ end
48
+
43
49
  def add_arithmetic(aRuntime)
44
50
  create_plus(aRuntime)
45
51
  create_minus(aRuntime)
@@ -72,6 +78,7 @@ module Skeem
72
78
 
73
79
  def add_string_procedures(aRuntime)
74
80
  create_object_predicate(aRuntime, 'string?')
81
+ create_string_equal(aRuntime)
75
82
  create_string_append(aRuntime)
76
83
  create_string_length(aRuntime)
77
84
  create_string2symbol(aRuntime)
@@ -102,6 +109,19 @@ module Skeem
102
109
  create_debug(aRuntime)
103
110
  end
104
111
 
112
+ def create_set!(aRuntime)
113
+ primitive = ->(runtime, var_ref, expr) do
114
+ if runtime.include?(var_ref.child)
115
+ redefinition = SkmDefinition.new(nil, var_ref.child, expr)
116
+ redefinition.evaluate(runtime)
117
+ else
118
+ raise StandardError, "Unbound variable: '#{var.value}'"
119
+ end
120
+ end
121
+
122
+ define_primitive_proc(aRuntime, 'set!', binary, primitive)
123
+ end
124
+
105
125
  def create_plus(aRuntime)
106
126
  # arglist should be a Ruby Array
107
127
  primitive = ->(runtime, arglist) do
@@ -338,6 +358,22 @@ module Skeem
338
358
  define_primitive_proc(aRuntime, 'or', zero_or_more, primitive)
339
359
  end
340
360
 
361
+ def create_string_equal(aRuntime)
362
+ primitive = ->(runtime, first_operand, arglist) do
363
+ first_one = first_operand.evaluate(runtime)
364
+ if arglist.empty?
365
+ boolean(true)
366
+ else
367
+ operands = evaluate_array(arglist, runtime)
368
+ first_value = first_one.value
369
+ all_equal = operands.all? { |elem| first_value == elem.value }
370
+ boolean(all_equal)
371
+ end
372
+ end
373
+
374
+ define_primitive_proc(aRuntime, 'string=?', one_or_more, primitive)
375
+ end
376
+
341
377
  def create_string_append(aRuntime)
342
378
  primitive = ->(runtime, arglist) do
343
379
  if arglist.empty?
data/lib/skeem/runtime.rb CHANGED
@@ -12,6 +12,11 @@ module Skeem
12
12
  def include?(anIdentifier)
13
13
  environment.include?(normalize_key(anIdentifier))
14
14
  end
15
+
16
+ def fetch(aKey)
17
+ key_value = normalize_key(aKey)
18
+ include?(key_value) ? environment.fetch(key_value) : nil
19
+ end
15
20
 
16
21
  def define(aKey, anEntry)
17
22
  environment.define(normalize_key(aKey), anEntry)
@@ -20,8 +25,15 @@ module Skeem
20
25
  def evaluate(aKey)
21
26
  key_value = normalize_key(aKey)
22
27
  if include?(key_value)
23
- definition = environment.fetch(key_value)
24
- definition.expression.evaluate(self)
28
+ entry = environment.fetch(key_value)
29
+ case entry
30
+ when Primitive::PrimitiveProcedure
31
+ entry
32
+ when SkmDefinition
33
+ entry.expression.evaluate(self)
34
+ else
35
+ raise StandardError, entry.inspect
36
+ end
25
37
  else
26
38
  err = StandardError
27
39
  key = aKey.kind_of?(SkmIdentifier) ? aKey.value : key_value
@@ -58,8 +58,16 @@ module Skeem
58
58
  when SkmVariableReference
59
59
  other_key = expression.variable.evaluate(aRuntime)
60
60
  if var_key.value != other_key.value
61
+ entry = aRuntime.fetch(other_key)
61
62
  result = expression.evaluate(aRuntime)
62
- else
63
+ if entry.kind_of?(Primitive::PrimitiveProcedure)
64
+ @expression = entry
65
+ elsif entry.kind_of?(SkmDefinition)
66
+ if entry.expression.kind_of?(SkmLambda)
67
+ @expression = entry.expression
68
+ end
69
+ end
70
+ else
63
71
  # INFINITE LOOP DANGER: definition of 'x' is a reference to 'x'!
64
72
  # Way out: the lookup for the reference should start from outer
65
73
  # environment.
@@ -88,7 +96,7 @@ module Skeem
88
96
 
89
97
  # call method should only invoked when the expression is a SkmLambda
90
98
  def call(aRuntime, aProcedureCall)
91
- unless expression.kind_of?(SkmLambda)
99
+ unless [SkmLambda, Primitive::PrimitiveProcedure].include?(expression.class)
92
100
  err_msg = "Expected a SkmLambda instead of #{expression.class}"
93
101
  raise StandardError, err_msg
94
102
  end
@@ -45,5 +45,7 @@
45
45
  (lambda (z)
46
46
  (* z z)))
47
47
 
48
+ (define symbol=? string=?)
49
+
48
50
  (define list
49
51
  (lambda args args))
data/lib/skeem/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Skeem
2
- VERSION = '0.0.24'.freeze
2
+ VERSION = '0.0.25'.freeze
3
3
  end
@@ -1,9 +1,12 @@
1
1
  require 'stringio'
2
2
  require_relative '../spec_helper' # Use the RSpec framework
3
+ require_relative '../../lib/skeem/datum_dsl'
3
4
  require_relative '../../lib/skeem/interpreter' # Load the class under test
4
5
 
5
6
  module Skeem
6
7
  describe Interpreter do
8
+ include DatumDSL
9
+
7
10
  context 'Initialization:' do
8
11
  it 'should be initialized without argument' do
9
12
  expect { Interpreter.new() }.not_to raise_error
@@ -329,6 +332,41 @@ SKEEM
329
332
  expect(result.members[index]).to eq(value)
330
333
  end
331
334
  end
335
+
336
+ it 'should implement the unquote of vectors' do
337
+ source = '`#( ,(+ 1 2) 4)'
338
+ result = subject.run(source)
339
+ expect(result).to be_kind_of(SkmVector)
340
+ predictions = [
341
+ [SkmInteger, 3],
342
+ [SkmInteger, 4]
343
+ ]
344
+ predictions.each_with_index do |(type, value), index|
345
+ expect(result.members[index]).to be_kind_of(type)
346
+ expect(result.members[index]).to eq(value)
347
+ end
348
+
349
+ source = "`#()"
350
+ result = subject.run(source)
351
+ expect(result).to be_kind_of(SkmVector)
352
+ expect(result).to be_empty
353
+
354
+ # Nested vectors
355
+ source = '`#(a b #(,(+ 2 3) c) d)'
356
+ result = subject.run(source)
357
+ # expected: #(a b #(5 c) d)
358
+ expect(result).to be_kind_of(SkmVector)
359
+ predictions = [
360
+ [SkmIdentifier, 'a'],
361
+ [SkmIdentifier, 'b'],
362
+ [SkmVector, vector([integer(5), identifier('c')])],
363
+ [SkmIdentifier, 'd']
364
+ ]
365
+ predictions.each_with_index do |(type, value), index|
366
+ expect(result.members[index]).to be_kind_of(type)
367
+ expect(result.members[index]).to eq(value)
368
+ end
369
+ end
332
370
 
333
371
  it 'should implement the quasiquotation of lists' do
334
372
  source = '(quasiquote (+ 1 2))'
@@ -368,10 +406,25 @@ SKEEM
368
406
  result = subject.run(source)
369
407
  expect(result).to be_kind_of(SkmList)
370
408
  expect(result).to be_null
409
+
410
+ # nested lists
411
+ source = '`(a b (,(+ 2 3) c) d)'
412
+ result = subject.run(source)
413
+ # expected: (a b (5 c) d)
414
+ expect(result).to be_kind_of(SkmList)
415
+ predictions = [
416
+ [SkmIdentifier, 'a'],
417
+ [SkmIdentifier, 'b'],
418
+ [SkmList, list([integer(5), identifier('c')])],
419
+ [SkmIdentifier, 'd']
420
+ ]
421
+ predictions.each_with_index do |(type, value), index|
422
+ expect(result.members[index]).to be_kind_of(type)
423
+ expect(result.members[index]).to eq(value)
424
+ end
371
425
  end
372
426
  =begin
373
427
  `(+ 2 ,(* 3 4)) (+ 2 12)
374
- `(a b (,(+ 2 3) c) d) (a b (5 c) d)
375
428
  `(a b ,(reverse '(c d e)) f g) (a b (e d c) f g)
376
429
  (let ([a 1] [b 2])
377
430
  `(,a . ,b)) (1 . 2)
@@ -514,6 +567,18 @@ SKEEM
514
567
  expect(result).to eq(expectation)
515
568
  end
516
569
  end
570
+
571
+ it 'should implement the symbol=? procedure' do
572
+ checks = [
573
+ ["(symbol=? 'a 'a)", true],
574
+ ["(symbol=? 'a (string->symbol \"a\"))", true],
575
+ ["(symbol=? 'a 'b)", false]
576
+ ]
577
+ checks.each do |(skeem_expr, expectation)|
578
+ result = subject.run(skeem_expr)
579
+ expect(result).to eq(expectation)
580
+ end
581
+ end
517
582
 
518
583
  it 'should implement the list procedure' do
519
584
  checks = [
@@ -6,8 +6,26 @@ require_relative '../../../lib/skeem/interpreter'
6
6
  module Skeem
7
7
  module Primitive
8
8
  describe 'Testing primitive procedures' do
9
- subject { Interpreter.new }
10
- context 'Arithmetic operators:' do
9
+ subject { Interpreter.new }
10
+
11
+ context 'Arithmetic operators:' do
12
+ it 'should implement the set! form' do
13
+ skeem1 = <<-SKEEM
14
+ (define x 2)
15
+ (+ x 1)
16
+ SKEEM
17
+ result = subject.run(skeem1)
18
+ expect(result.last).to eq(3) # x is bound to value 2
19
+ skeem2 = <<-SKEEM
20
+ (set! x 4)
21
+ (+ x 1)
22
+ SKEEM
23
+ result = subject.run(skeem2)
24
+ expect(result.last).to eq(5) # x is now bound to value 4
25
+ end
26
+ end # context
27
+
28
+ context 'Arithmetic operators:' do
11
29
  it 'should implement the addition operator' do
12
30
  [
13
31
  ['(+)', 0], # '+' as nullary operator. Example from section 6.2.6
@@ -294,6 +312,18 @@ module Skeem
294
312
  end
295
313
  end
296
314
 
315
+ it 'should implement the string=? procedure' do
316
+ checks = [
317
+ ['(string=? "Mom" "Mom")', true],
318
+ ['(string=? "Mom" "Mum")', false],
319
+ ['(string=? "Mom" "Dad")', false]
320
+ ]
321
+ checks.each do |(skeem_expr, expectation)|
322
+ result = subject.run(skeem_expr)
323
+ expect(result).to eq(expectation)
324
+ end
325
+ end
326
+
297
327
  it 'should implement the string-append procedure' do
298
328
  checks = [
299
329
  ['(string-append)', ''],
@@ -323,22 +353,24 @@ module Skeem
323
353
  it 'should implement the symbol? procedure' do
324
354
  checks = [
325
355
  ['(symbol? #f)', false],
326
- ['(symbol? "bar")', false]
356
+ ['(symbol? "bar")', false],
357
+ ["(symbol? '())", false],
358
+ ["(symbol? 'foo)", true],
327
359
  ]
328
360
  checks.each do |(skeem_expr, expectation)|
329
361
  result = subject.run(skeem_expr)
330
362
  expect(result).to eq(expectation)
331
363
  end
332
- end
364
+ end
333
365
  end # context
334
366
 
335
367
  context 'List procedures:' do
336
368
  it 'should implement the list? procedure' do
337
369
  checks = [
338
- #['(list? #f)', false],
339
- #['(list? 1)', false],
340
- #['(list? "bar")', false],
341
- #['(list? (list 1 2 3))', true],
370
+ ['(list? #f)', false],
371
+ ['(list? 1)', false],
372
+ ['(list? "bar")', false],
373
+ ['(list? (list 1 2 3))', true],
342
374
  ['(list? (list))', true]
343
375
  ]
344
376
  checks.each do |(skeem_expr, expectation)|
@@ -39,14 +39,14 @@ module Skeem
39
39
  context 'Evaluation:' do
40
40
  include Primitive::PrimitiveBuilder
41
41
 
42
- it 'should evaluate a given entry' do
43
- entry = double('three')
44
- result = double('fake-procedure')
45
- expect(entry).to receive(:expression).and_return(result)
46
- expect(result).to receive(:evaluate).with(subject).and_return(integer(3))
47
- subject.define('three', entry)
48
- expect(subject.evaluate('three')).to eq(3)
49
- end
42
+ # it 'should evaluate a given entry' do
43
+ # entry = integer(3)
44
+ # result = double('fake-procedure')
45
+ # expect(entry).to receive(:expression).and_return(result)
46
+ # expect(result).to receive(:evaluate).with(subject).and_return(integer(3))
47
+ # subject.define('three', entry)
48
+ # expect(subject.evaluate('three')).to eq(3)
49
+ # end
50
50
 
51
51
  it 'should evaluate a given list' do
52
52
  add_primitives(subject)
@@ -1,6 +1,7 @@
1
1
  require 'ostruct'
2
2
  require_relative '../spec_helper' # Use the RSpec framework
3
3
  require_relative '../../lib/skeem/runtime'
4
+ require_relative '../../lib/skeem/primitive/primitive_builder'
4
5
  require_relative '../../lib/skeem/s_expr_nodes' # Load the classes under test
5
6
 
6
7
  module Skeem
@@ -26,6 +27,8 @@ module Skeem
26
27
  end # context
27
28
 
28
29
  context 'Provided services:' do
30
+ include Primitive::PrimitiveBuilder
31
+
29
32
  let(:runtime) { Runtime.new(Environment.new) }
30
33
 
31
34
  it 'should create an entry when evaluating' do
@@ -34,6 +37,39 @@ module Skeem
34
37
  expect(runtime).to include(sample_symbol)
35
38
  end
36
39
 
40
+ it 'should optimize an entry that aliases a primitive proc' do
41
+ add_primitives(runtime)
42
+ identifier = SkmIdentifier.create('plus')
43
+ proc_name = SkmIdentifier.create('+')
44
+ var_ref = SkmVariableReference.new(nil, proc_name)
45
+ instance = SkmDefinition.new(nil, identifier, var_ref) # (define plus +)
46
+ expect(instance.expression).to eq(var_ref)
47
+ instance.evaluate(runtime)
48
+ # Optimization by getting rid of indirection
49
+ expect(instance.expression).to be_kind_of(Primitive::PrimitiveProcedure)
50
+ end
51
+
52
+ it 'should optimize an entry that aliases a lambda proc' do
53
+ # Let's define a (dummy) lambda
54
+ formals = SkmFormals.new([], :fixed)
55
+ dummy_body = { :defs => [], :sequence => [SkmInteger.create(3)]}
56
+ lbd = SkmLambda.new(nil, formals, dummy_body)
57
+ id = SkmIdentifier.create('some-lambda')
58
+ def1 = SkmDefinition.new(nil, id, lbd)
59
+ def1.evaluate(runtime)
60
+
61
+ # Let's create an alias to the lambda
62
+ other_name = SkmIdentifier.create('aliased-lambda')
63
+ var_ref = SkmVariableReference.new(nil, id)
64
+
65
+ # (define aliased-lambda some-lambda)
66
+ def2 = SkmDefinition.new(nil, other_name, var_ref)
67
+ expect(def2.expression).to eq(var_ref)
68
+ def2.evaluate(runtime)
69
+ # Optimization by getting rid of indirection
70
+ expect(def2.expression).to eq(lbd)
71
+ end
72
+
37
73
  it 'should quasiquote its variable and expression' do
38
74
  alter_ego = subject.quasiquote(runtime)
39
75
  expect(alter_ego).to eq(subject)
@@ -1,6 +1,7 @@
1
1
  require_relative '../spec_helper' # Use the RSpec framework
2
2
  require_relative '../../lib/skeem/datum_dsl'
3
3
  require_relative '../../lib/skeem/environment'
4
+ require_relative '../../lib/skeem/s_expr_nodes'
4
5
  require_relative '../../lib/skeem/skm_unary_expression' # Load the classes under test
5
6
 
6
7
  module Skeem
@@ -176,15 +177,14 @@ module Skeem
176
177
  end # context
177
178
 
178
179
  context 'Provided services:' do
179
- let(:dummy_def) { double('fake-definition') }
180
+ let(:sample_def) { SkmDefinition.new(nil, identifier('three'), integer(3)) }
180
181
  let(:runtime) { Runtime.new(Environment.new) }
181
182
 
182
183
  before(:each) do
183
- runtime.define('three', dummy_def)
184
+ runtime.define('three', sample_def)
184
185
  end
185
186
 
186
187
  it "should return the variable's value at evaluation" do
187
- expect(dummy_def).to receive(:expression).and_return(integer(3))
188
188
  expect(subject.evaluate(runtime)).to eq(3)
189
189
  end
190
190
 
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.24
4
+ version: 0.0.25
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-11-08 00:00:00.000000000 Z
11
+ date: 2018-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley