l43_peg 0.1.9 → 0.3.0

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +34 -279
  3. data/lib/l43_peg/box.rb +27 -0
  4. data/lib/l43_peg/combinators/boxed.rb +16 -0
  5. data/lib/l43_peg/combinators/choice.rb +30 -0
  6. data/lib/l43_peg/combinators/debug_parser.rb +5 -5
  7. data/lib/l43_peg/combinators/ignore.rb +12 -0
  8. data/lib/l43_peg/combinators/lazy.rb +20 -0
  9. data/lib/l43_peg/combinators/many.rb +26 -21
  10. data/lib/l43_peg/combinators/map.rb +26 -0
  11. data/lib/l43_peg/combinators/map_error.rb +24 -0
  12. data/lib/l43_peg/combinators/satisfy.rb +36 -0
  13. data/lib/l43_peg/combinators/sequence.rb +41 -0
  14. data/lib/l43_peg/combinators.rb +62 -73
  15. data/lib/l43_peg/errors.rb +11 -0
  16. data/lib/l43_peg/failure.rb +17 -1
  17. data/lib/l43_peg/input.rb +12 -10
  18. data/lib/l43_peg/mappers.rb +21 -3
  19. data/lib/l43_peg/parser.rb +4 -8
  20. data/lib/l43_peg/parsers/char_parser.rb +48 -8
  21. data/lib/l43_peg/parsers/end_parser.rb +13 -12
  22. data/lib/l43_peg/parsers/eol_parser.rb +55 -0
  23. data/lib/l43_peg/parsers/escape_parser.rb +21 -0
  24. data/lib/l43_peg/parsers/failure_parser.rb +2 -2
  25. data/lib/l43_peg/parsers/int_parser.rb +9 -7
  26. data/lib/l43_peg/parsers/list_parser.rb +33 -0
  27. data/lib/l43_peg/parsers/literal_parser.rb +23 -0
  28. data/lib/l43_peg/parsers/name_parser.rb +13 -0
  29. data/lib/l43_peg/parsers/rgx_parser.rb +35 -20
  30. data/lib/l43_peg/parsers/string_parser.rb +26 -0
  31. data/lib/l43_peg/parsers/success_parser.rb +21 -0
  32. data/lib/l43_peg/parsers/symbol_parser.rb +19 -0
  33. data/lib/l43_peg/parsers/ws_parser.rb +20 -0
  34. data/lib/l43_peg/parsers.rb +50 -24
  35. data/lib/l43_peg/stop.rb +7 -7
  36. data/lib/l43_peg/success.rb +10 -12
  37. data/lib/l43_peg/version.rb +1 -1
  38. data/lib/l43_peg.rb +10 -29
  39. metadata +37 -13
  40. data/lib/l43_peg/cache.rb +0 -9
  41. data/lib/l43_peg/combinators/sel.rb +0 -24
  42. data/lib/l43_peg/combinators/seq.rb +0 -29
  43. data/lib/l43_peg/helper.rb +0 -13
  44. data/lib/l43_peg/parsers/rgx_tokenizer.rb +0 -24
  45. data/lib/l43_peg/parsers/sel_rgx_parser.rb +0 -25
  46. data/lib/l43_peg/parsers/token_parser.rb +0 -32
  47. data/lib/l43_peg/parsers/tokens_parser.rb +0 -63
  48. data/lib/l43_peg/parsers/verb_parser.rb +0 -23
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43Peg
4
+ module Combinators
5
+
6
+ module Sequence extend self
7
+
8
+ def sequence(*parsers, name: nil, flatten: false, join: false, box: nil, &mapper)
9
+ name ||= "sequence(#{parsers.flatten.map(&:name).join(", ")})"
10
+ parser = Parser.new(name) {|input, _name=nil| _seq(input:, name:, parsers: parsers.flatten, flatten:, join:)}
11
+ if mapper
12
+ parser.map(name:, box:, &mapper)
13
+ elsif box
14
+ parser.map(name) { Box.make(box).new(it) }
15
+ else
16
+ parser
17
+ end
18
+ end
19
+
20
+ private
21
+ def _seq(input:, name:, parsers:, flatten: false, join: false)
22
+ curr_input = input
23
+ ast = []
24
+ parsed = String.new
25
+ parsers.each do |parser|
26
+ result = parser.(curr_input)
27
+ # p result
28
+ case result
29
+ in L43Peg::Failure => failure
30
+ return failure.update(name:)
31
+ in {ast: new_ast, rest: curr_input, parsed: new_parsed}
32
+ ast.push(new_ast) unless new_ast == nil
33
+ parsed << new_parsed
34
+ end
35
+ end
36
+ L43Peg::Success.new(ast:, rest: curr_input, parsed:)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,82 +1,71 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'helper'
4
- require_relative 'mappers'
3
+ require "l43_peg/combinators/boxed"
4
+ require "l43_peg/combinators/choice"
5
+ require "l43_peg/combinators/debug_parser"
6
+ require "l43_peg/combinators/ignore"
7
+ require "l43_peg/combinators/lazy"
8
+ require "l43_peg/combinators/many"
9
+ require "l43_peg/combinators/map"
10
+ require "l43_peg/combinators/map_error"
11
+ require "l43_peg/combinators/satisfy"
12
+ require "l43_peg/combinators/sequence"
5
13
 
