skeem 0.0.17 → 0.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0c8a2b41577b541764b4ff1e2702ba30502020a4
4
- data.tar.gz: f2844f165479416e313c25452ed7e6d05a8cf722
3
+ metadata.gz: 40345a7d71935a1329252989a9203c2acd2df799
4
+ data.tar.gz: d6825f7d3703966a81261551dbcde76e3aa9633f
5
5
  SHA512:
6
- metadata.gz: a070f9dd299720eb3b5c2b71a3133270183d923a766b9d3a6d10ed62e539875bd59f9fc30aec016756ba82ff359978dec03a8f76a12771710c1f58cdeac64c1d
7
- data.tar.gz: 364f3f3d0da779b2cd0e6e083902b9db9fd97368b6254ac80cc700069c53187524fdaf9f1e9724fd3d4dd176cccaa0a0cf786265006fff8b1dd69190641107ba
6
+ metadata.gz: a8a2f3b40f5869a33962577afa77b33ec6e117bdbcba6764ef485cd9a7941fb15d3fd1b708f1488d8b1f5cacea302d186c2c07165ba3fb42ee8ba755aa99e5f1
7
+ data.tar.gz: e3ebc87f20fd00380e020e0ca9e24b29e1943d1504bdc4606fda873b05cda6c8af58ed1d497bebc5ee8f0abd40aa2fc2bdf6d9e74250e7c1d4a28acdee831e1d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## [0.0.18] - 2018-10-14
2
+ Reworked procedure argument-passing.
3
+
4
+ ### Added
5
+ - Classes `SkmArity`, `SkmFormals` implement the core argument counting checks.
6
+
7
+ ### Changed
8
+ - Class `PrimitiveProcedure` vastly reworked to support Scheme's argument passing convention.
9
+ - Class `PrimitiveBuilder` primitive procedures now check the number of arguments.
10
+ - Class `SkmLambda` vastly reworked to support Scheme's argument passing convention.
11
+
12
+ ### Fixed
13
+ - Method `Tokenizer#skip_whitespaces` Fix: comment lines were ignored in line counting.
14
+ - Method `Tokenizer#_next_token` Fix: mistake in regex for period (dot) recognition.
15
+
1
16
  ## [0.0.17] - 2018-10-06
2
17
  - Fix: now support calls of anonymous lambda procedures.
3
18
 
data/lib/skeem/grammar.rb CHANGED
@@ -43,16 +43,16 @@ module Skeem
43
43
  rule('operand_plus' => 'operand').as 'last_operand'
44
44
  rule 'operator' => 'expression'
45
45
  rule 'operand' => 'expression'
46
- rule('lambda_expression' => 'LPAREN LAMBDA formals body RPAREN').as 'lambda_expression'
47
46
  rule 'def_formals' => 'identifier_star'
48
- # rule('def_formals' => 'identifier_star period identifier').as 'dotted_formals'
49
- rule('formals' => 'LPAREN identifier_star RPAREN').as 'identifiers_as_formals'
50
- rule 'formals' => 'IDENTIFIER'
51
- rule 'formals' => 'LPAREN identifier_plus PERIOD IDENTIFIER RPAREN'
47
+ # rule('def_formals' => 'identifier_star period identifier').as 'OTHER_NAME_formals'
48
+ rule('lambda_expression' => 'LPAREN LAMBDA formals body RPAREN').as 'lambda_expression'
49
+ rule('formals' => 'LPAREN identifier_star RPAREN').as 'fixed_arity_formals'
50
+ rule('formals' => 'IDENTIFIER').as 'variadic_formals'
51
+ rule('formals' => 'LPAREN identifier_plus PERIOD IDENTIFIER RPAREN').as 'dotted_formals'
52
52
  rule('identifier_star' => 'identifier_star IDENTIFIER').as 'identifier_star'
53
53
  rule('identifier_star' => []).as 'no_identifier_yet'
54
- rule 'identifier_plus' => 'identifier_plus IDENTIFIER'
55
- rule 'identifier_plus' => 'IDENTIFIER'
54
+ rule('identifier_plus' => 'identifier_plus IDENTIFIER').as 'multiple_identifiers'
55
+ rule('identifier_plus' => 'IDENTIFIER').as 'last_identifier'
56
56
  rule('body' => 'definition_star sequence').as 'body'
