packcr 0.0.8 → 0.1.1

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/packcr/context.rb +14 -5
  4. data/lib/packcr/generated/context.rb +184 -85
  5. data/lib/packcr/generated/node/action_node.rb +16 -1
  6. data/lib/packcr/generated/node/alternate_node.rb +47 -7
  7. data/lib/packcr/generated/node/capture_node.rb +16 -1
  8. data/lib/packcr/generated/node/charclass_node.rb +73 -6
  9. data/lib/packcr/generated/node/eof_node.rb +8 -1
  10. data/lib/packcr/generated/node/error_node.rb +8 -1
  11. data/lib/packcr/generated/node/expand_node.rb +8 -1
  12. data/lib/packcr/generated/node/predicate_node.rb +37 -2
  13. data/lib/packcr/generated/node/quantity_node.rb +56 -2
  14. data/lib/packcr/generated/node/reference_node.rb +11 -2
  15. data/lib/packcr/generated/node/rule_node.rb +32 -1
  16. data/lib/packcr/generated/node/sequence_node.rb +19 -1
  17. data/lib/packcr/generated/node/string_node.rb +50 -2
  18. data/lib/packcr/generator.rb +4 -3
  19. data/lib/packcr/node/action_node.rb +2 -2
  20. data/lib/packcr/node/alternate_node.rb +2 -2
  21. data/lib/packcr/node/capture_node.rb +2 -2
  22. data/lib/packcr/node/charclass_node.rb +15 -15
  23. data/lib/packcr/node/eof_node.rb +2 -2
  24. data/lib/packcr/node/error_node.rb +2 -2
  25. data/lib/packcr/node/expand_node.rb +2 -2
  26. data/lib/packcr/node/predicate_node.rb +3 -3
  27. data/lib/packcr/node/quantity_node.rb +4 -4
  28. data/lib/packcr/node/reference_node.rb +5 -4
  29. data/lib/packcr/node/rule_node.rb +4 -3
  30. data/lib/packcr/node/sequence_node.rb +2 -2
  31. data/lib/packcr/node/string_node.rb +17 -3
  32. data/lib/packcr/parser.rb +403 -254
  33. data/lib/packcr/stream.rb +1 -1
  34. data/lib/packcr/templates/context/source.c.erb +30 -26
  35. data/lib/packcr/templates/context/source.rb.erb +160 -159
  36. data/lib/packcr/templates/context/source.rs.erb +627 -0
  37. data/lib/packcr/templates/node/action.rs.erb +6 -0
  38. data/lib/packcr/templates/node/alternate.rs.erb +38 -0
  39. data/lib/packcr/templates/node/capture.rs.erb +13 -0
  40. data/lib/packcr/templates/node/charclass_utf8.rs.erb +41 -0
  41. data/lib/packcr/templates/node/predicate_neg.rs.erb +24 -0
  42. data/lib/packcr/templates/node/quantity_many.rs.erb +77 -0
  43. data/lib/packcr/templates/node/reference.rs.erb +3 -0
  44. data/lib/packcr/templates/node/rule.rs.erb +43 -0
  45. data/lib/packcr/templates/node/sequence.rs.erb +12 -0
  46. data/lib/packcr/templates/node/string_many.rs.erb +10 -0
  47. data/lib/packcr/templates/node/string_many_reverse.rs.erb +7 -0
  48. data/lib/packcr/templates/node/string_one.rs.erb +9 -0
  49. data/lib/packcr/templates/node/string_one_reverse.rs.erb +6 -0
  50. data/lib/packcr/util.rb +9 -0
  51. data/lib/packcr/version.rb +1 -1
  52. metadata +20 -67
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cacf5e781ba49bcc7d755ac5ba6e348646a08e4a807efd4375ba720232262556
4
- data.tar.gz: ce70800733fbc71527539174f373786489f3fdda416b5435bf8aa687de1cd321
3
+ metadata.gz: ec849cdb4d638eb308cdefaf4a2e743ec49e71b46b7343265f8f8b78f5508c5a
4
+ data.tar.gz: d9c7e0db4c53e247edfb28ca9a6266364e0647152a202c92ca94bdf60ed0e5e0
5
5
  SHA512:
6
- metadata.gz: f0cf28feb81c97f094f6b098656ab4ec30f312e99c84789c5e5f53cf8a520c4c63a561d48a240b7ab0699d2e063820e3b923118d33e2a9e3b5dfc2884993ac9d
7
- data.tar.gz: c858ad58e577af40013bbd7438eb3c4066d0c76b0a6407c5ab051ae63a379936594da974a1c203a72659d3dcebc4794c0aaaf5beea2ab98169388a89c0c79773
6
+ metadata.gz: d7b5ec64eb5ea1a407efb518ee0a169f7d0a24294e9d0924fbd7c9bf7a95fd20de13f0ccda290e8deea34b4745d08e07022576fa120dc58f2d81cb4c0e12ee4e
7
+ data.tar.gz: aa881736505df4e86e9a511b48941781e89a3618445e71a88034097624750e902edaa986ea0d875e537933034c4e0e12b5e8a971206732f2084f0b06d2db2b90
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- PackCR is a parser generator for C or Ruby.
5
+ PackCR is a parser generator for C, Ruby, and Rust.
6
6
  This library is a port of PackCC rewritten in ruby.
7
7
  The Original PackCC is https://github.com/arithy/packcc.
8
8
 
@@ -40,9 +40,9 @@ class Packcr
40
40
  get_header_code: @hname,
41
41
  }
42
42
  @hid = File.basename(@hname).upcase.gsub(/[^A-Z0-9]/, "_")
43
- when :rb
43
+ when :rb, :rs
44
44
  @patterns = {
45
- get_source_code: "#{path}.rb",
45
+ get_source_code: "#{path}.#{@lang}",
46
46
  }
47
47
  else
48
48
  raise "unexpected lang: #{@lang}"
@@ -96,6 +96,17 @@ class Packcr
96
96
  end
97
97
  end
98
98
 
99
+ def line_comment_code(line)
100
+ line = line.chomp
101
+ case @lang
102
+ when :c
103
+ line.gsub("*/", "* /")
104
+ "/* #{line} */"
105
+ when :rb
106
+ "# #{line}"
107
+ end
108
+ end
109
+
99
110
  def class_name
100
111
  prefix.gsub(/(?:_|^|(\W))([a-z])/) { "#{::Regexp.last_match(1)}#{::Regexp.last_match(2)}".upcase }
101
112
  end
@@ -150,9 +161,7 @@ class Packcr
150
161
 
151
162
  next if @errnum.zero?
152
163
 
153
- results.each do |_, r|
154
- r.close
155
- end
164
+ results.each_value(&:close)
156
165
  return false
157
166
  end
158
167
 
