skeem 0.0.25 → 0.0.26

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