ebnf 1.1.2 → 2.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 (56) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +218 -196
  3. data/UNLICENSE +1 -1
  4. data/VERSION +1 -1
  5. data/bin/ebnf +40 -21
  6. data/etc/abnf-core.ebnf +52 -0
  7. data/etc/abnf.abnf +121 -0
  8. data/etc/abnf.ebnf +124 -0
  9. data/etc/abnf.sxp +45 -0
  10. data/etc/doap.ttl +23 -15
  11. data/etc/ebnf.ebnf +21 -33
  12. data/etc/ebnf.html +171 -160
  13. data/etc/{ebnf.rb → ebnf.ll1.rb} +30 -107
  14. data/etc/ebnf.ll1.sxp +182 -183
  15. data/etc/ebnf.peg.rb +90 -0
  16. data/etc/ebnf.peg.sxp +84 -0
  17. data/etc/ebnf.sxp +40 -41
  18. data/etc/iso-ebnf.ebnf +140 -0
  19. data/etc/iso-ebnf.isoebnf +138 -0
  20. data/etc/iso-ebnf.sxp +65 -0
  21. data/etc/sparql.ebnf +4 -4
  22. data/etc/sparql.html +1603 -1751
  23. data/etc/sparql.ll1.sxp +7372 -7372
  24. data/etc/sparql.peg.rb +532 -0
  25. data/etc/sparql.peg.sxp +597 -0
  26. data/etc/sparql.sxp +363 -362
  27. data/etc/turtle.ebnf +3 -3
  28. data/etc/turtle.html +465 -517
  29. data/etc/{turtle.rb → turtle.ll1.rb} +3 -4
  30. data/etc/turtle.ll1.sxp +425 -425
  31. data/etc/turtle.peg.rb +182 -0
  32. data/etc/turtle.peg.sxp +199 -0
  33. data/etc/turtle.sxp +103 -101
  34. data/lib/ebnf.rb +7 -2
  35. data/lib/ebnf/abnf.rb +301 -0
  36. data/lib/ebnf/abnf/core.rb +23 -0
  37. data/lib/ebnf/abnf/meta.rb +111 -0
  38. data/lib/ebnf/base.rb +128 -87
  39. data/lib/ebnf/bnf.rb +1 -26
  40. data/lib/ebnf/ebnf/meta.rb +90 -0
  41. data/lib/ebnf/isoebnf.rb +229 -0
  42. data/lib/ebnf/isoebnf/meta.rb +75 -0
  43. data/lib/ebnf/ll1.rb +140 -8
  44. data/lib/ebnf/ll1/lexer.rb +37 -32
  45. data/lib/ebnf/ll1/parser.rb +113 -73
  46. data/lib/ebnf/ll1/scanner.rb +83 -51
  47. data/lib/ebnf/native.rb +320 -0
  48. data/lib/ebnf/parser.rb +285 -302
  49. data/lib/ebnf/peg.rb +39 -0
  50. data/lib/ebnf/peg/parser.rb +561 -0
  51. data/lib/ebnf/peg/rule.rb +241 -0
  52. data/lib/ebnf/rule.rb +453 -163
  53. data/lib/ebnf/terminals.rb +21 -0
  54. data/lib/ebnf/writer.rb +561 -88
  55. metadata +114 -28
  56. data/etc/sparql.rb +0 -45773
@@ -29,7 +29,7 @@ module EBNF::LL1
29
29
  # warn error.inspect
30
30
  # end
31
31
  #
32
- # @see http://en.wikipedia.org/wiki/Lexical_analysis
32
+ # @see https://en.wikipedia.org/wiki/Lexical_analysis
33
33
  class Lexer
34
34
  include Enumerable
35
35
 
@@ -43,10 +43,10 @@ module EBNF::LL1
43
43
  "\\'" => '\'', # \u0027 (apostrophe-quote, single quote mark)
44
44
  '\\\\' => '\\' # \u005C (backslash)
45
45
  }.freeze