@@ -20,6 +20,8 @@ class Packcr
20
20
  erbout << "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct #{prefix}_context_tag #{prefix}_context_t;\n\n#{prefix}_context_t *#{prefix}_create(#{auxil_def}auxil);\nint #{prefix}_parse(#{prefix}_context_t *ctx, #{value_def}*ret);\nvoid #{prefix}_destroy(#{prefix}_context_t *ctx);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* !PACKCR_INCLUDED_#{@hid} */\n".freeze
21
21
 
22
22
  erbout
23
+ else
24
+ raise "unknown lang #{lang}"
23
25
  end
24
26
  end
25
27
 
@@ -29,10 +31,10 @@ class Packcr
29
31
  erbout = +""
30
32
  erbout << "/* A packrat parser generated by PackCR #{Packcr::VERSION} */\n\n".freeze
31
33
 
32
- code(:esource).each do |code|
33
- erbout << "#{stream.get_code_block(code, 0, @iname)}".freeze
34
- end
35
34
  if !code(:esource).empty?
35
+ code(:esource).each do |code|
36
+ erbout << "#{stream.get_code_block(code, 0, @iname)}".freeze
37
+ end
36
38
  erbout << "\n".freeze
37
39
  end
38
40
  erbout << "#ifdef _MSC_VER\n#undef _CRT_SECURE_NO_WARNINGS\n#define _CRT_SECURE_NO_WARNINGS\n#endif /* _MSC_VER */\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifndef _MSC_VER\n#if defined __GNUC__ && defined _WIN32 /* MinGW */\n#ifndef PACKCR_USE_SYSTEM_STRNLEN\n#define strnlen(str, maxlen) packcr_strnlen(str, maxlen)\nstatic size_t packcr_strnlen(const char *str, size_t maxlen) {\n size_t i;\n for (i = 0; i < maxlen && str[i]; i++);\n return i;\n}\n#endif /* !PACKCR_USE_SYSTEM_STRNLEN */\n#endif /* defined __GNUC__ && defined _WIN32 */\n#endif /* !_MSC_VER */\n\n#include \"#{@hname}\"\n".freeze
@@ -191,7 +193,29 @@ class Packcr
191
193
  if @location
192
194
  erbout << " memo->offset_loc = ctx->position_offset_loc;\n".freeze
193
195
  end
194
- erbout << " }\n } else {\n c = packcr_get_rule_thunk_chunk(ctx, rule);\n }\n if (c == NULL) return PACKCR_FALSE;\n if (value == NULL) value = &null;\n memset(value, 0, sizeof(packcr_value_t)); /* in case */\n packcr_thunk_array__add(ctx->auxil, thunks, packcr_thunk__create_node(ctx->auxil, &c->thunks, value));\n return PACKCR_TRUE;\n}\n\nMARK_FUNC_AS_USED\nstatic void packcr_do_action(packcr_context_t *ctx, const packcr_thunk_array_t *thunks, packcr_value_t *value) {\n size_t i;\n for (i = 0; i < thunks->len; i++) {\n packcr_thunk_t *const thunk = thunks->buf[i];\n switch (thunk->type) {\n case PACKCR_THUNK_LEAF:\n thunk->data.leaf.action(ctx, thunk, value);\n break;\n case PACKCR_THUNK_NODE:\n packcr_do_action(ctx, thunk->data.node.thunks, thunk->data.node.value);\n break;\n default: /* unknown */\n break;\n }\n }\n}\n\n".freeze
196
+ erbout << " }\n } else {\n c = packcr_get_rule_thunk_chunk(ctx, rule);\n }\n if (c == NULL) return PACKCR_FALSE;\n if (value == NULL) value = &null;\n memset(value, 0, sizeof(packcr_value_t)); /* in case */\n packcr_thunk_array__add(ctx->auxil, thunks, packcr_thunk__create_node(ctx->auxil, &c->thunks, value));\n return PACKCR_TRUE;\n}\n\nMARK_FUNC_AS_USED\nstatic void packcr_do_action(packcr_context_t *ctx, const packcr_thunk_array_t *thunks, packcr_value_t *value) {\n size_t i;\n for (i = 0; i < thunks->len; i++) {\n packcr_thunk_t *const thunk = thunks->buf[i];\n switch (thunk->type) {\n case PACKCR_THUNK_LEAF:\n thunk->data.leaf.action(ctx, thunk, value);\n break;\n case PACKCR_THUNK_NODE:\n packcr_do_action(ctx, thunk->data.node.thunks, thunk->data.node.value);\n break;\n default: /* unknown */\n break;\n }\n }\n}\n\n#{prefix}_context_t *#{prefix}_create(#{auxil_def}auxil) {\n return packcr_context__create(auxil);\n}\n\nvoid #{prefix}_destroy(#{prefix}_context_t *ctx) {\n packcr_context__destroy(ctx);\n}\n".freeze
197
+
198
+ if !@root.rules.empty?
199
+ erbout << "\n".freeze
200
+
201
+ @root.rules.each do |rule|
202
+ erbout << "static packcr_thunk_chunk_t *packcr_evaluate_rule_#{rule.name}(packcr_context_t *ctx, size_t offset".freeze
203
+ if @location
204
+ erbout << ", packcr_location_t offset_loc".freeze
205
+ end
206
+ erbout << ", packcr_rule_set_t *limits);\n".freeze
207
+ end
208
+ end
209
+ erbout << "\nint #{prefix}_parse(#{prefix}_context_t *ctx, #{value_def}*ret) {\n size_t pos = ctx->buffer_start_position;\n".freeze
210
+
211
+ if !@root.rules.empty?
212
+ erbout << " if (packcr_apply_rule(ctx, packcr_evaluate_rule_#{@root.rules[0].name}, &ctx->thunks, ret, ctx->position_offset".freeze
213
+ if @location
214
+ erbout << ", ctx->position_offset_loc".freeze
215
+ end
216
+ erbout << ", NULL))\n packcr_do_action(ctx, &ctx->thunks, ret);\n else\n PACKCR_ERROR(ctx->auxil);\n packcr_commit_buffer(ctx);\n".freeze
217
+ end
218
+ erbout << " packcr_thunk_array__revert(ctx->auxil, &ctx->thunks, 0);\n return pos != ctx->buffer_start_position && packcr_refill_buffer(ctx, 1) >= 1;\n}\n\n".freeze
195
219
 
196
220
  @root.rules.each do |rule|
197
221
  rule.codes.each do |code|
@@ -233,30 +257,12 @@ class Packcr
233
257
  erbout << "#undef __\n#undef auxil\n}\n\n".freeze
234
258
  end
235
259
  end
236
- @root.rules.each do |rule|
237
- erbout << "static packcr_thunk_chunk_t *packcr_evaluate_rule_#{rule.name}(packcr_context_t *ctx, size_t offset".freeze
238
- if @location
239
- erbout << ", packcr_location_t offset_loc".freeze
240
- end
241
- erbout << ", packcr_rule_set_t *limits);\n".freeze
242
- end
243
260
  erbout << "\n".freeze
