d-parse 0.1

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 (73) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +10 -0
  3. data/Gemfile.lock +104 -0
  4. data/Guardfile +3 -0
  5. data/LICENSE +19 -0
  6. data/NEWS.md +0 -0
  7. data/README.md +137 -0
  8. data/Rakefile +14 -0
  9. data/d-parse.gemspec +26 -0
  10. data/lib/d-parse.rb +10 -0
  11. data/lib/d-parse/dsl.rb +71 -0
  12. data/lib/d-parse/failure.rb +46 -0
  13. data/lib/d-parse/parser.rb +102 -0
  14. data/lib/d-parse/parsers.rb +26 -0
  15. data/lib/d-parse/parsers/combinators/alt.rb +32 -0
  16. data/lib/d-parse/parsers/combinators/repeat.rb +42 -0
  17. data/lib/d-parse/parsers/combinators/seq.rb +49 -0
  18. data/lib/d-parse/parsers/highlevel/char_in.rb +13 -0
  19. data/lib/d-parse/parsers/highlevel/intersperse.rb +18 -0
  20. data/lib/d-parse/parsers/highlevel/json.rb +237 -0
  21. data/lib/d-parse/parsers/highlevel/opt.rb +16 -0
  22. data/lib/d-parse/parsers/highlevel/string.rb +13 -0
  23. data/lib/d-parse/parsers/highlevel/whitespace_char.rb +15 -0
  24. data/lib/d-parse/parsers/modifiers/capturing.rb +13 -0
  25. data/lib/d-parse/parsers/modifiers/describe.rb +28 -0
  26. data/lib/d-parse/parsers/modifiers/ignore.rb +17 -0
  27. data/lib/d-parse/parsers/modifiers/lazy.rb +18 -0
  28. data/lib/d-parse/parsers/modifiers/map.rb +24 -0
  29. data/lib/d-parse/parsers/primitives/any.rb +22 -0
  30. data/lib/d-parse/parsers/primitives/bind.rb +25 -0
  31. data/lib/d-parse/parsers/primitives/char.rb +27 -0
  32. data/lib/d-parse/parsers/primitives/char_not.rb +27 -0
  33. data/lib/d-parse/parsers/primitives/char_not_in.rb +30 -0
  34. data/lib/d-parse/parsers/primitives/eof.rb +21 -0
  35. data/lib/d-parse/parsers/primitives/except.rb +33 -0
  36. data/lib/d-parse/parsers/primitives/fail.rb +17 -0
  37. data/lib/d-parse/parsers/primitives/succeed.rb +13 -0
  38. data/lib/d-parse/position.rb +31 -0
  39. data/lib/d-parse/success.rb +35 -0
  40. data/lib/d-parse/version.rb +3 -0
  41. data/samples/parse-bind +25 -0
  42. data/samples/parse-csv +19 -0
  43. data/samples/parse-errortest +45 -0
  44. data/samples/parse-fun +61 -0
  45. data/samples/parse-json +18 -0
  46. data/samples/parse-readme +27 -0
  47. data/spec/d-parse/failure_spec.rb +36 -0
  48. data/spec/d-parse/parser_spec.rb +77 -0
  49. data/spec/d-parse/parsers/alt_spec.rb +48 -0
  50. data/spec/d-parse/parsers/any_spec.rb +15 -0
  51. data/spec/d-parse/parsers/bind_spec.rb +31 -0
  52. data/spec/d-parse/parsers/capture_spec.rb +11 -0
  53. data/spec/d-parse/parsers/char_in_spec.rb +22 -0
  54. data/spec/d-parse/parsers/char_not_in_spec.rb +23 -0
  55. data/spec/d-parse/parsers/char_not_spec.rb +16 -0
  56. data/spec/d-parse/parsers/char_spec.rb +22 -0
  57. data/spec/d-parse/parsers/describe_spec.rb +22 -0
  58. data/spec/d-parse/parsers/end_of_input_spec.rb +20 -0
  59. data/spec/d-parse/parsers/except_spec.rb +20 -0
  60. data/spec/d-parse/parsers/fail_spec.rb +12 -0
  61. data/spec/d-parse/parsers/intersperse_spec.rb +18 -0
  62. data/spec/d-parse/parsers/json_spec.rb +69 -0
  63. data/spec/d-parse/parsers/lazy_spec.rb +16 -0
  64. data/spec/d-parse/parsers/map_spec.rb +54 -0
  65. data/spec/d-parse/parsers/optional_spec.rb +16 -0
  66. data/spec/d-parse/parsers/or_spec.rb +26 -0
  67. data/spec/d-parse/parsers/repeat_spec.rb +40 -0
  68. data/spec/d-parse/parsers/sequence_spec.rb +52 -0
  69. data/spec/d-parse/parsers/string_spec.rb +19 -0
  70. data/spec/d-parse/parsers/succeed_spec.rb +12 -0
  71. data/spec/d-parse/parsers/whitespace_char_spec.rb +14 -0
  72. data/spec/spec_helper.rb +97 -0
  73. metadata +140 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2672e347527fd59383d12db21f5a64e1cfe16e5b
