yaparc 0.3.0 → 0.4.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/.envrc +1 -0
- data/CHANGELOG.md +10 -0
- data/Gemfile +1 -1
- data/README +6 -6
- data/Rakefile +12 -6
- data/TODO +11 -0
- data/lib/yaparc/abstract_parser.rb +16 -0
- data/lib/yaparc/alt.rb +22 -0
- data/lib/yaparc/apply.rb +18 -0
- data/lib/yaparc/char.rb +16 -0
- data/lib/yaparc/cr.rb +11 -0
- data/lib/yaparc/digit.rb +11 -0
- data/lib/yaparc/fail_parser.rb +11 -0
- data/lib/yaparc/ident.rb +18 -0
- data/lib/yaparc/identifier.rb +32 -0
- data/lib/yaparc/item.rb +17 -0
- data/lib/yaparc/literal.rb +13 -0
- data/lib/yaparc/many.rb +14 -0
- data/lib/yaparc/many_one.rb +27 -0
- data/lib/yaparc/nat.rb +11 -0
- data/lib/yaparc/natural.rb +12 -0
- data/lib/yaparc/no_fail.rb +20 -0
- data/lib/yaparc/parsable.rb +20 -0
- data/lib/yaparc/regex.rb +22 -0
- data/lib/yaparc/satisfy.rb +28 -0
- data/lib/yaparc/seq.rb +33 -0
- data/lib/yaparc/space.rb +11 -0
- data/lib/yaparc/string.rb +24 -0
- data/lib/yaparc/succeed.rb +14 -0
- data/lib/yaparc/symbol.rb +11 -0
- data/lib/yaparc/tokenize.rb +18 -0
- data/lib/yaparc/white_space.rb +11 -0
- data/lib/yaparc/zero_one.rb +20 -0
- data/lib/yaparc.rb +38 -608
- data/sig/yaparc.gen.rbs +89 -92
- data/yaparc.gemspec +17 -17
- metadata +32 -8
- data/manifest.scm +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: db3016ed938d0853130447ce2155b9e9978faf79798f137c99624d1539a9fc2b
|
|
4
|
+
data.tar.gz: 505b913fc07f99bf8cc7fb1be125aefedb767bb589d92f40c3bdc35ff6cccfb1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d53c0470dac4fa661db80607a2e752ab830a751fdd14f3453f838a80ffcd13993b013d32a2aa4c171dc3e8d4cf85154651ed656179669796c8cdac5120503c66
|
|
7
|
+
data.tar.gz: 75a76818b13a77821106dd09c65c5ae2c3a005ca8361797b75167b708f57846c4664a267f1eb40bba2d7ec9e75ec995291a77e51353423bd232f921475374d59
|
data/.envrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
use guix ruby ruby-rubocop
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.4.0 - 2026-06-23
|
|
6
|
+
|
|
7
|
+
* Remove `message` attribute from `Yaparc::Base`.
|
|
8
|
+
* Rename `Yaparc::Fail` to `Yaparc::FailParser`.
|
|
9
|
+
* Move `OK`, `Fail`, `Error` outside of `Result` and removed `Result` module.
|
|
10
|
+
* Remove `tree` attribute of `Parsable` module.
|
|
11
|
+
* Use newer keyword arguments. This might cause troubles with Ruby version 2
|
|
12
|
+
or lower.
|
|
13
|
+
* Limit `Tokenize`'s `prefix` and `postfix` write only.
|
|
14
|
+
|
|
5
15
|
## 0.3.0 - 2024-12-31
|
|
6
16
|
|
|
7
17
|
* Added `Yaparc::VERSION` constant.
|
data/Gemfile
CHANGED
data/README
CHANGED
|
@@ -26,7 +26,7 @@ Yaparc::Result::OK indicates success.
|
|
|
26
26
|
=== Primitive Parsers
|
|
27
27
|
|
|
28
28
|
* Yaparc::Succeed
|
|
29
|
-
* Yaparc::
|
|
29
|
+
* Yaparc::FailParser
|
|
30
30
|
* Yaparc::Item
|
|
31
31
|
* Yaparc::Satisfy
|
|
32
32
|
|
|
@@ -41,14 +41,14 @@ returns the singleton array <tt>[[1, "blah, blah, blah"]]</tt>.
|
|
|
41
41
|
parser.parse("blah, blah, blah")
|
|
42
42
|
#=> #<Yaparc::Result::OK:0xb7aaaf5c @input="blah, blah, blah", @value=1>
|
|
43
43
|
|
|
44
|
-
====
|
|
44
|
+
==== FailParser class
|
|
45
45
|
|
|
46
|
-
The parser Yaparc::
|
|
47
|
-
string.
|
|
46
|
+
The parser Yaparc::FailParser always fails, regardless of the contents of the
|
|
47
|
+
input string.
|
|
48
48
|
|
|
49
|
-
parser = Yaparc::
|
|
49
|
+
parser = Yaparc::FailParser.new
|
|
50
50
|
parser.parse("abc")
|
|
51
|
-
#=> #<Yaparc::Result::
|
|
51
|
+
#=> #<Yaparc::Result::FailParser:0xb7aa56b0 @value=nil>
|
|
52
52
|
|
|
53
53
|
==== Item class
|
|
54
54
|
|
data/Rakefile
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require 'bundler/gem_tasks'
|
|
4
|
+
require 'rake/testtask'
|
|
5
|
+
require "net/http"
|
|
5
6
|
|
|
6
7
|
Rake::TestTask.new(:test) do |t|
|
|
7
|
-
t.libs <<
|
|
8
|
-
t.libs <<
|
|
9
|
-
t.test_files = FileList[
|
|
8
|
+
t.libs << 'test'
|
|
9
|
+
t.libs << 'lib'
|
|
10
|
+
t.test_files = FileList['test/**/*_test.rb']
|
|
10
11
|
end
|
|
11
12
|
|
|
12
|
-
require
|
|
13
|
+
require 'rubocop/rake_task'
|
|
13
14
|
|
|
14
15
|
RuboCop::RakeTask.new
|
|
15
16
|
|
|
@@ -19,3 +20,8 @@ desc 'Generate signatures'
|
|
|
19
20
|
task :gensig do
|
|
20
21
|
sh 'typeprof', '-o', 'sig/yaparc.gen.rbs', 'sig/yaparc.rbs', *Dir['lib/**/*.rb']
|
|
21
22
|
end
|
|
23
|
+
|
|
24
|
+
file "test/abc.html" do |t|
|
|
25
|
+
doc = Net::HTTP.get("web.archive.org", "/web/20120814155205/http://www.norbeck.nu:80/abc/bnf/abc20bnf.htm")
|
|
26
|
+
File.write(t.name, doc)
|
|
27
|
+
end
|
data/TODO
ADDED
data/lib/yaparc/alt.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
class Alt
|
|
5
|
+
include Parsable
|
|
6
|
+
|
|
7
|
+
def initialize(*parsers)
|
|
8
|
+
@parser = lambda do |input|
|
|
9
|
+
final_result = Fail.new(input:)
|
|
10
|
+
parsers.each do |parser|
|
|
11
|
+
case result = parser.parse(input)
|
|
12
|
+
in Fail
|
|
13
|
+
next
|
|
14
|
+
in OK
|
|
15
|
+
break final_result = result
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
final_result
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/yaparc/apply.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
class Apply
|
|
5
|
+
include Parsable
|
|
6
|
+
|
|
7
|
+
def initialize(parser)
|
|
8
|
+
@parser = lambda do |input|
|
|
9
|
+
result = parser.parse(input)
|
|
10
|
+
if result.instance_of?(OK)
|
|
11
|
+
Succeed.new(yield(result.value)).parse(result.input)
|
|
12
|
+
else
|
|
13
|
+
FailParser.new.parse(input)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/yaparc/char.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
class Char
|
|
5
|
+
include Parsable
|
|
6
|
+
|
|
7
|
+
def initialize(char, case_sensitive = true)
|
|
8
|
+
equal_char = if case_sensitive
|
|
9
|
+
->(i) { i == char }
|
|
10
|
+
else # in case of case-insentive
|
|
11
|
+
->(i) { i.casecmp(char) == 0 }
|
|
12
|
+
end
|
|
13
|
+
@parser = proc { Satisfy.new(equal_char) }
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/yaparc/cr.rb
ADDED
data/lib/yaparc/digit.rb
ADDED
data/lib/yaparc/ident.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
class Ident
|
|
5
|
+
include Parsable
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@parser = proc do
|
|
9
|
+
Seq.new(
|
|
10
|
+
Satisfy.new(IS_LOWER),
|
|
11
|
+
Many.new(Satisfy.new(IS_ALPHANUM), '')
|
|
12
|
+
) do |head, tail|
|
|
13
|
+
head + tail
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
# Refer to http://www.cs.nott.ac.uk/~gmh/monparsing.pdf, p.23
|
|
5
|
+
class Identifier
|
|
6
|
+
include Yaparc::Parsable
|
|
7
|
+
|
|
8
|
+
IDENTIFIER_REGEX = /\A[a-zA-Z_]+[a-zA-Z0-9_]*/
|
|
9
|
+
|
|
10
|
+
def initialize(regex: nil, exclude: nil)
|
|
11
|
+
identifier_regex = ::Yaparc::Regex.new(regex || IDENTIFIER_REGEX)
|
|
12
|
+
|
|
13
|
+
tokenizer = Tokenize.new(identifier_regex)
|
|
14
|
+
|
|
15
|
+
unless exclude
|
|
16
|
+
@parser = proc { tokenizer }
|
|
17
|
+
return
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
@parser = lambda do |input|
|
|
21
|
+
keyword_parsers = exclude.map { |keyword| Yaparc::String.new(keyword) }
|
|
22
|
+
|
|
23
|
+
case result = Yaparc::Alt.new(*keyword_parsers).parse(input)
|
|
24
|
+
when Yaparc::OK
|
|
25
|
+
Yaparc::FailParser.new
|
|
26
|
+
else # Fail or Error
|
|
27
|
+
tokenizer
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/yaparc/item.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
class Item
|
|
5
|
+
include Parsable
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@parser = lambda do |input|
|
|
9
|
+
if input.nil? || input.empty?
|
|
10
|
+
Fail.new(input:)
|
|
11
|
+
else
|
|
12
|
+
OK.new(value: input[0], input: input[1..])
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/yaparc/many.rb
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
# permits zero or more applications of parser.
|
|
5
|
+
class Many
|
|
6
|
+
include Parsable
|
|
7
|
+
|
|
8
|
+
def initialize(parser, identity = [])
|
|
9
|
+
@parser = proc {
|
|
10
|
+
Alt.new(ManyOne.new(parser, identity), Succeed.new(identity))
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
# requires at least one successfull application of parser.
|
|
5
|
+
class ManyOne
|
|
6
|
+
include Parsable
|
|
7
|
+
|
|
8
|
+
def initialize(parser, identity = [])
|
|
9
|
+
@parser = lambda do |_input|
|
|
10
|
+
Seq.new(parser, Many.new(parser, identity)) do |head, tail|
|
|
11
|
+
case head
|
|
12
|
+
when ::String, ::Array, ::Integer
|
|
13
|
+
head + tail
|
|
14
|
+
when ::Hash
|
|
15
|
+
head.merge(tail)
|
|
16
|
+
else
|
|
17
|
+
if tail.nil?
|
|
18
|
+
head
|
|
19
|
+
else
|
|
20
|
+
[head] + tail
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/yaparc/nat.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
# hutton92:_higher_order_funct_parsin,p.19
|
|
5
|
+
# https://www.cambridge.org/core/journals/journal-of-functional-programming/article/higherorder-functions-for-parsing/0490F2C8511F7625F9FC15BFFEDBB0AA
|
|
6
|
+
class NoFail
|
|
7
|
+
include Parsable
|
|
8
|
+
|
|
9
|
+
def initialize(parser)
|
|
10
|
+
@parser = lambda do |input|
|
|
11
|
+
result = parser.parse(input)
|
|
12
|
+
if result.instance_of?(Fail)
|
|
13
|
+
Error.new(value: result.value, input: result.input)
|
|
14
|
+
else
|
|
15
|
+
Succeed.new(result.value)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Yaparc
|
|
2
|
+
module Parsable
|
|
3
|
+
IS_LOWER = ->(c) { c >= 'a' and c <= 'z' }
|
|
4
|
+
IS_ALPHANUM = ->(c) { (c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') }
|
|
5
|
+
IS_DIGIT = ->(i) { i >= '0' and i <= '9' }
|
|
6
|
+
IS_SPACE = ->(i) { i == ' ' }
|
|
7
|
+
IS_WHITESPACE = ->(i) { [' ', "\n", "\t"].include?(i) }
|
|
8
|
+
IS_CR = ->(i) { i == "\n" }
|
|
9
|
+
|
|
10
|
+
def parse(input)
|
|
11
|
+
result = @parser.call(input)
|
|
12
|
+
|
|
13
|
+
if result.respond_to?(:parse)
|
|
14
|
+
result.parse(input)
|
|
15
|
+
else
|
|
16
|
+
result
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/yaparc/regex.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
class Regex
|
|
5
|
+
include Parsable
|
|
6
|
+
|
|
7
|
+
def initialize(regex)
|
|
8
|
+
@regex = regex
|
|
9
|
+
@parser = lambda do |input|
|
|
10
|
+
if match = Regexp.new(regex).match(input)
|
|
11
|
+
if block_given?
|
|
12
|
+
Succeed.new(yield(*match.to_a[1..])).parse(match.post_match)
|
|
13
|
+
else
|
|
14
|
+
OK.new(value: match[0], input: match.post_match)
|
|
15
|
+
end
|
|
16
|
+
else
|
|
17
|
+
Fail.new(input:)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
class Satisfy
|
|
5
|
+
include Parsable
|
|
6
|
+
|
|
7
|
+
def initialize(predicate)
|
|
8
|
+
@parser = lambda do |input|
|
|
9
|
+
result = Item.new.parse(input)
|
|
10
|
+
|
|
11
|
+
if result.instance_of?(OK) && predicate.call(result.value)
|
|
12
|
+
Succeed.new(result.value, result.input)
|
|
13
|
+
else
|
|
14
|
+
FailParser.new
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def parse(input)
|
|
20
|
+
case parser = @parser.call(input)
|
|
21
|
+
in Succeed
|
|
22
|
+
parser.parse(parser.remaining)
|
|
23
|
+
in FailParser
|
|
24
|
+
parser.parse(input)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/yaparc/seq.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
class Seq
|
|
5
|
+
include Parsable
|
|
6
|
+
|
|
7
|
+
def initialize(*parsers)
|
|
8
|
+
@parser = lambda do |input|
|
|
9
|
+
args = []
|
|
10
|
+
initial_result = OK.new(input:)
|
|
11
|
+
final_result = parsers.inject(initial_result) do |subsequent, parser|
|
|
12
|
+
result = parser.parse(subsequent.input)
|
|
13
|
+
break Fail.new(input: subsequent.input) if result.instance_of?(Fail)
|
|
14
|
+
|
|
15
|
+
args << result.value
|
|
16
|
+
result
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
case final_result
|
|
20
|
+
in Fail
|
|
21
|
+
Fail.new(input: final_result.input)
|
|
22
|
+
in OK
|
|
23
|
+
final_value = if block_given?
|
|
24
|
+
yield(*args)
|
|
25
|
+
else
|
|
26
|
+
args.last
|
|
27
|
+
end
|
|
28
|
+
OK.new(value: final_value, input: final_result.input)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/yaparc/space.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
class String
|
|
5
|
+
include Parsable
|
|
6
|
+
|
|
7
|
+
def initialize(string, case_sensitive = true)
|
|
8
|
+
@parser = lambda do |_input|
|
|
9
|
+
result = Item.new.parse(string)
|
|
10
|
+
if result.instance_of?(OK)
|
|
11
|
+
Seq.new(
|
|
12
|
+
Char.new(result.value, case_sensitive),
|
|
13
|
+
Yaparc::String.new(result.input, case_sensitive),
|
|
14
|
+
Succeed.new(result.value + result.input)
|
|
15
|
+
) do |_, _, succeed_result|
|
|
16
|
+
succeed_result
|
|
17
|
+
end
|
|
18
|
+
else
|
|
19
|
+
Succeed.new(result)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
class Tokenize
|
|
5
|
+
include Parsable
|
|
6
|
+
|
|
7
|
+
attr_writer :prefix, :postfix
|
|
8
|
+
|
|
9
|
+
def initialize(parser, prefix: nil, postfix: nil)
|
|
10
|
+
@parser = lambda do |_input|
|
|
11
|
+
@prefix = prefix || WhiteSpace.new
|
|
12
|
+
@postfix = postfix || WhiteSpace.new
|
|
13
|
+
block_given? and yield self
|
|
14
|
+
Seq.new(@prefix, parser, @postfix) { |_, vs, _| vs }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require_relative 'parsable'
|
|
2
|
+
|
|
3
|
+
module Yaparc
|
|
4
|
+
class ZeroOne
|
|
5
|
+
include Parsable
|
|
6
|
+
|
|
7
|
+
def initialize(parser, identity = [])
|
|
8
|
+
@parser = lambda do |input|
|
|
9
|
+
case (result = parser.parse(input))
|
|
10
|
+
in Fail
|
|
11
|
+
OK.new(value: identity, input:)
|
|
12
|
+
in Error
|
|
13
|
+
Error.new(value: result.value, input: result.input)
|
|
14
|
+
in OK
|
|
15
|
+
result
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|