Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: c551b35cb170
Choose a base ref
...
head repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: be619da8e32b
Choose a head ref
  • 3 commits
  • 13 files changed
  • 1 contributor

Commits on Nov 17, 2016

  1. Copy the full SHA
    f2854ea View commit details
  2. Added @def inside macros that takes the value of the current method.

    …Fixes #1582
    Ary Borenszweig committed Nov 17, 2016
    Copy the full SHA
    df20bdc View commit details
  3. Fixed #3523: only use short block notation when stringifying a call i…

    …f the arguments are the ones reserved by the language (like __arg0)
    Ary Borenszweig committed Nov 17, 2016
    Copy the full SHA
    be619da View commit details
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@
* Added support for ARM (thanks @ysbaddaden)
* Added support for AArch64 (thanks @ysbaddaden)
* Added support for LLVM 3.9 (thanks @ysbaddaden)
* Added `__END_LINE__` magic constant in method default arguments: will be the last line of a call (if the call has a block, it will be the last line of that block)
* Added `@def` inside macros that takes the value of the current method
* API docs have a nicer style now (thanks @samueleaton)
* Slight improvement to debugging support (thanks @ggiraldez)
* Added iteration times to `Benchmark.ips` (thanks @RX14)
24 changes: 24 additions & 0 deletions spec/compiler/codegen/macro_spec.cr
Original file line number Diff line number Diff line change
@@ -1466,4 +1466,28 @@ describe "Code gen: macro" do
Foo.x
), inject_primitives: false).to_i.should eq(1)
end

it "expands @def in inline macro" do
run(%(
def foo
{{@def.name.stringify}}
end
foo
)).to_string.should eq("foo")
end

it "expands @def in macro" do
run(%(
macro foo
{{@def.name.stringify}}
end
def bar
foo
end
bar
)).to_string.should eq("bar")
end
end
45 changes: 45 additions & 0 deletions spec/compiler/codegen/magic_constants_spec.cr
Original file line number Diff line number Diff line change
@@ -109,4 +109,49 @@ describe "Code gen: magic constants" do
foo
), filename: "/foo/bar/baz.cr").to_string.should eq("/foo/bar")
end

it "does __END_LINE__ without block" do
run(%(
def foo(x = __END_LINE__)
x
end
foo
), inject_primitives: false).to_i.should eq(6)
end

it "does __END_LINE__ with block" do
run(%(
def foo(x = __END_LINE__)
yield
x
end
foo do
1
end
), inject_primitives: false).to_i.should eq(9)
end

it "does __END_LINE__ in macro without block" do
run(%(
macro foo(line = __END_LINE__)
{{line}}
end
foo
), inject_primitives: false).to_i.should eq(6)
end

