skeem 0.2.04 → 0.2.05
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 +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
|