d-parse 0.1

Sign up to get free protection for your applications and to get access to all the features.
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