| author | |
| committer | |
| log | b7dd88ad68aab5b6bc8321431d1a53b343b2dd37 |
| tree | 78a9cd198eb94529a5841eb68c6bf57f9be21607 |
| parent | 14b9cbd43c21ad2ba75b38ef5fc681c044e7662e |
closes #3210 files changed, 205 insertions(+), 19 deletions(-)
doc/langref.md+1-1| ... | @@ -160,7 +160,7 @@ SliceExpression : token(LBracket) Expression token(Ellipsis) option(Expression) | ... | @@ -160,7 +160,7 @@ SliceExpression : token(LBracket) Expression token(Ellipsis) option(Expression) |
| 160 | 160 | ||
| 161 | PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const))) | 161 | PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const))) |
| 162 | 162 | ||
| 163 | PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | 163 | PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | (token(AtSign) token(Symbol) FnCallExpression) |
| 164 | 164 | ||
| 165 | StructValueExpression : token(Type) token(LBrace) list(StructValueExpressionField, token(Comma)) token(RBrace) | 165 | StructValueExpression : token(Type) token(LBrace) list(StructValueExpressionField, token(Comma)) token(RBrace) |
| 166 | 166 |
src/analyze.cpp+40-2| ... | @@ -2064,6 +2064,41 @@ static TypeTableEntry *analyze_compiler_fn_type(CodeGen *g, ImportTableEntry *im | ... | @@ -2064,6 +2064,41 @@ static TypeTableEntry *analyze_compiler_fn_type(CodeGen *g, ImportTableEntry *im |
| 2064 | } | 2064 | } |
| 2065 | } | 2065 | } |
| 2066 | 2066 | ||
| 2067 | static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | ||
| 2068 | TypeTableEntry *expected_type, AstNode *node) | ||
| 2069 | { | ||
| 2070 | AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; | ||
| 2071 | Buf *name = &fn_ref_expr->data.symbol; | ||
| 2072 | |||
| 2073 | auto entry = g->builtin_fn_table.maybe_get(name); | ||
| 2074 | |||
| 2075 | if (entry) { | ||
| 2076 | BuiltinFnEntry *builtin_fn = entry->value; | ||
| 2077 | int actual_param_count = node->data.fn_call_expr.params.length; | ||
| 2078 | |||
| 2079 | assert(node->codegen_node); | ||
| 2080 | node->codegen_node->data.fn_call_node.builtin_fn = builtin_fn; | ||
| 2081 | |||
| 2082 | if (builtin_fn->param_count != actual_param_count) { | ||
| 2083 | add_node_error(g, node, | ||
| 2084 | buf_sprintf("expected %d arguments, got %d", | ||
| 2085 | builtin_fn->param_count, actual_param_count)); | ||
| 2086 | } | ||
| 2087 | |||
| 2088 | for (int i = 0; i < actual_param_count; i += 1) { | ||
| 2089 | AstNode *child = node->data.fn_call_expr.params.at(i); | ||
| 2090 | TypeTableEntry *expected_param_type = builtin_fn->param_types[i]; | ||
| 2091 | analyze_expression(g, import, context, expected_param_type, child); | ||
| 2092 | } | ||
| 2093 | |||
| 2094 | return builtin_fn->return_type; | ||
| 2095 | } else { | ||
| 2096 | add_node_error(g, node, | ||
| 2097 | buf_sprintf("invalid builtin function: '%s'", buf_ptr(name))); | ||
| 2098 | return g->builtin_types.entry_invalid; | ||
| 2099 | } | ||
| 2100 | } | ||
| 2101 | |||
| 2067 | static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 2102 | static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 2068 | TypeTableEntry *expected_type, AstNode *node) | 2103 | TypeTableEntry *expected_type, AstNode *node) |
| 2069 | { | 2104 | { |
| ... | @@ -2091,6 +2126,9 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import | ... | @@ -2091,6 +2126,9 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import |
| 2091 | return g->builtin_types.entry_invalid; | 2126 | return g->builtin_types.entry_invalid; |
| 2092 | } | 2127 | } |
| 2093 | } else if (fn_ref_expr->type == NodeTypeSymbol) { | 2128 | } else if (fn_ref_expr->type == NodeTypeSymbol) { |
| 2129 | if (node->data.fn_call_expr.is_builtin) { | ||
| 2130 | return analyze_builtin_fn_call_expr(g, import, context, expected_type, node); | ||
| 2131 | } | ||
| 2094 | name = &fn_ref_expr->data.symbol; | 2132 | name = &fn_ref_expr->data.symbol; |
| 2095 | } else { | 2133 | } else { |
| 2096 | add_node_error(g, node, | 2134 | add_node_error(g, node, |
| ... | @@ -2126,12 +2164,12 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import | ... | @@ -2126,12 +2164,12 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import |
| 2126 | if (fn_proto->is_var_args) { | 2164 | if (fn_proto->is_var_args) { |
| 2127 | if (actual_param_count < expected_param_count) { | 2165 | if (actual_param_count < expected_param_count) { |
| 2128 | add_node_error(g, node, | 2166 | add_node_error(g, node, |
| 2129 | buf_sprintf("wrong number of arguments. Expected at least %d, got %d.", | 2167 | buf_sprintf("expected at least %d arguments, got %d", |
| 2130 | expected_param_count, actual_param_count)); | 2168 | expected_param_count, actual_param_count)); |
| 2131 | } | 2169 | } |
| 2132 | } else if (expected_param_count != actual_param_count) { | 2170 | } else if (expected_param_count != actual_param_count) { |
| 2133 | add_node_error(g, node, | 2171 | add_node_error(g, node, |
| 2134 | buf_sprintf("wrong number of arguments. Expected %d, got %d.", | 2172 | buf_sprintf("expected %d arguments, got %d", |
| 2135 | expected_param_count, actual_param_count)); | 2173 | expected_param_count, actual_param_count)); |
| 2136 | } | 2174 | } |
| 2137 | 2175 |
src/analyze.hpp+20-7| ... | @@ -148,6 +148,20 @@ struct FnTableEntry { | ... | @@ -148,6 +148,20 @@ struct FnTableEntry { |
| 148 | HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table; | 148 | HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table; |
| 149 | }; | 149 | }; |
| 150 | 150 | ||
| 151 | enum BuiltinFnId { | ||
| 152 | BuiltinFnIdInvalid, | ||
| 153 | BuiltinFnIdArithmeticWithOverflow, | ||
| 154 | }; | ||
| 155 | |||
| 156 | struct BuiltinFnEntry { | ||
| 157 | BuiltinFnId id; | ||
| 158 | Buf name; | ||
| 159 | int param_count; | ||
| 160 | TypeTableEntry *return_type; | ||
| 161 | TypeTableEntry **param_types; | ||
| 162 | LLVMValueRef fn_val; | ||
| 163 | }; | ||
| 164 | |||
| 151 | struct CodeGen { | 165 | struct CodeGen { |
| 152 | LLVMModuleRef module; | 166 | LLVMModuleRef module; |
| 153 | ZigList<ErrorMsg*> errors; | 167 | ZigList<ErrorMsg*> errors; |
| ... | @@ -161,6 +175,7 @@ struct CodeGen { | ... | @@ -161,6 +175,7 @@ struct CodeGen { |
| 161 | HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table; | 175 | HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table; |
| 162 | HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table; | 176 | HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table; |
| 163 | HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table; | 177 | HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table; |
| 178 | HashMap<Buf *, BuiltinFnEntry *, buf_hash, buf_eql_buf> builtin_fn_table; | ||
| 164 | 179 | ||
| 165 | struct { | 180 | struct { |
| 166 | TypeTableEntry *entry_bool; | 181 | TypeTableEntry *entry_bool; |
| ... | @@ -342,6 +357,10 @@ struct WhileNode { | ... | @@ -342,6 +357,10 @@ struct WhileNode { |
| 342 | bool contains_break; | 357 | bool contains_break; |
| 343 | }; | 358 | }; |
| 344 | 359 | ||
| 360 | struct FnCallNode { | ||
| 361 | BuiltinFnEntry *builtin_fn; | ||
| 362 | }; | ||
| 363 | |||
| 345 | struct CodeGenNode { | 364 | struct CodeGenNode { |
| 346 | union { | 365 | union { |
| 347 | TypeNode type_node; // for NodeTypeType | 366 | TypeNode type_node; // for NodeTypeType |
| ... | @@ -363,17 +382,11 @@ struct CodeGenNode { | ... | @@ -363,17 +382,11 @@ struct CodeGenNode { |
| 363 | ParamDeclNode param_decl_node; // for NodeTypeParamDecl | 382 | ParamDeclNode param_decl_node; // for NodeTypeParamDecl |
| 364 | ImportNode import_node; // for NodeTypeUse | 383 | ImportNode import_node; // for NodeTypeUse |
| 365 | WhileNode while_node; // for NodeTypeWhileExpr | 384 | WhileNode while_node; // for NodeTypeWhileExpr |
| 385 | FnCallNode fn_call_node; // for NodeTypeFnCallExpr | ||
| 366 | } data; | 386 | } data; |
| 367 | ExprNode expr_node; // for all the expression nodes | 387 | ExprNode expr_node; // for all the expression nodes |
| 368 | }; | 388 | }; |
| 369 | 389 | ||
| 370 | static inline Buf *hack_get_fn_call_name(CodeGen *g, AstNode *node) { | ||
| 371 | // Assume that the expression evaluates to a simple name and return the buf | ||
| 372 | // TODO after type checking works we should be able to remove this hack | ||
| 373 | assert(node->type == NodeTypeSymbol); | ||
| 374 | return &node->data.symbol; | ||
| 375 | } | ||
| 376 | |||
| 377 | void semantic_analyze(CodeGen *g); | 390 | void semantic_analyze(CodeGen *g); |
| 378 | void add_node_error(CodeGen *g, AstNode *node, Buf *msg); | 391 | void add_node_error(CodeGen *g, AstNode *node, Buf *msg); |
| 379 | void alloc_codegen_node(AstNode *node); | 392 | void alloc_codegen_node(AstNode *node); |
src/codegen.cpp+105-2| ... | @@ -22,6 +22,7 @@ CodeGen *codegen_create(Buf *root_source_dir) { | ... | @@ -22,6 +22,7 @@ CodeGen *codegen_create(Buf *root_source_dir) { |
| 22 | g->str_table.init(32); | 22 | g->str_table.init(32); |
| 23 | g->link_table.init(32); | 23 | g->link_table.init(32); |
| 24 | g->import_table.init(32); | 24 | g->import_table.init(32); |
| 25 | g->builtin_fn_table.init(32); | ||
| 25 | g->build_type = CodeGenBuildTypeDebug; | 26 | g->build_type = CodeGenBuildTypeDebug; |
| 26 | g->root_source_dir = root_source_dir; | 27 | g->root_source_dir = root_source_dir; |
| 27 | 28 | ||
| ... | @@ -139,6 +140,41 @@ static TypeTableEntry *get_expr_type(AstNode *node) { | ... | @@ -139,6 +140,41 @@ static TypeTableEntry *get_expr_type(AstNode *node) { |
| 139 | return cast_type ? cast_type : node->codegen_node->expr_node.type_entry; | 140 | return cast_type ? cast_type : node->codegen_node->expr_node.type_entry; |
| 140 | } | 141 | } |
| 141 | 142 | ||
| 143 | static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { | ||
| 144 | assert(node->type == NodeTypeFnCallExpr); | ||
| 145 | AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; | ||
| 146 | assert(fn_ref_expr->type == NodeTypeSymbol); | ||
| 147 | BuiltinFnEntry *builtin_fn = node->codegen_node->data.fn_call_node.builtin_fn; | ||
| 148 | |||
| 149 | switch (builtin_fn->id) { | ||
| 150 | case BuiltinFnIdInvalid: | ||
| 151 | zig_unreachable(); | ||
| 152 | case BuiltinFnIdArithmeticWithOverflow: | ||
| 153 | { | ||
| 154 | int fn_call_param_count = node->data.fn_call_expr.params.length; | ||
| 155 | assert(fn_call_param_count == 3); | ||
| 156 | |||
| 157 | LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(0)); | ||
| 158 | LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(1)); | ||
| 159 | LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(2)); | ||
| 160 | |||
| 161 | LLVMValueRef params[] = { | ||
| 162 | op1, | ||
| 163 | op2, | ||
| 164 | }; | ||
| 165 | |||
| 166 | add_debug_source_node(g, node); | ||
| 167 | LLVMValueRef result_struct = LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 2, ""); | ||
| 168 | LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); | ||
| 169 | LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); | ||
| 170 | LLVMBuildStore(g->builder, result, ptr_result); | ||
| 171 | |||
| 172 | return overflow_bit; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | zig_unreachable(); | ||
| 176 | } | ||
| 177 | |||
| 142 | static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { | 178 | static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { |
| 143 | assert(node->type == NodeTypeFnCallExpr); | 179 | assert(node->type == NodeTypeFnCallExpr); |
| 144 | 180 | ||
| ... | @@ -159,7 +195,15 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { | ... | @@ -159,7 +195,15 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { |
| 159 | zig_unreachable(); | 195 | zig_unreachable(); |
| 160 | } | 196 | } |
| 161 | } else if (fn_ref_expr->type == NodeTypeSymbol) { | 197 | } else if (fn_ref_expr->type == NodeTypeSymbol) { |
| 162 | Buf *name = hack_get_fn_call_name(g, fn_ref_expr); | 198 | if (node->data.fn_call_expr.is_builtin) { |
| 199 | return gen_builtin_fn_call_expr(g, node); | ||
| 200 | } | ||
| 201 | |||
| 202 | // Assume that the expression evaluates to a simple name and return the buf | ||
| 203 | // TODO after we support function pointers we can make this generic | ||
| 204 | assert(fn_ref_expr->type == NodeTypeSymbol); | ||
| 205 | Buf *name = &fn_ref_expr->data.symbol; | ||
| 206 | |||
| 163 | struct_type = nullptr; | 207 | struct_type = nullptr; |
| 164 | first_param_expr = nullptr; | 208 | first_param_expr = nullptr; |
| 165 | fn_table_entry = g->cur_fn->import_entry->fn_table.get(name); | 209 | fn_table_entry = g->cur_fn->import_entry->fn_table.get(name); |
| ... | @@ -2167,6 +2211,64 @@ static void define_builtin_types(CodeGen *g) { | ... | @@ -2167,6 +2211,64 @@ static void define_builtin_types(CodeGen *g) { |
| 2167 | } | 2211 | } |
| 2168 | } | 2212 | } |
| 2169 | 2213 | ||
| 2214 | static void define_builtin_fns_int(CodeGen *g, TypeTableEntry *type_entry) { | ||
| 2215 | assert(type_entry->id == TypeTableEntryIdInt); | ||
| 2216 | struct OverflowFn { | ||
| 2217 | const char *bare_name; | ||
| 2218 | const char *signed_name; | ||
| 2219 | const char *unsigned_name; | ||
| 2220 | }; | ||
| 2221 | OverflowFn overflow_fns[] = { | ||
| 2222 | {"add", "sadd", "uadd"}, | ||
| 2223 | {"sub", "ssub", "usub"}, | ||
| 2224 | {"mul", "smul", "umul"}, | ||
| 2225 | }; | ||
| 2226 | for (int i = 0; i < sizeof(overflow_fns)/sizeof(overflow_fns[0]); i += 1) { | ||
| 2227 | OverflowFn *overflow_fn = &overflow_fns[i]; | ||
| 2228 | BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1); | ||
| 2229 | buf_resize(&builtin_fn->name, 0); | ||
| 2230 | buf_appendf(&builtin_fn->name, "%s_with_overflow_%s", overflow_fn->bare_name, buf_ptr(&type_entry->name)); | ||
| 2231 | builtin_fn->id = BuiltinFnIdArithmeticWithOverflow; | ||
| 2232 | builtin_fn->return_type = g->builtin_types.entry_bool; | ||
| 2233 | builtin_fn->param_count = 3; | ||
| 2234 | builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count); | ||
| 2235 | builtin_fn->param_types[0] = type_entry; | ||
| 2236 | builtin_fn->param_types[1] = type_entry; | ||
| 2237 | builtin_fn->param_types[2] = get_pointer_to_type(g, type_entry, false, false); | ||
| 2238 | |||
| 2239 | |||
| 2240 | const char *signed_str = type_entry->data.integral.is_signed ? | ||
| 2241 | overflow_fn->signed_name : overflow_fn->unsigned_name; | ||
| 2242 | Buf *llvm_name = buf_sprintf("llvm.%s.with.overflow.i%" PRIu64, signed_str, type_entry->size_in_bits); | ||
| 2243 | |||
| 2244 | LLVMTypeRef return_elem_types[] = { | ||
| 2245 | type_entry->type_ref, | ||
| 2246 | LLVMInt1Type(), | ||
| 2247 | }; | ||
| 2248 | LLVMTypeRef param_types[] = { | ||
| 2249 | type_entry->type_ref, | ||
| 2250 | type_entry->type_ref, | ||
| 2251 | }; | ||
| 2252 | LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); | ||
| 2253 | LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); | ||
| 2254 | builtin_fn->fn_val = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type); | ||
| 2255 | assert(LLVMGetIntrinsicID(builtin_fn->fn_val)); | ||
| 2256 | |||
| 2257 | g->builtin_fn_table.put(&builtin_fn->name, builtin_fn); | ||
| 2258 | } | ||
| 2259 | } | ||
| 2260 | |||
| 2261 | static void define_builtin_fns(CodeGen *g) { | ||
| 2262 | define_builtin_fns_int(g, g->builtin_types.entry_u8); | ||
| 2263 | define_builtin_fns_int(g, g->builtin_types.entry_u16); | ||
| 2264 | define_builtin_fns_int(g, g->builtin_types.entry_u32); | ||
| 2265 | define_builtin_fns_int(g, g->builtin_types.entry_u64); | ||
| 2266 | define_builtin_fns_int(g, g->builtin_types.entry_i8); | ||
| 2267 | define_builtin_fns_int(g, g->builtin_types.entry_i16); | ||
| 2268 | define_builtin_fns_int(g, g->builtin_types.entry_i32); | ||
| 2269 | define_builtin_fns_int(g, g->builtin_types.entry_i64); | ||
| 2270 | } | ||
| 2271 | |||
| 2170 | 2272 | ||
| 2171 | 2273 | ||
| 2172 | static void init(CodeGen *g, Buf *source_path) { | 2274 | static void init(CodeGen *g, Buf *source_path) { |
| ... | @@ -2228,9 +2330,10 @@ static void init(CodeGen *g, Buf *source_path) { | ... | @@ -2228,9 +2330,10 @@ static void init(CodeGen *g, Buf *source_path) { |
| 2228 | "", 0, !g->strip_debug_symbols); | 2330 | "", 0, !g->strip_debug_symbols); |
| 2229 | 2331 | ||
| 2230 | // This is for debug stuff that doesn't have a real file. | 2332 | // This is for debug stuff that doesn't have a real file. |
| 2231 | g->dummy_di_file = nullptr; //LLVMZigCreateFile(g->dbuilder, "", ""); | 2333 | g->dummy_di_file = nullptr; |
| 2232 | 2334 | ||
| 2233 | define_builtin_types(g); | 2335 | define_builtin_types(g); |
| 2336 | define_builtin_fns(g); | ||
| 2234 | 2337 | ||
| 2235 | } | 2338 | } |
| 2236 | 2339 |
src/parser.cpp+13-1| ... | @@ -1313,7 +1313,7 @@ static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) { | ... | @@ -1313,7 +1313,7 @@ static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) { |
| 1313 | } | 1313 | } |
| 1314 | 1314 | ||
| 1315 | /* | 1315 | /* |
| 1316 | PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | 1316 | PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | (token(AtSign) token(Symbol) FnCallExpression) |
| 1317 | KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | token(Null) | 1317 | KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | token(Null) |
| 1318 | */ | 1318 | */ |
| 1319 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { | 1319 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| ... | @@ -1356,6 +1356,18 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool | ... | @@ -1356,6 +1356,18 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 1356 | AstNode *node = ast_create_node(pc, NodeTypeNullLiteral, token); | 1356 | AstNode *node = ast_create_node(pc, NodeTypeNullLiteral, token); |
| 1357 | *token_index += 1; | 1357 | *token_index += 1; |
| 1358 | return node; | 1358 | return node; |
| 1359 | } else if (token->id == TokenIdAtSign) { | ||
| 1360 | *token_index += 1; | ||
| 1361 | Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); | ||
| 1362 | AstNode *name_node = ast_create_node(pc, NodeTypeSymbol, name_tok); | ||
| 1363 | ast_buf_from_token(pc, name_tok, &name_node->data.symbol); | ||
| 1364 | |||
| 1365 | AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, token); | ||
| 1366 | node->data.fn_call_expr.fn_ref_expr = name_node; | ||
| 1367 | ast_eat_token(pc, token_index, TokenIdLParen); | ||
| 1368 | ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params); | ||
| 1369 | node->data.fn_call_expr.is_builtin = true; | ||
| 1370 | return node; | ||
| 1359 | } else if (token->id == TokenIdSymbol) { | 1371 | } else if (token->id == TokenIdSymbol) { |
| 1360 | Token *next_token = &pc->tokens->at(*token_index + 1); | 1372 | Token *next_token = &pc->tokens->at(*token_index + 1); |
| 1361 | 1373 |
src/parser.hpp+1| ... | @@ -176,6 +176,7 @@ struct AstNodeBinOpExpr { | ... | @@ -176,6 +176,7 @@ struct AstNodeBinOpExpr { |
| 176 | struct AstNodeFnCallExpr { | 176 | struct AstNodeFnCallExpr { |
| 177 | AstNode *fn_ref_expr; | 177 | AstNode *fn_ref_expr; |
| 178 | ZigList<AstNode *> params; | 178 | ZigList<AstNode *> params; |
| 179 | bool is_builtin; | ||
| 179 | }; | 180 | }; |
| 180 | 181 | ||
| 181 | struct AstNodeArrayAccessExpr { | 182 | struct AstNodeArrayAccessExpr { |
src/tokenizer.cpp+5| ... | @@ -376,6 +376,10 @@ void tokenize(Buf *buf, Tokenization *out) { | ... | @@ -376,6 +376,10 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 376 | begin_token(&t, TokenIdTilde); | 376 | begin_token(&t, TokenIdTilde); |
| 377 | end_token(&t); | 377 | end_token(&t); |
| 378 | break; | 378 | break; |
| 379 | case '@': | ||
| 380 | begin_token(&t, TokenIdAtSign); | ||
| 381 | end_token(&t); | ||
| 382 | break; | ||
| 379 | case '-': | 383 | case '-': |
| 380 | begin_token(&t, TokenIdDash); | 384 | begin_token(&t, TokenIdDash); |
| 381 | t.state = TokenizeStateSawDash; | 385 | t.state = TokenizeStateSawDash; |
| ... | @@ -1074,6 +1078,7 @@ static const char * token_name(Token *token) { | ... | @@ -1074,6 +1078,7 @@ static const char * token_name(Token *token) { |
| 1074 | case TokenIdMaybe: return "Maybe"; | 1078 | case TokenIdMaybe: return "Maybe"; |
| 1075 | case TokenIdDoubleQuestion: return "DoubleQuestion"; | 1079 | case TokenIdDoubleQuestion: return "DoubleQuestion"; |
| 1076 | case TokenIdMaybeAssign: return "MaybeAssign"; | 1080 | case TokenIdMaybeAssign: return "MaybeAssign"; |
| 1081 | case TokenIdAtSign: return "AtSign"; | ||
| 1077 | } | 1082 | } |
| 1078 | return "(invalid token)"; | 1083 | return "(invalid token)"; |
| 1079 | } | 1084 | } |
src/tokenizer.hpp+1| ... | @@ -89,6 +89,7 @@ enum TokenId { | ... | @@ -89,6 +89,7 @@ enum TokenId { |
| 89 | TokenIdMaybe, | 89 | TokenIdMaybe, |
| 90 | TokenIdDoubleQuestion, | 90 | TokenIdDoubleQuestion, |
| 91 | TokenIdMaybeAssign, | 91 | TokenIdMaybeAssign, |
| 92 | TokenIdAtSign, | ||
| 92 | }; | 93 | }; |
| 93 | 94 | ||
| 94 | struct Token { | 95 | struct Token { |
std/std.zig-5| ... | @@ -63,10 +63,6 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) -> bool { | ... | @@ -63,10 +63,6 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) -> bool { |
| 63 | return true; | 63 | return true; |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | x *= radix; | ||
| 67 | x += digit; | ||
| 68 | |||
| 69 | /* TODO intrinsics mul and add with overflow | ||
| 70 | // x *= radix | 66 | // x *= radix |
| 71 | if (@mul_with_overflow_u64(x, radix, &x)) { | 67 | if (@mul_with_overflow_u64(x, radix, &x)) { |
| 72 | return true; | 68 | return true; |
| ... | @@ -76,7 +72,6 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) -> bool { | ... | @@ -76,7 +72,6 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) -> bool { |
| 76 | if (@add_with_overflow_u64(x, digit, &x)) { | 72 | if (@add_with_overflow_u64(x, digit, &x)) { |
| 77 | return true; | 73 | return true; |
| 78 | } | 74 | } |
| 79 | */ | ||
| 80 | 75 | ||
| 81 | i += 1; | 76 | i += 1; |
| 82 | } | 77 | } |
test/run_tests.cpp+19-1| ... | @@ -953,6 +953,24 @@ fn f(c: u8) -> u8 { | ... | @@ -953,6 +953,24 @@ fn f(c: u8) -> u8 { |
| 953 | } else { | 953 | } else { |
| 954 | 2 | 954 | 2 |
| 955 | } | 955 | } |
| 956 | } | ||
| 957 | )SOURCE", "OK\n"); | ||
| 958 | |||
| 959 | add_simple_case("overflow intrinsics", R"SOURCE( | ||
| 960 | use "std.zig"; | ||
| 961 | pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { | ||
| 962 | var result: u8; | ||
| 963 | if (!@add_with_overflow_u8(250, 100, &result)) { | ||
| 964 | print_str("BAD\n"); | ||
| 965 | } | ||
| 966 | if (@add_with_overflow_u8(100, 150, &result)) { | ||
| 967 | print_str("BAD\n"); | ||
| 968 | } | ||
| 969 | if (result != 250) { | ||
| 970 | print_str("BAD\n"); | ||
| 971 | } | ||
| 972 | print_str("OK\n"); | ||
| 973 | return 0; | ||
| 956 | } | 974 | } |
| 957 | )SOURCE", "OK\n"); | 975 | )SOURCE", "OK\n"); |
| 958 | } | 976 | } |
| ... | @@ -995,7 +1013,7 @@ fn a() { | ... | @@ -995,7 +1013,7 @@ fn a() { |
| 995 | b(1); | 1013 | b(1); |
| 996 | } | 1014 | } |
| 997 | fn b(a: i32, b: i32, c: i32) { } | 1015 | fn b(a: i32, b: i32, c: i32) { } |
| 998 | )SOURCE", 1, ".tmp_source.zig:3:6: error: wrong number of arguments. Expected 3, got 1."); | 1016 | )SOURCE", 1, ".tmp_source.zig:3:6: error: expected 3 arguments, got 1"); |
| 999 | 1017 | ||
| 1000 | add_compile_fail_case("invalid type", R"SOURCE( | 1018 | add_compile_fail_case("invalid type", R"SOURCE( |
| 1001 | fn a() -> bogus {} | 1019 | fn a() -> bogus {} |