244
261
 
245
262
  @root.rules.each do |rule|
246
263
  gen = ::Packcr::Generator.new(rule, @ascii, @location)
247
264
  erbout << "#{gen.generate_code(rule, 0, 0, false)}\n".freeze
248
265
  end
249
- erbout << "#{prefix}_context_t *#{prefix}_create(#{auxil_def}auxil) {\n return packcr_context__create(auxil);\n}\n\nint #{prefix}_parse(#{prefix}_context_t *ctx, #{value_def}*ret) {\n size_t pos = ctx->buffer_start_position;\n".freeze
250
-
251
- if !@root.rules.empty?
252
- erbout << " if (packcr_apply_rule(ctx, packcr_evaluate_rule_#{@root.rules[0].name}, &ctx->thunks, ret, ctx->position_offset".freeze
253
- if @location
254
- erbout << ", ctx->position_offset_loc".freeze
255
- end
256
- erbout << ", NULL))\n packcr_do_action(ctx, &ctx->thunks, ret);\n else\n PACKCR_ERROR(ctx->auxil);\n packcr_commit_buffer(ctx);\n".freeze
257
- end
258
- erbout << " packcr_thunk_array__revert(ctx->auxil, &ctx->thunks, 0);\n return pos != ctx->buffer_start_position && packcr_refill_buffer(ctx, 1) >= 1;\n}\n\nvoid #{prefix}_destroy(#{prefix}_context_t *ctx) {\n packcr_context__destroy(ctx);\n}\n".freeze
259
-
260
266
  if !code(:lsource).empty?
261
267
  erbout << "\n".freeze
262
268
 
@@ -267,14 +273,13 @@ class Packcr
267
273
  erbout
268
274
  when :rb
269
275
  erbout = +""
270
- erbout << "# A packrat parser generated by PackCR #{Packcr::VERSION}\n".freeze
276
+ erbout << "# A packrat parser generated by PackCR #{Packcr::VERSION}\n\n".freeze
271
277
 
272
278
  if !code(:esource).empty?
273
- erbout << "\n".freeze
274
-
275
279
  code(:esource).each do |code|
276
280
  erbout << "#{stream.get_code_block(code, 0, @iname)}".freeze
277
281
  end
282
+ erbout << "\n".freeze
278
283
  end
279
284
  erbout << "class #{class_name}\n".freeze
280
285
 
@@ -284,7 +289,40 @@ class Packcr
284
289
  code(:source).each do |code|
285
290
  erbout << " #{stream.get_code_block(code, 2, @iname)}\n".freeze
286
291
  end
287
- erbout << " def initialize(".freeze
292
+ erbout << " class LrMemoTable\n def initialize\n @memos = {}\n end\n\n def clear\n @memos.clear\n end\n\n def []=(index, rule_name, memo)\n entry = @memos[index] ||= {}\n entry[rule_name] = memo\n end\n\n def [](index, rule_name)\n @memos.dig(index, rule_name)\n end\n end\n\n class LrMemo\n attr_accessor :grow, :answer, :offset, :fail\n".freeze
293
+
294
+ if @location
295
+ erbout << " attr_accessor :offset_loc\n".freeze
296
+ end
297
+ erbout << "\n def initialize(offset".freeze
298
+ if @location
299
+ erbout << ", offset_loc".freeze
300
+ end
301
+ erbout << ")\n @offset = offset\n".freeze
302
+
303
+ if @location
304
+ erbout << " @offset_loc = offset_loc\n".freeze
305
+ end
306
+ erbout << " @fail = true\n @grow = false\n end\n\n def answer=(answer)\n @fail = nil\n @answer = answer\n end\n end\n\n class ThunkChunk\n attr_accessor :thunks, :capts, :pos, :values\n".freeze
307
+
308
+ if @location
309
+ erbout << " attr_accessor :pos_loc\n".freeze
310
+ end
311
+ erbout << "\n def initialize\n super\n @thunks = []\n @capts = {}\n @pos = 0\n @values = {}\n end\n\n def resize_captures(len)\n len.times do |i|\n @capts[i] = Capture.new\n end\n end\n end\n\n class ThunkLeaf\n attr_accessor :capt0, :capts, :value_refs, :action\n\n def initialize(action, capt0 = Capture.new, value_refs = {}, capts = {})\n @value_refs = value_refs\n @capts = capts\n @capt0 = capt0\n @action = action\n end\n\n def do_action(ctx, values, index)\n ctx.public_send(action, self, values, index)\n end\n end\n\n class ThunkNode\n attr_accessor :thunks, :values, :index\n\n def initialize(thunks, values, index)\n @thunks = thunks\n @values = values\n @index = index\n values[index] ||= Value.new if values\n end\n\n def do_action(ctx, _values, _index)\n @thunks.each do |thunk|\n thunk.do_action(ctx, @values, @index)\n end\n end\n\n def clear\n @thunks.clear\n end\n end\n\n class Capture\n attr_accessor :range_start, :range_end\n".freeze
312
+
313
+ if @location
314
+ erbout << " attr_accessor :start_loc, :end_loc\n".freeze
315
+ end
316
+ erbout << "\n def initialize(range_start = 0, range_end = 0".freeze
317
+ if @location
318
+ erbout << ", start_loc = nil, end_loc = nil".freeze
319
+ end
320
+ erbout << ")\n @range_start = range_start\n @range_end = range_end\n".freeze
321
+
322
+ if @location
323
+ erbout << " @start_loc = start_loc || Location.new\n @end_loc = end_loc || Location.new\n".freeze
324
+ end
325
+ erbout << " end\n\n def capture_string(buffer)\n @capture_string ||= buffer[@range_start, @range_end - @range_start]\n end\n end\n\n class Value\n attr_accessor :value\n end\n\n def initialize(".freeze
288
326
  if @auxil_type
289
327
  erbout << "#{auxil_type}, ".freeze
290
328
  end
@@ -326,43 +364,7 @@ class Packcr
326
364
  end
327
365
  erbout << ")\n @thunk.do_action(self, nil, 0)\n else\n raise SyntaxError, \"can't parse\"\n end\n commit_buffer\n".freeze
328
366
  end
