diff --git a/CHANGELOG.md b/CHANGELOG.md index 960bb0e9..12e7999f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a - Fixed the AST output by adding blocks to `Command` and `CommandCall` nodes in the `FieldVisitor`. - Fixed the location of lambda local variables (e.g., `->(; a) {}`). +- The `Elsif` node has been folded into `IfNode`. A `keyword` field has been added to `IfNode` specifying if the node represents an `if` or `elsif`. ## [6.0.1] - 2023-02-26 diff --git a/lib/syntax_tree/dsl.rb b/lib/syntax_tree/dsl.rb index 4506aa04..54845cd2 100644 --- a/lib/syntax_tree/dsl.rb +++ b/lib/syntax_tree/dsl.rb @@ -347,16 +347,6 @@ def Else(keyword, statements) ) end - # Create a new Elsif node. - def Elsif(predicate, statements, consequent) - Elsif.new( - predicate: predicate, - statements: statements, - consequent: consequent, - location: Location.default - ) - end - # Create a new EmbDoc node. def EmbDoc(value) EmbDoc.new(value: value, location: Location.default) @@ -478,8 +468,9 @@ def Ident(value) end # Create a new IfNode node. - def IfNode(predicate, statements, consequent) + def IfNode(keyword, predicate, statements, consequent) IfNode.new( + keyword: keyword, predicate: predicate, statements: statements, consequent: consequent, @@ -658,29 +649,11 @@ def Program(statements) Program.new(statements: statements, location: Location.default) end - # Create a new QSymbols node. - def QSymbols(beginning, elements) - QSymbols.new( - beginning: beginning, - elements: elements, - location: Location.default - ) - end - # Create a new QSymbolsBeg node. def QSymbolsBeg(value) QSymbolsBeg.new(value: value, location: Location.default) end - # Create a new QWords node. - def QWords(beginning, elements) - QWords.new( - beginning: beginning, - elements: elements, - location: Location.default - ) - end - # Create a new QWordsBeg node. def QWordsBeg(value) QWordsBeg.new(value: value, location: Location.default) @@ -839,15 +812,6 @@ def SymbolLiteral(value) SymbolLiteral.new(value: value, location: Location.default) end - # Create a new Symbols node. - def Symbols(beginning, elements) - Symbols.new( - beginning: beginning, - elements: elements, - location: Location.default - ) - end - # Create a new SymbolsBeg node. def SymbolsBeg(value) SymbolsBeg.new(value: value, location: Location.default) @@ -979,15 +943,6 @@ def Word(parts) Word.new(parts: parts, location: Location.default) end - # Create a new Words node. - def Words(beginning, elements) - Words.new( - beginning: beginning, - elements: elements, - location: Location.default - ) - end - # Create a new WordsBeg node. def WordsBeg(value) WordsBeg.new(value: value, location: Location.default) diff --git a/lib/syntax_tree/field_visitor.rb b/lib/syntax_tree/field_visitor.rb index f5607c67..6138573b 100644 --- a/lib/syntax_tree/field_visitor.rb +++ b/lib/syntax_tree/field_visitor.rb @@ -353,15 +353,6 @@ def visit_else(node) end end - def visit_elsif(node) - node(node, "elsif") do - field("predicate", node.predicate) - field("statements", node.statements) - field("consequent", node.consequent) if node.consequent - comments(node) - end - end - def visit_embdoc(node) node(node, "embdoc") { field("value", node.value) } end @@ -663,24 +654,10 @@ def visit_program(node) end end - def visit_qsymbols(node) - node(node, "qsymbols") do - list("elements", node.elements) - comments(node) - end - end - def visit_qsymbols_beg(node) node(node, "qsymbols_beg") { field("value", node.value) } end - def visit_qwords(node) - node(node, "qwords") do - list("elements", node.elements) - comments(node) - end - end - def visit_qwords_beg(node) node(node, "qwords_beg") { field("value", node.value) } end @@ -856,13 +833,6 @@ def visit_symbol_literal(node) end end - def visit_symbols(node) - node(node, "symbols") do - list("elements", node.elements) - comments(node) - end - end - def visit_symbols_beg(node) node(node, "symbols_beg") { field("value", node.value) } end @@ -982,13 +952,6 @@ def visit_word(node) end end - def visit_words(node) - node(node, "words") do - list("elements", node.elements) - comments(node) - end - end - def visit_words_beg(node) node(node, "words_beg") { field("value", node.value) } end diff --git a/lib/syntax_tree/mutation_visitor.rb b/lib/syntax_tree/mutation_visitor.rb index 0b4b9357..5a1295cb 100644 --- a/lib/syntax_tree/mutation_visitor.rb +++ b/lib/syntax_tree/mutation_visitor.rb @@ -322,14 +322,6 @@ def visit_else(node) ) end - # Visit a Elsif node. - def visit_elsif(node) - node.copy( - statements: visit(node.statements), - consequent: visit(node.consequent) - ) - end - # Visit a EmbDoc node. def visit_embdoc(node) node.copy @@ -599,27 +591,11 @@ def visit_program(node) node.copy(statements: visit(node.statements)) end - # Visit a QSymbols node. - def visit_qsymbols(node) - node.copy( - beginning: visit(node.beginning), - elements: visit_all(node.elements) - ) - end - # Visit a QSymbolsBeg node. def visit_qsymbols_beg(node) node.copy end - # Visit a QWords node. - def visit_qwords(node) - node.copy( - beginning: visit(node.beginning), - elements: visit_all(node.elements) - ) - end - # Visit a QWordsBeg node. def visit_qwords_beg(node) node.copy @@ -760,14 +736,6 @@ def visit_symbol_literal(node) node.copy(value: visit(node.value)) end - # Visit a Symbols node. - def visit_symbols(node) - node.copy( - beginning: visit(node.beginning), - elements: visit_all(node.elements) - ) - end - # Visit a SymbolsBeg node. def visit_symbols_beg(node) node.copy @@ -887,14 +855,6 @@ def visit_word(node) node.copy(parts: visit_all(node.parts)) end - # Visit a Words node. - def visit_words(node) - node.copy( - beginning: visit(node.beginning), - elements: visit_all(node.elements) - ) - end - # Visit a WordsBeg node. def visit_words_beg(node) node.copy diff --git a/lib/syntax_tree/node.rb b/lib/syntax_tree/node.rb index 3f013b31..d7def6c8 100644 --- a/lib/syntax_tree/node.rb +++ b/lib/syntax_tree/node.rb @@ -1128,10 +1128,12 @@ def initialize(lbracket) def format(q) q.group do q.text("[") - q.indent do - lbracket.comments.each do |comment| - q.breakable_force - comment.format(q) + if lbracket.is_a?(LBracket) + q.indent do + lbracket.comments.each do |comment| + q.breakable_force + comment.format(q) + end end end q.breakable_force @@ -1211,18 +1213,40 @@ def format(q) end q.group do - q.format(lbracket) + case lbracket + when QSymbolsBeg, QWordsBeg, WordsBeg, SymbolsBeg + q.text(lbracket.value) + else + q.format(lbracket) + end if contents q.indent do q.breakable_empty - q.format(contents) - q.if_break { q.text(",") } if q.trailing_comma? + case lbracket + when QSymbolsBeg, QWordsBeg + q.seplist(contents.parts, BREAKABLE_SPACE_SEPARATOR) do |part| + q.text(part.value) + end + when WordsBeg, SymbolsBeg + q.seplist(contents.parts, BREAKABLE_SPACE_SEPARATOR) do |part| + q.format(part) + end + else + q.format(contents) + q.if_break { q.text(",") } if q.trailing_comma? + end end end q.breakable_empty - q.text("]") + + case lbracket + when QSymbolsBeg, QWordsBeg, WordsBeg, SymbolsBeg + q.text(lbracket.value[-1] == "{" ? "}" : "]") + else + q.text("]") + end end end @@ -1396,8 +1420,7 @@ def ===(other) module AssignFormatting def self.skip_indent?(value) case value - when ArrayLiteral, HashLiteral, Heredoc, Lambda, QSymbols, QWords, - Symbols, Words + when ArrayLiteral, HashLiteral, Heredoc, Lambda true when CallNode skip_indent?(value.receiver) @@ -4853,95 +4876,6 @@ def ===(other) end end - # Elsif represents another clause in an +if+ or +unless+ chain. - # - # if variable - # elsif other_variable - # end - # - class Elsif < Node - # [Node] the expression to be checked - attr_reader :predicate - - # [Statements] the expressions to be executed - attr_reader :statements - - # [nil | Elsif | Else] the next clause in the chain - attr_reader :consequent - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(predicate:, statements:, consequent:, location:) - @predicate = predicate - @statements = statements - @consequent = consequent - @location = location - @comments = [] - end - - def accept(visitor) - visitor.visit_elsif(self) - end - - def child_nodes - [predicate, statements, consequent] - end - - def copy(predicate: nil, statements: nil, consequent: nil, location: nil) - node = - Elsif.new( - predicate: predicate || self.predicate, - statements: statements || self.statements, - consequent: consequent || self.consequent, - location: location || self.location - ) - - node.comments.concat(comments.map(&:copy)) - node - end - - alias deconstruct child_nodes - - def deconstruct_keys(_keys) - { - predicate: predicate, - statements: statements, - consequent: consequent, - location: location, - comments: comments - } - end - - def format(q) - q.group do - q.group do - q.text("elsif ") - q.nest("elsif".length - 1) { q.format(predicate) } - end - - unless statements.empty? - q.indent do - q.breakable_force - q.format(statements) - end - end - - if consequent - q.group do - q.breakable_force - q.format(consequent) - end - end - end - end - - def ===(other) - other.is_a?(Elsif) && predicate === other.predicate && - statements === other.statements && consequent === other.consequent - end - end - # EmbDoc represents a multi-line comment. # # =begin @@ -6460,25 +6394,29 @@ def contains_conditional? end end - # If represents the first clause in an +if+ chain. + # If an +if+ or +elsif+ clause in an +if+ chain. # # if predicate # end # class IfNode < Node + # [Kw] the opening keyword of the conditional statement + attr_reader :keyword + # [Node] the expression to be checked attr_reader :predicate # [Statements] the expressions to be executed attr_reader :statements - # [nil | Elsif | Else] the next clause in the chain + # [nil | IfNode | Else] the next clause in the chain attr_reader :consequent # [Array[ Comment | EmbDoc ]] the comments attached to this node attr_reader :comments - def initialize(predicate:, statements:, consequent:, location:) + def initialize(keyword:, predicate:, statements:, consequent:, location:) + @keyword = keyword @predicate = predicate @statements = statements @consequent = consequent @@ -6494,9 +6432,16 @@ def child_nodes [predicate, statements, consequent] end - def copy(predicate: nil, statements: nil, consequent: nil, location: nil) + def copy( + keyword: nil, + predicate: nil, + statements: nil, + consequent: nil, + location: nil + ) node = IfNode.new( + keyword: keyword || self.keyword, predicate: predicate || self.predicate, statements: statements || self.statements, consequent: consequent || self.consequent, @@ -6515,17 +6460,42 @@ def deconstruct_keys(_keys) statements: statements, consequent: consequent, location: location, + keyword: keyword, comments: comments } end def format(q) - ConditionalFormatter.new("if", self).format(q) + if keyword.value == "elsif" + q.group do + q.group do + q.text("elsif ") + q.nest("elsif".length - 1) { q.format(predicate) } + end + + unless statements.empty? + q.indent do + q.breakable_force + q.format(statements) + end + end + + if consequent + q.group do + q.breakable_force + q.format(consequent) + end + end + end + else + ConditionalFormatter.new(keyword.value, self).format(q) + end end def ===(other) other.is_a?(IfNode) && predicate === other.predicate && - statements === other.statements && consequent === other.consequent + statements === other.statements && consequent === other.consequent && + keyword === other.keyword end # Checks if the node was originally found in the modifier form. @@ -8666,86 +8636,6 @@ def ===(other) end end - # QSymbols represents a symbol literal array without interpolation. - # - # %i[one two three] - # - class QSymbols < Node - # [QSymbolsBeg] the token that opens this array literal - attr_reader :beginning - - # [Array[ TStringContent ]] the elements of the array - attr_reader :elements - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(beginning:, elements:, location:) - @beginning = beginning - @elements = elements - @location = location - @comments = [] - end - - def accept(visitor) - visitor.visit_qsymbols(self) - end - - def child_nodes - [] - end - - def copy(beginning: nil, elements: nil, location: nil) - node = - QSymbols.new( - beginning: beginning || self.beginning, - elements: elements || self.elements, - location: location || self.location - ) - - node.comments.concat(comments.map(&:copy)) - node - end - - alias deconstruct child_nodes - - def deconstruct_keys(_keys) - { - beginning: beginning, - elements: elements, - location: location, - comments: comments - } - end - - def format(q) - opening, closing = "%i[", "]" - - if elements.any? { |element| element.match?(/[\[\]]/) } - opening = beginning.value - closing = Quotes.matching(opening[2]) - end - - q.text(opening) - q.group do - q.indent do - q.breakable_empty - q.seplist( - elements, - ArrayLiteral::BREAKABLE_SPACE_SEPARATOR - ) { |element| q.format(element) } - end - q.breakable_empty - end - q.text(closing) - end - - def ===(other) - other.is_a?(QSymbols) && beginning === other.beginning && - ArrayMatch.call(elements, other.elements) - end - end - # QSymbolsBeg represents the beginning of a symbol literal array. # # %i[one two three] @@ -8788,82 +8678,6 @@ def ===(other) end end - # QWords represents a string literal array without interpolation. - # - # %w[one two three] - # - class QWords < Node - # [QWordsBeg] the token that opens this array literal - attr_reader :beginning - - # [Array[ TStringContent ]] the elements of the array - attr_reader :elements - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(beginning:, elements:, location:) - @beginning = beginning - @elements = elements - @location = location - @comments = [] - end - - def accept(visitor) - visitor.visit_qwords(self) - end - - def child_nodes - [] - end - - def copy(beginning: nil, elements: nil, location: nil) - QWords.new( - beginning: beginning || self.beginning, - elements: elements || self.elements, - location: location || self.location - ) - end - - alias deconstruct child_nodes - - def deconstruct_keys(_keys) - { - beginning: beginning, - elements: elements, - location: location, - comments: comments - } - end - - def format(q) - opening, closing = "%w[", "]" - - if elements.any? { |element| element.match?(/[\[\]]/) } - opening = beginning.value - closing = Quotes.matching(opening[2]) - end - - q.text(opening) - q.group do - q.indent do - q.breakable_empty - q.seplist( - elements, - ArrayLiteral::BREAKABLE_SPACE_SEPARATOR - ) { |element| q.format(element) } - end - q.breakable_empty - end - q.text(closing) - end - - def ===(other) - other.is_a?(QWords) && beginning === other.beginning && - ArrayMatch.call(elements, other.elements) - end - end - # QWordsBeg represents the beginning of a string literal array. # # %w[one two three] @@ -10630,82 +10444,6 @@ def ===(other) end end - # Symbols represents a symbol array literal with interpolation. - # - # %I[one two three] - # - class Symbols < Node - # [SymbolsBeg] the token that opens this array literal - attr_reader :beginning - - # [Array[ Word ]] the words in the symbol array literal - attr_reader :elements - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(beginning:, elements:, location:) - @beginning = beginning - @elements = elements - @location = location - @comments = [] - end - - def accept(visitor) - visitor.visit_symbols(self) - end - - def child_nodes - [] - end - - def copy(beginning: nil, elements: nil, location: nil) - Symbols.new( - beginning: beginning || self.beginning, - elements: elements || self.elements, - location: location || self.location - ) - end - - alias deconstruct child_nodes - - def deconstruct_keys(_keys) - { - beginning: beginning, - elements: elements, - location: location, - comments: comments - } - end - - def format(q) - opening, closing = "%I[", "]" - - if elements.any? { |element| element.match?(/[\[\]]/) } - opening = beginning.value - closing = Quotes.matching(opening[2]) - end - - q.text(opening) - q.group do - q.indent do - q.breakable_empty - q.seplist( - elements, - ArrayLiteral::BREAKABLE_SPACE_SEPARATOR - ) { |element| q.format(element) } - end - q.breakable_empty - end - q.text(closing) - end - - def ===(other) - other.is_a?(Symbols) && beginning === other.beginning && - ArrayMatch.call(elements, other.elements) - end - end - # SymbolsBeg represents the start of a symbol array literal with # interpolation. # @@ -11328,7 +11066,7 @@ class UnlessNode < Node # [Statements] the expressions to be executed attr_reader :statements - # [nil | Elsif | Else] the next clause in the chain + # [nil | IfNode | Else] the next clause in the chain attr_reader :consequent # [Array[ Comment | EmbDoc ]] the comments attached to this node @@ -12015,7 +11753,8 @@ def modifier? # # %W[a#{b}c xyz] # - # In the example above, there would be two Word nodes within a parent Words + # In the example above, there would be two Word nodes within a parent + # ArrayLiteral # node. class Word < Node # [Array[ StringEmbExpr | StringDVar | TStringContent ]] the parts of the @@ -12069,82 +11808,6 @@ def ===(other) end end - # Words represents a string literal array with interpolation. - # - # %W[one two three] - # - class Words < Node - # [WordsBeg] the token that opens this array literal - attr_reader :beginning - - # [Array[ Word ]] the elements of this array - attr_reader :elements - - # [Array[ Comment | EmbDoc ]] the comments attached to this node - attr_reader :comments - - def initialize(beginning:, elements:, location:) - @beginning = beginning - @elements = elements - @location = location - @comments = [] - end - - def accept(visitor) - visitor.visit_words(self) - end - - def child_nodes - [] - end - - def copy(beginning: nil, elements: nil, location: nil) - Words.new( - beginning: beginning || self.beginning, - elements: elements || self.elements, - location: location || self.location - ) - end - - alias deconstruct child_nodes - - def deconstruct_keys(_keys) - { - beginning: beginning, - elements: elements, - location: location, - comments: comments - } - end - - def format(q) - opening, closing = "%W[", "]" - - if elements.any? { |element| element.match?(/[\[\]]/) } - opening = beginning.value - closing = Quotes.matching(opening[2]) - end - - q.text(opening) - q.group do - q.indent do - q.breakable_empty - q.seplist( - elements, - ArrayLiteral::BREAKABLE_SPACE_SEPARATOR - ) { |element| q.format(element) } - end - q.breakable_empty - end - q.text(closing) - end - - def ===(other) - other.is_a?(Words) && beginning === other.beginning && - ArrayMatch.call(elements, other.elements) - end - end - # WordsBeg represents the beginning of a string literal array with # interpolation. # diff --git a/lib/syntax_tree/parser.rb b/lib/syntax_tree/parser.rb index 825cd90e..9b0bb46e 100644 --- a/lib/syntax_tree/parser.rb +++ b/lib/syntax_tree/parser.rb @@ -617,8 +617,7 @@ def on_args_new end # :call-seq: - # on_array: ((nil | Args) contents) -> - # ArrayLiteral | QSymbols | QWords | Symbols | Words + # on_array: ((nil | Args | ArrayLilteral) contents) -> ArrayLiteral def on_array(contents) if !contents || contents.is_a?(Args) lbracket = consume_token(LBracket) @@ -630,12 +629,10 @@ def on_array(contents) location: lbracket.location.to(rbracket.location) ) else - tstring_end = consume_tstring_end(contents.beginning.location) - - contents.class.new( - beginning: contents.beginning, - elements: contents.elements, - location: contents.location.to(tstring_end.location) + ArrayLiteral.new( + lbracket: contents.lbracket, + contents: contents.contents, + location: contents.location ) end end @@ -1575,7 +1572,8 @@ def on_elsif(predicate, statements, consequent) ending.location.start_column ) - Elsif.new( + IfNode.new( + keyword: beginning, predicate: predicate, statements: statements, consequent: consequent, @@ -2062,6 +2060,7 @@ def on_if(predicate, statements, consequent) ) IfNode.new( + keyword: beginning, predicate: predicate, statements: statements, consequent: consequent, @@ -2083,9 +2082,10 @@ def on_ifop(predicate, truthy, falsy) # :call-seq: # on_if_mod: (untyped predicate, untyped statement) -> IfNode def on_if_mod(predicate, statement) - consume_keyword(:if) + beginning = consume_keyword(:if) IfNode.new( + keyword: beginning, predicate: predicate, statements: Statements.new(body: [statement], location: statement.location), @@ -2996,11 +2996,14 @@ def nearest_nodes(node, comment) end # :call-seq: - # on_qsymbols_add: (QSymbols qsymbols, TStringContent element) -> QSymbols + # on_qsymbols_add: (ArrayLiteral qsymbols, TStringContent element) -> + # ArrayLiteral def on_qsymbols_add(qsymbols, element) - QSymbols.new( - beginning: qsymbols.beginning, - elements: qsymbols.elements << element, + qsymbols.contents.parts << element + + ArrayLiteral.new( + lbracket: qsymbols.lbracket, + contents: qsymbols.contents, location: qsymbols.location.to(element.location) ) end @@ -3025,23 +3028,26 @@ def on_qsymbols_beg(value) end # :call-seq: - # on_qsymbols_new: () -> QSymbols + # on_qsymbols_new: () -> ArrayLiteral def on_qsymbols_new beginning = consume_token(QSymbolsBeg) - QSymbols.new( - beginning: beginning, - elements: [], + ArrayLiteral.new( + lbracket: beginning, + contents: Args.new(parts: [], location: beginning.location), location: beginning.location ) end # :call-seq: - # on_qwords_add: (QWords qwords, TStringContent element) -> QWords + # on_qwords_add: (ArrayLiteral qwords, TStringContent element) -> + # ArrayLiteral def on_qwords_add(qwords, element) - QWords.new( - beginning: qwords.beginning, - elements: qwords.elements << element, + qwords.contents.parts << element + + ArrayLiteral.new( + lbracket: qwords.lbracket, + contents: qwords.contents, location: qwords.location.to(element.location) ) end @@ -3066,13 +3072,13 @@ def on_qwords_beg(value) end # :call-seq: - # on_qwords_new: () -> QWords + # on_qwords_new: () -> ArrayLiteral def on_qwords_new beginning = consume_token(QWordsBeg) - QWords.new( - beginning: beginning, - elements: [], + ArrayLiteral.new( + lbracket: beginning, + contents: Args.new(parts: [], location: beginning.location), location: beginning.location ) end @@ -3602,11 +3608,13 @@ def on_symbol_literal(value) end # :call-seq: - # on_symbols_add: (Symbols symbols, Word word) -> Symbols + # on_symbols_add: (ArrayLiteral symbols, Word word) -> ArrayLiteral def on_symbols_add(symbols, word) - Symbols.new( - beginning: symbols.beginning, - elements: symbols.elements << word, + symbols.contents.parts << word + + ArrayLiteral.new( + lbracket: symbols.lbracket, + contents: symbols.contents, location: symbols.location.to(word.location) ) end @@ -3631,13 +3639,13 @@ def on_symbols_beg(value) end # :call-seq: - # on_symbols_new: () -> Symbols + # on_symbols_new: () -> ArrayLiteral def on_symbols_new beginning = consume_token(SymbolsBeg) - Symbols.new( - beginning: beginning, - elements: [], + ArrayLiteral.new( + lbracket: beginning, + contents: Args.new(parts: [], location: beginning.location), location: beginning.location ) end @@ -4054,11 +4062,13 @@ def on_word_new end # :call-seq: - # on_words_add: (Words words, Word word) -> Words + # on_words_add: (ArrayLiteral words, Word word) -> ArrayLiteral def on_words_add(words, word) - Words.new( - beginning: words.beginning, - elements: words.elements << word, + words.contents.parts << word + + ArrayLiteral.new( + lbracket: words.lbracket, + contents: words.contents, location: words.location.to(word.location) ) end @@ -4083,13 +4093,13 @@ def on_words_beg(value) end # :call-seq: - # on_words_new: () -> Words + # on_words_new: () -> ArrayLiteral def on_words_new beginning = consume_token(WordsBeg) - Words.new( - beginning: beginning, - elements: [], + ArrayLiteral.new( + lbracket: beginning, + contents: Args.new(parts: [], location: beginning.location), location: beginning.location ) end diff --git a/lib/syntax_tree/translation/parser.rb b/lib/syntax_tree/translation/parser.rb index 8be4fc79..509da84f 100644 --- a/lib/syntax_tree/translation/parser.rb +++ b/lib/syntax_tree/translation/parser.rb @@ -254,7 +254,33 @@ def visit_args_forward(node) def visit_array(node) s( :array, - node.contents ? visit_all(node.contents.parts) : [], + if node.contents.nil? + [] + elsif node.lbracket.is_a?(QSymbolsBeg) + visit_all( + node.contents.parts.map do |part| + SymbolLiteral.new(value: part, location: part.location) + end + ) + elsif node.lbracket.is_a?(SymbolsBeg) + visit_all( + node.contents.parts.map do |element| + part = element.parts.first + + if element.parts.length == 1 && part.is_a?(TStringContent) + SymbolLiteral.new(value: part, location: part.location) + else + DynaSymbol.new( + parts: element.parts, + quote: nil, + location: element.location + ) + end + end + ) + else + visit_all(node.contents.parts) + end, if node.lbracket.nil? smap_collection_bare(srange_node(node)) else @@ -1037,50 +1063,6 @@ def visit_else(node) end end - # Visit an Elsif node. - def visit_elsif(node) - begin_start = node.predicate.end_char - begin_end = - if node.statements.empty? - node.statements.end_char - else - node.statements.body.first.start_char - end - - begin_token = - if buffer.source[begin_start...begin_end].include?("then") - srange_find(begin_start, begin_end, "then") - elsif buffer.source[begin_start...begin_end].include?(";") - srange_find(begin_start, begin_end, ";") - end - - else_token = - case node.consequent - when Elsif - srange_length(node.consequent.start_char, 5) - when Else - srange_length(node.consequent.start_char, 4) - end - - expression = srange(node.start_char, node.statements.end_char - 1) - - s( - :if, - [ - visit(node.predicate), - visit(node.statements), - visit(node.consequent) - ], - smap_condition( - srange_length(node.start_char, 5), - begin_token, - else_token, - nil, - expression - ) - ) - end - # Visit an ENDBlock node. def visit_END(node) s( @@ -1361,7 +1343,7 @@ def visit_if(node) else_token = case node.consequent - when Elsif + when IfNode srange_length(node.consequent.start_char, 5) when Else srange_length(node.consequent.start_char, 4) @@ -1942,33 +1924,6 @@ def visit_program(node) visit(node.statements) end - # Visit a QSymbols node. - def visit_qsymbols(node) - parts = - node.elements.map do |element| - SymbolLiteral.new(value: element, location: element.location) - end - - visit_array( - ArrayLiteral.new( - lbracket: node.beginning, - contents: Args.new(parts: parts, location: node.location), - location: node.location - ) - ) - end - - # Visit a QWords node. - def visit_qwords(node) - visit_array( - ArrayLiteral.new( - lbracket: node.beginning, - contents: Args.new(parts: node.elements, location: node.location), - location: node.location - ) - ) - end - # Visit a RangeNode node. def visit_range(node) s( @@ -2329,32 +2284,6 @@ def visit_symbol_literal(node) ) end - # Visit a Symbols node. - def visit_symbols(node) - parts = - node.elements.map do |element| - part = element.parts.first - - if element.parts.length == 1 && part.is_a?(TStringContent) - SymbolLiteral.new(value: part, location: part.location) - else - DynaSymbol.new( - parts: element.parts, - quote: nil, - location: element.location - ) - end - end - - visit_array( - ArrayLiteral.new( - lbracket: node.beginning, - contents: Args.new(parts: parts, location: node.location), - location: node.location - ) - ) - end - # Visit a TopConstField node. def visit_top_const_field(node) s( @@ -2661,17 +2590,6 @@ def visit_word(node) ) end - # Visit a Words node. - def visit_words(node) - visit_array( - ArrayLiteral.new( - lbracket: node.beginning, - contents: Args.new(parts: node.elements, location: node.location), - location: node.location - ) - ) - end - # Visit an XStringLiteral node. def visit_xstring_literal(node) s( diff --git a/lib/syntax_tree/visitor.rb b/lib/syntax_tree/visitor.rb index eb57acd2..4538524d 100644 --- a/lib/syntax_tree/visitor.rb +++ b/lib/syntax_tree/visitor.rb @@ -131,9 +131,6 @@ class Visitor < BasicVisitor # Visit an Else node. alias visit_else visit_child_nodes - # Visit an Elsif node. - alias visit_elsif visit_child_nodes - # Visit an EmbDoc node. alias visit_embdoc visit_child_nodes @@ -278,15 +275,9 @@ class Visitor < BasicVisitor # Visit a Program node. alias visit_program visit_child_nodes - # Visit a QSymbols node. - alias visit_qsymbols visit_child_nodes - # Visit a QSymbolsBeg node. alias visit_qsymbols_beg visit_child_nodes - # Visit a QWords node. - alias visit_qwords visit_child_nodes - # Visit a QWordsBeg node. alias visit_qwords_beg visit_child_nodes @@ -374,9 +365,6 @@ class Visitor < BasicVisitor # Visit a SymbolLiteral node. alias visit_symbol_literal visit_child_nodes - # Visit a Symbols node. - alias visit_symbols visit_child_nodes - # Visit a SymbolsBeg node. alias visit_symbols_beg visit_child_nodes @@ -434,9 +422,6 @@ class Visitor < BasicVisitor # Visit a Word node. alias visit_word visit_child_nodes - # Visit a Words node. - alias visit_words visit_child_nodes - # Visit a WordsBeg node. alias visit_words_beg visit_child_nodes diff --git a/lib/syntax_tree/yarv/compiler.rb b/lib/syntax_tree/yarv/compiler.rb index bd20bc19..079fb2cd 100644 --- a/lib/syntax_tree/yarv/compiler.rb +++ b/lib/syntax_tree/yarv/compiler.rb @@ -126,7 +126,14 @@ def self.compile(node) visit_methods do def visit_array(node) - node.contents ? visit_all(node.contents.parts) : [] + return [] unless node.contents + + case node.lbracket + when QSymbolsBeg, SymbolsBeg + visit_all(node.contents.parts).map(&:to_sym) + else + visit_all(node.contents.parts) + end end def visit_bare_assoc_hash(node) @@ -171,14 +178,6 @@ def visit_mrhs(node) visit_all(node.parts) end - def visit_qsymbols(node) - node.elements.map { |element| visit(element).to_sym } - end - - def visit_qwords(node) - visit_all(node.elements) - end - def visit_range(node) left, right = [visit(node.left), visit(node.right)] node.operator.value === ".." ? left..right : left...right @@ -205,10 +204,6 @@ def visit_symbol_literal(node) node.value.value.to_sym end - def visit_symbols(node) - node.elements.map { |element| visit(element).to_sym } - end - def visit_tstring_content(node) node.value end @@ -237,10 +232,6 @@ def visit_word(node) raise CompilationError end end - - def visit_words(node) - visit_all(node.elements) - end end # This isn't actually a visit method, though maybe it should be. It is @@ -384,6 +375,44 @@ def visit_args(node) end def visit_array(node) + case node.lbracket + when QWordsBeg + if options.frozen_string_literal? + iseq.duparray(node.accept(RubyVisitor.new)) + else + visit(node.contents) + iseq.newarray(node.contents.parts.length) + end + return + when WordsBeg + if options.frozen_string_literal? && + (compiled = RubyVisitor.compile(node)) + iseq.duparray(compiled) + else + visit_all(node.contents.parts) + iseq.newarray(node.contents.parts.length) + end + return + when SymbolsBeg + if (compiled = RubyVisitor.compile(node)) + iseq.duparray(compiled) + else + node.contents.parts.each do |element| + if element.parts.length == 1 && + element.parts.first.is_a?(TStringContent) + iseq.putobject(element.parts.first.value.to_sym) + else + length = visit_string_parts(element) + iseq.concatstrings(length) + iseq.intern + end + end + + iseq.newarray(node.contents.parts.length) + end + return + end + if (compiled = RubyVisitor.compile(node)) iseq.duparray(compiled) elsif node.contents && node.contents.parts.length == 1 && @@ -916,17 +945,6 @@ def visit_else(node) iseq.pop unless last_statement? end - def visit_elsif(node) - visit_if( - IfNode.new( - predicate: node.predicate, - statements: node.statements, - consequent: node.consequent, - location: node.location - ) - ) - end - def visit_ensure(node) end @@ -1054,6 +1072,7 @@ def visit_if(node) def visit_if_op(node) visit_if( IfNode.new( + keyword: Kw.new(value: "if", location: Location.default), predicate: node.predicate, statements: Statements.new(body: [node.truthy], location: Location.default), @@ -1419,19 +1438,6 @@ def visit_program(node) top_iseq end - def visit_qsymbols(node) - iseq.duparray(node.accept(RubyVisitor.new)) - end - - def visit_qwords(node) - if options.frozen_string_literal? - iseq.duparray(node.accept(RubyVisitor.new)) - else - visit_all(node.elements) - iseq.newarray(node.elements.length) - end - end - def visit_range(node) if (compiled = RubyVisitor.compile(node)) iseq.putobject(compiled) @@ -1635,25 +1641,6 @@ def visit_symbol_literal(node) iseq.putobject(node.accept(RubyVisitor.new)) end - def visit_symbols(node) - if (compiled = RubyVisitor.compile(node)) - iseq.duparray(compiled) - else - node.elements.each do |element| - if element.parts.length == 1 && - element.parts.first.is_a?(TStringContent) - iseq.putobject(element.parts.first.value.to_sym) - else - length = visit_string_parts(element) - iseq.concatstrings(length) - iseq.intern - end - end - - iseq.newarray(node.elements.length) - end - end - def visit_top_const_ref(node) iseq.opt_getconstant_path(constant_names(node)) end @@ -1838,16 +1825,6 @@ def visit_word(node) end end - def visit_words(node) - if options.frozen_string_literal? && - (compiled = RubyVisitor.compile(node)) - iseq.duparray(compiled) - else - visit_all(node.elements) - iseq.newarray(node.elements.length) - end - end - def visit_xstring_literal(node) iseq.putself length = visit_string_parts(node) diff --git a/lib/syntax_tree/yarv/decompiler.rb b/lib/syntax_tree/yarv/decompiler.rb index 4ea99e3a..bf6ffb0a 100644 --- a/lib/syntax_tree/yarv/decompiler.rb +++ b/lib/syntax_tree/yarv/decompiler.rb @@ -77,7 +77,7 @@ def decompile(iseq) Next(Args([])) ] - clause << IfNode(clause.pop, Statements(body), nil) + clause << IfNode(Kw("if"), clause.pop, Statements(body), nil) when Dup clause << clause.last when DupHash diff --git a/test/node_test.rb b/test/node_test.rb index 19fbeed2..c317ee19 100644 --- a/test/node_test.rb +++ b/test/node_test.rb @@ -452,7 +452,7 @@ def test_elsif SOURCE at = location(lines: 2..4, chars: 9..30) - assert_node(Elsif, source, at: at, &:consequent) + assert_node(IfNode, source, at: at, &:consequent) end def test_embdoc @@ -774,11 +774,11 @@ def test_program end def test_qsymbols - assert_node(QSymbols, "%i[one two three]") + assert_node(ArrayLiteral, "%i[one two three]") end def test_qwords - assert_node(QWords, "%w[one two three]") + assert_node(ArrayLiteral, "%w[one two three]") end def test_rational @@ -889,7 +889,7 @@ def test_symbol_literal end def test_symbols - assert_node(Symbols, "%I[one two three]") + assert_node(ArrayLiteral, "%I[one two three]") end def test_top_const_field @@ -990,11 +990,11 @@ def test_while_mod def test_word at = location(chars: 3..7) - assert_node(Word, "%W[word]", at: at) { |node| node.elements.first } + assert_node(Word, "%W[word]", at: at) { |node| node.contents.parts.first } end def test_words - assert_node(Words, "%W[one two three]") + assert_node(ArrayLiteral, "%W[one two three]") end def test_xstring_literal