printf var args proof of concept

See #167

Need to troubleshoot when we send 2 slices to printf. It goes
into an infinite loop.

This commit introduces 4 builtin functions:

 * `@isInteger`
 * `@isFloat`
 * `@canImplictCast`
 * `@typeName`
This commit is contained in:
Andrew Kelley 2017-01-24 01:58:20 -05:00
parent 32d8686da8
commit 4b3f18de3c
9 changed files with 412 additions and 38 deletions

View File

@ -370,10 +370,10 @@ TODO
## Built-in Functions
Built-in functions are prefixed with `@`. Remember that the `inline` keyword on
Built-in functions are prefixed with `@`. Remember that the `comptime` keyword on
a parameter means that the parameter must be known at compile time.
### @alloca(inline T: type, count: usize) -> []T
### @alloca(comptime T: type, count: usize) -> []T
Allocates memory in the stack frame of the caller. This temporary space is
automatically freed when the function that called alloca returns to its caller,
@ -391,13 +391,13 @@ The allocated memory contents are undefined.
This function returns a compile-time constant, which is the type of the
expression passed as an argument. The expression is *not evaluated*.
### @sizeOf(inline T: type) -> (number literal)
### @sizeOf(comptime T: type) -> (number literal)
This function returns the number of bytes it takes to store T in memory.
The result is a target-specific compile time constant.
### @alignOf(inline T: type) -> (number literal)
### @alignOf(comptime T: type) -> (number literal)
This function returns the number of bytes that this type should be aligned to
for the current target.
@ -414,10 +414,10 @@ false otherwise.
```
Function Operation
@addWithOverflow(inline T: type, a: T, b: T, result: &T) -> bool *x = a + b
@subWithOverflow(inline T: type, a: T, b: T, result: &T) -> bool *x = a - b
@mulWithOverflow(inline T: type, a: T, b: T, result: &T) -> bool *x = a * b
@shlWithOverflow(inline T: type, a: T, b: T, result: &T) -> bool *x = a << b
@addWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool *x = a + b
@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool *x = a - b
@mulWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool *x = a * b
@shlWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool *x = a << b
```
### @memset(dest: &u8, c: u8, byte_count: usize)
@ -475,27 +475,27 @@ aggressive optimizations.
This function is only valid within function scope.
### @maxValue(inline T: type) -> (number literal)
### @maxValue(comptime T: type) -> (number literal)
This function returns the maximum integer value of the integer type T.
The result is a compile time constant. For some types such as `c_long`, the
result is marked as depending on a compile variable.
### @minValue(inline T: type) -> (number literal)
### @minValue(comptime T: type) -> (number literal)
This function returns the minimum integer value of the integer type T.
The result is a compile time constant. For some types such as `c_long`, the
result is marked as depending on a compile variable.
### @memberCount(inline T: type) -> (number literal)
### @memberCount(comptime T: type) -> (number literal)
This function returns the number of enum values in an enum type.
The result is a compile time constant.
### @import(inline path: []u8) -> (namespace)
### @import(comptime path: []u8) -> (namespace)
This function finds a zig file corresponding to `path` and imports all the
public top level declarations into the resulting namespace.
@ -516,25 +516,25 @@ appending to a temporary buffer which is then parsed as C code.
This function is only valid at top level scope.
### @cInclude(inline path: []u8)
### @cInclude(comptime path: []u8)
This function can only occur inside `@c_import`.
This appends `#include <$path>\n` to the `c_import` temporary buffer.
### @cDefine(inline name: []u8, value)
### @cDefine(comptime name: []u8, value)
This function can only occur inside `@c_import`.
This appends `#define $name $value` to the `c_import` temporary buffer.
### @cUndef(inline name: []u8)
### @cUndef(comptime name: []u8)
This function can only occur inside `@c_import`.
This appends `#undef $name` to the `c_import` temporary buffer.
### @compileVar(inline name: []u8) -> (varying type)
### @compileVar(comptime name: []u8) -> (varying type)
This function returns a compile-time variable. There are built in compile
variables:
@ -584,10 +584,14 @@ error OutOfMem;
Then the string representation is "OutOfMem".
If there are no calls to `@err_name` in an entire application, then no error
If there are no calls to `@errorName` in an entire application, then no error
name table will be generated.
### @embedFile(inline path: []u8) -> [X]u8
### @typeName(T: type) -> []u8
This function returns the string representation of a type.
### @embedFile(comptime path: []u8) -> [X]u8
This function returns a compile time constant fixed-size array with length
equal to the byte count of the file given by `path`. The contents of the array
@ -610,7 +614,7 @@ The caller guarantees that this operation will have no remainder.
In debug mode, a remainder causes a panic. In release mode, a remainder is
undefined behavior.
### @truncate(inline T: type, integer) -> T
### @truncate(comptime T: type, integer) -> T
This function truncates bits from an integer type, resulting in a smaller
integer type.
@ -631,14 +635,14 @@ const b: u8 = @truncate(u8, a);
// b is now 0xcd
```
### @compileError(inline msg: []u8)
### @compileError(comptime msg: []u8)
This function, when semantically analyzed, causes a compile error with the message `msg`.
There are several ways that code avoids being semantically checked, such as using `if`
or `switch` with compile time constants, and inline functions.
or `switch` with compile time constants, and comptime functions.
### @intType(inline is_signed: bool, inline bit_count: u8) -> type
### @intType(comptime is_signed: bool, comptime bit_count: u8) -> type
This function returns an integer type with the given signness and bit count.
@ -646,3 +650,14 @@ This function returns an integer type with the given signness and bit count.
Makes the target function a test function.
### @isInteger(comptime T: type) -> bool
Returns whether a given type is an integer.
### @isFloat(comptime T: type) -> bool
Returns whether a given type is a float.
### @canImplicitCast(comptime T: type, value) -> bool
Returns whether a value can be implicitly casted to a given type.

View File

@ -2,8 +2,6 @@ const std = @import("std");
const io = std.io;
const str = std.str;
// TODO var args printing
pub fn main(args: [][]u8) -> %void {
const exe = args[0];
var catted_anything = false;

View File

@ -978,6 +978,9 @@ struct TypeTableEntry {
HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size;
TypeTableEntry *maybe_parent;
TypeTableEntry *error_parent;
// If we generate a constant name value for this type, we memoize it here.
// The type of this is array
ConstExprValue *cached_const_name_val;
};
struct PackageTableEntry {
@ -1086,6 +1089,10 @@ enum BuiltinFnId {
BuiltinFnIdSetFnVisible,
BuiltinFnIdSetDebugSafety,
BuiltinFnIdAlloca,
BuiltinFnIdTypeName,
BuiltinFnIdIsInteger,
BuiltinFnIdIsFloat,
BuiltinFnIdCanImplicitCast,
};
struct BuiltinFnEntry {
@ -1505,6 +1512,9 @@ enum IrInstructionId {
IrInstructionIdPtrToInt,
IrInstructionIdIntToEnum,
IrInstructionIdCheckSwitchProngs,
IrInstructionIdTestType,
IrInstructionIdTypeName,
IrInstructionIdCanImplicitCast,
};
struct IrInstruction {
@ -2188,6 +2198,26 @@ struct IrInstructionCheckSwitchProngs {
size_t range_count;
};
struct IrInstructionTestType {
IrInstruction base;
IrInstruction *type_value;
TypeTableEntryId type_id;
};
struct IrInstructionTypeName {
IrInstruction base;
IrInstruction *type_value;
};
struct IrInstructionCanImplicitCast {
IrInstruction base;
IrInstruction *type_value;
IrInstruction *target_value;
};
enum LValPurpose {
LValPurposeNone,
LValPurposeAssign,

View File

@ -2279,6 +2279,9 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdTestComptime:
case IrInstructionIdGeneratedCode:
case IrInstructionIdCheckSwitchProngs:
case IrInstructionIdTestType:
case IrInstructionIdTypeName:
case IrInstructionIdCanImplicitCast:
zig_unreachable();
case IrInstructionIdReturn:
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
@ -3652,6 +3655,10 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdImport, "import", 1);
create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1);
create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1);
create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1);
create_builtin_fn(g, BuiltinFnIdIsInteger, "isInteger", 1);
create_builtin_fn(g, BuiltinFnIdIsFloat, "isFloat", 1);
create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2);
create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);
create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);
create_builtin_fn(g, BuiltinFnIdFence, "fence", 1);

View File

@ -495,6 +495,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProng
return IrInstructionIdCheckSwitchProngs;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTestType *) {
return IrInstructionIdTestType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeName *) {
return IrInstructionIdTypeName;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCanImplicitCast *) {
return IrInstructionIdCanImplicitCast;
}
template<typename T>
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate<T>(1);
@ -2001,6 +2013,45 @@ static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope,
return &instruction->base;
}
static IrInstruction *ir_build_test_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value, TypeTableEntryId type_id)
{
IrInstructionTestType *instruction = ir_build_instruction<IrInstructionTestType>(
irb, scope, source_node);
instruction->type_value = type_value;
instruction->type_id = type_id;
ir_ref_instruction(type_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_type_name(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value)
{
IrInstructionTypeName *instruction = ir_build_instruction<IrInstructionTypeName>(
irb, scope, source_node);
instruction->type_value = type_value;
ir_ref_instruction(type_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_can_implicit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value, IrInstruction *target_value)
{
IrInstructionCanImplicitCast *instruction = ir_build_instruction<IrInstructionCanImplicitCast>(
irb, scope, source_node);
instruction->type_value = type_value;
instruction->target_value = target_value;
ir_ref_instruction(type_value, irb->current_basic_block);
ir_ref_instruction(target_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {
return nullptr;
}
@ -2605,6 +2656,28 @@ static IrInstruction *ir_instruction_checkswitchprongs_get_dep(IrInstructionChec
return nullptr;
}
static IrInstruction *ir_instruction_testtype_get_dep(IrInstructionTestType *instruction, size_t index) {
switch (index) {
case 0: return instruction->type_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_typename_get_dep(IrInstructionTypeName *instruction, size_t index) {
switch (index) {
case 0: return instruction->type_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_canimplicitcast_get_dep(IrInstructionCanImplicitCast *instruction, size_t index) {
switch (index) {
case 0: return instruction->type_value;
case 1: return instruction->target_value;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {
switch (instruction->id) {
case IrInstructionIdInvalid:
@ -2777,6 +2850,12 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
return ir_instruction_inttoenum_get_dep((IrInstructionIntToEnum *) instruction, index);
case IrInstructionIdCheckSwitchProngs:
return ir_instruction_checkswitchprongs_get_dep((IrInstructionCheckSwitchProngs *) instruction, index);
case IrInstructionIdTestType:
return ir_instruction_testtype_get_dep((IrInstructionTestType *) instruction, index);
case IrInstructionIdTypeName:
return ir_instruction_typename_get_dep((IrInstructionTypeName *) instruction, index);
case IrInstructionIdCanImplicitCast:
return ir_instruction_canimplicitcast_get_dep((IrInstructionCanImplicitCast *) instruction, index);
}
zig_unreachable();
}
@ -3594,6 +3673,18 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *
return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr);
}
static IrInstruction *ir_gen_test_type(IrBuilder *irb, Scope *scope, AstNode *node, TypeTableEntryId type_id) {
assert(node->type == NodeTypeFnCallExpr);
AstNode *type_node = node->data.fn_call_expr.params.at(0);
IrInstruction *type_value = ir_gen_node(irb, type_node, scope);
if (type_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
return ir_build_test_type(irb, scope, node, type_value, type_id);
}
static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeFnCallExpr);
@ -3995,6 +4086,33 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
return ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul);
case BuiltinFnIdShlWithOverflow:
return ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl);
case BuiltinFnIdTypeName:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_type_name(irb, scope, node, arg0_value);
}
case BuiltinFnIdIsInteger:
return ir_gen_test_type(irb, scope, node, TypeTableEntryIdInt);
case BuiltinFnIdIsFloat:
return ir_gen_test_type(irb, scope, node, TypeTableEntryIdFloat);
case BuiltinFnIdCanImplicitCast:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
return ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value);
}
}
zig_unreachable();
}
@ -8942,7 +9060,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi
case TypeTableEntryIdEnumTag:
case TypeTableEntryIdArgTuple:
{
ConstExprValue *out_val = ir_build_const_from(ira, &typeof_instruction->base, false);
ConstExprValue *out_val = ir_build_const_from(ira, &typeof_instruction->base, true);
// TODO depends_on_compile_var should be set based on whether the type of the expression
// depends_on_compile_var. but we currently don't have a thing to tell us if the type of
// something depends on a compile var
@ -9622,6 +9740,8 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
pointee_val = nullptr;
}
TypeTableEntry *canon_target_type = get_underlying_type(target_type);
ensure_complete_type(ira->codegen, target_type);
switch (canon_target_type->id) {
case TypeTableEntryIdInvalid:
case TypeTableEntryIdVar:
@ -10260,6 +10380,24 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc
return str_type;
}
static TypeTableEntry *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTypeName *instruction) {
IrInstruction *type_value = instruction->type_value->other;
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
if (type_entry->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *str_type = get_slice_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true);
if (!type_entry->cached_const_name_val) {
ConstExprValue *array_val = create_const_str_lit(ira->codegen, &type_entry->name);
type_entry->cached_const_name_val = create_const_slice(ira->codegen,
array_val, 0, buf_len(&type_entry->name), true);
}
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base,
type_value->value.depends_on_compile_var);
*out_val = *type_entry->cached_const_name_val;
return str_type;
}
static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstructionCImport *instruction) {
AstNode *node = instruction->base.source_node;
assert(node->type == NodeTypeFnCallExpr);
@ -11370,6 +11508,44 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_test_type(IrAnalyze *ira, IrInstructionTestType *instruction) {
IrInstruction *type_value = instruction->type_value->other;
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
if (type_entry->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, type_value->value.depends_on_compile_var);
out_val->data.x_bool = (type_entry->id == instruction->type_id);
return ira->codegen->builtin_types.entry_bool;
}
static TypeTableEntry *ir_analyze_instruction_can_implicit_cast(IrAnalyze *ira,
IrInstructionCanImplicitCast *instruction)
{
IrInstruction *type_value = instruction->type_value->other;
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
if (type_entry->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *target_value = instruction->target_value->other;
if (target_value->value.type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
ImplicitCastMatchResult result = ir_types_match_with_implicit_cast(ira, type_entry, target_value->value.type,
target_value);
if (result == ImplicitCastMatchResultReportedError) {
zig_panic("TODO refactor implicit cast tester to return bool without reporting errors");
}
// TODO in order to known depends_on_compile_var we have to known if the type of the target
// depends on a compile var
bool depends_on_compile_var = true;
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
out_val->data.x_bool = (result == ImplicitCastMatchResultYes);
return ira->codegen->builtin_types.entry_bool;
}
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
switch (instruction->id) {
case IrInstructionIdInvalid:
@ -11471,6 +11647,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_compile_err(ira, (IrInstructionCompileErr *)instruction);
case IrInstructionIdErrName:
return ir_analyze_instruction_err_name(ira, (IrInstructionErrName *)instruction);
case IrInstructionIdTypeName:
return ir_analyze_instruction_type_name(ira, (IrInstructionTypeName *)instruction);
case IrInstructionIdCImport:
return ir_analyze_instruction_c_import(ira, (IrInstructionCImport *)instruction);
case IrInstructionIdCInclude:
@ -11525,6 +11703,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_test_comptime(ira, (IrInstructionTestComptime *)instruction);
case IrInstructionIdCheckSwitchProngs:
return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction);
case IrInstructionIdTestType:
return ir_analyze_instruction_test_type(ira, (IrInstructionTestType *)instruction);
case IrInstructionIdCanImplicitCast:
return ir_analyze_instruction_can_implicit_cast(ira, (IrInstructionCanImplicitCast *)instruction);
case IrInstructionIdMaybeWrap:
case IrInstructionIdErrWrapCode:
case IrInstructionIdErrWrapPayload:
@ -11691,6 +11873,9 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdPtrToInt:
case IrInstructionIdIntToPtr:
case IrInstructionIdIntToEnum:
case IrInstructionIdTestType:
case IrInstructionIdTypeName:
case IrInstructionIdCanImplicitCast:
return false;
case IrInstructionIdAsm:
{

View File

@ -807,6 +807,26 @@ static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchP
fprintf(irp->f, ")");
}
static void ir_print_test_type(IrPrint *irp, IrInstructionTestType *instruction) {
fprintf(irp->f, "@testType(");
ir_print_other_instruction(irp, instruction->type_value);
fprintf(irp->f, ")");
}
static void ir_print_type_name(IrPrint *irp, IrInstructionTypeName *instruction) {
fprintf(irp->f, "@typeName(");
ir_print_other_instruction(irp, instruction->type_value);
fprintf(irp->f, ")");
}
static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCast *instruction) {
fprintf(irp->f, "@canImplicitCast(");
ir_print_other_instruction(irp, instruction->type_value);
fprintf(irp->f, ",");
ir_print_other_instruction(irp, instruction->target_value);
fprintf(irp->f, ")");
}
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction);
switch (instruction->id) {
@ -1064,6 +1084,15 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdCheckSwitchProngs:
ir_print_check_switch_prongs(irp, (IrInstructionCheckSwitchProngs *)instruction);
break;
case IrInstructionIdTestType:
ir_print_test_type(irp, (IrInstructionTestType *)instruction);
break;
case IrInstructionIdTypeName:
ir_print_type_name(irp, (IrInstructionTypeName *)instruction);
break;
case IrInstructionIdCanImplicitCast:
ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction);
break;
}
fprintf(irp->f, "\n");
}

View File

@ -50,10 +50,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void {
const compile_unit = findCompileUnit(st, return_address) ?? return error.MissingDebugInfo;
const name = %return compile_unit.die.getAttrString(st, DW.AT_name);
%return out_stream.printInt(usize, return_address);
%return out_stream.printf(" -> ");
%return out_stream.printf(name);
%return out_stream.printf("\n");
%return out_stream.printf("{} -> {}\n", return_address, name);
maybe_fp = *(&const ?&const u8)(fp);
}
},

View File

@ -80,7 +80,7 @@ pub const OutStream = struct {
self.index += 1;
}
pub fn write(self: &OutStream, bytes: []const u8) -> %usize {
pub fn write(self: &OutStream, bytes: []const u8) -> %void {
var src_bytes_left = bytes.len;
var src_index: usize = 0;
const dest_space_left = self.buffer.len - self.index;
@ -94,25 +94,89 @@ pub const OutStream = struct {
}
src_bytes_left -= copy_amt;
}
return bytes.len;
}
/// Prints a byte buffer, flushes the buffer, then returns the number of
/// bytes printed. The "f" is for "flush".
pub fn printf(self: &OutStream, str: []const u8) -> %usize {
const byte_count = %return self.write(str);
const State = enum { // TODO put inside printf function and make sure the name and debug info is correct
Start,
OpenBrace,
CloseBrace,
};
/// Calls print and then flushes the buffer.
pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) -> %void {
comptime var start_index: usize = 0;
comptime var state = State.Start;
comptime var next_arg: usize = 0;
inline for (format) |c, i| {
switch (state) {
State.Start => switch (c) {
'{' => {
if (start_index < i) %return self.write(format[start_index...i]);
state = State.OpenBrace;
},
'}' => {
if (start_index < i) %return self.write(format[start_index...i]);
state = State.CloseBrace;
},
else => {},
},
State.OpenBrace => switch (c) {
'{' => {
state = State.Start;
start_index = i;
},
'}' => {
%return self.printValue(args[next_arg]);
next_arg += 1;
state = State.Start;
start_index = i + 1;
},
else => @compileError("Unknown format character: " ++ c),
},
State.CloseBrace => switch (c) {
'}' => {
state = State.Start;
start_index = i;
},
else => @compileError("Single '}' encountered in format string"),
},
}
}
comptime {
if (args.len != next_arg) {
@compileError("Unused arguments");
}
if (state != State.Start) {
@compileError("Incomplete format string: " ++ format);
}
}
inline if (start_index < format.len) {
%return self.write(format[start_index...format.len]);
}
%return self.flush();
return byte_count;
}
pub fn printInt(self: &OutStream, comptime T: type, x: T) -> %usize {
pub fn printValue(self: &OutStream, value: var) -> %void {
const T = @typeOf(value);
if (@isInteger(T)) {
return self.printInt(T, value);
} else if (@isFloat(T)) {
return self.printFloat(T, value);
} else if (@canImplicitCast([]const u8, value)) {
const casted_value = ([]const u8)(value);
return self.write(casted_value);
} else {
@compileError("Unable to print type '" ++ @typeName(T) ++ "'");
}
}
pub fn printInt(self: &OutStream, comptime T: type, x: T) -> %void {
// TODO replace max_u64_base10_digits with math.log10(math.pow(2, @sizeOf(T)))
if (self.index + max_u64_base10_digits >= self.buffer.len) {
%return self.flush();
}
const amt_printed = bufPrintInt(T, self.buffer[self.index...], x);
self.index += amt_printed;
return amt_printed;
}
pub fn flush(self: &OutStream) -> %void {

View File

@ -517,3 +517,52 @@ fn testArray2DConstDoublePtr(ptr: &const f32) {
assert(ptr[0] == 1.0);
assert(ptr[1] == 2.0);
}
fn isInteger() {
@setFnTest(this);
comptime {
assert(@isInteger(i8));
assert(@isInteger(u8));
assert(@isInteger(i64));
assert(@isInteger(u64));
assert(!@isInteger(f32));
assert(!@isInteger(f64));
assert(!@isInteger(bool));
assert(!@isInteger(&i32));
}
}
fn isFloat() {
@setFnTest(this);
comptime {
assert(!@isFloat(i8));
assert(!@isFloat(u8));
assert(!@isFloat(i64));
assert(!@isFloat(u64));
assert(@isFloat(f32));
assert(@isFloat(f64));
assert(!@isFloat(bool));
assert(!@isFloat(&f32));
}
}
fn canImplicitCast() {
@setFnTest(this);
comptime {
assert(@canImplicitCast(i64, i32(3)));
assert(!@canImplicitCast(i32, f32(1.234)));
assert(@canImplicitCast([]const u8, "aoeu"));
}
}
fn typeName() {
@setFnTest(this);
comptime {
assert(str.eql(@typeName(i64), "i64"));
assert(str.eql(@typeName(&usize), "&usize"));
}
}