6
14
  module L43Peg
7
15
  module Combinators extend self
16
+ def boxed(parser, box, name: nil, &mapper) =
17
+ Boxed.boxed(parser, box, name:, &mapper)
8
18
 
9
- require 'l43_peg/combinators/debug_parser'
10
- require 'l43_peg/combinators/many'
11
- require 'l43_peg/combinators/sel'
12
- require 'l43_peg/combinators/seq'
13
-
14
- include L43Peg::Helper
15
- include L43Peg::Mappers
16
- include L43Peg::Parsers
17
-
18
- def args_parser(definitions, name: nil, stop: nil, &block)
19
- parser = tokens_parser(definitions, stop:, &block)
20
- name = name || "args_parser(#{definitions.inspect})"
21
- inner = many(parser)
22
- map(inner, name:, fn: join_maps)
23
- end
24
-
25
- def debug_parser(parser, name: nil, level: :default)
26
- name ||= "debug_parser(#{parser.name})"
27
- Parser.new(name, &DebugParser.parse_with_debug(parser:, level:, name:))
28
- end
29
-
30
- def many(parser, name: nil, min: 0, max: nil)
31
- Parser.new(name || "many(#{parser.name})") {|input, cache, name1=nil| Many.many(input:, cache:, name: name1 || name, parser:, min:, max:)}
32
- end
33
-
34
- def map(parser, name: nil, fn: nil, &mapper)
35
- raise ArgumentError, "must not provide keyword parameyer fn and a block" if fn && mapper
36
- mapper = fn || mapper
37
- raise ArgumentError, "must provide keyword parameyer fn or a block" unless mapper
38
- Parser.new(name || "map(#{parser.name})") {|input, cache, name=nil| _map(input:, cache:, name:, parser:, mapper:)}
39
- end
40
-
41
- def rgx_tokenize(rgx_specs, name: nil)
42
- name = name || "rgx_tokenize(#{rgx_specs.inspect})"
43
- many(sel_rgx_parser(rgx_specs, name:))
44
- end
45
-
46
- def sel(parsers, name: nil)
47
- name ||= "sel(#{parsers.map(&:name).join(", ")})"
48
- Parser.new(name) { |input, cache, name1=nil| Sel.sel(input:, cache:, name: name1 || name, parsers:) }
49
- end
50
-
51
- def sel_rgx_parser(rgx_specs, name: nil)
52
- name = name || "sel_rgx_parser(#{rgx_specs.inspect})"
53
- parsers = rgx_specs.map(&_mk_rgx_parser)
54
- sel(parsers, name:)
55
- end
56
-
57
- def seq(*parsers, name: nil)
58
- name ||= "seq(#{parsers.map(&:name).join(", ")})"
59
- Parser.new(name) {|input, cache, _name=nil| Seq.seq(input:, cache:, name:, parsers:)}
60
- end
61
-
62
- private
63
-
64
- def _map(input:, cache:, name:, parser:, mapper:)
65
- case parser.(input, cache:)
66
- in L43Peg::Failure => failure
67
- failure
68
- in L43Peg::Success => success
69
- success.map(&mapper)
70
- end
71
- end
72
-
73
- def _mk_rgx_parser
74
- -> rgxspec do
75
- name, rgx, capture, fn = rgxspec
76
- fn ||= -> (captures) { [name, captures] }
77
- map(Parsers.rgx_parser(rgx, capture:), &fn)
78
- end
79
- end
19
+ def choice(*parsers, name: nil) =
20
+ Choice.choice(*parsers, name:)
21
+
22
+ def debug_parser(parser:, name:, level:) =
23
+ DebugParser.debug_parser(parser:, name:, level:)
24
+
25
+ def ignore(parser, name: nil) =
26
+ Ignore.ignore(parser, name:)
27
+
28
+ def lazy(name: nil, &parser) = Lazy.lazy(name:, &parser)
29
+
30
+ def many(parser, name: nil, min: 0, max: nil) =
31
+ Many.many(parser, name:, min:, max:)
32
+
33
+ def map(parser, name: nil, fn: nil, box: nil, &mapper) =
34
+ Map.map(parser, name:, fn:, box:, &mapper)
35
+
36
+ def map_error(parser, name: nil, reason: nil) =
37
+ MapError.map_error(parser, name:, reason:)
38
+
39
+ def satisfy(parser, name: nil, fn: nil, &satisfier) =
40
+ Satisfy.satisfy(parser, name:, fn:, &satisfier)
41
+
42
+ def sequence(*parsers, name: nil, flatten: false, join: false, box: nil, &mapper) =
43
+ Sequence.sequence(*parsers, name:, flatten:, join:, box:, &mapper)
44
+ end
45
+
46
+ class Parser
47
+ def boxed(box, name: nil, &mapper) =
48
+ Combinators::Boxed.boxed(self, box, name:, &mapper)
49
+
50
+ def debug(name: nil, level: 10) =
51
+ Combinators::DebugParser.debug_parser(parser: self, name:, level:)
52
+
53
+ def ignore(name: nil) =
54
+ Combinators::Ignore.ignore(self, name:)
55
+
56
+ def lazy(name: nil) = Lazy.lazy(self, name:)
57
+
58
+ def many(name: nil, min: 0, max: nil) =
59
+ Combinators::Many.many(self, name:, min:, max:)
60
+
61
+ def map(name: nil, fn: nil, box: nil, &mapper) =
62
+ Combinators::Map.map(self, name:, fn:, box:, &mapper)
63
+
64
+ def map_error(name: nil, reason: nil) =
65
+ Combinators::MapError.map_error(self, name:, reason:)
66
+
67
+ def satisfy(name: nil, fn: nil, &satisfier) =
68
+ Combinators::Satisfy.satisfy(self, name:, fn:, &satisfier)
80
69
  end
