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.
- checksums.yaml +4 -4
- data/README.md +34 -279
- data/lib/l43_peg/box.rb +27 -0
- data/lib/l43_peg/combinators/boxed.rb +16 -0
- data/lib/l43_peg/combinators/choice.rb +30 -0
- data/lib/l43_peg/combinators/debug_parser.rb +5 -5
- data/lib/l43_peg/combinators/ignore.rb +12 -0
- data/lib/l43_peg/combinators/lazy.rb +20 -0
- data/lib/l43_peg/combinators/many.rb +26 -21
- data/lib/l43_peg/combinators/map.rb +26 -0
- data/lib/l43_peg/combinators/map_error.rb +24 -0
- data/lib/l43_peg/combinators/satisfy.rb +36 -0
- data/lib/l43_peg/combinators/sequence.rb +41 -0
- data/lib/l43_peg/combinators.rb +62 -73
- data/lib/l43_peg/errors.rb +11 -0
- data/lib/l43_peg/failure.rb +17 -1
- data/lib/l43_peg/input.rb +12 -10
- data/lib/l43_peg/mappers.rb +21 -3
- data/lib/l43_peg/parser.rb +4 -8
- data/lib/l43_peg/parsers/char_parser.rb +48 -8
- data/lib/l43_peg/parsers/end_parser.rb +13 -12
- data/lib/l43_peg/parsers/eol_parser.rb +55 -0
- data/lib/l43_peg/parsers/escape_parser.rb +21 -0
- data/lib/l43_peg/parsers/failure_parser.rb +2 -2
- data/lib/l43_peg/parsers/int_parser.rb +9 -7
- data/lib/l43_peg/parsers/list_parser.rb +33 -0
- data/lib/l43_peg/parsers/literal_parser.rb +23 -0
- data/lib/l43_peg/parsers/name_parser.rb +13 -0
- data/lib/l43_peg/parsers/rgx_parser.rb +35 -20
- data/lib/l43_peg/parsers/string_parser.rb +26 -0
- data/lib/l43_peg/parsers/success_parser.rb +21 -0
- data/lib/l43_peg/parsers/symbol_parser.rb +19 -0
- data/lib/l43_peg/parsers/ws_parser.rb +20 -0
- data/lib/l43_peg/parsers.rb +50 -24
- data/lib/l43_peg/stop.rb +7 -7
- data/lib/l43_peg/success.rb +10 -12
- data/lib/l43_peg/version.rb +1 -1
- data/lib/l43_peg.rb +10 -29
- metadata +37 -13
- data/lib/l43_peg/cache.rb +0 -9
- data/lib/l43_peg/combinators/sel.rb +0 -24
- data/lib/l43_peg/combinators/seq.rb +0 -29
- data/lib/l43_peg/helper.rb +0 -13
- data/lib/l43_peg/parsers/rgx_tokenizer.rb +0 -24
- data/lib/l43_peg/parsers/sel_rgx_parser.rb +0 -25
- data/lib/l43_peg/parsers/token_parser.rb +0 -32
- data/lib/l43_peg/parsers/tokens_parser.rb +0 -63
- 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
|
data/lib/l43_peg/combinators.rb
CHANGED
|
@@ -1,82 +1,71 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def
|
|
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
|
-
def
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def
|
|
52
|
-
name
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def
|
|
58
|
-
|
|
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
|
data/lib/l43_peg/failure.rb
CHANGED
|
@@ -3,9 +3,25 @@
|
|
|
3
3
|
module L43Peg
|
|
4
4
|
class Failure
|
|
5
5
|
extend L43::OpenObject
|
|
6
|
-
attributes
|
|
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,
|
|
7
|
+
attributes col: 1, src: "", lnb: 1
|
|
8
8
|
|
|
9
9
|
def debug
|
|
10
10
|
"Input<" +
|
|
11
|
-
["col:#{col}", "
|
|
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? =
|
|
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<#{
|
|
32
|
+
"Input<#{src[0...n].inspect}@#{position_hr}>"
|
|
31
33
|
else
|
|
32
|
-
"Input<#{
|
|
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
|
|
44
|
-
_split(n) => col, lnb,
|
|
45
|
-
self.class.new(
|
|
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 =
|
|
53
|
+
head = src.slice(0...n)
|
|
52
54
|
lines = head.count("\n")
|
|
53
|
-
tail =
|
|
55
|
+
tail = src.slice(n..-1) || ''
|
|
54
56
|
new_col =
|
|
55
57
|
if lines.zero?
|
|
56
58
|
col + n
|
data/lib/l43_peg/mappers.rb
CHANGED
|
@@ -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|
|
data/lib/l43_peg/parser.rb
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
10
|
-
@__instance__ ||= new
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
private
|
|
12
|
+
private
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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 |
|
|
15
|
-
L43Peg::Failure.new(
|
|
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
|
-
|
|
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
|