329
- erbout << " @thunk.clear\n refill_buffer(1) >= 1 && pos != @buffer_start_position\n end\n\n def run\n nil while parse\n end\n\n".freeze
330
-
331
- @root.rules.each do |rule|
332
- rule.codes.each do |code|
333
- erbout << " def action_#{rule.name}_#{code.index}(__packcr_in, __packcr_vars, __packcr_index)\n ____ = (__packcr_vars[__packcr_index] ||= Value.new).value if __packcr_vars\n".freeze
334
-
335
- code.vars.each do |ref|
336
- erbout << " #{ref.var} = (__packcr_in.value_refs[#{ref.index}] ||= Value.new).value\n".freeze
337
- end
338
- erbout << " __0 = __packcr_in.capt0.capture_string(@buffer)\n __0s = @buffer_start_position + __packcr_in.capt0.range_start\n __0e = @buffer_start_position + __packcr_in.capt0.range_end\n".freeze
339
-
340
- if @location
341
- erbout << " __0sl = @buffer_start_position_loc + __packcr_in.capt0.start_loc\n __0el = @buffer_start_position_loc + __packcr_in.capt0.end_loc\n".freeze
342
- end
343
- if @capture_in_code
344
- erbout << " __0c = __packcr_in.capt0\n".freeze
345
- end
346
- code.capts.each do |capture|
347
- erbout << " __#{capture.index + 1} = __packcr_in.capts[#{capture.index}].capture_string(@buffer)\n __#{capture.index + 1}s = @buffer_start_position + __packcr_in.capts[#{capture.index}].range_start\n __#{capture.index + 1}e = @buffer_start_position + __packcr_in.capts[#{capture.index}].range_end\n".freeze
348
-
349
- if @location
350
- erbout << " __#{capture.index + 1}sl = @buffer_start_position_loc + __packcr_in.capts[#{capture.index}].start_loc\n __#{capture.index + 1}el = @buffer_start_position_loc + __packcr_in.capts[#{capture.index}].end_loc\n".freeze
351
- end
352
- next unless @capture_in_code
353
-
354
- erbout << " __#{capture.index + 1}c = __packcr_in.capts[#{capture.index}]\n".freeze
355
- end
356
-
357
- erbout << "#{stream.get_code_block(code.code, 4, @iname)}\n __packcr_vars[__packcr_index].value = ____ if __packcr_vars\n end\n\n".freeze
358
- end
359
- end
360
- @root.rules.each do |rule|
361
- gen = ::Packcr::Generator.new(rule, @ascii, @location, :rb)
362
-
363
- erbout << "#{gen.generate_code(rule, 0, 2, false)}\n".freeze
364
- end
365
- erbout << " def grow_lr(rule, offset".freeze
367
+ erbout << " @thunk.clear\n refill_buffer(1) >= 1 && pos != @buffer_start_position\n end\n\n def run\n nil while parse\n end\n\n def grow_lr(rule, offset".freeze
366
368
  if @location
367
369
  erbout << ", offset_loc".freeze
368
370
  end
@@ -434,40 +436,135 @@ class Packcr
434
436
  if @location
435
437
  erbout << " memo.offset_loc = @position_offset_loc\n".freeze
436
438
  end
437
- erbout << " end\n else\n answer = rule_answer(rule)\n end\n\n if !answer\n return false\n end\n values ||= @global_values\n thunks << ThunkNode.new(answer.thunks, values, index)\n return true\n end\n\n def do_action(thunks, values, index)\n thunks.each do |thunk|\n thunk.do_action(self, values, index)\n end\n end\n\n class LrMemoTable\n def initialize\n @memos = {}\n end\n\n def clear\n @memos.clear\n end\n\n def []=(index, rule_name, memo)\n entry = @memos[index] ||= {}\n entry[rule_name] = memo\n end\n\n def [](index, rule_name)\n @memos.dig(index, rule_name)\n end\n end\n\n class LrMemo\n attr_accessor :grow, :answer, :offset, :fail\n".freeze
439
+ erbout << " end\n else\n answer = rule_answer(rule)\n end\n\n if !answer\n return false\n end\n values ||= @global_values\n thunks << ThunkNode.new(answer.thunks, values, index)\n return true\n end\n\n def do_action(thunks, values, index)\n thunks.each do |thunk|\n thunk.do_action(self, values, index)\n end\n end\n".freeze
438
440
 
439
- if @location
440
- erbout << " attr_accessor :offset_loc\n".freeze
441
+ @root.rules.each do |rule|
442
+ rule.codes.each do |code|
443
+ erbout << "\n def action_#{rule.name}_#{code.index}(__packcr_in, __packcr_vars, __packcr_index)\n ____ = (__packcr_vars[__packcr_index] ||= Value.new).value if __packcr_vars\n".freeze
444
+
445
+ code.vars.each do |ref|
446
+ erbout << " #{ref.var} = (__packcr_in.value_refs[#{ref.index}] ||= Value.new).value\n".freeze
447
+ end
448
+ erbout << " __0 = __packcr_in.capt0.capture_string(@buffer)\n __0s = @buffer_start_position + __packcr_in.capt0.range_start\n __0e = @buffer_start_position + __packcr_in.capt0.range_end\n".freeze
449
+
450
+ if @location
451
+ erbout << " __0sl = @buffer_start_position_loc + __packcr_in.capt0.start_loc\n __0el = @buffer_start_position_loc + __packcr_in.capt0.end_loc\n".freeze
452
+ end
453
+ if @capture_in_code
454
+ erbout << " __0c = __packcr_in.capt0\n".freeze
455
+ end
456
+ code.capts.each do |capture|
457
+ erbout << " __#{capture.index + 1} = __packcr_in.capts[#{capture.index}].capture_string(@buffer)\n __#{capture.index + 1}s = @buffer_start_position + __packcr_in.capts[#{capture.index}].range_start\n __#{capture.index + 1}e = @buffer_start_position + __packcr_in.capts[#{capture.index}].range_end\n".freeze
458
+
459
+ if @location
460
+ erbout << " __#{capture.index + 1}sl = @buffer_start_position_loc + __packcr_in.capts[#{capture.index}].start_loc\n __#{capture.index + 1}el = @buffer_start_position_loc + __packcr_in.capts[#{capture.index}].end_loc\n".freeze
461
+ end
462
+ next unless @capture_in_code
463
+
464
+ erbout << " __#{capture.index + 1}c = __packcr_in.capts[#{capture.index}]\n".freeze
465
+ end
466
+
467
+ erbout << "#{stream.get_code_block(code.code, 4, @iname)}\n __packcr_vars[__packcr_index].value = ____ if __packcr_vars\n end\n".freeze
468
+ end
441
469
  end
442
- erbout << "\n def initialize(offset".freeze
443
- if @location
444
- erbout << ", offset_loc".freeze
470
+ @root.rules.each do |rule|
471
+ erbout << "\n".freeze
472
+
473
+ gen = ::Packcr::Generator.new(rule, @ascii, @location, :rb)
474
+
475
+ erbout << "#{gen.generate_code(rule, 0, 2, false)}".freeze
445
476
  end
446
- erbout << ")\n @offset = offset\n".freeze
477
+ erbout << "end\n".freeze
447
478
 