81
70
  end
82
71
  # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43Peg
4
+ BadParserResult = Class.new(RuntimeError)
5
+ NotABox = Class.new(RuntimeError)
6
+
7
+ module Errors extend self
8
+ def bad_parser_result(msg) = raise BadParserResult, msg
9
+ end
10
+ end
11
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -3,9 +3,25 @@
3
3
  module L43Peg
4
4
  class Failure
5
5
  extend L43::OpenObject
6
- attributes cache: nil, input: nil, parsed_by: nil, position: [1, 1], reason: ""
6
+ attributes name:, parsed_by: nil, position: [1, 1], reason: ""
7
+
8
+ def deconstruct(*) = [:error, named_reason]
9
+
10
+ def map(name:, reason:)
11
+ update_attribute(:reason, reason)
12
+ .update_attribute_if(:name, name)
13
+ end
14
+
15
+ def named_reason
16
+ if name
17
+ "#{reason} (in #{name})"
18
+ else
19
+ reason
20
+ end
21
+ end
7
22
 
8
23
  def position_hr = "@#{position}"
24
+
9
25
  end
10
26
  end
11
27
  # SPDX-License-Identifier: Apache-2.0
data/lib/l43_peg/input.rb CHANGED
@@ -4,11 +4,11 @@ module L43Peg
4
4
  class Input
5
5
  extend L43::OpenObject
6
6
 
7
- attributes col: 1, input: "", lnb: 1, context: {}
7
+ attributes col: 1, src: "", lnb: 1
8
8
 
9
9
  def debug
10
10
  "Input<" +
11
- ["col:#{col}", "input:#{input.inspect}", "lnb:#{lnb}", "context:#{context.inspect}"].join(" ") +
11
+ ["col:#{col}", "src:#{src.inspect}", "lnb:#{lnb}"].join(" ") +
12
12
  ">"
13
13
  end
14
14
 
@@ -23,13 +23,15 @@ module L43Peg
23
23
  end
24
24
  end
25
25
 
26
- def empty? = input.empty?
26
+ def empty? = src.empty?
27
+
28
+ def first(n=1) = src[0...n]
27
29
 
28
30
  def head_hr(n, position: false)
29
31
  if position
30
- "Input<#{input[0...n].inspect}@#{position_hr}>"
32
+ "Input<#{src[0...n].inspect}@#{position_hr}>"
31
33
  else
