parser 2.0.0.pre1 → 2.0.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +150 -99
- data/lib/parser.rb +1 -1
- data/lib/parser/ast/node.rb +17 -0
- data/lib/parser/base.rb +10 -0
- data/lib/parser/builders/default.rb +17 -4
- data/lib/parser/diagnostic/engine.rb +49 -1
- data/lib/parser/lexer.rl +5 -0
- data/lib/parser/rewriter.rb +79 -0
- data/lib/parser/syntax_error.rb +12 -1
- data/lib/parser/version.rb +1 -1
- data/test/test_diagnostic_engine.rb +3 -1
- data/test/test_parser.rb +21 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4144b03e5cb0074bd4e6a62442b3954365d7ec8
|
4
|
+
data.tar.gz: 4276d7b16e60596143e0cc22b19ef0f048446af5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 644d1bb91060042670f4602788785bec67c8cc594ffa44d78922e74ba8201d954789e9b8953632b8f2c13b11efe1a2588cdde7bed8b3718c40189dad766104c0
|
7
|
+
data.tar.gz: 75a5830a2a534f63663e83caf4333f5d462a2a591bc72adec2a09d61fc90a2f2d3ea106c26d37df225949beb6e6139c2b2033e76d1448ec0f365160bc28d6ba7
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
Changelog
|
2
2
|
=========
|
3
3
|
|
4
|
+
v2.0.0.pre2 (2013-07-11)
|
5
|
+
------------------------
|
6
|
+
|
7
|
+
Features implemented:
|
8
|
+
* Allow to differentiate between __FILE__/__LINE__ and literals (closes #89). (Peter Zotov)
|
9
|
+
* Add attribute `diagnostic' to Parser::SyntaxError (closes #88). (Peter Zotov)
|
10
|
+
|
11
|
+
Bugs fixed:
|
12
|
+
* Don't treat byte order mark as an identifier (closes #91). (Peter Zotov)
|
13
|
+
|
4
14
|
v2.0.0.beta10 (2013-07-02)
|
5
15
|
--------------------------
|
6
16
|
|
data/README.md
CHANGED
@@ -5,143 +5,194 @@
|
|
5
5
|
[![Code Climate](https://codeclimate.com/github/whitequark/parser.png)](https://codeclimate.com/github/whitequark/parser)
|
6
6
|
[![Coverage Status](https://coveralls.io/repos/whitequark/parser/badge.png?branch=master)](https://coveralls.io/r/whitequark/parser)
|
7
7
|
|
8
|
-
_Parser_ is a production-ready Ruby parser written in pure Ruby. It
|
9
|
-
|
10
|
-
ruby_parser.
|
8
|
+
_Parser_ is a production-ready Ruby parser written in pure Ruby. It performs on
|
9
|
+
par or better than Ripper, Melbourne, JRubyParser or ruby\_parser.
|
11
10
|
|
12
|
-
You can also use [unparser](https://github.com/mbj/unparser) to produce
|
11
|
+
You can also use [unparser](https://github.com/mbj/unparser) to produce
|
12
|
+
equivalent source code from Parser's ASTs.
|
13
13
|
|
14
14
|
## Installation
|
15
15
|
|
16
|
-
Most recent version of Parser is 2.0; however, per
|
16
|
+
Most recent version of Parser is 2.0; however, per
|
17
|
+
[release schedule](https://github.com/whitequark/parser/issues/51), it stays in
|
18
|
+
the beta status for a while. However, it handles much more input than stable
|
19
|
+
1.x branch, and for new work it is advisable to use the beta versions.
|
17
20
|
|
18
|
-
|
19
|
-
$ gem install parser --pre
|
20
|
-
~~~
|
21
|
+
$ gem install parser --pre
|
21
22
|
|
22
23
|
## Usage
|
23
24
|
|
24
25
|
Parse a chunk of code:
|
25
26
|
|
26
|
-
|
27
|
-
require 'parser/current'
|
27
|
+
require 'parser/current'
|
28
28
|
|
29
|
-
p Parser::CurrentRuby.parse("2 + 2")
|
30
|
-
# (send
|
31
|
-
# (int 2) :+
|
32
|
-
# (int 2))
|
33
|
-
~~~
|
29
|
+
p Parser::CurrentRuby.parse("2 + 2")
|
30
|
+
# (send
|
31
|
+
# (int 2) :+
|
32
|
+
# (int 2))
|
34
33
|
|
35
34
|
Access the AST's source map:
|
36
35
|
|
37
|
-
|
38
|
-
|
39
|
-
#
|
40
|
-
# @
|
41
|
-
# @
|
42
|
-
# @
|
43
|
-
# @selector=#<Source::Range (string) 2...3>>
|
36
|
+
p Parser::CurrentRuby.parse("2 + 2").loc
|
37
|
+
# #<Parser::Source::Map::Send:0x007fe0ca8a69b8
|
38
|
+
# @begin=nil,
|
39
|
+
# @end=nil,
|
40
|
+
# @expression=#<Source::Range (string) 0...5>,
|
41
|
+
# @selector=#<Source::Range (string) 2...3>>
|
44
42
|
|
45
|
-
p Parser::CurrentRuby.parse("2 + 2").loc.selector.to_source
|
46
|
-
# "+"
|
47
|
-
~~~
|
43
|
+
p Parser::CurrentRuby.parse("2 + 2").loc.selector.to_source
|
44
|
+
# "+"
|
48
45
|
|
49
46
|
Parse a chunk of code and display all diagnostics:
|
50
47
|
|
51
|
-
|
52
|
-
parser =
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
buffer =
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
(
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
2
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
2
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
(
|
97
|
-
|
98
|
-
(int 2))
|
99
|
-
~~~
|
48
|
+
parser = Parser::CurrentRuby.new
|
49
|
+
parser.diagnostics.consumer = lambda do |diag|
|
50
|
+
puts diag.render
|
51
|
+
end
|
52
|
+
|
53
|
+
buffer = Parser::Source::Buffer.new('(string)')
|
54
|
+
buffer.source = "foo *bar"
|
55
|
+
|
56
|
+
p parser.parse(buffer)
|
57
|
+
# (string):1:5: warning: `*' interpreted as argument prefix
|
58
|
+
# foo *bar
|
59
|
+
# ^
|
60
|
+
# (send nil :foo
|
61
|
+
# (splat
|
62
|
+
# (send nil :bar)))
|
63
|
+
|
64
|
+
If you reuse the same parser object for multiple `#parse` runs, you need to
|
65
|
+
`#reset` it.
|
66
|
+
|
67
|
+
You can also use the `ruby-parse` utility (it's bundled with the gem) to play
|
68
|
+
with Parser:
|
69
|
+
|
70
|
+
$ ruby-parse -L -e "2+2"
|
71
|
+
(send
|
72
|
+
(int 2) :+
|
73
|
+
(int 2))
|
74
|
+
2+2
|
75
|
+
~ selector
|
76
|
+
~~~ expression
|
77
|
+
(int 2)
|
78
|
+
2+2
|
79
|
+
~ expression
|
80
|
+
(int 2)
|
81
|
+
2+2
|
82
|
+
|
83
|
+
$ ruby-parse -E -e "2+2"
|
84
|
+
2+2
|
85
|
+
^ tINTEGER 2 expr_end [0 <= cond] [0 <= cmdarg]
|
86
|
+
2+2
|
87
|
+
^ tPLUS "+" expr_beg [0 <= cond] [0 <= cmdarg]
|
88
|
+
2+2
|
89
|
+
^ tINTEGER 2 expr_end [0 <= cond] [0 <= cmdarg]
|
90
|
+
2+2
|
91
|
+
^ false "$eof" expr_end [0 <= cond] [0 <= cmdarg]
|
92
|
+
(send
|
93
|
+
(int 2) :+
|
94
|
+
(int 2))
|
100
95
|
|
101
96
|
## Features
|
102
97
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
98
|
+
* Precise source location reporting.
|
99
|
+
* [Documented](doc/AST_FORMAT.md) AST format which is convenient to work with.
|
100
|
+
* A simple interface and a powerful, tweakable one.
|
101
|
+
* Parses 1.8, 1.9, 2.0 and 2.1 (preliminary) syntax with backwards-compatible
|
102
|
+
AST formats.
|
103
|
+
* Parsing error recovery.
|
104
|
+
* Improved [clang-like][] diagnostic messages with location information.
|
105
|
+
* Written in pure Ruby, runs on MRI 1.8.7 or >=1.9.2, JRuby and Rubinius in 1.8
|
106
|
+
and 1.9 mode.
|
107
|
+
* Only two runtime dependencies: the gems [ast][] and [slop][].
|
108
|
+
* [Insane][insane-lexer] Ruby lexer rewritten from scratch in Ragel.
|
109
|
+
* 100% test coverage for Bison grammars (except error recovery).
|
110
|
+
* Readable, commented source code.
|
111
|
+
|
112
|
+
[clang-like]: http://clang.llvm.org/diagnostics.html
|
113
|
+
[ast]: http://rubygems.org/gems/ast
|
114
|
+
[slop]: http://rubygems.org/gems/slop
|
115
|
+
[insane-lexer]: http://whitequark.org/blog/2013/04/01/ruby-hacking-guide-ch-11-finite-state-lexer/
|
119
116
|
|
120
117
|
## Documentation
|
121
118
|
|
122
|
-
Documentation for parser is available online on
|
119
|
+
Documentation for parser is available online on
|
120
|
+
[rdoc.info](http://rdoc.info/github/whitequark/parser).
|
121
|
+
|
122
|
+
### Node names
|
123
|
+
|
124
|
+
Several Parser nodes seem to be confusing enough to warrant a dedicated README section.
|
125
|
+
|
126
|
+
#### (block)
|
127
|
+
|
128
|
+
The `(block)` node passes a Ruby block, that is, a closure, to a method call represented by its first child, a `send` node. To demonstrate:
|
129
|
+
|
130
|
+
```
|
131
|
+
$ ruby-parse -e 'foo { |x| x + 2 }'
|
132
|
+
(block
|
133
|
+
(send nil :foo)
|
134
|
+
(args
|
135
|
+
(arg :x))
|
136
|
+
(send
|
137
|
+
(lvar :x) :+
|
138
|
+
(int 2)))
|
139
|
+
```
|
140
|
+
|
141
|
+
#### (begin) and (kwbegin)
|
142
|
+
|
143
|
+
**TL;DR: Unless you perform rewriting, treat `(begin)` and `(kwbegin)` as the same node type.**
|
144
|
+
|
145
|
+
Both `(begin)` and `(kwbegin)` nodes represent compound statements, that is, several expressions which are executed sequentally and the value of the last one is the value of entire compound statement. They may take several forms in the source code:
|
146
|
+
|
147
|
+
* `foo; bar`: without delimiters
|
148
|
+
* `(foo; bar)`: parenthesized
|
149
|
+
* `begin foo; bar; end`: grouped with `begin` keyword
|
150
|
+
* `def x; foo; bar; end`: grouped inside a method definition
|
151
|
+
|
152
|
+
and so on.
|
153
|
+
|
154
|
+
Note that, despite its name, `kwbegin` node only has tangential relation to the `begin` keyword. Normally, Parser AST is semantic, that is, if two constructs look differently but behave identically, they get parsed to the same node. However, there exists a peculiar construct called post-loop in Ruby:
|
155
|
+
|
156
|
+
```
|
157
|
+
begin
|
158
|
+
body
|
159
|
+
end while condition
|
160
|
+
```
|
161
|
+
|
162
|
+
This specific syntactic construct, that is, keyword `begin..end` block followed by a postfix `while`, [behaves][postloop] very unlike other similar constructs, e.g. `(body) while condition`. While the body itself is wrapped into a `while-post` node, Parser also supports rewriting, and in that context it is important to not accidentally convert one kind of loop into another.
|
163
|
+
|
164
|
+
[postloop]: http://rosettacode.org/wiki/Loops/Do-while#Ruby
|
165
|
+
|
166
|
+
(Parser also needs the `(kwbegin)` node type internally, and it is highly problematic to map it back to `(begin)`.)
|
123
167
|
|
124
168
|
## Known issues
|
125
169
|
|
126
170
|
### Void value expressions
|
127
171
|
|
128
|
-
So-called "void value expressions" are not handled by Parser. For a description
|
172
|
+
So-called "void value expressions" are not handled by Parser. For a description
|
173
|
+
of what a void value expression is, see [this
|
174
|
+
gist](https://gist.github.com/JoshCheek/5625007) and [this Parser
|
175
|
+
issue](https://github.com/whitequark/parser/issues/72).
|
129
176
|
|
130
|
-
It is not clear which rules this piece of static analysis follows, or which
|
177
|
+
It is not clear which rules this piece of static analysis follows, or which
|
178
|
+
problem does it solve. It is not implemented because there is no clear
|
179
|
+
specification allowing us to verify the behavior.
|
131
180
|
|
132
181
|
## Contributors
|
133
182
|
|
134
|
-
|
135
|
-
|
183
|
+
* Peter Zotov ([whitequark][])
|
184
|
+
* Magnus Holm ([judofyr][])
|
136
185
|
|
137
|
-
|
138
|
-
|
186
|
+
[whitequark]: https://github.com/whitequark
|
187
|
+
[judofyr]: https://github.com/judofyr
|
139
188
|
|
140
189
|
## Acknowledgements
|
141
190
|
|
142
|
-
The lexer testsuite is derived from
|
191
|
+
The lexer testsuite is derived from
|
192
|
+
[ruby\_parser](http://github.com/seattlerb/ruby_parser).
|
143
193
|
|
144
|
-
The Bison parser rules are derived from [Ruby MRI](http://github.com/ruby/ruby)
|
194
|
+
The Bison parser rules are derived from [Ruby MRI](http://github.com/ruby/ruby)
|
195
|
+
parse.y.
|
145
196
|
|
146
197
|
## Contributing
|
147
198
|
|
data/lib/parser.rb
CHANGED
@@ -74,7 +74,7 @@ module Parser
|
|
74
74
|
:no_dot_digit_literal => 'no .<digit> floating literal anymore; put 0 before dot',
|
75
75
|
:bare_backslash => 'bare backslash only allowed before newline',
|
76
76
|
:unexpected => "unexpected `%{character}'",
|
77
|
-
:embedded_document => 'embedded document
|
77
|
+
:embedded_document => 'embedded document meets end of file (and they embark on a romantic journey)',
|
78
78
|
|
79
79
|
# Lexer warnings
|
80
80
|
:invalid_escape_use => 'invalid character syntax; use ?%{escape}',
|
data/lib/parser/ast/node.rb
CHANGED
@@ -1,11 +1,28 @@
|
|
1
1
|
module Parser
|
2
2
|
module AST
|
3
3
|
|
4
|
+
##
|
5
|
+
# {Parser::AST::Node} contains information about a single AST node and its
|
6
|
+
# child nodes, it extends the basic `AST::Node` class provided by the "ast"
|
7
|
+
# Gem.
|
8
|
+
#
|
9
|
+
# @!attribute [r] location
|
10
|
+
# @return [Parser::Source::Map]
|
11
|
+
#
|
4
12
|
class Node < ::AST::Node
|
5
13
|
attr_reader :location
|
6
14
|
|
7
15
|
alias loc location
|
8
16
|
|
17
|
+
##
|
18
|
+
# Assigns various properties to the current AST node. Currently only the
|
19
|
+
# location can be set.
|
20
|
+
#
|
21
|
+
# @param [Hash] properties
|
22
|
+
#
|
23
|
+
# @option properties [Parser::Source::Map] :location Location information
|
24
|
+
# of the node.
|
25
|
+
#
|
9
26
|
def assign_properties(properties)
|
10
27
|
if (location = properties[:location])
|
11
28
|
@location = location
|
data/lib/parser/base.rb
CHANGED
@@ -49,9 +49,19 @@ module Parser
|
|
49
49
|
end
|
50
50
|
|
51
51
|
attr_reader :diagnostics
|
52
|
+
|
53
|
+
attr_reader :builder
|
54
|
+
|
55
|
+
##
|
56
|
+
# @api internal
|
57
|
+
#
|
52
58
|
attr_reader :static_env
|
53
59
|
|
60
|
+
##
|
54
61
|
# The source file currently being parsed.
|
62
|
+
#
|
63
|
+
# @api internal
|
64
|
+
#
|
55
65
|
attr_reader :source_buffer
|
56
66
|
|
57
67
|
##
|
@@ -2,6 +2,11 @@ module Parser
|
|
2
2
|
|
3
3
|
class Builders::Default
|
4
4
|
attr_accessor :parser
|
5
|
+
attr_accessor :emit_file_line_as_literals
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@emit_file_line_as_literals = true
|
9
|
+
end
|
5
10
|
|
6
11
|
#
|
7
12
|
# Literals
|
@@ -257,12 +262,20 @@ module Parser
|
|
257
262
|
def accessible(node)
|
258
263
|
case node.type
|
259
264
|
when :__FILE__
|
260
|
-
|
261
|
-
node.loc
|
265
|
+
if @emit_file_line_as_literals
|
266
|
+
n(:str, [ node.loc.expression.source_buffer.name ],
|
267
|
+
node.loc)
|
268
|
+
else
|
269
|
+
node
|
270
|
+
end
|
262
271
|
|
263
272
|
when :__LINE__
|
264
|
-
|
265
|
-
node.loc
|
273
|
+
if @emit_file_line_as_literals
|
274
|
+
n(:int, [ node.loc.expression.line ],
|
275
|
+
node.loc)
|
276
|
+
else
|
277
|
+
node
|
278
|
+
end
|
266
279
|
|
267
280
|
when :__ENCODING__
|
268
281
|
n(:const, [ n(:const, [ nil, :Encoding], nil), :UTF_8 ],
|
@@ -1,11 +1,44 @@
|
|
1
1
|
module Parser
|
2
2
|
|
3
|
+
##
|
4
|
+
# {Parser::Diagnostic::Engine} provides a basic API for dealing with
|
5
|
+
# diagnostics by delegating them to registered consumers.
|
6
|
+
#
|
7
|
+
# Basic usage is as following:
|
8
|
+
#
|
9
|
+
# buffer = Parser::Source::Buffer.new(__FILE__)
|
10
|
+
# buffer.code = 'foobar'
|
11
|
+
#
|
12
|
+
# consumer = lambda do |diagnostic|
|
13
|
+
# puts diagnostic.message
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# engine = Parser::Diagnostic::Engine.new(consumer)
|
17
|
+
# diagnostic = Parser::Diagnostic.new(:warning, 'warning!', buffer, 1..2)
|
18
|
+
#
|
19
|
+
# engine.process(diagnostic) # => "warning!"
|
20
|
+
#
|
21
|
+
# @!attribute [rw] consumer
|
22
|
+
# @return [#call]
|
23
|
+
#
|
24
|
+
# @!attribute [rw] all_errors_are_fatal
|
25
|
+
# When set to `true` any error that is encountered will result in
|
26
|
+
# {Parser::SyntaxError} being raised.
|
27
|
+
# @return [TrueClass|FalseClass]
|
28
|
+
#
|
29
|
+
# @!attribute [rw] ignore_warnings
|
30
|
+
# When set to `true` warnings will be ignored.
|
31
|
+
# @return [TrueClass|FalseClass]
|
32
|
+
#
|
3
33
|
class Diagnostic::Engine
|
4
34
|
attr_accessor :consumer
|
5
35
|
|
6
36
|
attr_accessor :all_errors_are_fatal
|
7
37
|
attr_accessor :ignore_warnings
|
8
38
|
|
39
|
+
##
|
40
|
+
# @param [#call] consumer
|
41
|
+
#
|
9
42
|
def initialize(consumer=nil)
|
10
43
|
@consumer = consumer
|
11
44
|
|
@@ -13,6 +46,13 @@ module Parser
|
|
13
46
|
@ignore_warnings = false
|
14
47
|
end
|
15
48
|
|
49
|
+
##
|
50
|
+
# Processes a diagnostic and optionally raises {Parser::SyntaxError} when
|
51
|
+
# `all_errors_are_fatal` is set to true.
|
52
|
+
#
|
53
|
+
# @param [Parser::Diagnostic] diagnostic
|
54
|
+
# @return [Parser::Diagnostic::Engine]
|
55
|
+
#
|
16
56
|
def process(diagnostic)
|
17
57
|
if ignore?(diagnostic)
|
18
58
|
# do nothing
|
@@ -21,7 +61,7 @@ module Parser
|
|
21
61
|
end
|
22
62
|
|
23
63
|
if raise?(diagnostic)
|
24
|
-
raise Parser::SyntaxError, diagnostic
|
64
|
+
raise Parser::SyntaxError, diagnostic
|
25
65
|
end
|
26
66
|
|
27
67
|
self
|
@@ -29,11 +69,19 @@ module Parser
|
|
29
69
|
|
30
70
|
protected
|
31
71
|
|
72
|
+
##
|
73
|
+
# @param [Parser::Diagnostic] diagnostic
|
74
|
+
# @return [TrueClass|FalseClass]
|
75
|
+
#
|
32
76
|
def ignore?(diagnostic)
|
33
77
|
@ignore_warnings &&
|
34
78
|
diagnostic.level == :warning
|
35
79
|
end
|
36
80
|
|
81
|
+
##
|
82
|
+
# @param [Parser::Diagnostic] diagnostic
|
83
|
+
# @return [TrueClass|FalseClass]
|
84
|
+
#
|
37
85
|
def raise?(diagnostic)
|
38
86
|
(@all_errors_are_fatal &&
|
39
87
|
diagnostic.level == :error) ||
|
data/lib/parser/lexer.rl
CHANGED
data/lib/parser/rewriter.rb
CHANGED
@@ -1,5 +1,54 @@
|
|
1
1
|
module Parser
|
2
|
+
##
|
3
|
+
# {Parser::Rewriter} offers a basic API that makes it easy to rewrite
|
4
|
+
# existing ASTs. It's built on top of {Parser::AST::Processor} and
|
5
|
+
# {Parser::Source::Rewriter}.
|
6
|
+
#
|
7
|
+
# For example, assume you want to remove `do` tokens from a while statement.
|
8
|
+
# You can do this as following:
|
9
|
+
#
|
10
|
+
# class RemoveDo < Parser::Rewriter
|
11
|
+
# def on_while(node)
|
12
|
+
# # Check if the statement starts with "do"
|
13
|
+
# if node.location.begin.is?('do')
|
14
|
+
# remove(node.location.begin)
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# code = <<-EOF
|
20
|
+
# while true do
|
21
|
+
# puts 'hello'
|
22
|
+
# end
|
23
|
+
# EOF
|
24
|
+
#
|
25
|
+
# buffer = Parser::Source::Buffer.new('(example)')
|
26
|
+
# buffer.code = code
|
27
|
+
# parser = Parser::CurrentRuby.new
|
28
|
+
# ast = parser.parse(buffer)
|
29
|
+
# rewriter = RemoveDo.new
|
30
|
+
#
|
31
|
+
# # Rewrite the AST, returns a String with the new form.
|
32
|
+
# puts rewriter.rewrite(buffer, ast)
|
33
|
+
#
|
34
|
+
# This would result in the following Ruby code:
|
35
|
+
#
|
36
|
+
# while true
|
37
|
+
# puts 'hello'
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# Keep in mind that {Parser::Rewriter} does not take care of indentation when
|
41
|
+
# inserting/replacing code so you'll have to do this yourself.
|
42
|
+
#
|
2
43
|
class Rewriter < Parser::AST::Processor
|
44
|
+
##
|
45
|
+
# Rewrites the AST/source buffer and returns a String containing the new
|
46
|
+
# version.
|
47
|
+
#
|
48
|
+
# @param [Parser::Source::Buffer] source_buffer
|
49
|
+
# @param [Parser::AST::Node] ast
|
50
|
+
# @return [String]
|
51
|
+
#
|
3
52
|
def rewrite(source_buffer, ast)
|
4
53
|
@source_rewriter = Source::Rewriter.new(source_buffer)
|
5
54
|
|
@@ -10,22 +59,52 @@ module Parser
|
|
10
59
|
|
11
60
|
private
|
12
61
|
|
62
|
+
##
|
63
|
+
# Returns `true` if the specified node is an assignment node, returns false
|
64
|
+
# otherwise.
|
65
|
+
#
|
66
|
+
# @param [Parser::AST::Node] node
|
67
|
+
# @return [TrueClass|FalseClass]
|
68
|
+
#
|
13
69
|
def assignment?(node)
|
14
70
|
[:lvasgn, :ivasgn, :gvasgn, :cvasgn, :casgn].include?(node.type)
|
15
71
|
end
|
16
72
|
|
73
|
+
##
|
74
|
+
# Removes the source range.
|
75
|
+
#
|
76
|
+
# @param [Parser::Source::Range] range
|
77
|
+
#
|
17
78
|
def remove(range)
|
18
79
|
@source_rewriter.remove(range)
|
19
80
|
end
|
20
81
|
|
82
|
+
##
|
83
|
+
# Inserts new code before the given source range.
|
84
|
+
#
|
85
|
+
# @param [Parser::Source::Range] range
|
86
|
+
# @param [String] content
|
87
|
+
#
|
21
88
|
def insert_before(range, content)
|
22
89
|
@source_rewriter.insert_before(range, content)
|
23
90
|
end
|
24
91
|
|
92
|
+
##
|
93
|
+
# Inserts new code after the given source range.
|
94
|
+
#
|
95
|
+
# @param [Parser::Source::Range] range
|
96
|
+
# @param [String] content
|
97
|
+
#
|
25
98
|
def insert_after(range, content)
|
26
99
|
@source_rewriter.insert_after(range, content)
|
27
100
|
end
|
28
101
|
|
102
|
+
##
|
103
|
+
# Replaces the code of the source range `range` with `content`.
|
104
|
+
#
|
105
|
+
# @param [Parser::Source::Range] range
|
106
|
+
# @param [String] content
|
107
|
+
#
|
29
108
|
def replace(range, content)
|
30
109
|
@source_rewriter.replace(range, content)
|
31
110
|
end
|
data/lib/parser/syntax_error.rb
CHANGED
@@ -1,3 +1,14 @@
|
|
1
1
|
module Parser
|
2
|
-
|
2
|
+
##
|
3
|
+
# {Parser::SyntaxError} is raised whenever parser detects a syntax error
|
4
|
+
# (what a surprise!) similar to the standard SyntaxError class.
|
5
|
+
#
|
6
|
+
class SyntaxError < StandardError
|
7
|
+
attr_reader :diagnostic
|
8
|
+
|
9
|
+
def initialize(diagnostic)
|
10
|
+
@diagnostic = diagnostic
|
11
|
+
super(diagnostic.message)
|
12
|
+
end
|
13
|
+
end
|
3
14
|
end
|
data/lib/parser/version.rb
CHANGED
@@ -32,10 +32,12 @@ class TestDiagnosticEngine < Minitest::Test
|
|
32
32
|
|
33
33
|
error = Parser::Diagnostic.new(:error, 'foo', @buffer, 1..2)
|
34
34
|
|
35
|
-
assert_raises Parser::SyntaxError do
|
35
|
+
err = assert_raises Parser::SyntaxError, "foo" do
|
36
36
|
@engine.process(error)
|
37
37
|
end
|
38
38
|
|
39
|
+
assert_equal error, err.diagnostic
|
40
|
+
|
39
41
|
assert_equal [error], @queue
|
40
42
|
end
|
41
43
|
|
data/test/test_parser.rb
CHANGED
@@ -4315,7 +4315,28 @@ class TestParser < Minitest::Test
|
|
4315
4315
|
ALL_VERSIONS - %w(1.8 1.9))
|
4316
4316
|
end
|
4317
4317
|
|
4318
|
+
def test_file_line_non_literals
|
4319
|
+
with_versions(ALL_VERSIONS) do |_ver, parser|
|
4320
|
+
parser.builder.emit_file_line_as_literals = false
|
4321
|
+
|
4322
|
+
source_file = Parser::Source::Buffer.new('(comments)')
|
4323
|
+
source_file.source = "[__FILE__, __LINE__]"
|
4324
|
+
|
4325
|
+
ast = parser.parse(source_file)
|
4326
|
+
|
4327
|
+
assert_equal s(:array, s(:__FILE__), s(:__LINE__)), ast
|
4328
|
+
end
|
4329
|
+
end
|
4330
|
+
|
4318
4331
|
if defined?(Encoding)
|
4332
|
+
def test_bom
|
4333
|
+
assert_parses(
|
4334
|
+
s(:int, 1),
|
4335
|
+
%Q{\xef\xbb\xbf1}.force_encoding(Encoding::BINARY),
|
4336
|
+
%q{},
|
4337
|
+
%w(1.9 2.0 2.1))
|
4338
|
+
end
|
4339
|
+
|
4319
4340
|
def test_magic_encoding_comment
|
4320
4341
|
assert_parses(
|
4321
4342
|
s(:begin,
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.
|
4
|
+
version: 2.0.0.pre2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Zotov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-07-
|
11
|
+
date: 2013-07-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ast
|