it "does __END_LINE__ in macro with block" do
run(%(
macro foo(line = __END_LINE__)
{{line}}
end
foo do
1
end
), inject_primitives: false).to_i.should eq(8)
end
end
1 change: 1 addition & 0 deletions spec/compiler/parser/to_s_spec.cr
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ describe "ASTNode#to_s" do
expect_to_s %<%r(/)>, %(/\\//)
expect_to_s %(foo &.bar), %(foo(&.bar))
expect_to_s %(foo &.bar(1, 2, 3)), %(foo(&.bar(1, 2, 3)))
expect_to_s %(foo { |i| i.bar { i } }), "foo do |i|\n i.bar do\n i\n end\nend"
expect_to_s %(foo do |k, v|\n k.bar(1, 2, 3)\nend)
expect_to_s %(foo(3, &.*(2)))
expect_to_s %(return begin\n 1\n 2\nend)
3 changes: 3 additions & 0 deletions src/compiler/crystal/codegen/call.cr
Original file line number Diff line number Diff line change
@@ -119,9 +119,12 @@ class Crystal::CodeGenVisitor
arg = target_def.args[index]
default_value = arg.default_value.as(MagicConstant)
location = node.location
end_location = node.end_location
case default_value.name
when :__LINE__
call_args << int32(MagicConstant.expand_line(location))
when :__END_LINE__
call_args << int32(MagicConstant.expand_line(end_location))
when :__FILE__
call_args << build_string_constant(MagicConstant.expand_file(location))
when :__DIR__
14 changes: 8 additions & 6 deletions src/compiler/crystal/macros/interpreter.cr
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ module Crystal
getter last : ASTNode
property free_vars : Hash(String, TypeVar)?

def self.new(program, scope : Type, path_lookup : Type, a_macro : Macro, call)
def self.new(program, scope : Type, path_lookup : Type, a_macro : Macro, call, a_def : Def? = nil)
vars = {} of String => ASTNode
splat_index = a_macro.splat_index
double_splat = a_macro.double_splat
@@ -50,7 +50,7 @@ module Crystal

next if vars.has_key?(macro_arg.name)

default_value = default_value.expand_node(call.location) if default_value.is_a?(MagicConstant)
default_value = default_value.expand_node(call.location, call.end_location) if default_value.is_a?(MagicConstant)
vars[macro_arg.name] = default_value
end

@@ -68,14 +68,14 @@ module Crystal
vars[macro_block_arg.name] = call_block || Nop.new
end

new(program, scope, path_lookup, a_macro.location, vars, call.block)
new(program, scope, path_lookup, a_macro.location, vars, call.block, a_def)
end

record MacroVarKey, name : String, exps : Array(ASTNode)?

def initialize(@program : Program,
@scope : Type, @path_lookup : Type, @location : Location?,
@vars = {} of String => ASTNode, @block : Block? = nil)
@vars = {} of String => ASTNode, @block : Block? = nil, @def : Def? = nil)
@str = MemoryIO.new(512) # Can't be String::Builder because of `{{debug()}}
@last = Nop.new
end
@@ -397,8 +397,8 @@ module Crystal
produce_tuple = node.names.first == "T"
when GenericInstanceType
produce_tuple = ((splat_index = path_lookup.splat_index) &&
path_lookup.type_vars.keys.index(node.names.first) == splat_index) ||
(path_lookup.double_variadic? && path_lookup.type_vars.first_key == node.names.first)
path_lookup.type_vars.keys.index(node.names.first) == splat_index) ||
(path_lookup.double_variadic? && path_lookup.type_vars.first_key == node.names.first)
else
produce_tuple = false
end
@@ -444,6 +444,8 @@ module Crystal
when "@type"
target = @scope == @program.class_type ? @scope : @scope.instance_type
return @last = TypeNode.new(target)
when "@def"
return @last = @def || NilLiteral.new
end

node.raise "unknown macro instance var: '#{node.name}'"
8 changes: 4 additions & 4 deletions src/compiler/crystal/macros/macros.cr
Original file line number Diff line number Diff line change
@@ -32,14 +32,14 @@ class Crystal::Program
filename
end

def expand_macro(a_macro : Macro, call : Call, scope : Type, path_lookup : Type? = nil)
interpreter = MacroInterpreter.new self, scope, path_lookup || scope, a_macro, call
def expand_macro(a_macro : Macro, call : Call, scope : Type, path_lookup : Type? = nil, a_def : Def? = nil)
interpreter = MacroInterpreter.new self, scope, path_lookup || scope, a_macro, call, a_def
a_macro.body.accept interpreter
interpreter.to_s
end

def expand_macro(node : ASTNode, scope : Type, path_lookup : Type? = nil, free_vars = nil)
interpreter = MacroInterpreter.new self, scope, path_lookup || scope, node.location
def expand_macro(node : ASTNode, scope : Type, path_lookup : Type? = nil, free_vars = nil, a_def : Def? = nil)
interpreter = MacroInterpreter.new self, scope, path_lookup || scope, node.location, def: a_def
interpreter.free_vars = free_vars
node.accept interpreter
interpreter.to_s
2 changes: 1 addition & 1 deletion src/compiler/crystal/semantic/call.cr
Original file line number Diff line number Diff line change
@@ -1008,7 +1008,7 @@ class Crystal::Call
arg = typed_def.args[index]
default_value = arg.default_value.as(MagicConstant)
case default_value.name
when :__LINE__
when :__LINE__, :__END_LINE__
type = program.int32
when :__FILE__, :__DIR__
type = program.string
5 changes: 3 additions & 2 deletions src/compiler/crystal/semantic/semantic_visitor.cr
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ abstract class Crystal::SemanticVisitor < Crystal::Visitor

@free_vars : Hash(String, TypeVar)?
@path_lookup : Type?
@untyped_def : Def?
@typed_def : Def?
@block : Block?

