skeem 0.2.04 → 0.2.05
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +2 -2
- data/lib/skeem/grammar.rb +5 -2
- data/lib/skeem/primitive/primitive_builder.rb +53 -0
- data/lib/skeem/s_expr_builder.rb +5 -4
- data/lib/skeem/s_expr_nodes.rb +167 -13
- data/lib/skeem/skm_empty_list.rb +16 -8
- data/lib/skeem/skm_pair.rb +29 -4
- data/lib/skeem/skm_procedure_exec.rb +5 -3
- data/lib/skeem/skm_unary_expression.rb +64 -17
- data/lib/skeem/version.rb +1 -1
- data/spec/skeem/interpreter_spec.rb +20 -6
- data/spec/skeem/primitive/primitive_builder_spec.rb +33 -0
- data/spec/skeem/s_expr_nodes_spec.rb +4 -4
- data/spec/skeem/skm_pair_spec.rb +42 -1
- data/spec/skeem/skm_unary_expression_spec.rb +3 -4
- metadata +2 -5
- data/lib/skeem/environment.rb +0 -80
- data/spec/skeem/environment_spec.rb +0 -81
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8c3a61e9162fe0df33e51f373614d56cac6e5bc9c4743a101b8805bad158843
|
4
|
+
data.tar.gz: 52feb4bc5edf9e9cf64089ef0e3fbb6cff2d917d964ecdf4d1e00d1d1409caed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e99664327739a23130244b2cff9b81e9ad9be83773efc057cdb636f8642a3a1492a8de5ed0133857582c036228f27c22c5bf3e17949bd577f162b679c7aa090
|
7
|
+
data.tar.gz: 9014bdfed6b945a604ab55f940de00c9288773a0dd9a192825b5cd83e99d79c6434252e96289abbfe10b7f742ffa117034f767685c929b5dbefcb2d27e7c36d3
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.2.05] - 2019-05-26
|
2
|
+
- Passing more standard Scheme tests, `append` procedure implemented.
|
3
|
+
|
4
|
+
### Added
|
5
|
+
- File `primitive_builder.rb`: Added implementation for standard procedure `append`.
|
6
|
+
- Class `LambdaRep`: represents a parse tree of a lambda expression.
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
- File `grammar.rb`: Changed a grammar rule for (begin ...) expression because R7RS was too restrictive compared to main implementations.
|
10
|
+
- Class `SkmBuilder` several method updates (e.g. `reduce_alt_definition`)
|
11
|
+
|
12
|
+
### Removed
|
13
|
+
- Class `Environment` superseded by class `SkmFrame`
|
14
|
+
|
1
15
|
## [0.2.04] - 2019-04-29
|
2
16
|
- Support for local definitions [initial]
|
3
17
|
|
data/README.md
CHANGED
@@ -221,7 +221,7 @@ This section lists the implemented standard procedures
|
|
221
221
|
* Integer-level: `even?`, `odd?`
|
222
222
|
|
223
223
|
#### List procedures
|
224
|
-
* `list?`, `null?`, `pair?`, `car`, `cdr`, `caar`, `cadr`, `cdar`, `cddr`, `cons`, `length`, `list`, `list->vector`, `set-car!`, `set-cdr!`
|
224
|
+
* `list?`, `null?`, `pair?`, `append`, `car`, `cdr`, `caar`, `cadr`, `cdar`, `cddr`, `cons`, `length`, `list`, `list->vector`, `set-car!`, `set-cdr!`
|
225
225
|
|
226
226
|
#### String procedures
|
227
227
|
* `string?`, `string=?`, `string-append`, `string-length`, `string->symbol`
|
@@ -255,7 +255,7 @@ Here are a few other ones:
|
|
255
255
|
|
256
256
|
- [rubic gem](https://rubygems.org/gems/rubic). The last commit for the [project](https://github.com/notozeki/rubic) is June 2015.
|
257
257
|
|
258
|
-
- [RLisp](https://github.com/davydovanton/rlisp) ...Simple scheme interpreter written
|
258
|
+
- [RLisp](https://github.com/davydovanton/rlisp) ...Simple scheme interpreter written in ruby
|
259
259
|
## Contributing
|
260
260
|
|
261
261
|
Bug reports and pull requests are welcome on GitHub at https://github.com/famished-tiger/Skeem.
|
data/lib/skeem/grammar.rb
CHANGED
@@ -39,7 +39,7 @@ module Skeem
|
|
39
39
|
rule 'expression' => 'procedure_call'
|
40
40
|
rule 'expression' => 'lambda_expression'
|
41
41
|
rule 'expression' => 'conditional'
|
42
|
-
rule 'expression' => 'assignment'
|
42
|
+
rule 'expression' => 'assignment'
|
43
43
|
rule 'expression' => 'derived_expression'
|
44
44
|
rule 'literal' => 'quotation'
|
45
45
|
rule 'literal' => 'self-evaluating'
|
@@ -97,7 +97,10 @@ module Skeem
|
|
97
97
|
rule('assignment' => 'LPAREN SET! IDENTIFIER expression RPAREN').as 'assignment'
|
98
98
|
rule('derived_expression' => 'LPAREN LET LPAREN binding_spec_star RPAREN body RPAREN').as 'short_let_form'
|
99
99
|
rule('derived_expression' => 'LPAREN LET* LPAREN binding_spec_star RPAREN body RPAREN').as 'let_star_form'
|
100
|
-
|
100
|
+
|
101
|
+
# As the R7RS grammar is too restrictive,
|
102
|
+
# the next rule was made more general than its standard counterpart
|
103
|
+
rule('derived_expression' => 'LPAREN BEGIN body RPAREN').as 'begin_expression'
|
101
104
|
rule 'derived_expression' => 'quasiquotation'
|
102
105
|
rule('quasiquotation' => 'LPAREN QUASIQUOTE qq_template RPAREN').as 'quasiquotation'
|
103
106
|
rule('quasiquotation' => 'GRAVE_ACCENT qq_template').as 'quasiquotation_short'
|
@@ -104,6 +104,7 @@ module Skeem
|
|
104
104
|
create_cdr(aRuntime)
|
105
105
|
create_length(aRuntime)
|
106
106
|
create_list2vector(aRuntime)
|
107
|
+
create_append(aRuntime)
|
107
108
|
create_setcar(aRuntime)
|
108
109
|
create_setcdr(aRuntime)
|
109
110
|
end
|
@@ -485,6 +486,44 @@ module Skeem
|
|
485
486
|
|
486
487
|
define_primitive_proc(aRuntime, 'list->vector', unary, primitive)
|
487
488
|
end
|
489
|
+
|
490
|
+
def create_append(aRuntime)
|
491
|
+
primitive = ->(runtime, arglist) do
|
492
|
+
if arglist.empty?
|
493
|
+
result = SkmEmptyList.instance
|
494
|
+
elsif arglist.size == 1
|
495
|
+
result = arglist[0]
|
496
|
+
else
|
497
|
+
parts = evaluate_arguments(arglist, aRuntime)
|
498
|
+
but_last = parts.take(parts.length - 1)
|
499
|
+
check_arguments(but_last, [SkmPair, SkmEmptyList], 'list', 'append')
|
500
|
+
result = parts.shift.klone # First list is taken
|
501
|
+
parts.each do |arg|
|
502
|
+
case arg
|
503
|
+
when SkmPair
|
504
|
+
cloned = arg.klone
|
505
|
+
if result.kind_of?(SkmEmptyList)
|
506
|
+
result = cloned
|
507
|
+
else
|
508
|
+
if result.kind_of?(SkmEmptyList)
|
509
|
+
result = SkmPair.new(arg, SkmEmptyList.instance)
|
510
|
+
else
|
511
|
+
result.append_list(cloned)
|
512
|
+
end
|
513
|
+
end
|
514
|
+
when SkmEmptyList
|
515
|
+
# Do nothing
|
516
|
+
else
|
517
|
+
result.append(arg)
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
result
|
523
|
+
end
|
524
|
+
|
525
|
+
define_primitive_proc(aRuntime, 'append', zero_or_more, primitive)
|
526
|
+
end
|
488
527
|
|
489
528
|
def create_setcar(aRuntime)
|
490
529
|
primitive = ->(runtime, pair_arg, obj_arg) do
|
@@ -682,6 +721,20 @@ module Skeem
|
|
682
721
|
arglist.evaluate(aRuntime).to_a
|
683
722
|
end
|
684
723
|
end
|
724
|
+
|
725
|
+
def check_arguments(arguments, requiredRubyClass, requiredSkmType, aProcName)
|
726
|
+
arguments.each do |argument|
|
727
|
+
if requiredRubyClass.kind_of?(Array)
|
728
|
+
unless requiredRubyClass.include?(argument.class)
|
729
|
+
type_error(argument, requiredSkmType, aProcName)
|
730
|
+
end
|
731
|
+
else
|
732
|
+
unless argument.kind_of?(requiredRubyClass)
|
733
|
+
type_error(argument, requiredSkmType, aProcName)
|
734
|
+
end
|
735
|
+
end
|
736
|
+
end
|
737
|
+
end
|
685
738
|
|
686
739
|
def check_argtype(argument, requiredRubyClass, requiredSkmType, aProcName)
|
687
740
|
if requiredRubyClass.kind_of?(Array)
|
data/lib/skeem/s_expr_builder.rb
CHANGED
@@ -73,7 +73,7 @@ module Skeem
|
|
73
73
|
# rule('definition' => 'LPAREN DEFINE LPAREN IDENTIFIER def_formals RPAREN body RPAREN').as 'alt_definition'
|
74
74
|
# Equivalent to: (define IDENTIFIER (lambda (formals) body))
|
75
75
|
def reduce_alt_definition(_production, aRange, _tokens, theChildren)
|
76
|
-
lmbd =
|
76
|
+
lmbd = SkmLambdaRep.new(aRange, theChildren[4], theChildren[6])
|
77
77
|
# $stderr.puts lmbd.inspect
|
78
78
|
SkmBinding.new(theChildren[3], lmbd)
|
79
79
|
end
|
@@ -190,7 +190,7 @@ module Skeem
|
|
190
190
|
|
191
191
|
# rule('lambda_expression' => 'LPAREN LAMBDA formals body RPAREN').as 'lambda_expression'
|
192
192
|
def reduce_lambda_expression(_production, aRange, _tokens, theChildren)
|
193
|
-
lmbd =
|
193
|
+
lmbd = SkmLambdaRep.new(aRange, theChildren[2], theChildren[3])
|
194
194
|
# $stderr.puts lmbd.inspect
|
195
195
|
lmbd
|
196
196
|
end
|
@@ -251,6 +251,7 @@ module Skeem
|
|
251
251
|
# rule('sequence' => 'command_star expression').as 'sequence'
|
252
252
|
def reduce_sequence(_production, _range, _tokens, theChildren)
|
253
253
|
SkmPair.create_from_a(theChildren[0] << theChildren[1])
|
254
|
+
|
254
255
|
end
|
255
256
|
|
256
257
|
# rule('command_star' => 'command_star command').as 'multiple_commands'
|
@@ -283,9 +284,9 @@ module Skeem
|
|
283
284
|
SkmBindingBlock.new(:let_star, theChildren[3], theChildren[5])
|
284
285
|
end
|
285
286
|
|
286
|
-
# rule('derived_expression' => 'LPAREN BEGIN
|
287
|
+
# rule('derived_expression' => 'LPAREN BEGIN body RPAREN').as 'begin_expression'
|
287
288
|
def reduce_begin_expression(_production, aRange, _tokens, theChildren)
|
288
|
-
SkmSequencingBlock.new(theChildren[2])
|
289
|
+
SkmSequencingBlock.new(theChildren[2])
|
289
290
|
end
|
290
291
|
|
291
292
|
# rule('quasiquotation' => 'LPAREN QUASIQUOTE qq_template RPAREN').as 'quasiquotation'
|
data/lib/skeem/s_expr_nodes.rb
CHANGED
@@ -71,12 +71,12 @@ module Skeem
|
|
71
71
|
# $stderr.puts "Parent environment #{aRuntime.environment.parent.object_id.to_s(16)}, "
|
72
72
|
# $stderr.puts aRuntime.environment.inspect
|
73
73
|
end
|
74
|
-
# $stderr.puts ' operator: ' + operator.inspect
|
74
|
+
# $stderr.puts ' operator: ' + (operands.kind_of?(SkmLambda) ? "lambda #{object_id.to_s(16)}" : operator.inspect)
|
75
75
|
# $stderr.puts ' original operands: ' + operands.inspect
|
76
|
-
actuals = transform_operands(aRuntime)
|
77
|
-
# $stderr.puts ' transformed operands: ' + actuals.inspect
|
78
76
|
outcome, result = determine_callee(aRuntime)
|
79
77
|
if outcome == :callee
|
78
|
+
actuals = transform_operands(aRuntime)
|
79
|
+
# $stderr.puts ' transformed operands: ' + actuals.inspect
|
80
80
|
callee = result
|
81
81
|
# if callee.kind_of?(SkmLambda)
|
82
82
|
# aRuntime.push(callee.environment)
|
@@ -125,6 +125,8 @@ module Skeem
|
|
125
125
|
# callee = fetch_callee(aRuntime, result)
|
126
126
|
when Primitive::PrimitiveProcedure
|
127
127
|
callee = operator
|
128
|
+
when SkmLambdaRep
|
129
|
+
callee = operator.evaluate(aRuntime)
|
128
130
|
when SkmLambda
|
129
131
|
callee = operator
|
130
132
|
else
|
@@ -301,10 +303,11 @@ module Skeem
|
|
301
303
|
end # class
|
302
304
|
|
303
305
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
306
|
+
# Parse tree representation of a Lambda
|
307
|
+
# - Not bound to a frame (aka environment)
|
308
|
+
# - Knows the parse representation of its embedded definitions
|
309
|
+
# - Knows the parse representation of the body
|
310
|
+
class SkmLambdaRep < SkmMultiExpression
|
308
311
|
include DatumDSL
|
309
312
|
|
310
313
|
# @!attribute [r] formals
|
@@ -312,7 +315,6 @@ require_relative 'skm_procedure_exec'
|
|
312
315
|
attr_reader :formals
|
313
316
|
attr_reader :definitions
|
314
317
|
attr_reader :sequence
|
315
|
-
attr_reader :environment
|
316
318
|
|
317
319
|
def initialize(aPosition, theFormals, aBody)
|
318
320
|
super(aPosition)
|
@@ -321,6 +323,158 @@ require_relative 'skm_procedure_exec'
|
|
321
323
|
@sequence = aBody[:sequence]
|
322
324
|
end
|
323
325
|
|
326
|
+
def evaluate(aRuntime)
|
327
|
+
SkmLambda.new(self, aRuntime)
|
328
|
+
end
|
329
|
+
|
330
|
+
def callable?
|
331
|
+
true
|
332
|
+
end
|
333
|
+
|
334
|
+
def call(aRuntime, theActuals)
|
335
|
+
set_cond_environment(aRuntime.environment) # Last chance for anonymous lambda
|
336
|
+
application = SkmProcedureExec.new(self)
|
337
|
+
application.run!(aRuntime, theActuals)
|
338
|
+
end
|
339
|
+
|
340
|
+
def arity
|
341
|
+
formals.arity
|
342
|
+
end
|
343
|
+
|
344
|
+
def required_arity
|
345
|
+
formals.required_arity
|
346
|
+
end
|
347
|
+
|
348
|
+
alias eqv? equal?
|
349
|
+
alias skm_equal? equal?
|
350
|
+
|
351
|
+
def bound!(aFrame)
|
352
|
+
set_cond_environment(aFrame)
|
353
|
+
end
|
354
|
+
|
355
|
+
def inspect
|
356
|
+
result = inspect_prefix + "@object_id=#{object_id.to_s(16)}, "
|
357
|
+
result << inspect_specific
|
358
|
+
result << inspect_suffix
|
359
|
+
result
|
360
|
+
end
|
361
|
+
|
362
|
+
def associations
|
363
|
+
[:formals, :definitions, :sequence]
|
364
|
+
end
|
365
|
+
|
366
|
+
def bind_locals(aRuntime, theActuals)
|
367
|
+
actuals = theActuals
|
368
|
+
count_actuals = actuals.size
|
369
|
+
|
370
|
+
if (count_actuals < required_arity) ||
|
371
|
+
((count_actuals > required_arity) && !formals.variadic?)
|
372
|
+
# $stderr.puts "Error"
|
373
|
+
# $stderr.puts self.inspect
|
374
|
+
raise StandardError, msg_arity_mismatch(theActuals)
|
375
|
+
end
|
376
|
+
return if count_actuals.zero? && !formals.variadic?
|
377
|
+
bind_required_locals(aRuntime, theActuals)
|
378
|
+
if formals.variadic?
|
379
|
+
variadic_part_raw = actuals.drop(required_arity)
|
380
|
+
variadic_part = variadic_part_raw.map do |actual|
|
381
|
+
case actual
|
382
|
+
when ProcedureCall
|
383
|
+
actual.evaluate(aRuntime)
|
384
|
+
when SkmQuotation
|
385
|
+
actual.evaluate(aRuntime)
|
386
|
+
else
|
387
|
+
to_datum(actual)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
variadic_arg_name = formals.formals.last
|
391
|
+
args_coll = SkmPair.create_from_a(variadic_part)
|
392
|
+
a_def = SkmBinding.new(variadic_arg_name, args_coll)
|
393
|
+
a_def.evaluate(aRuntime)
|
394
|
+
aRuntime.add_binding(a_def.variable, a_def.value)
|
395
|
+
# $stderr.puts "Tef #{a_def.inspect}"
|
396
|
+
# $stderr.puts "Tef #{actuals.inspect}"
|
397
|
+
# $stderr.puts "Tef #{variadic_part.inspect}"
|
398
|
+
# $stderr.puts "Tef #{aProcedureCall.inspect}"
|
399
|
+
# a_def.evaluate(aRuntime)
|
400
|
+
end
|
401
|
+
#aProcedureCall.operands_consumed = true
|
402
|
+
end
|
403
|
+
|
404
|
+
private
|
405
|
+
|
406
|
+
# Purpose: bind each formal from lambda to an actual value from the call
|
407
|
+
def bind_required_locals(aRuntime, theActuals)
|
408
|
+
max_index = required_arity - 1
|
409
|
+
actuals = theActuals
|
410
|
+
formal_names = formals.formals.map(&:value)
|
411
|
+
|
412
|
+
formals.formals.each_with_index do |arg_name, index|
|
413
|
+
arg = actuals[index]
|
414
|
+
if arg.nil?
|
415
|
+
if actuals.empty? && formals.variadic?
|
416
|
+
arg = SkmPair.create_from_a([])
|
417
|
+
else
|
418
|
+
raise StandardError, "Unbound variable: '#{arg_name.value}'"
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
# IMPORTANT: execute procedure call in argument list now
|
423
|
+
arg = arg.evaluate(aRuntime) if arg.kind_of?(ProcedureCall)
|
424
|
+
unless arg.kind_of?(SkmElement)
|
425
|
+
arg = to_datum(arg)
|
426
|
+
end
|
427
|
+
# a_def = SkmDefinition.new(position, arg_name, arg)
|
428
|
+
a_def = SkmBinding.new(arg_name, arg)
|
429
|
+
# $stderr.puts "Lambda #{object_id.to_s(16)}"
|
430
|
+
# $stderr.puts "LOCAL #{arg_name.value} #{arg.inspect}"
|
431
|
+
if arg.kind_of?(SkmVariableReference) && !formal_names.include?(arg.value)
|
432
|
+
aRuntime.add_binding(arg_name, a_def)
|
433
|
+
else
|
434
|
+
aRuntime.add_binding(a_def.variable, a_def.evaluate(aRuntime))
|
435
|
+
end
|
436
|
+
break if index >= max_index
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
def msg_arity_mismatch(actuals)
|
441
|
+
# *** ERROR: wrong number of arguments for #<closure morph> (required 2, got 1)
|
442
|
+
msg1 = "Wrong number of arguments for procedure "
|
443
|
+
count_actuals = actuals.size
|
444
|
+
msg2 = "(required #{required_arity}, got #{count_actuals})"
|
445
|
+
msg1 + msg2
|
446
|
+
end
|
447
|
+
|
448
|
+
def inspect_specific
|
449
|
+
result = ''
|
450
|
+
result << '@formals ' + formals.inspect + ', '
|
451
|
+
result << '@definitions ' + definitions.inspect + ', '
|
452
|
+
result << '@sequence ' + sequence.inspect + inspect_suffix
|
453
|
+
|
454
|
+
result
|
455
|
+
end
|
456
|
+
end # class
|
457
|
+
|
458
|
+
|
459
|
+
|
460
|
+
|
461
|
+
require 'forwardable'
|
462
|
+
require_relative 'skm_procedure_exec'
|
463
|
+
|
464
|
+
class SkmLambda < SkmMultiExpression
|
465
|
+
include DatumDSL
|
466
|
+
extend Forwardable
|
467
|
+
|
468
|
+
attr_reader :representation
|
469
|
+
attr_reader :environment
|
470
|
+
|
471
|
+
def_delegators(:@representation, :formals, :definitions, :sequence)
|
472
|
+
|
473
|
+
def initialize(aRepresentation, aRuntime)
|
474
|
+
@representation = aRepresentation
|
475
|
+
@environment = aRuntime.environment
|
476
|
+
end
|
477
|
+
|
324
478
|
def evaluate(aRuntime)
|
325
479
|
self
|
326
480
|
end
|
@@ -429,7 +583,7 @@ require_relative 'skm_procedure_exec'
|
|
429
583
|
|
430
584
|
result
|
431
585
|
end
|
432
|
-
|
586
|
+
|
433
587
|
def dup_cond(aRuntime)
|
434
588
|
if environment
|
435
589
|
result = self
|
@@ -438,17 +592,17 @@ require_relative 'skm_procedure_exec'
|
|
438
592
|
twin.set_cond_environment(aRuntime.environment)
|
439
593
|
result = twin
|
440
594
|
end
|
441
|
-
|
595
|
+
|
442
596
|
result
|
443
597
|
end
|
444
|
-
|
598
|
+
|
445
599
|
def doppelganger(aRuntime)
|
446
600
|
twin = self.dup
|
447
601
|
twin.set_cond_environment(aRuntime.environment.dup)
|
448
602
|
result = twin
|
449
|
-
|
603
|
+
|
450
604
|
result
|
451
|
-
end
|
605
|
+
end
|
452
606
|
|
453
607
|
def set_cond_environment(theFrame)
|
454
608
|
# $stderr.puts "Lambda #{object_id.to_s(16)}, env [#{environment.object_id.to_s(16)}]"
|
data/lib/skeem/skm_empty_list.rb
CHANGED
@@ -2,7 +2,7 @@ require 'singleton'
|
|
2
2
|
require_relative 'skm_element'
|
3
3
|
|
4
4
|
module Skeem
|
5
|
-
# From R7RS: The empty list is a special object of its own type.
|
5
|
+
# From R7RS: The empty list is a special object of its own type.
|
6
6
|
# It is not a pair, it has no elements, and its length is zero.
|
7
7
|
class SkmEmptyList < SkmElement
|
8
8
|
include Singleton
|
@@ -14,30 +14,38 @@ module Skeem
|
|
14
14
|
def null?
|
15
15
|
true
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def pair?
|
19
19
|
false
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def length
|
23
23
|
0
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def empty?
|
27
27
|
true
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
def verbatim?
|
31
31
|
true
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
def skm_equal?(other)
|
35
35
|
equal?(other)
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
def to_a
|
39
39
|
[]
|
40
|
-
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def klone
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def append_list(aList)
|
47
|
+
aList
|
48
|
+
end
|
41
49
|
|
42
50
|
def evaluate(_runtime)
|
43
51
|
self
|
data/lib/skeem/skm_pair.rb
CHANGED
@@ -14,6 +14,15 @@ module Skeem
|
|
14
14
|
@cdr = tail
|
15
15
|
end
|
16
16
|
|
17
|
+
def klone
|
18
|
+
if cdr.kind_of?(SkmPair)
|
19
|
+
new_cdr = cdr.klone
|
20
|
+
else
|
21
|
+
new_cdr = cdr
|
22
|
+
end
|
23
|
+
self.class.new(car, new_cdr)
|
24
|
+
end
|
25
|
+
|
17
26
|
# Construct new instance with car and cdr respectively equal to
|
18
27
|
# car.evaluate and cdr.evaluate
|
19
28
|
def clone_evaluate(aRuntime)
|
@@ -82,6 +91,14 @@ module Skeem
|
|
82
91
|
result
|
83
92
|
end
|
84
93
|
|
94
|
+
def last_pair
|
95
|
+
if cdr.nil? || (cdr == SkmEmptyList.instance) || (!cdr.kind_of?(SkmPair))
|
96
|
+
self
|
97
|
+
else
|
98
|
+
cdr.last_pair
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
85
102
|
def last
|
86
103
|
self.to_a.last
|
87
104
|
end
|
@@ -116,15 +133,23 @@ module Skeem
|
|
116
133
|
end
|
117
134
|
|
118
135
|
def append(anElement)
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
self.cdr.append(anElement)
|
136
|
+
last_one = self.last_pair
|
137
|
+
if last_one.cdr.nil? || last_one.cdr.kind_of?(SkmEmptyList)
|
138
|
+
last_one.cdr = SkmPair.new(anElement, SkmEmptyList.instance)
|
123
139
|
else
|
124
140
|
raise StandardError, "Cannot append #{anElement.inspect}"
|
125
141
|
end
|
126
142
|
end
|
127
143
|
|
144
|
+
def append_list(aList)
|
145
|
+
last_one = self.last_pair
|
146
|
+
if last_one.cdr.nil? || last_one.cdr.kind_of?(SkmEmptyList)
|
147
|
+
last_one.cdr = aList
|
148
|
+
else
|
149
|
+
raise StandardError, "Cannot append list #{aList.inspect}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
128
153
|
def evaluate(aRuntime)
|
129
154
|
return SkmEmptyList.instance if empty?
|
130
155
|
if car.kind_of?(SkmIdentifier) && car.is_var_name
|
@@ -19,9 +19,11 @@ module Skeem
|
|
19
19
|
runtime.push(frame)
|
20
20
|
definition.bind_locals(runtime, theActuals)
|
21
21
|
evaluate_defs(aRuntime)
|
22
|
-
# definition.evaluate_defs(runtime)
|
23
22
|
# $stderr.puts "Locals"
|
24
|
-
#
|
23
|
+
# frame.bindings.each_pair do |key, elem|
|
24
|
+
# $stderr.print key.to_s + ' => '
|
25
|
+
# $stderr.puts elem.kind_of?(SkmLambda) ? " Lambda = #{elem.object_id.to_s(16)}" : elem.inspect
|
26
|
+
# end
|
25
27
|
result = definition.evaluate_sequence(runtime)
|
26
28
|
runtime.pop
|
27
29
|
# $stderr.puts "Lambda result: #{result.object_id.to_s(16)}" if result.kind_of?(SkmLambda)
|
@@ -33,8 +35,8 @@ module Skeem
|
|
33
35
|
|
34
36
|
def evaluate_defs(aRuntime)
|
35
37
|
definition.definitions.each do |bndng|
|
38
|
+
val = bndng.value.evaluate(aRuntime)
|
36
39
|
var = bndng.variable.evaluate(aRuntime)
|
37
|
-
val = bndng.value.evaluate(aRuntime)
|
38
40
|
frame.add_binding(var, val)
|
39
41
|
end
|
40
42
|
end
|
@@ -160,7 +160,7 @@ module Skeem
|
|
160
160
|
|
161
161
|
# Sequencing construct
|
162
162
|
class SkmSequencingBlock < SkmUnaryExpression
|
163
|
-
alias sequence child
|
163
|
+
alias sequence child # Can be a body
|
164
164
|
|
165
165
|
def initialize(aSequence)
|
166
166
|
super(nil, aSequence)
|
@@ -168,26 +168,73 @@ module Skeem
|
|
168
168
|
|
169
169
|
def evaluate(aRuntime)
|
170
170
|
result = nil
|
171
|
-
if sequence
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
171
|
+
return result if sequence.nil?
|
172
|
+
|
173
|
+
case sequence
|
174
|
+
when SkmPair
|
175
|
+
result = eval_pair(aRuntime)
|
176
|
+
when Hash
|
177
|
+
result = eval_body(aRuntime)
|
178
|
+
else
|
179
|
+
result = sequence.evaluate(aRuntime)
|
180
|
+
end
|
181
|
+
|
182
|
+
result
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
def eval_pair(aRuntime)
|
188
|
+
result = nil
|
189
|
+
sequence.to_a.each do |cmd|
|
190
|
+
begin
|
191
|
+
if cmd.kind_of?(SkmLambda)
|
192
|
+
result = cmd.dup_cond(aRuntime)
|
193
|
+
else
|
194
|
+
result = cmd.evaluate(aRuntime)
|
195
|
+
end
|
196
|
+
rescue NoMethodError => exc
|
197
|
+
$stderr.puts self.inspect
|
198
|
+
$stderr.puts sequence.inspect
|
199
|
+
$stderr.puts cmd.inspect
|
200
|
+
raise exc
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
result
|
205
|
+
end
|
206
|
+
|
207
|
+
def eval_body(aRuntime)
|
208
|
+
result = nil
|
209
|
+
aRuntime.push(SkmFrame.new(aRuntime.environment))
|
210
|
+
|
211
|
+
unless sequence[:defs].empty?
|
212
|
+
sequence[:defs].each do |dfn|
|
213
|
+
dfn.evaluate(aRuntime)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
if sequence[:sequence].kind_of?(SkmPair)
|
218
|
+
sequence[:sequence].to_a.each do |cmd|
|
219
|
+
begin
|
220
|
+
if cmd.kind_of?(SkmLambda)
|
221
|
+
result = cmd.dup_cond(aRuntime)
|
222
|
+
else
|
223
|
+
result = cmd.evaluate(aRuntime)
|
185
224
|
end
|
225
|
+
rescue NoMethodError => exc
|
226
|
+
$stderr.puts self.inspect
|
227
|
+
$stderr.puts sequence[:sequence].inspect
|
228
|
+
$stderr.puts cmd.inspect
|
229
|
+
raise exc
|
186
230
|
end
|
187
|
-
elsif
|
188
|
-
result = sequence.evaluate(aRuntime)
|
189
231
|
end
|
232
|
+
elsif
|
233
|
+
result = sequence.evaluate(aRuntime)
|
234
|
+
end
|
190
235
|
|
236
|
+
aRuntime.pop
|
237
|
+
|
191
238
|
result
|
192
239
|
end
|
193
240
|
end # class
|
data/lib/skeem/version.rb
CHANGED
@@ -304,20 +304,21 @@ SKEEM
|
|
304
304
|
result = subject.run(source)
|
305
305
|
expect(result.last.last.value).to eq(4)
|
306
306
|
end
|
307
|
-
|
308
|
-
|
307
|
+
|
309
308
|
it 'should support the nested define construct' do
|
310
309
|
source = <<-SKEEM
|
311
310
|
(define (quadruple x)
|
312
311
|
(define (double x) ; define a local procedure double
|
313
312
|
(+ x x))
|
314
313
|
(double (double x))) ; nested calls to the local procedure
|
315
|
-
|
314
|
+
|
316
315
|
(quadruple 5) ; => 20
|
317
316
|
SKEEM
|
318
317
|
result = subject.run(source)
|
319
|
-
expect(result.last.value).to eq(20)
|
318
|
+
expect(result.last.value).to eq(20)
|
320
319
|
end
|
320
|
+
|
321
|
+
|
321
322
|
end # context
|
322
323
|
|
323
324
|
context 'Binding constructs:' do
|
@@ -376,8 +377,6 @@ SKEEM
|
|
376
377
|
expect(result.last).to eq(3)
|
377
378
|
end
|
378
379
|
|
379
|
-
|
380
|
-
|
381
380
|
it 'should implement let* expression' do
|
382
381
|
source = <<-SKEEM
|
383
382
|
(let ((x 2) (y 3))
|
@@ -411,6 +410,21 @@ SKEEM
|
|
411
410
|
result = subject.run(source)
|
412
411
|
expect(result).to eq(7)
|
413
412
|
end
|
413
|
+
|
414
|
+
it 'should support begin as lambda body' do
|
415
|
+
source = <<-SKEEM
|
416
|
+
(define kube (lambda (x)
|
417
|
+
(begin
|
418
|
+
(define z x)
|
419
|
+
(* z z z)
|
420
|
+
)
|
421
|
+
))
|
422
|
+
(kube 3)
|
423
|
+
(kube 4)
|
424
|
+
SKEEM
|
425
|
+
result = subject.run(source)
|
426
|
+
expect(result.last).to eq(64)
|
427
|
+
end
|
414
428
|
end # context
|
415
429
|
|
416
430
|
context 'Quasiquotation:' do
|
@@ -452,7 +452,10 @@ SKEEM
|
|
452
452
|
['(list? #f)', false],
|
453
453
|
['(list? 1)', false],
|
454
454
|
['(list? "bar")', false],
|
455
|
+
["(list? 'a)", false],
|
456
|
+
["(list? '(a))", true],
|
455
457
|
["(list? '(1 2 3))", true],
|
458
|
+
["(list? '(3 . 4))", false],
|
456
459
|
["(list? '())", true]
|
457
460
|
]
|
458
461
|
checks.each do |(skeem_expr, expectation)|
|
@@ -563,6 +566,36 @@ SKEEM
|
|
563
566
|
end
|
564
567
|
end
|
565
568
|
|
569
|
+
def array2list_ids(arr)
|
570
|
+
arr.map { |elem| SkmIdentifier.create(elem) }
|
571
|
+
end
|
572
|
+
|
573
|
+
it 'should implement the append procedure' do
|
574
|
+
checks = [
|
575
|
+
["(append '(a b c) '())", array2list_ids(['a', 'b', 'c'])],
|
576
|
+
["(append '() '(a b c))", array2list_ids(['a', 'b', 'c'])],
|
577
|
+
["(append '(x) '(y))", array2list_ids(['x', 'y'])],
|
578
|
+
["(append '(a) '(b c d))", array2list_ids(['a', 'b', 'c', 'd'])],
|
579
|
+
["(append '(a b) '(c d))", array2list_ids(['a', 'b', 'c', 'd'])],
|
580
|
+
["(append '(a b) '(c) 'd)", array2list_ids(['a', 'b', 'c', 'd'])],
|
581
|
+
["(append '(a (b)) '((c)))", [SkmIdentifier.create('a'),
|
582
|
+
SkmPair.create_from_a(array2list_ids(['b'])),
|
583
|
+
SkmPair.create_from_a(array2list_ids(['c']))]]
|
584
|
+
]
|
585
|
+
checks.each do |(skeem_expr, expectation)|
|
586
|
+
result = subject.run(skeem_expr)
|
587
|
+
expect(result.to_a).to eq(expectation)
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
it 'should implement the procedure for an improper list' do
|
592
|
+
result = subject.run("(append '(a b) '(c . d))")
|
593
|
+
expect(result.car).to eq( SkmIdentifier.create('a'))
|
594
|
+
expect(result.cdr.car).to eq( SkmIdentifier.create('b'))
|
595
|
+
expect(result.cdr.cdr.car).to eq( SkmIdentifier.create('c'))
|
596
|
+
expect(result.cdr.cdr.cdr).to eq( SkmIdentifier.create('d'))
|
597
|
+
end
|
598
|
+
|
566
599
|
it 'should implement the list->vector procedure' do
|
567
600
|
checks = [
|
568
601
|
["(list->vector '())", []],
|
@@ -71,18 +71,18 @@ module Skeem
|
|
71
71
|
end # context
|
72
72
|
end # describe
|
73
73
|
|
74
|
-
describe
|
74
|
+
describe SkmLambdaRep do
|
75
75
|
let(:pos) { double('fake-position') }
|
76
76
|
let(:s_formals) { double('fake-formals') }
|
77
77
|
let(:s_defs) { double('fake-definitions') }
|
78
78
|
let(:s_sequence) { double('fake-sequence') }
|
79
79
|
let(:s_body) do { defs: s_defs, sequence: s_sequence } end
|
80
80
|
|
81
|
-
subject {
|
81
|
+
subject { SkmLambdaRep.new(pos, s_formals, s_body) }
|
82
82
|
|
83
83
|
context 'Initialization:' do
|
84
84
|
it 'should be initialized with a pos and 3 expressions' do
|
85
|
-
expect{
|
85
|
+
expect{ SkmLambdaRep.new(pos, s_formals, s_body) }.not_to raise_error
|
86
86
|
end
|
87
87
|
|
88
88
|
it 'should know its formals' do
|
@@ -100,7 +100,7 @@ module Skeem
|
|
100
100
|
|
101
101
|
context 'Provided services:' do
|
102
102
|
it 'should return its text representation' do
|
103
|
-
txt1 = '<Skeem::
|
103
|
+
txt1 = '<Skeem::SkmLambdaRep: @formals #<Double "fake-formals">, '
|
104
104
|
txt2 = '@definitions #<Double "fake-definitions">, '
|
105
105
|
txt3 = '@sequence #<Double "fake-sequence">>>'
|
106
106
|
# Remove "unpredictable" part of actual text
|
data/spec/skeem/skm_pair_spec.rb
CHANGED
@@ -34,11 +34,37 @@ module Skeem
|
|
34
34
|
end # context
|
35
35
|
|
36
36
|
context 'Provided services:' do
|
37
|
-
let(:runtime) { Runtime.new(
|
37
|
+
let(:runtime) { Runtime.new(SkmFrame.new) }
|
38
38
|
let(:list_length_2) { SkmPair.new(integer(10), subject) }
|
39
39
|
let(:quirk_element) { double('three') }
|
40
40
|
let(:quirk_members) { [integer(10), quirk_element] }
|
41
41
|
|
42
|
+
it 'should clone itself' do
|
43
|
+
cloned = subject.klone
|
44
|
+
expect(cloned.car).to eq(subject.car)
|
45
|
+
expect(cloned.cdr).to eq(subject.cdr)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should clone a proper list' do
|
49
|
+
pair2 = SkmPair.new(identifier('b'), SkmEmptyList.instance)
|
50
|
+
pair1 = SkmPair.new(identifier('a'), pair2)
|
51
|
+
|
52
|
+
cloned = pair1.klone
|
53
|
+
expect(cloned.car).to eq(identifier('a'))
|
54
|
+
expect(cloned.cdr.car).to eq(identifier('b'))
|
55
|
+
expect(cloned.cdr.cdr).to be_null
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should clone a improper list' do
|
59
|
+
pair2 = SkmPair.new(identifier('b'), identifier('c'))
|
60
|
+
pair1 = SkmPair.new(identifier('a'), pair2)
|
61
|
+
|
62
|
+
cloned = pair1.klone
|
63
|
+
expect(cloned.car).to eq(identifier('a'))
|
64
|
+
expect(cloned.cdr.car).to eq(identifier('b'))
|
65
|
+
expect(cloned.cdr.cdr).to eq(identifier('c'))
|
66
|
+
end
|
67
|
+
|
42
68
|
it 'should know its length' do
|
43
69
|
expect(subject.length).to eq(1)
|
44
70
|
|
@@ -88,6 +114,21 @@ module Skeem
|
|
88
114
|
expect(list_length_2.to_a).to eq([integer(10), sample_car])
|
89
115
|
end
|
90
116
|
|
117
|
+
it 'should return the last pair of a proper list' do
|
118
|
+
expect(subject.last_pair).to eq(subject)
|
119
|
+
|
120
|
+
pair2 = SkmPair.new(identifier('b'), SkmEmptyList.instance)
|
121
|
+
pair1 = SkmPair.new(identifier('a'), pair2)
|
122
|
+
expect(pair1.last_pair).to eq(pair1)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should return the last pair of an improper list' do
|
126
|
+
pair3 = SkmPair.new(identifier('c'), identifier('d'))
|
127
|
+
pair2 = SkmPair.new(identifier('b'), pair3)
|
128
|
+
pair1 = SkmPair.new(identifier('a'), pair2)
|
129
|
+
expect(pair1.last_pair).to eq(pair3)
|
130
|
+
end
|
131
|
+
|
91
132
|
it 'should return the last element of a list' do
|
92
133
|
expect(subject.last).to eq(sample_car)
|
93
134
|
expect(list_length_2.last).to eq(sample_car)
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require_relative '../spec_helper' # Use the RSpec framework
|
2
2
|
require_relative '../../lib/skeem/datum_dsl'
|
3
|
-
require_relative '../../lib/skeem/environment'
|
4
3
|
require_relative '../../lib/skeem/s_expr_nodes'
|
5
4
|
require_relative '../../lib/skeem/skm_unary_expression' # Load the classes under test
|
6
5
|
|
@@ -49,7 +48,7 @@ module Skeem
|
|
49
48
|
end # context
|
50
49
|
|
51
50
|
context 'Provided services:' do
|
52
|
-
let(:runtime) { Runtime.new(
|
51
|
+
let(:runtime) { Runtime.new(SkmFrame.new) }
|
53
52
|
|
54
53
|
# it 'should return the child(datum) at evaluation' do
|
55
54
|
# expect(subject.evaluate(runtime)).to be_equal(subject.child)
|
@@ -90,7 +89,7 @@ module Skeem
|
|
90
89
|
end # context
|
91
90
|
|
92
91
|
context 'Provided services:' do
|
93
|
-
let(:runtime) { Runtime.new(
|
92
|
+
let(:runtime) { Runtime.new(SkmFrame.new) }
|
94
93
|
|
95
94
|
it 'should return the child(template) at evaluation' do
|
96
95
|
expect(subject.evaluate(runtime)).to be_equal(subject.child)
|
@@ -131,7 +130,7 @@ module Skeem
|
|
131
130
|
end # context
|
132
131
|
|
133
132
|
context 'Provided services:' do
|
134
|
-
let(:runtime) { Runtime.new(
|
133
|
+
let(:runtime) { Runtime.new(SkmFrame.new) }
|
135
134
|
|
136
135
|
it 'should return the child(template) at evaluation' do
|
137
136
|
expect(subject.evaluate(runtime)).to be_equal(subject.child)
|
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.2.
|
4
|
+
version: 0.2.05
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|
@@ -86,7 +86,6 @@ files:
|
|
86
86
|
- lib/skeem.rb
|
87
87
|
- lib/skeem/datum_dsl.rb
|
88
88
|
- lib/skeem/element_visitor.rb
|
89
|
-
- lib/skeem/environment.rb
|
90
89
|
- lib/skeem/grammar.rb
|
91
90
|
- lib/skeem/interpreter.rb
|
92
91
|
- lib/skeem/parser.rb
|
@@ -111,7 +110,6 @@ files:
|
|
111
110
|
- skeem.gemspec
|
112
111
|
- spec/skeem/datum_dsl_spec.rb
|
113
112
|
- spec/skeem/element_visitor_spec.rb
|
114
|
-
- spec/skeem/environment_spec.rb
|
115
113
|
- spec/skeem/interpreter_spec.rb
|
116
114
|
- spec/skeem/lambda_spec.rb
|
117
115
|
- spec/skeem/parser_spec.rb
|
@@ -158,7 +156,6 @@ summary: Skeem is an interpreter of a subset of the Scheme programming language.
|
|
158
156
|
test_files:
|
159
157
|
- spec/skeem/datum_dsl_spec.rb
|
160
158
|
- spec/skeem/element_visitor_spec.rb
|
161
|
-
- spec/skeem/environment_spec.rb
|
162
159
|
- spec/skeem/interpreter_spec.rb
|
163
160
|
- spec/skeem/lambda_spec.rb
|
164
161
|
- spec/skeem/parser_spec.rb
|
data/lib/skeem/environment.rb
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
module Skeem
|
2
|
-
class Environment
|
3
|
-
attr_reader :outer
|
4
|
-
|
5
|
-
attr_reader(:bindings)
|
6
|
-
|
7
|
-
def initialize(outerEnv = nil)
|
8
|
-
@bindings = {}
|
9
|
-
@outer = outerEnv
|
10
|
-
end
|
11
|
-
|
12
|
-
def define(anIdentifier, anExpression)
|
13
|
-
raise StandardError, anIdentifier unless anIdentifier.kind_of?(String)
|
14
|
-
@bindings[anIdentifier] = anExpression
|
15
|
-
end
|
16
|
-
|
17
|
-
def fetch(anIdentifier)
|
18
|
-
found = bindings[anIdentifier]
|
19
|
-
if found.nil? && outer
|
20
|
-
found = outer.fetch(anIdentifier)
|
21
|
-
end
|
22
|
-
|
23
|
-
found
|
24
|
-
end
|
25
|
-
|
26
|
-
def empty?
|
27
|
-
my_result = bindings.empty?
|
28
|
-
if my_result && outer
|
29
|
-
my_result = outer.empty?
|
30
|
-
end
|
31
|
-
|
32
|
-
my_result
|
33
|
-
end
|
34
|
-
|
35
|
-
def size
|
36
|
-
my_result = bindings.size
|
37
|
-
my_result += outer.size if outer
|
38
|
-
|
39
|
-
my_result
|
40
|
-
end
|
41
|
-
|
42
|
-
def include?(anIdentifier)
|
43
|
-
my_result = bindings.include?(anIdentifier)
|
44
|
-
if my_result == false && outer
|
45
|
-
my_result = outer.include?(anIdentifier)
|
46
|
-
end
|
47
|
-
|
48
|
-
my_result
|
49
|
-
end
|
50
|
-
|
51
|
-
# The number of outer parents the current environment has.
|
52
|
-
# @return [Integer] The nesting levels
|
53
|
-
def depth
|
54
|
-
count = 0
|
55
|
-
|
56
|
-
curr_env = self
|
57
|
-
while curr_env.outer
|
58
|
-
count += 1
|
59
|
-
curr_env = curr_env.outer
|
60
|
-
end
|
61
|
-
|
62
|
-
count
|
63
|
-
end
|
64
|
-
|
65
|
-
def inspect
|
66
|
-
result = ''
|
67
|
-
if outer
|
68
|
-
result << outer.inspect
|
69
|
-
else
|
70
|
-
return "\n"
|
71
|
-
end
|
72
|
-
result << "\n----\n"
|
73
|
-
bindings.each_pair do |key, expr|
|
74
|
-
result << "#{key.inspect} => #{expr.inspect}\n"
|
75
|
-
end
|
76
|
-
|
77
|
-
result
|
78
|
-
end
|
79
|
-
end # class
|
80
|
-
end # module
|
@@ -1,81 +0,0 @@
|
|
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
|
-
let(:sample_env) { Environment.new }
|
7
|
-
context 'Initialization:' do
|
8
|
-
it 'could be initialized without argument' do
|
9
|
-
expect { Environment.new() }.not_to raise_error
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'could be initialized with optional argument' do
|
13
|
-
expect { Environment.new(sample_env) }.not_to raise_error
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'should have no default bindings' do
|
17
|
-
expect(subject).to be_empty
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'should have depth of zero or one' do
|
21
|
-
expect(subject.depth).to be_zero
|
22
|
-
|
23
|
-
instance = Environment.new(sample_env)
|
24
|
-
expect(instance.depth).to eq(1)
|
25
|
-
end
|
26
|
-
end # context
|
27
|
-
|
28
|
-
context 'Provided services:' do
|
29
|
-
it 'should add entries' do
|
30
|
-
entry = double('dummy')
|
31
|
-
subject.define('dummy', entry)
|
32
|
-
expect(subject.size).to eq(1)
|
33
|
-
expect(subject.bindings['dummy']).not_to be_nil
|
34
|
-
expect(subject.bindings['dummy']).to eq(entry)
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'should know whether it is empty' do
|
38
|
-
# Case 1: no entry
|
39
|
-
expect(subject.empty?).to be_truthy
|
40
|
-
|
41
|
-
# Case 2: existing entry
|
42
|
-
entry = double('dummy')
|
43
|
-
subject.define('dummy', entry)
|
44
|
-
expect(subject.empty?).to be_falsey
|
45
|
-
|
46
|
-
# Case 3: entry defined in outer environment
|
47
|
-
nested = Environment.new(subject)
|
48
|
-
expect(nested.empty?).to be_falsey
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'should retrieve entries' do
|
52
|
-
# Case 1: non-existing entry
|
53
|
-
expect(subject.fetch('dummy')).to be_nil
|
54
|
-
|
55
|
-
# Case 2: existing entry
|
56
|
-
entry = double('dummy')
|
57
|
-
subject.define('dummy', entry)
|
58
|
-
expect(subject.fetch('dummy')).to eq(entry)
|
59
|
-
|
60
|
-
# Case 3: entry defined in outer environment
|
61
|
-
nested = Environment.new(subject)
|
62
|
-
expect(nested.fetch('dummy')).to eq(entry)
|
63
|
-
end
|
64
|
-
|
65
|
-
it 'should know the total number of bindings' do
|
66
|
-
# Case 1: non-existing entry
|
67
|
-
expect(subject.size).to be_zero
|
68
|
-
|
69
|
-
# Case 2: existing entry
|
70
|
-
entry = double('dummy')
|
71
|
-
subject.define('dummy', entry)
|
72
|
-
expect(subject.size).to eq(1)
|
73
|
-
|
74
|
-
# Case 3: entry defined in outer environment
|
75
|
-
nested = Environment.new(subject)
|
76
|
-
expect(nested.size).to eq(1)
|
77
|
-
end
|
78
|
-
end # context
|
79
|
-
|
80
|
-
end # describe
|
81
|
-
end # module
|