mirror of
https://github.com/ziglang/zig.git
synced 2025-02-04 20:02:47 +00:00
add number literal type
it gets implicitly casted to whatever is needed. closes #24
This commit is contained in:
parent
3d8eb10897
commit
e411467e1d
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -7,6 +7,6 @@ extern {
|
||||
}
|
||||
|
||||
export fn _start() -> unreachable {
|
||||
printf("Hello, world!\n");
|
||||
printf(c"Hello, world!\n");
|
||||
exit(0);
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
547
src/analyze.cpp
547
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;
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
317
src/parser.cpp
317
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();
|
||||
}
|
||||
|
@ -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
|
||||
|
10
src/util.cpp
10
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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user