skeem 0.0.11 → 0.0.12

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: a42694aa5f20550be636b493bcf451db3b5d70f8
4
- data.tar.gz: a3f65ef0a724758a257fbaaf5570edf8c341ea08
3
+ metadata.gz: d588cbeb0a65a6d0821ca4298fb15f577b873a0f
4
+ data.tar.gz: b71fd66e1970299cef3e5802be3a8800cef3dd83
5
5
  SHA512:
6
- metadata.gz: afa3959e8f8e9873b08cc242954350dbf61ab0cb2296ce46175f68e70070498cc10446031b32011536e39b8f7cb9a2f7bd1977e949c98a7b541b61aae2310674
7
- data.tar.gz: 6c07561060a42c776a14534fa117734850c3878d83c915bda6c997eab1e29cf88417d35bdaa9fa0e0071ad7c3ac5aef38759ee7e0855aeaf2e774cd6fa810959
6
+ metadata.gz: e2aa7dec8f113fe704d859201f4176d7dcea1870a02d2fd7d6c89e96b1e2f3848d3e99c6283f95326a40a54af7c153834d8a213d6dcf8b3afd772bfbf2d28989
7
+ data.tar.gz: 3f46e06c1c4b603b3b0af8d49bfd2180ffd4aba3906492ae845dc2e0d2c727286f8e63e09d9e8aeb1ca5dae7f6e47635f7364022e688d713086ec7ed63e2428f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## [0.0.12] - 2018-09-17
2
+ Added primitive `define` and variable reference
3
+
4
+ ### Added
5
+ - Class `SkmDefinition` for representing a specific definition.
6
+ - Class `SkmVariableReference` for representing a variable reference (i.e. retrieving its value)
7
+ - Module `Convertible` implementing utility methods for converting "native" Ruby objects into their Skeem counterpart.
8
+ - Class `SkmBuilder`. Added methods to implement the semantic actions for `define` and variable reference.
9
+ - File `interpreter_spec.rb` added tests for `define` and variable reference.
10
+
11
+ ### Changed
12
+ - File `README.md` Changed demo snippet with example of variable definition and variable reference.
13
+
1
14
  ## [0.0.11] - 2018-09-16
2
15
  Added primitive procedures: `=`, `<`, `>`, `>=`, `<=`
3
16
 
data/README.md CHANGED
@@ -32,11 +32,14 @@ At this stage, the gem consists of a bare-bones interpreter.
32
32
 
33
33
  schemer = Skeem::Interpreter.new
34
34
  scheme_code =<<-SKEEM
35
- ; Let's try some arithmetic expression
36
- (+ (* 2 100) (* 1 10))
35
+ ; Let's define a Scheme variable
36
+ (define foobar (* 2 3 7))
37
+
38
+ ; Now retrieve its value
39
+ foobar
37
40
  SKEEM
38
41
  result = schemer.run(scheme_code)
