Skip to content

Commit b8b1e7e

Browse files
committed
[GR-18163] Add a tool to show the parse AST
PullRequest: truffleruby/3360
2 parents c5dcfbd + 634d83f commit b8b1e7e

File tree

4 files changed

+126
-0
lines changed

4 files changed

+126
-0
lines changed

doc/contributor/parser.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Parser
2+
3+
The parser in TruffleRuby is originally from [JRuby's parser](https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/parser/RubyParser.y).
4+
It has several modifications to use different names, uses Rope instead of ByteList, etc.
5+
6+
## Printing the parser AST
7+
8+
```bash
9+
$ jt -q ruby tool/parse_ast.rb '2 ** 42'
10+
Source:
11+
2 ** 42
12+
13+
AST:
14+
RootParseNode
15+
CallParseNode:**
16+
FixnumParseNode[value=2]
17+
ArrayParseNode
18+
FixnumParseNode[value=42]
19+
, null
20+
```
21+
22+
```bash
23+
$ jt -q ruby tool/parse_ast.rb some_file.rb
24+
```
25+
26+
You can also compare to JRuby's AST with:
27+
```bash
28+
$ jruby tool/parse_ast.rb '2 ** 42'
29+
Source:
30+
2 ** 42
31+
32+
AST:
33+
RootNode line: 0
34+
CallNode:** line: 0
35+
FixnumNode line: 0, long: 2
36+
ArrayNode line: 0
37+
FixnumNode line: 0, long: 42
38+
, null
39+
```

src/main/java/org/truffleruby/debug/TruffleDebugNodes.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.oracle.truffle.api.interop.UnsupportedTypeException;
3131
import com.oracle.truffle.api.nodes.RootNode;
3232
import com.oracle.truffle.api.object.DynamicObjectLibrary;
33+
import com.oracle.truffle.api.source.Source;
3334
import com.oracle.truffle.api.source.SourceSection;
3435
import org.graalvm.collections.Pair;
3536
import org.truffleruby.Layouts;
@@ -98,6 +99,11 @@
9899
import com.oracle.truffle.api.nodes.NodeUtil;
99100
import com.oracle.truffle.api.object.Shape;
100101
import com.oracle.truffle.api.utilities.TriState;
102+
import org.truffleruby.parser.RubyDeferredWarnings;
103+
import org.truffleruby.parser.RubySource;
104+
import org.truffleruby.parser.TranslatorDriver;
105+
import org.truffleruby.parser.parser.ParserConfiguration;
106+
import org.truffleruby.parser.scope.StaticScope;
101107

102108
@CoreModule("Truffle::Debug")
103109
public abstract class TruffleDebugNodes {
@@ -206,6 +212,28 @@ protected Object printBacktrace() {
206212

207213
}
208214

215+
@CoreMethod(names = "parse_ast", onSingleton = true, required = 1)
216+
public abstract static class ParseASTNode extends CoreMethodArrayArgumentsNode {
217+
@TruffleBoundary
218+
@Specialization(guards = "strings.isRubyString(code)")
219+
protected Object ast(Object code,
220+
@CachedLibrary(limit = "LIBSTRING_CACHE") RubyStringLibrary strings,
221+
@Cached MakeStringNode makeStringNode) {
222+
String codeString = strings.getJavaString(code);
223+
String name = "<parse_ast>";
224+
var source = Source.newBuilder("ruby", codeString, name).build();
225+
var rubySource = new RubySource(source, name);
226+
227+
var staticScope = new StaticScope(StaticScope.Type.LOCAL, null);
228+
var parserConfiguration = new ParserConfiguration(null, false, true, false);
229+
var rubyWarnings = new RubyDeferredWarnings();
230+
var rootParseNode = TranslatorDriver
231+
.parseToJRubyAST(getContext(), rubySource, staticScope, parserConfiguration, rubyWarnings);
232+
233+
return makeStringNode.executeMake(rootParseNode.toString(), Encodings.UTF_8, CodeRange.CR_UNKNOWN);
234+
}
235+
}
236+
209237
@CoreMethod(names = "ast", onSingleton = true, required = 1)
210238
public abstract static class ASTNode extends CoreMethodArrayArgumentsNode {
211239
@TruffleBoundary

src/main/java/org/truffleruby/parser/ast/FloatParseNode.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,9 @@ public void setValue(double value) {
7575
public List<ParseNode> childNodes() {
7676
return EMPTY_LIST;
7777
}
78+
79+
@Override
80+
protected String toStringInternal() {
81+
return "value=" + value;
82+
}
7883
}

tool/parse_ast.rb

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. This
2+
# code is released under a tri EPL/GPL/LGPL license. You can use it,
3+
# redistribute it and/or modify it under the terms of the:
4+
#
5+
# Eclipse Public License version 2.0, or
6+
# GNU General Public License version 2, or
7+
# GNU Lesser General Public License version 2.1.
8+
9+
# Based on bin/ast from JRuby
10+
11+
abort "Usage: jt ruby #{__FILE__} FILE or 'CODE'" unless ARGV.size == 1
12+
if File.exist?(ARGV[0])
13+
code = File.read(ARGV[0])
14+
else
15+
code = ARGV[0]
16+
end
17+
18+
def indexes(string, lindex, rindex)
19+
lindex = string.index("(", lindex) if lindex
20+
rindex = string.index(")", rindex) if rindex
21+
[lindex, rindex]
22+
end
23+
24+
def indent(string)
25+
depth = -1
26+
27+
lindex, rindex = indexes(string, 0, 0)
28+
29+
while lindex || rindex
30+
if lindex && lindex < rindex
31+
depth += 1
32+
string[lindex, 1] = "\n#{' ' * depth}"
33+
else
34+
depth -= 1
35+
string[rindex, 1] = "\n"
36+
end
37+
38+
lindex, rindex = indexes(string, lindex, rindex)
39+
end
40+
string.gsub(/,\s*$/, '').squeeze("\n")
41+
end
42+
43+
puts "Source:"
44+
puts code
45+
puts
46+
47+
print "AST:"
48+
if RUBY_ENGINE == "jruby"
49+
require 'jruby'
50+
ast_to_string = JRuby.parse(code).to_string
51+
else
52+
ast_to_string = Truffle::Debug.parse_ast(code)
53+
end
54+
puts indent(ast_to_string)

0 commit comments

Comments
 (0)