Class: Packcr::Context

Inherits:
Object
  • Object
show all
Defined in:
lib/packcr/context.rb,
lib/packcr/generated/context.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, lines: false, debug: false, ascii: false, lang: nil) {|_self| ... } ⇒ Context

Returns a new instance of Context.

Yields:

  • (_self)

Yield Parameters:



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/packcr/context.rb', line 10

def initialize(path, lines: false, debug: false, ascii: false, lang: nil)
  if !path
    raise ArgumentError, "bad path: #{path}";
  end

  @iname = path
  @debug = debug

  dirname = File.dirname(path)
  basename = File.basename(path, ".*")
  if !lang
    lang = File.extname(basename)[1..-1]&.to_sym
    if lang
      basename = File.basename(basename, ".*")
    else
      lang = :c
    end
  end
  if dirname == "."
    path = basename
  else
    path = File.join(dirname, basename)
  end

  @lang = lang.to_sym
  case @lang
  when :c
    @hname = "#{path}.h"
    @patterns = {
      get_source_code: "#{path}.c",
      get_header_code: @hname,
    }
    @hid = File.basename(@hname).upcase.gsub(/[^A-Z0-9]/, "_")
  when :rb
    @patterns = {
      get_source_code: "#{path}.rb",
    }
  else
    raise "unexpected lang: #{@lang}"
  end

  @lines = !!lines
  @ascii = !!ascii
  @utf8 = !ascii

  @errnum = 0

  @codes = {}
  @root = Node::RootNode.new

  return unless block_given?

  yield(self)
end

Instance Attribute Details

#auxil_typeObject

Returns the value of attribute auxil_type.



8
9
10
# File 'lib/packcr/context.rb', line 8

def auxil_type
  @auxil_type
end

#capture_in_codeObject

Returns the value of attribute capture_in_code.



8
9
10
# File 'lib/packcr/context.rb', line 8

def capture_in_code
  @capture_in_code
end

#errnumObject

Returns the value of attribute errnum.



8
9
10
# File 'lib/packcr/context.rb', line 8

def errnum
  @errnum
end

#langObject (readonly)

Returns the value of attribute lang.



7
8
9
# File 'lib/packcr/context.rb', line 7

def lang
  @lang
end

#prefixObject

Returns the value of attribute prefix.



8
9
10
# File 'lib/packcr/context.rb', line 8

def prefix
  @prefix
end

#rootObject (readonly)

Returns the value of attribute root.



7
8
9
# File 'lib/packcr/context.rb', line 7

def root
  @root
end

#value_typeObject

Returns the value of attribute value_type.



8
9
10
# File 'lib/packcr/context.rb', line 8

def value_type
  @value_type
end

Instance Method Details

#auxil_defObject



103
104
105
106
# File 'lib/packcr/context.rb', line 103

def auxil_def
  type = auxil_type
  "#{type}#{type =~ /\*$/ ? "" : " "}"
end

#class_nameObject



99
100
101
# File 'lib/packcr/context.rb', line 99

def class_name
  prefix.gsub(/(?:_|^|(\W))([a-z])/) { "#{::Regexp.last_match(1)}#{::Regexp.last_match(2)}".upcase }
end

#code(name) ⇒ Object



65
66
67
# File 'lib/packcr/context.rb', line 65

def code(name)
  @codes[name] ||= []
end

#dump_optionsObject



113
114
115
116
117
118
119
# File 'lib/packcr/context.rb', line 113

def dump_options
  $stdout.print <<~EOS
    value_type: '#{value_type}'
    auxil_type: '#{auxil_type}'
    prefix: '#{prefix}'
  EOS
end

#error(line, col, message) ⇒ Object



73
74
75
76
# File 'lib/packcr/context.rb', line 73

def error(line, col, message)
  warn "#{@iname}:#{line}:#{col}: #{message}"
  @errnum += 1
end

#generateObject



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/packcr/context.rb', line 141

def generate
  results = []

  @patterns.each do |meth, ofile|
    result = Tempfile.new
    result.unlink
    results << [ofile, result]
    stream = Packcr::Stream.new(result, ofile, @lines ? 0 : nil)
    stream.write Packcr.format_code(public_send(meth, @lang, stream)), rewrite_line_directive: true

    next if @errnum.zero?

    results.each do |_, r|
      r.close
    end
    return false
  end

  results.each do |(name, result)|
    result.rewind
    File.open(name, "wt") do |f|
      IO.copy_stream(result, f)
    end
  end
  true
end

#get_header_code(lang, stream) ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/packcr/generated/context.rb', line 3

def get_header_code(lang, stream)
  case lang
  when :c
    erbout = +""
    erbout << "/* A packrat parser generated by PackCR #{Packcr::VERSION} */\n\n".freeze

    code(:eheader).each do |code|
      erbout << "#{stream.get_code_block(code, 0, @iname)}".freeze
    end
    if !code(:eheader).empty?
      erbout << "\n".freeze
    end
    erbout << "#ifndef PACKCR_INCLUDED_#{@hid}\n#define PACKCR_INCLUDED_#{@hid}\n\n".freeze

    code(:header).each do |code|
      erbout << "#{stream.get_code_block(code, 0, @iname)}".freeze
    end
    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

    erbout
  end
end

#get_source_code(lang, stream) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
# File 'lib/packcr/generated/context.rb', line 26

