skeem 0.0.25 → 0.0.26

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: bb979d01736d3b23101b56c2b7c21dff003eed46
4
- data.tar.gz: 91dddadb24f3e6cc1aa146f7c5420d4d88708d56
3
+ metadata.gz: dac0bc87694eef9365c7c7ba6dab9b1827eab533
4
+ data.tar.gz: 5097264ce99da2c1e9a8e662e2276144a31b1660
5
5
  SHA512:
6
- metadata.gz: 1a3db261dbca2bf57909b90de1db6897026eea3baa739e80a52c2ee045eee1be23d685d903757ed1fb1ca92daac80cbbcdc9fb015d7f6daeb7d275262f5952fe
7
- data.tar.gz: 27f38b7d92bbe22f339de58945fd7dad7efc9b57ffc1257f626fd3d97b5bd063c3432eb124e28e75d67ff35f7bf75470af15ea8ddb633850d4b21b76e122012f
6
+ metadata.gz: 25d09cd405ff9463eba60c098933df4804ff28051a663af4b1bed8217aee280ebfba0a4ff86391d7500b606ccbede4f3cb2021e9713c0535600ce0c30291f72d
7
+ data.tar.gz: 5e36e23fe874f945b394adb866b4ffb85b62a014a7afacd5653c279d4d7d9902f2b012ddf19b680259e8cc73e69385a0a9bd556b47f2c4ae46b7a35297d2974b
@@ -1,3 +1,24 @@
1
+ ## [0.0.26] - 2018-11-25
2
+
3
+ ### Added
4
+ - Procedure `eqv?`
5
+ - Procedure `assert`
6
+ - Class `Runtime`, call stack added.
7
+ - Class `ProcedureCall`, attribute `call_site` added. It contains the location of the call (line and column)
8
+
9
+ ### Changed
10
+ - File `primitive_builder.rb` implementation of: not procedure removed, `not` is now implemented in `base.skm`.
11
+ - Method `Tokenizer#build_token` updated to remain compatible with new Rley (> 0.7.x))
12
+
13
+ ### Removed
14
+ - File `stoken.rb`: Class `SkmToken` is no more necessary.
15
+
16
+ ## [0.0.25] - 2018-11-11
17
+ Aliasing of procedures with 'set!' is supported.
18
+
19
+ ### Added
20
+ - Procedures `string=?`, `symbol=?`
21
+
1
22
  ## [0.0.24] - 2018-11-08
2
23
  Many internal refactoring, augmented spec files base, initial quasiquotation implementation.
3
24
 
data/README.md CHANGED
@@ -166,6 +166,9 @@ __Syntax:__
166
166
  ### Standard library
167
167
  This section lists the implemented standard procedures
168
168
 
169
+ #### Equivalence predicates
170
+ * `eqv?`
171
+
169
172
  #### Boolean procedures
170
173
  * `boolean?`, `and`, `or`, `not`
171
174
 
@@ -189,6 +192,9 @@ This section lists the implemented standard procedures
189
192
  #### Input/output procedures
190
193
  * `newline`
191
194
 
195
+ #### Special procedures
196
+ * `assert`
197
+
192
198
  Roadmap:
193
199
  - Extend language support
194
200
  - Implement REPL
@@ -55,6 +55,7 @@ module Skeem
55
55
  end
56
56
 
57
57
  def add_comparison(aRuntime)
58
+ create_eqv?(aRuntime)
58
59
  create_equal(aRuntime)
59
60
  create_lt(aRuntime)
60
61
  create_gt(aRuntime)
@@ -66,11 +67,11 @@ module Skeem
66
67
  create_object_predicate(aRuntime, 'number?')
67
68
  create_object_predicate(aRuntime, 'real?')
68
69
  create_object_predicate(aRuntime, 'integer?')
70
+ create_object_predicate(aRuntime, 'exact?')
69
71
  create_number2string(aRuntime)
70
72
  end
71
73
 
72
74
  def add_boolean_procedures(aRuntime)
73
- create_not(aRuntime)
74
75
  create_and(aRuntime)
75
76
  create_or(aRuntime)
76
77
  create_object_predicate(aRuntime, 'boolean?')
@@ -106,6 +107,7 @@ module Skeem
106
107
  end