@@ -263,7 +264,7 @@ abstract class Crystal::SemanticVisitor < Crystal::Visitor
generated_nodes = expand_macro(the_macro, node) do
old_args = node.args
node.args = args
expanded = @program.expand_macro the_macro, node, expansion_scope, @path_lookup
expanded = @program.expand_macro the_macro, node, expansion_scope, @path_lookup, @untyped_def
node.args = old_args
expanded
end
@@ -363,7 +364,7 @@ abstract class Crystal::SemanticVisitor < Crystal::Visitor
the_macro = Macro.new("macro_#{node.object_id}", [] of Arg, node).at(node.location)

generated_nodes = expand_macro(the_macro, node, mode: mode) do
@program.expand_macro node, (@scope || current_type), @path_lookup, @free_vars
@program.expand_macro node, (@scope || current_type), @path_lookup, @free_vars, @untyped_def
end

node.expanded = generated_nodes
4 changes: 3 additions & 1 deletion src/compiler/crystal/syntax/ast.cr
Original file line number Diff line number Diff line change
@@ -2036,10 +2036,12 @@ module Crystal
MagicConstant.new(@name)
end

def expand_node(location)
def expand_node(location, end_location)
case name
when :__LINE__
MagicConstant.expand_line_node(location)
when :__END_LINE__
MagicConstant.expand_line_node(end_location)
when :__FILE__
MagicConstant.expand_file_node(location)
when :__DIR__
10 changes: 10 additions & 0 deletions src/compiler/crystal/syntax/lexer.cr
Original file line number Diff line number Diff line change
@@ -1029,6 +1029,16 @@ module Crystal
return @token
end
end
when 'E'
if next_char == 'N' && next_char == 'D' && next_char == '_' && next_char == 'L' && next_char == 'I' && next_char == 'N' && next_char == 'E' && next_char == '_' && next_char == '_'
if ident_part_or_end?(peek_next_char)
scan_ident(start)
else
next_char
@token.type = :__END_LINE__
return @token
end
end
when 'F'
if next_char == 'I' && next_char == 'L' && next_char == 'E' && next_char == '_' && next_char == '_'
if ident_part_or_end?(peek_next_char)
6 changes: 4 additions & 2 deletions src/compiler/crystal/syntax/parser.cr
Original file line number Diff line number Diff line change
@@ -906,6 +906,8 @@ module Crystal
end
when :__LINE__
node_and_next_token MagicConstant.expand_line_node(@token.location)
when :__END_LINE__
raise "__END_LINE__ can only be used in default argument value", @token
when :__FILE__
node_and_next_token MagicConstant.expand_file_node(@token.location)
when :__DIR__
@@ -3335,7 +3337,7 @@ module Crystal
next_token_skip_space_or_newline

case @token.type
when :__LINE__, :__FILE__, :__DIR__
when :__LINE__, :__END_LINE__, :__FILE__, :__DIR__
default_value = MagicConstant.new(@token.type).at(@token.location)
next_token
else
@@ -3891,7 +3893,7 @@ module Crystal
end
when :"{"
return nil unless allow_curly
when :CHAR, :STRING, :DELIMITER_START, :STRING_ARRAY_START, :SYMBOL_ARRAY_START, :NUMBER, :IDENT, :SYMBOL, :INSTANCE_VAR, :CLASS_VAR, :CONST, :GLOBAL, :"$~", :"$?", :GLOBAL_MATCH_DATA_INDEX, :REGEX, :"(", :"!", :"[", :"[]", :"+", :"-", :"~", :"&", :"->", :"{{", :__LINE__, :__FILE__, :__DIR__, :UNDERSCORE
when :CHAR, :STRING, :DELIMITER_START, :STRING_ARRAY_START, :SYMBOL_ARRAY_START, :NUMBER, :IDENT, :SYMBOL, :INSTANCE_VAR, :CLASS_VAR, :CONST, :GLOBAL, :"$~", :"$?", :GLOBAL_MATCH_DATA_INDEX, :REGEX, :"(", :"!", :"[", :"[]", :"+", :"-", :"~", :"&", :"->", :"{{", :__LINE__, :__END_LINE__, :__FILE__, :__DIR__, :UNDERSCORE
# Nothing
when :"*", :"**"
if current_char.whitespace?
2 changes: 1 addition & 1 deletion src/compiler/crystal/syntax/to_s.cr
Original file line number Diff line number Diff line change
@@ -387,7 +387,7 @@ module Crystal
if block
# Check if this is foo &.bar
first_block_arg = block.args.first?
if first_block_arg && block.args.size == 1
if first_block_arg && block.args.size == 1 && block.args.first.name.starts_with?("__arg")
block_body = block.body
if block_body.is_a?(Call)
block_obj = block_body.obj