def get_source_code(lang, stream)
  case lang
  when :c
    erbout = +""
    erbout << "/* A packrat parser generated by PackCR #{Packcr::VERSION} */\n\n".freeze

    code(:esource).each do |code|
      erbout << "#{stream.get_code_block(code, 0, @iname)}".freeze
    end
    if !code(:esource).empty?
      erbout << "\n".freeze
    end
    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

    if !code(:location).empty?
      erbout << "\n".freeze

      code(:location).each do |code|
        erbout << "#{stream.get_code_block(code, 4, @iname)}".freeze
      end
    end
    erbout << "\n".freeze

    code(:source).each do |code|
      erbout << "#{stream.get_code_block(code, 0, @iname)}".freeze
    end
    erbout << "#if !defined __has_attribute || defined _MSC_VER\n#define __attribute__(x)\n#endif\n\n#ifdef _MSC_VER\n#define MARK_FUNC_AS_USED __pragma(warning(suppress:4505))\n#else\n#define MARK_FUNC_AS_USED __attribute__((__unused__))\n#endif\n\n#ifndef PACKCR_BUFFER_MIN_SIZE\n#define PACKCR_BUFFER_MIN_SIZE 256\n#endif /* !PACKCR_BUFFER_MIN_SIZE */\n\n#ifndef PACKCR_ARRAY_MIN_SIZE\n#define PACKCR_ARRAY_MIN_SIZE 2\n#endif /* !PACKCR_ARRAY_MIN_SIZE */\n\n#ifndef PACKCR_POOL_MIN_SIZE\n#define PACKCR_POOL_MIN_SIZE 65536\n#endif /* !PACKCR_POOL_MIN_SIZE */\n\n#define PACKCR_DBG_EVALUATE 0\n#define PACKCR_DBG_MATCH    1\n#define PACKCR_DBG_NOMATCH  2\n\n#define PACKCR_VOID_VALUE (~(size_t)0)\n\ntypedef enum packcr_bool_tag {\n    PACKCR_FALSE = 0,\n    PACKCR_TRUE\n} packcr_bool_t;\n\ntypedef struct packcr_char_array_tag {\n    char *buf;\n    size_t max;\n    size_t len;\n} packcr_char_array_t;\n\ntypedef struct packcr_range_tag {\n    size_t start;\n    size_t end;\n".freeze

    if @location
      erbout << "    packcr_location_t start_loc;\n    packcr_location_t end_loc;\n".freeze
    end
    erbout << "} packcr_range_t;\n\ntypedef #{value_def}packcr_value_t;\n\ntypedef #{auxil_def}packcr_auxil_t;\n\n".freeze

    if prefix != "packcr"
      erbout << "typedef #{prefix}_context_t packcr_context_t;\n\n".freeze
    end
    erbout << "typedef struct packcr_value_table_tag {\n    packcr_value_t *buf;\n    size_t max;\n    size_t len;\n} packcr_value_table_t;\n\ntypedef struct packcr_value_refer_table_tag {\n    packcr_value_t **buf;\n    size_t max;\n    size_t len;\n} packcr_value_refer_table_t;\n\ntypedef struct packcr_capture_tag {\n    packcr_range_t range;\n    char *string; /* mutable */\n} packcr_capture_t;\n\ntypedef struct packcr_capture_table_tag {\n    packcr_capture_t *buf;\n    size_t max;\n    size_t len;\n} packcr_capture_table_t;\n\ntypedef struct packcr_capture_const_table_tag {\n    const packcr_capture_t **buf;\n    size_t max;\n    size_t len;\n} packcr_capture_const_table_t;\n\ntypedef struct packcr_thunk_tag packcr_thunk_t;\ntypedef struct packcr_thunk_array_tag packcr_thunk_array_t;\n\ntypedef void (*packcr_action_t)(packcr_context_t *, packcr_thunk_t *, packcr_value_t *);\n\ntypedef enum packcr_thunk_type_tag {\n    PACKCR_THUNK_LEAF,\n    PACKCR_THUNK_NODE\n} packcr_thunk_type_t;\n\ntypedef struct packcr_thunk_leaf_tag {\n    packcr_value_refer_table_t values;\n    packcr_capture_const_table_t capts;\n    packcr_capture_t capt0;\n    packcr_action_t action;\n} packcr_thunk_leaf_t;\n\ntypedef struct packcr_thunk_node_tag {\n    const packcr_thunk_array_t *thunks; /* just a reference */\n    packcr_value_t *value; /* just a reference */\n} packcr_thunk_node_t;\n\ntypedef union packcr_thunk_data_tag {\n    packcr_thunk_leaf_t leaf;\n    packcr_thunk_node_t node;\n} packcr_thunk_data_t;\n\nstruct packcr_thunk_tag {\n    packcr_thunk_type_t type;\n    packcr_thunk_data_t data;\n};\n\nstruct packcr_thunk_array_tag {\n    packcr_thunk_t **buf;\n    size_t max;\n    size_t len;\n};\n\ntypedef struct packcr_thunk_chunk_tag {\n    packcr_value_table_t values;\n    packcr_capture_table_t capts;\n    packcr_thunk_array_t thunks;\n    size_t pos; /* the starting position in the character buffer */\n".freeze

    if @location
      erbout << "    packcr_location_t pos_loc;\n".freeze
    end
    erbout << "} packcr_thunk_chunk_t;\n\ntypedef struct packcr_lr_memo_tag packcr_lr_memo_t;\n\nstruct packcr_lr_memo_tag {\n    size_t offset;\n".freeze

    if @location
      erbout << "    packcr_location_t offset_loc;\n".freeze
    end
    erbout << "    packcr_lr_memo_t *hold;\n    packcr_thunk_chunk_t *chunk;\n    packcr_bool_t fail;\n    packcr_bool_t grow;\n};\n\ntypedef struct packcr_rule_set_tag packcr_rule_set_t;\n\ntypedef packcr_thunk_chunk_t *(*packcr_rule_t)(packcr_context_t *, size_t".freeze
    if @location
      erbout << ", packcr_location_t".freeze
    end
    erbout << ", packcr_rule_set_t*);\n\ntypedef struct packcr_rule_set_tag {\n    packcr_rule_t *buf;\n    size_t max;\n    size_t len;\n} packcr_rule_set_t;\n\ntypedef struct packcr_lr_memo_map_entry_tag {\n    packcr_rule_t rule;\n    packcr_lr_memo_t *memo;\n} packcr_lr_memo_map_entry_t;\n\ntypedef struct packcr_lr_memo_map_tag {\n    packcr_lr_memo_map_entry_t *buf;\n    size_t max;\n    size_t len;\n} packcr_lr_memo_map_t;\n\ntypedef struct packcr_lr_table_tag {\n    packcr_lr_memo_map_t **buf;\n    size_t max;\n    size_t len;\n    size_t ofs;\n} packcr_lr_table_t;\n\ntypedef struct packcr_memory_entry_tag packcr_memory_entry_t;\ntypedef struct packcr_memory_pool_tag packcr_memory_pool_t;\n\nstruct packcr_memory_entry_tag {\n    packcr_memory_entry_t *next;\n};\n\nstruct packcr_memory_pool_tag {\n    packcr_memory_pool_t *next;\n    size_t allocated;\n    size_t unused;\n};\n\ntypedef struct packcr_memory_recycler_tag {\n    packcr_memory_pool_t *pool_list;\n    packcr_memory_entry_t *entry_list;\n    size_t element_size;\n} packcr_memory_recycler_t;\n\nstruct #{prefix}_context_tag {\n    size_t buffer_start_position; /* the position in the input of the first character currently buffered */\n    size_t position_offset; /* the current parsing position in the character buffer */\n".freeze

    if @location
      erbout << "    packcr_location_t buffer_start_position_loc;\n    packcr_location_t position_offset_loc;\n".freeze
    end
    erbout << "    size_t level;\n    packcr_char_array_t buffer;\n    packcr_lr_table_t lrtable;\n    packcr_thunk_array_t thunks;\n    packcr_auxil_t auxil;\n    packcr_memory_recycler_t thunk_chunk_recycler;\n    packcr_memory_recycler_t lr_memo_recycler;\n};\n\n#ifndef PACKCR_ERROR\n#define PACKCR_ERROR(auxil) packcr_error()\nMARK_FUNC_AS_USED\nstatic void packcr_error(void) {\n    fprintf(stderr, \"Syntax error\\n\");\n    exit(1);\n}\n#endif /* !PACKCR_ERROR */\n\n#ifndef PACKCR_GETCHAR\n#define PACKCR_GETCHAR(auxil) getchar()\n#endif /* !PACKCR_GETCHAR */\n\n#ifndef PACKCR_MALLOC\n#define PACKCR_MALLOC(auxil, size) packcr_malloc_e(size)\nstatic void *packcr_malloc_e(size_t size) {\n    void *const p = malloc(size);\n    if (p == NULL) {\n        fprintf(stderr, \"Out of memory\\n\");\n        exit(1);\n    }\n    return p;\n}\n#endif /* !PACKCR_MALLOC */\n\n#ifndef PACKCR_REALLOC\n#define PACKCR_REALLOC(auxil, ptr, size) packcr_realloc_e(ptr, size)\nstatic void *packcr_realloc_e(void *ptr, size_t size) {\n    void *const p = realloc(ptr, size);\n    if (p == NULL) {\n        fprintf(stderr, \"Out of memory\\n\");\n        exit(1);\n    }\n    return p;\n}\n#endif /* !PACKCR_REALLOC */\n\n#ifndef PACKCR_FREE\n#define PACKCR_FREE(auxil, ptr) free(ptr)\n#endif /* !PACKCR_FREE */\n\n#ifndef PACKCR_DEBUG\n#define PACKCR_DEBUG(auxil, event, rule, level, pos, buffer, length) ((void)0)\n#endif /* !PACKCR_DEBUG */\n\nstatic char *packcr_strndup_e(packcr_auxil_t auxil, const char *str, size_t len) {\n    const size_t m = strnlen(str, len);\n    char *const s = (char *)PACKCR_MALLOC(auxil, m + 1);\n    memcpy(s, str, m);\n    s[m] = '\\0';\n    return s;\n}\n\nstatic void packcr_char_array__init(packcr_auxil_t auxil, packcr_char_array_t *array) {\n    array->len = 0;\n    array->max = 0;\n    array->buf = NULL;\n}\n\nstatic void packcr_char_array__add(packcr_auxil_t auxil, packcr_char_array_t *array, char ch) {\n    if (array->max <= array->len) {\n        const size_t n = array->len + 1;\n        size_t m = array->max;\n        if (m == 0) m = PACKCR_BUFFER_MIN_SIZE;\n        while (m < n && m != 0) m <<= 1;\n        if (m == 0) m = n;\n        array->buf = (char *)PACKCR_REALLOC(auxil, array->buf, m);\n        array->max = m;\n    }\n    array->buf[array->len++] = ch;\n}\n\nstatic void packcr_char_array__term(packcr_auxil_t auxil, packcr_char_array_t *array) {\n    PACKCR_FREE(auxil, array->buf);\n}\n\nstatic void packcr_value_table__init(packcr_auxil_t auxil, packcr_value_table_t *table) {\n    table->len = 0;\n    table->max = 0;\n    table->buf = NULL;\n}\n\nMARK_FUNC_AS_USED\nstatic void packcr_value_table__resize(packcr_auxil_t auxil, packcr_value_table_t *table, size_t len) {\n    if (table->max < len) {\n        size_t m = table->max;\n        if (m == 0) m = PACKCR_ARRAY_MIN_SIZE;\n        while (m < len && m != 0) m <<= 1;\n        if (m == 0) m = len;\n        table->buf = (packcr_value_t *)PACKCR_REALLOC(auxil, table->buf, sizeof(packcr_value_t) * m);\n        table->max = m;\n    }\n    table->len = len;\n}\n\nMARK_FUNC_AS_USED\nstatic void packcr_value_table__clear(packcr_auxil_t auxil, packcr_value_table_t *table) {\n    memset(table->buf, 0, sizeof(packcr_value_t) * table->len);\n}\n\nstatic void packcr_value_table__term(packcr_auxil_t auxil, packcr_value_table_t *table) {\n    PACKCR_FREE(auxil, table->buf);\n}\n\nstatic void packcr_value_refer_table__init(packcr_auxil_t auxil, packcr_value_refer_table_t *table) {\n    table->len = 0;\n    table->max = 0;\n    table->buf = NULL;\n}\n\nstatic void packcr_value_refer_table__resize(packcr_auxil_t auxil, packcr_value_refer_table_t *table, size_t len) {\n    size_t i;\n    if (table->max < len) {\n        size_t m = table->max;\n        if (m == 0) m = PACKCR_ARRAY_MIN_SIZE;\n        while (m < len && m != 0) m <<= 1;\n        if (m == 0) m = len;\n        table->buf = (packcr_value_t **)PACKCR_REALLOC(auxil, table->buf, sizeof(packcr_value_t *) * m);\n        table->max = m;\n    }\n    for (i = table->len; i < len; i++) table->buf[i] = NULL;\n    table->len = len;\n}\n\nstatic void packcr_value_refer_table__term(packcr_auxil_t auxil, packcr_value_refer_table_t *table) {\n    PACKCR_FREE(auxil, table->buf);\n}\n\nstatic void packcr_capture_table__init(packcr_auxil_t auxil, packcr_capture_table_t *table) {\n    table->len = 0;\n    table->max = 0;\n    table->buf = NULL;\n}\n\nMARK_FUNC_AS_USED\nstatic void packcr_capture_table__resize(packcr_auxil_t auxil, packcr_capture_table_t *table, size_t len) {\n    size_t i;\n    for (i = len; i < table->len; i++) PACKCR_FREE(auxil, table->buf[i].string);\n    if (table->max < len) {\n        size_t m = table->max;\n        if (m == 0) m = PACKCR_ARRAY_MIN_SIZE;\n        while (m < len && m != 0) m <<= 1;\n        if (m == 0) m = len;\n        table->buf = (packcr_capture_t *)PACKCR_REALLOC(auxil, table->buf, sizeof(packcr_capture_t) * m);\n        table->max = m;\n    }\n    for (i = table->len; i < len; i++) {\n        table->buf[i].range.start = 0;\n        table->buf[i].range.end = 0;\n".freeze

    if @location
      erbout << "        packcr_location_init(&table->buf[i].range.start_loc);\n        packcr_location_init(&table->buf[i].range.end_loc);\n".freeze
    end
    erbout << "        table->buf[i].string = NULL;\n    }\n    table->len = len;\n}\n\nstatic void packcr_capture_table__term(packcr_auxil_t auxil, packcr_capture_table_t *table) {\n    while (table->len > 0) {\n        table->len--;\n        PACKCR_FREE(auxil, table->buf[table->len].string);\n    }\n    PACKCR_FREE(auxil, table->buf);\n}\n\nstatic void packcr_capture_const_table__init(packcr_auxil_t auxil, packcr_capture_const_table_t *table) {\n    table->len = 0;\n    table->max = 0;\n    table->buf = NULL;\n}\n\nstatic void packcr_capture_const_table__resize(packcr_auxil_t auxil, packcr_capture_const_table_t *table, size_t len) {\n    size_t i;\n    if (table->max < len) {\n        size_t m = table->max;\n        if (m == 0) m = PACKCR_ARRAY_MIN_SIZE;\n        while (m < len && m != 0) m <<= 1;\n        if (m == 0) m = len;\n        table->buf = (const packcr_capture_t **)PACKCR_REALLOC(auxil, (packcr_capture_t **)table->buf, sizeof(const packcr_capture_t *) * m);\n        table->max = m;\n    }\n    for (i = table->len; i < len; i++) table->buf[i] = NULL;\n    table->len = len;\n}\n\nstatic void packcr_capture_const_table__term(packcr_auxil_t auxil, packcr_capture_const_table_t *table) {\n    PACKCR_FREE(auxil, (void *)table->buf);\n}\n\nMARK_FUNC_AS_USED\nstatic packcr_thunk_t *packcr_thunk__create_leaf(packcr_auxil_t auxil, packcr_action_t action, size_t valuec, size_t captc) {\n    packcr_thunk_t *const thunk = (packcr_thunk_t *)PACKCR_MALLOC(auxil, sizeof(packcr_thunk_t));\n    thunk->type = PACKCR_THUNK_LEAF;\n    packcr_value_refer_table__init(auxil, &thunk->data.leaf.values);\n    packcr_value_refer_table__resize(auxil, &thunk->data.leaf.values, valuec);\n    packcr_capture_const_table__init(auxil, &thunk->data.leaf.capts);\n    packcr_capture_const_table__resize(auxil, &thunk->data.leaf.capts, captc);\n    thunk->data.leaf.capt0.range.start = 0;\n    thunk->data.leaf.capt0.range.end = 0;\n".freeze

    if @location
      erbout << "    packcr_location_init(&thunk->data.leaf.capt0.range.start_loc);\n    packcr_location_init(&thunk->data.leaf.capt0.range.end_loc);\n".freeze
    end
    erbout << "    thunk->data.leaf.capt0.string = NULL;\n    thunk->data.leaf.action = action;\n    return thunk;\n}\n\nstatic packcr_thunk_t *packcr_thunk__create_node(packcr_auxil_t auxil, const packcr_thunk_array_t *thunks, packcr_value_t *value) {\n    packcr_thunk_t *const thunk = (packcr_thunk_t *)PACKCR_MALLOC(auxil, sizeof(packcr_thunk_t));\n    thunk->type = PACKCR_THUNK_NODE;\n    thunk->data.node.thunks = thunks;\n    thunk->data.node.value = value;\n    return thunk;\n}\n\nstatic void packcr_thunk__destroy(packcr_auxil_t auxil, packcr_thunk_t *thunk) {\n    if (thunk == NULL) return;\n    switch (thunk->type) {\n    case PACKCR_THUNK_LEAF:\n        PACKCR_FREE(auxil, thunk->data.leaf.capt0.string);\n        packcr_capture_const_table__term(auxil, &thunk->data.leaf.capts);\n        packcr_value_refer_table__term(auxil, &thunk->data.leaf.values);\n        break;\n    case PACKCR_THUNK_NODE:\n        break;\n    default: /* unknown */\n        break;\n    }\n    PACKCR_FREE(auxil, thunk);\n}\n\nstatic void packcr_thunk_array__init(packcr_auxil_t auxil, packcr_thunk_array_t *array) {\n    array->len = 0;\n    array->max = 0;\n    array->buf = NULL;\n}\n\nstatic void packcr_thunk_array__add(packcr_auxil_t auxil, packcr_thunk_array_t *array, packcr_thunk_t *thunk) {\n    if (array->max <= array->len) {\n        const size_t n = array->len + 1;\n        size_t m = array->max;\n        if (m == 0) m = PACKCR_ARRAY_MIN_SIZE;\n        while (m < n && m != 0) m <<= 1;\n        if (m == 0) m = n;\n        array->buf = (packcr_thunk_t **)PACKCR_REALLOC(auxil, array->buf, sizeof(packcr_thunk_t *) * m);\n        array->max = m;\n    }\n    array->buf[array->len++] = thunk;\n}\n\nstatic void packcr_thunk_array__revert(packcr_auxil_t auxil, packcr_thunk_array_t *array, size_t len) {\n    while (array->len > len) {\n        array->len--;\n        packcr_thunk__destroy(auxil, array->buf[array->len]);\n    }\n}\n\nstatic void packcr_thunk_array__term(packcr_auxil_t auxil, packcr_thunk_array_t *array) {\n    while (array->len > 0) {\n        array->len--;\n        packcr_thunk__destroy(auxil, array->buf[array->len]);\n    }\n    PACKCR_FREE(auxil, array->buf);\n}\n\nstatic void packcr_memory_recycler__init(packcr_auxil_t auxil, packcr_memory_recycler_t *recycler, size_t element_size) {\n    recycler->pool_list = NULL;\n    recycler->entry_list = NULL;\n    recycler->element_size = element_size;\n}\n\nstatic void *packcr_memory_recycler__supply(packcr_auxil_t auxil, packcr_memory_recycler_t *recycler) {\n    if (recycler->entry_list) {\n        packcr_memory_entry_t *const tmp = recycler->entry_list;\n        recycler->entry_list = tmp->next;\n        return tmp;\n    }\n    if (!recycler->pool_list || recycler->pool_list->unused == 0) {\n        size_t size = PACKCR_POOL_MIN_SIZE;\n        if (recycler->pool_list) {\n            size = recycler->pool_list->allocated << 1;\n            if (size == 0) size = recycler->pool_list->allocated;\n        }\n        {\n            packcr_memory_pool_t *const pool = (packcr_memory_pool_t *)PACKCR_MALLOC(\n                auxil, sizeof(packcr_memory_pool_t) + recycler->element_size * size\n            );\n            pool->allocated = size;\n            pool->unused = size;\n            pool->next = recycler->pool_list;\n            recycler->pool_list = pool;\n        }\n    }\n    recycler->pool_list->unused--;\n    return (char *)recycler->pool_list + sizeof(packcr_memory_pool_t) + recycler->element_size * recycler->pool_list->unused;\n}\n\nstatic void packcr_memory_recycler__recycle(packcr_auxil_t auxil, packcr_memory_recycler_t *recycler, void *ptr) {\n    packcr_memory_entry_t *const tmp = (packcr_memory_entry_t *)ptr;\n    tmp->next = recycler->entry_list;\n    recycler->entry_list = tmp;\n}\n\nstatic void packcr_memory_recycler__term(packcr_auxil_t auxil, packcr_memory_recycler_t *recycler) {\n    while (recycler->pool_list) {\n        packcr_memory_pool_t *const tmp = recycler->pool_list;\n        recycler->pool_list = tmp->next;\n        PACKCR_FREE(auxil, tmp);\n    }\n}\n\nMARK_FUNC_AS_USED\nstatic packcr_thunk_chunk_t *packcr_thunk_chunk__create(packcr_context_t *ctx) {\n    packcr_thunk_chunk_t *const chunk = (packcr_thunk_chunk_t *)packcr_memory_recycler__supply(ctx->auxil, &ctx->thunk_chunk_recycler);\n    packcr_value_table__init(ctx->auxil, &chunk->values);\n    packcr_capture_table__init(ctx->auxil, &chunk->capts);\n    packcr_thunk_array__init(ctx->auxil, &chunk->thunks);\n    chunk->pos = 0;\n    return chunk;\n}\n\nstatic void packcr_thunk_chunk__destroy(packcr_context_t *ctx, packcr_thunk_chunk_t *chunk) {\n    if (chunk == NULL) return;\n    packcr_thunk_array__term(ctx->auxil, &chunk->thunks);\n    packcr_capture_table__term(ctx->auxil, &chunk->capts);\n    packcr_value_table__term(ctx->auxil, &chunk->values);\n    packcr_memory_recycler__recycle(ctx->auxil, &ctx->thunk_chunk_recycler, chunk);\n}\n\nstatic void packcr_rule_set__init(packcr_auxil_t auxil, packcr_rule_set_t *set) {\n    set->len = 0;\n    set->max = 0;\n    set->buf = NULL;\n}\n\nstatic size_t packcr_rule_set__index(packcr_auxil_t auxil, const packcr_rule_set_t *set, packcr_rule_t rule) {\n    size_t i;\n    for (i = 0; i < set->len; i++) {\n        if (set->buf[i] == rule) return i;\n    }\n    return PACKCR_VOID_VALUE;\n}\n\nstatic packcr_bool_t packcr_rule_set__add(packcr_auxil_t auxil, packcr_rule_set_t *set, packcr_rule_t rule) {\n    const size_t i = packcr_rule_set__index(auxil, set, rule);\n    if (i != PACKCR_VOID_VALUE) return PACKCR_FALSE;\n    if (set->max <= set->len) {\n        const size_t n = set->len + 1;\n        size_t m = set->max;\n        if (m == 0) m = PACKCR_ARRAY_MIN_SIZE;\n        while (m < n && m != 0) m <<= 1;\n        if (m == 0) m = n;\n        set->buf = (packcr_rule_t *)PACKCR_REALLOC(auxil, set->buf, sizeof(packcr_rule_t) * m);\n        set->max = m;\n    }\n    set->buf[set->len++] = rule;\n    return PACKCR_TRUE;\n}\n\nstatic void packcr_rule_set__term(packcr_auxil_t auxil, packcr_rule_set_t *set) {\n    PACKCR_FREE(auxil, set->buf);\n}\n\nstatic packcr_lr_memo_t *packcr_lr_memo__create(packcr_context_t *ctx, size_t offset".freeze
    if @location
      erbout << ", packcr_location_t offset_loc".freeze
    end
    erbout << ") {\n    packcr_lr_memo_t *memo = (packcr_lr_memo_t *)packcr_memory_recycler__supply(ctx->auxil, &ctx->lr_memo_recycler);\n    memo->offset = offset;\n".freeze

    if @location
      erbout << "    memo->offset_loc = offset_loc;\n".freeze
    end
    erbout << "    memo->chunk = NULL;\n    memo->fail = PACKCR_TRUE;\n    memo->grow = PACKCR_FALSE;\n    memo->hold = NULL;\n    return memo;\n}\n\nstatic void packcr_lr_memo__set_chunk(packcr_context_t *ctx, packcr_lr_memo_t *memo, packcr_thunk_chunk_t *chunk) {\n    if (memo->chunk) {\n        packcr_lr_memo_t *const new_hold_memo = packcr_lr_memo__create(ctx, memo->offset".freeze

    if @location
      erbout << ", memo->offset_loc".freeze
    end
    erbout << ");\n        new_hold_memo->chunk = memo->chunk;\n        new_hold_memo->hold = memo->hold;\n        memo->hold = new_hold_memo;\n    }\n    memo->chunk = chunk;\n    memo->fail = PACKCR_FALSE;\n}\n\nstatic void packcr_lr_memo__destroy(packcr_context_t *ctx, packcr_lr_memo_t *memo) {\n    while (memo != NULL) {\n        packcr_lr_memo_t *const hold_memo = memo->hold;\n        packcr_thunk_chunk__destroy(ctx, memo->chunk);\n        packcr_memory_recycler__recycle(ctx->auxil, &ctx->lr_memo_recycler, memo);\n        memo = hold_memo;\n    }\n}\n\nstatic void packcr_lr_memo_map__init(packcr_auxil_t auxil, packcr_lr_memo_map_t *map) {\n    map->len = 0;\n    map->max = 0;\n    map->buf = NULL;\n}\n\nstatic size_t packcr_lr_memo_map__index(packcr_context_t *ctx, packcr_lr_memo_map_t *map, packcr_rule_t rule) {\n    size_t i;\n    for (i = 0; i < map->len; i++) {\n        if (map->buf[i].rule == rule) return i;\n    }\n    return PACKCR_VOID_VALUE;\n}\n\nstatic void packcr_lr_memo_map__put(packcr_context_t *ctx, packcr_lr_memo_map_t *map, packcr_rule_t rule, packcr_lr_memo_t *memo) {\n    const size_t i = packcr_lr_memo_map__index(ctx, map, rule);\n    if (i != PACKCR_VOID_VALUE) {\n        packcr_lr_memo__destroy(ctx, map->buf[i].memo);\n        map->buf[i].memo = memo;\n    }\n    else {\n        if (map->max <= map->len) {\n            const size_t n = map->len + 1;\n            size_t m = map->max;\n            if (m == 0) m = PACKCR_ARRAY_MIN_SIZE;\n            while (m < n && m != 0) m <<= 1;\n            if (m == 0) m = n;\n            map->buf = (packcr_lr_memo_map_entry_t *)PACKCR_REALLOC(ctx->auxil, map->buf, sizeof(packcr_lr_memo_map_entry_t) * m);\n            map->max = m;\n        }\n        map->buf[map->len].rule = rule;\n        map->buf[map->len].memo = memo;\n        map->len++;\n    }\n}\n\nstatic packcr_lr_memo_t *packcr_lr_memo_map__get(packcr_context_t *ctx, packcr_lr_memo_map_t *map, packcr_rule_t rule) {\n    const size_t i = packcr_lr_memo_map__index(ctx, map, rule);\n    return (i != PACKCR_VOID_VALUE) ? map->buf[i].memo : NULL;\n}\n\nstatic void packcr_lr_memo_map__term(packcr_context_t *ctx, packcr_lr_memo_map_t *map) {\n    while (map->len > 0) {\n        map->len--;\n        packcr_lr_memo__destroy(ctx, map->buf[map->len].memo);\n    }\n    PACKCR_FREE(ctx->auxil, map->buf);\n}\n\nstatic packcr_lr_memo_map_t *packcr_lr_memo_map__create(packcr_context_t *ctx) {\n    packcr_lr_memo_map_t *const memo = (packcr_lr_memo_map_t *)PACKCR_MALLOC(ctx->auxil, sizeof(packcr_lr_memo_map_t));\n    packcr_lr_memo_map__init(ctx->auxil, memo);\n    return memo;\n}\n\nstatic void packcr_lr_memo_map__destroy(packcr_context_t *ctx, packcr_lr_memo_map_t *memo) {\n    if (memo == NULL) return;\n    packcr_lr_memo_map__term(ctx, memo);\n    PACKCR_FREE(ctx->auxil, memo);\n}\n\nstatic void packcr_lr_table__init(packcr_auxil_t auxil, packcr_lr_table_t *table) {\n    table->ofs = 0;\n    table->len = 0;\n    table->max = 0;\n    table->buf = NULL;\n}\n\nstatic void packcr_lr_table__resize(packcr_context_t *ctx, packcr_lr_table_t *table, size_t len) {\n    size_t i;\n    for (i = len; i < table->len; i++) packcr_lr_memo_map__destroy(ctx, table->buf[i]);\n    if (table->max < len) {\n        size_t m = table->max;\n        if (m == 0) m = PACKCR_ARRAY_MIN_SIZE;\n        while (m < len && m != 0) m <<= 1;\n        if (m == 0) m = len;\n        table->buf = (packcr_lr_memo_map_t **)PACKCR_REALLOC(ctx->auxil, table->buf, sizeof(packcr_lr_memo_map_t *) * m);\n        table->max = m;\n    }\n    for (i = table->len; i < len; i++) table->buf[i] = NULL;\n    table->len = len;\n}\n\nstatic void packcr_lr_table__set_memo(packcr_context_t *ctx, packcr_lr_table_t *table, size_t index, packcr_rule_t rule, packcr_lr_memo_t *memo) {\n    index += table->ofs;\n    if (index >= table->len) packcr_lr_table__resize(ctx, table, index + 1);\n    if (table->buf[index] == NULL) table->buf[index] = packcr_lr_memo_map__create(ctx);\n    packcr_lr_memo_map__put(ctx, table->buf[index], rule, memo);\n}\n\nstatic packcr_lr_memo_t *packcr_lr_table__get_memo(packcr_context_t *ctx, packcr_lr_table_t *table, size_t index, packcr_rule_t rule) {\n    index += table->ofs;\n    if (index >= table->len || table->buf[index] == NULL) return NULL;\n    return packcr_lr_memo_map__get(ctx, table->buf[index], rule);\n}\n\nstatic void packcr_lr_table__shift(packcr_context_t *ctx, packcr_lr_table_t *table, size_t count) {\n    size_t i;\n    if (count > table->len - table->ofs) count = table->len - table->ofs;\n    for (i = 0; i < count; i++) packcr_lr_memo_map__destroy(ctx, table->buf[table->ofs++]);\n    if (table->ofs > (table->max >> 1)) {\n        memmove(table->buf, table->buf + table->ofs, sizeof(packcr_lr_memo_map_t *) * (table->len - table->ofs));\n        table->len -= table->ofs;\n        table->ofs = 0;\n    }\n}\n\nstatic void packcr_lr_table__term(packcr_context_t *ctx, packcr_lr_table_t *table) {\n    while (table->len > table->ofs) {\n        table->len--;\n        packcr_lr_memo_map__destroy(ctx, table->buf[table->len]);\n    }\n    PACKCR_FREE(ctx->auxil, table->buf);\n}\n\nstatic packcr_context_t *packcr_context__create(packcr_auxil_t auxil) {\n    packcr_context_t *const ctx = (packcr_context_t *)PACKCR_MALLOC(auxil, sizeof(packcr_context_t));\n    ctx->buffer_start_position = 0;\n    ctx->position_offset = 0;\n".freeze

    if @location
      erbout << "    packcr_location_init(&ctx->buffer_start_position_loc);\n    packcr_location_init(&ctx->position_offset_loc);\n".freeze
    end
    erbout << "    ctx->level = 0;\n    packcr_char_array__init(auxil, &ctx->buffer);\n    packcr_lr_table__init(auxil, &ctx->lrtable);\n    packcr_thunk_array__init(auxil, &ctx->thunks);\n    packcr_memory_recycler__init(auxil, &ctx->thunk_chunk_recycler, sizeof(packcr_thunk_chunk_t));\n    packcr_memory_recycler__init(auxil, &ctx->lr_memo_recycler, sizeof(packcr_lr_memo_t));\n    ctx->auxil = auxil;\n    return ctx;\n}\n\nstatic void packcr_context__destroy(packcr_context_t *ctx) {\n    if (ctx == NULL) return;\n    packcr_thunk_array__term(ctx->auxil, &ctx->thunks);\n    packcr_lr_table__term(ctx, &ctx->lrtable);\n    packcr_char_array__term(ctx->auxil, &ctx->buffer);\n    packcr_memory_recycler__term(ctx->auxil, &ctx->thunk_chunk_recycler);\n    packcr_memory_recycler__term(ctx->auxil, &ctx->lr_memo_recycler);\n    PACKCR_FREE(ctx->auxil, ctx);\n}\n\nstatic size_t packcr_refill_buffer(packcr_context_t *ctx, size_t num) {\n    if (ctx->buffer.len >= ctx->position_offset + num) return ctx->buffer.len - ctx->position_offset;\n    while (ctx->buffer.len < ctx->position_offset + num) {\n        const int c = PACKCR_GETCHAR(ctx->auxil);\n        if (c < 0) break;\n        packcr_char_array__add(ctx->auxil, &ctx->buffer, (char)c);\n    }\n    return ctx->buffer.len - ctx->position_offset;\n}\n\nMARK_FUNC_AS_USED\nstatic void packcr_commit_buffer(packcr_context_t *ctx) {\n    memmove(ctx->buffer.buf, ctx->buffer.buf + ctx->position_offset, ctx->buffer.len - ctx->position_offset);\n    ctx->buffer.len -= ctx->position_offset;\n    ctx->buffer_start_position += ctx->position_offset;\n    packcr_lr_table__shift(ctx, &ctx->lrtable, ctx->position_offset);\n    ctx->position_offset = 0;\n".freeze

    if @location
      erbout << "    ctx->buffer_start_position_loc = packcr_location_add(ctx->buffer_start_position_loc, ctx->position_offset_loc);\n    packcr_location_init(&ctx->position_offset_loc);\n".freeze
    end
    erbout << "}\n\nMARK_FUNC_AS_USED\nstatic const char *packcr_get_capture_string(packcr_context_t *ctx, const packcr_capture_t *capt) {\n    if (capt->string == NULL)\n        ((packcr_capture_t *)capt)->string =\n            packcr_strndup_e(ctx->auxil, ctx->buffer.buf + capt->range.start, capt->range.end - capt->range.start);\n    return capt->string;\n}\n\n".freeze

    if @utf8
      erbout << "static size_t packcr_get_char_as_utf32(packcr_context_t *ctx, int *out) { /* with checking UTF-8 validity */\n    int c, u;\n    size_t n;\n    if (packcr_refill_buffer(ctx, 1) < 1) return 0;\n    c = (int)(unsigned char)ctx->buffer.buf[ctx->position_offset];\n    n = (c < 0x80) ? 1 :\n        ((c & 0xe0) == 0xc0) ? 2 :\n        ((c & 0xf0) == 0xe0) ? 3 :\n        ((c & 0xf8) == 0xf0) ? 4 : 0;\n    if (n < 1) return 0;\n    if (packcr_refill_buffer(ctx, n) < n) return 0;\n    switch (n) {\n    case 1:\n        u = c;\n        break;\n    case 2:\n        u = c & 0x1f;\n        c = (int)(unsigned char)ctx->buffer.buf[ctx->position_offset + 1];\n        if ((c & 0xc0) != 0x80) return 0;\n        u <<= 6; u |= c & 0x3f;\n        if (u < 0x80) return 0;\n        break;\n    case 3:\n        u = c & 0x0f;\n        c = (int)(unsigned char)ctx->buffer.buf[ctx->position_offset + 1];\n        if ((c & 0xc0) != 0x80) return 0;\n        u <<= 6; u |= c & 0x3f;\n        c = (int)(unsigned char)ctx->buffer.buf[ctx->position_offset + 2];\n        if ((c & 0xc0) != 0x80) return 0;\n        u <<= 6; u |= c & 0x3f;\n        if (u < 0x800) return 0;\n        break;\n    case 4:\n        u = c & 0x07;\n        c = (int)(unsigned char)ctx->buffer.buf[ctx->position_offset + 1];\n        if ((c & 0xc0) != 0x80) return 0;\n        u <<= 6; u |= c & 0x3f;\n        c = (int)(unsigned char)ctx->buffer.buf[ctx->position_offset + 2];\n        if ((c & 0xc0) != 0x80) return 0;\n        u <<= 6; u |= c & 0x3f;\n        c = (int)(unsigned char)ctx->buffer.buf[ctx->position_offset + 3];\n        if ((c & 0xc0) != 0x80) return 0;\n        u <<= 6; u |= c & 0x3f;\n        if (u < 0x10000 || u > 0x10ffff) return 0;\n        break;\n    default:\n        return 0;\n    }\n    if (out) *out = u;\n    return n;\n}\n\n".freeze
    end
    erbout << "static void packcr_grow_lr(packcr_context_t *ctx, packcr_rule_t rule, size_t offset".freeze
    if @location
      erbout << ", packcr_location_t offset_loc".freeze
    end
    erbout << ") {\n    while(1) {\n        const size_t old_offset = ctx->position_offset;\n        packcr_thunk_chunk_t *chunk;\n        packcr_lr_memo_t *memo;\n        packcr_rule_set_t limits;\n        ctx->position_offset = offset;\n".freeze

    if @location
      erbout << "        ctx->position_offset_loc = offset_loc;\n".freeze
    end
    erbout << "        packcr_rule_set__init(ctx->auxil, &limits);\n        packcr_rule_set__add(ctx->auxil, &limits, rule);\n        chunk = rule(ctx, offset".freeze

    if @location
      erbout << ", offset_loc".freeze
    end
    erbout << ", &limits);\n        packcr_rule_set__term(ctx->auxil, &limits);\n        if (!chunk)\n            break;\n        if (ctx->position_offset <= old_offset) {\n            packcr_thunk_chunk__destroy(ctx, chunk);\n            break;\n        }\n        memo = packcr_lr_table__get_memo(ctx, &ctx->lrtable, offset, rule);\n        packcr_lr_memo__set_chunk(ctx, memo, chunk);\n        memo->offset = ctx->position_offset;\n".freeze

    if @location
      erbout << "        memo->offset_loc = ctx->position_offset_loc;\n".freeze
    end
    erbout << "    }\n}\n\nMARK_FUNC_AS_USED\nstatic packcr_thunk_chunk_t *packcr_get_rule_thunk_chunk(packcr_context_t *ctx, packcr_rule_t rule) {\n    packcr_thunk_chunk_t *c = NULL;\n    size_t offset = ctx->position_offset;\n".freeze

    if @location
      erbout << "    packcr_location_t offset_loc = ctx->position_offset_loc;\n".freeze
    end
    erbout << "    packcr_lr_memo_t *memo = packcr_lr_table__get_memo(ctx, &ctx->lrtable, offset, rule);\n\n    if (memo == NULL) {\n        memo = packcr_lr_memo__create(ctx, offset".freeze

    if @location
      erbout << ", offset_loc".freeze
    end
    erbout << ");\n        packcr_lr_table__set_memo(ctx, &ctx->lrtable, offset, rule, memo);\n        c = rule(ctx, offset".freeze

    if @location
      erbout << ", offset_loc".freeze
    end
    erbout << ", NULL);\n        packcr_lr_memo__set_chunk(ctx, memo, c);\n        memo->offset = ctx->position_offset;\n".freeze

    if @location
      erbout << "        memo->offset_loc = ctx->position_offset_loc;\n".freeze
    end
    erbout << "        if (memo->grow) {\n            packcr_grow_lr(ctx, rule, offset".freeze

    if @location
      erbout << ", offset_loc".freeze
    end
    erbout << ");\n            memo->grow = PACKCR_FALSE;\n            ctx->position_offset = memo->offset;\n".freeze

    if @location
      erbout << "            ctx->position_offset_loc = memo->offset_loc;\n".freeze
    end
    erbout << "            return memo->chunk;\n        }\n        return c;\n    } else if (memo->fail) {\n        packcr_lr_memo__set_chunk(ctx, memo, NULL);\n        memo->grow = PACKCR_TRUE;\n        return NULL;\n    }\n    ctx->position_offset = memo->offset;\n".freeze

    if @location
      erbout << "    ctx->position_offset_loc = memo->offset_loc;\n".freeze
    end
    erbout << "    return memo->chunk;\n}\n\nMARK_FUNC_AS_USED\nstatic packcr_bool_t packcr_apply_rule(packcr_context_t *ctx, packcr_rule_t rule, packcr_thunk_array_t *thunks, packcr_value_t *value, size_t offset".freeze

    if @location
      erbout << ", packcr_location_t offset_loc".freeze
    end
    erbout << ", packcr_rule_set_t *limits) {\n    static packcr_value_t null;\n    packcr_thunk_chunk_t *c;\n    if (limits != NULL) {\n        packcr_lr_memo_t *memo;\n        packcr_rule_set__add(ctx->auxil, limits, rule);\n        c = rule(ctx, offset".freeze

    if @location
      erbout << ", offset_loc".freeze
    end
    erbout << ", limits);\n        memo = packcr_lr_table__get_memo(ctx, &ctx->lrtable, offset, rule);\n        if (memo == NULL || ctx->position_offset <= memo->offset) {\n            if (memo) {\n                c = memo->chunk;\n                ctx->position_offset = memo->offset;\n".freeze

    if @location
      erbout << "                ctx->position_offset_loc = memo->offset_loc;\n".freeze
    end
    erbout << "            }\n        } else {\n            packcr_lr_memo__set_chunk(ctx, memo, c);\n            memo->offset = ctx->position_offset;\n".freeze

    if @location
      erbout << "            memo->offset_loc = ctx->position_offset_loc;\n".freeze
    end
    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

    @root.rules.each do |rule|
      rule.codes.each do |code|
        erbout << "static void packcr_action_#{rule.name}_#{code.index}(#{prefix}_context_t *__packcr_ctx, packcr_thunk_t *__packcr_in, packcr_value_t *__packcr_out) {\n#define auxil (__packcr_ctx->auxil)\n#define __ (*__packcr_out)\n".freeze

        code.vars.each do |ref|
          erbout << "#define #{ref.var} (*__packcr_in->data.leaf.values.buf[#{ref.index}])\n".freeze
        end
        erbout << "#define _0 packcr_get_capture_string(__packcr_ctx, &__packcr_in->data.leaf.capt0)\n#define _0s ((const size_t)(__packcr_ctx->buffer_start_position + __packcr_in->data.leaf.capt0.range.start))\n#define _0e ((const size_t)(__packcr_ctx->buffer_start_position + __packcr_in->data.leaf.capt0.range.end))\n".freeze

        if @location
          erbout << "#define _0sl ((const packcr_location_t)(packcr_location_add(__packcr_ctx->buffer_start_position_loc, __packcr_in->data.leaf.capt0.range.start_loc)))\n#define _0el ((const packcr_location_t)(packcr_location_add(__packcr_ctx->buffer_start_position_loc, __packcr_in->data.leaf.capt0.range.end_loc)))\n".freeze
        end
        if @capture_in_code
          erbout << "#define _0c __packcr_in->data.leaf.capt0\n".freeze
        end
        code.capts.each do |capture|
          erbout << "#define _#{capture.index + 1} packcr_get_capture_string(__packcr_ctx, __packcr_in->data.leaf.capts.buf[#{capture.index}])\n#define _#{capture.index + 1}s ((const size_t)(__packcr_ctx->buffer_start_position + __packcr_in->data.leaf.capts.buf[#{capture.index}]->range.start))\n#define _#{capture.index + 1}e ((const size_t)(__packcr_ctx->buffer_start_position + __packcr_in->data.leaf.capts.buf[#{capture.index}]->range.end))\n".freeze

          if @location
            erbout << "#define _#{capture.index + 1}sl ((const packcr_location_t)(packcr_location_add(__packcr_ctx->buffer_start_position_loc, __packcr_in->data.leaf.capts.buf[#{capture.index}]->range.start_loc)))\n#define _#{capture.index + 1}el ((const packcr_location_t)(packcr_location_add(__packcr_ctx->buffer_start_position_loc, __packcr_in->data.leaf.capts.buf[#{capture.index}]->range.end_loc)))\n".freeze
          end
          next unless @capture_in_code

          erbout << "#define _#{capture.index + 1}c (*__packcr_in->data.leaf.capts.buf[#{capture.index}])\n".freeze
        end
        erbout << "#{stream.get_code_block(code.code, 4, @iname)}".freeze
        code.capts.reverse_each do |capture|
          if @location
            erbout << "#undef _#{capture.index + 1}el\n#undef _#{capture.index + 1}sl\n".freeze
          end
          erbout << "#undef _#{capture.index + 1}e\n#undef _#{capture.index + 1}s\n#undef _#{capture.index + 1}\n".freeze
        end
        erbout << "#undef _0e\n#undef _0s\n#undef _0\n".freeze

        code.vars.reverse_each do |ref|
          erbout << "#undef #{ref.var}\n".freeze
        end
        erbout << "#undef __\n#undef auxil\n}\n\n".freeze
      end
    end
    @root.rules.each do |rule|
      erbout << "static packcr_thunk_chunk_t *packcr_evaluate_rule_#{rule.name}(packcr_context_t *ctx, size_t offset".freeze
      if @location
        erbout << ", packcr_location_t offset_loc".freeze
      end
      erbout << ", packcr_rule_set_t *limits);\n".freeze
    end
    erbout << "\n".freeze

    @root.rules.each do |rule|
      gen = ::Packcr::Generator.new(rule, @ascii, @location)
      erbout << "#{gen.generate_code(rule, 0, 0, false)}\n".freeze
    end
    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

    if !@root.rules.empty?
      erbout << "    if (packcr_apply_rule(ctx, packcr_evaluate_rule_#{@root.rules[0].name}, &ctx->thunks, ret, ctx->position_offset".freeze
      if @location
        erbout << ", ctx->position_offset_loc".freeze
      end
      erbout << ", NULL))\n        packcr_do_action(ctx, &ctx->thunks, ret);\n    else\n        PACKCR_ERROR(ctx->auxil);\n    packcr_commit_buffer(ctx);\n".freeze
    end
    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

    if !code(:lsource).empty?
      erbout << "\n".freeze

      code(:lsource).each do |code|
        erbout << "#{stream.get_code_block(code, 0, @iname)}".freeze
      end
    end
    erbout
  when :rb
    erbout = +""
    erbout << "# A packrat parser generated by PackCR #{Packcr::VERSION}\n".freeze

    if !code(:esource).empty?
      erbout << "\n".freeze

      code(:esource).each do |code|
        erbout << "#{stream.get_code_block(code, 0, @iname)}".freeze
      end
    end
    erbout << "class #{class_name}\n".freeze

    code(:location).each do |code|
      erbout << "#{stream.get_code_block(code, 2, @iname)}\n".freeze
    end
    code(:source).each do |code|
      erbout << "  #{stream.get_code_block(code, 2, @iname)}\n".freeze
    end
    erbout << "  def initialize(".freeze
    if @auxil_type
      erbout << "#{auxil_type}, ".freeze
    end
    erbout << "debug: false)\n".freeze

    if @utf8
      erbout << "    @buffer = +\"\"\n".freeze

    else
      erbout << "    @buffer = +\"\".b\n".freeze
    end
    erbout << "\n    @buffer_start_position = 0\n    @position_offset = 0\n    @level = 0\n    @thunk = ThunkNode.new([], nil, 0)\n    @memos = LrMemoTable.new\n    @debug = debug\n    @global_values = {}\n".freeze

    if @location
      erbout << "    @buffer_start_position_loc = Location.new\n    @position_offset_loc = Location.new\n".freeze
    end
    code(:init).each do |code|
      erbout << "#{stream.get_code_block(code, 4, @iname)}".freeze
    end
    erbout << "  end\n\n  def debug\n    yield if @debug\n  end\n\n  def getc\n".freeze

    if @utf8
      erbout << "    $stdin.getc\n".freeze

    else
      erbout << "    $stdin.getc&.b\n".freeze
    end
    erbout << "  end\n\n  def refill_buffer(num, mode = nil)\n    len = @buffer.length\n    if len >= @position_offset + num\n      return len - @position_offset\n    end\n    while len < @position_offset + num\n      c = getc\n      break if !c\n      @buffer << c\n      len = @buffer.length\n    end\n    return len - @position_offset\n  end\n\n  def commit_buffer\n    @buffer = @buffer[@position_offset, @buffer.length - @position_offset]\n    @buffer_start_position += @position_offset\n    @memos.clear\n    @position_offset = 0\n".freeze

    if @location
      erbout << "    @buffer_start_position_loc = @buffer_start_position_loc + @position_offset_loc\n    @position_offset_loc = Location.new\n".freeze
    end
    erbout << "  end\n\n  def parse\n    pos = @buffer_start_position\n".freeze

    if !@root.rules.empty?
      erbout << "    if apply_rule(:evaluate_rule_#{@root.rules[0].name}, @thunk.thunks, nil, 0, @buffer_start_position".freeze
      if @location
        erbout << ", @buffer_start_position_loc".freeze
      end
      erbout << ")\n      @thunk.do_action(self, nil, 0)\n    else\n      raise SyntaxError, \"can't parse\"\n    end\n    commit_buffer\n".freeze
    end
    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

    @root.rules.each do |rule|
      rule.codes.each do |code|
        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

        code.vars.each do |ref|
          erbout << "    #{ref.var} = (__packcr_in.value_refs[#{ref.index}]  ||= Value.new).value\n".freeze
        end
        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

        if @location
          erbout << "    __0sl = @buffer_start_position_loc + __packcr_in.capt0.start_loc\n    __0el = @buffer_start_position_loc + __packcr_in.capt0.end_loc\n".freeze
        end
        if @capture_in_code
          erbout << "    __0c = __packcr_in.capt0\n".freeze
        end
        code.capts.each do |capture|
          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

          if @location
            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
          end
          next unless @capture_in_code

          erbout << "    __#{capture.index + 1}c = __packcr_in.capts[#{capture.index}]\n".freeze
        end

        erbout << "#{stream.get_code_block(code.code, 4, @iname)}\n    __packcr_vars[__packcr_index].value = ____ if __packcr_vars\n  end\n\n".freeze
      end
    end
    @root.rules.each do |rule|
      gen = ::Packcr::Generator.new(rule, @ascii, @location, :rb)

      erbout << "#{gen.generate_code(rule, 0, 2, false)}\n".freeze
    end
    erbout << "  def grow_lr(rule, offset".freeze
    if @location
      erbout << ", offset_loc".freeze
    end
    erbout << ")\n    while true\n      old_offset = @position_offset\n      @position_offset = offset\n".freeze

    if @location
      erbout << "      @position_offset_loc = offset_loc\n".freeze
    end
    erbout << "      answer = public_send(rule, offset".freeze
    if @location
      erbout << ", offset_loc".freeze
    end
    erbout << ", limits: {rule => true})\n      if !answer || @position_offset <= old_offset\n        break\n      end\n      memo = @memos[offset, rule]\n      memo.answer = answer\n      memo.offset = @position_offset\n".freeze

    if @location
      erbout << "      memo.offset_loc = @position_offset_loc\n".freeze
    end
    erbout << "    end\n  end\n\n  def rule_answer(rule)\n    offset = @position_offset\n".freeze

    if @location
      erbout << "    offset_loc = @position_offset_loc\n".freeze
    end
    erbout << "    memo = @memos[offset, rule]\n\n    if !memo\n      memo = LrMemo.new(offset".freeze

    if @location
      erbout << ", offset_loc".freeze
    end
    erbout << ")\n      @memos[offset, rule] = memo\n      answer = public_send(rule, offset".freeze

    if @location
      erbout << ", offset_loc".freeze
    end
    erbout << ")\n      memo.answer = answer\n      memo.offset = @position_offset\n".freeze

    if @location
      erbout << "      memo.offset_loc = @position_offset_loc\n".freeze
    end
    erbout << "      if memo.grow\n        grow_lr(rule, offset".freeze

    if @location
      erbout << ", offset_loc".freeze
    end
    erbout << ")\n        memo.grow = false\n        answer = memo.answer\n        @position_offset = memo.offset\n".freeze

    if @location
      erbout << "        @position_offset_loc = memo.offset_loc\n".freeze
    end
    erbout << "      end\n      return answer\n    elsif memo.fail\n      memo.answer = nil\n      memo.grow = true\n      return nil\n    else\n      @position_offset = memo.offset\n".freeze

    if @location
      erbout << "      @position_offset_loc = memo.offset_loc\n".freeze
    end
    erbout << "      return memo.answer\n    end\n  end\n\n  def apply_rule(rule, thunks, values, index, offset".freeze
    if @location
      erbout << ", offset_loc".freeze
    end
    erbout << ", limits: nil)\n    if limits\n      limits = limits.merge(rule => true)\n      answer = public_send(rule, offset".freeze

    if @location
      erbout << ", offset_loc".freeze
    end
    erbout << ", limits: limits)\n      memo = @memos[offset, rule]\n      if !answer || @position_offset <= memo.offset\n        if memo\n          answer = memo.answer\n          @position_offset = memo.offset\n".freeze

    if @location
      erbout << "          @position_offset_loc = memo.offset_loc\n".freeze
    end
    erbout << "        end\n      else\n        memo.answer = answer\n        memo.offset = @position_offset\n".freeze

    if @location
      erbout << "        memo.offset_loc = @position_offset_loc\n".freeze
    end
    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

    if @location
      erbout << "    attr_accessor :offset_loc\n".freeze
    end
    erbout << "\n    def initialize(offset".freeze
    if @location
      erbout << ", offset_loc".freeze
    end
    erbout << ")\n      @offset = offset\n".freeze

    if @location
      erbout << "      @offset_loc = offset_loc\n".freeze
    end
    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

    if @location
      erbout << "    attr_accessor :pos_loc\n".freeze
    end
    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

    if @location
      erbout << "    attr_accessor :start_loc, :end_loc\n".freeze
    end
    erbout << "\n    def initialize(range_start = 0, range_end = 0".freeze
    if @location
      erbout << ", start_loc = nil, end_loc = nil".freeze
    end
    erbout << ")\n      @range_start = range_start\n      @range_end = range_end\n".freeze

    if @location
      erbout << "      @start_loc = start_loc || Location.new\n      @end_loc = end_loc || Location.new\n".freeze
    end
    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

    if !code(:lsource).empty?
      erbout << "\n".freeze

      code(:lsource).each do |code|
        erbout << "#{stream.get_code_block(code, 0, @iname)}".freeze
      end
    end
    erbout
  end
end

#inspectObject



69
70
71
# File 'lib/packcr/context.rb', line 69

def inspect
  format("#<%s:0x%016x>", self.class, object_id)
end

#parse_allObject



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/packcr/context.rb', line 121

def parse_all
  File.open(@iname, "rb") do |r|
    parser = Packcr::Parser.new(self, r, debug: @debug)
    nil while parser.parse
  end

  if !code(:location).empty?
    @location = true
  end

  @root.setup(self)

  if @debug
    @root.debug_dump
    dump_options
  end

  @errnum.zero?
end

#pass_value_code(var) ⇒ Object



90
91
92
93
94
95
96
97
# File 'lib/packcr/context.rb', line 90

def pass_value_code(var)
  case @lang
  when :c
    "__ = #{var};"
  when :rb
    "____ = #{var}"
  end
end

#value_defObject



108
109
110
111
# File 'lib/packcr/context.rb', line 108

def value_def
  type = value_type
  "#{type}#{type =~ /\*$/ ? "" : " "}"
end