448
- if @location
449
- erbout << " @offset_loc = offset_loc\n".freeze
479
+ if !code(:lsource).empty?
480
+ erbout << "\n".freeze
481
+
482
+ code(:lsource).each do |code|
483
+ erbout << "#{stream.get_code_block(code, 0, @iname)}".freeze
484
+ end
450
485
  end
451
- erbout << " @fail = true\n @grow = false\n end\n\n def answer=(answer)\n @fail = nil\n @answer = answer\n end\n end\n\n class ThunkChunk\n attr_accessor :thunks, :capts, :pos, :values\n".freeze
486
+ erbout
487
+ when :rs
488
+ erbout = +""
489
+ erbout << "/* A packrat parser generated by PackCR #{Packcr::VERSION} */\n\nuse std::cell::RefCell;\nuse std::collections::{HashMap, HashSet};\nuse std::io::Read;\nuse std::rc::Rc;\n".freeze
452
490
 
453
- if @location
454
- erbout << " attr_accessor :pos_loc\n".freeze
491
+ if !code(:esource).empty?
492
+ erbout << "\n".freeze
493
+
494
+ code(:esource).each do |code|
495
+ erbout << "#{stream.get_code_block(code, 0, @iname)}".freeze
496
+ end
455
497
  end
456
- erbout << "\n def initialize\n super\n @thunks = []\n @capts = {}\n @pos = 0\n @values = {}\n end\n\n def resize_captures(len)\n len.times do |i|\n @capts[i] = Capture.new\n end\n end\n end\n\n class ThunkLeaf\n attr_accessor :capt0, :capts, :value_refs, :action\n\n def initialize(action, capt0 = Capture.new, value_refs = {}, capts = {})\n @value_refs = value_refs\n @capts = capts\n @capt0 = capt0\n @action = action\n end\n\n def do_action(ctx, values, index)\n ctx.public_send(action, self, values, index)\n end\n end\n\n class ThunkNode\n attr_accessor :thunks, :values, :index\n\n def initialize(thunks, values, index)\n @thunks = thunks\n @values = values\n @index = index\n values[index] ||= Value.new if values\n end\n\n def do_action(ctx, _values, _index)\n @thunks.each do |thunk|\n thunk.do_action(ctx, @values, @index)\n end\n end\n\n def clear\n @thunks.clear\n end\n end\n\n class Capture\n attr_accessor :range_start, :range_end\n".freeze
498
+ use_value = @root.rules.map { |r| r.vars.size }.max > 0
499
+ erbout << "\nstruct LrMemoTable {\n memos: HashMap<usize, LrMemoMap>,\n}\nimpl LrMemoTable {\n fn new() -> Self {\n Self {\n memos: HashMap::new(),\n }\n }\n\n fn clear(&mut self) {\n self.memos.clear();\n }\n\n fn set(&mut self, index: usize, rule: Rule, memo: LrMemo) {\n let memo_map = self.memos.entry(index).or_default();\n memo_map.insert(rule, memo);\n }\n\n fn get(&mut self, index: usize, rule: Rule) -> &mut LrMemo {\n self.memos.get_mut(&index).unwrap().get_mut(&rule).unwrap()\n }\n fn has(&self, index: usize, rule: Rule) -> bool {\n self.memos\n .get(&index)\n .is_some_and(|memo_map| memo_map.contains_key(&rule))\n }\n}\n\nstruct LrMemo {\n offset: usize,\n answer: Option<Rc<RefCell<ThunkChunk>>>,\n grow: bool,\n}\n\nimpl LrMemo {\n fn new(offset: usize) -> Self {\n Self {\n offset,\n answer: None,\n grow: false,\n }\n }\n\n fn start_grow(&mut self) -> bool {\n if !self.grow && self.answer.is_none() {\n self.grow = true;\n true\n } else {\n false\n }\n }\n\n fn update(&mut self, answer: Option<ThunkChunk>, offset: usize) {\n self.answer = answer.map(|c| Rc::new(RefCell::new(c)));\n self.offset = offset;\n }\n\n fn clone_answer(&mut self) -> Option<Rc<RefCell<ThunkChunk>>> {\n self.answer.as_ref().map(Rc::clone)\n }\n}\n\nstruct ThunkChunk {\n thunks: Rc<RefCell<Vec<Thunk>>>,\n capts: CaptureTable,\n pos: usize,\n values: ValueTable,\n}\n\nimpl ThunkChunk {\n fn new(pos: usize) -> Self {\n Self {\n values: ValueTable::new(),\n capts: CaptureTable::new(),\n thunks: Rc::new(RefCell::new(Vec::new())),\n pos,\n }\n }\n\n fn push_leaf(\n &self,\n action: Action,\n end: usize,\n value_indices: &[usize],\n capt_indices: &[usize],\n ) {\n {\n let start = self.pos;\n let mut value_refs = HashMap::new();\n for &index in value_indices {\n value_refs.insert(index, ValueRef::new(index, self.values.clone()));\n }\n let mut capts = HashMap::new();\n for &index in capt_indices {\n capts.insert(index, self.capts[index].clone());\n }\n let leaf = Thunk::Leaf(ThunkLeaf::new(action, start, end, value_refs, capts));\n self.thunks.borrow_mut().push(leaf);\n }\n }\n\n fn value_ref(&self, index: usize) -> ValueRef {\n ValueRef::new(index, self.values.clone())\n }\n}\n\nenum Thunk {\n Leaf(ThunkLeaf),\n Node(ThunkNode),\n}\nimpl Thunk {\n fn do_action(&self, processor: &ThunkProcessor, value: ValueRef) -> Value {\n match self {\n Thunk::Leaf(leaf) => leaf.do_action(processor, value),\n Thunk::Node(node) => node.do_action(processor),\n }\n }\n}\n\n#[allow(dead_code)]\nstruct ThunkLeaf {\n value_refs: HashMap<usize, ValueRef>,\n capts: HashMap<usize, Capture>,\n capt0: Capture,\n action: Action,\n}\nimpl ThunkLeaf {\n fn new(\n action: Action,\n start: usize,\n end: usize,\n value_refs: HashMap<usize, ValueRef>,\n capts: HashMap<usize, Capture>,\n ) -> Self {\n Self {\n value_refs,\n capts,\n capt0: Capture { start, end },\n action,\n }\n }\n\n fn do_action(&self, processor: &ThunkProcessor, mut value: ValueRef) -> Value {\n value.with_mut(|v| {\n processor.call_action(self.action, self, v);\n });\n value.get()\n }\n".freeze
457
500
 
458
- if @location
459
- erbout << " attr_accessor :start_loc, :end_loc\n".freeze
501
+ if use_value
502
+ erbout << "\n fn values(&self) -> HashMap<usize, Value> {\n self.value_refs.iter().map(|(k, v)| (*k, v.get())).collect()\n }\n".freeze
460
503
  end
