skeem 0.2.15 → 0.2.16

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +17 -11
  3. data/CHANGELOG.md +5 -0
  4. data/Gemfile +2 -0
  5. data/README.md +3 -2
  6. data/Rakefile +2 -0
  7. data/appveyor.yml +3 -4
  8. data/bin/skeem +15 -15
  9. data/lib/skeem.rb +2 -0
  10. data/lib/skeem/datum_dsl.rb +12 -3
  11. data/lib/skeem/element_visitor.rb +5 -2
  12. data/lib/skeem/grammar.rb +86 -24
  13. data/lib/skeem/interpreter.rb +5 -3
  14. data/lib/skeem/parser.rb +6 -4
  15. data/lib/skeem/primitive/primitive_builder.rb +128 -115
  16. data/lib/skeem/primitive/primitive_procedure.rb +17 -20
  17. data/lib/skeem/runtime.rb +9 -5
  18. data/lib/skeem/s_expr_builder.rb +46 -104
  19. data/lib/skeem/s_expr_nodes.rb +116 -90
  20. data/lib/skeem/skeem_exception.rb +0 -0
  21. data/lib/skeem/skm_binding.rb +6 -7
  22. data/lib/skeem/skm_compound_datum.rb +8 -4
  23. data/lib/skeem/skm_element.rb +14 -12
  24. data/lib/skeem/skm_empty_list.rb +6 -4
  25. data/lib/skeem/skm_exception.rb +9 -0
  26. data/lib/skeem/skm_expression.rb +3 -1
  27. data/lib/skeem/skm_frame.rb +3 -2
  28. data/lib/skeem/skm_pair.rb +23 -18
  29. data/lib/skeem/skm_procedure_exec.rb +8 -6
  30. data/lib/skeem/skm_simple_datum.rb +13 -12
  31. data/lib/skeem/skm_unary_expression.rb +15 -17
  32. data/lib/skeem/tokenizer.rb +32 -25
  33. data/lib/skeem/version.rb +3 -1
  34. data/skeem.gemspec +6 -4
  35. data/spec/skeem/add4.skm +4 -0
  36. data/spec/skeem/datum_dsl_spec.rb +13 -12
  37. data/spec/skeem/element_visitor_spec.rb +12 -10
  38. data/spec/skeem/interpreter_spec.rb +74 -46
  39. data/spec/skeem/lambda_spec.rb +9 -7
  40. data/spec/skeem/parser_spec.rb +21 -19
  41. data/spec/skeem/primitive/primitive_builder_spec.rb +57 -48
  42. data/spec/skeem/primitive/primitive_procedure_spec.rb +15 -13
  43. data/spec/skeem/runtime_spec.rb +18 -16
  44. data/spec/skeem/s_expr_nodes_spec.rb +8 -6
  45. data/spec/skeem/skm_compound_datum_spec.rb +11 -9
  46. data/spec/skeem/skm_element_spec.rb +7 -5
  47. data/spec/skeem/skm_empty_list_spec.rb +7 -5
  48. data/spec/skeem/skm_frame_spec.rb +5 -4
  49. data/spec/skeem/skm_pair_spec.rb +4 -3
  50. data/spec/skeem/skm_procedure_exec_spec.rb +2 -0
  51. data/spec/skeem/skm_simple_datum_spec.rb +24 -22
  52. data/spec/skeem/skm_unary_expression_spec.rb +11 -9
  53. data/spec/skeem/tokenizer_spec.rb +53 -44
  54. data/spec/skeem_spec.rb +2 -0
  55. data/spec/spec_helper.rb +4 -2
  56. metadata +7 -4
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'skm_expression'
2
4
 
3
5
  module Skeem
@@ -29,7 +31,7 @@ module Skeem
29
31
  super(nil, aDatum)
30
32
  end
31
33
 
32
- def evaluate(aRuntime)
34
+ def evaluate(_runtime)
33
35
  datum