107
108
 
108
109
  def add_special_procedures(aRuntime)
110
+ create_assert(aRuntime)
109
111
  create_debug(aRuntime)
110
112
  end
111
113
 
@@ -194,7 +196,7 @@ module Skeem
194
196
  end
195
197
 
196
198
  def create_modulo(aRuntime)
197
- primitive = ->(runtime, argument1, argument2) do
199
+ primitive = ->(runtime, argument1, argument2) do
198
200
  operand_1 = argument1.evaluate(runtime)
199
201
  operand_2 = argument2.evaluate(runtime)
200
202
  raw_result = operand_1.value.modulo(operand_2.value)
@@ -203,6 +205,17 @@ module Skeem
203
205
 
204
206
  define_primitive_proc(aRuntime, 'floor-remainder', binary, primitive)
205
207
  end
208
+
209
+ def create_eqv?(aRuntime)
210
+ primitive = ->(runtime, argument1, argument2) do
211
+ operand_1 = argument1.evaluate(runtime)
212
+ operand_2 = argument2.evaluate(runtime)
213
+ raw_result = operand_1.eqv?(operand_2)
214
+ to_datum(raw_result)
215
+ end
216
+
217
+ define_primitive_proc(aRuntime, 'eqv?', binary, primitive)
218
+ end
206
219
 
207
220
  def create_equal(aRuntime)
208
221
  primitive = ->(runtime, first_operand, arglist) do
@@ -303,19 +316,6 @@ module Skeem
303
316
  define_primitive_proc(aRuntime, 'number->string', unary, primitive)
304
317
  end
305
318
 
306
- def create_not(aRuntime)
307
- primitive = ->(runtime, arg) do
308
- arg_evaluated = arg.evaluate(runtime)
309
- if arg_evaluated.boolean? && arg_evaluated.value == false
310
- boolean(true)
311
- else
312
- boolean(false)
313
- end
314
- end
315
-
316
- define_primitive_proc(aRuntime, 'not', unary, primitive)
317
- end
318
-
319
319
  def create_and(aRuntime)
320
320
  # arglist should be a Ruby Array
321
321
  primitive = ->(runtime, arglist) do
@@ -466,6 +466,23 @@ module Skeem
466
466
 
467
467
  define_primitive_proc(aRuntime, 'newline', nullary, primitive)
468
468
  end
469
+
470
+ def create_assert(aRuntime)
471
+ primitive = ->(runtime, arg) do
472
+ arg_evaluated = arg.evaluate(runtime)
473
+ if arg_evaluated.boolean? && arg_evaluated.value == false
474
+ assert_call = aRuntime.caller
475
+ pos = assert_call.call_site
476
+ # Error: assertion failed: (> 1 2)
477
+ msg = "assertion failed on line #{pos.line}, column #{pos.column}"
478
+ raise StandardError, 'Error: ' + msg
479
+ else
480
+ boolean(true)
481
+ end
482
+ end
483
+
484
+ define_primitive_proc(aRuntime, 'assert', unary, primitive)
485
+ end
469
486
 
470
487
  def create_debug(aRuntime)
471
488
  primitive = ->(runtime) do
@@ -4,9 +4,11 @@ require_relative 'environment'
4
4
  module Skeem
5
5
  class Runtime
6
6
  attr_reader(:environment)
7
+ attr_reader(:call_stack)
7
8
 
8
9
  def initialize(anEnvironment)
9
10
  @environment = anEnvironment
11
+ @call_stack = []
10
12
  end
11
13
 
12
14
  def include?(anIdentifier)
@@ -65,15 +67,34 @@ module Skeem
65
67
  return environment.depth
66
68
  end
67
69
 
70
+ def push(anEnvironment)
71
+ @environment = anEnvironment
72
+ end
73
+
68
74
  # Make the outer enviromnent thecurrent one inside the provided block
69
75
  def pop
70
76
  env = environment
71
77
  @environment = environment.outer
72
78
  env
73
79
  end