461
- erbout << "\n def initialize(range_start = 0, range_end = 0".freeze
462
- if @location
463
- erbout << ", start_loc = nil, end_loc = nil".freeze
504
+ erbout << "}\n\nstruct ThunkNode {\n thunks: Rc<RefCell<Vec<Thunk>>>,\n value: ValueRef,\n}\nimpl ThunkNode {\n fn do_action(&self, processor: &ThunkProcessor) -> Value {\n let mut v = 0;\n for thunk in self.thunks.borrow_mut().iter_mut() {\n v = thunk.do_action(processor, self.value.clone());\n }\n v\n }\n}\n\n#[derive(Clone)]\nstruct Capture {\n start: usize,\n end: usize,\n}\n\ntype Value = i32;\n\nstruct ValueTable {\n table: Rc<RefCell<HashMap<usize, Value>>>,\n}\nimpl ValueTable {\n fn new() -> Self {\n Self {\n table: Rc::new(RefCell::new(HashMap::new())),\n }\n }\n".freeze
505
+
506
+ if use_value
507
+ erbout << "\n fn clear(&mut self) {\n self.table.borrow_mut().clear();\n }\n".freeze
464
508
  end
465
- erbout << ")\n @range_start = range_start\n @range_end = range_end\n".freeze
509
+ erbout << "\n fn with_mut<F>(&mut self, index: usize, f: F)\n where\n F: FnOnce(&mut Value),\n {\n let mut table = self.table.borrow_mut();\n let value = table.entry(index).or_insert(0);\n f(value);\n }\n}\nimpl Clone for ValueTable {\n fn clone(&self) -> Self {\n Self {\n table: self.table.clone(),\n }\n }\n}\n\nstruct ValueRef {\n values: ValueTable,\n index: usize,\n}\nimpl ValueRef {\n fn new(index: usize, values: ValueTable) -> Self {\n Self { index, values }\n }\n\n fn with_mut<F>(&mut self, f: F)\n where\n F: FnOnce(&mut i32),\n {\n self.values.with_mut(self.index, f);\n }\n\n fn get(&self) -> i32 {\n *self.values.table.borrow().get(&self.index).unwrap_or(&0)\n }\n}\nimpl Clone for ValueRef {\n fn clone(&self) -> Self {\n Self {\n index: self.index,\n values: self.values.clone(),\n }\n }\n}\n\nstruct CaptureTable {\n table: HashMap<usize, Capture>,\n len: usize,\n}\n\ntype RuleSet = HashSet<Rule>;\ntype RuleLimit = Option<RuleSet>;\n\ntype LrMemoMap = HashMap<Rule, LrMemo>;\n\nimpl CaptureTable {\n fn new() -> Self {\n Self {\n table: HashMap::new(),\n len: 0,\n }\n }\n\n fn resize(&mut self, len: usize) {\n let current_len = self.len;\n if len > current_len {\n for i in current_len..len {\n self.table.insert(i, Capture { start: 0, end: 0 });\n }\n } else if len < current_len {\n for i in len..current_len {\n self.table.remove(&i);\n }\n }\n self.len = len;\n }\n}\nimpl std::ops::Index<usize> for CaptureTable {\n type Output = Capture;\n\n fn index(&self, index: usize) -> &Self::Output {\n &self.table[&index]\n }\n}\nimpl std::ops::IndexMut<usize> for CaptureTable {\n fn index_mut(&mut self, index: usize) -> &mut Self::Output {\n if self.len <= index {\n self.resize(index + 1);\n }\n self.table.get_mut(&index).unwrap()\n }\n}\n\nstruct Input {\n input: Box<dyn std::io::Read>,\n start_position: usize,\n position_offset: usize,\n buffer: String,\n closed: bool,\n}\n\nimpl Input {\n fn new(input: impl std::io::Read + 'static) -> Self {\n Self {\n input: Box::new(input),\n start_position: 0,\n position_offset: 0,\n buffer: String::new(),\n closed: false,\n }\n }\n\n fn refill_buffer(&mut self, num: usize) -> usize {\n let mut len = self.buffer.len();\n if len >= self.position_offset + num {\n return len - self.position_offset;\n }\n\n let mut input_buffer = [0u8; 1024];\n\n while len < self.position_offset + num {\n match self.input.read(&mut input_buffer) {\n Ok(0) => break,\n Ok(bytes_read) => {\n let s = std::string::String::from_utf8_lossy(&input_buffer[..bytes_read]);\n self.buffer.push_str(&s);\n }\n Err(_) => break,\n }\n len = self.buffer.len();\n }\n\n len - self.position_offset\n }\n\n fn commit_buffer(&mut self) {\n let position_offset = self.position_offset;\n\n self.buffer.drain(..position_offset);\n\n self.start_position += position_offset;\n self.position_offset = 0;\n }\n\n #[allow(dead_code)]\n fn get_char_as_utf32(&mut self) -> (i32, usize) {\n if self.refill_buffer(1) < 1 {\n return (0, 0);\n }\n\n let current_position = self.position_offset;\n let remaining_chars: &str = &self.buffer[current_position..];\n\n if let Some(ch) = remaining_chars.chars().next() {\n let bytes_used = ch.len_utf8();\n\n if self.refill_buffer(bytes_used) < bytes_used {\n return (0, 0);\n }\n\n let utf32_value = ch as u32 as i32;\n\n return (utf32_value, bytes_used);\n }\n\n (0, 0)\n }\n\n fn back_to(&mut self, memo: &mut LrMemo) -> Option<Rc<RefCell<ThunkChunk>>> {\n self.position_offset = memo.offset;\n memo.clone_answer()\n }\n}\n\nconst NOP: Result<usize, usize> = Ok(0);\nconst fn throw(label: usize) -> Result<usize, usize> {\n Err(label)\n}\n\nfn catch(label: usize, f: impl FnOnce() -> Result<usize, usize>) -> Result<usize, usize> {\n match f() {\n Ok(_) => NOP,\n Err(e) if e == label => NOP,\n Err(e) => throw(e),\n }\n}\n\nstruct #{class_name} {\n level: usize,\n memos: LrMemoTable,\n input: Input,\n}\n\nimpl #{class_name} {\n fn new(input: impl std::io::Read + 'static) -> Self {\n Self {\n level: 0,\n memos: LrMemoTable::new(),\n input: Input::new(input),\n }\n }\n\n fn parse(&mut self) -> Option<Value> {\n if self.input.closed {\n return None;\n }\n\n let mut answer = ThunkChunk::new(0);\n let pos = self.input.start_position;\n".freeze
466
510
 