4
+ data.tar.gz: 541544acc6be6004decf009531f9b9b6bdae15b0
5
+ SHA512:
6
+ metadata.gz: aa5dcb3e12a82314066025d0c0739a64657e29bec485b414bdd705cab39848fcee448c21d811ac046b486a89ab815a958dd5066aa73b3722f2cec22d6050108c
7
+ data.tar.gz: b6f0b08ce5a15a067e07c98618a8da9c13b3b39828f13af2ab316d37f07be31886e5d275b8a587c1512cb5ea1fa34465ad7bb6a593980a4d161bc6b782c3b928
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'coveralls', require: false
6
+ gem 'guard'
7
+ gem 'guard-rake'
8
+ gem 'rake'
9
+ gem 'rspec'
10
+ gem 'rubocop'
@@ -0,0 +1,104 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ d-parse (0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.3.0)
10
+ coderay (1.1.1)
11
+ coveralls (0.8.13)
12
+ json (~> 1.8)
13
+ simplecov (~> 0.11.0)
14
+ term-ansicolor (~> 1.3)
15
+ thor (~> 0.19.1)
16
+ tins (~> 1.6.0)
17
+ diff-lcs (1.2.5)
18
+ docile (1.1.5)
19
+ ffi (1.9.10)
20
+ formatador (0.2.5)
21
+ guard (2.14.0)
22
+ formatador (>= 0.2.4)
23
+ listen (>= 2.7, < 4.0)
24
+ lumberjack (~> 1.0)
25
+ nenv (~> 0.1)
26
+ notiffany (~> 0.0)
27
+ pry (>= 0.9.12)
28
+ shellany (~> 0.0)
29
+ thor (>= 0.18.1)
30
+ guard-rake (1.0.0)
31
+ guard
32
+ rake
33
+ json (1.8.3)
34
+ listen (3.1.5)
35
+ rb-fsevent (~> 0.9, >= 0.9.4)
36
+ rb-inotify (~> 0.9, >= 0.9.7)
37
+ ruby_dep (~> 1.2)
38
+ lumberjack (1.0.10)
39
+ method_source (0.8.2)
40
+ nenv (0.3.0)
41
+ notiffany (0.1.0)
42
+ nenv (~> 0.1)
43
+ shellany (~> 0.0)
44
+ parser (2.3.1.2)
45
+ ast (~> 2.2)
46
+ powerpack (0.1.1)
47
+ pry (0.10.3)
48
+ coderay (~> 1.1.0)
49
+ method_source (~> 0.8.1)
50
+ slop (~> 3.4)
51
+ rainbow (2.1.0)
52
+ rake (11.2.2)
53
+ rb-fsevent (0.9.7)
54
+ rb-inotify (0.9.7)
55
+ ffi (>= 0.5.0)
56
+ rspec (3.5.0)
57
+ rspec-core (~> 3.5.0)
58
+ rspec-expectations (~> 3.5.0)
59
+ rspec-mocks (~> 3.5.0)
60
+ rspec-core (3.5.0)
61
+ rspec-support (~> 3.5.0)
62
+ rspec-expectations (3.5.0)
63
+ diff-lcs (>= 1.2.0, < 2.0)
64
+ rspec-support (~> 3.5.0)
65
+ rspec-mocks (3.5.0)
66
+ diff-lcs (>= 1.2.0, < 2.0)
67
+ rspec-support (~> 3.5.0)
68
+ rspec-support (3.5.0)
69
+ rubocop (0.41.1)
70
+ parser (>= 2.3.1.1, < 3.0)
71
+ powerpack (~> 0.1)
72
+ rainbow (>= 1.99.1, < 3.0)
73
+ ruby-progressbar (~> 1.7)
74
+ unicode-display_width (~> 1.0, >= 1.0.1)
75
+ ruby-progressbar (1.8.1)
76
+ ruby_dep (1.3.1)
77
+ shellany (0.0.1)
78
+ simplecov (0.11.2)
79
+ docile (~> 1.1.0)
80
+ json (~> 1.8)
81
+ simplecov-html (~> 0.10.0)
82
+ simplecov-html (0.10.0)
83
+ slop (3.6.0)
84
+ term-ansicolor (1.3.2)
85
+ tins (~> 1.0)
86
+ thor (0.19.1)
87
+ tins (1.6.0)
88
+ unicode-display_width (1.1.0)
89
+
90
+ PLATFORMS
91
+ ruby
92
+
93
+ DEPENDENCIES
94
+ bundler (>= 1.11.2, < 2.0)
95
+ coveralls
96
+ d-parse!
97
+ guard
98
+ guard-rake
99
+ rake
100
+ rspec
101
+ rubocop
102
+
103
+ BUNDLED WITH
104
+ 1.12.5
@@ -0,0 +1,3 @@
1
+ guard 'rake', task: 'default' do
2
+ watch(%r{^(lib|test|spec)/})
3
+ end
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2016 Denis Defreyne and contributors
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/NEWS.md ADDED
File without changes
@@ -0,0 +1,137 @@
1
+ [![Build status](http://img.shields.io/travis/ddfreyne/d-parse.svg)](https://travis-ci.org/ddfreyne/d-parse)
2
+ [![Code Climate](http://img.shields.io/codeclimate/github/ddfreyne/d-parse.svg)](https://codeclimate.com/github/ddfreyne/d-parse)
3
+ [![Code Coverage](http://img.shields.io/coveralls/ddfreyne/d-parse.svg)](https://coveralls.io/r/ddfreyne/d-parse)
4
+
5
+ # D★Parse
6
+
7
+ _D★Parse_ is a parser combinator library for Ruby.
8
+
9
+ **STATUS**: Experimental. Pre-alpha. Use at your own risk.
10
+
11
+ ## Example
12
+
13
+ Here is a parser for a series of numbers:
14
+
15
+ ```ruby
16
+ require 'd-parse'
17
+
18
+ module Grammar
19
+ extend DParse::DSL
20
+
21
+ DIGIT = char_in('0'..'9')
22
+
23
+ ROOT =
24
+ seq(
25
+ intersperse(
26
+ repeat(DIGIT).capture.map { |d| d.to_i(10) },
27
+ char(',').ignore,
28
+ ).compact,
29
+ eof,
30
+ ).first
31
+ end
32
+
33
+ res = Grammar::ROOT.apply("1,2,100,582048,07,09")
34
+ case res
35
+ when DParse::Success
36
+ p res.data
37
+ when DParse::Failure
38
+ $stderr.puts res.pretty_message
39
+ exit 1
40
+ end
41
+ ```
42
+
43
+ ## Parsers
44
+
45
+ * `alt(p1, p2, …)` attempts to apply any of the given parsers.
46
+
47
+ * `any` parses any character.
48
+
49
+ * `char_in(cs)` parses a character that is in the `cs` collection.
50
+
51
+ * `char_not_in(cs)` parses a character that is not in the `cs` collection.
52
+
53
+ * `char_not(c)` parses a character that is not `c`.
54
+
55
+ * `char(c)` parses the single character `c`.
56
+
57
+ * `eof` parses the end of file.
58
+
59
+ * `fail` always fails.
60
+
61
+ * `describe(p, name)` sets the name of the parser, so that parsing failures of `p` return a failure with message “expected <var>name</var>”.
62
+
63
+ * `repeat(p)` tries to apply `p` as many times as possible, and never fails.
64
+
65
+ * `seq(p1, p2, …)` tries to apply the given parsers in sequence.
66
+
67
+ * `succeed` always succeeds, without advancing the position.
68
+
69
+ Special modifiers:
70
+
71
+ * `lazy { p }` references the parser `p`, which might not be defined yet. This is useful for recursive definitions.
72
+
73
+ * `p.capture` sets the data of the parsing result of `p`, if successful, to the data between the start and the end of the match.
74
+
75
+ * `p.ignore` sets the data of the parsing result of `p`, if successful, to `nil`. This is particularly useful in combination with `p.compact`.
76
+
77
+ * `p.map { |data| … }` sets the data of the parsing result of `p`, if successful, to the return value of the block. The block gets the data of the success as an argument.
78
+
79
+ * `p.first` sets the data of the parsing result of `p`, if successful, to the first element of the data of the success. This only works if the success data is an array.
80
+
81
+ * `p.second` sets the data of the parsing result of `p`, if successful, to the second element of the data of the success. This only works if the success data is an array.
82
+
83
+ * `p.select_odd` sets the data of the parsing result of `p`, if successful, to each odd element of the data of the success. This only works if the success data is an array.
84
+
85
+ * `p.select_even` sets the data of the parsing result of `p`, if successful, to each even element of the data of the success. This only works if the success data is an array.
86
+
87
+ * `p.compact` sets the data of the parsing result of `p`, if successful, to each non-nil element of the data of the success. This only works if the success data is an array. This is particularly useful in combination with `p.ignore`.
88
+
89
+ ## To do
90
+
91
+ As mentioned above, this software is in an early state, and still lacks many features. It is not yet a fully functional parser combinator library, but it’ll hopefully get there.
92
+
93
+ * Add more combinators (e.g. `repeat1`).
94
+
95
+ * Add support for backtracking, so that `seq(repeat(any), string('donkey'))` can parse `superdonkey`.
96
+
97
+ * Add failure descriptions to all parsers.
98
+
99
+ * Allow renaming failures, so that errors can be easier to understand for hoominz.
100
+
101
+ * Add tests for everything.
102
+
103
+ * Add documentation.
104
+
105
+ * Add support for parsing generic token streams, rather than just characters.
106
+
107
+ ## Commit message conventions
108
+
109
+ As an experiment, I’m going to use commit message conventions slightly adapted from [Angular.js](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md)’s.
110
+
111
+ ```
112
+ <type>(<scope>): <subject>
113
+ <BLANK LINE>
114
+ <body>
115
+ ```
116
+
117
+ The following types are supported:
118
+
119
+ * `feat` (new feature)
120
+ * `fix` (bug fix)
121
+ * `docs` (documentation)
122
+ * `style` (formatting, …)
123
+ * `refactor`
124
+ * `test` (adding tests)
125
+ * `chore` (maintenance, such as build infrastructure changes)
126
+
127
+ The following scopes are supported:
128
+
129
+ * `core`
130
+ * `parsers`
131
+ * `samples`
132
+
133
+ The following rules apply to the subject:
134
+
135
+ * Use the imperative, present tense.
136
+ * Do not capitalize the first letter.
137
+ * Do not end the subject with a period.
@@ -0,0 +1,14 @@
1
+ require 'rspec/core/rake_task'
2
+ require 'rubocop/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.rspec_opts = '-r ./spec/spec_helper.rb --color'
6
+ t.verbose = false
7
+ end
8
+
9
+ RuboCop::RakeTask.new(:rubocop) do |task|
10
+ task.options = %w(--display-cop-names --format simple)
11
+ task.patterns = ['lib/**/*.rb', 'spec/**/*.rb']
12
+ end
13
+
14
+ task default: [:spec, :rubocop]
@@ -0,0 +1,26 @@
1
+ require_relative 'lib/d-parse/version'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'd-parse'
5
+ s.version = DParse::VERSION
6
+ s.homepage = 'http://rubygems.org/gems/d-parse'
7
+ s.summary = 'parser combinator library'
8
+ s.description = 'D★Parse is a library for building parser combinators.'
9
+
10
+ s.author = 'Denis Defreyne'
11
+ s.email = 'denis.defreyne@stoneship.org'
12
+ s.license = 'MIT'
13
+
14
+ s.files =
15
+ Dir['[A-Z]*'] +
16
+ Dir['{lib,spec,samples}/**/*'] +
17
+ ['d-parse.gemspec']
18
+ s.require_paths = ['lib']
19
+
20
+ s.rdoc_options = ['--main', 'README.md']
21
+ s.extra_rdoc_files = ['LICENSE', 'README.md', 'NEWS.md']
22
+
23
+ s.required_ruby_version = '>= 2.1.0'
24
+
25
+ s.add_development_dependency('bundler', '>= 1.11.2', '< 2.0')
26
+ end
@@ -0,0 +1,10 @@
1
+ module DParse
2
+ end
3
+
4
+ require_relative 'd-parse/version.rb'
5
+ require_relative 'd-parse/position.rb'
6
+ require_relative 'd-parse/success.rb'
7
+ require_relative 'd-parse/failure.rb'
8
+ require_relative 'd-parse/parser.rb'
9
+ require_relative 'd-parse/parsers.rb'
10
+ require_relative 'd-parse/dsl.rb'
@@ -0,0 +1,71 @@
1
+ module DParse
2
+ module DSL
3
+ def alt(*ps)
4
+ DParse::Parsers::Alt.new(*ps)
5
+ end
6
+
7
+ def char(c)
8
+ DParse::Parsers::Char.new(c)
9
+ end
10
+
11
+ def char_in(cs)
12
+ DParse::Parsers::CharIn.new(cs)
13
+ end
14
+
15
+ def char_not(c)
16
+ DParse::Parsers::CharNot.new(c)
17
+ end
18
+
19
+ def char_not_in(cs)
20
+ DParse::Parsers::CharNotIn.new(cs)
21
+ end
22
+
23
+ def eof
24
+ DParse::Parsers::EOF.new
25
+ end
26
+
27
+ def intersperse(a, b)
28
+ DParse::Parsers::Intersperse.new(a, b)
29
+ end
30
+
31
+ def except(a, b)
32
+ DParse::Parsers::Except.new(a, b)
33
+ end
34
+
35
+ def lazy(&block)
36
+ DParse::Parsers::Lazy.new(&block)
37
+ end
38
+
39
+ def opt(p)
40
+ DParse::Parsers::Opt.new(p)
41
+ end
42
+
43
+ def describe(p, name)
44
+ DParse::Parsers::Describe.new(p, name)
45
+ end
46
+
47
+ def repeat(p)
48
+ DParse::Parsers::Repeat.new(p)
49
+ end
50
+
51
+ def seq(*ps)
52
+ DParse::Parsers::Seq.new(*ps)
53
+ end
54
+
55
+ def string(s)
56
+ DParse::Parsers::String.new(s)
57
+ end
58
+
59
+ def fail
60
+ DParse::Parsers::Fail.new
61
+ end
62
+
63
+ def succeed
64
+ DParse::Parsers::Succeed.new
65
+ end
66
+
67
+ def whitespace_char
68
+ DParse::Parsers::WhitespaceChar.new
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,46 @@
1
+ module DParse
2
+ class Failure
3
+ attr_reader :input
4
+ attr_reader :pos
5
+ attr_reader :origin
6
+
7
+ def initialize(input, pos, origin: nil)
8
+ @input = input
9
+ @pos = pos
10
+ @origin = origin
11
+ end
12
+
13
+ def map
14
+ self
15
+ end
16
+
17
+ def message
18
+ @_message ||= 'expected ' + (@origin ? @origin.expectation_message : '?')
19
+ end
20
+
21
+ def full_message
22
+ "#{message} at line #{@pos.line + 1}, column #{@pos.column + 1}"
23
+ end
24
+
25
+ def pretty_message
26
+ line = (input.lines[@pos.line] || '').rstrip
27
+ fancy_line = line.chars.map.with_index { |c, i| i == @pos.column ? "\e[31m" + c + "\e[0m" : c }.join
28
+
29
+ lines = [full_message, '', fancy_line, "\e[31m" + ' ' * @pos.column + '↑' + "\e[0m"]
30
+
31
+ lines.join("\n")
32
+ end
33
+
34
+ def to_s
35
+ "Failure(#{@pos}; #{message})"
36
+ end
37
+
38
+ def success?
39
+ false
40
+ end
41
+
42
+ def inspect
43
+ to_s
44
+ end
45
+ end
46
+ end