diff --git a/self_hosted/ast.jou b/self_hosted/ast.jou index 42649b62..2dc97124 100644 --- a/self_hosted/ast.jou +++ b/self_hosted/ast.jou @@ -75,16 +75,19 @@ class TreePrinter: enum AstExpressionKind: String Int + Long Byte Bool + Null FunctionCall GetVariable + GetEnumMember As # unary operators SizeOf # sizeof x - AddressOf # -x + AddressOf # &x Dereference # *x - Negate # &x + Negate # -x Not # not x PreIncr # ++x PostIncr # x++ @@ -111,8 +114,10 @@ class AstExpression: kind: AstExpressionKind # TODO: union + enum_member: AstEnumMember* # TODO: a pointer only because compiling the self-hosted compiler takes forever otherwise string: byte* int_value: int + long_value: long byte_value: byte bool_value: bool call: AstCall @@ -134,6 +139,8 @@ class AstExpression: printf("\"\n") elif self->kind == AstExpressionKind::Int: printf("%d (32-bit signed)\n", self->int_value) + elif self->kind == AstExpressionKind::Long: + printf("%lld (64-bit signed)\n", self->long_value) elif self->kind == AstExpressionKind::Byte: printf("%d (8-bit unsigned)\n", self->byte_value) elif self->kind == AstExpressionKind::Bool: @@ -141,16 +148,42 @@ class AstExpression: printf("True\n") else: printf("False\n") + elif self->kind == AstExpressionKind::Null: + printf("NULL\n") elif self->kind == AstExpressionKind::FunctionCall: printf("call function \"%s\"\n", &self->call.called_name[0]) self->call.print(tp) + elif self->kind == AstExpressionKind::GetVariable: + printf("get variable \"%s\"\n", &self->varname[0]) + elif self->kind == AstExpressionKind::GetEnumMember: + printf( + "get member \"%s\" from enum \"%s\"\n", + &self->enum_member->member_name[0], + &self->enum_member->enum_name[0], + ) elif self->kind == AstExpressionKind::As: printf("as ") self->as_expression->type.print(True) printf("\n") self->as_expression->value.print(tp.print_prefix(True)) - elif self->kind == AstExpressionKind::GetVariable: - printf("get variable \"%s\"\n", &self->varname[0]) + elif self->kind == AstExpressionKind::SizeOf: + printf("sizeof\n") + elif self->kind == AstExpressionKind::AddressOf: + printf("address of\n") + elif self->kind == AstExpressionKind::Dereference: + printf("dereference\n") + elif self->kind == AstExpressionKind::Negate: + printf("negate\n") + elif self->kind == AstExpressionKind::Not: + printf("not\n") + elif self->kind == AstExpressionKind::PreIncr: + printf("pre-increment\n") + elif self->kind == AstExpressionKind::PostIncr: + printf("post-increment\n") + elif self->kind == AstExpressionKind::PreDecr: + printf("pre-decrement\n") + elif self->kind == AstExpressionKind::PostDecr: + printf("post-decrement\n") elif self->kind == AstExpressionKind::Add: printf("add\n") elif self->kind == AstExpressionKind::Subtract: @@ -177,16 +210,6 @@ class AstExpression: printf("and\n") elif self->kind == AstExpressionKind::Or: printf("or\n") - elif self->kind == AstExpressionKind::Not: - printf("not\n") - elif self->kind == AstExpressionKind::PreIncr: - printf("pre-increment\n") - elif self->kind == AstExpressionKind::PreDecr: - printf("pre-decrement\n") - elif self->kind == AstExpressionKind::PostIncr: - printf("post-increment\n") - elif self->kind == AstExpressionKind::PostDecr: - printf("post-decrement\n") else: printf("?????\n") @@ -248,6 +271,10 @@ class AstExpression: or self->kind == AstExpressionKind::PostDecr ) +class AstEnumMember: + enum_name: byte[100] + member_name: byte[100] + class AstAsExpression: value: AstExpression type: AstType diff --git a/self_hosted/create_llvm_ir.jou b/self_hosted/create_llvm_ir.jou index 7b257906..6c339fc9 100644 --- a/self_hosted/create_llvm_ir.jou +++ b/self_hosted/create_llvm_ir.jou @@ -57,9 +57,12 @@ class AstToIR: def do_expression(self, ast: AstExpression*) -> LLVMValue*: if ast->kind == AstExpressionKind::String: return self->make_a_string_constant(ast->string) - + elif ast->kind == AstExpressionKind::Byte: + return LLVMConstInt(LLVMInt8Type(), ast->byte_value, False) elif ast->kind == AstExpressionKind::Int: return LLVMConstInt(LLVMInt32Type(), ast->int_value, False) + elif ast->kind == AstExpressionKind::Long: + return LLVMConstInt(LLVMInt64Type(), ast->long_value, False) elif ast->kind == AstExpressionKind::FunctionCall: function = LLVMGetNamedFunction(self->module, &ast->call.called_name[0]) @@ -68,8 +71,7 @@ class AstToIR: function_type = LLVMGetElementType(LLVMTypeOf(function)) assert(LLVMGetTypeKind(function_type) == LLVMTypeKind::Function) - args: LLVMValue** - args = malloc(sizeof args[0] * ast->call.nargs) + args: LLVMValue** = malloc(sizeof args[0] * ast->call.nargs) for i = 0; i < ast->call.nargs; i++: args[i] = self->do_expression(&ast->call.args[i]) result = LLVMBuildCall2(self->builder, function_type, function, args, ast->call.nargs, "function_call") diff --git a/self_hosted/parser.jou b/self_hosted/parser.jou index d266d7cd..356cf215 100644 --- a/self_hosted/parser.jou +++ b/self_hosted/parser.jou @@ -3,6 +3,7 @@ import "stdlib/mem.jou" import "./token.jou" import "./ast.jou" import "./errors_and_warnings.jou" +import "./paths.jou" def parse_type(tokens: Token**) -> AstType: if not ( @@ -81,6 +82,15 @@ def parse_function_signature(tokens: Token**) -> AstSignature: if arg.value != NULL: fail(arg.value->location, "arguments cannot have default values") + for i = 0; i < result.nargs; i++: + if strcmp(&result.args[i].name[0], &arg.name[0]) == 0: + message: byte[200] + snprintf( + &message[0], sizeof message, + "there are multiple arguments named '%s'", + &arg.name[0]) + fail(arg.name_location, &message[0]) + result.args = realloc(result.args, sizeof result.args[0] * (result.nargs+1)) result.args[result.nargs++] = arg @@ -102,13 +112,36 @@ def parse_function_signature(tokens: Token**) -> AstSignature: result.return_type = parse_type(tokens) return result -def get_actual_import_path(path_token: Token*, stdlib_path: byte*) -> byte*: - assert(path_token->kind == TokenKind::String) - assert(starts_with(path_token->long_string, "stdlib/")) - - path = malloc(strlen(path_token->long_string) + 100) - sprintf(path, "%s/%s", stdlib_path, &path_token->long_string[7]) - return path +def parse_import_path(path_token: Token*, stdlib_path: byte*) -> AstImport: + if path_token->kind != TokenKind::String: + path_token->fail_expected_got("a string to specify the file name") + + if starts_with(path_token->long_string, "stdlib/"): + # Starts with stdlib --> import from where stdlib actually is + tmp = NULL + part1 = stdlib_path + part2 = &path_token->long_string[7] + elif starts_with(path_token->long_string, "."): + # Relative to directory where the file is + tmp = strdup(path_token->location.path) + part1 = dirname(tmp) + part2 = path_token->long_string + else: + fail( + path_token->location, + "import path must start with 'stdlib/' (standard-library import) or a dot (relative import)" + ) + + # 1 for slash, 1 for \0, 1 for fun + path = malloc(strlen(part1) + strlen(part2) + 3) + sprintf(path, "%s/%s", part1, part2) + free(tmp) + + simplify_path(path) + return AstImport{ + specified_path = strdup(path_token->long_string), + resolved_path = path, + } def parse_call(tokens: Token**, open_paren: byte*, close_paren: byte*) -> AstCall: assert((*tokens)->kind == TokenKind::Name) # must be checked when calling this function @@ -117,7 +150,7 @@ def parse_call(tokens: Token**, open_paren: byte*, close_paren: byte*) -> AstCal if not (*tokens)->is_operator(open_paren): expected = malloc(100) - sprintf(expected, "a '%c' to denote the start of arguments", open_paren) + sprintf(expected, "a '%s' to denote the start of arguments", open_paren) (*tokens)->fail_expected_got(expected) ++*tokens @@ -130,7 +163,7 @@ def parse_call(tokens: Token**, open_paren: byte*, close_paren: byte*) -> AstCal if not (*tokens)->is_operator(close_paren): expected = malloc(100) - sprintf(expected, "a '%c'", close_paren) + sprintf(expected, "a '%s'", close_paren) (*tokens)->fail_expected_got(expected) ++*tokens @@ -143,6 +176,10 @@ def parse_elementary_expression(tokens: Token**) -> AstExpression: expr.kind = AstExpressionKind::Int expr.int_value = (*tokens)->int_value ++*tokens + elif (*tokens)->kind == TokenKind::Long: + expr.kind = AstExpressionKind::Long + expr.long_value = (*tokens)->long_value + ++*tokens elif (*tokens)->kind == TokenKind::Byte: expr.kind = AstExpressionKind::Byte expr.byte_value = (*tokens)->byte_value @@ -159,13 +196,27 @@ def parse_elementary_expression(tokens: Token**) -> AstExpression: expr.kind = AstExpressionKind::Bool expr.bool_value = False ++*tokens - elif (*tokens)->kind == TokenKind::Name and (&(*tokens)[1])->is_operator("("): - expr.kind = AstExpressionKind::FunctionCall - expr.call = parse_call(tokens, "(", ")") - elif (*tokens)->kind == TokenKind::Name: - expr.kind = AstExpressionKind::GetVariable - expr.varname = (*tokens)->short_string + elif (*tokens)->is_keyword("NULL"): + expr.kind = AstExpressionKind::Null ++*tokens + elif (*tokens)->kind == TokenKind::Name: + if (*tokens)[1].is_operator("("): + expr.kind = AstExpressionKind::FunctionCall + expr.call = parse_call(tokens, "(", ")") + elif (*tokens)[1].is_operator("::") and (*tokens)[2].kind == TokenKind::Name: + expr.kind = AstExpressionKind::GetEnumMember + expr.enum_member = malloc(sizeof *expr.enum_member) + *expr.enum_member = AstEnumMember{ + enum_name = (*tokens)->short_string, + member_name = (*tokens)[2].short_string, + } + ++*tokens + ++*tokens + ++*tokens + else: + expr.kind = AstExpressionKind::GetVariable + expr.varname = (*tokens)->short_string + ++*tokens elif (*tokens)->is_operator("("): ++*tokens expr = parse_expression(tokens) @@ -554,8 +605,12 @@ def parse_body(tokens: Token**) -> AstBody: return AstBody{ statements = result, nstatements = n } def parse_funcdef(tokens: Token**) -> AstFunction: + signature = parse_function_signature(tokens) + if signature.takes_varargs: + fail((*tokens)->location, "functions with variadic arguments cannot be defined yet") + return AstFunction{ - signature = parse_function_signature(tokens), + signature = signature, body = parse_body(tokens), } @@ -581,13 +636,12 @@ def parse_classdef(tokens: Token**) -> AstClassDef: ++*tokens return result -def parse_toplevel_node(dest: AstFile*, tokens: Token**, stdlib_path: byte*) -> void: +def parse_toplevel_node(tokens: Token**, stdlib_path: byte*) -> AstToplevelStatement: ts = AstToplevelStatement{location = (*tokens)->location} if (*tokens)->is_keyword("import"): ++*tokens - ts.the_import.specified_path = strdup((*tokens)->long_string) - ts.the_import.resolved_path = get_actual_import_path(*tokens, stdlib_path) + ts.the_import = parse_import_path(*tokens, stdlib_path) ++*tokens eat_newline(tokens) @@ -620,11 +674,17 @@ def parse_toplevel_node(dest: AstFile*, tokens: Token**, stdlib_path: byte*) -> else: (*tokens)->fail_expected_got("a definition or declaration") - dest->body = realloc(dest->body, sizeof dest->body[0] * (dest->body_len + 1)) - dest->body[dest->body_len++] = ts + return ts def parse(tokens: Token*, stdlib_path: byte*) -> AstFile: result = AstFile{path = tokens[0].location.path} while tokens->kind != TokenKind::EndOfFile: - parse_toplevel_node(&result, &tokens, stdlib_path) + result.body = realloc(result.body, sizeof result.body[0] * (result.body_len + 1)) + result.body[result.body_len++] = parse_toplevel_node(&tokens, stdlib_path) + + # This simplifies the compiler: it's easy to loop through all imports of the file. + for p = &result.body[1]; p != &result.body[result.body_len]; p++: + if p[-1].kind != AstToplevelStatementKind::Import and p->kind == AstToplevelStatementKind::Import: + fail(p->location, "imports must be in the beginning of the file") + return result diff --git a/self_hosted/parses_wrong.txt b/self_hosted/parses_wrong.txt index aed32b9d..1f949610 100644 --- a/self_hosted/parses_wrong.txt +++ b/self_hosted/parses_wrong.txt @@ -1,68 +1,50 @@ # This is a list of files that are not yet supported by the tokenizer or parser of the self-hosted compiler. -examples/x11_window.jou -tests/404/enum.jou tests/404/enum_member.jou tests/404/method_on_int.jou tests/404/method_on_class.jou tests/404/method_on_class_ptr.jou tests/404/class_field.jou -tests/404/var_addressof.jou tests/already_exists_error/global_var_import.jou tests/already_exists_error/global_var.jou tests/already_exists_error/class_and_enum.jou -tests/crash/null_deref.jou tests/other_errors/address_of_array_indexing.jou -tests/other_errors/address_of_minusminus.jou tests/other_errors/array0.jou tests/other_errors/brace_init_dupe.jou -tests/other_errors/duplicate_arg_name.jou tests/other_errors/duplicate_enum_member.jou tests/other_errors/dynamic_array_length.jou tests/other_errors/immediate_member_assign.jou -tests/other_errors/imported_error.jou -tests/other_errors/varargs_def.jou tests/other_errors/var_shadow.jou tests/should_succeed/add_sub_mul_div_mod.jou tests/should_succeed/array.jou tests/should_succeed/as.jou -tests/should_succeed/compiler_cli.jou tests/should_succeed/enum.jou tests/should_succeed/expfloat.jou tests/should_succeed/file.jou tests/should_succeed/global.jou tests/should_succeed/implicit_conversions.jou -tests/should_succeed/import_cycle.jou tests/should_succeed/imported/bar.jou -tests/should_succeed/imported/cycle.jou tests/should_succeed/local_import.jou tests/should_succeed/mathlibtest.jou tests/should_succeed/method.jou -tests/should_succeed/plusplus_minusminus.jou tests/should_succeed/pointer.jou tests/should_succeed/printf.jou tests/should_succeed/sizeof.jou -tests/should_succeed/sscanf.jou tests/should_succeed/string_syntax.jou tests/should_succeed/class.jou tests/should_succeed/unused_import.jou tests/syntax_error/array_size.jou -tests/syntax_error/bad_addressof.jou tests/syntax_error/bad_array.jou tests/syntax_error/bad_byte.jou tests/syntax_error/bad_field.jou tests/syntax_error/dot_after_e.jou tests/syntax_error/double_with_letters_after.jou tests/syntax_error/ee.jou -tests/syntax_error/import_after_def.jou -tests/syntax_error/import_missing_dot.jou -tests/syntax_error/import_missing_quotes.jou tests/syntax_error/indexing.jou tests/syntax_error/missing_field_names.jou tests/syntax_error/missing_number_after_eminus.jou tests/syntax_error/multidot_float.jou tests/syntax_error/class_init_js_syntax.jou tests/syntax_error/triple_equals.jou -tests/too_long/name.jou tests/wrong_type/array_mixed_types.jou tests/wrong_type/array_mixed_types_ptr.jou tests/wrong_type/array_to_ptr.jou @@ -70,19 +52,13 @@ tests/wrong_type/array_vararg.jou tests/wrong_type/arrow_operator_not_pointer.jou tests/wrong_type/arrow_operator_not_pointer_method.jou tests/wrong_type/arrow_operator_not_class.jou -tests/wrong_type/assign_to_deref_non_pointer.jou tests/wrong_type/brace_init_arg.jou tests/wrong_type/cannot_be_indexed.jou -tests/wrong_type/deref_non_pointer.jou tests/wrong_type/dot_operator.jou -tests/wrong_type/enum_member_from_class.jou tests/wrong_type/enum_to_int.jou tests/wrong_type/float_and_double.jou tests/wrong_type/index.jou tests/wrong_type/int_to_enum.jou -tests/wrong_type/neg.jou -tests/wrong_type/pointer_assignment.jou -tests/wrong_type/pointer_eq.jou tests/wrong_type/class_member_assign.jou tests/wrong_type/class_member_init.jou tests/should_succeed/linked_list.jou @@ -91,12 +67,9 @@ stdlib/str.jou stdlib/_windows_startup.jou tests/other_errors/method_on_ptr_called_on_class.jou tests/syntax_error/self_outside_class.jou -tests/should_succeed/int_literals.jou tests/other_errors/using_void_method.jou tests/already_exists_error/class_field.jou tests/already_exists_error/method.jou tests/should_succeed/imported/point_factory.jou tests/should_succeed/indirect_method_import.jou tests/404/indirect_import_symbol.jou -tests/syntax_error/import1.jou -tests/syntax_error/import_with_extra_crap_at_end.jou diff --git a/self_hosted/paths.jou b/self_hosted/paths.jou index 94d4347c..457f61eb 100644 --- a/self_hosted/paths.jou +++ b/self_hosted/paths.jou @@ -95,3 +95,34 @@ def get_path_to_file_in_jou_compiled(filename: byte*) -> byte*: result: byte* = malloc(strlen(filename) + 100) sprintf(result, "jou_compiled/self_hosted/%s", filename) return result + +# TODO: put this to stdlib? or does it do too much for a stdlib function? +def delete_slice(start: byte*, end: byte*) -> void: + memmove(start, end, strlen(end) + 1) + +def simplify_path(path: byte*) -> void: + if is_windows(): + # Backslash to forward slash. + for p = path; *p != '\0'; p++: + if *p == '\\': + *p = '/' + + # Delete "." components. + while starts_with(path, "./"): + delete_slice(path, &path[2]) + while True: + p = strstr(path, "/./") + if p == NULL: + break # TODO: walrus operator p := strstr(...) + delete_slice(p, &p[2]) + + # Delete unnecessary ".." components. + while True: + p = strstr(path, "/../") + if p == NULL: + break # TODO: walrus operator p := strstr(...) + + start_of_previous_component = p + while start_of_previous_component != path and start_of_previous_component[-1] != '/': + start_of_previous_component-- + delete_slice(start_of_previous_component, &p[4]) diff --git a/self_hosted/runs_wrong.txt b/self_hosted/runs_wrong.txt index a6a1bbbe..4f6193a5 100644 --- a/self_hosted/runs_wrong.txt +++ b/self_hosted/runs_wrong.txt @@ -36,7 +36,6 @@ tests/other_errors/continue_outside_loop.jou tests/other_errors/double_plusplus.jou tests/other_errors/dumb_assignment.jou tests/other_errors/dumb_assignment_with_plusequals.jou -tests/other_errors/duplicate_arg_name.jou tests/other_errors/duplicate_enum_member.jou tests/other_errors/dynamic_array_length.jou tests/other_errors/immediate_member_assign.jou @@ -48,7 +47,6 @@ tests/other_errors/redefine_imported_func.jou tests/other_errors/runtime_return_1.jou tests/other_errors/unexpected_return_value.jou tests/other_errors/using_void_function.jou -tests/other_errors/varargs_def.jou tests/other_errors/var_shadow.jou tests/should_succeed/add_sub_mul_div_mod.jou tests/should_succeed/and_or_not.jou @@ -70,7 +68,6 @@ tests/should_succeed/implicit_conversions.jou tests/should_succeed/import_cycle.jou tests/should_succeed/imported/bar.jou tests/should_succeed/imported/cycle.jou -tests/should_succeed/int_literals.jou tests/should_succeed/linked_list.jou tests/should_succeed/local_import.jou tests/should_succeed/loops.jou @@ -98,16 +95,12 @@ tests/syntax_error/class_init_js_syntax.jou tests/syntax_error/dot_after_e.jou tests/syntax_error/double_with_letters_after.jou tests/syntax_error/ee.jou -tests/syntax_error/import_after_def.jou -tests/syntax_error/import_missing_dot.jou -tests/syntax_error/import_missing_quotes.jou tests/syntax_error/indexing.jou tests/syntax_error/missing_field_names.jou tests/syntax_error/missing_number_after_eminus.jou tests/syntax_error/multidot_float.jou tests/syntax_error/self_outside_class.jou tests/syntax_error/triple_equals.jou -tests/too_long/name.jou tests/wrong_type/array_mixed_types.jou tests/wrong_type/array_mixed_types_ptr.jou tests/wrong_type/array_to_ptr.jou @@ -150,7 +143,5 @@ tests/already_exists_error/method.jou tests/should_succeed/imported/point_factory.jou tests/should_succeed/indirect_method_import.jou tests/404/indirect_import_symbol.jou -tests/syntax_error/import1.jou -tests/syntax_error/import_with_extra_crap_at_end.jou tests/other_errors/noreturn_but_return_without_value.jou tests/other_errors/noreturn_but_return_with_value.jou diff --git a/self_hosted/token.jou b/self_hosted/token.jou index 1388a8c2..5847477b 100644 --- a/self_hosted/token.jou +++ b/self_hosted/token.jou @@ -113,9 +113,9 @@ class Token: elif self->kind == TokenKind::String: strcpy(&got[0], "a string") elif self->kind == TokenKind::Name: - strcpy(&got[0], "a name") + snprintf(&got[0], sizeof got, "a variable name '%s'", &self->short_string[0]) elif self->kind == TokenKind::Keyword: - sprintf(&got[0], "the '%s' keyword", &self->short_string[0]) + snprintf(&got[0], sizeof got, "the '%s' keyword", &self->short_string[0]) elif self->kind == TokenKind::Newline: strcpy(&got[0], "end of line") elif self->kind == TokenKind::Indent: @@ -123,7 +123,7 @@ class Token: elif self->kind == TokenKind::Dedent: strcpy(&got[0], "less indentation") elif self->kind == TokenKind::Operator: - sprintf(&got[0], "'%s'", &self->short_string[0]) + snprintf(&got[0], sizeof got, "'%s'", &self->short_string[0]) elif self->kind == TokenKind::EndOfFile: strcpy(&got[0], "end of file") else: diff --git a/self_hosted/tokenizer.jou b/self_hosted/tokenizer.jou index f94bbefb..a633c275 100644 --- a/self_hosted/tokenizer.jou +++ b/self_hosted/tokenizer.jou @@ -179,7 +179,13 @@ class Tokenizer: b = self->read_byte() if is_identifier_or_number_byte(b): if destlen == sizeof dest - 1: - fail(self->location, "name or number is too long") + if '0' <= dest[0] and dest[0] <= '9': + template = "number is too long: %.20s..." + else: + template = "name is too long: %.20s..." + message: byte[100] + sprintf(&message[0], template, &dest[0]) + fail(self->location, &message[0]) dest[destlen++] = b else: self->unread_byte(b) diff --git a/self_hosted/tokenizes_wrong.txt b/self_hosted/tokenizes_wrong.txt index 97542606..14c9f19f 100644 --- a/self_hosted/tokenizes_wrong.txt +++ b/self_hosted/tokenizes_wrong.txt @@ -13,4 +13,3 @@ tests/should_succeed/mathlibtest.jou tests/should_succeed/expfloat.jou tests/should_succeed/implicit_conversions.jou tests/should_succeed/as.jou -tests/too_long/name.jou diff --git a/self_hosted/typecheck.jou b/self_hosted/typecheck.jou index 10db8666..eea6e36e 100644 --- a/self_hosted/typecheck.jou +++ b/self_hosted/typecheck.jou @@ -241,7 +241,10 @@ def typecheck_function_call(ctx: TypeContext*, call: AstCall*) -> Type*: signature_string = signature->to_string(False) - if call->nargs != signature->nargs: + if ( + call->nargs < signature->nargs + or (call->nargs > signature->nargs and not signature->takes_varargs) + ): snprintf( &message[0], sizeof message, "function %s takes %d argument%s, but it was called with %d argument%s", @@ -263,6 +266,21 @@ def typecheck_function_call(ctx: TypeContext*, call: AstCall*) -> Type*: free(tmp) typecheck_expression_with_implicit_cast(ctx, &call->args[i], signature->argtypes[i], &message[0]) + for i = signature->nargs; i < call->nargs; i++: + # This code runs for varargs, e.g. the things to format in printf(). + types = typecheck_expression(ctx, &call->args[i]) + + # TODO: do something to arrays + + if ( + (types->type->is_integer_type() and types->type->size_in_bits < 32) + or types->type == &bool_type + ): + # Add implicit cast to signed int, just like in C. + do_implicit_cast(types, int_type, Location{}, NULL) + + # TODO: cast float to double implicitly + free(signature_string) return signature->return_type @@ -274,6 +292,8 @@ def typecheck_expression_maybe_void(ctx: TypeContext*, expression: AstExpression result = byte_type->get_pointer_type() elif expression->kind == AstExpressionKind::Int: result = int_type + elif expression->kind == AstExpressionKind::Long: + result = long_type elif expression->kind == AstExpressionKind::FunctionCall: result = typecheck_function_call(ctx, &expression->call) if result == NULL: diff --git a/src/print.c b/src/print.c index 9d702faf..b46f6a9d 100644 --- a/src/print.c +++ b/src/print.c @@ -242,7 +242,7 @@ static void print_ast_expression(const AstExpression *expr, struct TreePrinter t case AST_EXPR_ADDRESS_OF: puts("address of"); n=1; break; case AST_EXPR_DEREFERENCE: puts("dereference"); n=1; break; - case AST_EXPR_NEG: puts("neg"); n=1; break; + case AST_EXPR_NEG: puts("negate"); n=1; break; case AST_EXPR_NOT: puts("not"); n=1; break; case AST_EXPR_PRE_INCREMENT: puts("pre-increment"); n=1; break; case AST_EXPR_PRE_DECREMENT: puts("pre-decrement"); n=1; break; diff --git a/src/tokenize.c b/src/tokenize.c index 607c600c..2f4319e2 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -88,7 +88,7 @@ static void read_identifier_or_number(struct State *st, char firstbyte, char (*d || ('0'<=firstbyte && firstbyte<='9' && (*dest)[destlen-1]=='e' && c=='-')) { if (destlen == sizeof *dest - 1) - fail_with_error(st->location, "name is too long: %.20s...", *dest); + fail_with_error(st->location, "%s is too long: %.20s...", isdigit(firstbyte)?"number":"name", *dest); (*dest)[destlen++] = c; } else { unread_byte(st, c); diff --git a/tests/syntax_error/call_missing_comma.jou b/tests/syntax_error/call_missing_comma.jou new file mode 100644 index 00000000..728c63b2 --- /dev/null +++ b/tests/syntax_error/call_missing_comma.jou @@ -0,0 +1,2 @@ +def foo() -> void: + printf("hello %d\n" 1) # Error: expected a ')', got an integer diff --git a/tests/too_long/huge_int.jou b/tests/too_long/huge_int.jou new file mode 100644 index 00000000..fbabd4e8 --- /dev/null +++ b/tests/too_long/huge_int.jou @@ -0,0 +1,2 @@ +def foo() -> void: + x = 123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123 # Error: number is too long: 12312312312312312312... diff --git a/tests/too_long/int.jou b/tests/too_long/int.jou index 8857f213..bc2e2178 100644 --- a/tests/too_long/int.jou +++ b/tests/too_long/int.jou @@ -1,3 +1,3 @@ def foo() -> void: - x = 1000000000000000L # no error - y = 100000000000000000000L # Error: value does not fit in a signed 64-bit integer + x = 1000000000 # no error + y = 1000000000000000 # Error: value does not fit in a signed 32-bit integer diff --git a/tests/too_long/long.jou b/tests/too_long/long.jou index bc2e2178..8857f213 100644 --- a/tests/too_long/long.jou +++ b/tests/too_long/long.jou @@ -1,3 +1,3 @@ def foo() -> void: - x = 1000000000 # no error - y = 1000000000000000 # Error: value does not fit in a signed 32-bit integer + x = 1000000000000000L # no error + y = 100000000000000000000L # Error: value does not fit in a signed 64-bit integer