46
- ESCAPE_CHAR4 = /\\u(?:[0-9A-Fa-f]{4,4})/.freeze # \uXXXX
47
- ESCAPE_CHAR8 = /\\U(?:[0-9A-Fa-f]{8,8})/.freeze # \UXXXXXXXX
48
- ECHAR = /\\./ # More liberal unescaping
49
- UCHAR = /#{ESCAPE_CHAR4}|#{ESCAPE_CHAR8}/.freeze
46
+ ESCAPE_CHAR4 = /\\u(?:[0-9A-Fa-f]{4,4})/u.freeze # \uXXXX
47
+ ESCAPE_CHAR8 = /\\U(?:[0-9A-Fa-f]{8,8})/u.freeze # \UXXXXXXXX
48
+ ECHAR = /\\./u.freeze # More liberal unescaping
49
+ UCHAR = /#{ESCAPE_CHAR4}|#{ESCAPE_CHAR8}/n.freeze
50
50
 
51
51
  ##
52
52
  # @return [Regexp] defines whitespace, including comments, otherwise whitespace must be explicit in terminals
@@ -59,7 +59,7 @@ module EBNF::LL1
59
59
  #
60
60
  # @param [String] string
61
61
  # @return [String]
62
- # @see http://www.w3.org/TR/rdf-sparql-query/#codepointEscape
62
+ # @see https://www.w3.org/TR/rdf-sparql-query/#codepointEscape
63
63
  def self.unescape_codepoints(string)
64
64
  string = string.dup
65
65
  string.force_encoding(Encoding::ASCII_8BIT) if string.respond_to?(:force_encoding)
@@ -81,7 +81,7 @@ module EBNF::LL1
81
81
  #
82
82
  # @param [String] input
83
83
  # @return [String]
84
- # @see http://www.w3.org/TR/rdf-sparql-query/#grammarEscapes
84
+ # @see https://www.w3.org/TR/rdf-sparql-query/#grammarEscapes
85
85
  def self.unescape_string(input)
86
86
  input.gsub(ECHAR) { |escaped| ESCAPE_CHARS[escaped] || escaped[1..-1]}
87
87
  end
@@ -98,8 +98,8 @@ module EBNF::LL1
98
98
  # @yieldparam [Lexer] lexer
99
99
  # @return [Lexer]
100
100
  # @raise [Lexer::Error] on invalid input
101
- def self.tokenize(input, terminals, options = {}, &block)
102
- lexer = self.new(input, terminals, options)
101
+ def self.tokenize(input, terminals, **options, &block)
102
+ lexer = self.new(input, terminals, **options)
103
103
  block_given? ? block.call(lexer) : lexer
104
104
  end
105
105
 
@@ -115,17 +115,23 @@ module EBNF::LL1
115
115
  # Whitespace between tokens, including comments
116
116
  # @option options[Integer] :high_water passed to scanner
117
117
  # @option options[Integer] :low_water passed to scanner
118
- def initialize(input = nil, terminals = nil, options = {})
118
+ def initialize(input = nil, terminals = nil, **options)
119
119
  @options = options.dup
120
120
  @whitespace = @options[:whitespace]
121
121
  @terminals = terminals.map do |term|
122
- term.is_a?(Array) ? Terminal.new(*term) : term
122
+ if term.is_a?(Array) && term.length ==3
123
+ # Last element is options
124
+ Terminal.new(term[0], term[1], **term[2])
125
+ elsif term.is_a?(Array)
126
+ Terminal.new(*term)
127
+ else
128
+ term
129
+ end
123
130
  end
124
131
 
125
132
  raise Error, "Terminal patterns not defined" unless @terminals && @terminals.length > 0
126
133
 
127
- @lineno = 1
128
- @scanner = Scanner.new(input, options)
134
+ @scanner = Scanner.new(input, **options)
129
135
  end
130
136
 
131
137
  ##
@@ -140,12 +146,6 @@ module EBNF::LL1
140
146
  # @return [String]
141
147
  attr_accessor :input
142
148
 
143
- ##
144
- # The current line number (zero-based).
145
- #
146
- # @return [Integer]
147
- attr_reader :lineno
148
-
149
149
  ##
150
150
  # Returns `true` if the input string is lexically valid.
151
151
  #
@@ -187,7 +187,7 @@ module EBNF::LL1
187
187
 
188
188
  @first ||= begin
189
189
  {} while !scanner.eos? && skip_whitespace
190
- return @scanner = nil if scanner.eos?
190
+ return nil if scanner.eos?
191
191
 
192
192
  token = match_token(*types)
193
193
 
@@ -226,7 +226,7 @@ module EBNF::LL1
226
226
  # @return [Token]