57
57
  rule 'definition_star' => 'definition_star definition'
58
58
  rule 'definition_star' => []
@@ -8,13 +8,13 @@ module Skeem
8
8
  include Primitive::PrimitiveBuilder
9
9
  attr_reader(:parser)
10
10
  attr_reader(:runtime)
11
-
11
+
12
12
  def initialize()
13
13
  @runtime = Runtime.new(Environment.new)
14
14
  add_primitives(runtime)
15
15
  add_standard(runtime)
16
16
  end
17
-
17
+
18
18
  def run(source)
19
19
  @parser ||= Parser.new
20
20
  @ptree = parser.parse(source)
@@ -22,17 +22,21 @@ module Skeem
22
22
  return @ptree.root.evaluate(runtime)
23
23
  end
24
24
 
25
+ def fetch(anIdentifier)
26
+ runtime.environment.fetch(anIdentifier)
27
+ end
28
+
25
29
  private
26
-
30
+
27
31
  def add_standard(aRuntime)
28
32
  std_pathname = File.dirname(__FILE__) + '/standard/base.skm'
29
33
  load_lib(std_pathname)
30
34
  end
31
-
35
+
32
36
  def load_lib(aPathname)
33
37
  lib_source = nil
34
- File.open(aPathname, 'r') do |lib|
35
- lib_source = lib.read
38
+ File.open(aPathname, 'r') do |lib|
39
+ lib_source = lib.read
36
40
  run(lib_source)
37
41
  end
38
42
  end
@@ -1,4 +1,4 @@
1
- require_relative '../primitive_procedure'
1
+ require_relative 'primitive_procedure'
2
2
  require_relative '../convertible'
3
3
 
4
4
  module Skeem
@@ -12,214 +12,274 @@ module Skeem
12
12
  add_boolean_procedures(aRuntime)
13
13
  add_string_procedures(aRuntime)
14
14
  add_symbol_procedures(aRuntime)
15
- add_output_procedures(aRuntime)
15
+ add_io_procedures(aRuntime)
16
16
  add_special_procedures(aRuntime)
17
17
  end
18
18
 
19
19
  private
20
+
21
+ def nullary
22
+ SkmArity.new(0, 0)
23
+ end
24
+
25
+ def unary
26
+ SkmArity.new(1, 1)
27
+ end
28
+
29
+ def binary
30
+ SkmArity.new(2, 2)
31
+ end
32
+
33
+ def zero_or_more
34
+ SkmArity.new(0, '*')
35
+ end
36
+
37
+ def one_or_more
38
+ SkmArity.new(1, '*')
39
+ end
20
40
 
21
41
  def add_arithmetic(aRuntime)
22
- def_procedure(aRuntime, create_plus)
23
- def_procedure(aRuntime, create_minus)
24
- def_procedure(aRuntime, create_multiply)
25
- def_procedure(aRuntime, create_divide)
26
- def_procedure(aRuntime, create_modulo)
42
+ create_plus(aRuntime)
43
+ create_minus(aRuntime)
44
+ create_multiply(aRuntime)
45
+ create_divide(aRuntime)
46
+ create_modulo(aRuntime)
27
47
  end
28
48
 
29
49
  def add_comparison(aRuntime)
30
- def_procedure(aRuntime, create_equal)
31
- def_procedure(aRuntime, create_lt)
32
- def_procedure(aRuntime, create_gt)
33
- def_procedure(aRuntime, create_lte)
34
- def_procedure(aRuntime, create_gte)
50
+ create_equal(aRuntime)
51
+ create_lt(aRuntime)
52
+ create_gt(aRuntime)
53
+ create_lte(aRuntime)
54
+ create_gte(aRuntime)
35
55
  end
36
56
 
37
57
  def add_number_predicates(aRuntime)
38
- def_procedure(aRuntime, create_number?)
39
- def_procedure(aRuntime, create_real?)
40
- def_procedure(aRuntime, create_integer?)
58
+ create_number?(aRuntime)
59
+ create_real?(aRuntime)
60
+ create_integer?(aRuntime)
41
61
  end
