Commit 5e212db29c
Changed files (10)
doc
std
doc/langref.md
@@ -5,9 +5,11 @@
```
Root : many(TopLevelDecl) "EOF"
-TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Import | ContainerDecl | VariableDeclaration
+TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Import | ContainerDecl | VariableDeclaration | ErrorValueDecl
-VariableDeclaration : option(FnVisibleMod) ("var" | "const") "symbol" ("=" Expression | ":" PrefixOpExpression option("=" Expression))
+ErrorValueDecl : option(FnVisibleMod) "%." "Symbol"
+
+VariableDeclaration : option(FnVisibleMod) ("var" | "const") "Symbol" ("=" Expression | ":" PrefixOpExpression option("=" Expression))
ContainerDecl : many(Directive) option(FnVisibleMod) ("struct" | "enum") "Symbol" "{" many(StructMember) "}"
@@ -77,7 +79,7 @@ ForExpression : "for" "(" "Symbol" "," Expression option("," "Symbol") ")" Expre
BoolOrExpression : BoolAndExpression "||" BoolOrExpression | BoolAndExpression
-ReturnExpression : "return" option(Expression)
+ReturnExpression : option("%" | "?") "return" option(Expression)
IfExpression : IfVarExpression | IfBoolExpression
@@ -133,7 +135,7 @@ StructLiteralField : "." "Symbol" "=" Expression
PrefixOp : "!" | "-" | "~" | "*" | ("&" option("const")) | "?"
-PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression
+PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression | ("%." "Symbol")
ArrayType : "[" option(Expression) "]" option("const") PrefixOpExpression
@@ -148,7 +150,7 @@ KeywordLiteral : "true" | "false" | "null" | "break" | "continue"
```
x() x[] x.y
-!x -x ~x *x &x ?x
+!x -x ~x *x &x ?x %x
x{}
* / %
+ -
@@ -199,12 +201,20 @@ c_ulonglong unsigned long long for ABI compatibility with C
### Boolean Type
The boolean type has the name `bool` and represents either true or false.
-### Function Types
+### Function Type
TODO
-### Array Types
-TODO
-Also, are there slices?
+### Fixed-Size Array Type
+
+Example: The string `"aoeu"` has type `[4]u8`.
+
+The size is known at compile time and is part of the type.
+
+### Slice Type
+
+A slice can be obtained with the slicing syntax: `array[start...end]`
+
+Example: `"aoeu"[0...2]` has type `[]u8`.
### Struct Types
TODO
@@ -213,10 +223,13 @@ TODO
TODO
### Unreachable Type
+
The unreachable type has the name `unreachable`. TODO explanation
### Void Type
-The void type has the name `void`. TODO explanation
+
+The void type has the name `void`. void types are zero bits and are omitted
+from codegen.
## Expressions
example/cat/main.zig
@@ -3,50 +3,50 @@ export executable "cat";
import "std.zig";
// Things to do to make this work:
-// * isize instead of usize for things
// * var args printing
-// * update std API
-// * !void type
+// * %void type
// * defer
-// * !return
-// * !! operator
-// * make main return !void
-// * how to reference error values (!void).Invalid ? !Invalid ?
-// * ~ is bool not, not !
+// * %return
+// * %% operator
+// * make main return %void
+// * how to reference error values %.Invalid
// * cast err type to string
+// * update std API
+
+pub %.Invalid;
-pub fn main(args: [][]u8) !void => {
+pub fn main(args: [][]u8) %void => {
const exe = args[0];
var catted_anything = false;
for (arg, args[1...]) {
if (arg == "-") {
catted_anything = true;
- !return cat_stream(stdin);
+ %return cat_stream(stdin);
} else if (arg[0] == '-') {
return usage(exe);
} else {
var is: InputStream;
- is.open(arg, OpenReadOnly) !! (err) => {
+ is.open(arg, OpenReadOnly) %% (err) => {
stderr.print("Unable to open file: {}", ([]u8])(err));
return err;
}
defer is.close();
catted_anything = true;
- !return cat_stream(is);
+ %return cat_stream(is);
}
}
- if (~catted_anything) {
- !return cat_stream(stdin)
+ if (!catted_anything) {
+ %return cat_stream(stdin)
}
}
-fn usage(exe: []u8) !void => {
+fn usage(exe: []u8) %void => {
stderr.print("Usage: {} [FILE]...\n", exe);
- return !Invalid;
+ return %.Invalid;
}
-fn cat_stream(is: InputStream) !void => {
+fn cat_stream(is: InputStream) %void => {
var buf: [1024 * 4]u8;
while (true) {
example/hello_world/hello.zig
@@ -3,6 +3,7 @@ export executable "hello";
import "std.zig";
pub fn main(args: [][]u8) i32 => {
+ //stderr.print_str("Hello, world!\n");
print_str("Hello, world!\n");
return 0;
}
src/all_types.hpp
@@ -129,10 +129,12 @@ enum NodeType {
NodeTypeDirective,
NodeTypeReturnExpr,
NodeTypeVariableDeclaration,
+ NodeTypeErrorValueDecl,
NodeTypeBinOpExpr,
NodeTypeNumberLiteral,
NodeTypeStringLiteral,
NodeTypeCharLiteral,
+ NodeTypeErrorLiteral,
NodeTypeSymbol,
NodeTypePrefixOpExpr,
NodeTypeFnCallExpr,
@@ -222,7 +224,14 @@ struct AstNodeBlock {
Expr resolved_expr;
};
+enum ReturnKind {
+ ReturnKindUnconditional,
+ ReturnKindMaybe,
+ ReturnKindError,
+};
+
struct AstNodeReturnExpr {
+ ReturnKind kind;
// might be null in case of return void;
AstNode *expr;
@@ -243,6 +252,14 @@ struct AstNodeVariableDeclaration {
Expr resolved_expr;
};
+struct AstNodeErrorValueDecl {
+ VisibMod visib_mod;
+ Buf name;
+
+ // populated by semantic analyzer
+ TopLevelDecl top_level_decl;
+};
+
enum BinOpType {
BinOpTypeInvalid,
BinOpTypeAssign,
@@ -358,6 +375,7 @@ enum PrefixOp {
PrefixOpConstAddressOf,
PrefixOpDereference,
PrefixOpMaybe,
+ PrefixOpError,
};
struct AstNodePrefixOpExpr {
@@ -564,6 +582,14 @@ struct AstNodeNumberLiteral {
Expr resolved_expr;
};
+struct AstNodeErrorLiteral {
+ Buf symbol;
+
+ // populated by semantic analyzer
+ NumLitCodeGen codegen;
+ Expr resolved_expr;
+};
+
struct AstNodeStructValueField {
Buf name;
AstNode *expr;
@@ -644,6 +670,7 @@ struct AstNode {
AstNodeBlock block;
AstNodeReturnExpr return_expr;
AstNodeVariableDeclaration variable_declaration;
+ AstNodeErrorValueDecl error_value_decl;
AstNodeBinOpExpr bin_op_expr;
AstNodeExternBlock extern_block;
AstNodeDirective directive;
@@ -668,6 +695,7 @@ struct AstNode {
AstNodeStringLiteral string_literal;
AstNodeCharLiteral char_literal;
AstNodeNumberLiteral number_literal;
+ AstNodeErrorLiteral error_literal;
AstNodeContainerInitExpr container_init_expr;
AstNodeStructValueField struct_val_field;
AstNodeNullLiteral null_literal;
@@ -738,6 +766,10 @@ struct TypeTableEntryMaybe {
TypeTableEntry *child_type;
};
+struct TypeTableEntryError {
+ TypeTableEntry *child_type;
+};
+
struct TypeTableEntryEnum {
AstNode *decl_node;
uint32_t field_count;
@@ -778,6 +810,7 @@ enum TypeTableEntryId {
TypeTableEntryIdStruct,
TypeTableEntryIdNumberLiteral,
TypeTableEntryIdMaybe,
+ TypeTableEntryIdError,
TypeTableEntryIdEnum,
TypeTableEntryIdFn,
};
@@ -799,6 +832,7 @@ struct TypeTableEntry {
TypeTableEntryStruct structure;
TypeTableEntryNumLit num_lit;
TypeTableEntryMaybe maybe;
+ TypeTableEntryError error;
TypeTableEntryEnum enumeration;
TypeTableEntryFn fn;
} data;
@@ -808,6 +842,7 @@ struct TypeTableEntry {
TypeTableEntry *unknown_size_array_parent[2];
HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size;
TypeTableEntry *maybe_parent;
+ TypeTableEntry *error_parent;
};
struct ImporterInfo {
src/analyze.cpp
@@ -43,7 +43,9 @@ static AstNode *first_executing_node(AstNode *node) {
case NodeTypeDirective:
case NodeTypeReturnExpr:
case NodeTypeVariableDeclaration:
+ case NodeTypeErrorValueDecl:
case NodeTypeNumberLiteral:
+ case NodeTypeErrorLiteral:
case NodeTypeStringLiteral:
case NodeTypeCharLiteral:
case NodeTypeSymbol:
@@ -104,6 +106,7 @@ TypeTableEntry *new_type_table_entry(TypeTableEntryId id) {
case TypeTableEntryIdNumberLiteral:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdFn:
+ case TypeTableEntryIdError:
// nothing to init
break;
case TypeTableEntryIdStruct:
@@ -215,6 +218,57 @@ static TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
}
}
+static TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
+ if (child_type->error_parent) {
+ return child_type->error_parent;
+ } else {
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdError);
+ zig_panic("TODO get_error_type");
+ // create a struct with a boolean whether this is the null value
+ assert(child_type->type_ref);
+ LLVMTypeRef elem_types[] = {
+ child_type->type_ref,
+ LLVMInt1Type(),
+ };
+ entry->type_ref = LLVMStructType(elem_types, 2, false);
+ buf_resize(&entry->name, 0);
+ buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name));
+ entry->size_in_bits = child_type->size_in_bits + 8;
+ entry->align_in_bits = child_type->align_in_bits;
+ assert(child_type->di_type);
+
+
+ LLVMZigDIScope *compile_unit_scope = LLVMZigCompileUnitToScope(g->compile_unit);
+ LLVMZigDIFile *di_file = nullptr;
+ unsigned line = 0;
+ entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder,
+ LLVMZigTag_DW_structure_type(), buf_ptr(&entry->name),
+ compile_unit_scope, di_file, line);
+
+ LLVMZigDIType *di_element_types[] = {
+ LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type),
+ "val", di_file, line, child_type->size_in_bits, child_type->align_in_bits, 0, 0,
+ child_type->di_type),
+ LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type),
+ "maybe", di_file, line, 8, 8, 8, 0,
+ child_type->di_type),
+ };
+ LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder,
+ compile_unit_scope,
+ buf_ptr(&entry->name),
+ di_file, line, entry->size_in_bits, entry->align_in_bits, 0,
+ nullptr, di_element_types, 2, 0, nullptr, "");
+
+ LLVMZigReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type);
+ entry->di_type = replacement_di_type;
+
+ entry->data.maybe.child_type = child_type;
+
+ child_type->maybe_parent = entry;
+ return entry;
+ }
+}
+
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);
@@ -922,6 +976,11 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
g->global_vars.append(var);
break;
}
+ case NodeTypeErrorValueDecl:
+ {
+ zig_panic("TODO resolve_top_level_decl NodeTypeErrorValueDecl");
+ break;
+ }
case NodeTypeUse:
// nothing to do here
break;
@@ -937,6 +996,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
case NodeTypeArrayAccessExpr:
case NodeTypeSliceExpr:
case NodeTypeNumberLiteral:
+ case NodeTypeErrorLiteral:
case NodeTypeStringLiteral:
case NodeTypeCharLiteral:
case NodeTypeBoolLiteral:
@@ -1005,6 +1065,7 @@ static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type,
case TypeTableEntryIdEnum:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdFn:
+ case TypeTableEntryIdError:
return false;
case TypeTableEntryIdInt:
if (is_num_lit_unsigned(num_lit)) {
@@ -2263,6 +2324,12 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry
}
}
+static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import,
+ BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node)
+{
+ zig_panic("TODO analyze_error_literal_expr");
+}
+
static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
@@ -3021,7 +3088,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
if (meta_type->id == TypeTableEntryIdInvalid) {
return g->builtin_types.entry_invalid;
} else if (meta_type->id == TypeTableEntryIdUnreachable) {
- add_node_error(g, node, buf_create_from_str("maybe unreachable type not allowed"));
+ add_node_error(g, node, buf_create_from_str("unable to wrap unreachable in maybe type"));
return g->builtin_types.entry_invalid;
} else {
return resolve_expr_const_val_as_type(g, node, get_maybe_type(g, meta_type));
@@ -3034,6 +3101,31 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
return get_maybe_type(g, type_entry);
}
}
+ case PrefixOpError:
+ {
+ TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node);
+
+ if (type_entry->id == TypeTableEntryIdInvalid) {
+ return type_entry;
+ } else if (type_entry->id == TypeTableEntryIdMetaType) {
+ TypeTableEntry *meta_type = resolve_type(g, expr_node);
+ if (meta_type->id == TypeTableEntryIdInvalid) {
+ return meta_type;
+ } else if (meta_type->id == TypeTableEntryIdUnreachable) {
+ add_node_error(g, node, buf_create_from_str("unable to wrap unreachable in error type"));
+ return g->builtin_types.entry_invalid;
+ } else {
+ return resolve_expr_const_val_as_type(g, node, get_error_type(g, meta_type));
+ }
+ } else if (type_entry->id == TypeTableEntryIdUnreachable) {
+ add_node_error(g, expr_node, buf_sprintf("unable to wrap unreachable in error type"));
+ return g->builtin_types.entry_invalid;
+ } else {
+ // TODO eval const expr
+ return get_error_type(g, type_entry);
+ }
+
+ }
}
zig_unreachable();
}
@@ -3099,6 +3191,37 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
return expected_type;
}
+static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+ TypeTableEntry *expected_type, AstNode *node)
+{
+ if (!context->fn_entry) {
+ add_node_error(g, node, buf_sprintf("return expression outside function definition"));
+ return g->builtin_types.entry_invalid;
+ }
+
+ if (node->data.return_expr.kind != ReturnKindUnconditional) {
+ zig_panic("TODO analyze_return_expr conditional");
+ }
+
+ TypeTableEntry *expected_return_type = get_return_type(context);
+ TypeTableEntry *actual_return_type;
+ if (node->data.return_expr.expr) {
+ actual_return_type = analyze_expression(g, import, context, expected_return_type, node->data.return_expr.expr);
+ } else {
+ actual_return_type = g->builtin_types.entry_void;
+ }
+
+ if (actual_return_type->id == TypeTableEntryIdUnreachable) {
+ // "return exit(0)" should just be "exit(0)".
+ add_node_error(g, node, buf_sprintf("returning is unreachable"));
+ actual_return_type = g->builtin_types.entry_invalid;
+ }
+
+ resolve_type_compatibility(g, context, node, expected_return_type, actual_return_type);
+
+ return g->builtin_types.entry_unreachable;
+}
+
static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
@@ -3140,29 +3263,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
}
case NodeTypeReturnExpr:
- {
- if (context->fn_entry) {
- TypeTableEntry *expected_return_type = get_return_type(context);
- TypeTableEntry *actual_return_type;
- if (node->data.return_expr.expr) {
- actual_return_type = analyze_expression(g, import, context, expected_return_type, node->data.return_expr.expr);
- } else {
- actual_return_type = g->builtin_types.entry_void;
- }
-
- if (actual_return_type->id == TypeTableEntryIdUnreachable) {
- // "return exit(0)" should just be "exit(0)".
- add_node_error(g, node, buf_sprintf("returning is unreachable"));
- actual_return_type = g->builtin_types.entry_invalid;
- }
-
- resolve_type_compatibility(g, context, node, expected_return_type, actual_return_type);
- } else {
- add_node_error(g, node, buf_sprintf("return expression outside function definition"));
- }
- return_type = g->builtin_types.entry_unreachable;
- break;
- }
+ return_type = analyze_return_expr(g, import, context, expected_type, node);
+ break;
case NodeTypeVariableDeclaration:
analyze_variable_declaration(g, import, context, expected_type, node);
return_type = g->builtin_types.entry_void;
@@ -3236,6 +3338,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
case NodeTypeNumberLiteral:
return_type = analyze_number_literal_expr(g, import, context, expected_type, node);
break;
+ case NodeTypeErrorLiteral:
+ return_type = analyze_error_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;
@@ -3294,6 +3399,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
case NodeTypeStructDecl:
case NodeTypeStructField:
case NodeTypeStructValueField:
+ case NodeTypeErrorValueDecl:
zig_unreachable();
}
assert(return_type);
@@ -3411,6 +3517,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
case NodeTypeExternBlock:
case NodeTypeUse:
case NodeTypeVariableDeclaration:
+ case NodeTypeErrorValueDecl:
// already took care of these
break;
case NodeTypeDirective:
@@ -3425,6 +3532,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
case NodeTypeArrayAccessExpr:
case NodeTypeSliceExpr:
case NodeTypeNumberLiteral:
+ case NodeTypeErrorLiteral:
case NodeTypeStringLiteral:
case NodeTypeCharLiteral:
case NodeTypeBoolLiteral:
@@ -3457,6 +3565,7 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode
{
switch (node->type) {
case NodeTypeNumberLiteral:
+ case NodeTypeErrorLiteral:
case NodeTypeStringLiteral:
case NodeTypeCharLiteral:
case NodeTypeBoolLiteral:
@@ -3464,6 +3573,7 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode
case NodeTypeGoto:
case NodeTypeBreak:
case NodeTypeContinue:
+ case NodeTypeErrorValueDecl:
// no dependencies on other top level declarations
break;
case NodeTypeSymbol:
@@ -3758,6 +3868,10 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
case NodeTypeUse:
// already taken care of
break;
+ case NodeTypeErrorValueDecl:
+ // error value declarations do not depend on other top level decls
+ resolve_top_level_decl(g, import, node);
+ break;
case NodeTypeDirective:
case NodeTypeParamDecl:
case NodeTypeFnDecl:
@@ -3769,6 +3883,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
case NodeTypeArrayAccessExpr:
case NodeTypeSliceExpr:
case NodeTypeNumberLiteral:
+ case NodeTypeErrorLiteral:
case NodeTypeStringLiteral:
case NodeTypeCharLiteral:
case NodeTypeBoolLiteral:
@@ -3966,6 +4081,8 @@ Expr *get_resolved_expr(AstNode *node) {
return &node->data.container_init_expr.resolved_expr;
case NodeTypeNumberLiteral:
return &node->data.number_literal.resolved_expr;
+ case NodeTypeErrorLiteral:
+ return &node->data.error_literal.resolved_expr;
case NodeTypeStringLiteral:
return &node->data.string_literal.resolved_expr;
case NodeTypeBlock:
@@ -4006,6 +4123,7 @@ Expr *get_resolved_expr(AstNode *node) {
case NodeTypeStructDecl:
case NodeTypeStructField:
case NodeTypeStructValueField:
+ case NodeTypeErrorValueDecl:
zig_unreachable();
}
zig_unreachable();
@@ -4015,6 +4133,8 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) {
switch (node->type) {
case NodeTypeNumberLiteral:
return &node->data.number_literal.codegen;
+ case NodeTypeErrorLiteral:
+ return &node->data.error_literal.codegen;
case NodeTypeFnCallExpr:
return &node->data.fn_call_expr.resolved_num_lit;
case NodeTypeReturnExpr:
@@ -4056,6 +4176,7 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) {
case NodeTypeStructField:
case NodeTypeStructValueField:
case NodeTypeArrayType:
+ case NodeTypeErrorValueDecl:
zig_unreachable();
}
zig_unreachable();
@@ -4069,7 +4190,10 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
return &node->data.fn_proto.top_level_decl;
case NodeTypeStructDecl:
return &node->data.struct_decl.top_level_decl;
+ case NodeTypeErrorValueDecl:
+ return &node->data.error_value_decl.top_level_decl;
case NodeTypeNumberLiteral:
+ case NodeTypeErrorLiteral:
case NodeTypeReturnExpr:
case NodeTypeBinOpExpr:
case NodeTypePrefixOpExpr:
src/codegen.cpp
@@ -829,6 +829,10 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
{
zig_panic("TODO codegen PrefixOpMaybe");
}
+ case PrefixOpError:
+ {
+ zig_panic("TODO codegen PrefixOpError");
+ }
}
zig_unreachable();
}
@@ -1937,6 +1941,12 @@ static LLVMValueRef gen_number_literal(CodeGen *g, AstNode *node) {
return gen_number_literal_raw(g, node, codegen_num_lit, &node->data.number_literal);
}
+static LLVMValueRef gen_error_literal(CodeGen *g, AstNode *node) {
+ assert(node->type == NodeTypeErrorLiteral);
+
+ zig_panic("TODO gen_error_literal");
+}
+
static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeSymbol);
VariableTableEntry *variable = node->data.symbol_expr.variable;
@@ -2070,6 +2080,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
return gen_asm_expr(g, node);
case NodeTypeNumberLiteral:
return gen_number_literal(g, node);
+ case NodeTypeErrorLiteral:
+ return gen_error_literal(g, node);
case NodeTypeStringLiteral:
{
Buf *str = &node->data.string_literal.buf;
@@ -2125,6 +2137,7 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
case NodeTypeArrayType:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
+ case NodeTypeErrorValueDecl:
zig_unreachable();
}
zig_unreachable();
src/parser.cpp
@@ -63,6 +63,16 @@ static const char *prefix_op_str(PrefixOp prefix_op) {
case PrefixOpConstAddressOf: return "&const";
case PrefixOpDereference: return "*";
case PrefixOpMaybe: return "?";
+ case PrefixOpError: return "%";
+ }
+ zig_unreachable();
+}
+
+static const char *return_prefix_str(ReturnKind kind) {
+ switch (kind) {
+ case ReturnKindError: return "%";
+ case ReturnKindMaybe: return "?";
+ case ReturnKindUnconditional: return "";
}
zig_unreachable();
}
@@ -99,8 +109,12 @@ const char *node_type_str(NodeType node_type) {
return "ReturnExpr";
case NodeTypeVariableDeclaration:
return "VariableDeclaration";
+ case NodeTypeErrorValueDecl:
+ return "ErrorValueDecl";
case NodeTypeNumberLiteral:
return "NumberLiteral";
+ case NodeTypeErrorLiteral:
+ return "ErrorLiteral";
case NodeTypeStringLiteral:
return "StringLiteral";
case NodeTypeCharLiteral:
@@ -214,10 +228,13 @@ void ast_print(AstNode *node, int indent) {
break;
}
case NodeTypeReturnExpr:
- fprintf(stderr, "%s\n", node_type_str(node->type));
- if (node->data.return_expr.expr)
- ast_print(node->data.return_expr.expr, indent + 2);
- break;
+ {
+ const char *prefix_str = return_prefix_str(node->data.return_expr.kind);
+ fprintf(stderr, "%s%s\n", prefix_str, node_type_str(node->type));
+ if (node->data.return_expr.expr)
+ ast_print(node->data.return_expr.expr, indent + 2);
+ break;
+ }
case NodeTypeVariableDeclaration:
{
Buf *name_buf = &node->data.variable_declaration.symbol;
@@ -228,6 +245,12 @@ void ast_print(AstNode *node, int indent) {
ast_print(node->data.variable_declaration.expr, indent + 2);
break;
}
+ case NodeTypeErrorValueDecl:
+ {
+ Buf *name_buf = &node->data.error_value_decl.name;
+ fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(name_buf));
+ break;
+ }
case NodeTypeExternBlock:
{
fprintf(stderr, "%s\n", node_type_str(node->type));
@@ -288,6 +311,11 @@ void ast_print(AstNode *node, int indent) {
}
break;
}
+ case NodeTypeErrorLiteral:
+ {
+ fprintf(stderr, "%s '%s'", node_type_str(node->type), buf_ptr(&node->data.error_literal.symbol));
+ break;
+ }
case NodeTypeStringLiteral:
{
const char *c = node->data.string_literal.c ? "c" : "";
@@ -1345,7 +1373,7 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand
}
/*
-PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | token(Symbol) | (token(AtSign) token(Symbol) FnCallExpression) | ArrayType | AsmExpression
+PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression | ("%." "Symbol")
KeywordLiteral : token(True) | token(False) | token(Null) | token(Break) | token(Continue)
*/
static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
@@ -1415,6 +1443,12 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
ast_buf_from_token(pc, dest_symbol, &node->data.goto_expr.name);
return node;
+ } else if (token->id == TokenIdPercentDot) {
+ *token_index += 1;
+ Token *symbol_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
+ AstNode *node = ast_create_node(pc, NodeTypeErrorLiteral, token);
+ ast_buf_from_token(pc, symbol_tok, &node->data.error_literal.symbol);
+ return node;
}
AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);
@@ -1612,6 +1646,7 @@ static PrefixOp tok_to_prefix_op(Token *token) {
case TokenIdAmpersand: return PrefixOpAddressOf;
case TokenIdStar: return PrefixOpDereference;
case TokenIdMaybe: return PrefixOpMaybe;
+ case TokenIdPercent: return PrefixOpError;
case TokenIdBoolAnd: return PrefixOpAddressOf;
default: return PrefixOpInvalid;
}
@@ -2031,20 +2066,46 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda
}
/*
-ReturnExpression : token(Return) option(Expression)
+ReturnExpression : option("%" | "?") "return" option(Expression)
*/
static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool mandatory) {
- Token *return_tok = &pc->tokens->at(*token_index);
- if (return_tok->id == TokenIdKeywordReturn) {
+ Token *token = &pc->tokens->at(*token_index);
+
+ ReturnKind kind;
+
+ if (token->id == TokenIdPercent) {
+ Token *next_token = &pc->tokens->at(*token_index + 1);
+ if (next_token->id == TokenIdKeywordReturn) {
+ kind = ReturnKindError;
+ *token_index += 2;
+ } else if (mandatory) {
+ ast_invalid_token_error(pc, token);
+ } else {
+ return nullptr;
+ }
+ } else if (token->id == TokenIdMaybe) {
+ Token *next_token = &pc->tokens->at(*token_index + 1);
+ if (next_token->id == TokenIdKeywordReturn) {
+ kind = ReturnKindMaybe;
+ *token_index += 2;
+ } else if (mandatory) {
+ ast_invalid_token_error(pc, token);
+ } else {
+ return nullptr;
+ }
+ } else if (token->id == TokenIdKeywordReturn) {
+ kind = ReturnKindUnconditional;
*token_index += 1;
- AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, return_tok);
- node->data.return_expr.expr = ast_parse_expression(pc, token_index, false);
- return node;
} else if (mandatory) {
- ast_invalid_token_error(pc, return_tok);
+ ast_invalid_token_error(pc, token);
} else {
return nullptr;
}
+
+ AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, token);
+ node->data.return_expr.kind = kind;
+ node->data.return_expr.expr = ast_parse_expression(pc, token_index, false);
+ return node;
}
/*
@@ -2054,27 +2115,46 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token
Token *first_token = &pc->tokens->at(*token_index);
VisibMod visib_mod;
+ bool is_const;
if (first_token->id == TokenIdKeywordPub) {
- *token_index += 1;
- visib_mod = VisibModPub;
+ Token *next_token = &pc->tokens->at(*token_index + 1);
+ if (next_token->id == TokenIdKeywordVar ||
+ next_token->id == TokenIdKeywordConst)
+ {
+ visib_mod = VisibModPub;
+ is_const = (next_token->id == TokenIdKeywordConst);
+ *token_index += 2;
+ } else if (mandatory) {
+ ast_invalid_token_error(pc, next_token);
+ } else {
+ return nullptr;
+ }
} else if (first_token->id == TokenIdKeywordExport) {
- *token_index += 1;
- visib_mod = VisibModExport;
+ Token *next_token = &pc->tokens->at(*token_index + 1);
+ if (next_token->id == TokenIdKeywordVar ||
+ next_token->id == TokenIdKeywordConst)
+ {
+ visib_mod = VisibModExport;
+ is_const = (next_token->id == TokenIdKeywordConst);
+ *token_index += 2;
+ } else if (mandatory) {
+ ast_invalid_token_error(pc, next_token);
+ } else {
+ return nullptr;
+ }
} else if (first_token->id == TokenIdKeywordVar ||
first_token->id == TokenIdKeywordConst)
{
visib_mod = VisibModPrivate;
+ is_const = (first_token->id == TokenIdKeywordConst);
+ *token_index += 1;
} else if (mandatory) {
ast_invalid_token_error(pc, first_token);
} else {
return nullptr;
}
- Token *var_or_const_tok = &pc->tokens->at(*token_index);
- bool is_const = (var_or_const_tok->id == TokenIdKeywordConst);
- *token_index += 1;
-
AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, first_token);
node->data.variable_declaration.is_const = is_const;
@@ -2836,7 +2916,54 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) {
}
/*
-TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use | StructDecl | VariableDeclaration | EnumDecl
+ErrorValueDecl : option(FnVisibleMod) "%." "Symbol"
+*/
+static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index, bool mandatory) {
+ Token *first_token = &pc->tokens->at(*token_index);
+
+ VisibMod visib_mod;
+
+ if (first_token->id == TokenIdKeywordPub) {
+ Token *next_token = &pc->tokens->at(*token_index + 1);
+ if (next_token->id == TokenIdPercentDot) {
+ visib_mod = VisibModPub;
+ *token_index += 2;
+ } else if (mandatory) {
+ ast_invalid_token_error(pc, next_token);
+ } else {
+ return nullptr;
+ }
+ } else if (first_token->id == TokenIdKeywordExport) {
+ Token *next_token = &pc->tokens->at(*token_index + 1);
+ if (next_token->id == TokenIdPercentDot) {
+ visib_mod = VisibModExport;
+ *token_index += 2;
+ } else if (mandatory) {
+ ast_invalid_token_error(pc, next_token);
+ } else {
+ return nullptr;
+ }
+ } else if (first_token->id == TokenIdPercentDot) {
+ visib_mod = VisibModPrivate;
+ *token_index += 1;
+ } else if (mandatory) {
+ ast_invalid_token_error(pc, first_token);
+ } else {
+ return nullptr;
+ }
+
+ Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
+ ast_eat_token(pc, token_index, TokenIdSemicolon);
+
+ AstNode *node = ast_create_node(pc, NodeTypeErrorValueDecl, first_token);
+ node->data.error_value_decl.visib_mod = visib_mod;
+ ast_buf_from_token(pc, name_tok, &node->data.error_value_decl.name);
+
+ return node;
+}
+
+/*
+TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Import | ContainerDecl | VariableDeclaration | ErrorValueDecl
*/
static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList<AstNode *> *top_level_decls) {
for (;;) {
@@ -2887,6 +3014,12 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis
continue;
}
+ AstNode *error_value_node = ast_parse_error_value_decl(pc, token_index, false);
+ if (error_value_node) {
+ top_level_decls->append(error_value_node);
+ continue;
+ }
+
return;
}
zig_unreachable();
src/tokenizer.cpp
@@ -584,6 +584,11 @@ void tokenize(Buf *buf, Tokenization *out) {
end_token(&t);
t.state = TokenizeStateStart;
break;
+ case '.':
+ t.cur_tok->id = TokenIdPercentDot;
+ end_token(&t);
+ t.state = TokenizeStateStart;
+ break;
default:
t.pos -= 1;
end_token(&t);
@@ -1092,6 +1097,7 @@ const char * token_name(TokenId id) {
case TokenIdDoubleQuestion: return "??";
case TokenIdMaybeAssign: return "?=";
case TokenIdAtSign: return "@";
+ case TokenIdPercentDot: return "%.";
}
return "(invalid token)";
}
src/tokenizer.hpp
@@ -91,6 +91,7 @@ enum TokenId {
TokenIdDoubleQuestion,
TokenIdMaybeAssign,
TokenIdAtSign,
+ TokenIdPercentDot,
};
struct Token {
std/std.zig
@@ -1,43 +1,177 @@
import "syscall.zig";
+//import "errno.zig";
pub const stdin_fileno : isize = 0;
pub const stdout_fileno : isize = 1;
pub const stderr_fileno : isize = 2;
-// TODO error handling
-pub fn os_get_random_bytes(buf: []u8) isize => {
- getrandom(buf.ptr, buf.len, 0)
+/*
+pub var stdin = InStream {
+ .fd = stdin_fileno,
+};
+
+pub var stdout = OutStream {
+ .fd = stdout_fileno,
+ .buffer = uninitialized,
+ .index = 0,
+ .buffered = true,
+};
+
+pub var stderr = OutStream {
+ .fd = stderr_fileno,
+ .buffer = uninitialized,
+ .index = 0,
+ .buffered = false,
+};
+
+pub %.Unexpected;
+pub %.DiskQuota;
+pub %.FileTooBig;
+pub %.SigInterrupt;
+pub %.Io;
+pub %.NoSpaceLeft;
+pub %.BadPerm;
+pub %.PipeFail;
+*/
+
+const buffer_size: u16 = 4 * 1024;
+const max_u64_base10_digits: isize = 20;
+
+/*
+pub struct OutStream {
+ fd: isize,
+ buffer: [buffer_size]u8,
+ index: @typeof(buffer_size),
+ buffered: bool,
+
+ pub fn print_str(os: &OutStream, str: []const u8) %isize => {
+ var src_bytes_left = str.len;
+ var src_index: @typeof(str.len) = 0;
+ const dest_space_left = os.buffer.len - index;
+
+ while (src_bytes_left > 0) {
+ const copy_amt = min_isize(dest_space_left, src_bytes_left);
+ @memcpy(&buffer[os.index], &str[src_index], copy_amt);
+ os.index += copy_amt;
+ if (os.index == os.buffer.len) {
+ %return os.flush();
+ }
+ src_bytes_left -= copy_amt;
+ }
+ if (!os.buffered) {
+ %return os.flush();
+ }
+ return str.len;
+ }
+
+ pub fn print_u64(os: &OutStream, x: u64) %isize => {
+ if (os.index + max_u64_base10_digits >= os.buffer.len) {
+ %return os.flush();
+ }
+ const amt_printed = buf_print_u64(buf[os.index...], x);
+ os.index += amt_printed;
+
+ if (!os.buffered) {
+ %return os.flush();
+ }
+
+ return amt_printed;
+ }
+
+
+ pub fn print_i64(os: &OutStream, x: i64) %isize => {
+ if (os.index + max_u64_base10_digits >= os.buffer.len) {
+ %return os.flush();
+ }
+ const amt_printed = buf_print_i64(buf[os.index...], x);
+ os.index += amt_printed;
+
+ if (!os.buffered) {
+ %return os.flush();
+ }
+
+ return amt_printed;
+ }
+
+
+ pub fn flush(os: &OutStream) %void => {
+ const amt_to_write = os.index;
+ os.index = 0;
+ switch (write(fd, os.buffer.ptr, amt_to_write)) {
+ EINVAL => unreachable{},
+ EDQUOT => %.DiskQuota,
+ EFBIG => %.FileTooBig,
+ EINTR => %.SigInterrupt,
+ EIO => %.Io,
+ ENOSPC => %.NoSpaceLeft,
+ EPERM => %.BadPerm,
+ EPIPE => %.PipeFail,
+ else => %.Unexpected,
+ }
+ }
+}
+
+pub struct InStream {
+ fd: isize,
+
+ pub fn readline(buf: []u8) %isize => {
+ const amt_read = read(stdin_fileno, buf.ptr, buf.len);
+ if (amt_read < 0) {
+ switch (-amt_read) {
+ EINVAL => unreachable{},
+ EFAULT => unreachable{},
+ EBADF => %.BadFd,
+ EINTR => %.SigInterrupt,
+ EIO => %.Io,
+ else => %.Unexpected,
+ }
+ }
+ return amt_read;
+ }
+
+}
+
+pub fn os_get_random_bytes(buf: []u8) %void => {
+ switch (getrandom(buf.ptr, buf.len, 0)) {
+ EINVAL => unreachable{},
+ EFAULT => unreachable{},
+ EINTR => %.SigInterrupt,
+ else => %.Unexpected,
+ }
}
+*/
+
-// TODO error handling
-// TODO handle buffering and flushing (mutex protected)
+// TODO remove this
pub fn print_str(str: []const u8) isize => {
fprint_str(stdout_fileno, str)
}
-// TODO error handling
-// TODO handle buffering and flushing (mutex protected)
+// TODO remove this
pub fn fprint_str(fd: isize, str: []const u8) isize => {
write(fd, str.ptr, str.len)
}
-// TODO handle buffering and flushing (mutex protected)
-// TODO error handling
+// TODO remove this
+pub fn os_get_random_bytes(buf: []u8) isize => {
+ getrandom(buf.ptr, buf.len, 0)
+}
+
+// TODO remove this
pub fn print_u64(x: u64) isize => {
var buf: [max_u64_base10_digits]u8;
const len = buf_print_u64(buf, x);
return write(stdout_fileno, buf.ptr, len);
}
-// TODO handle buffering and flushing (mutex protected)
-// TODO error handling
+// TODO remove this
pub fn print_i64(x: i64) isize => {
var buf: [max_u64_base10_digits]u8;
const len = buf_print_i64(buf, x);
return write(stdout_fileno, buf.ptr, len);
}
-// TODO error handling
+// TODO remove this
pub fn readline(buf: []u8, out_len: &isize) bool => {
const amt_read = read(stdin_fileno, buf.ptr, buf.len);
if (amt_read < 0) {
@@ -47,7 +181,8 @@ pub fn readline(buf: []u8, out_len: &isize) bool => {
return false;
}
-// TODO return ?u64 when we support returning struct byval
+
+// TODO return %u64 when we support errors
pub fn parse_u64(buf: []u8, radix: u8, result: &u64) bool => {
var x : u64 = 0;
@@ -74,6 +209,7 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) bool => {
}
fn char_to_digit(c: u8) u8 => {
+ // TODO use switch with range
if ('0' <= c && c <= '9') {
c - '0'
} else if ('A' <= c && c <= 'Z') {
@@ -85,8 +221,6 @@ fn char_to_digit(c: u8) u8 => {
}
}
-const max_u64_base10_digits: isize = 20;
-
fn buf_print_i64(out_buf: []u8, x: i64) isize => {
if (x < 0) {
out_buf[0] = '-';
@@ -112,7 +246,7 @@ fn buf_print_u64(out_buf: []u8, x: u64) isize => {
const len = buf.len - index;
- @memcpy(out_buf.ptr, &buf[index], len);
+ @memcpy(&out_buf[0], &buf[index], len);
return len;
}