227
227
  def recover(*types)
228
228
  until scanner.eos? || tok = match_token(*types)
229
- if scanner.skip_until(@whitespace || /\s/m).nil? # Skip past current "token"
229
+ if scanner.skip_until(@whitespace || /\s+/m).nil? # Skip past current "token"
230
230
  # No whitespace at the end, must be and end of string
231
231
  scanner.terminate
232
232
  else
@@ -236,6 +236,14 @@ module EBNF::LL1
236
236
  scanner.unscan if tok
237
237
  first
238
238
  end
239
+
240
+ ##
241
+ # The current line number (one-based).
242
+ #
243
+ # @return [Integer]
244
+ def lineno
245
+ scanner.lineno
246
+ end
239
247
  protected
240
248
 
241
249
  # @return [StringScanner]
@@ -246,9 +254,7 @@ module EBNF::LL1
246
254
  def skip_whitespace
247
255
  # skip all white space, but keep track of the current line number
248
256
  while @whitespace && !scanner.eos?
249
- if matched = scanner.scan(@whitespace)
250
- @lineno += matched.count("\n")
251
- else
257
+ unless scanner.scan(@whitespace)
252
258
  return
253
259
  end
254
260
  end
@@ -274,7 +280,6 @@ module EBNF::LL1
274
280
  if matched = scanner.scan(term.regexp)
275
281
  #STDERR.puts " matched #{term.type.inspect}: #{matched.inspect}"
276
282
  tok = token(term.type, term.canonicalize(matched))
277
- @lineno += matched.count("\n")
278
283
  return tok
279
284
  end
280
285
  end
@@ -300,7 +305,7 @@ module EBNF::LL1
300
305
  # Cause strings and codepoints to be unescaped.
301
306
  # @option options [Regexp] :partial_regexp
302
307
  # A regular expression matching the beginning of this terminal; useful for terminals that match things longer than the scanner low water mark.
303
- def initialize(type, regexp, options = {})
308
+ def initialize(type, regexp, **options)
304
309
  @type, @regexp, @options = type, regexp, options
305
310
  @partial_regexp = options[:partial_regexp]
306
311
  @map = options.fetch(:map, {})
@@ -353,8 +358,8 @@ module EBNF::LL1
353
358
  # Scanner instance with access to matched groups
354
359
  # @param [Hash{Symbol => Object}] options
355
360
  # @return [Token]
356
- def token(type, value, options = {})
357
- Token.new(type, value, options.merge(lineno: lineno))
361
+ def token(type, value, **options)
362
+ Token.new(type, value, lineno: lineno, **options)
358
363
  end
359
364
 
360
365
  ##
@@ -365,7 +370,7 @@ module EBNF::LL1
365
370
  # token.type #=> :LANGTAG
366
371
  # token.value #=> "en"
367
372
  #
368
- # @see http://en.wikipedia.org/wiki/Lexical_analysis#Token
373
+ # @see https://en.wikipedia.org/wiki/Lexical_analysis#Token
369
374
  class Token
370
375
  ##
371
376
  # The token's symbol type.
@@ -398,7 +403,7 @@ module EBNF::LL1
398
403
  # @param [String] value
399
404
  # @param [Hash{Symbol => Object}] options
400
405
  # @option options [Integer] :lineno (nil)
401
- def initialize(type, value, options = {})
406
+ def initialize(type, value, **options)
402
407
  @type = type.to_s.to_sym if type
403
408
  @value = value.to_s
404
409
  @options = options.dup
@@ -486,7 +491,7 @@ module EBNF::LL1
486
491
  # "invalid token '%' on line 10",
487
492
  # input: query, token: '%', lineno: 9)
488
493
  #
489
- # @see http://ruby-doc.org/core/classes/StandardError.html
494
+ # @see https://ruby-doc.org/core/classes/StandardError.html
490
495
  class Error < StandardError
491
496
  ##
492
497
  # The input string associated with the error.
@@ -514,7 +519,7 @@ module EBNF::LL1
514
519
  # @option options [String] :input (nil)
515
520
  # @option options [String] :token (nil)
516
521
  # @option options [Integer] :lineno (nil)
517
- def initialize(message, options = {})
522
+ def initialize(message, **options)
518
523
  @input = options[:input]