34
36
  end
35
37
 
@@ -47,7 +49,6 @@ module Skeem
47
49
  def inspect_specific
48
50
  datum.inspect
49
51
  end
50
-
51
52
  end # class
52
53
 
53
54
  class SkmQuasiquotation < SkmQuotation
@@ -60,7 +61,6 @@ module Skeem
60
61
  def quasiquote(aRuntime)
61
62
  child.quasiquote(aRuntime)
62
63
  end
63
-
64
64
  end # class
65
65
 
66
66
  class SkmUnquotation < SkmUnaryExpression
@@ -87,7 +87,7 @@ module Skeem
87
87
  end
88
88
  end # class
89
89
 
90
- class SkmVariableReference < SkmUnaryExpression
90
+ class SkmVariableReference < SkmUnaryExpression
91
91
  alias variable child
92
92
 
93
93
  def eqv?(other)
@@ -100,13 +100,13 @@ module Skeem
100
100
  aRuntime.evaluate(var_key)
101
101
  end
102
102
 
103
- def quasiquote(aRuntime)
103
+ def quasiquote(_runtime)
104
104
  self
105
105
  end
106
106
 
107
107
  # Confusing!
108
108
  # Value, here, means the value of the identifier (the variable's name).
109
- def value()
109
+ def value
110
110
  variable.value
111
111
  end
112
112
  end # class
@@ -155,7 +155,6 @@ module Skeem
155
155
  # $stderr.puts "Result SkmBindingBlock#evaluate: " + result.inspect
156
156
  result
157
157
  end
158
-
159
158
  end # class
160
159
 
161
160
  # Sequencing construct
@@ -193,11 +192,11 @@ module Skeem
193
192
  else
194
193
  result = cmd.evaluate(aRuntime)
195
194
  end
196
- rescue NoMethodError => exc
197
- $stderr.puts self.inspect
195
+ rescue NoMethodError => e
196
+ $stderr.puts inspect
198
197
  $stderr.puts sequence.inspect
199
198
  $stderr.puts cmd.inspect
200
- raise exc
199
+ raise e
201
200
  end
202
201
  end
203
202
 
@@ -222,21 +221,20 @@ module Skeem
222
221
  else
223
222
  result = cmd.evaluate(aRuntime)
224
223
  end
225
- rescue NoMethodError => exc
226
- $stderr.puts self.inspect
224
+ rescue NoMethodError => e
225
+ $stderr.puts inspect
227
226
  $stderr.puts sequence[:sequence].inspect
228
227
  $stderr.puts cmd.inspect
229
- raise exc
228
+ raise e
230
229
  end
231
230
  end
232
- elsif
231
+ else
233
232
  result = sequence.evaluate(aRuntime)
234
233
  end
235
234
 
236
235
  aRuntime.pop
237
-
236
+
238
237
  result
239
238
  end
240
239
  end # class
241
-
242
- end # module
240
+ end # module
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # File: tokenizer.rb
2
4
  # Tokenizer for Skeem language (a small subset of Scheme)
3
5
  require 'strscan'
@@ -25,9 +27,11 @@ module Skeem
25
27
  '(' => 'LPAREN',
26
28
  ')' => 'RPAREN',
27
29
  '.' => 'PERIOD',
30
+ '...' => 'ELLIPSIS',
28
31
  ',' => 'COMMA',
29
32
  ',@' => 'COMMA_AT_SIGN',
30
- '#(' => 'VECTOR_BEGIN'
33
+ '#(' => 'VECTOR_BEGIN',
34
+ '_' => 'UNDERSCORE'
31
35
  }.freeze
32
36
 
33
37
  # Here are all the implemented Scheme keywords (in uppercase)
@@ -35,15 +39,18 @@ module Skeem
35
39
  BEGIN
36
40
  COND
37
41
  DEFINE
42
+ DEFINE-SYNTAX
38
43
  DO