74
-
75
- def push(anEnvironment)
76
- @environment = anEnvironment
80
+
81
+ def push_call(aProcCall)
82
+ if aProcCall.kind_of?(ProcedureCall)
83
+ call_stack.push(aProcCall)
84
+ else
85
+ raise StandardError, "Invalid call object #{aProcCall.inspect}"
86
+ end
87
+ end
88
+
89
+ def pop_call()
90
+ if call_stack.empty?
91
+ raise StandardError, 'Skeem call stack empty!'
92
+ end
93
+ call_stack.pop
94
+ end
95
+
96
+ def caller
97
+ call_stack.last
77
98
  end
78
99
 
79
100
  private
@@ -112,7 +112,12 @@ module Skeem
112
112
 
113
113
  # rule('proc_call_args' => 'LPAREN operator operand_plus RPAREN')
114
114
  def reduce_proc_call_args(_production, aRange, _tokens, theChildren)
115
- ProcedureCall.new(aRange, theChildren[1], theChildren[2])
115
+ pcall = ProcedureCall.new(aRange, theChildren[1], theChildren[2])
116
+ if theChildren[1].kind_of?(SkmVariableReference)
117
+ pcall.call_site = theChildren[1].child.token.position
118
+ end
119
+
120
+ pcall
116
121
  end
117
122
 
118
123
  # rule('operand_plus' => 'operand_plus operand').as 'multiple_operands'
@@ -10,28 +10,28 @@ module Skeem
10
10
  def value
11
11
  :UNDEFINED
12
12
  end
13
-
13
+
14
14
  def ==(other)
15
15
  return true if other.kind_of?(SkmUndefined)
16
-
16
+
17
17
  result = case other
18
18
  when Symbol
19
19
  self.value == other
20
20
  when String
21
21
  self.value.to_s == other
22
- else
22
+ else
23
23
  raise StandardError, other.inspect
24
24
  end
25
25
  end
26
26
  end # class
27
-
27
+
28
28
  class SkmMultiExpression < SkmExpression
29
29
  # Part of the 'visitee' role in Visitor design pattern.
30
30
  # @param _visitor [SkmElementVisitor] the visitor
31
31
  def accept(aVisitor)
32
32
  aVisitor.visit_multi_expression(self)
33
33
  end
34
-
34
+
35
35
  # @return [Array] the names of attributes referencing child SkmElement.
36
36
  def associations
37
37
  raise NotImplementedError
@@ -66,7 +66,7 @@ module Skeem
66
66
  if entry.expression.kind_of?(SkmLambda)
67
67
  @expression = entry.expression
68
68
  end
69
- end
69
+ end
70
70
  else
71
71
  # INFINITE LOOP DANGER: definition of 'x' is a reference to 'x'!
72
72
  # Way out: the lookup for the reference should start from outer
@@ -82,11 +82,11 @@ module Skeem
82
82
 
83
83
  result
84
84
  end
85
-
85
+
86
86
  def quasiquote(aRuntime)
87
87
  quasi_var = variable.quasiquote(aRuntime)
88
88
  quasi_expression = variable.quasiquote(aRuntime)
89
-
89
+
90
90
  if quasi_var.equal?(variable) && quasi_expression.equal?(expression)
91
91
  self
92
92
  else
@@ -96,7 +96,7 @@ module Skeem
96
96
 
97
97
  # call method should only invoked when the expression is a SkmLambda
98
98
  def call(aRuntime, aProcedureCall)
99
- unless [SkmLambda, Primitive::PrimitiveProcedure].include?(expression.class)
99
+ unless [SkmLambda, Primitive::PrimitiveProcedure].include?(expression.class)
100
100
  err_msg = "Expected a SkmLambda instead of #{expression.class}"
101
101
  raise StandardError, err_msg
102
102
  end
@@ -111,7 +111,7 @@ module Skeem
111
111
  result << inspect_suffix
112
112
  result
113
113
  end
114
-
114
+
115
115
  def associations
116
116
  [:variable, :expression]
117
117
  end
@@ -120,6 +120,7 @@ module Skeem
120
120
  class ProcedureCall < SkmMultiExpression
121
121
  attr_reader :operator
122
122
  attr_reader :operands
123
+ attr_accessor :call_site
123
124
 
124
125
  def initialize(aPosition, anOperator, theOperands)
125
126
  super(aPosition)
@@ -147,16 +148,18 @@ module Skeem
147
148
  # $stderr.puts "## CALL(#{var_key.value}) ###################"
