skeem 0.0.17 → 0.0.18

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