42
62
 
43
63
  def add_boolean_procedures(aRuntime)
44
- def_procedure(aRuntime, create_not)
45
- def_procedure(aRuntime, create_boolean?)
64
+ create_not(aRuntime)
65
+ create_boolean?(aRuntime)
46
66
  end
47
67
 
48
68
  def add_string_procedures(aRuntime)
49
- def_procedure(aRuntime, create_string?)
69
+ create_string?(aRuntime)
50
70
  end
51
71
 
52
72
  def add_symbol_procedures(aRuntime)
53
- def_procedure(aRuntime, create_symbol?)
73
+ create_symbol?(aRuntime)
54
74
  end
55
75
 
56
- def add_output_procedures(aRuntime)
57
- def_procedure(aRuntime, create_newline)
76
+ def add_io_procedures(aRuntime)(aRuntime)
77
+ create_newline(aRuntime)
58
78
  end
59
79
 
60
80
  def add_special_procedures(aRuntime)
61
- def_procedure(aRuntime, create_debug)
81
+ create_debug(aRuntime)
62
82
  end
63
83
 
64
- def create_plus()
65
- plus_code = ->(runtime, arglist) do
66
- first_one = arglist.head.evaluate(runtime)
67
- operands = arglist.tail.to_eval_enum(runtime)
68
- raw_result = first_one.value
69
- operands.each { |elem| raw_result += elem.value }
70
- to_skm(raw_result)
84
+ def create_plus(aRuntime)
85
+ # arglist should be a Ruby Array
86
+ primitive = ->(runtime, arglist) do
87
+ if arglist.empty?
88
+ to_skm(0)
89
+ else
90
+ first_one = arglist.first.evaluate(runtime)
91
+ raw_result = first_one.value
92
+ operands = evaluate_tail(arglist, runtime)
93
+ operands.each { |elem| raw_result += elem.value }
94
+ to_skm(raw_result)
95
+ end
71
96
  end
72
-
73
- ['+', plus_code]
97
+ define_primitive_proc(aRuntime, '+', zero_or_more, primitive)
74
98
  end
75
99
 
76
- def create_minus()
77
- minus_code = ->(runtime, arglist) do
78
- first_one = arglist.head.evaluate(runtime)
100
+ def create_minus(aRuntime)
101
+ primitive = ->(runtime, first_operand, arglist) do
102
+ first_one = first_operand.evaluate(runtime)
79
103
  raw_result = first_one.value
80
- if arglist.length > 1
81
- operands = arglist.tail.to_eval_enum(runtime)
82
- operands.each { |elem| raw_result -= elem.value }
83
- else
104
+ if arglist.empty?
84
105
  raw_result = -raw_result
106
+ else
107
+ operands = evaluate_array(arglist, runtime)
108
+ operands.each { |elem| raw_result -= elem.value }
85
109
  end
86
110
  to_skm(raw_result)
87
111
  end
88
112
 
89
- ['-', minus_code]
113
+ define_primitive_proc(aRuntime, '-', one_or_more, primitive)
90
114
  end
91
115
 
92
- def create_multiply()
93
- multiply_code = ->(runtime, arglist) do
94
- first_one = arglist.head.evaluate(runtime)
95
- operands = arglist.tail.to_eval_enum(runtime)
96
- raw_result = first_one.value
97
- operands.each { |elem| raw_result *= elem.value }
98
- to_skm(raw_result)
116
+ def create_multiply(aRuntime)
117
+ primitive = ->(runtime, arglist) do
118
+ if arglist.empty?
119
+ to_skm(1)
120
+ else
121
+ first_one = arglist.first.evaluate(runtime)
122
+ raw_result = first_one.value
123
+ operands = evaluate_tail(arglist, runtime)
124
+ operands.each { |elem| raw_result *= elem.value }
125
+ to_skm(raw_result)
126
+ end
99
127
  end
100
-
101
- ['*', multiply_code]
128
+ define_primitive_proc(aRuntime, '*', zero_or_more, primitive)
102
129
  end
