yaparc 0.2.3 → 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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.envrc +1 -0
  4. data/.rdoc_options +1 -0
  5. data/CHANGELOG.md +19 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +20 -0
  8. data/README +106 -86
  9. data/Rakefile +27 -0
  10. data/TODO +11 -0
  11. data/lib/yaparc/abstract_parser.rb +16 -0
  12. data/lib/yaparc/alt.rb +22 -0
  13. data/lib/yaparc/apply.rb +18 -0
  14. data/lib/yaparc/char.rb +16 -0
  15. data/lib/yaparc/cr.rb +11 -0
  16. data/lib/yaparc/digit.rb +11 -0
  17. data/lib/yaparc/fail_parser.rb +11 -0
  18. data/lib/yaparc/ident.rb +18 -0
  19. data/lib/yaparc/identifier.rb +32 -0
  20. data/lib/yaparc/item.rb +17 -0
  21. data/lib/yaparc/literal.rb +13 -0
  22. data/lib/yaparc/many.rb +14 -0
  23. data/lib/yaparc/many_one.rb +27 -0
  24. data/lib/yaparc/nat.rb +11 -0
  25. data/lib/yaparc/natural.rb +12 -0
  26. data/lib/yaparc/no_fail.rb +20 -0
  27. data/lib/yaparc/parsable.rb +20 -0
  28. data/lib/yaparc/regex.rb +22 -0
  29. data/lib/yaparc/satisfy.rb +28 -0
  30. data/lib/yaparc/seq.rb +33 -0
  31. data/lib/yaparc/space.rb +11 -0
  32. data/lib/yaparc/string.rb +24 -0
  33. data/lib/yaparc/succeed.rb +14 -0
  34. data/lib/yaparc/symbol.rb +11 -0
  35. data/lib/yaparc/tokenize.rb +18 -0
  36. data/lib/yaparc/white_space.rb +11 -0
  37. data/lib/yaparc/zero_one.rb +20 -0
  38. data/lib/yaparc.rb +40 -605
  39. data/sig/yaparc.gen.rbs +217 -0
  40. data/sig/yaparc.rbs +4 -0
  41. data/yaparc.gemspec +36 -0
  42. metadata +115 -58
  43. data/test/n3-report.html +0 -169
  44. data/test/n3.bnf +0 -129
  45. data/test/test_abc.rb.bak +0 -112
  46. data/test/test_calc.rb +0 -112
  47. data/test/test_lambda.rb +0 -87
  48. data/test/test_metric.rb +0 -617
  49. data/test/test_owl.rb +0 -1039
  50. data/test/test_parser.rb +0 -460
  51. data/test/test_prolog.rb +0 -287
  52. data/test/test_sql.rb +0 -317
  53. data/test/test_uri.rb +0 -752
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: db3016ed938d0853130447ce2155b9e9978faf79798f137c99624d1539a9fc2b
4
+ data.tar.gz: 505b913fc07f99bf8cc7fb1be125aefedb767bb589d92f40c3bdc35ff6cccfb1
5
+ SHA512:
6
+ metadata.gz: d53c0470dac4fa661db80607a2e752ab830a751fdd14f3453f838a80ffcd13993b013d32a2aa4c171dc3e8d4cf85154651ed656179669796c8cdac5120503c66
7
+ data.tar.gz: 75a76818b13a77821106dd09c65c5ae2c3a005ca8361797b75167b708f57846c4664a267f1eb40bba2d7ec9e75ec995291a77e51353423bd232f921475374d59
data/.document ADDED
@@ -0,0 +1,3 @@
1
+ README
2
+ LICENSE
3
+ lib
data/.envrc ADDED
@@ -0,0 +1 @@
1
+ use guix ruby ruby-rubocop
data/.rdoc_options ADDED
@@ -0,0 +1 @@
1
+ main_page: README
data/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # yaparc change log
2
+
3
+ ## Unreleased
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
+
15
+ ## 0.3.0 - 2024-12-31
16
+
17
+ * Added `Yaparc::VERSION` constant.
18
+ * Removed `Yaparc` module's `@@identifier_regex` class variable.
19
+ * Added `Yaparc::IDENTIFIER_REGEX` constant.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2007-2009 Akimichi Tatsukawa <akimichi.tatsukawa@gmail.com>
2
+ Copyright 2024 gemmaro <gemmaro.dev@gmail.com>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the “Software”), to
6
+ deal in the Software without restriction, including without limitation the
7
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
+ sell copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
data/README CHANGED
@@ -1,57 +1,68 @@
1
- = Synopsis
1
+ = yaparc
2
2
 
