Skip to content

Commit

Permalink
Add MacroIf#is_unless? AST node method (#15304)
Browse files Browse the repository at this point in the history
  • Loading branch information
Blacksmoke16 authored Dec 25, 2024
1 parent 4772066 commit 9a218a0
Show file tree
Hide file tree
Showing 6 changed files with 24 additions and 6 deletions.
5 changes: 5 additions & 0 deletions spec/compiler/macro/macro_methods_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2726,6 +2726,11 @@ module Crystal
it "executes else" do
assert_macro %({{x.else}}), "\"foo\"", {x: MacroIf.new(BoolLiteral.new(true), StringLiteral.new("test"), StringLiteral.new("foo"))}
end

it "executes is_unless?" do
assert_macro %({{x.is_unless?}}), "true", {x: MacroIf.new(BoolLiteral.new(true), StringLiteral.new("test"), StringLiteral.new("foo"), is_unless: true)}
assert_macro %({{x.is_unless?}}), "false", {x: MacroIf.new(BoolLiteral.new(false), StringLiteral.new("test"), StringLiteral.new("foo"), is_unless: false)}
end
end

describe "macro for methods" do
Expand Down
2 changes: 1 addition & 1 deletion spec/compiler/parser/parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1156,7 +1156,7 @@ module Crystal
it_parses "macro foo;bar{% if x %}body{% else %}body2{%end%}baz;end", Macro.new("foo", [] of Arg, Expressions.from(["bar".macro_literal, MacroIf.new("x".var, "body".macro_literal, "body2".macro_literal), "baz;".macro_literal] of ASTNode))
it_parses "macro foo;bar{% if x %}body{% elsif y %}body2{%end%}baz;end", Macro.new("foo", [] of Arg, Expressions.from(["bar".macro_literal, MacroIf.new("x".var, "body".macro_literal, MacroIf.new("y".var, "body2".macro_literal)), "baz;".macro_literal] of ASTNode))
it_parses "macro foo;bar{% if x %}body{% elsif y %}body2{% else %}body3{%end%}baz;end", Macro.new("foo", [] of Arg, Expressions.from(["bar".macro_literal, MacroIf.new("x".var, "body".macro_literal, MacroIf.new("y".var, "body2".macro_literal, "body3".macro_literal)), "baz;".macro_literal] of ASTNode))
it_parses "macro foo;bar{% unless x %}body{% end %}baz;end", Macro.new("foo", [] of Arg, Expressions.from(["bar".macro_literal, MacroIf.new("x".var, Nop.new, "body".macro_literal), "baz;".macro_literal] of ASTNode))
it_parses "macro foo;bar{% unless x %}body{% end %}baz;end", Macro.new("foo", [] of Arg, Expressions.from(["bar".macro_literal, MacroIf.new("x".var, Nop.new, "body".macro_literal, is_unless: true), "baz;".macro_literal] of ASTNode))

it_parses "macro foo;bar{% for x in y %}\\ \n body{% end %}baz;end", Macro.new("foo", [] of Arg, Expressions.from(["bar".macro_literal, MacroFor.new(["x".var], "y".var, "body".macro_literal), "baz;".macro_literal] of ASTNode))
it_parses "macro foo;bar{% for x in y %}\\ \n body{% end %}\\ baz;end", Macro.new("foo", [] of Arg, Expressions.from(["bar".macro_literal, MacroFor.new(["x".var], "y".var, "body".macro_literal), "baz;".macro_literal] of ASTNode))
Expand Down
12 changes: 11 additions & 1 deletion src/compiler/crystal/macros.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2361,14 +2361,20 @@ module Crystal::Macros
end
end

# An `if` inside a macro, e.g.
# An `if`/`unless` inside a macro, e.g.
#
# ```
# {% if cond %}
# puts "Then"
# {% else %}
# puts "Else"
# {% end %}
#
# {% unless cond %}
# puts "Then"
# {% else %}
# puts "Else"
# {% end %}
# ```
class MacroIf < ASTNode
# The condition of the `if` clause.
Expand All @@ -2382,6 +2388,10 @@ module Crystal::Macros
# The `else` branch of the `if`.
def else : ASTNode
end

# Returns `true` if this node represents an `unless` conditional, otherwise returns `false`.
def is_unless? : BoolLiteral
end
end

# A `for` loop inside a macro, e.g.
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/crystal/macros/methods.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1541,6 +1541,8 @@ module Crystal
interpret_check_args { @then }
when "else"
interpret_check_args { @else }
when "is_unless?"
interpret_check_args { BoolLiteral.new @is_unless }
else
super
end
Expand Down
7 changes: 4 additions & 3 deletions src/compiler/crystal/syntax/ast.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2238,8 +2238,9 @@ module Crystal
property cond : ASTNode
property then : ASTNode
property else : ASTNode
property? is_unless : Bool

def initialize(@cond, a_then = nil, a_else = nil)
def initialize(@cond, a_then = nil, a_else = nil, @is_unless : Bool = false)
@then = Expressions.from a_then
@else = Expressions.from a_else
end
Expand All @@ -2251,10 +2252,10 @@ module Crystal
end

def clone_without_location
MacroIf.new(@cond.clone, @then.clone, @else.clone)
MacroIf.new(@cond.clone, @then.clone, @else.clone, @is_unless)
end

def_equals_and_hash @cond, @then, @else
def_equals_and_hash @cond, @then, @else, @is_unless
end

# for inside a macro:
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/crystal/syntax/parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3505,7 +3505,7 @@ module Crystal
end

a_then, a_else = a_else, a_then if is_unless
MacroIf.new(cond, a_then, a_else).at_end(token_end_location)
MacroIf.new(cond, a_then, a_else, is_unless: is_unless).at_end(token_end_location)
end

def parse_expression_inside_macro
Expand Down

0 comments on commit 9a218a0

Please sign in to comment.