519
524
  @token = options[:token]
520
525
  @lineno = options[:lineno]
@@ -3,12 +3,52 @@ require 'ebnf/ll1/lexer'
3
3
  module EBNF::LL1
4
4
  ##
5
5
  # A Generic LL1 parser using a lexer and branch tables defined using the SWAP tool chain (modified).
6
+ #
7
+ # # Creating terminal definitions and parser rules to parse generated grammars
8
+ #
9
+ # The parser is initialized to callbacks invoked on entry and exit
10
+ # to each `terminal` and `production`. A trivial parser loop can be described as follows:
11
+ #
12
+ # require 'ebnf/ll1/parser'
13
+ # require 'meta'
14
+ #
15
+ # class Parser
16
+ # include Meta
17
+ # include EBNF::LL1::Parser
18
+ #
19
+ # terminal(:SYMBOL, /([a-z]|[A-Z]|[0-9]|_)+/) do |prod, token, input|
20
+ # # Add data based on scanned token to input
21
+ # input[:symbol] = token.value
22
+ # end
23
+ #
24
+ # start_production(:rule) do |input, current, callback|
25
+ # # Process on start of production
26
+ # # Set state for entry into recursed rules through current
27
+ #
28
+ # # Callback to parser loop with callback
29
+ # end
30
+ #
31
+ # production(:rule) do |input, current, callback|
32
+ # # Process on end of production
33
+ # # return results in input, retrieve results from recursed rules in current
34
+ #
35
+ # # Callback to parser loop with callback
36
+ # end
37
+ #
38
+ # def initialize(input)
39
+ # parse(input, start_symbol,
40
+ # branch: BRANCH,
41
+ # first: FIRST,
42
+ # follow: FOLLOW,
43
+ # cleanup: CLEANUP
44
+ # ) do |context, *data|
45
+ # # Process calls from callback from productions
46
+ #
47
+ # rescue ArgumentError, RDF::LL1::Parser::Error => e
48
+ # progress("Parsing completed with errors:\n\t#{e.message}")
49
+ # raise RDF::ReaderError, e.message if validate?
50
+ # end
6
51
  module Parser
7
- ##
8
- # @private
9
- # level above which debug messages are supressed
10
- DEBUG_LEVEL = 10
11
-
12
52
  ##
13
53
  # @return [Integer] line number of current token
14
54
  attr_reader :lineno
@@ -51,10 +91,10 @@ module EBNF::LL1
51
91
  # @yieldparam [Proc] block
52
92
  # Block passed to initialization for yielding to calling parser.
53
93
  # Should conform to the yield specs for #initialize
54
- def terminal(term, regexp, options = {}, &block)
94
+ def terminal(term, regexp, **options, &block)
55
95
  @patterns ||= []
56
96
  # Passed in order to define evaulation sequence
57
- @patterns << EBNF::LL1::Lexer::Terminal.new(term, regexp, options)
97
+ @patterns << EBNF::LL1::Lexer::Terminal.new(term, regexp, **options)
58
98
  @terminal_handlers ||= {}
59
99
  @terminal_handlers[term] = block if block_given?
60
100
  end
@@ -122,7 +162,14 @@ module EBNF::LL1
122
162
 
123
163
  def method_missing(method, *args, &block)
124
164
  if @delegate ||= nil
125
- @delegate.send method, *args, &block
165
+ # special handling when last arg is **options
166
+ params = @delegate.method(method).parameters
167
+ if params.any? {|t, _| t == :keyrest} && args.last.is_a?(Hash)
168
+ opts = args.pop
169
+ @delegate.send(method, *args, **opts, &block)
170
+ else
171
+ @delegate.send(method, *args, &block)
172
+ end
126
173
  else
127
174
  super
128
175
  end
@@ -179,7 +226,7 @@ module EBNF::LL1
179
226
  # def each_statement(&block)
180
227
  # @callback = block
181
228
  #
182
- # parse(START.to_sym) do |context, *data|
229
+ # parse(input, START.to_sym) do |context, *data|
183
230
  # case context
184
231
  # when :statement
185
232
  # yield *data
@@ -198,16 +245,13 @@ module EBNF::LL1
198
245
  # Lists valid terminals that can precede each production (for error recovery).
199
246
  # @option options [Hash{Symbol,String => Array<Symbol,String>}] :follow ({})