3
- There are several implementations of parser combinator in ruby. This is a yet another simple combinator parser libraryin ruby.
3
+ == Synopsis
4
4
 
5
- = Requirements
5
+ There are several implementations of parser combinator in Ruby. This is an
6
+ yet another simple combinator parser library in Ruby.
6
7
 
7
- * Ruby (http://www.ruby-lang.org/)
8
- * RubyGem (http://rubyforge.org/projects/rubygems/)
8
+ == Install
9
9
 
10
- = Install
10
+ gem install yaparc
11
11
 
12
- $ gem install yaparc
12
+ == Usage
13
13
 
14
- = Usage
14
+ In combinator parser, each parser is construct as a function taking input
15
+ string as arguments. Larger parsers are built from smaller parsers. Although
16
+ combinators are higher-order functions in ordinary functional languages, they
17
+ are constructed as classes in yaparc, because Ruby has more object-oriented
18
+ than functional property.
15
19
 
16
- In combinator parser, each parser is construct as a function taking input string as arguments. Larger parsers are built from smaller parsers. Although combinators are higher-order functions in ordinary functional languages, they are constructed as classes in yaparc, because Ruby has more object-oriented than functional property.
20
+ All parsers has +parse+ method, each of which takes input string as its
21
+ arguments except Yaparc::Satisfy parser. Every parser returns either
22
+ Yaparc::Result::OK or Yaparc::Result::Fail as their result of parsing. An
23
+ instance of Yaparc::Result::Fail denotes faiilure, and instance of
24
+ Yaparc::Result::OK indicates success.
17
25
 
18
- All parsers has 'parse' method, each of which takes input string as its arguments except SatisfyParser. Every parser returns either Result::OK or Result::Fail as their result of parsing. An instance of Result::Fail denotes faiilure, and instance of Result::OK indicates success.
26
+ === Primitive Parsers
19
27
 
20
- == Primitive Parsers
28
+ * Yaparc::Succeed
29
+ * Yaparc::FailParser
30
+ * Yaparc::Item
31
+ * Yaparc::Satisfy
21
32
 
22
- * Succeed
23
- * Fail
24
- * Item
25
- * Satisfy
33
+ ==== Succeed class
26
34
 
27
- === Succeed class
28
-
29
- The parser Succeed always succeeds with the result value, without consuming any of the input string.
30
- In the following example, Succeed#parse takes an input string "blah, blah, blah" and returns the singleton array [[1, "blah, blah, blah"]].
35
+ The parser Yaparc::Succeed always succeeds with the result value, without
36
+ consuming any of the input string. In the following example,
37
+ Yaparc::Succeed#parse takes an input string <tt>blah, blah, blah</tt> and
38
+ returns the singleton array <tt>[[1, "blah, blah, blah"]]</tt>.
31
39
 
32
40
  parser = Yaparc::Succeed.new(1)
33
41
  parser.parse("blah, blah, blah")
34
- => #<Yaparc::Result::OK:0xb7aaaf5c @input="blah, blah, blah", @value=1>
42
+ #=> #<Yaparc::Result::OK:0xb7aaaf5c @input="blah, blah, blah", @value=1>
35
43
 
36
- === Fail class
44
+ ==== FailParser class
37
45
 
38
- The parser Fail always fails, regardless of the contents of the input string.
46
+ The parser Yaparc::FailParser always fails, regardless of the contents of the
47
+ input string.
39
48
 
40
- parser = Yaparc::Fail.new
49
+ parser = Yaparc::FailParser.new
41
50
  parser.parse("abc")
42
- => #<Yaparc::Result::Fail:0xb7aa56b0 @value=nil>
51
+ #=> #<Yaparc::Result::FailParser:0xb7aa56b0 @value=nil>
43
52
 
44
- === Item class
53
+ ==== Item class
45
54
 
46
- The parser Item fails if the input string is empty, and succeeds with the first character as the result value otherwise.
55
+ The parser Yaparc::Item fails if the input string is empty, and succeeds with
56
+ the first character as the result value otherwise.
47
57
 
48
58
  parser = Yaparc::Item.new
49
59
  parser.parse("abc")
50
- => #<Yaparc::Result::OK:0xb7a9fdb4 @input="bc", @value="a">
60
+ #=> #<Yaparc::Result::OK:0xb7a9fdb4 @input="bc", @value="a">
51
61
 
52
- === Satisfy class
62
+ ==== Satisfy class
53
63
 
54
- The parser Satisfy recognizes a single input via predicate which determines if an arbitrary input is suitable for the predicate.
64
+ The parser Yaparc::Satisfy recognizes a single input via predicate which
65
+ determines if an arbitrary input is suitable for the predicate.
55
66
 
56
67
  is_integer = lambda do |i|
57
68
  begin
@@ -63,80 +74,71 @@ The parser Satisfy recognizes a single input via predicate which determines if a
63
74
  end
64
75
  parser = Yaparc::Satisfy.new(is_integer)
65
76
  parser.parse("123")
66
- => #<Yaparc::Result::OK:0xb7a8f284 @input="23", @value="1">
67
-
68
-
69
- == Combining Parsers
70
-
71
- * Alt
72
- * Seq
73
- * Many
74
- * ManyOne
77
+ #=> #<Yaparc::Result::OK:0xb7a8f284 @input="23", @value="1">
75
78
 
79
+ === Combining Parsers
76
80
 
81
+ * Yaparc::Alt
82
+ * Yaparc::Seq
83
+ * Yaparc::Many
84
+ * Yaparc::ManyOne
77
85
 
78
- === Sequencing parser
86
+ ==== Sequencing parser
79
87
 
80
- The Seq corresponds to sequencing in BNF. The following parser recognizes anything that Symbol.new('+') or Natural.new would if placed in succession.
88
+ The Yaparc::Seq corresponds to sequencing in BNF. The following parser
89
+ recognizes anything that <tt>Symbol.new('+')</tt> or <tt>Natural.new</tt>
90
+ would if placed in succession.
81
91
 
82
92
  parser = Seq.new(Symbol.new('+'), Natural.new)
83
93
  parser.parse("+321")
84
- => #<Yaparc::Result::OK:0xb7a81ae4 @input="", @value=321>
94
+ #=> #<Yaparc::Result::OK:0xb7a81ae4 @input="", @value=321>
85
95
 
86
- if a block given to Seq, it analyses input string to construct its logical structure.
96
+ If a block given to Yaparc::Seq, it analyses input string to construct its
97
+ logical structure.
87
98
 
88
- parser = Yaparc::Seq.new(Yaparc::Symbol.new('+'), Yaparc::Natural.new) do | plus, nat|
99
+ parser = Yaparc::Seq.new(Yaparc::Symbol.new('+'), Yaparc::Natural.new) do |plus, nat|
89
100
  nat
90
101
  end
91
102
  parser.parse("+1234")
92
- => #<Yaparc::Result::OK:0xb7a70a00 @input="", @value=1234>
103
+ #=> #<Yaparc::Result::OK:0xb7a70a00 @input="", @value=1234>
93
104
 
94
105
  It produces a parse tree which expounds the semantic structure of the program.
95
106
 
96
- === Alternation parser
97
-
98
- The parser Alt class is an alternation parser, which returns the result of the first parser to succeed, and failure if neither does.
107
+ ==== Alternation parser
99
108
 
109
+ The parser Yaparc::Alt class is an alternation parser, which returns the
110
+ result of the first parser to succeed, and failure if neither does.
100
111
 
101
112
  parser = Yaparc::Alt.new(
102
- Yaparc::Seq.new(Yaparc::Symbol.new('+'), Yaparc::Natural.new) do | _, nat|
113
+ Yaparc::Seq.new(Yaparc::Symbol.new('+'), Yaparc::Natural.new) do |_, nat|
103
114
  nat
104
115
  end,
105
116
  Yaparc::Natural.new
106
117
  )
107
118
  parser.parse("1234")
108
- => #<Yaparc::Result::OK:0xb7a5a610 @input="", @value=1234>
119
+ #=> #<Yaparc::Result::OK:0xb7a5a610 @input="", @value=1234>
109
120
  parser.parse("-1234")
110
- => #<Yaparc::Result::Fail:0xb7a57ba4 @value=nil>
121
+ #=> #<Yaparc::Result::Fail:0xb7a57ba4 @value=nil>
111
122
 
123
+ ==== Many
112
124
 
113
- === Many
114
-
115
- In Many, zero or more applications of parser are admissible.
125
+ In Yaparc::Many, zero or more applications of parser are admissible.
116
126
 
117
127
  parser = Yaparc::Many.new(Yaparc::Satisfy.new(lambda {|i| i > '0' and i < '9'}))
118
128
  parser.parse("123abc")
119
- => #<Yaparc::Result::OK:0xb7a49dc4 @input="abc", @value="123">
120
-
121
- === ManyOne
122
-
123
- The ManyOne requires at least one successfull application of parser.
124
-
129
+ #=> #<Yaparc::Result::OK:0xb7a49dc4 @input="abc", @value="123">
125
130
 
126
- == Tokenized parser
131
+ ==== ManyOne
127
132
 
128
- * Identifier
133
+ The Yaparc::ManyOne requires at least one successfull application of parser.
129
134
 
130
- Parser for identifier
135
+ === Tokenized parser
131
136
 
132
- * Natural
137
+ Yaparc::Identifier :: Parser for identifier
138
+ Yaparc::Natural :: Parser for natural number
139
+ Yaparc::Symbol :: Parser for symbol
133
140
 
134
- Parser for natural number
135
-
136
- * Symbol
137
-
138
-
139
- == Regex parser
141
+ === Regex parser
140
142
 
141
143
  parser = Regex.new(/\A[0-9]+/)
142
144
  result = parser.parse("1234ab")
@@ -148,10 +150,10 @@ The ManyOne requires at least one successfull application of parser.
148
150
  result = parser.parse("1234:ab")
149
151
  assert_equal ["ab", "1234"], result.value
150
152
 
153
+ === Define your own parser
151
154
 
152
- == Define your own parser
153
-
154
- In order to construct parsers, you make parser class to be inherited from Yaparc::AbstractParser class.
155
+ In order to construct parsers, you make parser class to be inherited from
156
+ Yaparc::AbstractParser class.
155
157
 
156
158
  class Identifier < Yaparc::AbstractParser
157
159
  def initialize
@@ -161,36 +163,54 @@ In order to construct parsers, you make parser class to be inherited from Yaparc
161
163
  end
162
164
  end
163
165
 
164
- If you want to nest the same parser class in the parser definition, you have to choose this way.
165
- In the following example, note that Expr class is instantiated inside Expr#initialize method.
166
+ If you want to nest the same parser class in the parser definition, you have
167
+ to choose this way. In the following example, note that +Expr+ class is
168
+ instantiated inside <tt>Expr#initialize</tt> method.
166
169
 
167
170
  class Expr < Yaparc::AbstractParser
168
171
  def initialize
169
172
  @parser = lambda do
170
173
  Yaparc::Alt.new(
171
- Yaparc::Seq.new(Term.new,
172
- Yaparc::Symbol.new('+'),
173
- Expr.new) do |term, _, expr|
174
- ['+', term,expr]
175
- end,
176
- Term.new
177
- )
174
+ Yaparc::Seq.new(Term.new,
175
+ Yaparc::Symbol.new('+'),
176
+ Expr.new) do |term, _, expr|
177
+ ['+', term, expr]
178
+ end,
179
+ Term.new
180
+ )
178
181
  end
179
182
  end
183
+ end
180
184
 
181
- Constructing your parsers, it should be noted that left-recursion leads to non-termination of the parser.
185
+ Constructing your parsers, it should be noted that left-recursion leads to
186
+ non-termination of the parser.
182
187
 
183
- == Avoiding left-recursion
188
+ === Avoiding left-recursion
184
189
 
185
190
  A ::= A B | C
186
191
 
187
- is equivalent to
192
+ is equivalent to
188
193
 
189
194
  A ::= C B*
190
195
 
196
+ === Tokenization
197
+
198
+ When you want to tokenize input stream, use Yaparc::Tokenize class.
199
+
200
+ == About this project
201
+
202
+ RubyGems.org :: https://rubygems.org/gems/yaparc
203
+ RubyForge (archived) :: https://web.archive.org/web/20140515235842/http://rubyforge.org/projects/yaparc/
204
+
205
+ == Development
191
206
 
192
- == Tokenization
207
+ After checking out the repo, run <tt>bin/setup</tt> to install dependencies.
208
+ Then, run <tt>rake test</tt> to run the tests.
209
+ You can also run <tt>bin/console</tt> for an interactive prompt that will allow you to experiment.
193
210
 
194
- When you want to tokenize input stream, use Token class.
211
+ To install this gem onto your local machine, run <tt>bundle exec rake install</tt>.
212
+ To release a new version, update the version number in the library file, and then run <tt>bundle exec rake release</tt>, which will create a git tag for the version, push git commits and the created tag, and push the <tt>.gem</tt> file to [rubygems.org](https://rubygems.org).
195
213
 
214
+ == Contributing
196
215
 
216
+ Bug reports and pull requests are welcome.
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+ require "net/http"
6
+
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.libs << 'test'
9
+ t.libs << 'lib'
10
+ t.test_files = FileList['test/**/*_test.rb']
11
+ end
12
+
13
+ require 'rubocop/rake_task'
14
+
15
+ RuboCop::RakeTask.new
16
+
17
+ task default: %i[test rubocop]
18
+
19
+ desc 'Generate signatures'
20
+ task :gensig do
21
+ sh 'typeprof', '-o', 'sig/yaparc.gen.rbs', 'sig/yaparc.rbs', *Dir['lib/**/*.rb']
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
@@ -0,0 +1,11 @@
1
+ - [X] Result::Fail to Fail
2
+ - [X] Fail to FailParser
3
+ - [X] Remove raise
4
+ - [X] Use newer keyword arguments
5
+ - [X] Avoid accessors
6
+ - [X] Split modules into files
7
+ - [X] Split BNF files
8
+
9
+ # Local Variables:
10
+ # mode: org
11
+ # End:
@@ -0,0 +1,16 @@
1
+ require_relative 'parsable'
2
+
3
+ module Yaparc
4
+ class AbstractParser
5
+ include Parsable
6
+
7
+ def parse(input)
8
+ tree = @parser.call.parse(input)
9
+ @tree = if block_given?
10
+ yield tree
11
+ else
12
+ tree
13
+ end
14
+ end
15
+ end
16
+ end
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
@@ -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
@@ -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
@@ -0,0 +1,11 @@
1
+ require_relative 'parsable'
2
+
3
+ module Yaparc
4
+ class CR
5
+ include Parsable
6
+
7
+ def initialize
8
+ @parser = proc { Regex.new(/\A[ \t]+\n[ \t\n]+/) }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'parsable'
2
+
3
+ module Yaparc
4
+ class Digit
5
+ include Parsable
6
+
7
+ def initialize
8
+ @parser = proc { Satisfy.new(IS_DIGIT) }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'parsable'
2
+
3
+ module Yaparc
4
+ class FailParser
5
+ include Parsable
6
+
7
+ def initialize
8
+ @parser = ->(input) { Fail.new(input:) }
9
+ end
10
+ end
11
+ end
@@ -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
@@ -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
@@ -0,0 +1,13 @@
1
+ require_relative 'parsable'
2
+
3
+ module Yaparc
4
+ class Literal
5
+ include Parsable
6
+
7
+ def initialize(literal, case_sensitive = true)
8
+ @parser = proc {
9
+ Tokenize.new(Yaparc::String.new(literal, case_sensitive))
10
+ }
11
+ end
12
+ end
13
+ end
@@ -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,11 @@
1
+ require_relative 'parsable'
2
+
3
+ module Yaparc
4
+ class Nat
5
+ include Parsable
6
+
7
+ def initialize
8
+ @parser = proc { Seq.new(ManyOne.new(Digit.new, '')) { |vs| vs.to_i } }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'parsable'
2
+
3
+ module Yaparc
4
+ class Natural
5
+ include Parsable
6
+
7
+ # Accepts Yaparc::Tokenize::new's keyword arguments.
8
+ def initialize(**args)
9
+ @parser = proc { Tokenize.new(Nat.new, **args) }
10
+ end
11
+ end
12
+ end