148
149
  # $stderr.puts operands.inspect
149
150
  end
151
+ aRuntime.push_call(self)
150
152
  result = procedure.call(aRuntime, self)
153
+ aRuntime.pop_call
151
154
  # $stderr.puts "## RETURN #{result.inspect}"
152
155
  result
153
156
  end
154
-
157
+
155
158
  def quasiquote(aRuntime)
156
159
  quasi_operator = operator.quasiquote(aRuntime)
157
160
  quasi_operands = operands.map { |oper | oper.quasiquote(aRuntime) }
158
-
159
- self.class.new(position, quasi_operator, quasi_operands)
161
+
162
+ self.class.new(position, quasi_operator, quasi_operands)
160
163
  end
161
164
 
162
165
  def inspect
@@ -164,7 +167,7 @@ module Skeem
164
167
  result << '@operands ' + operands.inspect + inspect_suffix
165
168
  result
166
169
  end
167
-
170
+
168
171
  def associations
169
172
  [:operator, :operands]
170
173
  end
@@ -194,14 +197,14 @@ module Skeem
194
197
  condition_result = consequent.evaluate(aRuntime)
195
198
  end
196
199
  end
197
-
200
+
198
201
  def quasiquote(aRuntime)
199
202
  quasi_test = test.quasiquote(aRuntime)
200
203
  quasi_consequent = consequent.quasiquote(aRuntime)
201
204
  quasi_alternate = alternate.quasiquote(aRuntime)
202
-
203
- self.class.new(position, quasi_test, quasi_consequent, quasi_alternate)
204
- end
205
+
206
+ self.class.new(position, quasi_test, quasi_consequent, quasi_alternate)
207
+ end
205
208
 
206
209
  def inspect
207
210
  result = inspect_prefix + '@test ' + test.inspect + ', '
@@ -209,10 +212,10 @@ module Skeem
209
212
  result << '@alternate ' + alternate.inspect + inspect_suffix
210
213
  result
211
214
  end
212
-
215
+
213
216
  def associations
214
217
  [:test, :consequent, :alternate]
215
- end
218
+ end
216
219
  end # class
217
220
 
218
221
  SkmArity = Struct.new(:low, :high) do
@@ -301,15 +304,15 @@ module Skeem
301
304
  def evaluate(aRuntime)
302
305
  formals.evaluate(aRuntime)
303
306
  end
304
- =begin
307
+ =begin
305
308
  TODO
306
309
  def quasiquote(aRuntime)
307
310
  quasi_test = test.quasiquote(aRuntime)
308
311
  quasi_consequent = consequent.quasiquote(aRuntime)
309
312
  quasi_alternate = alternate.quasiquote(aRuntime)
310
-
311
- self.class.new(position, quasi_test, quasi_consequent, quasi_alternate)
312
- end
313
+
314
+ self.class.new(position, quasi_test, quasi_consequent, quasi_alternate)
315
+ end
313
316
  =end
314
317
  def call(aRuntime, aProcedureCall)
315
318
  aRuntime.nest
@@ -331,13 +334,15 @@ module Skeem
331
334
  formals.required_arity
332
335
  end
333
336
 
337
+ alias eqv? equal?
338
+
334
339
  def inspect
335
340
  result = inspect_prefix + '@formals ' + formals.inspect + ', '
336
341
  result << '@definitions ' + definitions.inspect + ', '
337
342
  result << '@sequence ' + sequence.inspect + inspect_suffix
338
343
  result
339
344
  end
340
-
345
+
341
346
  def associations
342
347
  [:formals, :definitions, :sequence]
343
348
  end
@@ -26,6 +26,8 @@ module Skeem
26
26
  members == other
27
27
  end
28
28
  end
29
+
30
+ alias eqv? equal?
29
31
 
30
32
  def evaluate(aRuntime)
31
33
  members_eval = members.map { |elem| elem.evaluate(aRuntime) }
@@ -19,9 +19,13 @@ module Skeem
19
19
  return lightweight
20
20
  end
21
21
 
22
- def symbol()
22
+ def symbol
23
23
  token.terminal
24
24
  end
25
+
26
+ def position
27
+ token.position
28
+ end
25
29
 
