ebnf 1.1.2 → 2.1.1

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