103
130
 
104
- def create_divide()
105
- divide_code = ->(runtime, arglist) do
106
- first_one = arglist.head.evaluate(runtime)
107
- operands = arglist.tail.to_eval_enum(runtime)
131
+
132
+ def create_divide(aRuntime)
133
+ primitive = ->(runtime, first_operand, arglist) do
134
+ first_one = first_operand.evaluate(runtime)
108
135
  raw_result = first_one.value
109
- operands.each { |elem| raw_result /= elem.value }
136
+ if arglist.empty?
137
+ raw_result = 1 / raw_result.to_f
138
+ else
139
+ operands = evaluate_array(arglist, runtime)
140
+ operands.each do |elem|
141
+ if raw_result > elem.value && raw_result.modulo(elem.value).zero?
142
+ raw_result /= elem.value
143
+ else
144
+ raw_result = raw_result.to_f
145
+ raw_result /= elem.value
146
+ end
147
+ end
148
+ end
110
149
  to_skm(raw_result)
111
150
  end
112
-
113
- ['/', divide_code]
151
+
152
+ define_primitive_proc(aRuntime, '/', one_or_more, primitive)
114
153
  end
115
154
 
116
-
117
- def create_modulo()
118
- modulo_code = ->(runtime, arglist) do
119
- operand_1 = arglist.head.evaluate(runtime)
120
- operands = arglist.tail.to_eval_enum(runtime)
121
- operand_2 = operands.first.evaluate(runtime)
155
+ def create_modulo(aRuntime)
156
+ primitive = ->(runtime, argument1, argument2) do
157
+ operand_1 = argument1.evaluate(runtime)
158
+ operand_2 = argument2.evaluate(runtime)
122
159
  raw_result = operand_1.value.modulo(operand_2.value)
123
160
  to_skm(raw_result)
124
161
  end
125
162
 
126
- ['floor-remainder', modulo_code, 'modulo', modulo_code]
163
+ define_primitive_proc(aRuntime, 'floor-remainder', binary, primitive)
127
164
  end
128
165
 
129
-
130
- def create_equal
131
- equal_code = ->(runtime, arglist) do
132
- first_one = arglist.head.evaluate(runtime)
133
- operands = arglist.tail.to_eval_enum(runtime)
134
- first_value = first_one.value
135
- all_equal = operands.all? { |elem| first_value == elem.value }
136
- to_skm(all_equal)
166
+ def create_equal(aRuntime)
167
+ primitive = ->(runtime, first_operand, arglist) do
168
+ first_one = first_operand.evaluate(runtime)
169
+ if arglist.empty?
170
+ to_skm(true)
171
+ else
172
+ operands = evaluate_array(arglist, runtime)
173
+ first_value = first_one.value
174
+ all_equal = operands.all? { |elem| first_value == elem.value }
175
+ to_skm(all_equal)
176
+ end
137
177
  end
138
178
 
139
- ['=', equal_code]
179
+ define_primitive_proc(aRuntime, '=', one_or_more, primitive)
140
180
  end
141
181
 
142
- def create_lt
143
- lt_code = ->(runtime, arglist) do
144
- operands = arglist.to_eval_enum(runtime)
145
- result = true
146
- operands.each_cons(2) do |(elem1, elem2)|
147
- result &&= elem1.value < elem2.value
182
+ def create_lt(aRuntime)
183
+ primitive = ->(runtime, first_operand, arglist) do
184
+ if arglist.empty?
185
+ to_skm(false)
186
+ else
187
+ operands = [first_operand.evaluate(runtime)]
188
+ operands.concat(evaluate_array(arglist, runtime))
189
+ result = true
190
+ operands.each_cons(2) do |(elem1, elem2)|
191
+ result &&= elem1.value < elem2.value
192
+ end
193
+ to_skm(result)
148
194
  end
149
- to_skm(result)
150
195
  end
151
196
 
152
- ['<', lt_code]
197
+ define_primitive_proc(aRuntime, '<', one_or_more, primitive)
153
198
  end
154
199
 
