dolos 0.2.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cfeffbbe4108e43ba81ccd210f2689910d310fc3947fe05ed22a9972e581e96a
4
- data.tar.gz: e181ccca4725008464fc92670b8120cf55262ba469d6ec494270081eb902c241
3
+ metadata.gz: 258d1d857ec1df4c70a50225e35114bbcc941b2901306d73db60543da1df93df
4
+ data.tar.gz: 3ec6f50215db6b4d719acc9552b5e6be34248452a7dcc4a6bef369e5e3c6ca2e
5
5
  SHA512:
6
- metadata.gz: 27fe73f34d41692d31c00d30bc03869cdefcef4ee785acbb72da12c9bceb85b78357da7c3d6d1435598b40fb635fb5e71d56254c18597c8d7d424748afc7c098
7
- data.tar.gz: 9dd78f5e313830742505bbeb1488ed643a700facc2193ed3e996eef795913a50bb660a2af2dcd7c9a73e6e5f84befa7e461b2cf10d4bd11f22d68901ecc59e9b
6
+ metadata.gz: be62167ed5bd8b4bc57b5670434afc4b58789749f4306e70399416467160ba5f3a86a11adab7056d1b60ad72f96f3ed44e51fdc90601500a368e7ca7d99b7e19
7
+ data.tar.gz: 5e8f105222057ff27d9daafc2b2b5ec2e5e7b96ee7be84c665214b1d84e52fa0c87d126020a5606418b1a41002f69edbcbdf2d6bd514667c6b1317318c162373
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ AllCops:
2
+ DisabledByDefault: true
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # Dolos
2
+ [![Gem version](https://badge.fury.io/rb/dolos.svg)](https://rubygems.org/gems/dolos)
3
+ [![Build status](https://github.com/benetis/dolos/actions/workflows/ruby.yml/badge.svg)](https://github.com/benetis/dolos/actions)
2
4
 
3
- <img height="256" src="docs/dolos_stable_diff.png" width="256"/>
5
+
6
+ <img height="256" src="docs/images/dolos_stable_diff.png" width="256"/>
4
7
 
5
8
 
6
9
  ### Disclaimer
@@ -40,13 +43,13 @@ require 'dolos_common_parsers/common_parsers'
40
43
  include Dolos
41
44
  # frozen_string_literal: true
42
45
  require_relative 'dolos'
43
- require_relative 'dolos_common_parsers/common_parsers'
46
+ require_relative 'dolos_parsers/common_parsers'
44
47
 
45
48
  include Dolos
46
49
 
47
50
  # Include common parsers
48
51
  # In future this can be more structured, moved them to separate module to prevent breaking changes
49
- include Dolos::CommonParsers
52
+ include Dolos::Common
50
53
 
51
54
  # Library usage example
52
55
  # Parse out a name and address from a letter
data/docs/.nojekyll ADDED
File without changes
data/docs/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # Dolos
2
+
3
+ ## What is Dolos?
4
+ Dolos is parser combinator library for Ruby. It is inspired by FastParse and Scala Parser Combinators.
5
+
6
+ ## What are parser combinators?
7
+ Parser combinators are a way to build parsers from smaller parsers. For example, you can build a parser for a number from a parser for a digit.
8
+ This is a very simple example, but it can be used to build more complex parsers.
9
+ Parsers are lazy and only run when needed. This allows to build complex parsers before passing input to them.
10
+ ```ruby
11
+ hello = string("Hello")
12
+ greeting = hello >> c(" ") >> string("Ruby developer!")
13
+ greeting.run("Hello Ruby developer!") # => Success
14
+ ```
15
+
16
+ ## What's different from alternatives?
17
+ This library focuses on two things:
18
+ - Parsers integrate well into Ruby code. There is no need to keep them in separate classes.
19
+ - Fine grained control over parsers. You can `map` and adjust each parser separately
20
+ - Two ways of capturing values: traditional `>>`, other product operators to construct value and `capture!`
21
+ - For simple parsers `capture!` can be used to very quickly capture values into flat arrays
22
+ - Running parsers will not throw exceptions and instead return a result object. Exceptions don't play well with parsing.
data/docs/_sidebar.md ADDED
@@ -0,0 +1,4 @@
1
+ * [Home](/)
2
+ * [Getting started](getting_started.md)
3
+ * [Installation](getting_started.md#installation)
4
+ * [Usage](getting_started.md#usage)
@@ -0,0 +1,52 @@
1
+ # Getting started
2
+
3
+ ## Installation
4
+
5
+ Install the gem and add it to your Gemfile:
6
+ ```shell
7
+ $ bundle add dolos
8
+ ```
9
+ Or manually:
10
+ ```ruby
11
+ gem 'dolos'
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ Two things to do:
17
+ - require library
18
+ - include module `Dolos` and `Dolos::Common`
19
+
20
+ ```ruby
21
+ require 'dolos'
22
+
23
+ include Dolos
24
+ include Dolos::Common # Common parsers
25
+ ```
26
+
27
+ ### Basic parsers
28
+
29
+ A simple parser which matches one word.
30
+
31
+ ```ruby
32
+ require 'dolos'
33
+ include Dolos
34
+
35
+ hello = c("Hello") # c("") is an alias for string(""). Can be read as: case-sensitive string match
36
+
37
+ hello.run("Hello").success? # => true
38
+
39
+ hello.run("hello").success? # => failure
40
+ ```
41
+
42
+ After defining parser, it can be ran with `run('my-input')` method. It returns a `Result` object.
43
+
44
+ ### Result
45
+
46
+ Result can be either `Success` or `Failure`. It can be checked with `success?` or `failure?` methods.
47
+
48
+ Success will also have `value` property which will contain the result of the parser. There is also `captures`, but
49
+ that's for later.
50
+
51
+
52
+ Failure will have `inspect` method which will return a string with the error message. It will show error position as well.
data/docs/index.html ADDED
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Document</title>
6
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
7
+ <meta name="description" content="Description">
8
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
9
+ <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
10
+ </head>
11
+ <body>
12
+ <div id="app"></div>
13
+ <script>
14
+ window.$docsify = {
15
+ name: '',
16
+ repo: ''
17
+ }
18
+ window.$docsify = {
19
+ loadSidebar: true
20
+ }
21
+ </script>
22
+ <!-- Docsify v4 -->
23
+ <script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
24
+ <script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-ruby.min.js"></script>
25
+ </body>
26
+ </html>
data/examples/letter.rb CHANGED
@@ -6,7 +6,7 @@ include Dolos
6
6
 
7
7
  # Include common parsers
8
8
  # In future this can be more structured, moved them to separate module to prevent breaking changes
9
- include Dolos::CommonParsers
9
+ include Dolos::Common
10
10
 
11
11
  # Library usage example
12
12
  # Parse out a name and address from a letter
@@ -1,7 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dolos
4
- module CommonParsers
4
+ # Common parsers
5
+ # Separated from the main library to improve them later on
6
+ # These will change, new ones will be added. Once API stabilises, we will see what to do
7
+ # We have to be careful what is in the scope when we include this main module
8
+ # Probably a package of parsers following some RFC will be added as well.
9
+ # Keeping them separate for now
10
+ module Common
5
11
  def ws
6
12
  regex(/\s/)
7
13
  end
data/lib/dolos/parsers.rb CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  module Dolos
4
4
  module Parsers
5
+
6
+ # String parser
7
+ # Matches exactly the given string
8
+ # string('hello').run('hello') => Success.new('hello', 5)
9
+ # Alias: c, for case-sensitive. Ex: c('hello').run('hello') => Success.new('hello', 5)
5
10
  def string(str)
6
11
  utf8_str = str.encode('UTF-8')
7
12
 
@@ -21,9 +26,12 @@ module Dolos
21
26
  end
22
27
  end
23
28
  end
24
-
25
29
  alias_method :c, :string
26
30
 
31
+ # Regex parser
32
+ # Accepts a regex, matches the regex against the input
33
+ # parser = regex(/\d+/)
34
+ # result = parser.run('123') # => Success.new('123', 3)
27
35
  def regex(pattern)
28
36
  Parser.new do |state|
29
37
  state.input.mark_offset
@@ -41,6 +49,8 @@ module Dolos
41
49
  end
42
50
  end
43
51
 
52
+ # Matches any character
53
+ # any_char.run('a') # => Success.new('a', 1)
44
54
  def any_char
45
55
  Parser.new do |state|
46
56
  state.input.mark_offset
@@ -62,6 +72,7 @@ module Dolos
62
72
  end
63
73
 
64
74
  # Matches any character in a string
75
+ # Passed string can be imagined as a set of characters
65
76
  # Example:
66
77
  # char_in('abc').run('b') # => Success.new('b', 1)
67
78
  def char_in(characters_string)
data/lib/dolos/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dolos
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/dolos.rb CHANGED
@@ -15,16 +15,24 @@ module Dolos
15
15
  @parser_proc = block
16
16
  end
17
17
 
18
+ # Run the parser with the given input
19
+ # Returns a Result<Success|Failure>
20
+ # string("hello").run("hello") => Success.new("hello", 5)
18
21
  def run(input)
19
22
  run_with_state(ParserState.new(input))
20
23
  end
21
24
 
25
+
22
26
  def run_with_state(state)
23
27
  result = @parser_proc.call(state)
24
28
  state.last_success_position = state.input.offset if result.success?
25
29
  result
26
30
  end
27
31
 
32
+ # Capture the result of the parser
33
+ # p = string("hello").capture!
34
+ # p.run("hello").captures => ["hello"]
35
+ # Captures is a flat array of all captured values
28
36
  def capture!(wrap_in = nil)
29
37
  Parser.new do |state|
30
38
  result = run_with_state(state)
@@ -32,7 +40,10 @@ module Dolos
32
40
  end
33
41
  end
34
42
 
35
- # Will call `map` on captures
43
+ # Map the captures of the parser
44
+ # p = string("hello").map_captures { |captures| captures.map(&:upcase) }
45
+ # p.run("hello") => Success.new("hello", 5, ["HELLO"])
46
+ # This only maps over captures, not the value
36
47
  def map_captures(&block)
37
48
  Parser.new do |state|
38
49
  result = run_with_state(state)
@@ -40,7 +51,9 @@ module Dolos
40
51
  end
41
52
  end
42
53
 
43
- # Will call block on tuple of value
54
+ # Map the result of the parser
55
+ # p = string("hello").map { |s| s.upcase }
56
+ # p.run("hello") => Success.new("HELLO", 5)
44
57
  def map(&block)
45
58
  Parser.new do |state|
46
59
  result = run_with_state(state)
@@ -48,6 +61,7 @@ module Dolos
48
61
  end
49
62
  end
50
63
 
64
+ # Combine the result of the parser with another parser
51
65
  def combine(&block)
52
66
  Parser.new do |state|
53
67
  result = run_with_state(state)
@@ -62,12 +76,10 @@ module Dolos
62
76
  end
63
77
  end
64
78
 
65
- def flatten
66
- map_captures do |captures|
67
- captures.flatten
68
- end
69
- end
70
-
79
+ # Combine the result of the parser with another parser
80
+ # Has an alias of `&`
81
+ # p = string("hello") & string("world")
82
+ # p.run("helloworld") => Success.new(["hello", "world"], 10)
71
83
  def product(other_parser)
72
84
  combine do |value1, capture1|
73
85
  other_parser.map do |value2|
@@ -79,6 +91,10 @@ module Dolos
79
91
  end
80
92
  alias_method :&, :product
81
93
 
94
+
95
+ # Combine the result of the parser with another parser
96
+ # Discards the result of the second parser
97
+ # p = string("hello") << string("world")
82
98
  def product_l(other_parser)
83
99
  combine do |value1, capture1|
84
100
  other_parser.map do |_|
@@ -89,6 +105,9 @@ module Dolos
89
105
  end
90
106
  end
91
107
 
108
+ # Combine the result of the parser with another parser
109
+ # Discards the result of the first parser
110
+ # p = string("hello") >> string("world")
92
111
  def product_r(other_parser)
93
112
  combine do |_, capture1|
94
113
  other_parser.map do |value2|
@@ -102,6 +121,10 @@ module Dolos
102
121
  alias_method :<<, :product_l
103
122
  alias_method :>>, :product_r
104
123
 
124
+ # Combine the result of the parser with another parser
125
+ # If the first parser fails, it will try the second parser
126
+ # p = string("hello") | string("world") | string("!")
127
+ # p.run("hello") => Success.new("hello", 5)
105
128
  def choice(other_parser)
106
129
  Parser.new do |state|
107
130
  result = run_with_state(state)
@@ -114,6 +137,9 @@ module Dolos
114
137
  end
115
138
  alias_method :|, :choice
116
139
 
140
+
141
+ # Repeat the parser n times
142
+ # Separator is optional, its another parser that will be run between each repetition
117
143
  # rep0 # 0 or more
118
144
  # rep # 1 or more
119
145
  # rep(n = 2) # exactly 2
@@ -156,11 +182,17 @@ module Dolos
156
182
  end
157
183
  end
158
184
  end
185
+
186
+ # Repeat the parser zero or more times
187
+ # c(" ").rep0.run(" ") => Success.new([" ", " ", " "], 3)
159
188
  def zero_or_more
160
189
  repeat(n_min: 0, n_max: Float::INFINITY)
161
190
  end
162
191
  alias_method :rep0, :zero_or_more
163
192
 
193
+ # Repeat the parser one or more times
194
+ # Same as rep0, but must match at least once
195
+ # c(" ").rep.run("A") => Failure.new("...")
164
196
  def one_or_more(exactly = nil)
165
197
  if exactly.nil?
166
198
  repeat(n_min: 1, n_max: Float::INFINITY)
@@ -170,6 +202,8 @@ module Dolos
170
202
  end
171
203
  alias_method :rep, :one_or_more
172
204
 
205
+ # Make parser optional
206
+ # c(" ").opt.run("A") => Success.new([], 0)
173
207
  def optional
174
208
  Parser.new do |state|
175
209
  result = run_with_state(state.dup)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dolos
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - benetis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-08-22 00:00:00.000000000 Z
11
+ date: 2023-08-23 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Parser combinators library for Ruby. In active development, not stable
14
14
  yet.
@@ -19,6 +19,7 @@ extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
21
  - ".rspec"
22
+ - ".rubocop.yml"
22
23
  - CHANGELOG.md
23
24
  - LICENSE.txt
24
25
  - README.md
@@ -27,15 +28,20 @@ files:
27
28
  - benchmarks/json/nested_json_166.json
28
29
  - benchmarks/json/nested_json_1m.json
29
30
  - benchmarks/letter.rb
30
- - docs/dolos_stable_diff.png
31
+ - docs/.nojekyll
32
+ - docs/README.md
33
+ - docs/_sidebar.md
34
+ - docs/getting_started.md
35
+ - docs/images/dolos_stable_diff.png
36
+ - docs/index.html
31
37
  - examples/letter.rb
32
38
  - lib/dolos.rb
39
+ - lib/dolos/common.rb
33
40
  - lib/dolos/parser_state.rb
34
41
  - lib/dolos/parsers.rb
35
42
  - lib/dolos/result.rb
36
43
  - lib/dolos/string_io_wrapper.rb
37
44
  - lib/dolos/version.rb
38
- - lib/dolos_common_parsers/common_parsers.rb
39
45
  - sig/dolos.rbs
40
46
  - sig/dolos/common_parsers.rbs
41
47
  - sig/dolos/parser.rbs