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.
@@ -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
@@ -0,0 +1,3 @@
1
+ class Parsby
2
+ VERSION = "0.1.0"
3
+ end
@@ -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: []