155
- def create_gt
156
- gt_code = ->(runtime, arglist) do
157
- operands = arglist.to_eval_enum(runtime)
158
- result = true
159
- operands.each_cons(2) do |(elem1, elem2)|
160
- result &&= elem1.value > elem2.value
200
+ def create_gt(aRuntime)
201
+ primitive = ->(runtime, first_operand, arglist) do
202
+ if arglist.empty?
203
+ to_skm(false)
204
+ else
205
+ operands = [first_operand.evaluate(runtime)]
206
+ operands.concat(evaluate_array(arglist, runtime))
207
+ result = true
208
+ operands.each_cons(2) do |(elem1, elem2)|
209
+ result &&= elem1.value > elem2.value
210
+ end
211
+ to_skm(result)
161
212
  end
162
- to_skm(result)
163
213
  end
164
214
 
165
- ['>', gt_code]
215
+ define_primitive_proc(aRuntime, '>', one_or_more, primitive)
166
216
  end
167
217
 
168
- def create_lte
169
- lte_code = ->(runtime, arglist) do
170
- operands = arglist.to_eval_enum(runtime)
171
- result = true
172
- operands.each_cons(2) do |(elem1, elem2)|
173
- result &&= elem1.value <= elem2.value
218
+ def create_lte(aRuntime)
219
+ primitive = ->(runtime, first_operand, arglist) do
220
+ if arglist.empty?
221
+ to_skm(true)
222
+ else
223
+ operands = [first_operand.evaluate(runtime)]
224
+ operands.concat(evaluate_array(arglist, runtime))
225
+ result = true
226
+ operands.each_cons(2) do |(elem1, elem2)|
227
+ result &&= elem1.value <= elem2.value
228
+ end
229
+ to_skm(result)
174
230
  end
175
- to_skm(result)
176
231
  end
177
232
 
178
- ['<=', lte_code]
233
+ define_primitive_proc(aRuntime, '<=', one_or_more, primitive)
179
234
  end
180
235
 
181
- def create_gte
182
- gte_code = ->(runtime, arglist) do
183
- operands = arglist.to_eval_enum(runtime)
184
- result = true
185
- operands.each_cons(2) do |(elem1, elem2)|
186
- result &&= elem1.value >= elem2.value
236
+ def create_gte(aRuntime)
237
+ primitive = ->(runtime, first_operand, arglist) do
238
+ if arglist.empty?
239
+ to_skm(true)
240
+ else
241
+ operands = [first_operand.evaluate(runtime)]
242
+ operands.concat(evaluate_array(arglist, runtime))
243
+ result = true
244
+ operands.each_cons(2) do |(elem1, elem2)|
245
+ result &&= elem1.value >= elem2.value
246
+ end
247
+ to_skm(result)
187
248
  end
188
- to_skm(result)
189
249
  end
190
250
 
191
- ['>=', gte_code]
251
+ define_primitive_proc(aRuntime, '>=', one_or_more, primitive)
192
252
  end
193
253
 
194
- def create_number?()
195
- pred_code = ->(runtime, arg) do
196
- arg_evaluated = arg.evaluate(runtime)
254
+ def create_number?(aRuntime)
255
+ primitive = ->(runtime, object) do
256
+ arg_evaluated = object.evaluate(runtime)
197
257
  to_skm(arg_evaluated.number?)
198
258
  end
199
259
 
200
- ['number?', pred_code]
260
+ define_primitive_proc(aRuntime, 'number?', unary, primitive)
201
261
  end
202
262
 
203
- def create_real?()
204
- pred_code = ->(runtime, arg) do
263
+ def create_real?(aRuntime)
264
+ primitive = ->(runtime, arg) do
205
265
  arg_evaluated = arg.evaluate(runtime)
206
266
  to_skm(arg_evaluated.real?)
207
267
  end
208
268
 
209
- ['real?', pred_code]
269
+ define_primitive_proc(aRuntime, 'real?', unary, primitive)
210
270
  end
211
271
 
212
- def create_integer?()
213
- pred_code = ->(runtime, arg) do
272
+ def create_integer?(aRuntime)
273
+ primitive = ->(runtime, arg) do
214
274
  arg_evaluated = arg.evaluate(runtime)
