skeem 0.2.15 → 0.2.16

Sign up to get free protection for your applications and to get access to all the features.
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