skeem 0.2.14 → 0.2.18

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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +451 -195
  3. data/.travis.yml +27 -0
  4. data/CHANGELOG.md +35 -1
  5. data/Gemfile +2 -0
  6. data/README.md +125 -56
  7. data/Rakefile +2 -0
  8. data/appveyor.yml +3 -4
  9. data/bin/cubic.skm +4 -0
  10. data/bin/hello-world.skm +1 -0
  11. data/bin/skeem +72 -0
  12. data/lib/skeem/datum_dsl.rb +40 -30
  13. data/lib/skeem/element_visitor.rb +5 -2
  14. data/lib/skeem/grammar.rb +88 -26
  15. data/lib/skeem/interpreter.rb +9 -7
  16. data/lib/skeem/parser.rb +6 -4
  17. data/lib/skeem/primitive/primitive_builder.rb +148 -122
  18. data/lib/skeem/primitive/primitive_procedure.rb +23 -25
  19. data/lib/skeem/runtime.rb +17 -15
  20. data/lib/skeem/s_expr_builder.rb +49 -117
  21. data/lib/skeem/s_expr_nodes.rb +147 -132
  22. data/lib/skeem/skeem_exception.rb +1 -0
  23. data/lib/skeem/skm_binding.rb +9 -11
  24. data/lib/skeem/skm_compound_datum.rb +9 -6
  25. data/lib/skeem/skm_element.rb +15 -13
  26. data/lib/skeem/skm_empty_list.rb +6 -4
  27. data/lib/skeem/skm_exception.rb +9 -0
  28. data/lib/skeem/skm_expression.rb +3 -1
  29. data/lib/skeem/skm_frame.rb +3 -2
  30. data/lib/skeem/skm_pair.rb +26 -18
  31. data/lib/skeem/skm_procedure_exec.rb +11 -6
  32. data/lib/skeem/skm_simple_datum.rb +23 -20
  33. data/lib/skeem/skm_unary_expression.rb +34 -37
  34. data/lib/skeem/standard/base.skm +4 -0
  35. data/lib/skeem/tokenizer.rb +38 -28
  36. data/lib/skeem/version.rb +3 -1
  37. data/lib/skeem.rb +2 -0
  38. data/skeem.gemspec +9 -6
  39. data/spec/skeem/add4.skm +4 -0
  40. data/spec/skeem/datum_dsl_spec.rb +13 -12
  41. data/spec/skeem/element_visitor_spec.rb +14 -10
  42. data/spec/skeem/interpreter_spec.rb +84 -44
  43. data/spec/skeem/lambda_spec.rb +13 -11
  44. data/spec/skeem/parser_spec.rb +23 -19
  45. data/spec/skeem/primitive/primitive_builder_spec.rb +65 -48
  46. data/spec/skeem/primitive/primitive_procedure_spec.rb +14 -12
  47. data/spec/skeem/runtime_spec.rb +20 -18
  48. data/spec/skeem/s_expr_nodes_spec.rb +8 -6
  49. data/spec/skeem/skm_compound_datum_spec.rb +12 -10
  50. data/spec/skeem/skm_element_spec.rb +7 -5
  51. data/spec/skeem/skm_empty_list_spec.rb +7 -5
  52. data/spec/skeem/skm_frame_spec.rb +5 -4
  53. data/spec/skeem/skm_pair_spec.rb +9 -8
  54. data/spec/skeem/skm_procedure_exec_spec.rb +2 -0
  55. data/spec/skeem/skm_simple_datum_spec.rb +24 -22
  56. data/spec/skeem/skm_unary_expression_spec.rb +11 -9
  57. data/spec/skeem/tokenizer_spec.rb +54 -43
  58. data/spec/skeem_spec.rb +2 -0
  59. data/spec/spec_helper.rb +15 -10
  60. metadata +18 -10
@@ -0,0 +1 @@
1
+ # frozen_string_literal: true
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'skm_element'
2
4
 
3
5
  module Skeem
4
-
5
6
  # An identifier that is not a syntactic keyword can be used as a variable.