215
275
  to_skm(arg_evaluated.integer?)
216
276
  end
217
277
 
218
- ['integer?', pred_code]
278
+ define_primitive_proc(aRuntime, 'integer?', unary, primitive)
219
279
  end
220
280
 
221
- def create_not()
222
- logical = ->(runtime, arg) do
281
+ def create_not(aRuntime)
282
+ primitive = ->(runtime, arg) do
223
283
  arg_evaluated = arg.evaluate(runtime)
224
284
  if arg_evaluated.boolean? && arg_evaluated.value == false
225
285
  to_skm(true)
@@ -227,51 +287,52 @@ module Skeem
227
287
  to_skm(false)
228
288
  end
229
289
  end
230
- ['not', logical]
290
+
291
+ define_primitive_proc(aRuntime, 'not', unary, primitive)
231
292
  end
232
293
 
233
- def create_boolean?()
234
- pred_code = ->(runtime, arg) do
294
+ def create_boolean?(aRuntime)
295
+ primitive = ->(runtime, arg) do
235
296
  arg_evaluated = arg.evaluate(runtime)
236
297
  to_skm(arg_evaluated.boolean?)
237
298
  end
238
299
 
239
- ['boolean?', pred_code]
300
+ define_primitive_proc(aRuntime, 'boolean?', unary, primitive)
240
301
  end
241
302
 
242
- def create_string?()
243
- pred_code = ->(runtime, arg) do
303
+ def create_string?(aRuntime)
304
+ primitive = ->(runtime, arg) do
244
305
  arg_evaluated = arg.evaluate(runtime)
245
306
  to_skm(arg_evaluated.string?)
246
307
  end
247
308
 
248
- ['string?', pred_code]
309
+ define_primitive_proc(aRuntime, 'string?', unary, primitive)
249
310
  end
250
311
 
251
- def create_symbol?
252
- pred_code = ->(runtime, arg) do
312
+ def create_symbol?(aRuntime)
313
+ primitive = ->(runtime, arg) do
253
314
  arg_evaluated = arg.evaluate(runtime)
254
315
  to_skm(arg_evaluated.symbol?)
255
316
  end
256
317
 
257
- ['symbol?', pred_code]
318
+ define_primitive_proc(aRuntime, 'symbol?', unary, primitive)
258
319
  end
259
320
 
260
- def create_newline
261
- nl_code = ->(runtime, arg) do
321
+ def create_newline(aRuntime)
322
+ primitive = ->(runtime) do
262
323
  # @TODO: make output stream configurable
263
324
  print "\n"
264
325
  end
265
326
 
266
- ['newline', nl_code]
327
+ define_primitive_proc(aRuntime, 'newline', nullary, primitive)
267
328
  end
268
329
 
269
- def create_debug
270
- debug_code = ->(runtime, arg) do
330
+ def create_debug(aRuntime)
331
+ primitive = ->(runtime) do
271
332
  require 'debug'
272
333
  end
273
334
 
274
- ['debug', debug_code]
335
+ define_primitive_proc(aRuntime, 'debug', nullary, primitive)
275
336
  end
276
337
 
277
338
 
@@ -281,10 +342,23 @@ module Skeem
281
342
  define(aRuntime, func.identifier, func)
282
343
  end
283
344
  end
345
+
346
+ def define_primitive_proc(aRuntime, anIdentifier, anArity, aRubyLambda)
347
+ primitive = PrimitiveProcedure.new(anIdentifier, anArity, aRubyLambda)
348
+ define(aRuntime, primitive.identifier, primitive)
349
+ end
284
350
 
285
351
  def define(aRuntime, aKey, anEntry)
286
352
  aRuntime.define(aKey, anEntry)
287
353
  end
354
+
355
+ def evaluate_array(anArray, aRuntime)
356
+ anArray.map { |elem| elem.evaluate(aRuntime) }
357
+ end
358
+
359
+ def evaluate_tail(anArray, aRuntime)
360
+ evaluate_array(anArray.drop(1), aRuntime)
361
+ end
288
362
  end # module
289
363
  end # module
290
364
  end # module