26
30
  # Equality operator.
27
31
  # Returns true when: self and 'other' are identical, or
@@ -39,6 +43,8 @@ module Skeem
39
43
 
40
44
  result
41
45
  end
46
+
47
+ alias eqv? ==
42
48
 
43
49
  def done!()
44
50
  # Do nothing
@@ -87,18 +93,42 @@ module Skeem
87
93
  def number?
88
94
  true
89
95
  end
96
+
97
+ def eqv?(other)
98
+ return true if self.equal?(other)
99
+
100
+ result = if other.kind_of?(SkmNumber)
101
+ if self.exact? != other.exact?
102
+ false
103
+ else
104
+ self.value == other.value
105
+ end
106
+ else
107
+ self.value == other
108
+ end
109
+
110
+ result
111
+ end
90
112
  end # class
91
113
 
92
114
  class SkmReal < SkmNumber
93
115
  def real?
94
116
  true
95
117
  end
118
+
119
+ def exact?
120
+ false
121
+ end
96
122
  end # class
97
123
 
98
124
  class SkmInteger < SkmReal
99
125
  def integer?
100
126
  true
101
127
  end
128
+
129
+ def exact?
130
+ true
131
+ end
102
132
  end # class
103
133
 
104
134
  class SkmString < SkmSimpleDatum
@@ -111,6 +141,8 @@ module Skeem
111
141
  true
112
142
  end
113
143
 
144
+ alias eqv? equal?
145
+
114
146
  def length
115
147
  value.length
116
148
  end
@@ -88,6 +88,10 @@ module Skeem
88
88
 
89
89
  class SkmVariableReference < SkmUnaryExpression
90
90
  alias variable child
91
+
92
+ def eqv?(other)
93
+ child == other.child
94
+ end
91
95
 
92
96
  def evaluate(aRuntime)
93
97
  var_key = variable.evaluate(aRuntime)
@@ -1,10 +1,14 @@
1
1
  ; Standard R7RS procedures from section 6.2.6