6
7
  # A variable may give a name to value that is bound (i.e. associated)
7
8
  # to that variable.
@@ -16,10 +17,13 @@ module Skeem
16
17
  # @param anIdentifier [SkmIdentifier] The variable name
17
18
  # @param aValue [SkmElement] The value to bind to the variable.
18
19
  def initialize(anIdentifier, aValue)
20
+ super(nil)
19
21
  @variable = anIdentifier
20
22
  @value = aValue
21
23
  end
22
24
 
25
+ # rubocop: disable Style/NegatedIfElseCondition
26
+
23
27
  def evaluate(aRuntime)
24
28
  name = variable.evaluate(aRuntime)
25
29
 
@@ -41,6 +45,7 @@ module Skeem
41
45
  binding_action(aRuntime, name, result)
42
46
  result
43
47
  end
48
+ # rubocop: enable Style/NegatedIfElseCondition
44
49
 
45
50
  protected
46
51
 
@@ -56,26 +61,19 @@ module Skeem
56
61
 
57
62
  result
58
63
  end
59
-
60
64
  end # class
61
65
 
62
-
63
66
  class SkmUpdateBinding < SkmBinding
64
-
65
67
  protected
66
68
 
67
69
  def binding_action(aRuntime, anIdentifier, anExpression)
68
70
  aRuntime.update_binding(anIdentifier, anExpression)
69
71
  end
70
72
  end # class
71
-
73
+
72
74
  class SkmDelayedUpdateBinding < SkmBinding
73
75
  attr_reader :new_val
74
-
75
- def initialize(anIdentifier, aValue)
76
- super(anIdentifier, aValue)
77
- end
78
-
76
+
79
77
  def do_it!(aRuntime)
80
78
  aRuntime.update_binding(variable, new_val)
81
79
  end
@@ -85,5 +83,5 @@ module Skeem
85
83
  def binding_action(_runtime, _identifier, anExpression)
86
84
  @new_val = anExpression
87
85
  end
88
- end # class
86
+ end # class
89
87
  end # module
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
  require_relative 'skm_element'
3
5
 
@@ -18,10 +20,11 @@ module Skeem
18
20
  end
19
21
 
20
22
  def ==(other)
21
- return true if self.equal?(other)
22
- result = case other
23
+ return true if equal?(other)
24
+
25
+ case other
23
26
  when SkmCompoundDatum
24
- self.class == other.class && self.members == other.members
27
+ self.class == other.class && members == other.members
25
28
  when Array
26
29
  members == other
27
30
  end
@@ -65,8 +68,8 @@ module Skeem
65
68
  protected
66
69
 
67
70
  def inspect_specific
68
- result = ''
69
- members.each { |elem| result << elem.inspect + ', ' }
71
+ result = +''
72
+ members.each { |elem| result << "#{elem.inspect}, " }
70
73
  result.sub!(/, $/, '')
71
74
  result
72
75
  end
@@ -77,4 +80,4 @@ module Skeem
77
80
  true
78
81
  end
79
82
  end # class
80
- end # module
83
+ end # module
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Skeem
2
4
  # Abstract class. Generalization of any S-expr element.
3
5
  SkmElement = Struct.new(:position) do
4
6
  def initialize(aPosition)
5
7
  self.position = aPosition
6
8
  end
7
-
9
+
8
10
  def callable?
9
11
  false
10
12
  end
@@ -12,15 +14,15 @@ module Skeem
12
14
  def number?
13
15
  false
14
16
  end
15
-
17
+
16
18
  def complex?
17
19
  false
18
- end
20
+ end
19
21
 
20
22
  def real?
21
23
  false
22
24
  end
23
-
25
+
24
26
  def rational?
25
27
  false
26
28
  end
@@ -32,7 +34,7 @@ module Skeem
32
34
  def boolean?
33
35
  false
34
36
  end
35
-
37
+
36
38
  def char?
37
39
  false
38
40
  end
@@ -56,7 +58,7 @@ module Skeem
56
58
  def pair?