467
- if @location
468
- erbout << " @start_loc = start_loc || Location.new\n @end_loc = end_loc || Location.new\n".freeze
511
+ if !@root.rules.empty?
512
+ erbout << "\n let value = {\n let node = self.rule_thunk_node(\n Rule::#{Packcr.camelize(@root.rules[0].name)},\n &mut answer,\n 0,\n self.input.position_offset,\n &None,\n );\n self.memos.clear();\n node.map(|node| node.do_action(&ThunkProcessor::new(&self.input.buffer)))\n };\n self.input.commit_buffer();\n if pos == self.input.start_position || self.input.refill_buffer(1) < 1 {\n self.input.closed = true;\n }\n value\n".freeze
513
+ end
514
+ erbout << " }\n\n fn grow_lr(&mut self, rule: Rule, offset: usize) {\n loop {\n let old_offset = self.input.position_offset;\n self.input.position_offset = offset;\n\n let answer = self.call_rule(rule, offset, Some(RuleSet::new()));\n match answer {\n Some(answer) if self.input.position_offset > old_offset => {\n let memo = self.memos.get(offset, rule);\n memo.update(Some(answer), self.input.position_offset);\n }\n _ => {\n return;\n }\n }\n }\n }\n\n fn rule_answer_without_limits(&mut self, rule: Rule) -> Option<Rc<RefCell<ThunkChunk>>> {\n let offset = self.input.position_offset;\n if !self.memos.has(offset, rule) {\n let memo = LrMemo::new(offset);\n self.memos.set(offset, rule, memo);\n let answer = self.call_rule(rule, offset, None);\n\n let memo = self.memos.get(offset, rule);\n memo.update(answer, self.input.position_offset);\n if !memo.grow {\n return self.input.back_to(memo);\n }\n self.grow_lr(rule, offset);\n\n let memo = self.memos.get(offset, rule);\n memo.grow = false;\n return self.input.back_to(memo);\n }\n\n let memo = self.memos.get(offset, rule);\n if memo.start_grow() {\n return None;\n }\n self.input.back_to(memo)\n }\n\n fn rule_answer_with_limits(\n &mut self,\n rule: Rule,\n limits: RuleSet,\n ) -> Option<Rc<RefCell<ThunkChunk>>> {\n let offset = self.input.position_offset;\n let answer = self.call_rule(rule, offset, Some(limits));\n if !self.memos.has(offset, rule) {\n return answer.map(|answer| Rc::new(RefCell::new(answer)));\n }\n\n let memo = self.memos.get(offset, rule);\n if self.input.position_offset > memo.offset {\n memo.update(answer, self.input.position_offset);\n }\n self.input.back_to(memo)\n }\n\n fn apply_rule(\n &mut self,\n rule: Rule,\n parent: &mut ThunkChunk,\n value_index: usize,\n offset: usize,\n limits: &RuleLimit,\n ) -> bool {\n let node = self.rule_thunk_node(rule, parent, value_index, offset, limits);\n node.is_some_and(|node| {\n parent.thunks.borrow_mut().push(Thunk::Node(node));\n true\n })\n }\n\n fn rule_thunk_node(\n &mut self,\n rule: Rule,\n parent: &ThunkChunk,\n value_index: usize,\n offset: usize,\n limits: &RuleLimit,\n ) -> Option<ThunkNode> {\n match limits {\n Some(limit_set)\n if self.input.position_offset == offset && !limit_set.contains(&rule) =>\n {\n self.rule_answer_with_limits(rule, limits.clone().unwrap())\n }\n _ => self.rule_answer_without_limits(rule),\n }\n .map(|answer| ThunkNode {\n thunks: answer.borrow().thunks.clone(),\n value: parent.value_ref(value_index),\n })\n }\n\n fn call_rule(\n &mut self,\n rule: Rule,\n offset: usize,\n mut limits: RuleLimit,\n ) -> Option<ThunkChunk> {\n if let Some(ref mut limits) = limits {\n limits.insert(rule);\n }\n match rule {\n".freeze
515
+
516
+ @root.rules.each do |rule|
517
+ erbout << " Rule::#{Packcr.camelize(rule.name)} => self.evaluate_rule_#{rule.name}(offset, limits),\n".freeze
518
+ end
519
+ erbout << " }\n }\n".freeze
520
+
521
+ @root.rules.each do |rule|
522
+ erbout << "\n".freeze
523
+
524
+ gen = ::Packcr::Generator.new(rule, @ascii, @location, :rs)
525
+
526
+ erbout << "#{gen.generate_code(rule, 0, 4, false)}".freeze
527
+ end
528
+ erbout << "}\n\nstruct ThunkProcessor<'a> {\n buffer: &'a str,\n}\n\nimpl<'a> ThunkProcessor<'a> {\n fn new(buffer: &'a str) -> Self {\n Self { buffer }\n }\n\n fn call_action(&self, action: Action, thunk: &ThunkLeaf, value: &mut i32) {\n match action {\n".freeze
529
+
530
+ @root.rules.each do |rule|
531
+ rule.codes.each do |code|
532
+ erbout << " Action::#{Packcr.camelize(rule.name)}#{code.index} => self.action_#{rule.name}_#{code.index}(thunk, value),\n".freeze
533
+ end
534
+ end
535
+ erbout << " }\n }\n".freeze
536
+
537
+ @root.rules.each do |rule|
538
+ rule.codes.each do |code|
539
+ erbout << "\n #[allow(unused_variables, non_snake_case)]\n fn action_#{rule.name}_#{code.index}(&self, leaf: &ThunkLeaf, out: &mut i32) {\n".freeze
540
+
541
+ code.vars.each_with_index do |ref, i|
542
+ if i == 0
543
+ erbout << " let values = leaf.values();\n".freeze
544
+ end
545
+ erbout << " let #{ref.var} = values[&#{ref.index}];\n".freeze
546
+ end
547
+ erbout << " let _0 = {\n let capt = &leaf.capt0;\n &self.buffer[(capt.start)..(capt.end)]\n };\n".freeze
548
+
549
+ code.capts.size.times do |i|
550
+ erbout << " let _#{i + 1} = {\n let capt = &leaf.capts[&#{i}];\n &self.buffer[(capt.start)..(capt.end)]\n };\n".freeze
551
+ end
552
+ erbout << " #{stream.get_code_block(code.code, 4, @iname)} }\n".freeze
553
+ end
554
+ end
555
+ erbout << "}\n\n#[derive(Copy, Clone)]\nenum Action {\n".freeze
556
+
557
+ @root.rules.each do |rule|
558
+ rule.codes.each do |code|
559
+ erbout << " #{Packcr.camelize(rule.name)}#{code.index},\n".freeze
560
+ end
561
+ end
562
+ erbout << "}\n\n#[derive(Copy, Clone, PartialEq, Eq, Hash)]\nenum Rule {\n".freeze
563
+
564
+ @root.rules.each do |rule|
565
+ erbout << " #{Packcr.camelize(rule.name)},\n".freeze
469
566
  end
470
- erbout << " end\n\n def capture_string(buffer)\n @capture_string ||= buffer[@range_start, @range_end - @range_start]\n end\n end\n\n class Value\n attr_accessor :value\n end\nend\n".freeze
567
+ erbout << "}\n".freeze
471
568
 