32
- "Input<#{input[0...n].inspect}>"
34
+ "Input<#{src[0...n].inspect}>"
33
35
  end
34
36
  end
35
37
 
@@ -40,17 +42,17 @@ module L43Peg
40
42
  private
41
43
 
42
44
  def _drop_by_n(n)
43
- return self if input.empty?
44
- _split(n) => col, lnb, head, tail
45
- self.class.new(input: tail, context:, col:, lnb:)
45
+ return self if src.empty?
46
+ _split(n) => col, lnb, _head, tail
47
+ self.class.new(src: tail, col:, lnb:)
46
48
  end
47
49
 
48
50
  # Very inefficent but sufficent for my use cases so far
49
51
  # and convenient for regex parsers
50
52
  def _split(n)
51
- head = input.slice(0...n)
53
+ head = src.slice(0...n)
52
54
  lines = head.count("\n")
53
- tail = input.slice(n..-1) || ''
55
+ tail = src.slice(n..-1) || ''
54
56
  new_col =
55
57
  if lines.zero?
56
58
  col + n
@@ -1,14 +1,32 @@
1
1
  # frozen_string_literal: true
2
+ # :markup: markdown
2
3
 
4
+ require_relative 'box'
3
5
  module L43Peg
4
- module Mappers
6
+ module Mappers extend self
7
+ ##
8
+ # coming soon
9
+ # @return instance of a class names `class_name`
10
+ def box(class_name, value)
11
+ # klass = BoxFactory.make_a_box_class(class_name)
12
+ klass = Box.make(class_name)
13
+ klass.new(value)
14
+ end
15
+
16
+ def boxed(class_name, &blk) # :nodoc:
17
+ # klass = BoxFactory.make_a_box_class(class_name)
18
+ klass = Box.make(class_name)
19
+ -> (ast) { klass.new(ast, &blk) }
20
+ end
5
21
 
6
- def join_and_to_i
22
+ def join_and_to_i # :nodoc:
7
23
  -> list do
8
24
  list.join.to_i
9
25
  end
10
26
  end
11
-
27
+
28
+ ##
29
+ # Returns a mapper that maps the ast on a list of maps
12
30
  def join_maps
13
31
  -> maps do
14
32
  maps.reduce Hash.new do |map, entry|
@@ -1,16 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module L43Peg
4
- # A wrapper
5
4
  class Parser
6
5
 
7
6
  attr_reader :fn, :name
8
7
 
9
- # Calls the parse function of this parser instance.
10
- #
11
- # @param input [L43Peg::Input] to be parsed
12
- def call(input, cache: L43Peg::Cache.new)
13
- fn.(input, cache, name)
8
+ def call(input)
9
+ fn.(input)
14
10
  end
15
11
 
16
12
  private
@@ -19,7 +15,7 @@ module L43Peg
19
15
  @name = name
20
16
  @fn = fn
21
17
  end
22
-
18
+
23
19
  end
24
20
  end
25
- # SPDX-License-Identifier: Apache-2.0
21
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,27 +1,67 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'l43_peg/parser'
3
4
  module L43Peg
4
5
  module Parsers
5
6
  class CharParser < L43Peg::Parser
6
7
 
7
8
  private
8
9
 
9
- def initialize(charset=nil)
10
+ def initialize(charset=nil, name: nil, allow_eol: false)
11
+ raise ArgumentError, "must not provide a set of characters and allow_eol: true" if charset && allow_eol
12
+
10
13
  charset = _mk_set(charset)
11
- super("char_parser(#{charset})") do |input, cache, name|
12
- if input.input.empty?
13
- L43Peg::Failure.new(cache:, input:, parsed_by: self, reason: "cannot parse at end of input #{name}")
14
- elsif charset.nil? || charset.member?(input.input[0])
15
- L43Peg::Success.new(ast: input.input[0], cache:, rest: input.drop, position: input.position)
14
+ name ||= _mk_name(charset:, allow_eol:)
15
+
16
+ if charset
17
+ super(name) { parse_in_set(it, charset:, name:) }
16
18
  else
17
- L43Peg::Failure.new(cache:, input:, parsed_by: self, reason: "char #{input.input[0].inspect}")
19
+ super(name) { parse_any_char(it, name:, allow_eol:) }
18
20
  end