200
247
  # Lists valid terminals that can follow each production (for error recovery).
201
- # @option options [Boolean] :validate (false)
202
- # whether to validate the parsed statements and values. If not validating, the parser will attempt to recover from errors.
203
- # @option options [Boolean] :progress
204
- # Show progress of parser productions
205
- # @option options [Boolean] :debug
206
- # Detailed debug output
207
- # @option options [Boolean] :reset_on_start
208
- # Reset the parser state if the start token set with `prod` is found in a production. This reduces the production stack depth growth, which is appropriate for some grammars.
209
248
  # @option options[Integer] :high_water passed to lexer
249
+ # @option options [Logger] :logger for errors/progress/debug.
210
250
  # @option options[Integer] :low_water passed to lexer
251
+ # @option options [Boolean] :reset_on_start
252
+ # Reset the parser state if the start token set with `prod` is found in a production. This reduces the production stack depth growth, which is appropriate for some grammars.
253
+ # @option options [Boolean] :validate (false)
254
+ # whether to validate the parsed statements and values. If not validating, the parser will attempt to recover from errors.
211
255
  # @yield [context, *data]
212
256
  # Yields for to return data to parser
213
257
  # @yieldparam [:statement, :trace] context
@@ -218,18 +262,14 @@ module EBNF::LL1
218
262
  # @raise [Exception] Raises exceptions for parsing errors
219
263
  # or errors raised during processing callbacks. Internal
220
264
  # errors are raised using {Error}.
221
- # @see http://cs.adelaide.edu.au/~charles/lt/Lectures/07-ErrorRecovery.pdf
222
- def parse(input = nil, start = nil, options = {}, &block)
265
+ # @see https://cs.adelaide.edu.au/~charles/lt/Lectures/07-ErrorRecovery.pdf
266
+ def parse(input = nil, start = nil, **options, &block)
223
267
  @options = options.dup
224
- @options[:debug] ||= case
225
- when @options[:progress] then 2
226
- when @options[:validate] then 1
227
- end
228
268
  @branch = options[:branch]
229
269
  @first = options[:first] ||= {}
230
270
  @follow = options[:follow] ||= {}
231
271
  @cleanup = options[:cleanup] ||= {}
232
- @lexer = input.is_a?(Lexer) ? input : Lexer.new(input, self.class.patterns, @options)
272
+ @lexer = input.is_a?(Lexer) ? input : Lexer.new(input, self.class.patterns, **@options)
233
273
  @productions = []
234
274
  @parse_callback = block
235
275
  @recovering = false
@@ -349,9 +389,9 @@ module EBNF::LL1
349
389
  end
350
390
 
351
391
  # Get the list of follows for this sequence, this production and the stacked productions.
352
- debug("recovery", "stack follows:", level: 4)
392
+ debug("recovery", "stack follows:")
353
393
  todo_stack.reverse.each do |todo|
354
- debug("recovery", level: 4) {" #{todo[:prod]}: #{@follow[todo[:prod]].inspect}"}
394
+ debug("recovery") {" #{todo[:prod]}: #{@follow[todo[:prod]].inspect}"}
355
395
  end
356
396
 
357
397
  # Find all follows to the top of the stack
@@ -459,15 +499,16 @@ module EBNF::LL1
459
499
  protected
460
500
 
461
501
  ##
462
- # Error information, used as level `0` debug messages.
502
+ # Error information, used as level `3` logger messages.
503
+ # Messages may be logged and are saved for reporting at end of parsing.
463
504
  #
464
505
  # @param [String] node Relevant location associated with message
465
506
  # @param [String] message Error string
466
- # @param [Hash] options
507
+ # @param [Hash{Symbol => Object}] options
467
508
  # @option options [URI, #to_s] :production
468
509
  # @option options [Token] :token
469
- # @see {#debug}
470
- def error(node, message, options = {})
510
+ # @see #debug
511
+ def error(node, message, **options)
471
512
  lineno = @lineno || (options[:token].lineno if options[:token].respond_to?(:lineno))
472
513
  m = "ERROR "
473
514
  m += "[line: #{lineno}] " if lineno
@@ -476,83 +517,82 @@ module EBNF::LL1
476
517
  m += ", production = #{options[:production].inspect}" if options[:production]
477
518
  @error_log << m unless @recovering