472
569
  if !code(:lsource).empty?
473
570
  erbout << "\n".freeze
@@ -477,6 +574,8 @@ class Packcr
477
574
  end
478
575
  end
479
576
  erbout
577
+ else
578
+ raise "unknown lang #{lang}"
480
579
  end
481
580
  end
482
581
  end
@@ -1,7 +1,7 @@
1
1
  class Packcr
2
2
  class Node
3
3
  class ActionNode
4
- def get_code(gen, onfail, indent, bare, oncut)
4
+ def get_code(gen, onfail, indent, unwrap, oncut)
5
5
  case gen.lang
6
6
  when :c
7
7
  erbout = +""
@@ -53,6 +53,21 @@ class Packcr
53
53
  erbout << " )\n)\n".freeze
54
54
 
55
55
  erbout
56
+ when :rs
57
+ erbout = +""
58
+ erbout << "answer.push_leaf(Action::#{Packcr.camelize(gen.rule.name)}#{index}, self.input.position_offset, &[".freeze
59
+ vars.each_with_index do |var, i|
60
+ erbout << "#{", " if i > 0}#{var.index}".freeze
61
+ end
62
+ erbout << "], &[".freeze
63
+ capts.each_with_index do |capt, i|
64
+ erbout << "#{", " if i > 0}#{capt.index}".freeze
65
+ end
66
+ erbout << "]);\n".freeze
67
+
68
+ erbout
69
+ else
70
+ raise "unknown lang #{gen.lang}"
56
71
  end
57
72
  end
58
73
  end
@@ -1,7 +1,7 @@
1
1
  class Packcr
2
2
  class Node
3
3
  class AlternateNode
4
- def get_code(gen, onfail, indent, bare, oncut)
4
+ def get_code(gen, onfail, indent, unwrap, oncut)
5
5
  case gen.lang
6
6
  when :c
7
7
  erbout = +""
@@ -19,13 +19,12 @@ class Packcr
19
19
  r = expr.reachability
20
20
 
21
21
  erbout << "#{gen.generate_code(expr, l, 4, false)}".freeze
22
- case r
23
- when Packcr::CODE_REACH__ALWAYS_SUCCEED
22
+ if r == Packcr::CODE_REACH__ALWAYS_SUCCEED
24
23
  if c
25
24
  erbout << " /* unreachable codes omitted */\n".freeze
26
25
  end
27
26
  break
28
- when Packcr::CODE_REACH__BOTH
27
+ elsif r == Packcr::CODE_REACH__BOTH
29
28
  erbout << " goto L#{format("%04d", m)};\n".freeze
30
29
  end
31
30
  erbout << "L#{format("%04d", l)}:;\n ctx->position_offset = p;\n".freeze
@@ -64,15 +63,14 @@ class Packcr
64
63
  r = expr.reachability
65
64
 
66
65
  erbout << "#{gen.generate_code(expr, l, 4, false, oncut: onfail)}".freeze
67
- case r
68
- when Packcr::CODE_REACH__ALWAYS_SUCCEED
66
+ if r == Packcr::CODE_REACH__ALWAYS_SUCCEED
69
67
  if c
70
68
  erbout << " # unreachable codes omitted\n".freeze
71
69
  end
72
70
  erbout << " end\n".freeze
73
71
 
74
72
  break
75
- when Packcr::CODE_REACH__BOTH
73
+ elsif r == Packcr::CODE_REACH__BOTH
76
74
  erbout << " throw(#{m})\n".freeze
77
75
  end
78
76
  erbout << " end\n".freeze
@@ -91,6 +89,48 @@ class Packcr
91
89
  erbout << "end\n".freeze
92
90
 
93
91
  erbout
92
+ when :rs
93
+ erbout = +""
94
+ m = gen.next_label
95
+ erbout << "catch(#{m}, || {\n let p = self.input.position_offset;\n".freeze
96
+
97
+ if gen.location
98
+ erbout << " TODO\n".freeze
99
+ end
100
+ nodes.each_with_index do |expr, i|
101
+ c = i + 1 < nodes.length
102
+ if expr.reversible?(gen)
103
+
104
+ erbout << "#{gen.generate_code(expr, m, 4, false, reverse: true, oncut: onfail)}".freeze
105
+ else
106
+ l = gen.next_label
107
+ r = expr.reachability
108
+ if r == Packcr::CODE_REACH__ALWAYS_SUCCEED
109
+ erbout << " catch(#{l}, || {\n#{gen.generate_code(expr, l, 8, false, oncut: onfail)}".freeze
110
+ if c
111
+ erbout << " // unreachable codes omitted\n".freeze
112
+ end
113
+ erbout << " NOP\n })?\n".freeze
114
+
115
+ break
116
+ else
117
+ erbout << " catch(#{l}, || {\n#{gen.generate_code(expr, l, 8, false, oncut: onfail)} throw(#{m})\n })?;\n".freeze
118
+ end
119
+ end
120
+ erbout << " self.input.position_offset = p;\n".freeze
121
+
122
+ if gen.location
123
+ erbout << " TODO\n".freeze
124
+ end
125
+ next if c
126
+
127
+ erbout << " throw(#{onfail})\n".freeze
128
+ end
129
+ erbout << "})?;\n".freeze
130
+
131
+ erbout
132
+ else
133
+ raise "unknown lang #{gen.lang}"
94
134
  end
95
135
  end
96
136
  end
@@ -1,7 +1,7 @@
1
1
  class Packcr
2
2
  class Node
3
3
  class CaptureNode
4
- def get_code(gen, onfail, indent, bare, oncut)
4
+ def get_code(gen, onfail, indent, unwrap, oncut)
5
5
  case gen.lang
6
6
  when :c
7
7
  erbout = +""
@@ -32,6 +32,21 @@ class Packcr
32
32
  erbout << "q_loc#{gen.level} = @position_offset_loc\ncapt#{gen.level}.start_loc = p_loc#{gen.level}\ncapt#{gen.level}.end_loc = q_loc#{gen.level}\n".freeze
33
33
  end
34
34
  erbout
35
+ when :rs
36
+ erbout = +""
37
+ erbout << "let p_inner = self.input.position_offset;\n".freeze
38
+
39
+ if gen.location
40
+ erbout << "TODO\n".freeze
41
+ end
42
+ erbout << "{\n#{gen.generate_code(expr, onfail, 4, false)}}\nlet q = self.input.position_offset;\nanswer.capts[#{index}].start = p_inner;\nanswer.capts[#{index}].end = q;\n".freeze
43
+
44
+ if gen.location
45
+ erbout << "TODO\n".freeze
46
+ end
47
+ erbout
48
+ else
49
+ raise "unknown lang #{gen.lang}"
35
50
  end
36
51
  end
37
52
  end