21
+
22
+ end
23
+
24
+ def parse_in_set(input, name:, charset:)
25
+ if input.empty?
26
+ return L43Peg::Failure.new(parsed_by: self, reason: 'cannot parse at end of input', name:)
27
+ end
28
+ ast = input.first
29
+ if charset.member?(ast)
30
+ L43Peg::Success.new(ast:, rest: input.drop, parsed: ast, position: input.position)
31
+ else
32
+ L43Peg::Failure.new(
33
+ parsed_by: self,
34
+ reason: "char #{ast.inspect} is not in required set",
35
+ name:)
36
+ end
37
+ end
38
+
39
+ def parse_any_char(input, name:, allow_eol:)
40
+ if input.empty?
41
+ return L43Peg::Failure.new(parsed_by: self, reason: 'cannot parse at end of input', name:)
42
+ end
43
+ ast = input.first
44
+ if ast == "\n" && !allow_eol
45
+ reason = "cannot parse at end of line, this can be allowed by providing 'allow_eol: true'"
46
+ L43Peg::Failure.new(parsed_by: self, reason:, name:)
47
+ else
48
+ L43Peg::Success.new(ast:, rest: input.drop, parsed: ast, position: input.position)
49
+ end
50
+ end
51
+
52
+ def _mk_name(charset:, allow_eol:)
53
+ if charset
54
+ "char_parser(#{charset&.join&.inspect})"
55
+ else
56
+ "char_parser(allow_eol: #{allow_eol})"
19
57
  end
20
58
  end
21
59
 
22
60
  def _mk_set(charset)
23
61
  return unless charset
24
62
  case charset
63
+ when Integer
64
+ Set.new([charset.chr])
25
65
  when String
26
66
  Set.new(charset.split(""))
27
67
  when Set
@@ -33,4 +73,4 @@ module L43Peg
33
73
  end
34
74
  end
35
75
  end
36
- # SPDX-License-Identifier: AGPL-3.0-or-later
76
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,25 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../parsers'
4
3
 
5
4
  module L43Peg
6
5
  module Parsers
7
6
  class EndParser < L43Peg::Parser
7
+ class << self
8
+ def instance
9
+ @__instance__ ||= _mk_end_parser
10
+ end
8
11
 
9
- def self.instance
10
- @__instance__ ||= new
11
- end
12
-
13
- private
12
+ private
14
13
 
15
- def initialize
16
- super('end_parser') do |input, cache, _name=nil|
17
- if input.input.empty?
18
- L43Peg::Success.new(cache:, rest: input, position: input.position)
19
- else
20
- L43Peg::Failure.new(cache:, input:, parsed_by: self, reason: "not at end of input")
14
+ def _mk_end_parser
15
+ Parser.new('end_parser') do |input|
16
+ if input.empty?
17
+ L43Peg::Success.new(ast: nil, rest: input, parsed: "")
18
+ else
19
+ L43Peg::Failure.new(name: 'end_parser', parsed_by: self, reason: 'not at end of input', position: input.position)
20
+ end
21
21
  end
22
22
  end
23
+
23
24
  end
24
25
  end
25
26
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43Peg
4
+ module Parsers
5
+ class EolParser < L43Peg::Parser
6
+
7
+ class << self
8
+ def eol_parser(succeed_at_end: false)
9
+ if succeed_at_end
10
+ @__eol_at_end_parser__ ||= eol_at_end_parser
11
+ else
12
+ @__eol_parser__ ||= eol_not_end_parser
13
+ end
14
+ end
15
+
16
+ private
17
+ def eol_at_end_parser
18
+ Parser.new('eol_parser(succeed_at_end: true)', &parse_eol_at_end)
19
+ end
20
+
21
+ def eol_not_end_parser
22
+ Parser.new('eol_parser(succeed_at_end: false)', &parse_eol)
23
+ end
24
+
25
+ def parse_eol_at_end
26
+ -> input do
27
+ if input.first == "\n"
28
+ L43Peg::Success.new(ast: "\n", parsed: "\n", rest: input.drop)
29
+ elsif input.empty?
30
+ L43Peg::Success.new(ast: nil, parsed: "", rest: input)
31
+ else
32
+ L43Peg::Failure.new(name: 'eol_parser(succeed_at_end: true)', reason: 'not at end of line')
33
+ end
34
+ end
35
+ end
36
+
37
+ def parse_eol
38
+ -> input do
39
+ if input.first == "\n"
40
+ L43Peg::Success.new(ast: "\n", parsed: "\n", rest: input.drop)
41
+ elsif input.empty?
42
+ reason = "end of input not considered end of line unless activated with succeed_at_end: true"
43
+ L43Peg::Failure.new(name: 'eol_parser(succeed_at_end: false)', reason:)
44
+ else
45
+ L43Peg::Failure.new(
46
+ name: 'eol_parser(succeed_at_end: false)',
47
+ reason: 'not at end of line')
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43Peg
4
+ module Parsers
5
+ module EscapeParser extend self
6
+ def escape_parser(escape_char_set, escapees=nil, name: nil, with_box: nil)
7
+ name = name || "escape_parser(#{escape_char_set.inspect}, #{escapees.inspect})"
8
+ s = Combinators.sequence(
9
+ Parsers.char_parser(escape_char_set),
10
+ Parsers.char_parser(escapees)
11
+ )
12
+ if with_box
13
+ s.map(name:) { |_, escaped| escaped }.boxed(with_box)
14
+ else
15
+ s.map(name:) { |_, escaped| escaped }
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -11,8 +11,8 @@ module L43Peg
11
11
  private
