l43_peg 0.0.1 → 0.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.
- checksums.yaml +4 -4
- data/LICENSE +235 -201
- data/README.md +57 -64
- data/lib/l43/match_data_extenstion.rb +6 -0
- data/lib/l43/open_object.rb +38 -0
- data/lib/l43/require_helper.rb +8 -0
- data/lib/l43_peg/cache.rb +9 -0
- data/lib/l43_peg/combinators/many.rb +38 -0
- data/lib/l43_peg/combinators/seq.rb +29 -0
- data/lib/l43_peg/combinators.rb +51 -0
- data/lib/l43_peg/failure.rb +9 -0
- data/lib/l43_peg/helper.rb +13 -0
- data/lib/l43_peg/input.rb +48 -0
- data/lib/l43_peg/mappers.rb +21 -0
- data/lib/l43_peg/parser.rb +19 -0
- data/lib/l43_peg/parsers/char_parser.rb +36 -0
- data/lib/l43_peg/parsers/end_parser.rb +27 -0
- data/lib/l43_peg/parsers/failure_parser.rb +21 -0
- data/lib/l43_peg/parsers/int_parser.rb +26 -0
- data/lib/l43_peg/parsers/rgx_parser.rb +45 -0
- data/lib/l43_peg/parsers/token_parser.rb +32 -0
- data/lib/l43_peg/parsers/tokens_parser.rb +61 -0
- data/lib/l43_peg/parsers/verb_parser.rb +23 -0
- data/lib/l43_peg/parsers.rb +19 -0
- data/lib/l43_peg/success.rb +30 -0
- data/lib/l43_peg/tokens.rb +46 -0
- data/lib/l43_peg.rb +6 -3
- metadata +25 -2
data/README.md
CHANGED
@@ -1,91 +1,84 @@
|
|
1
|
-
#
|
1
|
+
# L43Peg
|
2
2
|
|
3
|
+
## A Parse Expression Grammar library for Ruby
|
3
4
|
|
4
|
-
|
5
|
+
### This Version (v0.1.x) is Alpha Quality (many PEG features are missing, like recursion and even alternatives.
|
5
6
|
|
6
|
-
|
7
|
+
It is however released because it offers quite some nice parsing of ARGV which shall be demonstrated by the following
|
8
|
+
[speculations](https://rubygems.org/gems/speculate_about)
|
7
9
|
|
8
|
-
|
10
|
+
See [README_spec.rb](spec/speculations/README_spec.rb) for the generated code for details
|
9
11
|
|
10
|
-
|
12
|
+
### Context: `arg_parser`
|
11
13
|
|
12
|
-
|
13
|
-
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
|
14
|
+
Given the following argument specification
|
14
15
|
|
16
|
+
```ruby
|
17
|
+
include L43Peg::Combinators
|
18
|
+
let :args_spec do
|
19
|
+
{
|
20
|
+
start: "--start=(.*)",
|
21
|
+
end: "(?:--end|-e)=(.*)",
|
22
|
+
kwd: "--(alpha|beta|gamma)"
|
23
|
+
}
|
24
|
+
end
|
15
25
|
```
|
16
|
-
cd existing_repo
|
17
|
-
git remote add origin https://gitlab.com/lab421/l43_my_ruby.git
|
18
|
-
git branch -M main
|
19
|
-
git push -uf origin main
|
20
|
-
```
|
21
|
-
|
22
|
-
## Integrate with your tools
|
23
|
-
|
24
|
-
- [ ] [Set up project integrations](https://gitlab.com/lab421/l43_my_ruby/-/settings/integrations)
|
25
|
-
|
26
|
-
## Collaborate with your team
|
27
|
-
|
28
|
-
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
29
|
-
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
30
|
-
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
31
|
-
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
32
|
-
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
|
33
|
-
|
34
|
-
## Test and Deploy
|
35
|
-
|
36
|
-
Use the built-in continuous integration in GitLab.
|
37
26
|
|
38
|
-
|
39
|
-
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
40
|
-
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
41
|
-
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
42
|
-
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
27
|
+
And the assoicated parser
|
43
28
|
|
44
|
-
|
29
|
+
```ruby
|
30
|
+
let(:parser) { args_parser(args_spec) }
|
31
|
+
```
|
45
32
|
|
46
|
-
|
33
|
+
Then we can parse some input
|
47
34
|
|
48
|
-
|
35
|
+
```ruby
|
36
|
+
assert_parse_success(parser, %w[--start=42 --beta -e=44], ast: {start: "42", kwd: "beta", end: "44"}, rest: [])
|
37
|
+
```
|
49
38
|
|
50
|
-
|
51
|
-
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
|
39
|
+
And we can get the rest in a list of tokens
|
52
40
|
|
53
|
-
|
54
|
-
|
41
|
+
```ruby
|
42
|
+
assert_parse_success(parser, %w[--start=42 --beta -e=44 -s=not_an_arg --end=too_late], ast: {start: "42", kwd: "beta", end: "44"}, rest: %w[-s=not_an_arg --end=too_late])
|
43
|
+
```
|
55
44
|
|
56
|
-
|
57
|
-
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
45
|
+
Also note that multiple values are passed into an array
|
58
46
|
|
59
|
-
|
60
|
-
|
47
|
+
```ruby
|
48
|
+
input = %w[--end=42 --beta -e=44 --beta --end=not_too_late --gamma]
|
49
|
+
ast = {end: %w[42 44 not_too_late], kwd: %w[beta beta gamma]}
|
50
|
+
assert_parse_success(parser, input, ast:, rest: [])
|
51
|
+
```
|
61
52
|
|
62
|
-
|
63
|
-
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
53
|
+
#### Context: Postprocessing
|
64
54
|
|
65
|
-
|
66
|
-
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
55
|
+
When we map the parser
|
67
56
|
|
68
|
-
|
69
|
-
|
57
|
+
```ruby
|
58
|
+
let :int_args do
|
59
|
+
{
|
60
|
+
start: "--start=(.*)",
|
61
|
+
end: "--end=(.*)",
|
62
|
+
inc: "--inc=(.*)"
|
63
|
+
}
|
64
|
+
end
|
65
|
+
let(:int_arg_parser) {args_parser(int_args, name: "int parser", &:to_i)}
|
66
|
+
```
|
70
67
|
|
71
|
-
|
72
|
-
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
68
|
+
Then we can convert the string valus
|
73
69
|
|
74
|
-
|
75
|
-
|
70
|
+
```ruby
|
71
|
+
assert_parse_success(parser, %w[--start=42 --end=44 --inc=2], ast: {start: 42, end: 44, inc: 2}, rest: [])
|
72
|
+
```
|
76
73
|
|
77
|
-
## Contributing
|
78
|
-
State if you are open to contributions and what your requirements are for accepting them.
|
79
74
|
|
80
|
-
|
75
|
+
## Author
|
81
76
|
|
82
|
-
|
77
|
+
Copyright © 2024 Robert Dober
|
78
|
+
robert.dober@gmail.com
|
83
79
|
|
84
|
-
|
85
|
-
Show your appreciation to those who have contributed to the project.
|
80
|
+
# LICENSE
|
86
81
|
|
87
|
-
|
88
|
-
For open source projects, say how it is licensed.
|
82
|
+
GNU AFFERO GENERAL PUBLIC LICENSE, Version 3, 19 November 2007. Please refer to [LICENSE](LICENSE) for details.
|
89
83
|
|
90
|
-
|
91
|
-
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
84
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module L43
|
4
|
+
module OpenObject
|
5
|
+
def attributes(*atts, **defaults)
|
6
|
+
attr_reader(*atts)
|
7
|
+
attr_reader(*defaults.keys)
|
8
|
+
|
9
|
+
define_method :== do |other|
|
10
|
+
other&.to_h == to_h
|
11
|
+
end
|
12
|
+
|
13
|
+
define_method :initialize do |**kwds|
|
14
|
+
missing = atts - kwds.keys
|
15
|
+
raise ArgumentError, "missing required keyword parameters: #{missing.inspect}" unless missing.empty?
|
16
|
+
spurious = kwds.keys - atts - defaults.keys
|
17
|
+
raise ArgumentError, "spurious keyword parameters: #{spurious.inspect}" unless spurious.empty?
|
18
|
+
|
19
|
+
values = defaults.merge(kwds)
|
20
|
+
values.each do |key, value|
|
21
|
+
instance_variable_set("@#{key}", value)
|
22
|
+
end
|
23
|
+
_init if respond_to?(:_init)
|
24
|
+
end
|
25
|
+
|
26
|
+
define_method :to_h do |*|
|
27
|
+
first = atts.inject Hash.new do |h, attribute|
|
28
|
+
h.update(attribute => send(attribute))
|
29
|
+
end
|
30
|
+
defaults.keys.inject first do |h, attribute|
|
31
|
+
h.update(attribute => send(attribute))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
alias_method :deconstruct_keys, :to_h
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../helper"
|
4
|
+
module L43Peg
|
5
|
+
module Combinators
|
6
|
+
module Many extend self
|
7
|
+
|
8
|
+
include L43Peg::Helper
|
9
|
+
def many(input:, cache:, name:, parser:, min:, max:)
|
10
|
+
curr_input = input
|
11
|
+
curr_cache = cache
|
12
|
+
count = 0
|
13
|
+
ast = []
|
14
|
+
|
15
|
+
loop do
|
16
|
+
if max && count == max
|
17
|
+
return succeed_parser(ast, curr_input, cache: curr_cache)
|
18
|
+
end
|
19
|
+
case parser.(curr_input, cache: curr_cache)
|
20
|
+
in L43Peg::Success => success
|
21
|
+
ast.push(success.ast)
|
22
|
+
curr_input = success.rest
|
23
|
+
curr_cache = success.cache
|
24
|
+
count += 1
|
25
|
+
in L43Peg::Failure
|
26
|
+
if count < min
|
27
|
+
return fail_parser("many #{name} should match at least #{min} times but did only #{count} times")
|
28
|
+
end
|
29
|
+
return succeed_parser(ast, curr_input, cache: curr_cache)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../helper"
|
4
|
+
module L43Peg
|
5
|
+
module Combinators
|
6
|
+
module Seq extend self
|
7
|
+
|
8
|
+
include L43Peg::Helper
|
9
|
+
def seq(input:, cache:, name:, parsers:)
|
10
|
+
curr_input = input
|
11
|
+
curr_cache = cache
|
12
|
+
ast = []
|
13
|
+
parsers.each do |parser|
|
14
|
+
case parser.(curr_input, cache: curr_cache)
|
15
|
+
in L43Peg::Failure => failure
|
16
|
+
return L43Peg::Failure.new(reason: "#{failure.reason} in (#{name})", position: failure.position, input:)
|
17
|
+
in L43Peg::Success => success
|
18
|
+
ast.push(success.ast)
|
19
|
+
curr_input = success.rest
|
20
|
+
curr_cache = success.cache
|
21
|
+
end
|
22
|
+
end
|
23
|
+
succeed_parser(ast, curr_input, cache: curr_cache)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
require_relative 'mappers'
|
5
|
+
require_relative 'parser'
|
6
|
+
require_relative 'parsers'
|
7
|
+
require_subdir('combinators') {}
|
8
|
+
|
9
|
+
module L43Peg
|
10
|
+
module Combinators extend self
|
11
|
+
|
12
|
+
include L43Peg::Helper
|
13
|
+
include L43Peg::Mappers
|
14
|
+
include L43Peg::Parsers
|
15
|
+
|
16
|
+
def args_parser(definitions, name: nil, &block)
|
17
|
+
parser = tokens_parser(definitions, &block)
|
18
|
+
name = name || "args_parser(#{definitions.inspect})"
|
19
|
+
inner = many(parser)
|
20
|
+
map(inner, name:, fn: join_maps)
|
21
|
+
end
|
22
|
+
|
23
|
+
def many(parser, name: nil, min: 0, max: nil)
|
24
|
+
Parser.new(name || "many(#{parser.name})") {|input, cache, name1=nil| Many.many(input:, cache:, name: name1 || name, parser:, min:, max:)}
|
25
|
+
end
|
26
|
+
|
27
|
+
def map(parser, name: nil, fn: nil, &mapper)
|
28
|
+
raise ArgumentError, "must not provide keyword parameyer fn and a block" if fn && mapper
|
29
|
+
mapper = fn || mapper
|
30
|
+
raise ArgumentError, "must provide keyword parameyer fn or a block" unless mapper
|
31
|
+
Parser.new(name || "map(#{parser.name})") {|input, cache, name=nil| _map(input:, cache:, name:, parser:, mapper:)}
|
32
|
+
end
|
33
|
+
|
34
|
+
def seq(*parsers, name: nil)
|
35
|
+
name ||= "seq(#{parsers.map(&:name).join(", ")})"
|
36
|
+
Parser.new(name) {|input, cache, _name=nil| Seq.seq(input:, cache:, name:, parsers:)}
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def _map(input:, cache:, name:, parser:, mapper:)
|
42
|
+
case parser.(input, cache:)
|
43
|
+
in L43Peg::Failure => failure
|
44
|
+
failure
|
45
|
+
in L43Peg::Success => success
|
46
|
+
success.map(&mapper)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module L43Peg
|
4
|
+
module Helper
|
5
|
+
def fail_parser(reason)
|
6
|
+
L43Peg::Failure.new(reason:)
|
7
|
+
end
|
8
|
+
def succeed_parser(ast, input, cache: nil)
|
9
|
+
L43Peg::Success.new(ast:, rest: input, cache: cache || input.cache)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module L43Peg
|
4
|
+
class Input
|
5
|
+
extend L43::OpenObject
|
6
|
+
|
7
|
+
attributes col: 1, input: "", lnb: 1, context: {}
|
8
|
+
|
9
|
+
def drop(by=nil)
|
10
|
+
case by
|
11
|
+
when nil
|
12
|
+
_drop_by_n(1)
|
13
|
+
when String
|
14
|
+
_drop_by_n(by.length)
|
15
|
+
else
|
16
|
+
_drop_by_n(by)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def empty? = input.empty?
|
21
|
+
|
22
|
+
def position = [@col, @lnb]
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def _drop_by_n(n)
|
27
|
+
return self if input.empty?
|
28
|
+
_split(n) => col, lnb, head, tail
|
29
|
+
self.class.new(input: tail, context:, col:, lnb:)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Very inefficent but sufficent for my use cases so far
|
33
|
+
# and convenient for regex parsers
|
34
|
+
def _split(n)
|
35
|
+
head = input.slice(0...n)
|
36
|
+
lines = head.count("\n")
|
37
|
+
tail = input.slice(n..-1) || ''
|
38
|
+
new_col =
|
39
|
+
if lines.zero?
|
40
|
+
col + n
|
41
|
+
else
|
42
|
+
1 + head.sub(%r{\A.*\n}m, "").length
|
43
|
+
end
|
44
|
+
[new_col, lnb + lines, head, tail]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module L43Peg
|
4
|
+
module Mappers
|
5
|
+
|
6
|
+
def join_and_to_i
|
7
|
+
-> list do
|
8
|
+
list.join.to_i
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def join_maps
|
13
|
+
-> maps do
|
14
|
+
maps.reduce Hash.new do |map, entry|
|
15
|
+
map.merge(entry) { |*args| args.drop(1).flatten }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module L43Peg
|
4
|
+
class Parser
|
5
|
+
|
6
|
+
attr_reader :fn, :name
|
7
|
+
|
8
|
+
def call(input, cache: L43Peg::Cache.new) = fn.(input, cache, name)
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def initialize(name, &fn)
|
13
|
+
@name = name
|
14
|
+
@fn = fn
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module L43Peg
|
4
|
+
module Parsers
|
5
|
+
class CharParser < L43Peg::Parser
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def initialize(charset=nil)
|
10
|
+
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)
|
16
|
+
else
|
17
|
+
L43Peg::Failure.new(cache:, input:, parsed_by: self, reason: "char #{input.input[0].inspect}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def _mk_set(charset)
|
23
|
+
return unless charset
|
24
|
+
case charset
|
25
|
+
when String
|
26
|
+
Set.new(charset.split(""))
|
27
|
+
when Set
|
28
|
+
charset
|
29
|
+
else
|
30
|
+
Set.new(charset)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../parsers'
|
4
|
+
|
5
|
+
module L43Peg
|
6
|
+
module Parsers
|
7
|
+
class EndParser < L43Peg::Parser
|
8
|
+
|
9
|
+
def self.instance
|
10
|
+
@__instance__ ||= new
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
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")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module L43Peg
|
4
|
+
module Parsers
|
5
|
+
class FailureParser < L43Peg::Parser
|
6
|
+
|
7
|
+
def self.instance
|
8
|
+
@__instance__ ||= new
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
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")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../parsers'
|
4
|
+
|
5
|
+
module L43Peg
|
6
|
+
module Parsers
|
7
|
+
class IntParser < L43Peg::Parser
|
8
|
+
extend L43Peg::Combinators
|
9
|
+
extend L43Peg::Mappers
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def instance
|
13
|
+
@__instance__ ||= _mk_int_parser
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
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)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module L43Peg
|
4
|
+
module Parsers
|
5
|
+
class RgxParser < L43Peg::Parser
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def initialize(rgx, name: nil, **options)
|
10
|
+
name = name || "rgx_parser(#{rgx.inspect})"
|
11
|
+
rgx = _mk_rgx(rgx)
|
12
|
+
super(name) do |input, cache, _name|
|
13
|
+
case rgx.match(input.input)
|
14
|
+
in MatchData => md
|
15
|
+
ast = _from_match(md, options)
|
16
|
+
L43Peg::Success.new(ast:, cache:, rest: input.drop(md[0]), position: input.position)
|
17
|
+
else
|
18
|
+
L43Peg::Failure.new(cache:, input:, parsed_by: self, reason: "input does not match #{rgx.inspect} (in #{name})")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def _from_match(md, options)
|
24
|
+
case options[:capture]
|
25
|
+
in NilClass
|
26
|
+
md[1] || md[0]
|
27
|
+
in Integer => n
|
28
|
+
md[n]
|
29
|
+
in :all
|
30
|
+
md.to_a
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def _mk_rgx(rgx)
|
35
|
+
case rgx
|
36
|
+
when String
|
37
|
+
Regexp.compile("\\A#{rgx}")
|
38
|
+
else
|
39
|
+
rgx
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module L43Peg
|
4
|
+
module Parsers
|
5
|
+
class TokenParser < L43Peg::Parser
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def initialize(token_spec, name: nil, **options)
|
10
|
+
_check_arg!(token_spec)
|
11
|
+
name = name || "token_parser(#{token_spec.inspect})"
|
12
|
+
super(name) do |tokens, cache, _name|
|
13
|
+
if result = tokens.match(token_spec, options[:capture])
|
14
|
+
L43Peg::Success.new(ast: result, cache:, rest: tokens.drop(1), position: tokens.position)
|
15
|
+
else
|
16
|
+
reason = "token #{tokens.head.inspect} does not match #{token_spec.inspect} (in #{name})"
|
17
|
+
L43Peg::Failure.new(cache:, input: tokens, parsed_by: self, reason:)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def _check_arg!(token_spec)
|
23
|
+
case token_spec
|
24
|
+
when Regexp, String
|
25
|
+
else
|
26
|
+
raise ArgumentError, "#{token_spec.inspect} must be a regular expression or a string"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|