Commit e411467e1d
Changed files (17)
doc
example
arrays
expressions
hello_world
structs
std
test
doc/langref.md
@@ -200,10 +200,11 @@ as
### Numbers
- Number literals | Example | Exponentiation
--------------------------------------------
- Decimal integer | 98222 | N/A
- Hex integer | 0xff | N/A
- Octal integer | 0o77 | N/A
- Binary integer | 0b11110000 | N/A
- Floating-point | 123.0E+77 | Optional
+ Number literals | Example | Exponentiation
+--------------------------------------------------
+ Decimal integer | 98222 | N/A
+ Hex integer | 0xff | N/A
+ Octal integer | 0o77 | N/A
+ Binary integer | 0b11110000 | N/A
+ Floating-point | 123.0E+77 | Optional
+ Hex floating point | TODO | TODO
example/arrays/arrays.zig
@@ -1,15 +1,11 @@
export executable "arrays";
-#link("c")
-extern {
- fn puts(s: *const u8) -> i32;
- fn exit(code: i32) -> unreachable;
-}
+use "std.zig";
-export fn _start() -> unreachable {
+export fn main(argc: isize, argv: *mut *mut u8, env: *mut *mut u8) -> i32 {
let mut array : [i32; 5];
- let mut i = 0;
+ let mut i : i32 = 0;
loop_start:
if i == 5 {
goto loop_end;
@@ -21,21 +17,22 @@ loop_start:
loop_end:
i = 0;
- let mut accumulator = 0;
+ let mut accumulator : i32 = 0;
loop_2_start:
if i == 5 {
goto loop_2_end;
}
- accumulator = accumulator + array[i];
+ accumulator += array[i];
i = i + 1;
goto loop_2_start;
loop_2_end:
if accumulator == 15 {
- puts("OK");
+ print_str("OK" as string);
}
- exit(0);
+
+ return 0;
}
example/expressions/expressions.zig
@@ -14,19 +14,19 @@ fn other_exit() -> unreachable {
export fn _start() -> unreachable {
let a : i32 = 1;
- let b = 2;
+ let b = 2 as i32;
// let c : i32; // not yet support for const variables
// let d; // parse error
if (a + b == 3) {
- let no_conflict = 5;
- if (no_conflict == 5) { puts("OK 1"); }
+ let no_conflict : i32 = 5;
+ if (no_conflict == 5) { puts(c"OK 1"); }
}
let c = {
- let no_conflict = 10;
+ let no_conflict : i32 = 10;
no_conflict
};
- if (c == 10) { puts("OK 2"); }
+ if (c == 10) { puts(c"OK 2"); }
void_fun(1, void, 2);
@@ -44,12 +44,12 @@ fn void_fun(a : i32, b : void, c : i32) -> void {
}
fn test_mutable_vars() {
- let mut i = 0;
+ let mut i : i32 = 0;
loop_start:
if i == 3 {
goto done;
}
- puts("loop");
+ puts(c"loop");
i = i + 1;
goto loop_start;
done:
example/hello_world/hello_libc.zig
@@ -7,6 +7,6 @@ extern {
}
export fn _start() -> unreachable {
- printf("Hello, world!\n");
+ printf(c"Hello, world!\n");
exit(0);
}
example/multiple_files/foo.zig
@@ -3,7 +3,7 @@ use "libc.zig";
// purposefully conflicting function with main.zig
// but it's private so it should be OK
fn private_function() {
- puts("it works!");
+ puts(c"it works!");
}
pub fn print_text() {
example/multiple_files/libc.zig
@@ -1,5 +1,5 @@
#link("c")
extern {
- pub fn puts(s: *mut u8) -> i32;
+ pub fn puts(s: *const u8) -> i32;
pub fn exit(code: i32) -> unreachable;
}
example/structs/structs.zig
@@ -22,6 +22,6 @@ struct Foo {
fn test_foo(foo : Foo) {
if foo.b {
- print_str("OK" as string);
+ print_str("OK\n" as string);
}
}
src/analyze.cpp
@@ -122,7 +122,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool
}
}
-static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, int array_size) {
+static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size) {
auto existing_entry = child_type->arrays_by_size.maybe_get(array_size);
if (existing_entry) {
return existing_entry->value;
@@ -130,7 +130,7 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray);
entry->type_ref = LLVMArrayType(child_type->type_ref, array_size);
buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "[%s; %d]", buf_ptr(&child_type->name), array_size);
+ buf_appendf(&entry->name, "[%s; %" PRIu64 "]", buf_ptr(&child_type->name), array_size);
entry->size_in_bits = child_type->size_in_bits * array_size;
entry->align_in_bits = child_type->align_in_bits;
@@ -145,11 +145,6 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in
}
}
-static int parse_int(Buf *number) {
- // TODO: think about integer size of array sizes
- return atoi(buf_ptr(number));
-}
-
static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeType);
alloc_codegen_node(node);
@@ -192,16 +187,15 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
}
AstNode *size_node = node->data.type.array_size;
- int size; // TODO: think about integer size of array sizes
- if (size_node->type != NodeTypeNumberLiteral) {
- add_node_error(g, size_node,
- buf_create_from_str("array size must be literal number"));
- size = -1;
+ if (size_node->type == NodeTypeNumberLiteral &&
+ is_num_lit_unsigned(size_node->data.number_literal.kind))
+ {
+ type_node->entry = get_array_type(g, child_type, size_node->data.number_literal.data.x_uint);
} else {
- size = parse_int(&size_node->data.number);
+ add_node_error(g, size_node,
+ buf_create_from_str("array size must be literal unsigned integer"));
+ type_node->entry = g->builtin_types.entry_invalid;
}
-
- type_node->entry = get_array_type(g, child_type, size);
return type_node->entry;
}
}
@@ -625,6 +619,44 @@ static void get_struct_field(TypeTableEntry *struct_type, Buf *name, TypeStructF
*out_i = -1;
}
+static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, TypeTableEntry *other_type) {
+ NumLit num_lit = literal_type->data.num_lit.kind;
+ uint64_t lit_size_in_bits = num_lit_bit_count(num_lit);
+
+ switch (other_type->id) {
+ case TypeTableEntryIdInvalid:
+ case TypeTableEntryIdNumberLiteral:
+ zig_unreachable();
+ case TypeTableEntryIdVoid:
+ case TypeTableEntryIdBool:
+ case TypeTableEntryIdUnreachable:
+ case TypeTableEntryIdPointer:
+ case TypeTableEntryIdArray:
+ case TypeTableEntryIdStruct:
+ return false;
+ case TypeTableEntryIdInt:
+ if (is_num_lit_signed(num_lit)) {
+ if (!other_type->data.integral.is_signed) {
+ return false;
+ }
+
+ return lit_size_in_bits <= other_type->size_in_bits;
+ } else if (is_num_lit_unsigned(num_lit)) {
+
+ return lit_size_in_bits <= other_type->size_in_bits;
+ } else {
+ return false;
+ }
+ case TypeTableEntryIdFloat:
+ if (is_num_lit_float(num_lit)) {
+ return lit_size_in_bits <= other_type->size_in_bits;
+ } else {
+ return false;
+ }
+ }
+}
+
+
static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
AstNode *node)
{
@@ -790,6 +822,16 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
cast_node->op = CastOpArrayToString;
context->cast_expr_alloca_list.append(node);
return wanted_type;
+ } else if (actual_type->id == TypeTableEntryIdNumberLiteral &&
+ num_lit_fits_in_other_type(g, actual_type, wanted_type))
+ {
+ AstNode *literal_node = node->data.cast_expr.expr;
+ assert(literal_node->codegen_node);
+ NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node;
+ assert(!codegen_num_lit->resolved_type);
+ codegen_num_lit->resolved_type = wanted_type;
+ cast_node->op = CastOpNothing;
+ return wanted_type;
} else {
add_node_error(g, node,
buf_sprintf("invalid cast from type '%s' to '%s'",
@@ -799,6 +841,314 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
}
}
+static TypeTableEntry * resolve_rhs_number_literal(CodeGen *g, AstNode *non_literal_node,
+ TypeTableEntry *non_literal_type, AstNode *literal_node, TypeTableEntry *literal_type)
+{
+ assert(literal_node->codegen_node);
+ NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node;
+
+ if (num_lit_fits_in_other_type(g, literal_type, non_literal_type)) {
+ assert(!codegen_num_lit->resolved_type);
+ codegen_num_lit->resolved_type = non_literal_type;
+ return non_literal_type;
+ } else {
+ return nullptr;
+ }
+}
+
+static TypeTableEntry * resolve_number_literals(CodeGen *g, AstNode *node1, AstNode *node2) {
+ TypeTableEntry *type1 = node1->codegen_node->expr_node.type_entry;
+ TypeTableEntry *type2 = node2->codegen_node->expr_node.type_entry;
+
+ if (type1->id == TypeTableEntryIdNumberLiteral &&
+ type2->id == TypeTableEntryIdNumberLiteral)
+ {
+ assert(node1->codegen_node);
+ assert(node2->codegen_node);
+
+ NumberLiteralNode *codegen_num_lit_1 = &node1->codegen_node->data.num_lit_node;
+ NumberLiteralNode *codegen_num_lit_2 = &node2->codegen_node->data.num_lit_node;
+
+ assert(!codegen_num_lit_1->resolved_type);
+ assert(!codegen_num_lit_2->resolved_type);
+
+ if (is_num_lit_float(type1->data.num_lit.kind) &&
+ is_num_lit_float(type2->data.num_lit.kind))
+ {
+ codegen_num_lit_1->resolved_type = g->builtin_types.entry_f64;
+ codegen_num_lit_2->resolved_type = g->builtin_types.entry_f64;
+ return g->builtin_types.entry_f64;
+ } else if (is_num_lit_signed(type1->data.num_lit.kind) &&
+ is_num_lit_signed(type2->data.num_lit.kind))
+ {
+ codegen_num_lit_1->resolved_type = g->builtin_types.entry_i64;
+ codegen_num_lit_2->resolved_type = g->builtin_types.entry_i64;
+ return g->builtin_types.entry_i64;
+ } else if (is_num_lit_unsigned(type1->data.num_lit.kind) &&
+ is_num_lit_unsigned(type2->data.num_lit.kind))
+ {
+ codegen_num_lit_1->resolved_type = g->builtin_types.entry_u64;
+ codegen_num_lit_2->resolved_type = g->builtin_types.entry_u64;
+ return g->builtin_types.entry_u64;
+ } else {
+ return nullptr;
+ }
+ } else if (type1->id == TypeTableEntryIdNumberLiteral) {
+ return resolve_rhs_number_literal(g, node2, type2, node1, type1);
+ } else {
+ assert(type2->id == TypeTableEntryIdNumberLiteral);
+ return resolve_rhs_number_literal(g, node1, type1, node2, type2);
+ }
+}
+
+static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+ TypeTableEntry *expected_type, AstNode *node)
+{
+ switch (node->data.bin_op_expr.bin_op) {
+ case BinOpTypeAssign:
+ case BinOpTypeAssignTimes:
+ case BinOpTypeAssignDiv:
+ case BinOpTypeAssignMod:
+ case BinOpTypeAssignPlus:
+ case BinOpTypeAssignMinus:
+ case BinOpTypeAssignBitShiftLeft:
+ case BinOpTypeAssignBitShiftRight:
+ case BinOpTypeAssignBitAnd:
+ case BinOpTypeAssignBitXor:
+ case BinOpTypeAssignBitOr:
+ case BinOpTypeAssignBoolAnd:
+ case BinOpTypeAssignBoolOr:
+ {
+ AstNode *lhs_node = node->data.bin_op_expr.op1;
+ TypeTableEntry *expected_rhs_type = nullptr;
+ if (lhs_node->type == NodeTypeSymbol) {
+ Buf *name = &lhs_node->data.symbol;
+ LocalVariableTableEntry *var = find_local_variable(context, name);
+ if (var) {
+ if (var->is_const) {
+ add_node_error(g, lhs_node,
+ buf_sprintf("cannot assign to constant variable"));
+ } else {
+ if (!is_op_allowed(var->type, node->data.bin_op_expr.bin_op)) {
+ if (var->type->id != TypeTableEntryIdInvalid) {
+ add_node_error(g, lhs_node,
+ buf_sprintf("operator not allowed for type '%s'",
+ buf_ptr(&var->type->name)));
+ }
+ } else {
+ expected_rhs_type = var->type;
+ }
+ }
+ } else {
+ add_node_error(g, lhs_node,
+ buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name)));
+ }
+ } else if (lhs_node->type == NodeTypeArrayAccessExpr) {
+ expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node);
+ } else if (lhs_node->type == NodeTypeFieldAccessExpr) {
+ alloc_codegen_node(lhs_node);
+ expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node);
+ } else {
+ add_node_error(g, lhs_node,
+ buf_sprintf("assignment target must be variable, field, or array element"));
+ }
+ analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2);
+ return g->builtin_types.entry_void;
+ }
+ case BinOpTypeBoolOr:
+ case BinOpTypeBoolAnd:
+ analyze_expression(g, import, context, g->builtin_types.entry_bool,
+ node->data.bin_op_expr.op1);
+ analyze_expression(g, import, context, g->builtin_types.entry_bool,
+ node->data.bin_op_expr.op2);
+ return g->builtin_types.entry_bool;
+ case BinOpTypeCmpEq:
+ case BinOpTypeCmpNotEq:
+ case BinOpTypeCmpLessThan:
+ case BinOpTypeCmpGreaterThan:
+ case BinOpTypeCmpLessOrEq:
+ case BinOpTypeCmpGreaterOrEq:
+ {
+ AstNode *op1 = node->data.bin_op_expr.op1;
+ AstNode *op2 = node->data.bin_op_expr.op2;
+ TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1);
+ TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, op2);
+ bool cmp_ok = false;
+ if (lhs_type->id == TypeTableEntryIdInvalid || rhs_type->id == TypeTableEntryIdInvalid) {
+ cmp_ok = true;
+ } else if (lhs_type->id == TypeTableEntryIdNumberLiteral ||
+ rhs_type->id == TypeTableEntryIdNumberLiteral)
+ {
+ cmp_ok = resolve_number_literals(g, op1, op2);
+ } else if (lhs_type->id == TypeTableEntryIdInt) {
+ if (rhs_type->id == TypeTableEntryIdInt &&
+ lhs_type->data.integral.is_signed == rhs_type->data.integral.is_signed &&
+ lhs_type->size_in_bits == rhs_type->size_in_bits)
+ {
+ cmp_ok = true;
+ }
+ } else if (lhs_type->id == TypeTableEntryIdFloat) {
+ if (rhs_type->id == TypeTableEntryIdFloat &&
+ lhs_type->size_in_bits == rhs_type->size_in_bits)
+ {
+ cmp_ok = true;
+ }
+ }
+ if (!cmp_ok) {
+ add_node_error(g, node, buf_sprintf("unable to compare '%s' with '%s'",
+ buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
+ }
+ return g->builtin_types.entry_bool;
+ }
+ case BinOpTypeBinOr:
+ case BinOpTypeBinXor:
+ case BinOpTypeBinAnd:
+ {
+ // TODO: don't require i32
+ analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
+ analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
+ return g->builtin_types.entry_i32;
+ }
+ case BinOpTypeBitShiftLeft:
+ case BinOpTypeBitShiftRight:
+ {
+ // TODO: don't require i32
+ analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
+ analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
+ return g->builtin_types.entry_i32;
+ }
+ case BinOpTypeAdd:
+ case BinOpTypeSub:
+ {
+ AstNode *op1 = node->data.bin_op_expr.op1;
+ AstNode *op2 = node->data.bin_op_expr.op2;
+ TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1);
+ TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, op2);
+
+ TypeTableEntry *return_type = nullptr;
+
+ if (lhs_type->id == TypeTableEntryIdInvalid || rhs_type->id == TypeTableEntryIdInvalid) {
+ return_type = g->builtin_types.entry_invalid;
+ } else if (lhs_type->id == TypeTableEntryIdNumberLiteral ||
+ rhs_type->id == TypeTableEntryIdNumberLiteral)
+ {
+ return_type = resolve_number_literals(g, op1, op2);
+ } else if (lhs_type->id == TypeTableEntryIdInt &&
+ lhs_type == rhs_type)
+ {
+ return_type = lhs_type;
+ } else if (lhs_type->id == TypeTableEntryIdFloat &&
+ lhs_type == rhs_type)
+ {
+ return_type = lhs_type;
+ }
+ if (!return_type) {
+ if (node->data.bin_op_expr.bin_op == BinOpTypeAdd) {
+ add_node_error(g, node, buf_sprintf("unable to add '%s' and '%s'",
+ buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
+ } else {
+ add_node_error(g, node, buf_sprintf("unable to subtract '%s' and '%s'",
+ buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
+ }
+ return g->builtin_types.entry_invalid;
+ }
+ return return_type;
+ }
+ case BinOpTypeMult:
+ case BinOpTypeDiv:
+ case BinOpTypeMod:
+ {
+ // TODO: don't require i32
+ analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
+ analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
+ return g->builtin_types.entry_i32;
+ }
+ case BinOpTypeInvalid:
+ zig_unreachable();
+ }
+ zig_unreachable();
+}
+
+static TypeTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+ TypeTableEntry *expected_type, AstNode *node)
+{
+ AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;
+
+ TypeTableEntry *explicit_type = nullptr;
+ if (variable_declaration->type != nullptr) {
+ explicit_type = resolve_type(g, variable_declaration->type);
+ if (explicit_type->id == TypeTableEntryIdUnreachable) {
+ add_node_error(g, variable_declaration->type,
+ buf_sprintf("variable of type 'unreachable' not allowed"));
+ explicit_type = g->builtin_types.entry_invalid;
+ }
+ }
+
+ TypeTableEntry *implicit_type = nullptr;
+ if (variable_declaration->expr != nullptr) {
+ implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr);
+ if (implicit_type->id == TypeTableEntryIdUnreachable) {
+ add_node_error(g, node,
+ buf_sprintf("variable initialization is unreachable"));
+ implicit_type = g->builtin_types.entry_invalid;
+ } else if (implicit_type->id == TypeTableEntryIdNumberLiteral) {
+ add_node_error(g, node,
+ buf_sprintf("unable to infer variable type"));
+ implicit_type = g->builtin_types.entry_invalid;
+ }
+ }
+
+ if (implicit_type == nullptr && variable_declaration->is_const) {
+ add_node_error(g, node, buf_sprintf("variables must have initial values or be declared 'mut'."));
+ implicit_type = g->builtin_types.entry_invalid;
+ }
+
+ TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type;
+ assert(type != nullptr); // should have been caught by the parser
+
+ LocalVariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol);
+ if (existing_variable) {
+ add_node_error(g, node,
+ buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol)));
+ } else {
+ LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1);
+ buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol);
+ variable_entry->type = type;
+ variable_entry->is_const = variable_declaration->is_const;
+ variable_entry->is_ptr = true;
+ variable_entry->decl_node = node;
+ context->variable_table.put(&variable_entry->name, variable_entry);
+ }
+ return g->builtin_types.entry_void;
+}
+
+static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+ TypeTableEntry *expected_type, AstNode *node)
+{
+ TypeTableEntry *num_lit_type = g->num_lit_types[node->data.number_literal.kind];
+ if (node->data.number_literal.overflow) {
+ add_node_error(g, node,
+ buf_sprintf("number literal too large to be represented in any type"));
+ return g->builtin_types.entry_invalid;
+ } else if (expected_type) {
+ if (expected_type->id == TypeTableEntryIdInvalid) {
+ return g->builtin_types.entry_invalid;
+ } else if (num_lit_fits_in_other_type(g, num_lit_type, expected_type)) {
+ NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node;
+ assert(!codegen_num_lit->resolved_type);
+ codegen_num_lit->resolved_type = expected_type;
+
+ return expected_type;
+ } else {
+ add_node_error(g, node, buf_sprintf("expected type '%s', got '%s'",
+ buf_ptr(&expected_type->name), buf_ptr(&num_lit_type->name)));
+ return g->builtin_types.entry_invalid;
+ }
+ } else {
+ return num_lit_type;
+ }
+}
+
static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
@@ -854,51 +1204,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
break;
}
case NodeTypeVariableDeclaration:
- {
- AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;;
-
- TypeTableEntry *explicit_type = nullptr;
- if (variable_declaration->type != nullptr) {
- explicit_type = resolve_type(g, variable_declaration->type);
- if (explicit_type->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, variable_declaration->type,
- buf_sprintf("variable of type 'unreachable' not allowed"));
- }
- }
-
- TypeTableEntry *implicit_type = nullptr;
- if (variable_declaration->expr != nullptr) {
- implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr);
- if (implicit_type->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, node,
- buf_sprintf("variable initialization is unreachable"));
- }
- }
-
- if (implicit_type == nullptr && variable_declaration->is_const) {
- add_node_error(g, node, buf_sprintf("variables must have initial values or be declared 'mut'."));
- }
-
- TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type;
- assert(type != nullptr); // should have been caught by the parser
-
- LocalVariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol);
- if (existing_variable) {
- add_node_error(g, node,
- buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol)));
- } else {
- LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1);
- buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol);
- variable_entry->type = type;
- variable_entry->is_const = variable_declaration->is_const;
- variable_entry->is_ptr = true;
- variable_entry->decl_node = node;
- context->variable_table.put(&variable_entry->name, variable_entry);
- }
- return_type = g->builtin_types.entry_void;
- break;
- }
-
+ return_type = analyze_variable_declaration(g, import, context, expected_type, node);
+ break;
case NodeTypeGoto:
{
FnTableEntry *fn_table_entry = get_context_fn_entry(context);
@@ -928,120 +1235,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
break;
}
case NodeTypeBinOpExpr:
- {
- switch (node->data.bin_op_expr.bin_op) {
- case BinOpTypeAssign:
- case BinOpTypeAssignTimes:
- case BinOpTypeAssignDiv:
- case BinOpTypeAssignMod:
- case BinOpTypeAssignPlus:
- case BinOpTypeAssignMinus:
- case BinOpTypeAssignBitShiftLeft:
- case BinOpTypeAssignBitShiftRight:
- case BinOpTypeAssignBitAnd:
- case BinOpTypeAssignBitXor:
- case BinOpTypeAssignBitOr:
- case BinOpTypeAssignBoolAnd:
- case BinOpTypeAssignBoolOr:
- {
- AstNode *lhs_node = node->data.bin_op_expr.op1;
- TypeTableEntry *expected_rhs_type = nullptr;
- if (lhs_node->type == NodeTypeSymbol) {
- Buf *name = &lhs_node->data.symbol;
- LocalVariableTableEntry *var = find_local_variable(context, name);
- if (var) {
- if (var->is_const) {
- add_node_error(g, lhs_node,
- buf_sprintf("cannot assign to constant variable"));
- } else {
- if (!is_op_allowed(var->type, node->data.bin_op_expr.bin_op)) {
- add_node_error(g, lhs_node,
- buf_sprintf("operator not allowed for type '%s'", buf_ptr(&var->type->name)));
- } else {
- expected_rhs_type = var->type;
- }
- }
- } else {
- add_node_error(g, lhs_node,
- buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name)));
- }
- } else if (lhs_node->type == NodeTypeArrayAccessExpr) {
- expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node);
- } else if (lhs_node->type == NodeTypeFieldAccessExpr) {
- alloc_codegen_node(lhs_node);
- expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node);
- } else {
- add_node_error(g, lhs_node,
- buf_sprintf("assignment target must be variable, field, or array element"));
- }
- analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2);
- return_type = g->builtin_types.entry_void;
- break;
- }
- case BinOpTypeBoolOr:
- case BinOpTypeBoolAnd:
- analyze_expression(g, import, context, g->builtin_types.entry_bool,
- node->data.bin_op_expr.op1);
- analyze_expression(g, import, context, g->builtin_types.entry_bool,
- node->data.bin_op_expr.op2);
- return_type = g->builtin_types.entry_bool;
- break;
- case BinOpTypeCmpEq:
- case BinOpTypeCmpNotEq:
- case BinOpTypeCmpLessThan:
- case BinOpTypeCmpGreaterThan:
- case BinOpTypeCmpLessOrEq:
- case BinOpTypeCmpGreaterOrEq:
- // TODO think how should type checking for these work?
- analyze_expression(g, import, context, g->builtin_types.entry_i32,
- node->data.bin_op_expr.op1);
- analyze_expression(g, import, context, g->builtin_types.entry_i32,
- node->data.bin_op_expr.op2);
- return_type = g->builtin_types.entry_bool;
- break;
- case BinOpTypeBinOr:
- case BinOpTypeBinXor:
- case BinOpTypeBinAnd:
- {
- // TODO: don't require i32
- analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
- analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
- return_type = g->builtin_types.entry_i32;
- break;
- }
- case BinOpTypeBitShiftLeft:
- case BinOpTypeBitShiftRight:
- {
- // TODO: don't require i32
- analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
- analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
- return_type = g->builtin_types.entry_i32;
- break;
- }
- case BinOpTypeAdd:
- case BinOpTypeSub:
- // TODO think how should type checking for these work?
- analyze_expression(g, import, context, g->builtin_types.entry_i32,
- node->data.bin_op_expr.op1);
- analyze_expression(g, import, context, g->builtin_types.entry_i32,
- node->data.bin_op_expr.op2);
- return_type = g->builtin_types.entry_i32;
- break;
- case BinOpTypeMult:
- case BinOpTypeDiv:
- case BinOpTypeMod:
- {
- // TODO: don't require i32
- analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
- analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
- return_type = g->builtin_types.entry_i32;
- break;
- }
- case BinOpTypeInvalid:
- zig_unreachable();
- }
- break;
- }
+ return_type = analyze_bin_op_expr(g, import, context, expected_type, node);
+ break;
case NodeTypeFnCallExpr:
{
@@ -1116,10 +1311,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
return_type = analyze_field_access_expr(g, import, context, node);
break;
case NodeTypeNumberLiteral:
- // TODO: generic literal int type
- return_type = g->builtin_types.entry_i32;
+ return_type = analyze_number_literal_expr(g, import, context, expected_type, node);
break;
-
case NodeTypeStringLiteral:
if (node->data.string_literal.c) {
return_type = g->builtin_types.entry_c_string_literal;
src/analyze.hpp
@@ -42,6 +42,10 @@ struct TypeTableEntryStruct {
TypeStructField *fields;
};
+struct TypeTableEntryNumLit {
+ NumLit kind;
+};
+
enum TypeTableEntryId {
TypeTableEntryIdInvalid,
TypeTableEntryIdVoid,
@@ -52,6 +56,7 @@ enum TypeTableEntryId {
TypeTableEntryIdPointer,
TypeTableEntryIdArray,
TypeTableEntryIdStruct,
+ TypeTableEntryIdNumberLiteral,
};
struct TypeTableEntry {
@@ -69,12 +74,13 @@ struct TypeTableEntry {
TypeTableEntryInt integral;
TypeTableEntryArray array;
TypeTableEntryStruct structure;
+ TypeTableEntryNumLit num_lit;
} data;
// use these fields to make sure we don't duplicate type table entries for the same type
TypeTableEntry *pointer_const_parent;
TypeTableEntry *pointer_mut_parent;
- HashMap<int, TypeTableEntry *, int_hash, int_eq> arrays_by_size;
+ HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size;
};
@@ -134,10 +140,13 @@ struct CodeGen {
struct {
TypeTableEntry *entry_bool;
TypeTableEntry *entry_u8;
+ TypeTableEntry *entry_u64;
TypeTableEntry *entry_i32;
+ TypeTableEntry *entry_i64;
TypeTableEntry *entry_isize;
TypeTableEntry *entry_usize;
TypeTableEntry *entry_f32;
+ TypeTableEntry *entry_f64;
TypeTableEntry *entry_c_string_literal;
TypeTableEntry *entry_string;
TypeTableEntry *entry_void;
@@ -145,6 +154,8 @@ struct CodeGen {
TypeTableEntry *entry_invalid;
} builtin_types;
+ TypeTableEntry *num_lit_types[NumLitCount];
+
LLVMTargetDataRef target_data_ref;
unsigned pointer_size_bytes;
bool is_static;
@@ -242,6 +253,7 @@ enum CastOp {
CastOpPtrToInt,
CastOpIntWidenOrShorten,
CastOpArrayToString,
+ CastOpNothing,
};
struct CastNode {
@@ -251,6 +263,10 @@ struct CastNode {
LLVMValueRef ptr;
};
+struct NumberLiteralNode {
+ TypeTableEntry *resolved_type;
+};
+
struct CodeGenNode {
union {
TypeNode type_node; // for NodeTypeType
@@ -262,6 +278,7 @@ struct CodeGenNode {
StructDeclNode struct_decl_node; // for NodeTypeStructDecl
FieldAccessNode field_access_node; // for NodeTypeFieldAccessExpr
CastNode cast_node; // for NodeTypeCastExpr
+ NumberLiteralNode num_lit_node; // for NodeTypeNumberLiteral
} data;
ExprNode expr_node; // for all the expression nodes
};
src/codegen.cpp
@@ -279,6 +279,8 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
CastNode *cast_node = &node->codegen_node->data.cast_node;
switch (cast_node->op) {
+ case CastOpNothing:
+ return expr_val;
case CastOpPtrToInt:
return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
case CastOpIntWidenOrShorten:
@@ -901,11 +903,29 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
return gen_asm_expr(g, node);
case NodeTypeNumberLiteral:
{
- Buf *number_str = &node->data.number;
- LLVMTypeRef number_type = LLVMInt32Type();
- LLVMValueRef number_val = LLVMConstIntOfStringAndSize(number_type,
- buf_ptr(number_str), buf_len(number_str), 10);
- return number_val;
+ NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node;
+ assert(codegen_num_lit);
+ TypeTableEntry *type_entry = codegen_num_lit->resolved_type;
+ assert(type_entry);
+
+ // TODO this is kinda iffy. make sure josh is on board with this
+ node->codegen_node->expr_node.type_entry = type_entry;
+
+ if (type_entry->id == TypeTableEntryIdInt) {
+ // here the union has int64_t and uint64_t and we purposefully read
+ // the uint64_t value in either case, because we want the twos
+ // complement representation
+
+ return LLVMConstInt(type_entry->type_ref,
+ node->data.number_literal.data.x_uint,
+ type_entry->data.integral.is_signed);
+ } else if (type_entry->id == TypeTableEntryIdFloat) {
+
+ return LLVMConstReal(type_entry->type_ref,
+ node->data.number_literal.data.x_float);
+ } else {
+ zig_panic("bad number literal type");
+ }
}
case NodeTypeStringLiteral:
{
@@ -1186,6 +1206,20 @@ static void do_code_gen(CodeGen *g) {
#endif
}
+static const NumLit num_lit_kinds[] = {
+ NumLitF32,
+ NumLitF64,
+ NumLitF128,
+ NumLitI8,
+ NumLitU8,
+ NumLitI16,
+ NumLitU16,
+ NumLitI32,
+ NumLitU32,
+ NumLitI64,
+ NumLitU64,
+};
+
static void define_builtin_types(CodeGen *g) {
{
// if this type is anywhere in the AST, we should never hit codegen.
@@ -1193,6 +1227,19 @@ static void define_builtin_types(CodeGen *g) {
buf_init_from_str(&entry->name, "(invalid)");
g->builtin_types.entry_invalid = entry;
}
+
+ assert(NumLitCount == array_length(num_lit_kinds));
+ for (int i = 0; i < NumLitCount; i += 1) {
+ NumLit num_lit_kind = num_lit_kinds[i];
+ // This type should just create a constant with whatever actual number
+ // type is expected at the time.
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumberLiteral);
+ buf_resize(&entry->name, 0);
+ buf_appendf(&entry->name, "(%s literal)", num_lit_str(num_lit_kind));
+ entry->data.num_lit.kind = num_lit_kind;
+ g->num_lit_types[i] = entry;
+ }
+
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool);
entry->type_ref = LLVMInt1Type();
@@ -1217,6 +1264,19 @@ static void define_builtin_types(CodeGen *g) {
g->type_table.put(&entry->name, entry);
g->builtin_types.entry_u8 = entry;
}
+ {
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
+ entry->type_ref = LLVMInt64Type();
+ buf_init_from_str(&entry->name, "u64");
+ entry->size_in_bits = 64;
+ entry->align_in_bits = 64;
+ entry->data.integral.is_signed = false;
+ entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
+ entry->size_in_bits, entry->align_in_bits,
+ LLVMZigEncoding_DW_ATE_unsigned());
+ g->type_table.put(&entry->name, entry);
+ g->builtin_types.entry_u64 = entry;
+ }
g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
@@ -1231,6 +1291,19 @@ static void define_builtin_types(CodeGen *g) {
g->type_table.put(&entry->name, entry);
g->builtin_types.entry_i32 = entry;
}
+ {
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
+ entry->type_ref = LLVMInt64Type();
+ buf_init_from_str(&entry->name, "i64");
+ entry->size_in_bits = 64;
+ entry->align_in_bits = 64;
+ entry->data.integral.is_signed = true;
+ entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
+ entry->size_in_bits, entry->align_in_bits,
+ LLVMZigEncoding_DW_ATE_signed());
+ g->type_table.put(&entry->name, entry);
+ g->builtin_types.entry_i64 = entry;
+ }
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8);
@@ -1269,6 +1342,18 @@ static void define_builtin_types(CodeGen *g) {
g->type_table.put(&entry->name, entry);
g->builtin_types.entry_f32 = entry;
}
+ {
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
+ entry->type_ref = LLVMFloatType();
+ buf_init_from_str(&entry->name, "f64");
+ entry->size_in_bits = 64;
+ entry->align_in_bits = 64;
+ entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
+ entry->size_in_bits, entry->align_in_bits,
+ LLVMZigEncoding_DW_ATE_float());
+ g->type_table.put(&entry->name, entry);
+ g->builtin_types.entry_f64 = entry;
+ }
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid);
entry->type_ref = LLVMVoidType();
src/parseh.cpp
@@ -544,7 +544,7 @@ void parse_h_file(const char *target_path, ZigList<const char *> *clang_argv, FI
char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS");
if (ZIG_PARSEH_CFLAGS) {
- Buf tmp_buf = {0};
+ Buf tmp_buf = BUF_INIT;
char *start = ZIG_PARSEH_CFLAGS;
char *space = strstr(start, " ");
while (space) {
src/parser.cpp
@@ -12,6 +12,7 @@
#include <stdarg.h>
#include <stdio.h>
+
static const char *bin_op_str(BinOpType bin_op) {
switch (bin_op) {
case BinOpTypeInvalid: return "(invalid)";
@@ -273,9 +274,19 @@ void ast_print(AstNode *node, int indent) {
ast_print(node->data.prefix_op_expr.primary_expr, indent + 2);
break;
case NodeTypeNumberLiteral:
- fprintf(stderr, "NumberLiteral %s\n",
- buf_ptr(&node->data.number));
- break;
+ {
+ NumLit num_lit = node->data.number_literal.kind;
+ const char *name = node_type_str(node->type);
+ const char *kind_str = num_lit_str(num_lit);
+ if (is_num_lit_signed(num_lit)) {
+ fprintf(stderr, "%s %s %" PRId64 "\n", name, kind_str, node->data.number_literal.data.x_int);
+ } else if (is_num_lit_unsigned(num_lit)) {
+ fprintf(stderr, "%s %s %" PRIu64 "\n", name, kind_str, node->data.number_literal.data.x_uint);
+ } else {
+ fprintf(stderr, "%s %s %f\n", name, kind_str, node->data.number_literal.data.x_float);
+ }
+ break;
+ }
case NodeTypeStringLiteral:
{
const char *c = node->data.string_literal.c ? "c" : "";
@@ -574,6 +585,186 @@ static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool
if (offset_map) offset_map->append(pos);
}
+enum ParseNumLitState {
+ ParseNumLitStateStart,
+ ParseNumLitStateBase,
+ ParseNumLitStateDigits,
+ ParseNumLitStateExpectFirstDigit,
+ ParseNumLitStateDecimal,
+ ParseNumLitStateESign,
+ ParseNumLitStateEDigit,
+};
+
+static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLiteral *num_lit) {
+ ParseNumLitState state = ParseNumLitStateStart;
+ unsigned long long base = 10;
+ bool negative = false;
+ int digits_start;
+ int digits_end;
+ int decimal_start = -1;
+ int decimal_end;
+ bool e_present = false;
+ bool e_positive;
+ int e_digit_start;
+ int e_digit_end;
+
+ for (int i = token->start_pos; i < token->end_pos; i += 1) {
+ uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i);
+ switch (state) {
+ case ParseNumLitStateStart:
+ if (c == '-') {
+ negative = true;
+ } else if (c == '0') {
+ state = ParseNumLitStateBase;
+ } else if (c >= '1' && c <= '9') {
+ digits_start = i;
+ state = ParseNumLitStateDigits;
+ } else {
+ zig_unreachable();
+ }
+ break;
+ case ParseNumLitStateBase:
+ if (c == 'x') {
+ base = 16;
+ state = ParseNumLitStateExpectFirstDigit;
+ } else if (c == 'o') {
+ base = 8;
+ state = ParseNumLitStateExpectFirstDigit;
+ } else if (c == 'b') {
+ base = 2;
+ state = ParseNumLitStateExpectFirstDigit;
+ } else {
+ zig_unreachable();
+ }
+ break;
+
+ case ParseNumLitStateExpectFirstDigit:
+ state = ParseNumLitStateDigits;
+ break;
+
+ case ParseNumLitStateDigits:
+ if (c == '.') {
+ assert(base == 10);
+ digits_end = i;
+ decimal_start = i + 1;
+ state = ParseNumLitStateDecimal;
+ }
+ break;
+ case ParseNumLitStateDecimal:
+ if (c == 'E') {
+ e_present = false;
+ decimal_end = i;
+ state = ParseNumLitStateESign;
+ }
+ break;
+ case ParseNumLitStateESign:
+ if (c == '+') {
+ e_positive = true;
+ e_digit_start = i + 1;
+ state = ParseNumLitStateEDigit;
+ } else if (c == '-') {
+ e_positive = false;
+ e_digit_start = i + 1;
+ state = ParseNumLitStateEDigit;
+ } else {
+ zig_unreachable();
+ }
+ break;
+ case ParseNumLitStateEDigit:
+ assert(c >= '0' && c <= '9');
+ break;
+ }
+ }
+
+ switch (state) {
+ case ParseNumLitStateDigits:
+ digits_end = token->end_pos;
+ break;
+ case ParseNumLitStateDecimal:
+ decimal_end = token->end_pos;
+ break;
+ case ParseNumLitStateEDigit:
+ e_digit_end = token->end_pos;
+ break;
+ case ParseNumLitStateBase:
+ num_lit->kind = NumLitU8;
+ num_lit->data.x_uint = 0;
+ return;
+ case ParseNumLitStateESign:
+ case ParseNumLitStateExpectFirstDigit:
+ case ParseNumLitStateStart:
+ zig_unreachable();
+ }
+
+ if (decimal_start >= 0) {
+ // float
+ double x;
+
+ (void)x;
+ zig_panic("TODO parse float");
+ } else {
+ // integer
+ unsigned long long x = 0;
+
+ unsigned long long mult = 1;
+ for (int i = digits_end - 1; ; i -= 1) {
+ uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i);
+ unsigned long long digit = (c - '0');
+
+ // digit *= mult
+ if (__builtin_umulll_overflow(digit, mult, &digit)) {
+ num_lit->overflow = true;
+ return;
+ }
+
+ // x += digit
+ if (__builtin_uaddll_overflow(x, digit, &x)) {
+ num_lit->overflow = true;
+ return;
+ }
+
+ if (i == digits_start)
+ break;
+
+ // mult *= base
+ if (__builtin_umulll_overflow(mult, base, &mult)) {
+ num_lit->overflow = true;
+ return;
+ }
+ }
+
+ if (negative) {
+ if (x <= 128ull) {
+ num_lit->kind = NumLitI8;
+ } else if (x <= 32768ull) {
+ num_lit->kind = NumLitI16;
+ } else if (x <= 2147483648ull) {
+ num_lit->kind = NumLitI32;
+ } else if (x <= 9223372036854775808ull) {
+ num_lit->kind = NumLitI64;
+ } else {
+ num_lit->overflow = true;
+ return;
+ }
+
+ num_lit->data.x_int = -((int64_t)x);
+ } else {
+ num_lit->data.x_uint = x;
+
+ if (x <= UINT8_MAX) {
+ num_lit->kind = NumLitU8;
+ } else if (x <= UINT16_MAX) {
+ num_lit->kind = NumLitU16;
+ } else if (x <= UINT32_MAX) {
+ num_lit->kind = NumLitU32;
+ } else {
+ num_lit->kind = NumLitU64;
+ }
+ }
+ }
+}
+
+
__attribute__ ((noreturn))
static void ast_invalid_token_error(ParseContext *pc, Token *token) {
Buf token_value = BUF_INIT;
@@ -829,7 +1020,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
if (token->id == TokenIdNumberLiteral) {
AstNode *node = ast_create_node(pc, NodeTypeNumberLiteral, token);
- ast_buf_from_token(pc, token, &node->data.number);
+ parse_number_literal(pc, token, &node->data.number_literal);
*token_index += 1;
return node;
} else if (token->id == TokenIdStringLiteral) {
@@ -2152,3 +2343,121 @@ AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner, Er
pc.root = ast_parse_root(&pc, &token_index);
return pc.root;
}
+
+const char *num_lit_str(NumLit num_lit) {
+ switch (num_lit) {
+ case NumLitF32:
+ return "f32";
+ case NumLitF64:
+ return "f64";
+ case NumLitF128:
+ return "f128";
+ case NumLitI8:
+ return "i8";
+ case NumLitI16:
+ return "i16";
+ case NumLitI32:
+ return "i32";
+ case NumLitI64:
+ return "i64";
+ case NumLitU8:
+ return "u8";
+ case NumLitU16:
+ return "u16";
+ case NumLitU32:
+ return "u32";
+ case NumLitU64:
+ return "u64";
+ case NumLitCount:
+ zig_unreachable();
+ }
+ zig_unreachable();
+}
+
+bool is_num_lit_signed(NumLit num_lit) {
+ switch (num_lit) {
+ case NumLitI8:
+ case NumLitI16:
+ case NumLitI32:
+ case NumLitI64:
+ return true;
+
+ case NumLitF32:
+ case NumLitF64:
+ case NumLitF128:
+ case NumLitU8:
+ case NumLitU16:
+ case NumLitU32:
+ case NumLitU64:
+ return false;
+ case NumLitCount:
+ zig_unreachable();
+ }
+ zig_unreachable();
+}
+
+bool is_num_lit_unsigned(NumLit num_lit) {
+ switch (num_lit) {
+ case NumLitF32:
+ case NumLitF64:
+ case NumLitF128:
+ case NumLitI8:
+ case NumLitI16:
+ case NumLitI32:
+ case NumLitI64:
+ return false;
+ case NumLitU8:
+ case NumLitU16:
+ case NumLitU32:
+ case NumLitU64:
+ return true;
+ case NumLitCount:
+ zig_unreachable();
+ }
+ zig_unreachable();
+}
+
+bool is_num_lit_float(NumLit num_lit) {
+ switch (num_lit) {
+ case NumLitF32:
+ case NumLitF64:
+ case NumLitF128:
+ return true;
+ case NumLitI8:
+ case NumLitI16:
+ case NumLitI32:
+ case NumLitI64:
+ case NumLitU8:
+ case NumLitU16:
+ case NumLitU32:
+ case NumLitU64:
+ return false;
+ case NumLitCount:
+ zig_unreachable();
+ }
+ zig_unreachable();
+}
+
+uint64_t num_lit_bit_count(NumLit num_lit) {
+ switch (num_lit) {
+ case NumLitI8:
+ case NumLitU8:
+ return 8;
+ case NumLitI16:
+ case NumLitU16:
+ return 16;
+ case NumLitI32:
+ case NumLitU32:
+ case NumLitF32:
+ return 32;
+ case NumLitI64:
+ case NumLitU64:
+ case NumLitF64:
+ return 64;
+ case NumLitF128:
+ return 128;
+ case NumLitCount:
+ zig_unreachable();
+ }
+ zig_unreachable();
+}
src/parser.hpp
@@ -267,6 +267,36 @@ struct AstNodeStringLiteral {
bool c;
};
+enum NumLit {
+ NumLitF32,
+ NumLitF64,
+ NumLitF128,
+ NumLitI8,
+ NumLitU8,
+ NumLitI16,
+ NumLitU16,
+ NumLitI32,
+ NumLitU32,
+ NumLitI64,
+ NumLitU64,
+
+ NumLitCount
+};
+
+struct AstNodeNumberLiteral {
+ NumLit kind;
+
+ // overflow is true if when parsing the number, we discovered it would not
+ // fit without losing data in a uint64_t, int64_t, or double
+ bool overflow;
+
+ union {
+ uint64_t x_uint;
+ int64_t x_int;
+ double x_float;
+ } data;
+};
+
struct AstNode {
enum NodeType type;
int line;
@@ -300,7 +330,7 @@ struct AstNode {
AstNodeStructDecl struct_decl;
AstNodeStructField struct_field;
AstNodeStringLiteral string_literal;
- Buf number;
+ AstNodeNumberLiteral number_literal;
Buf symbol;
bool bool_literal;
} data;
@@ -329,4 +359,11 @@ const char *node_type_str(NodeType node_type);
void ast_print(AstNode *node, int indent);
+const char *num_lit_str(NumLit num_lit);
+bool is_num_lit_signed(NumLit num_lit);
+bool is_num_lit_unsigned(NumLit num_lit);
+bool is_num_lit_float(NumLit num_lit);
+uint64_t num_lit_bit_count(NumLit num_lit);
+
+
#endif
src/util.cpp
@@ -21,8 +21,16 @@ void zig_panic(const char *format, ...) {
}
uint32_t int_hash(int i) {
- return *reinterpret_cast<uint32_t*>(&i);
+ return (uint32_t)(i % UINT32_MAX);
}
bool int_eq(int a, int b) {
return a == b;
}
+
+uint32_t uint64_hash(uint64_t i) {
+ return (uint32_t)(i % UINT32_MAX);
+}
+
+bool uint64_eq(uint64_t a, uint64_t b) {
+ return a == b;
+}
src/util.hpp
@@ -81,5 +81,7 @@ static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str)
uint32_t int_hash(int i);
bool int_eq(int a, int b);
+uint32_t uint64_hash(uint64_t i);
+bool uint64_eq(uint64_t a, uint64_t b);
#endif
std/std.zig
@@ -15,10 +15,9 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize {
// TODO error handling
// TODO handle buffering and flushing
-// TODO non-i32 integer literals so we can remove the casts
// TODO constants for SYS_write and stdout_fileno
pub fn print_str(str : string) -> isize {
- let SYS_write = 1;
- let stdout_fileno = 1;
- return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize);
+ let SYS_write : isize = 1;
+ let stdout_fileno : isize = 1;
+ return syscall3(SYS_write, stdout_fileno, str.ptr as isize, str.len as isize);
}
test/run_tests.cpp
@@ -266,7 +266,7 @@ extern {
export fn _start() -> unreachable {
let a : i32 = 1;
- let b = 2;
+ let b = 2 as i32;
if (a + b == 3) {
puts(c"OK");
}
@@ -299,12 +299,12 @@ extern {
export fn _start() -> unreachable {
if (true) {
- let no_conflict = 5;
+ let no_conflict : i32 = 5;
if (no_conflict == 5) { puts(c"OK 1"); }
}
let c = {
- let no_conflict = 10;
+ let no_conflict = 10 as i32;
no_conflict
};
if (c == 10) { puts(c"OK 2"); }
@@ -343,7 +343,7 @@ export fn _start() -> unreachable {
let mut zero : i32;
if (zero == 0) { puts(c"zero"); }
- let mut i = 0;
+ let mut i = 0 as i32;
loop_start:
if i == 3 {
goto done;
@@ -366,7 +366,7 @@ extern {
export fn _start() -> unreachable {
let mut array : [i32; 5];
- let mut i = 0;
+ let mut i : i32 = 0;
loop_start:
if i == 5 {
goto loop_end;
@@ -378,7 +378,7 @@ loop_start:
loop_end:
i = 0;
- let mut accumulator = 0;
+ let mut accumulator = 0 as i32;
loop_2_start:
if i == 5 {
goto loop_2_end;
@@ -611,7 +611,7 @@ fn f() -> i32 {
fn f() {
if (0) {}
}
- )SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got 'i32'");
+ )SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got '(u8 literal)'");
add_compile_fail_case("assign unreachable", R"SOURCE(
fn f() {
@@ -685,13 +685,12 @@ fn f(...) {}
}
-static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) {
+static void print_compiler_invocation(TestCase *test_case) {
printf("%s", zig_exe);
for (int i = 0; i < test_case->compiler_args.length; i += 1) {
printf(" %s", test_case->compiler_args.at(i));
}
printf("\n");
- printf("%s\n", buf_ptr(zig_stderr));
}
static void run_test(TestCase *test_case) {
@@ -716,21 +715,24 @@ static void run_test(TestCase *test_case) {
printf("========= Expected this compile error: =========\n");
printf("%s\n", err_text);
printf("================================================\n");
- print_compiler_invocation(test_case, &zig_stderr);
+ print_compiler_invocation(test_case);
+ printf("%s\n", buf_ptr(&zig_stderr));
exit(1);
}
}
return; // success
} else {
printf("\nCompile failed with return code 0 (Expected failure):\n");
- print_compiler_invocation(test_case, &zig_stderr);
+ print_compiler_invocation(test_case);
+ printf("%s\n", buf_ptr(&zig_stderr));
exit(1);
}
}
if (return_code != 0) {
printf("\nCompile failed with return code %d:\n", return_code);
- print_compiler_invocation(test_case, &zig_stderr);
+ print_compiler_invocation(test_case);
+ printf("%s\n", buf_ptr(&zig_stderr));
exit(1);
}
@@ -740,6 +742,7 @@ static void run_test(TestCase *test_case) {
if (return_code != 0) {
printf("\nProgram exited with return code %d:\n", return_code);
+ print_compiler_invocation(test_case);
printf("%s", tmp_exe_path);
for (int i = 0; i < test_case->program_args.length; i += 1) {
printf(" %s", test_case->program_args.at(i));
@@ -750,6 +753,12 @@ static void run_test(TestCase *test_case) {
}
if (!buf_eql_str(&program_stdout, test_case->output)) {
+ printf("\n");
+ print_compiler_invocation(test_case);
+ printf("%s", tmp_exe_path);
+ for (int i = 0; i < test_case->program_args.length; i += 1) {
+ printf(" %s", test_case->program_args.at(i));
+ }
printf("\n");
printf("==== Test failed. Expected output: ====\n");
printf("%s\n", test_case->output);