478
519
  @recovering = true
479
- debug(node, m, options.merge(level: 0))
520
+ debug(node, m, level: options.fetch(:level, 3), **options)
480
521
  if options[:raise] || @options[:validate]
481
522
  raise Error.new(m, lineno: lineno, token: options[:token], production: options[:production])
482
523
  end
483
524
  end
484
525
 
485
526
  ##
486
- # Warning information, used as level `1` debug messages.
527
+ # Warning information, used as level `2` logger messages.
528
+ # Messages may be logged and are saved for reporting at end of parsing.
487
529
  #
488
530
  # @param [String] node Relevant location associated with message
489
531
  # @param [String] message Error string
490
532
  # @param [Hash] options
491
533
  # @option options [URI, #to_s] :production
492
534
  # @option options [Token] :token
493
- # @see {#debug}
494
- def warn(node, message, options = {})
535
+ # @see #debug
536
+ def warn(node, message, **options)
537
+ lineno = @lineno || (options[:token].lineno if options[:token].respond_to?(:lineno))
495
538
  m = "WARNING "
496
- m += "[line: #{@lineno}] " if @lineno
539
+ m += "[line: #{lineno}] " if lineno
497
540
  m += message
498
541
  m += " (found #{options[:token].inspect})" if options[:token]
499
542
  m += ", production = #{options[:production].inspect}" if options[:production]
500
543
  @error_log << m unless @recovering
501
- debug(node, m, options.merge(level: 1))
544
+ debug(node, m, level: 2, lineno: lineno, **options)
502
545
  end
503
546
 
504
547
  ##
505
- # Progress output when parsing. Passed as level `2` debug messages.
548
+ # Progress logged when parsing. Passed as level `1` logger messages.
549
+ #
550
+ # The call is ignored, unless `@options[:logger]` is set.
506
551
  #
507
- # @overload progress(node, message, options)
552
+ # @overload progress(node, message, **options, &block)
508
553
  # @param [String] node Relevant location associated with message
509
554
  # @param [String] message ("")
510
555
  # @param [Hash] options
511
556
  # @option options [Integer] :depth
512
557
  # Recursion depth for indenting output
513
- # @see {#debug}
558
+ # @see #debug
514
559
  def progress(node, *args, &block)
515
- return unless @options[:progress] || @options[:debug]
560
+ return unless @options[:logger]
561
+ lineno = @lineno || (options[:token].lineno if options[:token].respond_to?(:lineno))
516
562
  args << {} unless args.last.is_a?(Hash)
517
- args.last[:level] ||= 2
563
+ args.last[:level] ||= 1
564
+ args.last[:lineno] ||= lineno
518
565
  debug(node, *args, &block)
519
566
  end
520
567
 
521
568
  ##
522
- # Progress output when debugging.
569
+ # Debug logging.
523
570
  #
524
- # The call is ignored, unless `@options[:debug]` is set, in which
525
- # case it yields tracing information as indicated. Additionally,
526
- # if `@options[:debug]` is an Integer, the call is aborted if the
527
- # `:level` option is less than than `:level`.
571
+ # The call is ignored, unless `@options[:logger]` is set.
528
572
  #
529
- # @overload debug(node, message, options)
573
+ # @overload debug(node, message, **options)
530
574
  # @param [Array<String>] args Relevant location associated with message
531
575
  # @param [Hash] options
532
576
  # @option options [Integer] :depth
533
577
  # Recursion depth for indenting output
534
- # @option options [Integer] :level
535
- # Level assigned to message, by convention, level `0` is for
536
- # errors, level `1` is for warnings, level `2` is for parser
537
- # progress information, and anything higher is for various levels
538
- # of debug information.
539
- #
540
- # @yield trace, level, lineno, depth, args
541
- # @yieldparam [:trace] trace
542
- # @yieldparam [Integer] level
543
- # @yieldparam [Integer] lineno
544
- # @yieldparam [Integer] depth Recursive depth of productions
545
- # @yieldparam [Array<String>] args
546
- # @yieldreturn [String] added to message
547
- def debug(*args)
548
- return unless @options[:debug] && @parse_callback
578
+ # @yieldreturn [String] additional string appended to `message`.
579
+ def debug(*args, &block)
580
+ return unless @options[:logger]
549
581
  options = args.last.is_a?(Hash) ? args.pop : {}