2
+
3
+ ; (zero? z)
4
+ ; Returns true iff z is zero
2
5
  (define zero?
3
6
  (lambda (z)
4
7
  (if (= z 0)
5
8
  #t
6
9
  #f)))
7
10
 
11
+ ; (positive? x)
8
12
  ; Return true if x greater or equal to zero; false otherwise
9
13
  (define positive?
10
14
  (lambda (x)
@@ -12,29 +16,43 @@
12
16
  #t
13
17
  #f)))
14
18
 
19
+ ; (negative? x)
20
+ ; Returns true iff x < 0
15
21
  (define negative?
16
22
  (lambda (x)
17
23
  (if (< x 0)
18
24
  #t
19
25
  #f)))
20
26
 
27
+ ; (inexact? z)
28
+ ; Returns true iff z is not an exact number
29
+ (define inexact?
30
+ (lambda (z)
31
+ (not (exact? z))))
32
+
21
33
  ; For backwards compatibility
22
34
  (define modulo
23
35
  (lambda (x y)
24
36
  (floor-remainder x y)))
25
37
 
38
+ ; (odd? n)
39
+ ; Returns true iff n is odd
26
40
  (define odd?
27
41
  (lambda (n)
28
42
  (if (= (modulo n 2) 1)
29
43
  #t
30
44
  #f)))
31
45
 
46
+ ; (even? n)
47
+ ; Returns true iff n is even
32
48
  (define even?
33
49
  (lambda (n)
34
50
  (if (= (modulo n 2) 0)
35
51
  #t
36
52
  #f)))
37
53
 
54
+ ; (abs x)
55
+ ; Returns the absolute value of a number
38
56
  (define abs
39
57
  (lambda (x)
40
58
  (if (positive? x)
@@ -45,7 +63,15 @@
45
63
  (lambda (z)
46
64
  (* z z)))
47
65
 
48
- (define symbol=? string=?)
49
-
66
+ ; (not obj)
67
+ ; Logical inverse of obj
68
+ (define (not obj)
69
+ (if obj #f #t))
70
+
71
+ ; (list arg ...)
72
+ ; Allocates and returns a new list from its arguments
50
73
  (define list
51
- (lambda args args))
74
+ (lambda args args))
75
+
76
+
77
+ (define symbol=? string=?)
@@ -1,7 +1,7 @@
1
1
  # File: tokenizer.rb
2
2
  # Tokenizer for Skeem language (a small subset of Scheme)
3
3
  require 'strscan'
4
- require_relative 'stoken'
4
+ require 'rley'
5
5
 
6
6
  module Skeem
7
7
  # A tokenizer for the Skeem dialect.
@@ -58,7 +58,7 @@ module Skeem
58
58
  @line_start = 0
59
59
  end
60
60
 
61
- # @return [Array<SToken>] | Returns a sequence of tokens
61
+ # @return [Array<SkmToken>] | Returns a sequence of tokens
62
62
  def tokens
63
63
  tok_sequence = []
64
64
  until @scanner.eos?
@@ -148,8 +148,8 @@ other literal data (section 2.4).
148
148
  begin
149
149
  value = convert_to(aLexeme, aSymbolName, aFormat)
150
150
  col = scanner.pos - aLexeme.size - @line_start + 1
151
- pos = Position.new(@lineno, col)
152
- token = SToken.new(value, aSymbolName, pos)
151
+ pos = Rley::Lexical::Position.new(@lineno, col)
152
+ token = Rley::Lexical::Token.new(value, aSymbolName, pos)
153
153
  rescue StandardError => exc
154
154
  puts "Failing with '#{aSymbolName}' and '#{aLexeme}'"
155
155
  raise exc
@@ -1,3 +1,3 @@
1
1
  module Skeem
2
- VERSION = '0.0.25'.freeze
2
+ VERSION = '0.0.26'.freeze
3
3
  end
@@ -55,7 +55,7 @@ SUMMARY
55
55
  PkgExtending.pkg_files(spec)
56
56
  PkgExtending.pkg_documentation(spec)
57
57
  # Runtime dependencies
58
- spec.add_dependency 'rley', '~> 0.6'
58
+ spec.add_dependency 'rley', '~> 0.7'
59
59
 
60
60
  # Development dependencies
61
61
  spec.add_development_dependency 'bundler', '~> 1.16'
@@ -568,18 +568,22 @@ SKEEM
568
568
  end
569
569
  end
570
570
 
571
- it 'should implement the symbol=? procedure' do
571
+ it 'should implement the not procedure' do
572
572
  checks = [
573
- ["(symbol=? 'a 'a)", true],
574
- ["(symbol=? 'a (string->symbol \"a\"))", true],
575
- ["(symbol=? 'a 'b)", false]
573
+ ['(not #t)', false],
574
+ ['(not 3)', false],
575
+ ['(not (list 3))', false],
576
+ ['(not #f)', true],
577
+ ["(not '())", false],
578
+ ['(not (list))', false],
579
+ ["(not 'nil)", false]
576
580
  ]
577
581
  checks.each do |(skeem_expr, expectation)|
578
582
  result = subject.run(skeem_expr)
579
583
  expect(result).to eq(expectation)
580
584
  end
581
- end
582
-
585
+ end
586
+
583
587
  it 'should implement the list procedure' do
584
588
  checks = [
585
589
  ['(list)', []],
@@ -591,6 +595,18 @@ SKEEM
591
595
  expect(result.members).to eq(expectation)
592
596
  end
593
597
  end
598
+
599
+ it 'should implement the symbol=? procedure' do
600
+ checks = [
601
+ ["(symbol=? 'a 'a)", true],
602
+ ["(symbol=? 'a (string->symbol \"a\"))", true],
603
+ ["(symbol=? 'a 'b)", false]
604
+ ]
605
+ checks.each do |(skeem_expr, expectation)|
606
+ result = subject.run(skeem_expr)
607
+ expect(result).to eq(expectation)
608
+ end
609
+ end
594
610
  end # context
595
611
  end # describe
596
612
  end # module
@@ -88,6 +88,26 @@ SKEEM
88
88
  end # context
89
89
 
90
90
  context 'Comparison operators' do
91
+ it 'should implement the eqv? procedure' do
92
+ checks = [
93
+ ['(eqv? #f #f)', true],
94
+ ['(eqv? #t #t)', true],
95
+ ['(eqv? #f #t)', false],
96
+ ["(eqv? 'a 'a)", true],
97
+ ["(eqv? 'a 'b)", false],
98
+ ['(eqv? 2 2)', true],
99
+ ['(eqv? 2 2.0)', false],
100
+ ['(eqv? 3 2)', false],
101
+ ['(eqv? 100000000 100000000)', true],
102
+ ['(eqv? "a" "a")', false],
103
+ ['(eqv? "a" "b")', false]
104
+ ]
105
+ checks.each do |(skeem_expr, expectation)|
106
+ result = subject.run(skeem_expr)
107
+ expect(result).to eq(expectation)
108
+ end
109
+ end
110
+
91
111
  it 'should implement the equality operator' do
92
112
  checks = [
93
113
  ['(= 3)', true], # '=' as unary operator
@@ -222,18 +242,6 @@ SKEEM
222
242
  end # context
223
243
 
224
244
  context 'Boolean procedures:' do
225
- it 'should implement the not procedure' do
226
- checks = [
227
- ['(not #t)', false],
228
- ['(not 3)', false],
229
- ['(not #f)', true]
230
- ]
231
- checks.each do |(skeem_expr, expectation)|
232
- result = subject.run(skeem_expr)
233
- expect(result).to eq(expectation)
234
- end
235
- end
236
-
237
245
  it 'should implement the and procedure' do
238
246
  checks = [
239
247
  ['(and (= 2 2) (> 2 1))', true],
@@ -280,7 +288,8 @@ SKEEM
280
288
  it 'should implement the boolean? procedure' do
281
289
  checks = [
282
290
  ['(boolean? #f)', true],
283
- ['(boolean? 0)', false]
291
+ ['(boolean? 0)', false],
292
+ ["(boolean? '())", false]
284
293
  ]
285
294
  checks.each do |(skeem_expr, expectation)|
286
295
  result = subject.run(skeem_expr)
@@ -469,6 +478,28 @@ SKEEM
469
478
  $stdout = default_stdout
470
479
  end
471
480
  end # context
481
+
482
+ context 'Miscellaneous procedures' do
483
+ it 'should return true when an assertion succeeds' do
484
+ source = <<-SKEEM
485
+ (define x 2)
486
+ (define y 1)
487
+ (assert (> x y))
488
+ SKEEM
489
+ expect(subject.run(source).last).to eq(true)
490
+ end
491
+
492
+ it 'should raise an error when an assertion fails' do
493
+ source = <<-SKEEM
494
+ (define x 1)
495
+ (define y 2)
496
+ (assert (> x y))
497
+ SKEEM
498
+ err = StandardError
499
+ msg = 'Error: assertion failed on line 3, column 4'
500
+ expect { subject.run(source) }.to raise_error(err, msg)
501
+ end
502
+ end # context
472
503
  end # describe
473
504
  end # module
474
505
  end # module
@@ -1,5 +1,6 @@
1
1
  require_relative '../spec_helper' # Use the RSpec framework
2
2
  require_relative '../../lib/skeem/datum_dsl'
3
+ require_relative '../../lib/skeem/s_expr_nodes'
3
4
  require_relative '../../lib/skeem/primitive/primitive_builder'
4
5
 
5
6
  require_relative '../../lib/skeem/runtime' # Load the class under test
@@ -19,6 +20,10 @@ module Skeem
19
20
  it 'should know the environment' do
20
21
  expect(subject.environment).to eq(some_env)
21
22
  end
23
+
24
+ it 'should have an empty call stack' do
25
+ expect(subject.call_stack).to be_empty
26
+ end
22
27
  end # context
23
28
 
24
29
  context 'Provided services:' do
@@ -56,7 +61,7 @@ module Skeem
56
61
  end
57
62
  end # context
58
63
 
59
- context 'Environment nesting' do
64
+ context 'Environment nesting:' do
60
65
  it 'should add nested environment' do
61
66
  expect(subject.depth).to be_zero
62
67
  env_before = subject.environment
@@ -78,5 +83,31 @@ module Skeem
78
83
  expect(subject.depth).to be_zero
79
84
  end
80
85
  end # context
86
+
87
+ context 'Call stack operations:' do
88
+ let(:sample_call) do
89
+ pos = double('fake-position')
90
+ ProcedureCall.new(pos, identifier('boolean?'), [integer(42)])
91
+ end
92
+
93
+ it 'should push a call to the stack call' do
94
+ expect { subject.push_call(sample_call) }.not_to raise_error
95
+ expect(subject.call_stack.size). to eq(1)
96
+ expect(subject.caller).to eq(sample_call)
97
+
98
+ subject.push_call(sample_call.clone)
99
+ expect(subject.call_stack.size). to eq(2)
100
+ end
101
+
102
+ it 'should pop a call from the call stack' do
103
+ subject.push_call(sample_call)
104
+ expect { subject.pop_call }.not_to raise_error
105
+ expect(subject.call_stack).to be_empty
106
+
107
+ err = StandardError
108
+ msg = 'Skeem call stack empty!'
109
+ expect { subject.pop_call }.to raise_error(err, msg)
110
+ end
111
+ end # context
81
112
  end # describe
82
113
  end # module
@@ -155,6 +155,19 @@ module Skeem
155
155
  it 'should react positively to real? predicate' do
156
156
  expect(subject).to be_real
157
157
  end
158
+
159
+ it 'should react negatively to exact? predicate' do
160
+ expect(subject).not_to be_exact
161
+ end
162
+
163
+ it 'should implement the eqv? predicate' do
164
+ same = SkmReal.create(0.51)
165
+ different = SkmReal.create(1.21)
166
+
167
+ expect(subject).to be_eqv(subject)
168
+ expect(subject).to be_eqv(same)
169
+ expect(subject).not_to be_eqv(different)
170
+ end
158
171
  end # context
159
172
  end # describe
160
173
 
@@ -182,6 +195,20 @@ module Skeem
182
195
  it 'should react positively to integer? predicate' do
183
196
  expect(subject).to be_real
184
197
  end
198
+
199
+ it 'should react positively to exact? predicate' do
200
+ expect(subject).to be_exact
201
+ end
202
+
203
+ it 'should implement the eqv? predicate' do
204
+ three = SkmInteger.create(3)
205
+ real_3 = SkmReal.create(3.0)
206
+ four = SkmInteger.create(4)
207
+
208
+ expect(subject).to be_eqv(three)
209
+ expect(subject).not_to be_eqv(real_3)
210
+ expect(subject).not_to be_eqv(four)
211
+ end
185
212
  end # context
186
213
  end # describe
187
214
 
@@ -32,7 +32,7 @@ module Skeem
32
32
  it 'should tokenize single char delimiters' do
33
33
  subject.reinitialize("( ) ' ` . , ,@")
34
34
  tokens = subject.tokens
35
- tokens.each { |token| expect(token).to be_kind_of(SToken) }
35
+ tokens.each { |token| expect(token).to be_kind_of(Rley::Lexical::Token) }
36
36
  terminals = tokens.map(&:terminal)
37
37
  prediction = %w[LPAREN RPAREN APOSTROPHE
38
38
  GRAVE_ACCENT PERIOD
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.25
4
+ version: 0.0.26
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-11 00:00:00.000000000 Z
11
+ date: 2018-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.6'
19
+ version: '0.7'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.6'
26
+ version: '0.7'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -102,7 +102,6 @@ files:
102
102
  - lib/skeem/skm_simple_datum.rb
103
103
  - lib/skeem/skm_unary_expression.rb
104
104
  - lib/skeem/standard/base.skm
105
- - lib/skeem/stoken.rb
106
105
  - lib/skeem/tokenizer.rb
107
106
  - lib/skeem/version.rb
108
107
  - skeem.gemspec
@@ -1,22 +0,0 @@
1
- require 'rley' # Load the Rley gem
2
-
3
- module Skeem
4
- Position = Struct.new(:line, :column) do
5
- def to_s
6
- "line #{line}, column #{column}"
7
- end
8
- end
9
-
10
- # Specialization of Token class.
11
- # It stores the position in (line, row) of the token
12
- class SToken < Rley::Lexical::Token
13
- attr_reader(:position)
14
-
15
- def initialize(theLexeme, aTerminal, aPosition)
16
- super(theLexeme, aTerminal)
17
- @position = aPosition
18
- end
19
- end # class
20
- end # module
21
-
22
- # End of file