diff options
Diffstat (limited to 'ast.rb')
-rw-r--r-- | ast.rb | 114 |
1 files changed, 100 insertions, 14 deletions
@@ -20,21 +20,47 @@ module RubyVM::AbstractSyntaxTree # call-seq: - # RubyVM::AbstractSyntaxTree.parse(string) -> RubyVM::AbstractSyntaxTree::Node + # RubyVM::AbstractSyntaxTree.parse(string, keep_script_lines: RubyVM.keep_script_lines, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node # # Parses the given _string_ into an abstract syntax tree, # returning the root node of that tree. # - # SyntaxError is raised if the given _string_ is invalid syntax. - # # RubyVM::AbstractSyntaxTree.parse("x = 1 + 2") # # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-1:9> - def self.parse string, keep_script_lines: false - Primitive.ast_s_parse string, keep_script_lines + # + # If <tt>keep_script_lines: true</tt> option is provided, the text of the parsed + # source is associated with nodes and is available via Node#script_lines. + # + # If <tt>keep_tokens: true</tt> option is provided, Node#tokens are populated. + # + # SyntaxError is raised if the given _string_ is invalid syntax. To overwrite this + # behavior, <tt>error_tolerant: true</tt> can be provided. In this case, the parser + # will produce a tree where expressions with syntax errors would be represented by + # Node with <tt>type=:ERROR</tt>. + # + # root = RubyVM::AbstractSyntaxTree.parse("x = 1; p(x; y=2") + # # <internal:ast>:33:in `parse': syntax error, unexpected ';', expecting ')' (SyntaxError) + # # x = 1; p(x; y=2 + # # ^ + # + # root = RubyVM::AbstractSyntaxTree.parse("x = 1; p(x; y=2", error_tolerant: true) + # # (SCOPE@1:0-1:15 + # # tbl: [:x, :y] + # # args: nil + # # body: (BLOCK@1:0-1:15 (LASGN@1:0-1:5 :x (LIT@1:4-1:5 1)) (ERROR@1:7-1:11) (LASGN@1:12-1:15 :y (LIT@1:14-1:15 2)))) + # root.children.last.children + # # [(LASGN@1:0-1:5 :x (LIT@1:4-1:5 1)), + # # (ERROR@1:7-1:11), + # # (LASGN@1:12-1:15 :y (LIT@1:14-1:15 2))] + # + # Note that parsing continues even after the errored expression. + # + def self.parse string, keep_script_lines: RubyVM.keep_script_lines, error_tolerant: false, keep_tokens: false + Primitive.ast_s_parse string, keep_script_lines, error_tolerant, keep_tokens end # call-seq: - # RubyVM::AbstractSyntaxTree.parse_file(pathname) -> RubyVM::AbstractSyntaxTree::Node + # RubyVM::AbstractSyntaxTree.parse_file(pathname, keep_script_lines: RubyVM.keep_script_lines, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node # # Reads the file from _pathname_, then parses it like ::parse, # returning the root node of the abstract syntax tree. @@ -44,13 +70,15 @@ module RubyVM::AbstractSyntaxTree # # RubyVM::AbstractSyntaxTree.parse_file("my-app/app.rb") # # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-31:3> - def self.parse_file pathname, keep_script_lines: false - Primitive.ast_s_parse_file pathname, keep_script_lines + # + # See ::parse for explanation of keyword argument meaning and usage. + def self.parse_file pathname, keep_script_lines: RubyVM.keep_script_lines, error_tolerant: false, keep_tokens: false + Primitive.ast_s_parse_file pathname, keep_script_lines, error_tolerant, keep_tokens end # call-seq: - # RubyVM::AbstractSyntaxTree.of(proc) -> RubyVM::AbstractSyntaxTree::Node - # RubyVM::AbstractSyntaxTree.of(method) -> RubyVM::AbstractSyntaxTree::Node + # RubyVM::AbstractSyntaxTree.of(proc, keep_script_lines: RubyVM.keep_script_lines, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node + # RubyVM::AbstractSyntaxTree.of(method, keep_script_lines: RubyVM.keep_script_lines, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node # # Returns AST nodes of the given _proc_ or _method_. # @@ -63,8 +91,25 @@ module RubyVM::AbstractSyntaxTree # # RubyVM::AbstractSyntaxTree.of(method(:hello)) # # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-3:3> - def self.of body, keep_script_lines: false - Primitive.ast_s_of body, keep_script_lines + # + # See ::parse for explanation of keyword argument meaning and usage. + def self.of body, keep_script_lines: RubyVM.keep_script_lines, error_tolerant: false, keep_tokens: false + Primitive.ast_s_of body, keep_script_lines, error_tolerant, keep_tokens + end + + # call-seq: + # RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(backtrace_location) -> integer + # + # Returns the node id for the given backtrace location. + # + # begin + # raise + # rescue => e + # loc = e.backtrace_locations.first + # RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(loc) + # end # => 0 + def self.node_id_for_backtrace_location backtrace_location + Primitive.node_id_for_backtrace_location backtrace_location end # RubyVM::AbstractSyntaxTree::Node instances are created by parse methods in @@ -122,6 +167,47 @@ module RubyVM::AbstractSyntaxTree end # call-seq: + # node.tokens -> array + # + # Returns tokens corresponding to the location of the node. + # Returns +nil+ if +keep_tokens+ is not enabled when #parse method is called. + # + # root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2", keep_tokens: true) + # root.tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...] + # root.tokens.map{_1[2]}.join # => "x = 1 + 2" + # + # Token is an array of: + # + # - id + # - token type + # - source code text + # - location [ first_lineno, first_column, last_lineno, last_column ] + def tokens + return nil unless all_tokens + + all_tokens.each_with_object([]) do |token, a| + loc = token.last + if ([first_lineno, first_column] <=> [loc[0], loc[1]]) <= 0 && + ([last_lineno, last_column] <=> [loc[2], loc[3]]) >= 0 + a << token + end + end + end + + # call-seq: + # node.all_tokens -> array + # + # Returns all tokens for the input script regardless the receiver node. + # Returns +nil+ if +keep_tokens+ is not enabled when #parse method is called. + # + # root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2", keep_tokens: true) + # root.all_tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...] + # root.children[-1].all_tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...] + def all_tokens + Primitive.ast_node_all_tokens + end + + # call-seq: # node.children -> array # # Returns AST nodes under this one. Each kind of node @@ -179,8 +265,8 @@ module RubyVM::AbstractSyntaxTree lines = script_lines if lines lines = lines[first_lineno - 1 .. last_lineno - 1] - lines[-1] = lines[-1][0...last_column] - lines[0] = lines[0][first_column..-1] + lines[-1] = lines[-1].byteslice(0...last_column) + lines[0] = lines[0].byteslice(first_column..-1) lines.join else nil |