550
- debug_level = options.fetch(:level, 3)
551
- return if @options[:debug].is_a?(Integer) && debug_level > @options[:debug]
552
-
582
+ lineno = @lineno || (options[:token].lineno if options[:token].respond_to?(:lineno))
583
+ level = options.fetch(:level, 0)
553
584
  depth = options[:depth] || self.depth
554
- args << yield if block_given?
555
- @parse_callback.call(:trace, debug_level, @lineno, depth, *args)
585
+
586
+ if self.respond_to?(:log_debug)
587
+ level = [:debug, :info, :warn, :error, :fatal][level]
588
+ log_debug(*args, **options.merge(level: level, lineno: lineno, depth: depth), &block)
589
+ elsif @options[:logger].respond_to?(:add)
590
+ args << yield if block_given?
591
+ @options[:logger].add(level, "[#{lineno}]" + (" " * depth) + args.join(" "))
592
+ elsif @options[:logger].respond_to?(:<<)
593
+ args << yield if block_given?
594
+ @options[:logger] << "[#{lineno}]" + (" " * depth) + args.join(" ")
595
+ end
556
596
  end
557
597
 
558
598
  private
@@ -563,7 +603,7 @@ module EBNF::LL1
563
603
  if handler
564
604
  # Create a new production data element, potentially allowing handler
565
605
  # to customize before pushing on the @prod_data stack
566
- progress("#{prod}(:start):#{@prod_data.length}") {@prod_data.last}
606
+ debug("#{prod}(:start):#{@prod_data.length}") {@prod_data.last}
567
607
  data = {}
568
608
  begin
569
609
  self.class.eval_with_binding(self) {
@@ -577,12 +617,12 @@ module EBNF::LL1
577
617
  elsif [:merge, :star].include?(@cleanup[prod])
578
618
  # Save current data to merge later
579
619
  @prod_data << {}
580
- progress("#{prod}(:start}:#{@prod_data.length}:cleanup:#{@cleanup[prod]}") { get_token.inspect + (@recovering ? ' recovering' : '')}
620
+ debug("#{prod}(:start}:#{@prod_data.length}:cleanup:#{@cleanup[prod]}") { get_token.inspect + (@recovering ? ' recovering' : '')}
581
621
  else
582
622
  # Make sure we push as many was we pop, even if there is no
583
623
  # explicit start handler
584
624
  @prod_data << {} if self.class.production_handlers[prod]
585
- progress("#{prod}(:start:#{@prod_data.length})") { get_token.inspect + (@recovering ? ' recovering' : '')}
625
+ debug("#{prod}(:start:#{@prod_data.length})") { get_token.inspect + (@recovering ? ' recovering' : '')}
586
626
  end
587
627
  #puts "prod_data(s): " + @prod_data.inspect
588
628
  end
@@ -616,7 +656,7 @@ module EBNF::LL1
616
656
  else Array(input[k]) + Array(v)
617
657
  end
618
658
  end
619
- progress("#{prod}(:finish):#{@prod_data.length} cleanup:#{@cleanup[prod]}") {@prod_data.last}
659
+ debug("#{prod}(:finish):#{@prod_data.length} cleanup:#{@cleanup[prod]}") {@prod_data.last}
620
660
  else
621
661
  progress("#{prod}(:finish):#{@prod_data.length}") { "recovering" if @recovering }
622
662
  end
@@ -723,7 +763,7 @@ module EBNF::LL1
723
763
  # "invalid token '%' on line 10",
724
764
  # token: '%', lineno: 9, production: :turtleDoc)
725
765
  #
726
- # @see http://ruby-doc.org/core/classes/StandardError.html
766
+ # @see https://ruby-doc.org/core/classes/StandardError.html
727
767
  class Error < StandardError
728
768
  ##
729
769
  # The current production.
@@ -751,7 +791,7 @@ module EBNF::LL1
751
791
  # @option options [Symbol] :production (nil)
752
792
  # @option options [String] :token (nil)
753
793
  # @option options [Integer] :lineno (nil)
754
- def initialize(message, options = {})
794
+ def initialize(message, **options)
755
795
  @production = options[:production]
756
796
  @token = options[:token]
757
797
  @lineno = options[:lineno] || (@token.lineno if @token.respond_to?(:lineno))