57
59
  false
58
60
  end
59
-
61
+
60
62
  def procedure?
61
63
  false
62
64
  end
@@ -73,11 +75,11 @@ module Skeem
73
75
  msg = "Missing implementation of method #{self.class.name}##{__method__}"
74
76
  raise NotImplementedError, msg
75
77
  end
76
-
78
+
77
79
  def skm_eq?(other)
78
80
  # Default implementation, to override when necessary
79
- self.eqv?(other)
80
- end
81
+ eqv?(other)
82
+ end
81
83
 
82
84
  # @return [TrueClass, FalseClass] true if quoted element is identical to itself
83
85
  def verbatim?
@@ -117,8 +119,8 @@ module Skeem
117
119
  # Do nothing
118
120
  end
119
121
 
120
- def inspect
121
- result = inspect_prefix
122
+ def inspect
123
+ result = inspect_prefix.dup
122
124
  result << inspect_specific
123
125
  result << inspect_suffix
124
126
 
@@ -136,7 +138,7 @@ module Skeem
136
138
  end
137
139
 
138
140
  def inspect_specific
139
- raise NotImplementedError, "Missing #{self.class.to_s + '#' + 'inspect_specific'}"
141
+ raise NotImplementedError, "Missing #{self.class}#inspect_specific"
140
142
  end
141
143
  end # struct
142
- end # module
144
+ end # module
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'singleton'
2
4
  require_relative 'skm_element'
3
5
 
@@ -42,7 +44,7 @@ module Skeem
42
44
  def klone
43
45
  self
44
46
  end
45
-
47
+
46
48
  def append_list(aList)
47
49
  aList
48
50
  end
@@ -64,14 +66,14 @@ module Skeem
64
66
  protected
65
67
 
66
68
  def inspect_specific
67
- '()'
69
+ +'()'
68
70
  end
69
71
 
70
72
  private
71
73
 
72
- def initialize()
74
+ def initialize
73
75
  super(0)
74
- self.freeze
76
+ freeze
75
77
  end
76
78
  end # class
77
79
  end # module
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skeem
4
+ class SkmException < RuntimeError
5
+ end # class
6
+
7
+ class SkmError < SkmException
8
+ end # class
9
+ end # module
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'skm_element'
2
4
 
3
5
  module Skeem
4
6
  # Abstract class.
5
7
  class SkmExpression < SkmElement
6
8
  end # class
7
- end # module
9
+ end # module
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'skm_simple_datum'
2
4
 
3
5
  module Skeem
@@ -139,6 +141,5 @@ module Skeem
139
141
  # Notify the value that it is bound to a variable from this frame.
140
142
  anExpression.bound!(self)
141
143
  end
142
-
143
144
  end # class
144
- end # module
145
+ end # module
@@ -1,8 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'skm_empty_list'
2
4
 
3
5
  module Skeem
4
6
  class SkmPair < SkmElement
7
+ # @return [SkmElement]
5
8
  attr_accessor :car
9
+
10
+ # @return [SkmElement]
6
11
  attr_accessor :cdr
7
12
 
8
13
  alias first car
@@ -41,11 +46,12 @@ module Skeem
41
46
  def self.create_from_a(anArray)
42
47
  current = nil
43
48
  return SkmEmptyList.instance if anArray.empty?
49
+
44
50
  anArray.reverse_each do |elem|
45
51
  if current.nil?
46
- current = self.new(elem, SkmEmptyList.instance)
52
+ current = new(elem, SkmEmptyList.instance)
47
53
  else
48
- current = self.new(elem, current)
54
+ current = new(elem, current)
49
55
  end
50
56
  end
51
57
 
@@ -54,6 +60,7 @@ module Skeem
54
60
 
55
61
  def empty?
56
62
  return false if car
63
+
57
64
  if [SkmPair, SkmEmptyList].include? cdr.class
58
65
  cdr.empty?
59
66
  else
@@ -92,20 +99,20 @@ module Skeem
92
99
  end