39
44
  ELSE
40
45
  IF
46
+ INCLUDE
41
47
  LAMBDA
42
48
  LET
43
49
  LET*
44
50
  QUASIQUOTE
45
51
  QUOTE
46
52
  SET!
53
+ SYNTAX-RULES
47
54
  UNQUOTE
48
55
  UNQUOTE-SPLICING
49
56
  ].map { |x| [x, x] } .to_h
@@ -57,7 +64,6 @@ module Skeem
57
64
  reinitialize(source)
58
65
  end
59
66
 
60
-
61
67
  # @param source [String] Skeem text to tokenize.
62
68
  def reinitialize(source)
63
69
  @scanner.string = source
@@ -88,9 +94,9 @@ module Skeem
88
94
  if "()'`".include? curr_ch
89
95
  # Delimiters, separators => single character token
90
96
  token = build_token(@@lexeme2name[curr_ch], scanner.getch)
91
- elsif (lexeme = scanner.scan(/(?:\.)(?=\s)/)) # Single char occurring alone
92
- token = build_token('PERIOD', lexeme)
93
- elsif (lexeme = scanner.scan(/(?:,@?)|(?:=>)/))
97
+ elsif (lexeme = scanner.scan(/(?:\.|_)(?=\s|\()/)) # Single char occurring alone
98
+ token = build_token(@@lexeme2name[curr_ch], scanner.getch)
99
+ elsif (lexeme = scanner.scan(/(?:,@?)|(?:=>)|(?:\.\.\.)/))
94
100
  token = build_token(@@lexeme2name[lexeme], lexeme)
95
101
  elsif (token = recognize_char_token)
96
102
  # Do nothing
@@ -99,17 +105,17 @@ module Skeem
99
105
  elsif (lexeme = scanner.scan(/(?:#[dD])?[+-]?[0-9]+(?:.0+)?(?=\s|[|()";]|$)/))
100
106
  token = build_token('INTEGER', lexeme) # Decimal radix
101
107
  elsif (lexeme = scanner.scan(/#[xX][+-]?[0-9a-fA-F]+(?=\s|[|()";]|$)/))
102
- token = build_token('INTEGER', lexeme) # Hexadecimal radix
108
+ token = build_token('INTEGER', lexeme) # Hexadecimal radix
103
109
  elsif (lexeme = scanner.scan(/[+-]?[0-9]+(?:\.[0-9]*)?(?:(?:e|E)[+-]?[0-9]+)?/))
104
110
  # Order dependency: must be tested after INTEGER case
105
111
  token = build_token('REAL', lexeme)
106
112
  elsif (lexeme = scanner.scan(/#(?:(?:true)|(?:false)|(?:u8)|[\\\(tfeiodx]|(?:\d+[=#]))/))
107
- token = cardinal_token(lexeme)
113
+ token = cardinal_token(lexeme)
108
114
  elsif (lexeme = scanner.scan(/"(?:\\"|[^"])*"/)) # Double quotes literal?
109
115
  token = build_token('STRING_LIT', lexeme)
110
116
  elsif (lexeme = scanner.scan(/[a-zA-Z!$%&*\/:<=>?@^_~][a-zA-Z0-9!$%&*+-.\/:<=>?@^_~+-]*/))
111
117
  keyw = @@keywords[lexeme.upcase]
112
- tok_type = keyw ? keyw : 'IDENTIFIER'
118
+ tok_type = keyw || 'IDENTIFIER'
113
119
  token = build_token(tok_type, lexeme)
114
120
  elsif (lexeme = scanner.scan(/\|(?:[^|])*\|/)) # Vertical bar delimited
115
121
  token = build_token('IDENTIFIER', lexeme)
@@ -151,17 +157,17 @@ other literal data (section 2.4).
151
157
  return token
152
158
  end
153
159
 
154
- def recognize_char_token()
160
+ def recognize_char_token
155
161
  token = nil
156
- if lexeme = scanner.scan(/#\\/)
157
- if lexeme = scanner.scan(/(?:alarm|backspace|delete|escape|newline|null|return|space|tab)/)
162
+ if (lexeme = scanner.scan(/#\\/))
163
+ if (lexeme = scanner.scan(/(?:alarm|backspace|delete|escape|newline|null|return|space|tab)/))
158
164
  token = build_token('CHAR', lexeme, :name)
159
- elsif lexeme = scanner.scan(/[^x]/)
165
+ elsif (lexeme = scanner.scan(/[^x]/))
160
166
  token = build_token('CHAR', lexeme, :escaped)
161
- elsif lexeme = scanner.scan(/x[0-9a-fA-F]+/)
167
+ elsif (lexeme = scanner.scan(/x[0-9a-fA-F]+/))
162
168
  token = build_token('CHAR', lexeme, :hex_value)
163
- elsif lexeme = scanner.scan(/x/)
164
- token = build_token('CHAR', lexeme, :escaped)
169
+ elsif (lexeme = scanner.scan(/x/))
170
+ token = build_token('CHAR', lexeme, :escaped)
165
171
  end
166
172
  end
167
173
 
@@ -174,9 +180,9 @@ other literal data (section 2.4).
174
180
  col = scanner.pos - aLexeme.size - @line_start + 1
175
181
  pos = Rley::Lexical::Position.new(@lineno, col)
176
182
  token = Rley::Lexical::Token.new(value, symb, pos)
177
- rescue StandardError => exc
183
+ rescue StandardError => e
178
184
  puts "Failing with '#{aSymbolName}' and '#{aLexeme}'"
179
- raise exc
185
+ raise e
180
186
  end
181
187
 
182
188
  return token
@@ -207,11 +213,11 @@ other literal data (section 2.4).
207
213
  return [value, symb]
208
214
  end
209
215
 
210
- def to_boolean(aLexeme, aFormat)
211
- result = (aLexeme =~ /^#t/) ? true : false
216
+ def to_boolean(aLexeme, _format)
217
+ (aLexeme =~ /^#t/) ? true : false
212
218
  end
213
219
 
214
- def to_integer(aLexeme, aFormat)
220
+ def to_integer(aLexeme, _format)
215
221
  literal = aLexeme.downcase
216
222
  prefix_pattern = /^#[dx]/
217
223
  matching = literal.match(prefix_pattern)
@@ -226,7 +232,7 @@ other literal data (section 2.4).
226
232
  else
227
233
  format = :default
228
234
  end
229
-
235
+
230
236
  case format
231
237
  when :default, :base10
232
238
  value = literal.to_i
@@ -303,11 +309,11 @@ other literal data (section 2.4).
303
309
 
304
310
  name2char[name]
305
311
  end
306
-
312
+
307
313
  def escaped_char(aLexeme)
308
314
  aLexeme.chr
309
315
  end
310
-
316
+
311
317
  def hex_value_char(aLexeme)
312
318
  hex_literal = aLexeme.sub(/^x/, '')
313
319
  hex_value = hex_literal.to_i(16)
@@ -338,14 +344,14 @@ other literal data (section 2.4).
338
344
  skip_block_comment
339
345
  next
340
346
  end
341
- break unless ws_found or cmt_found
347
+ break unless ws_found || cmt_found
342
348
  end
343
349
 
344
350
  curr_pos = scanner.pos
345
351
  return if curr_pos == pre_pos
346
352
  end
347
353
 
348
- def skip_block_comment()
354
+ def skip_block_comment
349
355
  # require 'debug'
350
356
  scanner.skip(/#\|/)
351
357
  nesting_level = 1
@@ -354,6 +360,7 @@ other literal data (section 2.4).
354
360
  unless comment_part
355
361
  raise ScanError, "Unterminated '#| ... |#' comment on line #{lineno}"
356
362
  end
363
+
357
364
  case scanner.matched
358
365
  when /(?:(?:\r\n)|\r|\n)/
359
366
  next_line
data/lib/skeem/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Skeem
2
- VERSION = '0.2.15'.freeze
4
+ VERSION = '0.2.16'
3
5
  end
data/skeem.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  lib = File.expand_path('../lib', __FILE__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
  require 'skeem/version'
@@ -21,6 +23,7 @@ module PkgExtending
21
23
  'lib/**/*.rb',
22
24
  'lib/**/*.skm',
23
25
  'spec/**/*.rb',
26
+ 'spec/**/*.skm'
24
27
  ]
25
28
  aPackage.files = file_list
26
29
  aPackage.test_files = Dir['spec/**/*_spec.rb']
@@ -49,6 +52,7 @@ DESCR
49
52
  SUMMARY
50
53
  spec.homepage = 'https://github.com/famished-tiger/Skeem'
51
54
  spec.license = 'MIT'
55
+ spec.required_ruby_version = '>= 2.3.0'
52
56
 
53
57
  spec.bindir = 'bin'
54
58
  spec.executables << 'skeem'
@@ -59,11 +63,9 @@ SUMMARY
59
63
  spec.add_dependency 'rley', '~> 0.7'
60
64
 
61
65
  # Development dependencies
62
- if RUBY_VERSION <= '2.2'
63
- spec.add_development_dependency 'bundler', '~> 1.16'
64
- else
65
66
  spec.add_development_dependency 'bundler', '~> 2.0'
66
- end
67
67
  spec.add_development_dependency 'rake', '~> 12.0'
68
68
  spec.add_development_dependency 'rspec', '~> 3.0'
69
+
70
+
69
71
  end
@@ -0,0 +1,4 @@
1
+ (define add4
2
+ (let ((x 4))
3
+ (lambda (y) (+ x y))))
4
+ (add4 6) ; => 10
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper'
2
4
 
3
5
 
@@ -35,15 +37,15 @@ module Skeem
35
37
  ['+456', 456]
36
38
  ]
37
39
  end
38
-
40
+
39
41
  let(:rational_tests) do
40
42
  [
41
- [-Rational(2,3), -Rational(2, 3)],
43
+ [-Rational(2, 3), -Rational(2, 3)],
42
44
  [Rational(22, 7), Rational(22, 7)],
43
45
  ['-2/3', -Rational(2, 3)],
44
46
  ['+22/7', Rational(22, 7)]
45
47
  ]
46
- end
48
+ end
47
49
 
48
50
  let(:real_tests) do
49
51
  [
@@ -60,13 +62,13 @@ module Skeem
60
62
 
61
63
  let(:string_tests) do
62
64
  [
63
- ['hello', 'hello']
65
+ %w[hello hello]
64
66
  ]
65
67
  end
66
68
 
67
69
  let(:identifier_tests) do
68
70
  [
69
- ['define', 'define'],
71
+ %w[define define],
70
72
  [SkmString.create('positive?'), 'positive?']
71
73
  ]
72
74
  end
@@ -76,7 +78,7 @@ module Skeem
76
78
  ['#t', SkmBoolean.create(true)],
77
79
  [-1, SkmInteger.create(-1)],
78
80
  [1.41, 1.41],
79
- ['foo', 'foo']
81
+ %w[foo foo]
80
82
  ]
81
83
  end
82
84
 
@@ -92,12 +94,12 @@ module Skeem
92
94
  expect(subject.integer(literal)).to eq(predicted)
93
95
  end
94
96
  end
95
-
97
+
96
98
  it 'should convert rational literals' do
97
99
  rational_tests.each do |(literal, predicted)|
98
100
  expect(subject.rational(literal)).to eq(predicted)
99
101
  end
100
- end
102
+ end
101
103
 
102
104
  it 'should convert real number literals' do
103
105
  real_tests.each do |(literal, predicted)|
@@ -178,12 +180,12 @@ module Skeem
178
180
  expect(subject.to_datum(literal)).to eq(predicted)
179
181
  end
180
182
  end
181
-
183
+
182
184
  it 'should recognize & convert rational literals' do
183
185
  rational_tests.each do |(literal, predicted)|
184
186
  expect(subject.to_datum(literal)).to eq(predicted)
185
187
  end
186
- end
188
+ end
187
189
 
188
190
  it 'should recognize & convert real number literals' do
189
191
  real_tests.each do |(literal, predicted)|
@@ -219,6 +221,5 @@ module Skeem
219
221
  # $stderr.puts duplicate.inspect
220
222
  expect(duplicate.to_a).to eq([f, g])
221
223
  end
222
-
223
224
  end # describe
224
- end # module
225
+ end # module
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper'
2
4
  require_relative '../../lib/skeem/datum_dsl'
3
5
 
@@ -7,12 +9,12 @@ require_relative '../../lib/skeem/element_visitor'
7
9
  module Skeem
8
10
  describe SkmElementVisitor do
9
11
  include DatumDSL
10
- let(:simple_datum) { integer 42 }
11
- let(:listener) do
12
- fake = double('fake-subscriber')
13
- fake.define_singleton_method(:accept_all) {}
14
- fake
15
- end
12
+ let(:simple_datum) { integer 42 }
13
+ let(:listener) do
14
+ fake = double('fake-subscriber')
15
+ fake.define_singleton_method(:accept_all) {}
16
+ fake
17
+ end
16
18
 
17
19
  # Default instantiation rule
18
20
  subject { SkmElementVisitor.new(simple_datum) }
@@ -122,7 +124,7 @@ module Skeem
122
124
  end
123
125
 
124
126
  it 'should allow the visit of a nested compound datum' do
125
- nested_list = list ['uno', 'twei', 'three']
127
+ nested_list = list %w[uno twei three']
126
128
  vec = vector ['#false', 3, nested_list, 'foo']
127
129
  instance = SkmElementVisitor.new(vec)
128
130
  instance.subscribe(listener)
@@ -132,7 +134,7 @@ module Skeem
132
134
  expect(listener).to receive(:after_simple_datum).with(runtime, vec.members[0]).ordered
133
135
  expect(listener).to receive(:before_simple_datum).with(runtime, vec.members[1]).ordered
134
136
  expect(listener).to receive(:after_simple_datum).with(runtime, vec.members[1]).ordered
135
-
137
+
136
138
  expect(listener).to receive(:before_pair).with(runtime, nested_list).ordered
137
139
  expect(listener).to receive(:before_car).with(runtime, nested_list, nested_list.car).ordered
138
140
  expect(nested_list.car).to eq('uno')
@@ -159,7 +161,7 @@ module Skeem
159
161
  expect(listener).to receive(:after_cdr).with(runtime, nested_list.cdr, nested_list.cdr.cdr).ordered
160
162
  expect(listener).to receive(:after_pair).with(runtime, nested_list.cdr).ordered
161
163
  expect(listener).to receive(:after_cdr).with(runtime, nested_list, nested_list.cdr).ordered
162
- expect(listener).to receive(:after_pair).with(runtime, nested_list).ordered
164
+ expect(listener).to receive(:after_pair).with(runtime, nested_list).ordered
163
165
  expect(listener).to receive(:before_simple_datum).with(runtime, vec.members[3]).ordered
164
166
  expect(listener).to receive(:after_simple_datum).with(runtime, vec.members[3]).ordered
165
167
  expect(listener).to receive(:after_children).with(runtime, vec, vec.members).ordered
@@ -168,4 +170,4 @@ module Skeem
168
170
  end
169
171
  end # context
170
172
  end # describe
171
- end # module
173
+ end # module