12
12
 
13
13
  def initialize
14
- super('failure_parser') do |input, cache, _name=nil|
15
- L43Peg::Failure.new(cache:, input:, parsed_by: self, reason: "this parser always fails")
14
+ super('failure_parser') do |_|
15
+ L43Peg::Failure.new(parsed_by: self, reason: "this parser always fails", name: nil)
16
16
  end
17
17
  end
18
18
  end
@@ -1,14 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../parsers'
4
-
5
3
  module L43Peg
6
4
  module Parsers
7
5
  class IntParser < L43Peg::Parser
8
- extend L43Peg::Combinators
9
- extend L43Peg::Mappers
10
-
11
6
  class << self
7
+ include Combinators
12
8
  def instance
13
9
  @__instance__ ||= _mk_int_parser
14
10
  end
@@ -16,10 +12,16 @@ module L43Peg
16
12
  private
17
13
 
18
14
  def _mk_int_parser
19
- map(seq(many(Parsers.char_parser("+-"), max: 1), many(Parsers.char_parser("0".."9"), min: 1), name: "int_parser"), &join_and_to_i)
15
+ s = sequence(
16
+ many(Parsers.char_parser("+-"), max: 1),
17
+ many(Parsers.char_parser("0".."9"), min: 1),
18
+ name: "int_parser"
19
+ )
20
+ s = map(s, &Mappers.join_and_to_i)
21
+ map_error(s, name: 'int_parser', reason: 'could not parse int')
20
22
  end
21
- end
22
23
 
24
+ end
23
25
  end
24
26
  end
25
27
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43Peg
4
+ module Parsers
5
+ module ListParser extend self
6
+
7
+ def list_parser(
8
+ element_parser:,
9
+ name: nil, head_parser: nil, open_parser: nil, close_parser: nil, sep_parser: nil
10
+ )
11
+ name = name || "list_parser(#{element_parser.name})"
12
+ open_parser ||= char_parser(40).ignore # avoid literal open_paren for nvim indent for now
13
+ close_parser ||= char_parser(41).ignore # idem
14
+ sep_parser ||= ws_parser(min: 1).ignore
15
+ head_parser ||= element_parser
16
+
17
+ sequence(
18
+ open_parser,
19
+ head_parser,
20
+ many(
21
+ sequence(
22
+ sep_parser,
23
+ element_parser
24
+ ).map(&:first)
25
+ ),
26
+ close_parser
27
+ )
28
+ .map { |h, t| [h, *t] }
29
+ end
30
+ end
31
+ end
32
+ end
33
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43Peg
4
+ module Parsers
5
+ class LiteralParser < L43Peg::Parser
6
+
7
+ private
8
+
9
+ def initialize(verb, name: nil)
10
+ name = name || "literal_parser(#{verb.inspect})"
11
+ super(name) do |input, _name|
12
+ if input.src.start_with?(verb)
13
+ L43Peg::Success.new(ast: verb, rest: input.drop(verb), position: input.position)
14
+ else
15
+ L43Peg::Failure.new(parsed_by: self, reason: "input does not start with #{verb.inspect}", name:)
16
+ end
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module L43Peg
4
+ module Parsers
5
+ module NameParser extend self
6
+ def name_parser(name: nil, begin_rgx: :alpha, inner_rgx: /[_[:alnum:]]*/, with_box: nil)
7
+ name = name || "name_parser"
8
+ Combinators.map(RgxParser.new(begin_rgx, inner_rgx, name:), name:, &:to_sym)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ # SPDX-License-Identifier: AGPL-3.0-or-later