skeem 0.0.6 → 0.0.7

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: 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