dolos 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/README.md +6 -3
- data/docs/.nojekyll +0 -0
- data/docs/README.md +22 -0
- data/docs/_sidebar.md +4 -0
- data/docs/getting_started.md +52 -0
- data/docs/index.html +26 -0
- data/examples/letter.rb +1 -1
- data/lib/{dolos_common_parsers/common_parsers.rb → dolos/common.rb} +7 -1
- data/lib/dolos/parsers.rb +12 -1
- data/lib/dolos/version.rb +1 -1
- data/lib/dolos.rb +42 -8
- metadata +10 -4
- /data/docs/{dolos_stable_diff.png → images/dolos_stable_diff.png} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 258d1d857ec1df4c70a50225e35114bbcc941b2901306d73db60543da1df93df
|
4
|
+
data.tar.gz: 3ec6f50215db6b4d719acc9552b5e6be34248452a7dcc4a6bef369e5e3c6ca2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be62167ed5bd8b4bc57b5670434afc4b58789749f4306e70399416467160ba5f3a86a11adab7056d1b60ad72f96f3ed44e51fdc90601500a368e7ca7d99b7e19
|
7
|
+
data.tar.gz: 5e8f105222057ff27d9daafc2b2b5ec2e5e7b96ee7be84c665214b1d84e52fa0c87d126020a5606418b1a41002f69edbcbdf2d6bd514667c6b1317318c162373
|
data/.rubocop.yml
ADDED
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
|
-
|
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 '
|
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::
|
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,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::
|
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
|
-
|
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
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
|
-
#
|
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
|
-
#
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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.
|
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-
|
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
|
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
|
File without changes
|