parsby 0.1.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 +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +41 -0
- data/README.md +607 -0
- data/Rakefile +6 -0
- data/bin/all-methods +35 -0
- data/bin/console +40 -0
- data/bin/methods-with-pending-documentation +49 -0
- data/bin/setup +8 -0
- data/bin/tested-methods +47 -0
- data/bin/vestigial-methods +30 -0
- data/lib/parsby.rb +804 -0
- data/lib/parsby/combinators.rb +384 -0
- data/lib/parsby/example/arithmetic_parser.rb +96 -0
- data/lib/parsby/example/csv_parser.rb +41 -0
- data/lib/parsby/example/json_parser.rb +92 -0
- data/lib/parsby/example/lisp_parser.rb +135 -0
- data/lib/parsby/version.rb +3 -0
- data/parsby.gemspec +42 -0
- metadata +121 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'parsby'
|
2
|
+
|
3
|
+
# This is based on:
|
4
|
+
#
|
5
|
+
# RFC 4180: Common Format and MIME Type for Comma-Separated Values (CSV) Files
|
6
|
+
module Parsby::Example
|
7
|
+
module CsvParser
|
8
|
+
include Parsby::Combinators
|
9
|
+
extend self
|
10
|
+
|
11
|
+
def parse(io)
|
12
|
+
csv.parse io
|
13
|
+
end
|
14
|
+
|
15
|
+
define_combinator :csv do
|
16
|
+
many(record) < eof
|
17
|
+
end
|
18
|
+
|
19
|
+
define_combinator :record do
|
20
|
+
sep_by(lit(","), cell) < (eol | eof)
|
21
|
+
end
|
22
|
+
|
23
|
+
define_combinator :cell do
|
24
|
+
quoted_cell | non_quoted_cell
|
25
|
+
end
|
26
|
+
|
27
|
+
define_combinator :quoted_cell do
|
28
|
+
non_quote = join(many(any_char.that_fail(lit('"'))))
|
29
|
+
inner = sep_by(lit('""'), non_quote).fmap {|r| r.join '"' }
|
30
|
+
lit('"') > inner < lit('"')
|
31
|
+
end
|
32
|
+
|
33
|
+
define_combinator :non_quoted_cell do
|
34
|
+
join(many(any_char.that_fail(lit(",") | lit("\"") | eol)))
|
35
|
+
end
|
36
|
+
|
37
|
+
define_combinator :eol do
|
38
|
+
lit("\r\n") | lit("\n")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'parsby'
|
2
|
+
|
3
|
+
module Parsby::Example
|
4
|
+
module JsonParser
|
5
|
+
include Parsby::Combinators
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def parse(io)
|
9
|
+
(spaced(value) < eof).parse io
|
10
|
+
end
|
11
|
+
|
12
|
+
define_combinator :value do
|
13
|
+
null | bool | number | string | array | object
|
14
|
+
end
|
15
|
+
|
16
|
+
define_combinator :null do
|
17
|
+
lit("null") > pure(nil)
|
18
|
+
end
|
19
|
+
|
20
|
+
define_combinator :bool do
|
21
|
+
choice(
|
22
|
+
lit("true") > pure(true),
|
23
|
+
lit("false") > pure(false),
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
# This has been adopted as Parsby::Combinators#fractional_decimal, but
|
28
|
+
# we leave this definition here since this module is supposed to be an
|
29
|
+
# example of using Parsby, and this works well for showing how to use
|
30
|
+
# `group` and `fmap`.
|
31
|
+
define_combinator :number do
|
32
|
+
sign = lit("-") | lit("+")
|
33
|
+
group(
|
34
|
+
optional(sign),
|
35
|
+
decimal,
|
36
|
+
optional(group(
|
37
|
+
lit("."),
|
38
|
+
decimal,
|
39
|
+
)),
|
40
|
+
optional(group(
|
41
|
+
lit("e") | lit("E"),
|
42
|
+
optional(sign),
|
43
|
+
decimal,
|
44
|
+
)),
|
45
|
+
).fmap do |(sign, whole, (_, fractional), (_, exponent_sign, exponent))|
|
46
|
+
n = whole
|
47
|
+
n += fractional.to_f / 10 ** fractional.to_s.length if fractional
|
48
|
+
n *= -1 if sign == "-"
|
49
|
+
if exponent
|
50
|
+
e = exponent
|
51
|
+
e *= -1 if exponent_sign == "-"
|
52
|
+
n *= 10 ** e
|
53
|
+
end
|
54
|
+
n
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
define_combinator :string do
|
59
|
+
between(lit('"'), lit('"'),
|
60
|
+
join(many(choice(
|
61
|
+
any_char.that_fails(lit('"') | lit("\\")),
|
62
|
+
lit("\\") > choice(
|
63
|
+
lit('"'),
|
64
|
+
lit("\\"),
|
65
|
+
lit("/"),
|
66
|
+
lit("f") > pure("\f"),
|
67
|
+
lit("b") > pure("\b"),
|
68
|
+
lit("r") > pure("\r"),
|
69
|
+
lit("n") > pure("\n"),
|
70
|
+
lit("t") > pure("\t"),
|
71
|
+
lit("u") > join(hex_digit * 4).fmap {|s| [s.hex].pack("U") },
|
72
|
+
),
|
73
|
+
)))
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
define_combinator :array do
|
78
|
+
between(lit("["), ws > lit("]"), sep_by(lit(","), spaced(lazy { value })))
|
79
|
+
end
|
80
|
+
|
81
|
+
define_combinator :object do
|
82
|
+
between(lit("{"), ws > lit("}"),
|
83
|
+
sep_by(lit(","),
|
84
|
+
spaced(group(
|
85
|
+
string < spaced(lit(":")),
|
86
|
+
lazy { value }
|
87
|
+
)),
|
88
|
+
)
|
89
|
+
).fmap(&:to_h)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require "parsby"
|
2
|
+
|
3
|
+
module Parsby::Example
|
4
|
+
module LispParser
|
5
|
+
include Parsby::Combinators
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def parse(io)
|
9
|
+
sexp_sequence.parse io
|
10
|
+
end
|
11
|
+
|
12
|
+
define_combinator :sexp_sequence do
|
13
|
+
many(spaced(sexp)) < eof
|
14
|
+
end
|
15
|
+
|
16
|
+
define_combinator :sexp, wrap: false do
|
17
|
+
splicer.start {|m| lazy { choice(m.end(abbrev), m.end(atom), m.end(list)) } }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Add comments to definition of whitespace. whitespace is defined using
|
21
|
+
# whitespace_1, so we cover both with this.
|
22
|
+
define_combinator :whitespace_1 do
|
23
|
+
join(many_1(super() | comment))
|
24
|
+
end
|
25
|
+
|
26
|
+
define_combinator :comment do
|
27
|
+
lit(";") \
|
28
|
+
+ join(many(any_char.that_fails(lit("\n")))) \
|
29
|
+
+ (lit("\n") | (eof > pure("")))
|
30
|
+
end
|
31
|
+
|
32
|
+
# Parses sexps with abbreviations, like '(foo bar) or `(foo ,bar).
|
33
|
+
define_combinator :abbrev do
|
34
|
+
~splicer.start do
|
35
|
+
choice(
|
36
|
+
lit("'") > sexp.fmap {|s| [:quote, [s, nil]]},
|
37
|
+
lit("`") > sexp.fmap {|s| [:quasiquote, [s, nil]]},
|
38
|
+
lit(",@") > sexp.fmap {|s| [:"unquote-splicing", [s, nil]]},
|
39
|
+
lit(",") > sexp.fmap {|s| [:unquote, [s, nil]]},
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
define_combinator :list do
|
45
|
+
braces = {"(" => ")", "[" => "]"}
|
46
|
+
|
47
|
+
~splicer.start do |m|
|
48
|
+
m.end(char_in(braces.keys.join)).then do |opening_brace|
|
49
|
+
spaced(list_insides(m)) < m.end(lit(braces[opening_brace]))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
define_combinator :list_insides do |splicing_marker|
|
55
|
+
optional(
|
56
|
+
group(
|
57
|
+
splicing_marker.end(sexp),
|
58
|
+
choice(
|
59
|
+
spaced(splicing_marker.end(lit("."))) > splicing_marker.end(sexp),
|
60
|
+
whitespace > lazy { list_insides(splicing_marker) },
|
61
|
+
),
|
62
|
+
)
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
define_combinator :atom do
|
67
|
+
~choice(number, string, self.nil, symbol)
|
68
|
+
end
|
69
|
+
|
70
|
+
define_combinator :nil, wrap: false do
|
71
|
+
splicer.start { ilit("nil") > pure(nil) }
|
72
|
+
end
|
73
|
+
|
74
|
+
define_combinator :symbol_char do
|
75
|
+
char_in(
|
76
|
+
[
|
77
|
+
*('a'..'z'),
|
78
|
+
*('A'..'Z'),
|
79
|
+
*('0'..'9'),
|
80
|
+
# Got list from R6RS; removed '.' for simplicity.
|
81
|
+
*%w(! $ % & * + - / : < = > ? @ ^ _ ~),
|
82
|
+
].flatten.join
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
define_combinator :symbol do
|
87
|
+
~splicer.start { join(many_1(symbol_char)).fmap(&:to_sym) }
|
88
|
+
end
|
89
|
+
|
90
|
+
define_combinator :hex_digit do
|
91
|
+
char_in(
|
92
|
+
[*("0".."9"), *("a".."f"), *("A".."F")]
|
93
|
+
.flatten
|
94
|
+
.join
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
define_combinator :escape_sequence do
|
99
|
+
lit("\\") > choice([
|
100
|
+
lit("\"") > pure("\""),
|
101
|
+
lit("n") > pure("\n"),
|
102
|
+
lit("t") > pure("\t"),
|
103
|
+
lit("r") > pure("\r"),
|
104
|
+
lit("x") > (hex_digit * 2)
|
105
|
+
.fmap {|(d1, d2)| (d1 + d2).to_i(16).chr },
|
106
|
+
lit("\\"),
|
107
|
+
])
|
108
|
+
end
|
109
|
+
|
110
|
+
define_combinator :string do
|
111
|
+
~splicer.start do
|
112
|
+
between(lit('"'), lit('"'),
|
113
|
+
join(many(choice(
|
114
|
+
any_char.that_fails(lit("\\") | lit('"')),
|
115
|
+
escape_sequence,
|
116
|
+
)))
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
define_combinator :number do
|
122
|
+
~splicer.start do
|
123
|
+
group(
|
124
|
+
optional(lit("-") | lit("+")),
|
125
|
+
decimal,
|
126
|
+
optional(group(lit("."), optional(decimal))),
|
127
|
+
).fmap do |(sign, whole_part, (_, fractional_part))|
|
128
|
+
n = whole_part
|
129
|
+
n += (fractional_part || 0).to_f / 10 ** fractional_part.to_s.length
|
130
|
+
sign == "-" ? -n : n
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
data/parsby.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "parsby/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "parsby"
|
8
|
+
spec.version = Parsby::VERSION
|
9
|
+
spec.authors = ["Jorge Luis Martinez Gomez"]
|
10
|
+
spec.email = ["jol@jol.dev"]
|
11
|
+
|
12
|
+
spec.summary = %q{Parser combinator library inspired by Haskell's Parsec}
|
13
|
+
#spec.description = %q{TODO: Write a longer description or delete this line.}
|
14
|
+
#spec.homepage = "TODO: Put your gem's website or public repo URL here."
|
15
|
+
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
|
+
#if spec.respond_to?(:metadata)
|
19
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
20
|
+
|
21
|
+
# #spec.metadata["homepage_uri"] = spec.homepage
|
22
|
+
# #spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
|
23
|
+
# #spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
24
|
+
#else
|
25
|
+
# raise "RubyGems 2.0 or newer is required to protect against " \
|
26
|
+
# "public gem pushes."
|
27
|
+
#end
|
28
|
+
|
29
|
+
# Specify which files should be added to the gem when it is released.
|
30
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
31
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
32
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
33
|
+
end
|
34
|
+
spec.bindir = "exe"
|
35
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
36
|
+
spec.require_paths = ["lib"]
|
37
|
+
|
38
|
+
spec.add_development_dependency "bundler", "~> 1.17"
|
39
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
40
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
41
|
+
spec.add_development_dependency "pry"
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: parsby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jorge Luis Martinez Gomez
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-09-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.17'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.17'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- jol@jol.dev
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".rspec"
|
78
|
+
- ".ruby-version"
|
79
|
+
- ".travis.yml"
|
80
|
+
- Gemfile
|
81
|
+
- Gemfile.lock
|
82
|
+
- README.md
|
83
|
+
- Rakefile
|
84
|
+
- bin/all-methods
|
85
|
+
- bin/console
|
86
|
+
- bin/methods-with-pending-documentation
|
87
|
+
- bin/setup
|
88
|
+
- bin/tested-methods
|
89
|
+
- bin/vestigial-methods
|
90
|
+
- lib/parsby.rb
|
91
|
+
- lib/parsby/combinators.rb
|
92
|
+
- lib/parsby/example/arithmetic_parser.rb
|
93
|
+
- lib/parsby/example/csv_parser.rb
|
94
|
+
- lib/parsby/example/json_parser.rb
|
95
|
+
- lib/parsby/example/lisp_parser.rb
|
96
|
+
- lib/parsby/version.rb
|
97
|
+
- parsby.gemspec
|
98
|
+
homepage:
|
99
|
+
licenses: []
|
100
|
+
metadata: {}
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 2.6.11
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: Parser combinator library inspired by Haskell's Parsec
|
121
|
+
test_files: []
|