93
100
 
94
101
  def last_pair
95
- if cdr.nil? || (cdr == SkmEmptyList.instance) || (!cdr.kind_of?(SkmPair))
102
+ if cdr.nil? || (cdr == SkmEmptyList.instance) || !cdr.kind_of?(SkmPair)
96
103
  self
97
104
  else
98
105
  cdr.last_pair
99
106
  end
100
107
  end
101
-
108
+
102
109
  def proper?
103
- last_node = self.last_pair
110
+ last_node = last_pair
104
111
  last_node.cdr.nil? || (last_node.cdr == SkmEmptyList.instance)
105
112
  end
106
113
 
107
114
  def last
108
- self.to_a.last
115
+ to_a.last
109
116
  end
110
117
 
111
118
  def eqv?(_other)
@@ -138,7 +145,7 @@ module Skeem
138
145
  end
139
146
 
140
147
  def append(anElement)
141
- last_one = self.last_pair
148
+ last_one = last_pair
142
149
  if last_one.cdr.nil? || last_one.cdr.kind_of?(SkmEmptyList)
143
150
  last_one.cdr = SkmPair.new(anElement, SkmEmptyList.instance)
144
151
  else
@@ -147,7 +154,7 @@ module Skeem
147
154
  end
148
155
 
149
156
  def append_list(aList)
150
- last_one = self.last_pair
157
+ last_one = last_pair
151
158
  if last_one.cdr.nil? || last_one.cdr.kind_of?(SkmEmptyList)
152
159
  last_one.cdr = aList
153
160
  else
@@ -157,22 +164,23 @@ module Skeem
157
164
 
158
165
  def evaluate(aRuntime)
159
166
  return SkmEmptyList.instance if empty?
167
+
160
168
  if car.kind_of?(SkmIdentifier) && car.is_var_name
161
169
  result = aRuntime.evaluate_form(self)
162
170
  else
163
171
  begin
164
172
  result = clone_evaluate(aRuntime)
165
- rescue NoMethodError => exc
166
- $stderr.puts 'SkmPair#evaluate: ' + self.inspect
167
- $stderr.puts 'SkmPair as Array: ' + self.to_a.inspect
168
- raise exc
173
+ rescue NoMethodError => e
174
+ $stderr.puts "SkmPair#evaluate: #{inspect}"
175
+ $stderr.puts "SkmPair as Array: #{to_a.inspect}"
176
+ raise e
169
177
  end
170
178
  end
171
179
  result
172
180
  end
173
181
 
174
182
  def quasiquote(aRuntime)
175
- members_eval = self.to_a.map { |elem| elem.quasiquote(aRuntime) }
183
+ members_eval = to_a.map { |elem| elem.quasiquote(aRuntime) }
176
184
  self.class.create_from_a(members_eval)
177
185
  end
178
186
 
@@ -184,16 +192,17 @@ module Skeem
184
192
 
185
193
  def quoted!
186
194
  car.quoted!
187
- cdr.quoted! if cdr
195
+ cdr&.quoted!
188
196
  end
189
197
 
190
198
  def unquoted!
191
199
  car.unquoted!
192
- cdr.unquoted! if cdr
200
+ cdr&.unquoted!
193
201
  end
194
202
 
195
- def verbatim?()
203
+ def verbatim?
196
204
  return false unless car.verbatim?
205
+
197
206
  if cdr.nil?
198
207
  true
199
208
  else
@@ -211,6 +220,5 @@ module Skeem
211
220
 
212
221
  result
213
222
  end
214
-
215
223
  end # class
216
- end # module
224
+ end # module
@@ -1,8 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'runtime'
2
4
 
3
5
  module Skeem
4
6
  class SkmProcedureExec
7
+ # @return [SkmFrame]
5
8
  attr_reader :frame
9
+
10
+ # @return [SkmLambda]
6
11
  attr_reader :definition
7
12
 
8
13
  def initialize(aLambda)
@@ -27,18 +32,18 @@ module Skeem
27
32
  result = definition.evaluate_sequence(runtime)
