Last active
March 7, 2019 00:01
-
-
Save fcard/8563a55c8d28e874886ea743ffe137ad to your computer and use it in GitHub Desktop.
Simple Printf constructed using the generated functions from the julia language
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
exports fmt and @fmt, functions to build | |
format specifiers in an alternate | |
way to @fmt_str | |
""" | |
module Cons | |
export fmt, @fmt | |
import Base: @pure | |
import ..GenPrintf: PrintArgument, PrintValue, | |
PrintBoth, FormatSpecifier | |
""" | |
builds format specifiers. | |
examples: | |
@fmt("abc") | |
@fmt("hello, ", {}) | |
""" | |
macro fmt(args...) | |
fmt(args...) | |
end | |
""" | |
builds format specifiers. | |
examples: | |
fmt("abc") | |
a = "hello, " | |
fmt(a, :{}) | |
""" | |
@pure function fmt(literal::Union{Number, String}) | |
PrintValue{Symbol(String(literal))}() | |
end | |
@pure function fmt(ex::Expr) | |
if ex.head == :tuple | |
fmt(ex.args...) | |
else | |
@assert ex == :{} "invalid formatting expression" | |
PrintArgument() | |
end | |
end | |
@pure function fmt(ex::Tuple) | |
fmt(args...) | |
end | |
@pure function fmt(ex::FormatSpecifier) | |
ex | |
end | |
@pure function fmt(args...) | |
PrintBoth(fmt(args[1]), fmt(args[2:end]...)) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module GenPrintf | |
export printf, @fmt_str | |
abstract type FormatSpecifier end | |
struct PrintArgument <: FormatSpecifier end | |
struct PrintValue{V} <: FormatSpecifier end | |
struct PrintBoth{A,B} <: FormatSpecifier | |
a::A | |
b::B | |
end | |
const ARGN_ERR = | |
"wrong number of arguments sent to printf" | |
include("impl.jl") | |
include("meta.jl") | |
include("cons.jl") | |
include("list.jl") | |
""" | |
Macro to create formatting strings. | |
{} are where arguments will go. | |
examples: | |
fmt"ab cd" | |
fmt"hello, {}!" | |
""" | |
macro fmt_str(s) | |
values = split(unescape_string(s), "{}") | |
print_list(values) | |
end | |
""" | |
Prints formatted output. Use the fmt macro for it | |
to generate code at compile time. | |
examples: | |
printf(fmt"ab cd") | |
printf(fmt"hello, {}!", "world") | |
printf("ab{}{}", "c", "d") | |
""" | |
@generated function printf(fmt::FormatSpecifier, args...) | |
printf_impl(fmt, length(args)) | |
end | |
function printf(fmt::String, args...) | |
i = 1 | |
values = split(fmt, "{}") | |
@assert length(values) == length(args)+1 ARGN_ERR | |
for value in values | |
print(value) | |
if i != length(args) + 1 | |
print(args[i]) | |
i += 1 | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Base: @pure | |
@pure function printf_impl(fmt::Type{PrintArgument}, len, i=1) | |
assert_argument_number(len, 1) | |
quote | |
Base.print(args[$i]) | |
end | |
end | |
@pure assert_argument_number(len, required) = | |
@assert len == required ARGN_ERR | |
@pure function printf_impl(fmt::Type{PrintValue{V}}, len, i=1) where V | |
assert_argument_number(len, 0) | |
quote | |
Base.print($(String(V))) | |
end | |
end | |
@pure function printf_impl(fmt::Type{PrintBoth{A,B}}, len, i=1) where {A,B} | |
l1 = arglength(A) | |
l2 = arglength(B) | |
assert_argument_number(len, l1+l2) | |
quote | |
$(printf_impl(A, l1, i)) | |
$(printf_impl(B, l2, i+l1)) | |
end | |
end | |
@pure function printf_impl(fmt::Type{String}, len, i=1) where {A,B} | |
:? | |
end | |
@pure function arglength(fmt) | |
a = Nothing | |
b = fmt | |
result = 0 | |
while b <: PrintBoth | |
a <: PrintArgument && (result += 1) | |
a = b.parameters[1] | |
b = b.parameters[2] | |
end | |
a <: PrintArgument && (result += 1) | |
b <: PrintArgument && (result += 1) | |
result | |
end | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Base: @pure | |
@pure function print_list(values) | |
argument() = PrintArgument() | |
to_value(x) = PrintValue{Symbol(String(x))}() | |
if length(values) == 1 | |
to_value(values[1]) | |
else | |
if values[1] == "" | |
PrintBoth( | |
argument(), | |
print_list(values[2:end]) | |
) | |
else | |
PrintBoth( | |
to_value(values[1]), | |
print_list([""; values[2:end]]) | |
) | |
end | |
end | |
end | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
exports @printf_code and printf_code, which returns | |
the code generated for printf. | |
""" | |
module Meta | |
export @printf_code, printf_code | |
import Base: @pure | |
import ..GenPrintf: printf_impl | |
""" | |
returns the code that printf will generate. | |
e.g. `@printf_code(fmt"abc")` will return | |
``` | |
quote | |
print("abc") | |
end | |
``` | |
""" | |
macro printf_code(fmt, args...) | |
quote | |
flatten_body( | |
remove_line_nodes( | |
printf_impl( | |
typeof($(esc(fmt))), | |
$(length(args)) | |
) | |
) | |
) | |
end | |
end | |
""" | |
returns the code that printf will generate. | |
e.g. `printf_code(fmt"abc")` will return | |
``` | |
quote | |
print("abc") | |
end | |
``` | |
""" | |
@pure function printf_code(fmt, args...) | |
flatten_body( | |
remove_line_nodes( | |
printf_impl( | |
typeof(fmt), | |
length(args) | |
) | |
) | |
) | |
end | |
@pure remove_line_nodes(x) = x | |
@pure function remove_line_nodes(x::Expr) | |
Expr( | |
x.head, | |
map( | |
remove_line_nodes, | |
filter(!x->x isa LineNumberNode, x.args) | |
)... | |
) | |
end | |
@pure flatten_body(x) = x | |
@pure function flatten_body(x::Expr) | |
result_args = [] | |
for arg in x.args | |
if arg isa Expr && arg.head == :block | |
append!(result_args, flatten_body(arg).args) | |
else | |
push!(result_args, arg) | |
end | |
end | |
Expr(:block, result_args...) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment