skeem 0.0.11 → 0.0.12

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