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

expression type which enables variable argument functions #132

Closed
andrewrk opened this issue Mar 4, 2016 · 1 comment
Closed

expression type which enables variable argument functions #132

andrewrk opened this issue Mar 4, 2016 · 1 comment
Labels
enhancement Solving this issue will likely involve adding new logic or components to the codebase.
Milestone

Comments

@andrewrk
Copy link
Member

andrewrk commented Mar 4, 2016

Here is my proposal for an expression type which makes it possible to have variable argument functions. It requires a lot of advanced stuff that isn't implemented yet, such as:

  • generics (see generics #22)
  • inline parameters: the parameter must always be evaluated at compile time at the callsite, and then the function becomes inline
  • expressions as parameters: these must always be inline, and then you use @eval to evaluate the expression.
  • @can_implicit_cast the idea of this builtin is to tell you if an implicit cast is allowed. but this will probably be replaced by having the template string contain the types just like C's printf.
  • having an array as the last parameter in a function allowing you to call it with any number of arguments with matching types. We might add syntax for this to make it more explicit.

So, with this example, calling os.printf("int: % string: %", 3, some_string); would effectively result in this code, after inlining the function calls:

switch (os.print_str("int: ")) {
    error.Ok => |count| {
        switch (os.print_u64(3)) {
            error.Ok => |count_2| {
                switch (os.print_str(" string: ")) {
                    error.Ok => |count_3| {
                        switch (os.print_str(some_string)) {
                            error.Ok => |count_4| {
                                switch (os.flush()) {
                                    error.Ok => count + count_2 + count_3 + count_4,
                                    else => |err| err,
                                }
                            },
                            else => |err| err,
                        }
                    },
                    else => |err| err,
                }
            },
            else => |err| err,
        }
    },
    else => |err| err,
}

io.zig

struct OutStream {
    fd: isize,
    buffer: [4 * 1024]u8,
    index: isize,

    fn print_one(T: type)(os: &OutStream, raw_value: T) -> %isize {
        return switch (@typeof(value)) {
            i64 => os.print_i64(value),
            u64 => os.print_u64(value),
            f64 => os.print_f64(value),
            []u8 => os.print_str(value),
            else => unreachable{},
        }
    }

    pub print(os: &OutStream, inline format: []const u8, inline args: []expr) -> %isize {
        var next_arg: isize = 0;
        var print_count: isize = 0;
        var start: isize = 0;
        for (format) |c, i| {
            if (c == '%') {
                const slice = format[start...i];
                if (slice.len > 0) {
                    print_count += os.print_str(slice) %% |err| return err;
                }
                start = i + 1;

                const arg = args[next_arg];
                next_arg += 1;

                const raw_value = @eval(arg);
                // this casting allows number literals and string literals to work
                // since we need to cast those types before invoking a generic function
                const value = if (@can_implicit_cast(i64, raw_value)) {
                    i64(raw_value)
                } else if (@can_implicit_cast(u64, raw_value)) {
                    u64(raw_value)
                } else if (@can_implicit_cast([]u8, raw_value)) {
                    ([]u8)(raw_value)
                } else if (@can_implicit_cast(f64, raw_value)) {
                    f64(raw_value)
                } else {
                    raw_value
                }
                const T = @typeof(value);
                print_count += os.print_one(T)(value) %% |err| return err;
            }
        }

        if (next_arg != args.len) unreachable{};

        return print_count;
    }

    pub printf(os: &OutStream, args: []expr) -> %isize {
        const result = os.print(args) %% |err| return err;
        os.flush() %% |err| return err;
        return result;
    }

    pub fn print_i64(os: &OutStream, x: i64) -> %isize {
        // ...
    }

    pub fn print_u64(os: &OutStream, x: u64) -> %isize {
        // ...
    }

    pub fn print_f64(os: &OutStream, x: f64) -> %isize {
        // ...
    }

    pub fn print_str(os: &OutStream, str: []const u8) -> %isize {
        // ...
    }

    pub fn print_byte(os: &OutStream, c: u8) -> %void {
        // ...
    }

    pub fn flush(os: &OutStream) -> %void {
        // ...
    }
}

pub fn assert(inline arg: expr) {
    if (!@compile_var("is_release")) {
        if (!@eval(arg)) unreachable{};
    }
}

main.zig

const io = @import("std").io;

pub fn main() {
    %%io.stdout.printf("string: {}  integer: {}  float: {}\n", "hello", 3, 5.0);
}
@andrewrk andrewrk added the enhancement Solving this issue will likely involve adding new logic or components to the codebase. label Mar 4, 2016
@andrewrk andrewrk added this to the 0.1.0 milestone Mar 4, 2016
@andrewrk
Copy link
Member Author

Superseded by #167

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Solving this issue will likely involve adding new logic or components to the codebase.
Projects
None yet
Development

No branches or pull requests

1 participant