skeem 0.0.6 → 0.0.7

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: bfe901703f31ea172032969d5a52e73a99467917
4
- data.tar.gz: e9ea6993d7f4f4a107e4c43dc8e342affe103439
3
+ metadata.gz: c9907100633d80e63603f094700423139f320600
4
+ data.tar.gz: 5d4b89ed16eb0c029d98ce02c88eed4cc459404d
5
5
  SHA512:
6
- metadata.gz: 8b034f52223d95e8d40e3de77596b126484116f900a5890d7da6c9f9de56a0ed1cd08d0ac0e80d1e6b92acc17da6663f3da55eb7b01a2041512e109d3a423285
7
- data.tar.gz: f315e8f48f9c97391df7a665fd01c2fc6ea4ed292bc9c075bec7332e2706a01cfe00c0aad0ea6d54fccc50e3fb84bc8afc10596a8cedc3726413d3824343baac
6
+ metadata.gz: edd237e0152cbac2cf89bd0724436db40865fddddec40eb4cd3de595909c8a1c09e424f0903903a3cbacf2e3c6fc63fb98770523acda5ffa2099a382f8eccd41
7
+ data.tar.gz: 3d9605b4577d2701c18cb675b4ea37907b8d7c990c3a9139b6227931b2b68ddf0be8617b6980bf03e640821a958e5d3419b423c95bda128dc6db2837dda4a21e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## [0.0.7] - 2018-09-12
2
+ Proof of concept of a primitive function: '+' operator.
3
+ Demo works but code needs some polishing and testing.
4
+
5
+ ### Added
6
+ - Class `Environment`. Holds a mapping between symbol names and their associated value.
7
+ - Class `PrimitiveBuilder`. Builder class that seeds the default environment with primitive functions (now, limited to '+')
8
+ - Class `PrimitiveFunc` Internal representation of primitive functions.
9
+ - Class `Runtime`. Holds all context data of the Skeem interpreter.
10
+ - Methods `SExprBuilder#reduce_proc_call`, `SExprBuilder#reduce_multiple_operands`, SExprBuilder#reduce_last_operand
11
+
12
+ ### Changed
13
+ - Class `Tokenize` Added support for Scheme semi-colon comments.
14
+ - File `grammar.rb` Added syntax rules for procedure calling.
15
+ - File `s_expr_nodes.rb` Class and code refactoring.
16
+ - File `README.md` Changed demo snippet with example plus operator.
17
+
18
+
1
19
  ## [0.0.6] - 2018-09-01
2
20
  Initial (minimalistic) interpreter implementation.
3
21
  ### Added
data/README.md CHANGED
@@ -31,9 +31,12 @@ At this stage, the gem consists of a bare-bones interpreter.
31
31
  require 'skeem'
32
32
 
33
33
  schemer = Skeem::Interpreter.new
34
- scheme_code = '"Hello, world"'
34
+ scheme_code =<<-SKEEM
35
+ ; Let's try the addition operator
36
+ (+ 3 4 5)
37
+ SKEEM
35
38
  result = schemer.run(scheme_code)