39
- puts result.value # => 210
42
+ puts result.value # => 42
40
43
  ```
41
44
 
42
45
  Roadmap:
@@ -0,0 +1,17 @@
1
+ require_relative 's_expr_nodes'
2
+
3
+ module Skeem
4
+ module Convertible
5
+ # Convert Ruby object into its Skeem counterpart
6
+ def to_skm(native_obj)
7
+ case native_obj
8
+ when TrueClass, FalseClass
9
+ SkmBoolean.create(native_obj)
10
+ when Float
11
+ SkmReal.create(native_obj)
12
+ when Integer
13
+ SkmInteger.create(native_obj)
14
+ end
15
+ end
16
+ end # module
17
+ end # module
data/lib/skeem/grammar.rb CHANGED
@@ -21,14 +21,14 @@ module Skeem
21
21
  # add_terminals('BEGIN', 'DEFINE')
22
22
  add_terminals('DEFINE')
23
23
 
24
- rule 'program' => 'cmd_or_def_plus'
25
- rule 'cmd_or_def_plus' => 'cmd_or_def_plus cmd_or_def'
26
- rule 'cmd_or_def_plus' => 'cmd_or_def'
24
+ rule('program' => 'cmd_or_def_plus').as 'main'
25
+ rule('cmd_or_def_plus' => 'cmd_or_def_plus cmd_or_def').as 'multiple_cmd_def'
26
+ rule('cmd_or_def_plus' => 'cmd_or_def').as 'last_cmd_def'
27
27
  rule 'cmd_or_def' => 'command'
28
28
  rule 'cmd_or_def' => 'definition'
29
29
  rule 'command' => 'expression'
30
- rule 'definition' => 'LPAREN DEFINE IDENTIFIER expression RPAREN'
31
- rule 'expression' => 'IDENTIFIER'
30
+ rule('definition' => 'LPAREN DEFINE IDENTIFIER expression RPAREN').as 'definition'
31
+ rule('expression' => 'IDENTIFIER').as 'variable_reference'
32
32
  rule 'expression' => 'literal'
33
33
  rule 'expression' => 'procedure_call'
34
34
  rule 'literal' => 'self-evaluating'
@@ -1,8 +1,10 @@
1
1
  require_relative '../primitive_procedure'
2
+ require_relative '../convertible'
2
3
 
3
4
  module Skeem
4
5
  module Primitive
5
6
  module PrimitiveBuilder
7
+ include Convertible
6
8
  def add_primitives(aRuntime)
7
9
  add_arithmetic(aRuntime)
8
10
  add_comparison(aRuntime)
@@ -238,18 +240,6 @@ module Skeem
238
240
  def define(aRuntime, aKey, anEntry)
239
241
  aRuntime.define(aKey, anEntry)
240
242
  end
241
-
242
- # Convert Ruby object into its Skeem counterpart
243
- def to_skm(native_obj)
244
- case native_obj
245
- when TrueClass, FalseClass
246
- SkmBoolean.create(native_obj)
247
- when Float
248
- SkmReal.create(native_obj)
249
- when Integer
250
- SkmInteger.create(native_obj)
251
- end
252
- end
253
243
  end # module
254
244
  end # module
255
245
  end # module
@@ -35,6 +35,38 @@ module Skeem
35
35
  Terminal2NodeClass
36
36
  end
37
37
 
38
+ # rule('program' => 'cmd_or_def_plus').as 'main'
39
+ def reduce_main(_production, _range, _tokens, theChildren)
40
+ last_child = theChildren.last
41
+ result = if last_child.members.size == 1
42
+ last_child.members[0]
43
+ else
44
+ last_child
45
+ end
46
+ end
47
+
48
+ # rule('cmd_or_def_plus' => 'cmd_or_def_plus cmd_or_def').as 'multiple_cmd_def'
49
+ def reduce_multiple_cmd_def(_production, _range, _tokens, theChildren)
50
+ theChildren[0].members << theChildren[1]
51
+ theChildren[0]
52
+ end
53
+
54
+ # rule('cmd_or_def_plus' => 'cmd_or_def').as 'last_cmd_def'
55
+ def reduce_last_cmd_def(_production, _range, _tokens, theChildren)
56
+ SkmList.new([theChildren.last])
57
+ end
58
+
59
+ # rule('definition' => 'LPAREN DEFINE IDENTIFIER expression RPAREN')
60
+ # .as 'definition'
61
+ def reduce_definition(_production, aRange, _tokens, theChildren)
62
+ SkmDefinition.new(aRange, theChildren[2], theChildren[3])
63
+ end
64
+
65
+ # rule('expression' => 'IDENTIFIER').as 'variable_reference'
66
+ def reduce_variable_reference(_production, aRange, _tokens, theChildren)
67
+ SkmVariableReference.new(aRange, theChildren[0])
68
+ end
69
+
38
70
  # rule('proc_call_args' => 'LPAREN operator operand_plus RPAREN')
39
71
  def reduce_proc_call_args(_production, aRange, _tokens, theChildren)
40
72
  ProcedureCall.new(aRange, theChildren[1], theChildren[2])
@@ -199,25 +199,74 @@ module Skeem
199
199
  alias subnodes members
200
200
  end # class
201
201
 
202
+ class SkmDefinition < SkmElement
203
+ attr_reader :variable
204
+ attr_reader :expression
205
+
206
+ def initialize(aPosition, aVariable, theExpression)
207
+ super(aPosition)
208
+ @variable = aVariable
209
+ @expression = theExpression
210
+ end
211
+
212
+ def evaluate(aRuntime)
213
+ var_key = variable.evaluate(aRuntime)
214
+ aRuntime.define(var_key, self)
215
+ self
216
+ end
217
+ end # class
218
+
219
+ class SkmVariableReference < SkmElement
220
+ attr_reader :variable
221
+
222
+ def initialize(aPosition, aVariable)
223
+ super(aPosition)
224
+ @variable = aVariable
225
+ end
226
+
227
+ def evaluate(aRuntime)
228
+ var_key = variable.evaluate(aRuntime)
229
+ unless aRuntime.include?(var_key.value)
230
+ err = StandardError
231
+ key = var_key.kind_of?(SkmIdentifier) ? var_key.value : var_key
232
+ err_msg = "Unknown variable '#{key}'"
233
+ raise err, err_msg
234
+ end
235
+ definition = aRuntime.environment.bindings[var_key.value]
236
+ result = definition.expression.evaluate(aRuntime)
237
+ end
238
+
239
+ # Confusing!
240
+ # Value, here, means the value of the identifier (the variable's name).
241
+ def value()
242
+ variable.value
243
+ end
244
+ end
245
+
202
246
  class ProcedureCall < SkmElement
203
247
  attr_reader :operator
204
248
  attr_reader :operands
205
249
 
206
250
  def initialize(aPosition, anOperator, theOperands)
207
251
  super(aPosition)
208
- @operator = anOperator
252
+ if anOperator.kind_of?(SkmVariableReference)
253
+ # Kinky: variable names are procedure names, not variable reference
254
+ @operator = SkmIdentifier.create(anOperator.value)
255
+ else
256
+ @operator = anOperator
257
+ end
209
258
  @operands = SkmList.new(theOperands)
210
259
  end
211
260
 
212
261
  def evaluate(aRuntime)
213
- proc_key = operator.evaluate(aRuntime)
214
- unless aRuntime.include?(proc_key.value)
262
+ var_key = operator.evaluate(aRuntime)
263
+ unless aRuntime.include?(var_key.value)
215
264
  err = StandardError
216
- key = proc_key.kind_of?(SkmIdentifier) ? proc_key.value : proc_key
217
- err_msg = "Unknown function '#{key}'"
265
+ key = var_key.kind_of?(SkmIdentifier) ? var_key.value : var_key
266
+ err_msg = "Unknown procedure '#{key}'"
218
267
  raise err, err_msg
219
268
  end
220
- procedure = aRuntime.environment.bindings[proc_key.value]
269
+ procedure = aRuntime.environment.bindings[var_key.value]
221
270
  result = procedure.call(aRuntime, self)
222
271
  end
223
272
 
data/lib/skeem/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Skeem
2
- VERSION = '0.0.11'.freeze
2
+ VERSION = '0.0.12'.freeze
3
3
  end
@@ -76,18 +76,24 @@ module Skeem
76
76
  expect(result.value).to eq(predicted)
77
77
  end
78
78
  end
79
-
80
- it 'should evaluate isolated identifiers' do
81
- samples = [
82
- ['the-word-recursion-has-many-meanings',
83
- 'the-word-recursion-has-many-meanings']
84
- ]
85
- samples.each do |source, predicted|
86
- result = subject.run(source)
87
- expect(result).to be_kind_of(SkmIdentifier)
88
- expect(result.value).to eq(predicted)
89
- end
79
+ end # context
80
+
81
+ context 'Built-in primitives' do
82
+ it 'should implement variable definition' do
83
+ result = subject.run('(define x 28)')
84
+ expect(result).to be_kind_of(SkmDefinition)
90
85
  end
86
+
87
+ it 'should implement variable reference' do
88
+ source = <<-SKEEM
89
+ ; Example from R7RS section 4.1.1
90
+ (define x 28)
91
+ x
92
+ SKEEM
93
+ result = subject.run(source)
94
+ expect(result).to be_kind_of(SkmInteger)
95
+ expect(result.value).to eq(28)
96
+ end
91
97
  end # context
92
98
 
93
99
  context 'Built-in primitive procedures' do
@@ -120,9 +126,14 @@ module Skeem
120
126
  end
121
127
 
122
128
  it 'should implement the product of numbers' do
123
- result = subject.run('(* 2 3 4)')
124
- expect(result).to be_kind_of(SkmInteger)
125
- expect(result.value).to eq(24)
129
+ checks = [
130
+ ['(* 5 8)', 40],
131
+ ['(* 2 3 4)', 24]
132
+ ]
133
+ checks.each do |(skeem_expr, expectation)|
134
+ result = subject.run(skeem_expr)
135
+ expect(result.value).to eq(expectation)
136
+ end
126
137
  end
127
138
 
128
139
  it 'should implement the division of numbers' do
@@ -75,7 +75,7 @@ module Skeem
75
75
  ]
76
76
  samples.each do |source, predicted|
77
77
  ptree = subject.parse(source)
78
- expect(ptree.root).to be_kind_of(SkmIdentifier)
78
+ expect(ptree.root).to be_kind_of(SkmVariableReference)
79
79
  expect(ptree.root.value).to eq(predicted)
80
80
  end
81
81
  end
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.11
4
+ version: 0.0.12
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-16 00:00:00.000000000 Z
11
+ date: 2018-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley
@@ -85,6 +85,7 @@ files:
85
85
  - Rakefile
86
86
  - appveyor.yml
87
87
  - lib/skeem.rb
88
+ - lib/skeem/convertible.rb
88
89
  - lib/skeem/environment.rb
89
90
  - lib/skeem/grammar.rb
90
91
  - lib/skeem/interpreter.rb