28
33
  runtime.pop
29
34
  # $stderr.puts "Lambda result: #{result.object_id.to_s(16)}" if result.kind_of?(SkmLambda)
30
-
35
+
31
36
  result
32
37
  end
33
-
38
+
34
39
  private
35
-
40
+
36
41
  def evaluate_defs(aRuntime)
37
42
  definition.definitions.each do |bndng|
38
- val = bndng.value.evaluate(aRuntime)
43
+ val = bndng.value.evaluate(aRuntime)
39
44
  var = bndng.variable.evaluate(aRuntime)
40
45
  frame.add_binding(var, val)
41
- end
46
+ end
42
47
  end
43
48
  end # class
44
- end # module
49
+ end # module
@@ -1,10 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'skm_element'
2
4
 
3
5
  module Skeem
4
6
  # Abstract class. Root of class hierarchy needed for Interpreter
5
7
  # design pattern
6
8
  class SkmSimpleDatum < SkmElement
9
+ # @return [Rley::Syntax::Token] token object corresponding to Skeem element
7
10
  attr_reader :token
11
+
12
+ # @return [Object]
8
13
  attr_reader :value
9
14
 
10
15
  def initialize(aToken, aPosition)
@@ -14,7 +19,7 @@ module Skeem
14
19
  end
15
20
 
16
21
  def self.create(aValue)
17
- lightweight = self.allocate
22
+ lightweight = allocate
18
23
  lightweight.init_value(aValue)
19
24
  return lightweight
20
25
  end
@@ -33,15 +38,13 @@ module Skeem
33
38
  # @param other [SkmSimpleDatum, Object] object to compare with.
34
39
  # @return [TrueClass, FalseClass]
35
40
  def ==(other)
36
- return true if self.equal?(other)
37
-
38
- result = if other.kind_of?(SkmSimpleDatum)
39
- self.value == other.value
40
- else
41
- self.value == other
42
- end
41
+ return true if equal?(other)
43
42
 
44
- result
43
+ if other.kind_of?(SkmSimpleDatum)
44
+ value == other.value
45
+ else
46
+ value == other
47
+ end
45
48
  end
46
49
 
47
50
  alias eqv? ==
@@ -51,7 +54,7 @@ module Skeem
51
54
  true
52
55
  end
53
56
 
54
- def done!()
57
+ def done!
55
58
  # Do nothing
56
59
  end
57
60
 
@@ -102,21 +105,22 @@ module Skeem
102
105
  false
103
106
  end
104
107
 
108
+ # rubocop: disable Style/NegatedIfElseCondition
109
+
105
110
  def eqv?(other)
106
- return true if self.equal?(other)
111
+ return true if equal?(other)
107
112
 
108
- result = if other.kind_of?(SkmNumber)
109
- if self.exact? != other.exact?
113
+ if other.kind_of?(SkmNumber)
114
+ if exact? != other.exact?
110
115
  false
111
116
  else
112
- self.value == other.value
117
+ value == other.value
113
118
  end
114
119
  else
115
- self.value == other
120
+ value == other
116
121
  end
117
-
118
- result
119
122
  end
123
+ # rubocop: enable Style/NegatedIfElseCondition
120
124
  end # class
121
125
 
122
126
  class SkmReal < SkmNumber
@@ -159,7 +163,6 @@ module Skeem
159
163
  char_value = int_value < 0xff ? int_value.chr : [int_value].pack('U')
160
164
  create(char_value)
161
165
  end
162
-
163
166
  end # class
164
167
 
165
168
  class SkmString < SkmSimpleDatum
@@ -199,7 +202,7 @@ module Skeem
199
202
  end
200
203
 
201
204
  def verbatim?
202
- not is_var_name
205
+ !is_var_name
203
206
  end
204
207
 
205
208
  def quoted!
@@ -213,4 +216,4 @@ module Skeem
213
216
 
214
217
  class SkmReserved < SkmIdentifier
215
218
  end # class
216
- end # module
219
+ end # module