36
- puts result.value # => "Hello, world"
39
+ puts result.value # => 12
37
40
  ```
38
41
 
39
42
  Roadmap:
@@ -0,0 +1,19 @@
1
+ require 'forwardable'
2
+
3
+ module Skeem
4
+ class Environment
5
+ extend Forwardable
6
+ def_delegator :@bindings, :empty?
7
+
8
+ attr_reader(:bindings)
9
+
10
+ def initialize()
11
+ @bindings = {}
12
+ end
13
+
14
+ def define(anIdentifier, anExpression)
15
+ raise StandardError, anIdentifier unless anIdentifier.kind_of?(String)
16
+ @bindings[anIdentifier] = anExpression
17
+ end
18
+ end # class
19
+ end # module
data/lib/skeem/grammar.rb CHANGED
@@ -30,10 +30,17 @@ module Skeem
30
30
  rule 'definition' => 'LPAREN DEFINE IDENTIFIER expression RPAREN'
31
31
  rule 'expression' => 'IDENTIFIER'
32
32
  rule 'expression' => 'literal'
33
+ rule 'expression' => 'procedure_call'
33
34
  rule 'literal' => 'self-evaluating'
34
35
  rule 'self-evaluating' => 'BOOLEAN'
35
36
  rule 'self-evaluating' => 'number'
36
37
  rule 'self-evaluating' => 'STRING_LIT'
38
+ rule 'procedure_call' => 'LPAREN operator RPAREN'
39
+ rule('procedure_call' => 'LPAREN operator operand_plus RPAREN').as 'proc_call_args'
40
+ rule('operand_plus' => 'operand_plus operand').as 'multiple_operands'
41
+ rule('operand_plus' => 'operand').as 'last_operand'
42
+ rule 'operator' => 'expression'
43
+ rule 'operand' => 'expression'
37
44
  rule 'number' => 'INTEGER'
38
45
  rule 'number' => 'REAL'
39
46
  end
@@ -1,16 +1,23 @@
1
1
  require_relative 'parser'
2
+ require_relative 'environment'
3
+ require_relative 'runtime'
4
+ require_relative './primitive/primitive_builder'
2
5
 
3
6
  module Skeem
4
7
  class Interpreter
8
+ include Primitive::PrimitiveBuilder
5
9
  attr_reader(:parser)
10
+ attr_reader(:runtime)
6
11
 
7
12
  def initialize()
13
+ @runtime = Runtime.new(Environment.new)
14
+ add_primitives(runtime)
8
15
  end
9
16
 
10
17
  def run(source)
11
18
  @parser ||= Parser.new
12
19
  @ptree = parser.parse(source)
13
- return @ptree.root.interpret
20
+ return @ptree.root.evaluate(runtime)
14
21
  end
15
22
  end # class
16
23
  end # module
@@ -0,0 +1,38 @@
1
+ require_relative '../primitive_func'
2
+
3
+ module Skeem
4
+ module Primitive
5
+ module PrimitiveBuilder
6
+ def add_primitives(aRuntime)
7
+ add_arithmetic(aRuntime)
8
+ end
9
+
10
+ private
11
+
12
+ def add_arithmetic(aRuntime)
13
+ def_func(aRuntime, create_plus)
14
+ end
15
+
16
+ def create_plus()
17
+ plus_code = ->(runtime, arglist) do
18
+ operands = arglist.to_eval_enum(runtime)
19
+ raw_result = operands.reduce(0) do |interim, elem|
20
+ interim += elem.value
21
+ end
22
+ SExprInteger.create(raw_result)
23
+ end
24
+
25
+ ['+', plus_code]
26
+ end
27
+
28
+ def def_func(aRuntime, aPair)
29
+ func = PrimitiveFunc.new(aPair.first, aPair.last)
30
+ define(aRuntime, func.identifier, func)
31
+ end
32
+
33
+ def define(aRuntime, aKey, anEntry)
34
+ aRuntime.define(aKey, anEntry)
35
+ end
36
+ end # module
37
+ end # module
38
+ end # module
@@ -0,0 +1,18 @@
1
+ require_relative 's_expr_nodes'
2
+
3
+ module Skeem
4
+ class PrimitiveFunc
5
+ attr_reader(:identifier)
6
+ attr_reader(:code)
7
+
8
+ def initialize(anId, aLambda)
9
+ @identifier = anId.kind_of?(String) ? SExprIdentifier.create(anId) : anId
10
+ @code = aLambda
11
+ end
12
+
13
+ def call(aRuntime, aProcedureCall)
14
+ args = aProcedureCall.operands
15
+ return @code.call(aRuntime, args)
16
+ end
17
+ end # class
18
+ end # module
@@ -0,0 +1,30 @@
1
+ module Skeem
2
+ class Runtime
3
+ attr_reader(:environment)
4
+
5
+ def initialize(anEnvironment)
6
+ @environment = anEnvironment
7
+ end
8
+
9
+ def include?(anIdentifier)
10
+ environment.bindings.include?(normalize_key(anIdentifier))
11
+ end
12
+
13
+ def define(aKey, anEntry)
14
+ environment.define(normalize_key(aKey), anEntry)
15
+ end
16
+
17
+ private
18
+
19
+ def normalize_key(aKey)
20
+ result = case aKey
21
+ when String
22
+ aKey
23
+ else
24
+ aKey.evaluate(self).value
25
+ end
26
+
27
+ result
28
+ end
29
+ end # class
30
+ end # module
@@ -11,11 +11,11 @@ module Skeem
11
11
  # nodes) and using a step by step approach.
12
12
  class SExprBuilder < Rley::ParseRep::ASTBaseBuilder
13
13
  Terminal2NodeClass = {
14
- 'BOOLEAN' => SExprBooleanNode,
15
- 'IDENTIFIER' => SExprIdentifierNode,
16
- 'INTEGER' => SExprIntegerNode,
17
- 'REAL' => SExprRealNode,
18
- 'STRING_LIT' => SExprStringNode
14
+ 'BOOLEAN' => SExprBoolean,
15
+ 'IDENTIFIER' => SExprIdentifier,
16
+ 'INTEGER' => SExprInteger,
17
+ 'REAL' => SExprReal,
18
+ 'STRING_LIT' => SExprString
19
19
  }.freeze
20
20
 
21
21
  # Create a new AST builder instance.
@@ -34,6 +34,21 @@ module Skeem
34
34
  def terminal2node
35
35
  Terminal2NodeClass
36
36
  end
37
+
38
+ # rule('proc_call_args' => 'LPAREN operator operand_plus RPAREN')
39
+ def reduce_proc_call_args(_production, aRange, _tokens, theChildren)
40
+ ProcedureCall.new(aRange, theChildren[1], theChildren[2])
41
+ end
42
+
43
+ # rule('operand_plus' => 'operand_plus operand').as 'multiple_operands'
44
+ def reduce_multiple_operands(_production, _range, _tokens, theChildren)
45
+ theChildren[0] << theChildren[1]
46
+ end
47
+
48
+ # rule('operand_plus' => 'operand').as 'last_operand'
49
+ def reduce_last_operand(_production, _range, _tokens, theChildren)
50
+ [theChildren.last]
51
+ end
37
52
  end # class
38
53
  end # module
39
54
  # End of file
@@ -1,29 +1,62 @@
1
1
  # Classes that implement nodes of Abstract Syntax Trees (AST) representing
2
2
  # Skeem parse results.
3
3
 
4
+ require 'forwardable'
5
+
4
6
  module Skeem
7
+ # Abstract class. Generalization of any S-expr element.
8
+ SExprElement = Struct.new(:position) do
9
+ def initialize(aPosition)
10
+ self.position = aPosition
11
+ end
12
+
13
+ def evaluate(_runtime)
14
+ raise NotImplementedError
15
+ end
16
+
17
+ def done!()
18
+ # Do nothing
19
+ end
20
+
21
+ # Abstract method.
22
+ # Part of the 'visitee' role in Visitor design pattern.
23
+ # @param _visitor[ParseTreeVisitor] the visitor
24
+ def accept(_visitor)
25
+ raise NotImplementedError
26
+ end
27
+ end # struct
28
+
5
29
  # Abstract class. Root of class hierarchy needed for Interpreter
6
30
  # design pattern
7
- SExprTerminalNode = Struct.new(:token, :value, :position) do
31
+ class SExprTerminal < SExprElement
32
+ attr_reader :token
33
+ attr_reader :value
34
+
8
35
  def initialize(aToken, aPosition)
9
- self.token = aToken
10
- self.position = aPosition
36
+ super(aPosition)
37
+ @token = aToken
11
38
  init_value(aToken.lexeme)
12
39
  end
13
40
 
41
+ def self.create(aValue)
42
+ lightweight = self.allocate
43
+ lightweight.init_value(aValue)
44
+ return lightweight
45
+ end
46
+
14
47
  # This method can be overriden
15
48
  def init_value(aValue)
16
- self.value = aValue
49
+ @value = aValue
17
50
  end
18
51
 
19
52
  def symbol()
20
53
  token.terminal
21
54
  end
22
55
 
23
- def interpret()
56
+ def evaluate(_runtime)
24
57
  return self
25
58
  end
26
-
59
+
27
60
  def done!()
28
61
  # Do nothing
29
62
  end
@@ -33,44 +66,65 @@ module Skeem
33
66
  def accept(aVisitor)
34
67
  aVisitor.visit_terminal(self)
35
68
  end
36
- end
69
+ end # class
70
+
71
+ class SExprBoolean < SExprTerminal
72
+ end # class
37
73
 
38
- class SExprBooleanNode < SExprTerminalNode
74
+ class SExprNumber < SExprTerminal
39
75
  end # class
40
-
41
- class SExprNumberNode < SExprTerminalNode
42
- end # class
43
-
44
- class SExprRealNode < SExprNumberNode
76
+
77
+ class SExprReal < SExprTerminal
45
78
  end # class
46
-
47
- class SExprIntegerNode < SExprRealNode
79
+
80
+ class SExprInteger < SExprReal
48
81
  end # class
49
-
50
- class SExprStringNode < SExprTerminalNode
82
+
83
+ class SExprString < SExprTerminal
51
84
  # Override
52
85
  def init_value(aValue)
53
- self.value = aValue.dup
54
- end
86
+ super(aValue.dup)
87
+ end
55
88
  end # class
56
-
57
- class SExprIdentifierNode < SExprTerminalNode
89
+
90
+ class SExprIdentifier < SExprTerminal
58
91
  # Override
59
92
  def init_value(aValue)
60
- self.value = aValue.dup
61
- end
93
+ super(aValue.dup)
94
+ end
62
95
  end # class
63
-
64
- =begin
65
- class SExprCompositeNode
66
- attr_accessor(:children)
67
- attr_accessor(:symbol)
68
- attr_accessor(:position)
69
96
 
70
- def initialize(aSymbol, aPosition)
71
- @symbol = aSymbol
72
- @children = []
73
- @position = aPosition
97
+ class SExprReserved < SExprIdentifier
98
+ end # class
99
+
100
+
101
+ class SExprList < SExprElement
102
+ attr_accessor(:members)
103
+ extend Forwardable
104
+
105
+ def_delegator :@members, :first, :empty?
106
+
107
+ def initialize()
108
+ super(nil)
109
+ @members = []
110
+ end
111
+
112
+ def rest()
113
+ members.slice(1..-1)
114
+ end
115
+
116
+ # Factory method.
117
+ # Construct an Enumerator that will return iteratively the result
118
+ # of 'evaluate' method of each members of self.
119
+ def to_eval_enum(aRuntime)
120
+ elements = self.members
121
+
122
+ new_enum = Enumerator.new do |result|
123
+ context = aRuntime
124
+ elements.each { |elem| result << elem.evaluate(context) }
125
+ end
126
+
127
+ new_enum
74
128
  end
75
129
 
76
130
  # Part of the 'visitee' role in Visitor design pattern.
@@ -78,21 +132,38 @@ module Skeem
78
132
  def accept(aVisitor)
79
133
  aVisitor.visit_nonterminal(self)
80
134
  end
81
-
135
+
82
136
  def done!()
83
137
  # Do nothing
84
138
  end
85
139
 
86
- alias subnodes children
140
+ alias children members
141
+ alias subnodes members
142
+ alias head first
143
+ alias tail rest
87
144
  end # class
88
145
 
89
- class SExprUnaryOpNode < SExprCompositeNode
90
- def initialize(aSymbol, aPosition)
91
- super(aSymbol, aPosition)
146
+ class ProcedureCall < SExprElement
147
+ attr_reader :operator
148
+ attr_reader :operands
149
+
150
+ def initialize(aPosition, anOperator, theOperands)
151
+ super(aPosition)
152
+ @operator = anOperator
153
+ @operands = SExprList.new
154
+ @operands.instance_variable_set(:@members, theOperands)
155
+ end
156
+
157
+ def evaluate(aRuntime)
158
+ procedure_key = operator.evaluate(aRuntime)
159
+ err = StandardError
160
+ err_msg = "Unknown function #{procedure_key}"
161
+ raise err, err_msg unless aRuntime.include?(procedure_key.value)
162
+ procedure = aRuntime.environment.bindings[procedure_key.value]
163
+ result = procedure.call(aRuntime, self)
92
164
  end
93
165
 
94
- alias members children
166
+ alias children operands
95
167
  end # class
96
- =end
97
168
  end # module
98
169
  # End of file
@@ -185,6 +185,7 @@ module Skeem
185
185
 
186
186
  loop do
187
187
  ws_found = false
188
+ cmt_found = false
188
189
  found = scanner.skip(/[ \t\f]+/)
189
190
  ws_found = true if found
190
191
  found = scanner.skip(/(?:\r\n)|\r|\n/)
@@ -193,7 +194,12 @@ module Skeem
193
194
  @lineno += 1
194
195
  @line_start = scanner.pos
195
196
  end
196
- break unless ws_found
197
+ next_ch = scanner.peek(1)
198
+ if next_ch == ';'
199
+ cmt_found = true
200
+ scanner.skip(/;[^\r\n]*(?:(?:\r\n)|\r|\n)?/)
201
+ end
202
+ break unless ws_found or cmt_found
197
203
  end
198
204
 
199
205
  curr_pos = scanner.pos
data/lib/skeem/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Skeem
2
- VERSION = '0.0.6'.freeze
2
+ VERSION = '0.0.7'.freeze
3
3
  end
@@ -0,0 +1,27 @@
1
+ require_relative '../spec_helper' # Use the RSpec framework
2
+ require_relative '../../lib/skeem/environment' # Load the class under test
3
+
4
+ module Skeem
5
+ describe Environment do
6
+ context 'Initialization:' do
7
+ it 'should be initialized without argument' do
8
+ expect { Environment.new() }.not_to raise_error
9
+ end
10
+
11
+ it 'should have no bindings' do
12
+ expect(subject.bindings).to be_empty
13
+ end
14
+ end # context
15
+
16
+ context 'Provided services:' do
17
+ it 'should add entries' do
18
+ entry = double('dummy')
19
+ subject.define('dummy', entry)
20
+ expect(subject.bindings.size).to eq(1)
21
+ expect(subject.bindings['dummy']).not_to be_nil
22
+ expect(subject.bindings['dummy']).to eq(entry)
23
+ end
24
+ end # context
25
+
26
+ end # describe
27
+ end # module
@@ -8,11 +8,19 @@ module Skeem
8
8
  expect { Interpreter.new() }.not_to raise_error
9
9
  end
10
10
 
11
- it 'should not have its parser initialized' do
11
+ it 'should not have a parser' do
12
12
  expect(subject.parser).to be_nil
13
13
  end
14
+
15
+ it 'should have a runtime object' do
16
+ expect(subject.runtime).to be_kind_of(Runtime)
17
+ end
18
+
19
+ it 'should come with built-in functions' do
20
+ expect(subject.runtime.environment).not_to be_empty
21
+ end
14
22
  end # context
15
-
23
+
16
24
  context 'Interpreting self-evaluating expressions' do
17
25
  it 'should evaluate isolated booleans' do
18
26
  samples = [
@@ -23,38 +31,37 @@ module Skeem
23
31
  ]
24
32
  samples.each do |source, predicted|
25
33
  result = subject.run(source)
26
- expect(result).to be_kind_of(SExprBooleanNode)
34
+ expect(result).to be_kind_of(SExprBoolean)
27
35
  expect(result.value).to eq(predicted)
28
36
  end
29
- end
30
- end
31
-
32
- it 'should evaluate isolated integers' do
37
+ end
38
+
39
+ it 'should evaluate isolated integers' do
33
40
  samples = [
34
- ['0', 0],
35
- ['3', 3],
36
- ['-3', -3],
37
- ['+12345', 12345],
38
- ['-12345', -12345]
39
- ]
41
+ ['0', 0],
42
+ ['3', 3],
43
+ ['-3', -3],
44
+ ['+12345', 12345],
45
+ ['-12345', -12345]
46
+ ]
40
47
  samples.each do |source, predicted|
41
48
  result = subject.run(source)
42
- expect(result).to be_kind_of(SExprIntegerNode)
49
+ expect(result).to be_kind_of(SExprInteger)
43
50
  expect(result.value).to eq(predicted)
44
51
  end
45
52
  end
46
-
53
+
47
54
  it 'should evaluate isolated real numbers' do
48
55
  samples = [
49
- ['0.0', 0.0],
50
- ['3.14', 3.14],
51
- ['-3.14', -3.14],
52
- ['+123e+45', 123e+45],
53
- ['-123e-45', -123e-45]
54
- ]
56
+ ['0.0', 0.0],
57
+ ['3.14', 3.14],
58
+ ['-3.14', -3.14],
59
+ ['+123e+45', 123e+45],
60
+ ['-123e-45', -123e-45]
61
+ ]
55
62
  samples.each do |source, predicted|
56
63
  result = subject.run(source)
57
- expect(result).to be_kind_of(SExprRealNode)
64
+ expect(result).to be_kind_of(SExprReal)
58
65
  expect(result.value).to eq(predicted)
59
66
  end
60
67
  end
@@ -65,20 +72,28 @@ module Skeem
65
72
  ]
66
73
  samples.each do |source, predicted|
67
74
  result = subject.run(source)
68
- expect(result).to be_kind_of(SExprStringNode)
75
+ expect(result).to be_kind_of(SExprString)
69
76
  expect(result.value).to eq(predicted)
70
77
  end
71
78
  end
72
79
 
73
80
  it 'should evaluate isolated identifiers' do
74
81
  samples = [
75
- ['the-word-recursion-has-many-meanings', 'the-word-recursion-has-many-meanings']
76
- ]
82
+ ['the-word-recursion-has-many-meanings',
83
+ 'the-word-recursion-has-many-meanings']
84
+ ]
77
85
  samples.each do |source, predicted|
78
86
  result = subject.run(source)
79
- expect(result).to be_kind_of(SExprIdentifierNode)
87
+ expect(result).to be_kind_of(SExprIdentifier)
80
88
  expect(result.value).to eq(predicted)
81
89
  end
82
90
  end
91
+
92
+ it 'should support procedure calls' do
93
+ result = subject.run('(+ 3 4)')
94
+ expect(result).to be_kind_of(SExprInteger)
95
+ expect(result.value).to eq(7)
96
+ end
97
+ end # context
83
98
  end # describe
84
99
  end # module
@@ -23,7 +23,7 @@ module Skeem
23
23
  ]
24
24
  samples.each do |source, predicted|
25
25
  ptree = subject.parse(source)
26
- expect(ptree.root).to be_kind_of(SExprBooleanNode)
26
+ expect(ptree.root).to be_kind_of(SExprBoolean)
27
27
  expect(ptree.root.value).to eq(predicted)
28
28
  end
29
29
  end
@@ -38,7 +38,7 @@ module Skeem
38
38
  ]
39
39
  samples.each do |source, predicted|
40
40
  ptree = subject.parse(source)
41
- expect(ptree.root).to be_kind_of(SExprIntegerNode)
41
+ expect(ptree.root).to be_kind_of(SExprInteger)
42
42
  expect(ptree.root.value).to eq(predicted)
43
43
  end
44
44
  end
@@ -53,7 +53,7 @@ module Skeem
53
53
  ]
54
54
  samples.each do |source, predicted|
55
55
  ptree = subject.parse(source)
56
- expect(ptree.root).to be_kind_of(SExprRealNode)
56
+ expect(ptree.root).to be_kind_of(SExprReal)
57
57
  expect(ptree.root.value).to eq(predicted)
58
58
  end
59
59
  end
@@ -64,7 +64,7 @@ module Skeem
64
64
  ]
65
65
  samples.each do |source, predicted|
66
66
  ptree = subject.parse(source)
67
- expect(ptree.root).to be_kind_of(SExprStringNode)
67
+ expect(ptree.root).to be_kind_of(SExprString)
68
68
  expect(ptree.root.value).to eq(predicted)
69
69
  end
70
70
  end
@@ -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(SExprIdentifierNode)
78
+ expect(ptree.root).to be_kind_of(SExprIdentifier)
79
79
  expect(ptree.root.value).to eq(predicted)
80
80
  end
81
81
  end
@@ -0,0 +1,15 @@
1
+ require_relative '../../spec_helper' # Use the RSpec framework
2
+
3
+ # Load the class under test
4
+ require_relative '../../../lib/skeem/primitive/primitive_builder'
5
+
6
+ module Skeem
7
+ describe Primitive::PrimitiveBuilder do
8
+
9
+ context 'Initialization:' do
10
+ it 'should be initialized without argument' do
11
+ expect { Interpreter.new() }.not_to raise_error
12
+ end
13
+ end # context
14
+ end # describe
15
+ end # module
@@ -0,0 +1,35 @@
1
+ require_relative '../spec_helper' # Use the RSpec framework
2
+ require_relative '../../lib/skeem/environment' # Load the class under test
3
+ require_relative '../../lib/skeem/runtime' # Load the class under test
4
+
5
+ module Skeem
6
+ describe Runtime do
7
+ let(:some_env) { Environment.new }
8
+ subject { Runtime.new(some_env) }
9
+
10
+ context 'Initialization:' do
11
+ it 'should be initialized with an environment' do
12
+ expect { Runtime.new(Environment.new) }.not_to raise_error
13
+ end
14
+
15
+ it 'should know the environment' do
16
+ expect(subject.environment).to eq(some_env)
17
+ end
18
+ end # context
19
+
20
+ context 'Provided services:' do
21
+ it 'should add entries to the environment' do
22
+ entry = double('dummy')
23
+ subject.define('dummy', entry)
24
+ expect(subject.environment.bindings.size).to eq(1)
25
+ end
26
+
27
+ it 'should know the keys in the environment' do
28
+ expect(subject.include?('dummy')).to be_falsey
29
+ entry = double('dummy')
30
+ subject.define('dummy', entry)
31
+ expect(subject.include?('dummy')).to be_truthy
32
+ end
33
+ end # context
34
+ end # describe
35
+ end # module
@@ -101,7 +101,7 @@ module Skeem
101
101
  it 'should tokenize strings' do
102
102
  examples = [
103
103
  # Some examples taken from R7RS document
104
- '"Hello world!"',
104
+ '"Hello, world"',
105
105
  '"The word \"recursion\" has many meanings."'
106
106
  ]
107
107
 
@@ -114,15 +114,12 @@ module Skeem
114
114
  end
115
115
  end
116
116
  end # context
117
-
118
-
119
117
  # For later:
120
118
  # "Another example:\ntwo lines of text"
121
119
  # "Here's text \
122
120
  # containing just one line"
123
121
  # "\x03B1; is named GREEK SMALL LETTER ALPHA."
124
122
 
125
-
126
123
  context 'Identifier recognition:' do
127
124
  it 'should tokenize identifier' do
128
125
  examples = [
@@ -142,6 +139,37 @@ module Skeem
142
139
  end
143
140
  end
144
141
  end # context
142
+
143
+ context 'Semi-colon comments:' do
144
+ it 'should skip heading comments' do
145
+ input = "; Starting comment\n \"Some text\""
146
+ subject.reinitialize(input)
147
+ token = subject.tokens.first
148
+ expect(token.terminal).to eq('STRING_LIT')
149
+ expect(token.lexeme).to eq('Some text')
150
+ end
151
+
152
+ it 'should skip trailing comments' do
153
+ input = "\"Some text\"; Trailing comment"
154
+ subject.reinitialize(input)
155
+ token = subject.tokens.first
156
+ expect(token.terminal).to eq('STRING_LIT')
157
+ expect(token.lexeme).to eq('Some text')
158
+ end
159
+
160
+ it 'should skip embedded comments' do
161
+ input = "\"First text\"; Middle comment\n\"Second text\""
162
+ subject.reinitialize(input)
163
+ tokens = subject.tokens
164
+ expect(tokens.size).to eq(2)
165
+ token = tokens[0]
166
+ expect(token.terminal).to eq('STRING_LIT')
167
+ expect(token.lexeme).to eq('First text')
168
+ token = tokens[1]
169
+ expect(token.terminal).to eq('STRING_LIT')
170
+ expect(token.lexeme).to eq('Second text')
171
+ end
172
+ end
145
173
  =begin
146
174
  context 'Scanning Scheme sample code' do
147
175
  it 'should read examples from lis.py page' 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.0.6
4
+ version: 0.0.7
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-01 00:00:00.000000000 Z
11
+ date: 2018-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley
@@ -85,17 +85,24 @@ files:
85
85
  - Rakefile
86
86
  - appveyor.yml
87
87
  - lib/skeem.rb
88
+ - lib/skeem/environment.rb
88
89
  - lib/skeem/grammar.rb
89
90
  - lib/skeem/interpreter.rb
90
91
  - lib/skeem/parser.rb
92
+ - lib/skeem/primitive/primitive_builder.rb
93
+ - lib/skeem/primitive_func.rb
94
+ - lib/skeem/runtime.rb
91
95
  - lib/skeem/s_expr_builder.rb
92
96
  - lib/skeem/s_expr_nodes.rb
93
97
  - lib/skeem/stoken.rb
94
98
  - lib/skeem/tokenizer.rb
95
99
  - lib/skeem/version.rb
96
100
  - skeem.gemspec
101
+ - spec/skeem/environment_spec.rb
97
102
  - spec/skeem/interpreter_spec.rb
98
103
  - spec/skeem/parser_spec.rb
104
+ - spec/skeem/primitive/primitive_builder_spec.rb
105
+ - spec/skeem/runtime_spec.rb
99
106
  - spec/skeem/tokenizer_spec.rb
100
107
  - spec/skeem_spec.rb
101
108
  - spec/spec_helper.rb
@@ -126,7 +133,10 @@ specification_version: 4
126
133
  summary: Skeem is an interpreter of a subset of the Scheme programming language. Scheme
127
134
  is a descendent of the Lisp language.
128
135
  test_files:
136
+ - spec/skeem/environment_spec.rb
129
137
  - spec/skeem/interpreter_spec.rb
130
138
  - spec/skeem/parser_spec.rb
139
+ - spec/skeem/primitive/primitive_builder_spec.rb
140
+ - spec/skeem/runtime_spec.rb
131
141
  - spec/skeem/tokenizer_spec.rb
132
142
  - spec/skeem_spec.rb