mirror of
https://github.com/ziglang/zig.git
synced 2024-11-14 16:13:24 +00:00
prepare codebase for struct and string support
parsing code for structs, strings, and c string literals partial semantic analyzing code for structs, strings, and c string literals
This commit is contained in:
parent
4c16eaa640
commit
a10277bd94
@ -32,7 +32,11 @@ zig | C equivalent | Description
|
||||
```
|
||||
Root : many(TopLevelDecl) token(EOF)
|
||||
|
||||
TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use
|
||||
TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use | StructDecl
|
||||
|
||||
StructDecl : many(Directive) token(Struct) token(Symbol) token(LBrace) many(StructField) token(RBrace)
|
||||
|
||||
StructField : token(Symbol) token(Colon) Type token(Comma)
|
||||
|
||||
Use : many(Directive) token(Use) token(String) token(Semicolon)
|
||||
|
||||
@ -126,7 +130,9 @@ CastExpression : PrefixOpExpression token(as) Type | PrefixOpExpression
|
||||
|
||||
PrefixOpExpression : PrefixOp SuffixOpExpression | SuffixOpExpression
|
||||
|
||||
SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression)
|
||||
SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression)
|
||||
|
||||
FieldAccessExpression : token(Dot) token(Symbol)
|
||||
|
||||
FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
|
||||
|
||||
@ -146,7 +152,7 @@ KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False)
|
||||
## Operator Precedence
|
||||
|
||||
```
|
||||
x() x[]
|
||||
x() x[] x.y
|
||||
!x -x ~x
|
||||
as
|
||||
* / %
|
||||
@ -165,11 +171,11 @@ as
|
||||
|
||||
### Characters and Strings
|
||||
|
||||
| Example | Characters | Escapes | Null Terminated
|
||||
-------------------------------------------------------------------------------
|
||||
Byte | 'H' | All ASCII | Byte | No
|
||||
UTF-8 Bytes | "hello" | All Unicode | Byte & Unicode | No
|
||||
UTF-8 C string | c"hello" | All Unicode | Byte & Unicode | Yes
|
||||
| Example | Characters | Escapes | Null Term | Type
|
||||
---------------------------------------------------------------------------------
|
||||
Byte | 'H' | All ASCII | Byte | No | u8
|
||||
UTF-8 Bytes | "hello" | All Unicode | Byte & Unicode | No | [5; u8]
|
||||
UTF-8 C string | c"hello" | All Unicode | Byte & Unicode | Yes | *const u8
|
||||
|
||||
### Byte Escapes
|
||||
|
||||
|
@ -9,7 +9,7 @@ endif
|
||||
|
||||
syn keyword zigKeyword fn return mut const extern unreachable export pub as use while asm
|
||||
syn keyword zigKeyword if else let void goto type enum struct continue break match volatile
|
||||
syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128
|
||||
syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 string
|
||||
|
||||
syn keyword zigConstant null
|
||||
|
||||
|
@ -3,6 +3,6 @@ export executable "hello";
|
||||
use "std.zig";
|
||||
|
||||
export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 {
|
||||
print_str("Hello, world!\n", 14 as isize);
|
||||
print_str("Hello, world!\n");
|
||||
return 0;
|
||||
}
|
||||
|
27
example/structs/structs.zig
Normal file
27
example/structs/structs.zig
Normal file
@ -0,0 +1,27 @@
|
||||
export executable "structs";
|
||||
|
||||
use "std.zig";
|
||||
|
||||
export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 {
|
||||
let mut foo : Foo;
|
||||
|
||||
foo.a = foo.a + 1;
|
||||
|
||||
foo.b = foo.a == 1;
|
||||
|
||||
test_foo(foo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
a : i32,
|
||||
b : bool,
|
||||
c : f32,
|
||||
}
|
||||
|
||||
fn test_foo(foo : Foo) {
|
||||
if foo.b {
|
||||
print_str("OK");
|
||||
}
|
||||
}
|
222
src/analyze.cpp
222
src/analyze.cpp
@ -13,6 +13,11 @@
|
||||
static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
TypeTableEntry *expected_type, AstNode *node);
|
||||
|
||||
static void alloc_codegen_node(AstNode *node) {
|
||||
assert(!node->codegen_node);
|
||||
node->codegen_node = allocate<CodeGenNode>(1);
|
||||
}
|
||||
|
||||
static AstNode *first_executing_node(AstNode *node) {
|
||||
switch (node->type) {
|
||||
case NodeTypeFnCallExpr:
|
||||
@ -44,6 +49,9 @@ static AstNode *first_executing_node(AstNode *node) {
|
||||
case NodeTypeLabel:
|
||||
case NodeTypeGoto:
|
||||
case NodeTypeAsmExpr:
|
||||
case NodeTypeFieldAccessExpr:
|
||||
case NodeTypeStructDecl:
|
||||
case NodeTypeStructField:
|
||||
return node;
|
||||
}
|
||||
zig_panic("unreachable");
|
||||
@ -129,6 +137,7 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in
|
||||
entry->di_type = LLVMZigCreateDebugArrayType(g->dbuilder, entry->size_in_bits,
|
||||
entry->align_in_bits, child_type->di_type, array_size);
|
||||
entry->data.array.child_type = child_type;
|
||||
entry->data.array.len = array_size;
|
||||
|
||||
g->type_table.put(&entry->name, entry);
|
||||
child_type->arrays_by_size.put(array_size, entry);
|
||||
@ -143,8 +152,7 @@ static int parse_int(Buf *number) {
|
||||
|
||||
static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeType);
|
||||
assert(!node->codegen_node);
|
||||
node->codegen_node = allocate<CodeGenNode>(1);
|
||||
alloc_codegen_node(node);
|
||||
TypeNode *type_node = &node->codegen_node->data.type_node;
|
||||
switch (node->data.type.type) {
|
||||
case AstNodeTypeTypePrimitive:
|
||||
@ -259,8 +267,7 @@ static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_
|
||||
Buf *name = &label_node->data.label.name;
|
||||
fn_table_entry->label_table.put(name, label_entry);
|
||||
|
||||
assert(!label_node->codegen_node);
|
||||
label_node->codegen_node = allocate<CodeGenNode>(1);
|
||||
alloc_codegen_node(label_node);
|
||||
label_node->codegen_node->data.label_entry = label_entry;
|
||||
}
|
||||
}
|
||||
@ -302,8 +309,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
||||
g->fn_table.put(name, fn_table_entry);
|
||||
}
|
||||
|
||||
assert(!fn_proto->codegen_node);
|
||||
fn_proto->codegen_node = allocate<CodeGenNode>(1);
|
||||
alloc_codegen_node(fn_proto);
|
||||
fn_proto->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry;
|
||||
}
|
||||
break;
|
||||
@ -319,8 +325,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
||||
if (entry) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
|
||||
assert(!node->codegen_node);
|
||||
node->codegen_node = allocate<CodeGenNode>(1);
|
||||
alloc_codegen_node(node);
|
||||
node->codegen_node->data.fn_def_node.skip = true;
|
||||
skip = true;
|
||||
} else if (is_pub) {
|
||||
@ -328,8 +333,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
||||
if (entry) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
|
||||
assert(!node->codegen_node);
|
||||
node->codegen_node = allocate<CodeGenNode>(1);
|
||||
alloc_codegen_node(node);
|
||||
node->codegen_node->data.fn_def_node.skip = true;
|
||||
skip = true;
|
||||
}
|
||||
@ -358,8 +362,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
||||
resolve_function_proto(g, proto_node, fn_table_entry);
|
||||
|
||||
|
||||
assert(!proto_node->codegen_node);
|
||||
proto_node->codegen_node = allocate<CodeGenNode>(1);
|
||||
alloc_codegen_node(proto_node);
|
||||
proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry;
|
||||
|
||||
preview_function_labels(g, node->data.fn_def.body, fn_table_entry);
|
||||
@ -409,6 +412,31 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
||||
buf_sprintf("root export declaration only valid in root source file"));
|
||||
}
|
||||
break;
|
||||
case NodeTypeStructDecl:
|
||||
{
|
||||
StructDeclNode *struct_codegen = &node->codegen_node->data.struct_decl_node;
|
||||
TypeTableEntry *type_entry = struct_codegen->type_entry;
|
||||
|
||||
int field_count = node->data.struct_decl.fields.length;;
|
||||
type_entry->data.structure.field_count = field_count;
|
||||
type_entry->data.structure.fields = allocate<TypeStructField>(field_count);
|
||||
|
||||
LLVMTypeRef *element_types = allocate<LLVMTypeRef>(field_count);
|
||||
|
||||
for (int i = 0; i < field_count; i += 1) {
|
||||
AstNode *field_node = node->data.struct_decl.fields.at(i);
|
||||
TypeStructField *type_struct_field = &type_entry->data.structure.fields[i];
|
||||
type_struct_field->name = &field_node->data.struct_field.name;
|
||||
type_struct_field->type_entry = resolve_type(g, field_node->data.struct_field.type);
|
||||
|
||||
element_types[i] = type_struct_field->type_entry->type_ref;
|
||||
}
|
||||
// TODO align_in_bits and size_in_bits
|
||||
// TODO set up ditype for the struct
|
||||
LLVMStructSetBody(type_entry->type_ref, element_types, field_count, false);
|
||||
|
||||
break;
|
||||
}
|
||||
case NodeTypeUse:
|
||||
// nothing to do here
|
||||
break;
|
||||
@ -436,6 +464,68 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
|
||||
case NodeTypeLabel:
|
||||
case NodeTypeGoto:
|
||||
case NodeTypeAsmExpr:
|
||||
case NodeTypeFieldAccessExpr:
|
||||
case NodeTypeStructField:
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) {
|
||||
switch (node->type) {
|
||||
case NodeTypeStructDecl:
|
||||
{
|
||||
alloc_codegen_node(node);
|
||||
StructDeclNode *struct_codegen = &node->codegen_node->data.struct_decl_node;
|
||||
|
||||
Buf *name = &node->data.struct_decl.name;
|
||||
auto table_entry = g->type_table.maybe_get(name);
|
||||
if (table_entry) {
|
||||
struct_codegen->type_entry = table_entry->value;
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("redefinition of '%s'", buf_ptr(name)));
|
||||
} else {
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct);
|
||||
entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(name));
|
||||
buf_init_from_buf(&entry->name, name);
|
||||
// put off adding the debug type until we do the full struct body
|
||||
// this type is incomplete until we do another pass
|
||||
g->type_table.put(&entry->name, entry);
|
||||
struct_codegen->type_entry = entry;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeTypeExternBlock:
|
||||
case NodeTypeFnDef:
|
||||
case NodeTypeRootExportDecl:
|
||||
case NodeTypeUse:
|
||||
// nothing to do
|
||||
break;
|
||||
case NodeTypeDirective:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeFnProto:
|
||||
case NodeTypeType:
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeReturnExpr:
|
||||
case NodeTypeVariableDeclaration:
|
||||
case NodeTypeRoot:
|
||||
case NodeTypeBlock:
|
||||
case NodeTypeBinOpExpr:
|
||||
case NodeTypeFnCallExpr:
|
||||
case NodeTypeArrayAccessExpr:
|
||||
case NodeTypeNumberLiteral:
|
||||
case NodeTypeStringLiteral:
|
||||
case NodeTypeUnreachable:
|
||||
case NodeTypeVoid:
|
||||
case NodeTypeBoolLiteral:
|
||||
case NodeTypeSymbol:
|
||||
case NodeTypeCastExpr:
|
||||
case NodeTypePrefixOpExpr:
|
||||
case NodeTypeIfExpr:
|
||||
case NodeTypeLabel:
|
||||
case NodeTypeGoto:
|
||||
case NodeTypeAsmExpr:
|
||||
case NodeTypeFieldAccessExpr:
|
||||
case NodeTypeStructField:
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
@ -460,7 +550,9 @@ static FnTableEntry *get_context_fn_entry(BlockContext *context) {
|
||||
return fn_proto_node->codegen_node->data.fn_proto_node.fn_table_entry;
|
||||
}
|
||||
|
||||
static void check_type_compatibility(CodeGen *g, AstNode *node, TypeTableEntry *expected_type, TypeTableEntry *actual_type) {
|
||||
static void check_type_compatibility(CodeGen *g, AstNode *node,
|
||||
TypeTableEntry *expected_type, TypeTableEntry *actual_type)
|
||||
{
|
||||
if (expected_type == nullptr)
|
||||
return; // anything will do
|
||||
if (expected_type == actual_type)
|
||||
@ -471,7 +563,7 @@ static void check_type_compatibility(CodeGen *g, AstNode *node, TypeTableEntry *
|
||||
return; // sorry toots; gotta run. good luck with that expected type.
|
||||
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("type mismatch. expected %s. got %s",
|
||||
buf_sprintf("expected type '%s', got '%s'",
|
||||
buf_ptr(&expected_type->name),
|
||||
buf_ptr(&actual_type->name)));
|
||||
}
|
||||
@ -507,6 +599,55 @@ LocalVariableTableEntry *find_local_variable(BlockContext *context, Buf *name) {
|
||||
}
|
||||
}
|
||||
|
||||
static TypeStructField *get_struct_field(TypeTableEntry *struct_type, Buf *name) {
|
||||
for (int i = 0; i < struct_type->data.structure.field_count; i += 1) {
|
||||
TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
|
||||
if (buf_eql_buf(type_struct_field->name, name)) {
|
||||
return type_struct_field;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
AstNode *node)
|
||||
{
|
||||
TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr,
|
||||
node->data.field_access_expr.struct_expr);
|
||||
|
||||
TypeTableEntry *return_type;
|
||||
|
||||
if (struct_type->id == TypeTableEntryIdStruct) {
|
||||
Buf *field_name = &node->data.field_access_expr.field_name;
|
||||
TypeStructField *type_struct_field = get_struct_field(struct_type, field_name);
|
||||
if (type_struct_field) {
|
||||
return_type = type_struct_field->type_entry;
|
||||
} else {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&struct_type->name)));
|
||||
return_type = g->builtin_types.entry_invalid;
|
||||
}
|
||||
} else if (struct_type->id == TypeTableEntryIdArray) {
|
||||
Buf *name = &node->data.field_access_expr.field_name;
|
||||
if (buf_eql_str(name, "len")) {
|
||||
return_type = g->builtin_types.entry_usize;
|
||||
} else {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("no member named '%s' in '%s'", buf_ptr(name),
|
||||
buf_ptr(&struct_type->name)));
|
||||
return_type = g->builtin_types.entry_invalid;
|
||||
}
|
||||
} else {
|
||||
if (struct_type->id != TypeTableEntryIdInvalid) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name)));
|
||||
}
|
||||
return_type = g->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
return return_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
AstNode *node)
|
||||
{
|
||||
@ -554,8 +695,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
TypeTableEntry *expected_type, AstNode *node)
|
||||
{
|
||||
TypeTableEntry *return_type = nullptr;
|
||||
assert(!node->codegen_node);
|
||||
node->codegen_node = allocate<CodeGenNode>(1);
|
||||
alloc_codegen_node(node);
|
||||
switch (node->type) {
|
||||
case NodeTypeBlock:
|
||||
{
|
||||
@ -698,9 +838,11 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
}
|
||||
} else if (lhs_node->type == NodeTypeArrayAccessExpr) {
|
||||
expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node);
|
||||
} else if (lhs_node->type == NodeTypeFieldAccessExpr) {
|
||||
expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node);
|
||||
} else {
|
||||
add_node_error(g, lhs_node,
|
||||
buf_sprintf("expected a bare identifier"));
|
||||
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;
|
||||
@ -835,15 +977,21 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
// for reading array access; assignment handled elsewhere
|
||||
return_type = analyze_array_access_expr(g, import, context, node);
|
||||
break;
|
||||
case NodeTypeFieldAccessExpr:
|
||||
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;
|
||||
break;
|
||||
|
||||
case NodeTypeStringLiteral:
|
||||
return_type = g->builtin_types.entry_string_literal;
|
||||
if (node->data.string_literal.c) {
|
||||
return_type = g->builtin_types.entry_c_string_literal;
|
||||
} else {
|
||||
return_type = get_array_type(g, g->builtin_types.entry_u8, buf_len(&node->data.string_literal.buf));
|
||||
}
|
||||
break;
|
||||
|
||||
case NodeTypeUnreachable:
|
||||
return_type = g->builtin_types.entry_unreachable;
|
||||
break;
|
||||
@ -884,7 +1032,10 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
{
|
||||
return_type = wanted_type;
|
||||
} else {
|
||||
zig_panic("TODO analyze_expression cast expr");
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("TODO handle cast from '%s' to '%s'",
|
||||
buf_ptr(&actual_type->name), buf_ptr(&wanted_type->name)));
|
||||
return_type = g->builtin_types.entry_invalid;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -946,6 +1097,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
case NodeTypeFnDef:
|
||||
case NodeTypeUse:
|
||||
case NodeTypeLabel:
|
||||
case NodeTypeStructDecl:
|
||||
case NodeTypeStructField:
|
||||
zig_unreachable();
|
||||
}
|
||||
assert(return_type);
|
||||
@ -970,8 +1123,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
|
||||
AstNode *fn_proto_node = node->data.fn_def.fn_proto;
|
||||
assert(fn_proto_node->type == NodeTypeFnProto);
|
||||
|
||||
assert(!node->codegen_node);
|
||||
node->codegen_node = allocate<CodeGenNode>(1);
|
||||
alloc_codegen_node(node);
|
||||
BlockContext *context = new_block_context(node, nullptr);
|
||||
node->codegen_node->data.fn_def_node.block_context = context;
|
||||
|
||||
@ -1044,6 +1196,9 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
|
||||
buf_sprintf("invalid directive: '%s'", buf_ptr(name)));
|
||||
}
|
||||
break;
|
||||
case NodeTypeStructDecl:
|
||||
// nothing to do
|
||||
break;
|
||||
case NodeTypeDirective:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeFnProto:
|
||||
@ -1068,6 +1223,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
|
||||
case NodeTypeLabel:
|
||||
case NodeTypeGoto:
|
||||
case NodeTypeAsmExpr:
|
||||
case NodeTypeFieldAccessExpr:
|
||||
case NodeTypeStructField:
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
@ -1082,6 +1239,16 @@ static void find_function_declarations_root(CodeGen *g, ImportTableEntry *import
|
||||
|
||||
}
|
||||
|
||||
static void preview_types_root(CodeGen *g, ImportTableEntry *import, AstNode *node) {
|
||||
assert(node->type == NodeTypeRoot);
|
||||
|
||||
for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) {
|
||||
AstNode *child = node->data.root.top_level_decls.at(i);
|
||||
preview_types(g, import, child);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, AstNode *node) {
|
||||
assert(node->type == NodeTypeRoot);
|
||||
|
||||
@ -1092,6 +1259,17 @@ static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, A
|
||||
}
|
||||
|
||||
void semantic_analyze(CodeGen *g) {
|
||||
{
|
||||
auto it = g->import_table.entry_iterator();
|
||||
for (;;) {
|
||||
auto *entry = it.next();
|
||||
if (!entry)
|
||||
break;
|
||||
|
||||
ImportTableEntry *import = entry->value;
|
||||
preview_types_root(g, import, import->root);
|
||||
}
|
||||
}
|
||||
{
|
||||
auto it = g->import_table.entry_iterator();
|
||||
for (;;) {
|
||||
|
@ -28,6 +28,18 @@ struct TypeTableEntryInt {
|
||||
|
||||
struct TypeTableEntryArray {
|
||||
TypeTableEntry *child_type;
|
||||
uint64_t len;
|
||||
};
|
||||
|
||||
struct TypeStructField {
|
||||
Buf *name;
|
||||
TypeTableEntry *type_entry;
|
||||
};
|
||||
|
||||
struct TypeTableEntryStruct {
|
||||
bool is_packed;
|
||||
int field_count;
|
||||
TypeStructField *fields;
|
||||
};
|
||||
|
||||
enum TypeTableEntryId {
|
||||
@ -39,6 +51,7 @@ enum TypeTableEntryId {
|
||||
TypeTableEntryIdFloat,
|
||||
TypeTableEntryIdPointer,
|
||||
TypeTableEntryIdArray,
|
||||
TypeTableEntryIdStruct,
|
||||
};
|
||||
|
||||
struct TypeTableEntry {
|
||||
@ -55,6 +68,7 @@ struct TypeTableEntry {
|
||||
TypeTableEntryPointer pointer;
|
||||
TypeTableEntryInt integral;
|
||||
TypeTableEntryArray array;
|
||||
TypeTableEntryStruct structure;
|
||||
} data;
|
||||
|
||||
// use these fields to make sure we don't duplicate type table entries for the same type
|
||||
@ -122,8 +136,10 @@ struct CodeGen {
|
||||
TypeTableEntry *entry_u8;
|
||||
TypeTableEntry *entry_i32;
|
||||
TypeTableEntry *entry_isize;
|
||||
TypeTableEntry *entry_usize;
|
||||
TypeTableEntry *entry_f32;
|
||||
TypeTableEntry *entry_string_literal;
|
||||
TypeTableEntry *entry_c_string_literal;
|
||||
TypeTableEntry *entry_string;
|
||||
TypeTableEntry *entry_void;
|
||||
TypeTableEntry *entry_unreachable;
|
||||
TypeTableEntry *entry_invalid;
|
||||
@ -211,6 +227,10 @@ struct BlockNode {
|
||||
BlockContext *block_context;
|
||||
};
|
||||
|
||||
struct StructDeclNode {
|
||||
TypeTableEntry *type_entry;
|
||||
};
|
||||
|
||||
struct CodeGenNode {
|
||||
union {
|
||||
TypeNode type_node; // for NodeTypeType
|
||||
@ -219,6 +239,7 @@ struct CodeGenNode {
|
||||
LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel
|
||||
AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign
|
||||
BlockNode block_node; // for NodeTypeBlock
|
||||
StructDeclNode struct_decl_node; // for NodeTypeStructDecl
|
||||
} data;
|
||||
ExprNode expr_node; // for all the expression nodes
|
||||
};
|
||||
|
@ -106,12 +106,12 @@ static void add_debug_source_node(CodeGen *g, AstNode *node) {
|
||||
g->cur_block_context->di_scope);
|
||||
}
|
||||
|
||||
static LLVMValueRef find_or_create_string(CodeGen *g, Buf *str) {
|
||||
static LLVMValueRef find_or_create_string(CodeGen *g, Buf *str, bool c) {
|
||||
auto entry = g->str_table.maybe_get(str);
|
||||
if (entry) {
|
||||
return entry->value;
|
||||
}
|
||||
LLVMValueRef text = LLVMConstString(buf_ptr(str), buf_len(str), false);
|
||||
LLVMValueRef text = LLVMConstString(buf_ptr(str), buf_len(str), !c);
|
||||
LLVMValueRef global_value = LLVMAddGlobal(g->module, LLVMTypeOf(text), "");
|
||||
LLVMSetLinkage(global_value, LLVMPrivateLinkage);
|
||||
LLVMSetInitializer(global_value, text);
|
||||
@ -204,6 +204,28 @@ static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) {
|
||||
return LLVMBuildLoad(g->builder, ptr, "");
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeFieldAccessExpr);
|
||||
|
||||
TypeTableEntry *struct_type = get_expr_type(node->data.field_access_expr.struct_expr);
|
||||
LLVMValueRef struct_ptr = gen_expr(g, node->data.field_access_expr.struct_expr);
|
||||
Buf *name = &node->data.field_access_expr.field_name;
|
||||
|
||||
// TODO add struct support
|
||||
(void)struct_ptr;
|
||||
|
||||
if (struct_type->id == TypeTableEntryIdArray) {
|
||||
if (buf_eql_str(name, "len")) {
|
||||
return LLVMConstInt(g->builtin_types.entry_usize->type_ref,
|
||||
struct_type->data.array.len, false);
|
||||
} else {
|
||||
zig_panic("gen_field_access_expr bad array field");
|
||||
}
|
||||
} else {
|
||||
zig_panic("gen_field_access_expr bad struct type");
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypePrefixOpExpr);
|
||||
assert(node->data.prefix_op_expr.primary_expr);
|
||||
@ -785,6 +807,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
||||
return gen_fn_call_expr(g, node);
|
||||
case NodeTypeArrayAccessExpr:
|
||||
return gen_array_access_expr(g, node);
|
||||
case NodeTypeFieldAccessExpr:
|
||||
return gen_field_access_expr(g, node);
|
||||
case NodeTypeUnreachable:
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildUnreachable(g->builder);
|
||||
@ -809,8 +833,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
||||
}
|
||||
case NodeTypeStringLiteral:
|
||||
{
|
||||
Buf *str = &node->data.string;
|
||||
LLVMValueRef str_val = find_or_create_string(g, str);
|
||||
Buf *str = &node->data.string_literal.buf;
|
||||
LLVMValueRef str_val = find_or_create_string(g, str, node->data.string_literal.c);
|
||||
LLVMValueRef indices[] = {
|
||||
LLVMConstInt(LLVMInt32Type(), 0, false),
|
||||
LLVMConstInt(LLVMInt32Type(), 0, false)
|
||||
@ -864,6 +888,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
||||
case NodeTypeExternBlock:
|
||||
case NodeTypeDirective:
|
||||
case NodeTypeUse:
|
||||
case NodeTypeStructDecl:
|
||||
case NodeTypeStructField:
|
||||
zig_unreachable();
|
||||
}
|
||||
zig_unreachable();
|
||||
@ -1072,7 +1098,7 @@ static void do_code_gen(CodeGen *g) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static void define_primitive_types(CodeGen *g) {
|
||||
static void define_builtin_types(CodeGen *g) {
|
||||
{
|
||||
// if this type is anywhere in the AST, we should never hit codegen.
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInvalid);
|
||||
@ -1103,7 +1129,7 @@ static void define_primitive_types(CodeGen *g) {
|
||||
g->type_table.put(&entry->name, entry);
|
||||
g->builtin_types.entry_u8 = entry;
|
||||
}
|
||||
g->builtin_types.entry_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
|
||||
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);
|
||||
entry->type_ref = LLVMInt32Type();
|
||||
@ -1130,6 +1156,19 @@ static void define_primitive_types(CodeGen *g) {
|
||||
g->type_table.put(&entry->name, entry);
|
||||
g->builtin_types.entry_isize = entry;
|
||||
}
|
||||
{
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
|
||||
entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8);
|
||||
buf_init_from_str(&entry->name, "usize");
|
||||
entry->size_in_bits = g->pointer_size_bytes * 8;
|
||||
entry->align_in_bits = g->pointer_size_bytes * 8;
|
||||
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_usize = entry;
|
||||
}
|
||||
{
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
|
||||
entry->type_ref = LLVMFloatType();
|
||||
@ -1160,6 +1199,43 @@ static void define_primitive_types(CodeGen *g) {
|
||||
g->type_table.put(&entry->name, entry);
|
||||
g->builtin_types.entry_unreachable = entry;
|
||||
}
|
||||
{
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct);
|
||||
|
||||
TypeTableEntry *const_pointer_to_u8 = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
|
||||
|
||||
unsigned element_count = 2;
|
||||
LLVMTypeRef element_types[] = {
|
||||
const_pointer_to_u8->type_ref,
|
||||
g->builtin_types.entry_usize->type_ref
|
||||
};
|
||||
entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), "string");
|
||||
LLVMStructSetBody(entry->type_ref, element_types, element_count, false);
|
||||
|
||||
buf_init_from_str(&entry->name, "string");
|
||||
entry->size_in_bits = g->pointer_size_bytes * 2 * 8;
|
||||
entry->align_in_bits = g->pointer_size_bytes;
|
||||
entry->data.structure.is_packed = false;
|
||||
entry->data.structure.field_count = element_count;
|
||||
entry->data.structure.fields = allocate<TypeStructField>(element_count);
|
||||
entry->data.structure.fields[0].name = buf_create_from_str("ptr");
|
||||
entry->data.structure.fields[0].type_entry = const_pointer_to_u8;
|
||||
entry->data.structure.fields[1].name = buf_create_from_str("len");
|
||||
entry->data.structure.fields[1].type_entry = g->builtin_types.entry_usize;
|
||||
|
||||
LLVMZigDIType *di_element_types[] = {
|
||||
const_pointer_to_u8->di_type,
|
||||
g->builtin_types.entry_usize->di_type
|
||||
};
|
||||
LLVMZigDIScope *compile_unit_scope = LLVMZigCompileUnitToScope(g->compile_unit);
|
||||
LLVMZigDIFile *difile = nullptr; // TODO make sure this ok
|
||||
entry->di_type = LLVMZigCreateDebugStructType(g->dbuilder, compile_unit_scope,
|
||||
"string", difile, 0, entry->size_in_bits, entry->align_in_bits, 0,
|
||||
nullptr, di_element_types, element_count, 0, nullptr, "");
|
||||
|
||||
g->type_table.put(&entry->name, entry);
|
||||
g->builtin_types.entry_string = entry;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1213,8 +1289,6 @@ static void init(CodeGen *g, Buf *source_path) {
|
||||
LLVMZigSetFastMath(g->builder, true);
|
||||
|
||||
|
||||
define_primitive_types(g);
|
||||
|
||||
Buf *producer = buf_sprintf("zig %s", ZIG_VERSION_STRING);
|
||||
bool is_optimized = g->build_type == CodeGenBuildTypeRelease;
|
||||
const char *flags = "";
|
||||
@ -1224,6 +1298,7 @@ static void init(CodeGen *g, Buf *source_path) {
|
||||
buf_ptr(producer), is_optimized, flags, runtime_version,
|
||||
"", 0, !g->strip_debug_symbols);
|
||||
|
||||
define_builtin_types(g);
|
||||
|
||||
}
|
||||
|
||||
|
156
src/parser.cpp
156
src/parser.cpp
@ -106,6 +106,12 @@ const char *node_type_str(NodeType node_type) {
|
||||
return "Label";
|
||||
case NodeTypeAsmExpr:
|
||||
return "AsmExpr";
|
||||
case NodeTypeFieldAccessExpr:
|
||||
return "FieldAccessExpr";
|
||||
case NodeTypeStructDecl:
|
||||
return "StructDecl";
|
||||
case NodeTypeStructField:
|
||||
return "StructField";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -259,9 +265,12 @@ void ast_print(AstNode *node, int indent) {
|
||||
buf_ptr(&node->data.number));
|
||||
break;
|
||||
case NodeTypeStringLiteral:
|
||||
fprintf(stderr, "StringLiteral '%s'\n",
|
||||
buf_ptr(&node->data.string));
|
||||
break;
|
||||
{
|
||||
const char *c = node->data.string_literal.c ? "c" : "";
|
||||
fprintf(stderr, "StringLiteral %s'%s'\n", c,
|
||||
buf_ptr(&node->data.string_literal.buf));
|
||||
break;
|
||||
}
|
||||
case NodeTypeUnreachable:
|
||||
fprintf(stderr, "Unreachable\n");
|
||||
break;
|
||||
@ -295,6 +304,19 @@ void ast_print(AstNode *node, int indent) {
|
||||
case NodeTypeAsmExpr:
|
||||
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||
break;
|
||||
case NodeTypeFieldAccessExpr:
|
||||
fprintf(stderr, "%s '%s'\n", node_type_str(node->type),
|
||||
buf_ptr(&node->data.field_access_expr.field_name));
|
||||
ast_print(node->data.field_access_expr.struct_expr, indent + 2);
|
||||
break;
|
||||
case NodeTypeStructDecl:
|
||||
fprintf(stderr, "%s '%s'\n",
|
||||
node_type_str(node->type), buf_ptr(&node->data.struct_decl.name));
|
||||
break;
|
||||
case NodeTypeStructField:
|
||||
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.struct_field.name));
|
||||
ast_print(node->data.struct_field.type, indent + 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -475,18 +497,28 @@ static void parse_asm_template(ParseContext *pc, AstNode *node) {
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, ZigList<SrcPos> *offset_map) {
|
||||
static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool *out_c_str,
|
||||
ZigList<SrcPos> *offset_map)
|
||||
{
|
||||
// skip the double quotes at beginning and end
|
||||
// convert escape sequences
|
||||
// detect c string literal
|
||||
|
||||
buf_resize(buf, 0);
|
||||
bool escape = false;
|
||||
bool first = true;
|
||||
bool skip_quote;
|
||||
SrcPos pos = {token->start_line, token->start_column};
|
||||
for (int i = token->start_pos; i < token->end_pos - 1; i += 1) {
|
||||
uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i);
|
||||
if (first) {
|
||||
first = false;
|
||||
if (i == token->start_pos) {
|
||||
skip_quote = (c == 'c');
|
||||
if (out_c_str) {
|
||||
*out_c_str = skip_quote;
|
||||
} else if (skip_quote) {
|
||||
ast_error(pc, token, "C string literal not allowed here");
|
||||
}
|
||||
} else if (skip_quote) {
|
||||
skip_quote = false;
|
||||
} else {
|
||||
if (escape) {
|
||||
switch (c) {
|
||||
@ -541,13 +573,20 @@ static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool ma
|
||||
static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory);
|
||||
static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory);
|
||||
|
||||
|
||||
static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {
|
||||
if (token->id != token_id) {
|
||||
ast_invalid_token_error(pc, token);
|
||||
}
|
||||
}
|
||||
|
||||
static Token *ast_eat_token(ParseContext *pc, int *token_index, TokenId token_id) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
ast_expect_token(pc, token, token_id);
|
||||
*token_index += 1;
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
static AstNode *ast_parse_directive(ParseContext *pc, int token_index, int *new_token_index) {
|
||||
Token *number_sign = &pc->tokens->at(token_index);
|
||||
token_index += 1;
|
||||
@ -569,7 +608,7 @@ static AstNode *ast_parse_directive(ParseContext *pc, int token_index, int *new_
|
||||
token_index += 1;
|
||||
ast_expect_token(pc, param_str, TokenIdStringLiteral);
|
||||
|
||||
parse_string_literal(pc, param_str, &node->data.directive.param, nullptr);
|
||||
parse_string_literal(pc, param_str, &node->data.directive.param, nullptr, nullptr);
|
||||
|
||||
Token *r_paren = &pc->tokens->at(token_index);
|
||||
token_index += 1;
|
||||
@ -782,7 +821,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
|
||||
return node;
|
||||
} else if (token->id == TokenIdStringLiteral) {
|
||||
AstNode *node = ast_create_node(pc, NodeTypeStringLiteral, token);
|
||||
parse_string_literal(pc, token, &node->data.string, nullptr);
|
||||
parse_string_literal(pc, token, &node->data.string_literal.buf, &node->data.string_literal.c, nullptr);
|
||||
*token_index += 1;
|
||||
return node;
|
||||
} else if (token->id == TokenIdKeywordUnreachable) {
|
||||
@ -832,9 +871,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
|
||||
}
|
||||
|
||||
/*
|
||||
SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression)
|
||||
SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression)
|
||||
FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
|
||||
ArrayAccessExpression : token(LBracket) Expression token(RBracket)
|
||||
FieldAccessExpression : token(Dot) token(Symbol)
|
||||
*/
|
||||
static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
AstNode *primary_expr = ast_parse_primary_expr(pc, token_index, mandatory);
|
||||
@ -861,6 +901,16 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo
|
||||
*token_index += 1;
|
||||
ast_expect_token(pc, r_bracket, TokenIdRBracket);
|
||||
|
||||
return node;
|
||||
} else if (token->id == TokenIdDot) {
|
||||
*token_index += 1;
|
||||
|
||||
Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol);
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, token);
|
||||
node->data.field_access_expr.struct_expr = primary_expr;
|
||||
ast_buf_from_token(pc, name_token, &node->data.field_access_expr.field_name);
|
||||
|
||||
return node;
|
||||
} else {
|
||||
return primary_expr;
|
||||
@ -1397,14 +1447,6 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand
|
||||
return node;
|
||||
}
|
||||
|
||||
static Token *ast_eat_token(ParseContext *pc, int *token_index, TokenId token_id) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
ast_expect_token(pc, token, token_id);
|
||||
*token_index += 1;
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen)
|
||||
*/
|
||||
@ -1421,7 +1463,7 @@ static void ast_parse_asm_input_item(ParseContext *pc, int *token_index, AstNode
|
||||
|
||||
AsmInput *asm_input = allocate<AsmInput>(1);
|
||||
ast_buf_from_token(pc, alias, &asm_input->asm_symbolic_name);
|
||||
parse_string_literal(pc, constraint, &asm_input->constraint, nullptr);
|
||||
parse_string_literal(pc, constraint, &asm_input->constraint, nullptr, nullptr);
|
||||
asm_input->expr = expr_node;
|
||||
node->data.asm_expr.input_list.append(asm_input);
|
||||
}
|
||||
@ -1442,7 +1484,7 @@ static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNod
|
||||
|
||||
AsmOutput *asm_output = allocate<AsmOutput>(1);
|
||||
ast_buf_from_token(pc, alias, &asm_output->asm_symbolic_name);
|
||||
parse_string_literal(pc, constraint, &asm_output->constraint, nullptr);
|
||||
parse_string_literal(pc, constraint, &asm_output->constraint, nullptr, nullptr);
|
||||
ast_buf_from_token(pc, out_symbol, &asm_output->variable_name);
|
||||
node->data.asm_expr.output_list.append(asm_output);
|
||||
}
|
||||
@ -1464,7 +1506,7 @@ static void ast_parse_asm_clobbers(ParseContext *pc, int *token_index, AstNode *
|
||||
*token_index += 1;
|
||||
|
||||
Buf *clobber_buf = buf_alloc();
|
||||
parse_string_literal(pc, string_tok, clobber_buf, nullptr);
|
||||
parse_string_literal(pc, string_tok, clobber_buf, nullptr, nullptr);
|
||||
node->data.asm_expr.clobber_list.append(clobber_buf);
|
||||
|
||||
Token *comma = &pc->tokens->at(*token_index);
|
||||
@ -1565,7 +1607,7 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand
|
||||
ast_expect_token(pc, template_tok, TokenIdStringLiteral);
|
||||
*token_index += 1;
|
||||
|
||||
parse_string_literal(pc, template_tok, &node->data.asm_expr.asm_template,
|
||||
parse_string_literal(pc, template_tok, &node->data.asm_expr.asm_template, nullptr,
|
||||
&node->data.asm_expr.offset_map);
|
||||
parse_asm_template(pc, node);
|
||||
|
||||
@ -1877,7 +1919,7 @@ static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, b
|
||||
*token_index += 1;
|
||||
ast_expect_token(pc, export_name, TokenIdStringLiteral);
|
||||
|
||||
parse_string_literal(pc, export_name, &node->data.root_export_decl.name, nullptr);
|
||||
parse_string_literal(pc, export_name, &node->data.root_export_decl.name, nullptr, nullptr);
|
||||
|
||||
Token *semicolon = &pc->tokens->at(*token_index);
|
||||
*token_index += 1;
|
||||
@ -1889,9 +1931,7 @@ static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, b
|
||||
/*
|
||||
Use : many(Directive) token(Use) token(String) token(Semicolon)
|
||||
*/
|
||||
static AstNode *ast_parse_use(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
assert(mandatory == false);
|
||||
|
||||
static AstNode *ast_parse_use(ParseContext *pc, int *token_index) {
|
||||
Token *use_kw = &pc->tokens->at(*token_index);
|
||||
if (use_kw->id != TokenIdKeywordUse)
|
||||
return nullptr;
|
||||
@ -1907,7 +1947,7 @@ static AstNode *ast_parse_use(ParseContext *pc, int *token_index, bool mandatory
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeUse, use_kw);
|
||||
|
||||
parse_string_literal(pc, use_name, &node->data.use.path, nullptr);
|
||||
parse_string_literal(pc, use_name, &node->data.use.path, nullptr, nullptr);
|
||||
|
||||
node->data.use.directives = pc->directive_list;
|
||||
pc->directive_list = nullptr;
|
||||
@ -1916,7 +1956,57 @@ static AstNode *ast_parse_use(ParseContext *pc, int *token_index, bool mandatory
|
||||
}
|
||||
|
||||
/*
|
||||
TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use
|
||||
StructDecl : many(Directive) token(Struct) token(Symbol) token(LBrace) many(StructField) token(RBrace)
|
||||
StructField : token(Symbol) token(Colon) Type token(Comma)
|
||||
*/
|
||||
static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) {
|
||||
Token *struct_kw = &pc->tokens->at(*token_index);
|
||||
if (struct_kw->id != TokenIdKeywordStruct)
|
||||
return nullptr;
|
||||
*token_index += 1;
|
||||
|
||||
Token *struct_name = &pc->tokens->at(*token_index);
|
||||
*token_index += 1;
|
||||
ast_expect_token(pc, struct_name, TokenIdSymbol);
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeStructDecl, struct_kw);
|
||||
ast_buf_from_token(pc, struct_name, &node->data.struct_decl.name);
|
||||
|
||||
ast_eat_token(pc, token_index, TokenIdLBrace);
|
||||
|
||||
for (;;) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
|
||||
if (token->id == TokenIdRBrace) {
|
||||
*token_index += 1;
|
||||
break;
|
||||
} else if (token->id == TokenIdSymbol) {
|
||||
AstNode *field_node = ast_create_node(pc, NodeTypeStructField, token);
|
||||
*token_index += 1;
|
||||
|
||||
ast_buf_from_token(pc, token, &field_node->data.struct_field.name);
|
||||
|
||||
ast_eat_token(pc, token_index, TokenIdColon);
|
||||
|
||||
field_node->data.struct_field.type = ast_parse_type(pc, *token_index, token_index);
|
||||
|
||||
ast_eat_token(pc, token_index, TokenIdComma);
|
||||
|
||||
node->data.struct_decl.fields.append(field_node);
|
||||
} else {
|
||||
ast_invalid_token_error(pc, token);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
node->data.struct_decl.directives = pc->directive_list;
|
||||
pc->directive_list = nullptr;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use | StructDecl
|
||||
*/
|
||||
static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList<AstNode *> *top_level_decls) {
|
||||
for (;;) {
|
||||
@ -1943,12 +2033,18 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis
|
||||
continue;
|
||||
}
|
||||
|
||||
AstNode *use_node = ast_parse_use(pc, token_index, false);
|
||||
AstNode *use_node = ast_parse_use(pc, token_index);
|
||||
if (use_node) {
|
||||
top_level_decls->append(use_node);
|
||||
continue;
|
||||
}
|
||||
|
||||
AstNode *struct_node = ast_parse_struct_decl(pc, token_index);
|
||||
if (struct_node) {
|
||||
top_level_decls->append(struct_node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pc->directive_list->length > 0) {
|
||||
ast_error(pc, directive_token, "invalid directive");
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ enum NodeType {
|
||||
NodeTypePrefixOpExpr,
|
||||
NodeTypeFnCallExpr,
|
||||
NodeTypeArrayAccessExpr,
|
||||
NodeTypeFieldAccessExpr,
|
||||
NodeTypeUse,
|
||||
NodeTypeVoid,
|
||||
NodeTypeBoolLiteral,
|
||||
@ -47,6 +48,8 @@ enum NodeType {
|
||||
NodeTypeLabel,
|
||||
NodeTypeGoto,
|
||||
NodeTypeAsmExpr,
|
||||
NodeTypeStructDecl,
|
||||
NodeTypeStructField,
|
||||
};
|
||||
|
||||
struct AstNodeRoot {
|
||||
@ -152,6 +155,11 @@ struct AstNodeArrayAccessExpr {
|
||||
AstNode *subscript;
|
||||
};
|
||||
|
||||
struct AstNodeFieldAccessExpr {
|
||||
AstNode *struct_expr;
|
||||
Buf field_name;
|
||||
};
|
||||
|
||||
struct AstNodeExternBlock {
|
||||
ZigList<AstNode *> *directives;
|
||||
ZigList<AstNode *> fn_decls;
|
||||
@ -231,6 +239,22 @@ struct AstNodeAsmExpr {
|
||||
ZigList<Buf*> clobber_list;
|
||||
};
|
||||
|
||||
struct AstNodeStructDecl {
|
||||
Buf name;
|
||||
ZigList<AstNode *> fields;
|
||||
ZigList<AstNode *> *directives;
|
||||
};
|
||||
|
||||
struct AstNodeStructField {
|
||||
Buf name;
|
||||
AstNode *type;
|
||||
};
|
||||
|
||||
struct AstNodeStringLiteral {
|
||||
Buf buf;
|
||||
bool c;
|
||||
};
|
||||
|
||||
struct AstNode {
|
||||
enum NodeType type;
|
||||
int line;
|
||||
@ -260,8 +284,11 @@ struct AstNode {
|
||||
AstNodeLabel label;
|
||||
AstNodeGoto go_to;
|
||||
AstNodeAsmExpr asm_expr;
|
||||
AstNodeFieldAccessExpr field_access_expr;
|
||||
AstNodeStructDecl struct_decl;
|
||||
AstNodeStructField struct_field;
|
||||
AstNodeStringLiteral string_literal;
|
||||
Buf number;
|
||||
Buf string;
|
||||
Buf symbol;
|
||||
bool bool_literal;
|
||||
} data;
|
||||
|
@ -28,10 +28,9 @@
|
||||
case '8': \
|
||||
case '9'
|
||||
|
||||
#define ALPHA \
|
||||
#define ALPHA_EXCEPT_C \
|
||||
'a': \
|
||||
case 'b': \
|
||||
case 'c': \
|
||||
case 'd': \
|
||||
case 'e': \
|
||||
case 'f': \
|
||||
@ -82,6 +81,10 @@
|
||||
case 'Y': \
|
||||
case 'Z'
|
||||
|
||||
#define ALPHA \
|
||||
ALPHA_EXCEPT_C: \
|
||||
case 'c'
|
||||
|
||||
#define SYMBOL_CHAR \
|
||||
ALPHA: \
|
||||
case DIGIT: \
|
||||
@ -90,6 +93,7 @@
|
||||
enum TokenizeState {
|
||||
TokenizeStateStart,
|
||||
TokenizeStateSymbol,
|
||||
TokenizeStateSymbolFirst,
|
||||
TokenizeStateNumber,
|
||||
TokenizeStateString,
|
||||
TokenizeStateSawDash,
|
||||
@ -201,6 +205,8 @@ static void end_token(Tokenize *t) {
|
||||
t->cur_tok->id = TokenIdKeywordVolatile;
|
||||
} else if (mem_eql_str(token_mem, token_len, "asm")) {
|
||||
t->cur_tok->id = TokenIdKeywordAsm;
|
||||
} else if (mem_eql_str(token_mem, token_len, "struct")) {
|
||||
t->cur_tok->id = TokenIdKeywordStruct;
|
||||
}
|
||||
|
||||
t->cur_tok = nullptr;
|
||||
@ -224,7 +230,11 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
switch (c) {
|
||||
case WHITESPACE:
|
||||
break;
|
||||
case ALPHA:
|
||||
case 'c':
|
||||
t.state = TokenizeStateSymbolFirst;
|
||||
begin_token(&t, TokenIdSymbol);
|
||||
break;
|
||||
case ALPHA_EXCEPT_C:
|
||||
case '_':
|
||||
t.state = TokenizeStateSymbol;
|
||||
begin_token(&t, TokenIdSymbol);
|
||||
@ -526,6 +536,22 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TokenizeStateSymbolFirst:
|
||||
switch (c) {
|
||||
case '"':
|
||||
t.cur_tok->id = TokenIdStringLiteral;
|
||||
t.state = TokenizeStateString;
|
||||
break;
|
||||
case SYMBOL_CHAR:
|
||||
t.state = TokenizeStateSymbol;
|
||||
break;
|
||||
default:
|
||||
t.pos -= 1;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case TokenizeStateSymbol:
|
||||
switch (c) {
|
||||
case SYMBOL_CHAR:
|
||||
@ -589,6 +615,7 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
tokenize_error(&t, "unterminated string");
|
||||
break;
|
||||
case TokenizeStateSymbol:
|
||||
case TokenizeStateSymbolFirst:
|
||||
case TokenizeStateNumber:
|
||||
case TokenizeStateSawDash:
|
||||
case TokenizeStatePipe:
|
||||
@ -643,6 +670,7 @@ static const char * token_name(Token *token) {
|
||||
case TokenIdKeywordGoto: return "Goto";
|
||||
case TokenIdKeywordVolatile: return "Volatile";
|
||||
case TokenIdKeywordAsm: return "Asm";
|
||||
case TokenIdKeywordStruct: return "Struct";
|
||||
case TokenIdLParen: return "LParen";
|
||||
case TokenIdRParen: return "RParen";
|
||||
case TokenIdComma: return "Comma";
|
||||
|
@ -32,6 +32,7 @@ enum TokenId {
|
||||
TokenIdKeywordGoto,
|
||||
TokenIdKeywordAsm,
|
||||
TokenIdKeywordVolatile,
|
||||
TokenIdKeywordStruct,
|
||||
TokenIdLParen,
|
||||
TokenIdRParen,
|
||||
TokenIdComma,
|
||||
|
@ -161,6 +161,31 @@ LLVMZigDIType *LLVMZigCreateDebugArrayType(LLVMZigDIBuilder *dibuilder, uint64_t
|
||||
return reinterpret_cast<LLVMZigDIType*>(di_type);
|
||||
}
|
||||
|
||||
|
||||
LLVMZigDIType *LLVMZigCreateDebugStructType(LLVMZigDIBuilder *dibuilder, LLVMZigDIScope *scope,
|
||||
const char *name, LLVMZigDIFile *file, unsigned line_number, uint64_t size_in_bits,
|
||||
uint64_t align_in_bits, unsigned flags, LLVMZigDIType *derived_from,
|
||||
LLVMZigDIType **types_array, int types_array_len, unsigned run_time_lang, LLVMZigDIType *vtable_holder,
|
||||
const char *unique_id)
|
||||
{
|
||||
SmallVector<Metadata *, 8> fields;
|
||||
for (int i = 0; i < types_array_len; i += 1) {
|
||||
DIType *ditype = reinterpret_cast<DIType*>(types_array[i]);
|
||||
fields.push_back(ditype);
|
||||
}
|
||||
DIType *di_type = reinterpret_cast<DIBuilder*>(dibuilder)->createStructType(
|
||||
reinterpret_cast<DIScope*>(scope),
|
||||
name,
|
||||
reinterpret_cast<DIFile*>(file),
|
||||
line_number, size_in_bits, align_in_bits, flags,
|
||||
reinterpret_cast<DIType*>(derived_from),
|
||||
reinterpret_cast<DIBuilder*>(dibuilder)->getOrCreateArray(fields),
|
||||
run_time_lang,
|
||||
reinterpret_cast<DIType*>(vtable_holder),
|
||||
unique_id);
|
||||
return reinterpret_cast<LLVMZigDIType*>(di_type);
|
||||
}
|
||||
|
||||
LLVMZigDISubroutineType *LLVMZigCreateSubroutineType(LLVMZigDIBuilder *dibuilder_wrapped,
|
||||
LLVMZigDIFile *file, LLVMZigDIType **types_array, int types_array_len, unsigned flags)
|
||||
{
|
||||
|
@ -49,6 +49,11 @@ LLVMZigDIType *LLVMZigCreateDebugArrayType(LLVMZigDIBuilder *dibuilder,
|
||||
uint64_t size_in_bits, uint64_t align_in_bits, LLVMZigDIType *elem_type,
|
||||
int elem_count);
|
||||
|
||||
LLVMZigDIType *LLVMZigCreateDebugStructType(LLVMZigDIBuilder *dibuilder, LLVMZigDIScope *scope,
|
||||
const char *name, LLVMZigDIFile *file, unsigned line_number, uint64_t size_in_bits,
|
||||
uint64_t align_in_bits, unsigned flags, LLVMZigDIType *derived_from,
|
||||
LLVMZigDIType **types_array, int types_array_len, unsigned run_time_lang, LLVMZigDIType *vtable_holder,
|
||||
const char *unique_id);
|
||||
|
||||
LLVMZigDISubroutineType *LLVMZigCreateSubroutineType(LLVMZigDIBuilder *dibuilder_wrapped,
|
||||
LLVMZigDIFile *file, LLVMZigDIType **types_array, int types_array_len, unsigned flags);
|
||||
|
@ -17,8 +17,11 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize {
|
||||
// TODO zig strings instead of C strings
|
||||
// TODO handle buffering and flushing
|
||||
// TODO non-i32 integer literals so we can remove the casts
|
||||
pub fn print_str(str : *const u8, len: isize) {
|
||||
// TODO constants for SYS_write and stdout_fileno
|
||||
//pub fn print_str(str : string) -> isize {
|
||||
pub fn print_str(str : *const u8, len: isize) -> isize {
|
||||
let SYS_write = 1;
|
||||
let stdout_fileno = 1;
|
||||
syscall3(SYS_write as isize, stdout_fileno as isize, str as isize, len);
|
||||
//return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize);
|
||||
return syscall3(SYS_write as isize, stdout_fileno as isize, str as isize, len);
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ static void add_compiling_test_cases(void) {
|
||||
}
|
||||
|
||||
export fn _start() -> unreachable {
|
||||
puts("Hello, world!");
|
||||
puts(c"Hello, world!");
|
||||
exit(0);
|
||||
}
|
||||
)SOURCE", "Hello, world!\n");
|
||||
@ -126,7 +126,7 @@ static void add_compiling_test_cases(void) {
|
||||
}
|
||||
|
||||
fn this_is_a_function() -> unreachable {
|
||||
puts("OK");
|
||||
puts(c"OK");
|
||||
exit(0);
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
@ -146,7 +146,7 @@ static void add_compiling_test_cases(void) {
|
||||
/// this is a documentation comment
|
||||
/// doc comment line 2
|
||||
export fn _start() -> unreachable {
|
||||
puts(/* mid-line comment /* nested */ */ "OK");
|
||||
puts(/* mid-line comment /* nested */ */ c"OK");
|
||||
exit(0);
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
@ -180,7 +180,7 @@ static void add_compiling_test_cases(void) {
|
||||
// purposefully conflicting function with main source file
|
||||
// but it's private so it should be OK
|
||||
fn private_function() {
|
||||
puts("OK");
|
||||
puts(c"OK");
|
||||
}
|
||||
|
||||
pub fn print_text() {
|
||||
@ -198,17 +198,17 @@ static void add_compiling_test_cases(void) {
|
||||
|
||||
export fn _start() -> unreachable {
|
||||
if 1 != 0 {
|
||||
puts("1 is true");
|
||||
puts(c"1 is true");
|
||||
} else {
|
||||
puts("1 is false");
|
||||
puts(c"1 is false");
|
||||
}
|
||||
if 0 != 0 {
|
||||
puts("0 is true");
|
||||
puts(c"0 is true");
|
||||
} else if 1 - 1 != 0 {
|
||||
puts("1 - 1 is true");
|
||||
puts(c"1 - 1 is true");
|
||||
}
|
||||
if !(0 != 0) {
|
||||
puts("!0 is true");
|
||||
puts(c"!0 is true");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
@ -227,7 +227,7 @@ static void add_compiling_test_cases(void) {
|
||||
|
||||
export fn _start() -> unreachable {
|
||||
if add(22, 11) == 33 {
|
||||
puts("pass");
|
||||
puts(c"pass");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
@ -244,7 +244,7 @@ static void add_compiling_test_cases(void) {
|
||||
if a == 0 {
|
||||
goto done;
|
||||
}
|
||||
puts("loop");
|
||||
puts(c"loop");
|
||||
loop(a - 1);
|
||||
|
||||
done:
|
||||
@ -268,7 +268,7 @@ export fn _start() -> unreachable {
|
||||
let a : i32 = 1;
|
||||
let b = 2;
|
||||
if (a + b == 3) {
|
||||
puts("OK");
|
||||
puts(c"OK");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
@ -282,10 +282,10 @@ extern {
|
||||
}
|
||||
|
||||
export fn _start() -> unreachable {
|
||||
if (true) { puts("OK 1"); }
|
||||
if (false) { puts("BAD 1"); }
|
||||
if (!true) { puts("BAD 2"); }
|
||||
if (!false) { puts("OK 2"); }
|
||||
if (true) { puts(c"OK 1"); }
|
||||
if (false) { puts(c"BAD 1"); }
|
||||
if (!true) { puts(c"BAD 2"); }
|
||||
if (!false) { puts(c"OK 2"); }
|
||||
exit(0);
|
||||
}
|
||||
)SOURCE", "OK 1\nOK 2\n");
|
||||
@ -300,14 +300,14 @@ extern {
|
||||
export fn _start() -> unreachable {
|
||||
if (true) {
|
||||
let no_conflict = 5;
|
||||
if (no_conflict == 5) { puts("OK 1"); }
|
||||
if (no_conflict == 5) { puts(c"OK 1"); }
|
||||
}
|
||||
|
||||
let c = {
|
||||
let no_conflict = 10;
|
||||
no_conflict
|
||||
};
|
||||
if (c == 10) { puts("OK 2"); }
|
||||
if (c == 10) { puts(c"OK 2"); }
|
||||
exit(0);
|
||||
}
|
||||
)SOURCE", "OK 1\nOK 2\n");
|
||||
@ -327,7 +327,7 @@ export fn _start() -> unreachable {
|
||||
fn void_fun(a : i32, b : void, c : i32) {
|
||||
let v = b;
|
||||
let vv : void = if (a == 1) {v} else {};
|
||||
if (a + c == 3) { puts("OK"); }
|
||||
if (a + c == 3) { puts(c"OK"); }
|
||||
return vv;
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
@ -341,14 +341,14 @@ extern {
|
||||
|
||||
export fn _start() -> unreachable {
|
||||
let mut zero : i32;
|
||||
if (zero == 0) { puts("zero"); }
|
||||
if (zero == 0) { puts(c"zero"); }
|
||||
|
||||
let mut i = 0;
|
||||
loop_start:
|
||||
if i == 3 {
|
||||
goto done;
|
||||
}
|
||||
puts("loop");
|
||||
puts(c"loop");
|
||||
i = i + 1;
|
||||
goto loop_start;
|
||||
done:
|
||||
@ -391,7 +391,7 @@ loop_2_start:
|
||||
loop_2_end:
|
||||
|
||||
if accumulator == 15 {
|
||||
puts("OK");
|
||||
puts(c"OK");
|
||||
}
|
||||
|
||||
exit(0);
|
||||
@ -403,7 +403,7 @@ loop_2_end:
|
||||
use "std.zig";
|
||||
|
||||
export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 {
|
||||
print_str("Hello, world!\n", 14 as isize);
|
||||
print_str(c"Hello, world!\n", 14 as isize);
|
||||
return 0;
|
||||
}
|
||||
)SOURCE", "Hello, world!\n");
|
||||
@ -430,11 +430,11 @@ fn a() {}
|
||||
|
||||
add_compile_fail_case("unreachable with return", R"SOURCE(
|
||||
fn a() -> unreachable {return;}
|
||||
)SOURCE", 1, ".tmp_source.zig:2:24: error: type mismatch. expected unreachable. got void");
|
||||
)SOURCE", 1, ".tmp_source.zig:2:24: error: expected type 'unreachable', got 'void'");
|
||||
|
||||
add_compile_fail_case("control reaches end of non-void function", R"SOURCE(
|
||||
fn a() -> i32 {}
|
||||
)SOURCE", 1, ".tmp_source.zig:2:15: error: type mismatch. expected i32. got void");
|
||||
)SOURCE", 1, ".tmp_source.zig:2:15: error: expected type 'i32', got 'void'");
|
||||
|
||||
add_compile_fail_case("undefined function call", R"SOURCE(
|
||||
fn a() {
|
||||
@ -514,16 +514,16 @@ fn f(a : i32) {
|
||||
|
||||
add_compile_fail_case("variable has wrong type", R"SOURCE(
|
||||
fn f() -> i32 {
|
||||
let a = "a";
|
||||
let a = c"a";
|
||||
a
|
||||
}
|
||||
)SOURCE", 1, ".tmp_source.zig:2:15: error: type mismatch. expected i32. got *const u8");
|
||||
)SOURCE", 1, ".tmp_source.zig:2:15: error: expected type 'i32', got '*const u8'");
|
||||
|
||||
add_compile_fail_case("if condition is bool, not int", R"SOURCE(
|
||||
fn f() {
|
||||
if (0) {}
|
||||
}
|
||||
)SOURCE", 1, ".tmp_source.zig:3:9: error: type mismatch. expected bool. got i32");
|
||||
)SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got 'i32'");
|
||||
|
||||
add_compile_fail_case("assign unreachable", R"SOURCE(
|
||||
fn f() {
|
||||
@ -551,11 +551,11 @@ a_label:
|
||||
}
|
||||
)SOURCE", 1, ".tmp_source.zig:3:1: error: label 'a_label' defined but not used");
|
||||
|
||||
add_compile_fail_case("expected bare identifier", R"SOURCE(
|
||||
add_compile_fail_case("bad assignment target", R"SOURCE(
|
||||
fn f() {
|
||||
3 = 3;
|
||||
}
|
||||
)SOURCE", 1, ".tmp_source.zig:3:5: error: expected a bare identifier");
|
||||
)SOURCE", 1, ".tmp_source.zig:3:5: error: assignment target must be variable, field, or array element");
|
||||
|
||||
add_compile_fail_case("assign to constant variable", R"SOURCE(
|
||||
fn f() {
|
||||
|
Loading…
Reference in New Issue
Block a user