Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

compiler: Introduce non-nilable read_file macro #7094

Merged
merged 4 commits into from
Nov 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions spec/compiler/macro/macro_methods_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1766,10 +1766,12 @@ module Crystal
>, filename = __FILE__).to_string.should eq(File.read("#{__DIR__}/../data/build"))
end

it "reads file (doesn't exists)" do
run(%q<
{{read_file("#{__DIR__}/../data/build_foo")}} ? 10 : 20
>, filename = __FILE__).to_i.should eq(20)
it "reads file (doesn't exist)" do
expect_raises(Crystal::TypeException, "No such file or directory") do
run(%q<
{{read_file("#{__DIR__}/../data/build_foo")}}
>, filename = __FILE__)
end
end
end

Expand All @@ -1780,9 +1782,29 @@ module Crystal
>, filename = __FILE__).to_string.should eq(File.read("spec/compiler/data/build"))
end

it "reads file (doesn't exists)" do
it "reads file (doesn't exist)" do
expect_raises(Crystal::TypeException, "No such file or directory") do
run(%q<
{{read_file("spec/compiler/data/build_foo")}}
>, filename = __FILE__)
end
end
end
end

describe "read_file?" do
context "with absolute path" do
it "reads file (doesn't exist)" do
run(%q<
{{read_file?("#{__DIR__}/../data/build_foo")}} ? 10 : 20
>, filename = __FILE__).to_i.should eq(20)
end
end

context "with relative path" do
it "reads file (doesn't exist)" do
run(%q<
{{read_file("spec/compiler/data/build_foo")}} ? 10 : 20
{{read_file?("spec/compiler/data/build_foo")}} ? 10 : 20
>, filename = __FILE__).to_i.should eq(20)
end
end
Expand Down
13 changes: 10 additions & 3 deletions src/compiler/crystal/macros.cr
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ module Crystal::Macros
def raise(message) : NoReturn
end

# Reads a file: if it exists, returns a `StringLiteral` with its contents;
# otherwise `nil` is returned.
# Reads a file and returns a `StringLiteral` with its contents.
#
# Gives a compile-time error if the file doesn't exist or if
# reading the file fails.
#
# To read a file relative to where the macro is defined, use:
#
Expand All @@ -77,7 +79,12 @@ module Crystal::Macros
# ```
#
# NOTE: Relative paths are resolved to the current working directory.
def read_file(filename) : StringLiteral | NilLiteral
def read_file(filename) : StringLiteral
end

# Same as `read_file`, except that `nil` is returned on any I/O failure
# instead of issuing a compile-time failure.
def read_file?(filename) : StringLiteral | NilLiteral
end

# Compiles and execute a Crystal program and returns its output
Expand Down
12 changes: 8 additions & 4 deletions src/compiler/crystal/macros/methods.cr
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ module Crystal
interpret_raise(node)
when "read_file"
interpret_read_file(node)
when "read_file?"
interpret_read_file(node, nilable: true)
when "run"
interpret_run(node)
else
Expand Down Expand Up @@ -198,16 +200,18 @@ module Crystal
macro_raise(node, node.args, self)
end

def interpret_read_file(node)
def interpret_read_file(node, nilable = false)
unless node.args.size == 1
node.wrong_number_of_arguments "macro call 'read_file'", node.args.size, 1
node.wrong_number_of_arguments "macro call '#{node.name}'", node.args.size, 1
end

node.args[0].accept self
filename = @last.to_macro_id
if File.file?(filename)

begin
@last = StringLiteral.new(File.read(filename))
else
rescue ex
node.raise ex.to_s unless nilable
@last = NilLiteral.new
end
end
Expand Down