Fix, separate out the function pass from the decl pass. Diagnose non-visible symbols as not visible rather than missing. Fix crash when module identifier is incorrect. !! operator added. New character literal parsing. Added simple test framework.

This commit is contained in:
Christoffer Lerno
2020-07-16 16:45:06 +02:00
committed by Christoffer Lerno
parent b4c661eaad
commit f45d6ef84b
43 changed files with 871 additions and 259 deletions

View File

@@ -90,4 +90,4 @@ add_executable(c3c
target_compile_options(c3c PRIVATE -Wimplicit-int -Werror -Wall -Wno-unknown-pragmas -Wextra -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
target_link_libraries(c3c m ${llvm_libs})
target_link_libraries(c3c m ${llvm_libs})

View File

@@ -50,12 +50,16 @@ There are some small work being done on the parser here, but most of the structu
- Improved integration with C.
- Generic macros.
- Update of error system
- Imports aren't quite stable
- Strings, vararrays aren't finalized
- Stdlib not started
- More tests and support for multi file tests.
#### What's working?
- Lexing and parsing works (except for the exceptions noted above).
- Simple "hello world"
- Most simpler C code.
- Lexing and parsing works with some minor exceptions
- Normal code works
- You can use any C function by declaring it as a normal C3 function with external
(For more details see missing.txt)

View File

@@ -1,8 +1,40 @@
module bar;
import baz;
import gen;
typedef int as Bob;
/*
macro namespaceFor(ns; void(int i) $body)
{
for (usize i = 0; i < ns.avail; i++)
{
@$body(i);
}
}
macro usize nameHashFn(char *strp, usize strl)
{
usize hash = 5381;
for (usize i = 0; i < strl; i++)
{
hash = ((hash << 5) + hash) ^ cast(strp[i], usize);
}
return hash;
}
macro INode *nodesGet(nodes, index)
{
return cast(node + 1, INode**)[index];
}
macro lexReturnPuncTok!(tok, skip, implicit lex, implicit srcp)
{
lex.toktype = tok;
lex.tokp = srcp;
lex.srcp = srcp + skip;
return;
}
*/
func int! testThrow4(int x)
{
return x > 4 ? TestErr1! : TestErr2!;
@@ -776,12 +808,14 @@ func void show()
num = frob(num + 1);
printf("Num: %d\n", num);
}
func void testAllErrors()
func void! testAllErrors()
{
printf("Try test all\n");
int! i = 7;
int*! iptr = &i;
int* xd = iptr!!;
printf("%d\n", *xd);
int**! iptrptr = &iptr;
catch (err = iptrptr)
{
@@ -1368,6 +1402,7 @@ public func int! decode(char[] infile, byte[] out)
*/
func int main(int x)
{
baz::runBaz();
testBreak();
show();
testMacros();

View File

@@ -0,0 +1,17 @@
module baz;
import gen;
extern func void printf(char *hello, ...);
//define Bobafett(int) as BobaInt;
public func void runBaz()
{
printf("Baz func\n");
// BobaInt x = { 1 };
}
func void notVisible()
{
}

View File

@@ -0,0 +1,6 @@
module gen (Type);
/*
struct BobaFett
{
Type x;
}*/

View File

@@ -1,24 +0,0 @@
enum EnumWithErrorWithMissingName : int (int)
// @error The function parameter must be named
{
TEST
}
enum EnumWithErrorData : int (int
// @error Unexpected end of parameter list
{
TEST
}
enum EnumTestOverflow
{
VALUE = 0x80000000,
// @error does not fit into 'int'
}
enum EnumTestErrorType : float
// @error The enum type must be an integer type not 'float'
{
VALUE_BOOM
}

View File

@@ -1,12 +0,0 @@
module errors;
error TheError
{
FOO_MISSING,
NO_SUCH_FILE,
}
error OtherError
{
BAR_OVERFLOWED
}

View File

@@ -273,6 +273,11 @@ static void parse_option()
build_options.lib_dir[build_options.lib_count++] = check_dir(next_arg());
return;
}
if (match_longopt("test"))
{
build_options.test_mode = true;
return;
}
if (match_longopt("path"))
{
if (at_end() || next_is_opt()) error_exit("error: --path needs a directory.");

View File

@@ -113,6 +113,7 @@ typedef struct
bool debug_mode;
bool emit_llvm;
bool emit_bitcode;
bool test_mode;
} BuildOptions;

View File

@@ -499,11 +499,6 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent)
if (!expr) return;
switch (expr->expr_kind)
{
case EXPR_FAIL_CHECK:
DUMP("(fail-check");
DUMPEXPC(expr);
DUMPEXPR(expr->fail_check_expr);
DUMPEND();
case EXPR_DECL_LIST:
DUMP("(decllist");
DUMPASTS(expr->dexpr_list_expr);
@@ -649,7 +644,7 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent)
DUMPEND();
case EXPR_GUARD:
DUMP("(guard");
DUMPEXPR(expr->guard_expr);
DUMPEXPR(expr->guard_expr.inner);
DUMPEND();
case EXPR_ELSE:
if (expr->else_expr.is_jump)

View File

@@ -112,6 +112,10 @@ void compiler_compile(BuildTarget *target)
{
sema_analysis_pass_decls(contexts[i]);
}
VECEACH(contexts, i)
{
sema_analysis_pass_functions(contexts[i]);
}
if (diagnostics.errors > 0) exit(EXIT_FAILURE);
llvm_codegen_setup();

View File

@@ -397,7 +397,6 @@ typedef struct
void *continue_target;
unsigned scope_id;
AstId parent;
} LabelDecl;
typedef struct
@@ -631,6 +630,12 @@ typedef struct
TokenId span;
} Label;
typedef struct
{
Expr *inner;
AstId defer;
} ExprGuard;
struct _Expr
{
ExprKind expr_kind : 8;
@@ -647,12 +652,11 @@ struct _Expr
ExprRange range_expr;
ExprStructValue struct_value_expr;
ExprTypeAccess type_access;
Expr *guard_expr;
ExprGuard guard_expr;
Expr *trycatch_expr;
ExprElse else_expr;
ExprBinary binary_expr;
ExprTernary ternary_expr;
Expr *fail_check_expr;
ExprUnary unary_expr;
ExprPostUnary post_expr;
ExprCall call_expr;
@@ -969,6 +973,18 @@ typedef union
{
const char *string;
long double value;
struct
{
union
{
uint8_t b[8];
uint8_t u8;
uint16_t u16;
uint32_t u32;
uint64_t u64;
} char_lit;
char width;
};
} TokenData;
typedef struct
@@ -987,7 +1003,6 @@ typedef struct
typedef struct _Context
{
unsigned current_block;
BuildTarget *target;
Path *module_name;
TokenId* module_parameters;
@@ -1299,7 +1314,7 @@ static inline uint32_t TOKKLEN(Token token) { return TOKKLOC(token)->length; }
#define TOKLEN(T) _Generic((T), TokenId: TOKILEN, Token:TOKKLEN)(T)
#define TOKVALID(_tok) (_tok.index != 0)
Decl *module_find_symbol(Module *module, const char *symbol, ModuleSymbolSearch search);
Decl *module_find_symbol(Module *module, const char *symbol, ModuleSymbolSearch search, Decl **private_decl);
void parse_file(Context *context);
Path *path_create_from_string(Context *context, const char *string, size_t len, SourceSpan span);
@@ -1315,13 +1330,14 @@ const char *resolve_status_to_string(ResolveStatus status);
void sema_analysis_pass_process_imports(Context *context);
void sema_analysis_pass_conditional_compilation(Context *context);
void sema_analysis_pass_decls(Context *context);
void sema_analysis_pass_functions(Context *context);
bool sema_add_local(Context *context, Decl *decl);
bool sema_unwrap_var(Context *context, Decl *decl);
bool sema_rewrap_var(Context *context, Decl *decl);
bool sema_analyse_statement(Context *context, Ast *statement);
Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl **ambiguous_other_decl);
Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl **ambiguous_other_decl, Decl **private_decl);
bool sema_resolve_type_info(Context *context, TypeInfo *type_info);
bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info);

View File

@@ -23,6 +23,22 @@ typedef enum
static void print_error2(SourceLocation *location, const char *message, PrintType print_type)
{
if (build_options.test_mode)
{
switch (print_type)
{
case PRINT_TYPE_ERROR:
eprintf("Error|%s|%d|%s\n", location->file->name, location->line, message);
return;
case PRINT_TYPE_PREV:
return;
case PRINT_TYPE_WARN:
eprintf("Warning|%s|%d|%s\n", location->file->name, location->line, message);
return;
default:
UNREACHABLE
}
}
static const int LINES_SHOWN = 4;
unsigned max_line_length = (int)round(log10(location->line)) + 1;
@@ -47,7 +63,7 @@ static void print_error2(SourceLocation *location, const char *message, PrintTyp
uint32_t line_number = location->line + 1 - i;
SourceLoc line_start = location->file->lines[line_number - 1];
SourceLoc line_end = line_number == lines_in_file ? location->file->end_id :
SourceLoc line_end = line_number == lines_in_file ? location->file->end_id + 1 :
location->file->lines[line_number];
uint32_t line_len = line_end - line_start - 1;
start = location->file->contents + line_start - location->file->start_id;

View File

@@ -190,7 +190,6 @@ typedef enum
EXPR_DESIGNATED_INITIALIZER,
EXPR_COMPOUND_LITERAL,
EXPR_FAILABLE,
EXPR_FAIL_CHECK,
EXPR_DECL_LIST
} ExprKind;
@@ -365,6 +364,7 @@ typedef enum
TOKEN_STRING, // "Teststring"
TOKEN_INTEGER, // 123 0x23 0b10010 0o327
TOKEN_CHAR_LITERAL, // 'a' 'FO' 'BARS' '\u1232'
TOKEN_REAL, // 0x23.2p-2a 43.23e23
TOKEN_COMMENT, // Comment

View File

@@ -82,7 +82,7 @@ static bool add_error_token(Lexer *lexer, const char *message, ...)
add_generic_token(lexer, TOKEN_INVALID_TOKEN, &loc, &data);
va_list list;
va_start(list, message);
diag_verror_range(loc, message, list);
sema_verror_range(loc, message, list);
va_end(list);
return false;
}
@@ -425,34 +425,132 @@ static inline bool scan_digit(Lexer *lexer)
#pragma mark --- Character & string scan
static inline int64_t scan_hex_literal(Lexer *lexer, int positions)
{
int64_t hex = 0;
for (int j = 0; j < positions; j++)
{
hex <<= 4U;
int i = char_to_nibble(next(lexer));
if (i < 0)
{
if (lexer->current[-1] == '\'')
{
backtrack(lexer);
return -1;
}
while (lexer->current[0] != '\'' && lexer->current[0] != '\0' && ++j < positions)
{
next(lexer);
}
return -1;
}
hex += i;
}
return hex;
}
static inline bool scan_char(Lexer *lexer)
{
int width = 0;
char c;
union
{
uint8_t u8;
uint16_t u16;
uint32_t u32;
uint64_t u64;
uint8_t b[8];
} bytes;
while ((c = next(lexer)) != '\'')
{
if (c == '\0' || c == '\n') return add_error_token(lexer, "Character literal did not terminate.");
width++;
// Was this an escape?
if (c == '\0')
{
return add_error_token(lexer, "Character literal did not terminate.");
}
if (width > 7)
{
width++;
continue;
}
if (c != '\\')
{
bytes.b[width++] = c;
}
if (c == '\\')
{
// Yes, so check if it's hex:
if (next(lexer) == 'x')
c = next(lexer);
const char *start = lexer->current;
char escape = is_valid_escape(c);
if (escape == -1)
{
// Walk through the two characters.
for (int i = 0; i < 2; i++)
lexer->lexing_start = start;
return add_error_token(lexer, "Invalid escape sequence '\\%c'.", c);
}
switch (escape)
{
case 'x':
{
if (!is_hex(next(lexer)))
int64_t hex = scan_hex_literal(lexer, 2);
if (hex < 0)
{
return add_error_token(lexer,
"An escape sequence starting with "
"'\\x' needs to be followed by "
"a two digit hexadecimal number.");
lexer->lexing_start = start;
// Fix underlining if this is an unfinished escape.
return add_error_token(lexer, "Expected a two character hex value after \\x.");
}
bytes.b[width++] = hex;
break;
}
case 'u':
{
int64_t hex = scan_hex_literal(lexer, 4);
if (hex < 0)
{
lexer->lexing_start = start;
return add_error_token(lexer, "Expected a four character hex value after \\u.");
}
if (build_target.little_endian)
{
bytes.b[width++] = hex & 0xFF;
bytes.b[width++] = hex >> 8;
}
else
{
bytes.b[width++] = hex >> 8;
bytes.b[width++] = hex & 0xFF;
}
break;
}
case 'U':
{
int64_t hex = scan_hex_literal(lexer, 8);
if (hex < 0)
{
lexer->lexing_start = start;
return add_error_token(lexer, "Expected an eight character hex value after \\U.");
}
if (build_target.little_endian)
{
bytes.b[width++] = hex & 0xFF;
bytes.b[width++] = (hex >> 8) & 0xFF;
bytes.b[width++] = (hex >> 16) & 0xFF;
bytes.b[width++] = hex >> 24;
}
else
{
bytes.b[width++] = hex >> 24;
bytes.b[width++] = (hex >> 16) & 0xFF;
bytes.b[width++] = (hex >> 8) & 0xFF;
bytes.b[width++] = hex & 0xFF;
}
break;
}
default:
bytes.b[width++] = escape;
}
}
}
if (width == 0)
{
return add_error_token(lexer, "The character literal was empty.");
@@ -461,7 +559,13 @@ static inline bool scan_char(Lexer *lexer)
{
add_error_token(lexer, "Character literals may only be 1, 2 or 8 characters wide.");
}
return add_token(lexer, TOKEN_INTEGER, lexer->lexing_start);
TokenData *data;
SourceLocation *loc;
add_generic_token(lexer, TOKEN_CHAR_LITERAL, &loc, &data);
data->char_lit.u64 = bytes.u64;
data->width = (char)width;
return true;
}
static inline bool scan_string(Lexer *lexer)
@@ -643,9 +747,9 @@ static bool lexer_scan_token_inner(Lexer *lexer)
case ']':
return add_token(lexer, TOKEN_RBRACKET, "]");
case '.':
if (match(lexer, '.')) return match(lexer, '.') ? add_token(lexer, TOKEN_ELLIPSIS, "...") : add_token(lexer,
TOKEN_DOTDOT,
"..");
if (match(lexer, '.')) return match(lexer, '.')
? add_token(lexer, TOKEN_ELLIPSIS, "...")
: add_token(lexer, TOKEN_DOTDOT, "..");
return add_token(lexer, TOKEN_DOT, ".");
case '~':
return add_token(lexer, TOKEN_BIT_NOT, "~");
@@ -748,7 +852,13 @@ void lexer_init_with_file(Lexer *lexer, File *file)
lexer->lexer_index = file->token_start_id;
while(1)
{
if (!lexer_scan_token_inner(lexer)) break;
if (!lexer_scan_token_inner(lexer))
{
if (reached_end(lexer)) break;
while (!reached_end(lexer) && peek(lexer) != '\n') next(lexer);
lexer->lexing_start = lexer->current;
continue;
}
}
}

View File

@@ -179,8 +179,6 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
UNREACHABLE
case EXPR_MACRO_BLOCK:
TODO
case EXPR_FAIL_CHECK:
UNREACHABLE
case EXPR_IDENTIFIER:
return decl_ref(expr->identifier_expr.decl);
case EXPR_UNARY:
@@ -928,6 +926,7 @@ static inline LLVMValueRef gencontext_emit_else_jump_expr(GenContext *context, E
return value;
}
static LLVMValueRef gencontext_emit_else_expr(GenContext *context, Expr *expr)
{
if (expr->else_expr.is_jump) return gencontext_emit_else_jump_expr(context, expr);
@@ -979,6 +978,47 @@ static LLVMValueRef gencontext_emit_else_expr(GenContext *context, Expr *expr)
}
static inline LLVMValueRef gencontext_emit_guard_expr(GenContext *context, Expr *expr)
{
LLVMBasicBlockRef guard_block = gencontext_create_free_block(context, "guard_block");
LLVMBasicBlockRef no_err_block = gencontext_create_free_block(context, "noerr_block");
// Store catch/error var
PUSH_ERROR();
// Set the catch/error var
LLVMValueRef error_var = gencontext_emit_alloca(context, llvm_type(type_error), "");
context->error_var = error_var;
context->catch_block = guard_block;
LLVMValueRef value = gencontext_emit_expr(context, expr->guard_expr.inner);
// Restore.
POP_ERROR();
// Emit success and to end.
gencontext_emit_br(context, no_err_block);
POP_ERROR();
// Emit else
gencontext_emit_block(context, guard_block);
// Ensure we are on a branch that is non empty.
if (gencontext_check_block_branch_emit(context))
{
gencontext_emit_defer(context, expr->guard_expr.defer, 0);
LLVMBuildRet(context->builder, gencontext_emit_load(context, type_error, error_var));
context->current_block = NULL;
context->current_block_is_target = NULL;
}
gencontext_emit_block(context, no_err_block);
return value;
}
static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr)
{
BinaryOp binary_op = expr->binary_expr.operator;
@@ -1366,33 +1406,6 @@ static inline LLVMValueRef gencontext_emit_failable(GenContext *context, Expr *e
return LLVMGetUndef(llvm_type(expr->type));
}
LLVMValueRef gencontext_emit_check_failable(GenContext *context, Expr *expr)
{
PUSH_ERROR();
LLVMBasicBlockRef after_check_block = gencontext_create_free_block(context, "after_check");
context->error_var = NULL;
context->catch_block = after_check_block;
gencontext_emit_expr(context, expr->fail_check_expr);
POP_ERROR();
LLVMBasicBlockRef phi_block = gencontext_create_free_block(context, "checkphi");
LLVMBasicBlockRef normal_block = context->current_block;
gencontext_emit_br(context, phi_block);
gencontext_emit_block(context, after_check_block);
gencontext_emit_br(context, phi_block);
gencontext_emit_block(context, phi_block);
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(type_bool), "val");
LLVMValueRef logic_values[2] = { llvm_int(type_bool, 0), llvm_int(type_bool, 1) };
LLVMBasicBlockRef blocks[2] = { after_check_block, normal_block };
LLVMAddIncoming(phi, logic_values, blocks, 2);
return phi;
}
LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr)
{
NESTED_RETRY:
@@ -1405,8 +1418,6 @@ NESTED_RETRY:
case EXPR_DESIGNATED_INITIALIZER:
// Should only appear when generating designated initializers.
UNREACHABLE
case EXPR_FAIL_CHECK:
return gencontext_emit_check_failable(context, expr);
case EXPR_FAILABLE:
return gencontext_emit_failable(context, expr);
case EXPR_TRY:
@@ -1436,7 +1447,7 @@ NESTED_RETRY:
case EXPR_POST_UNARY:
return gencontext_emit_post_unary_expr(context, expr);
case EXPR_GUARD:
return gencontext_emit_trycatch_expr(context, expr);
return gencontext_emit_guard_expr(context, expr);
case EXPR_TYPEID:
return gencontext_emit_typeid(context, expr);
case EXPR_TYPE_ACCESS:

View File

@@ -4,7 +4,7 @@
#include "compiler_internal.h"
Decl *module_find_symbol(Module *module, const char *symbol, ModuleSymbolSearch search)
Decl *module_find_symbol(Module *module, const char *symbol, ModuleSymbolSearch search, Decl **private_decl)
{
Decl *decl = stable_get(&module->symbols, symbol);
if (decl)
@@ -12,11 +12,18 @@ Decl *module_find_symbol(Module *module, const char *symbol, ModuleSymbolSearch
switch (decl->visibility)
{
case VISIBLE_LOCAL:
*private_decl = decl;
decl = NULL;
break;
case VISIBLE_EXTERN:
decl = NULL;
break;
case VISIBLE_MODULE:
if (search == MODULE_SYMBOL_SEARCH_EXTERNAL) decl = NULL;
if (search == MODULE_SYMBOL_SEARCH_EXTERNAL)
{
*private_decl = decl;
decl = NULL;
}
break;
case VISIBLE_PUBLIC:
break;
@@ -27,7 +34,7 @@ Decl *module_find_symbol(Module *module, const char *symbol, ModuleSymbolSearch
if (search == MODULE_SYMBOL_SEARCH_THIS) search = MODULE_SYMBOL_SEARCH_PARENT;
VECEACH (module->sub_modules, i)
{
if ((decl = module_find_symbol(module->sub_modules[i], symbol, search))) break;
if ((decl = module_find_symbol(module->sub_modules[i], symbol, search, private_decl))) break;
}
}
return decl;

View File

@@ -224,24 +224,13 @@ static bool token_may_end_expression(TokenType type)
return false;
}
}
static inline Expr *parse_check_failable(Context *context, Expr *left_side)
{
Expr *expr_unwrap = EXPR_NEW_EXPR(EXPR_FAIL_CHECK, left_side);
advance_and_verify(context, TOKEN_QUESTION);
expr_unwrap->fail_check_expr = left_side;
RANGE_EXTEND_PREV(expr_unwrap);
return expr_unwrap;
}
static Expr *parse_ternary_expr(Context *context, Expr *left_side)
{
assert(expr_ok(left_side));
if (TOKEN_IS(TOKEN_QUESTION) && token_may_end_expression(context->next_tok.type))
{
return parse_check_failable(context, left_side);
}
Expr *expr_ternary = EXPR_NEW_EXPR(EXPR_TERNARY, left_side);
expr_ternary->ternary_expr.cond = left_side;
@@ -459,9 +448,10 @@ static Expr *parse_try_expr(Context *context, Expr *left)
static Expr *parse_bangbang_expr(Context *context, Expr *left)
{
Expr *guard_expr = EXPR_NEW_TOKEN(EXPR_GUARD, context->tok);
Expr *guard_expr = EXPR_NEW_EXPR(EXPR_GUARD, left);
advance_and_verify(context, TOKEN_BANGBANG);
guard_expr->guard_expr = left;
guard_expr->guard_expr.inner = left;
RANGE_EXTEND_PREV(guard_expr);
return guard_expr;
}
@@ -499,73 +489,7 @@ static Expr *parse_integer(Context *context, Expr *left)
Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST, context->tok);
const char *string = TOKSTR(context->tok);
const char *end = string + TOKLEN(context->tok);
if (string[0] == '\'')
{
union
{
uint8_t u8;
uint16_t u16;
uint32_t u32;
uint64_t u64;
uint8_t b[8];
} bytes;
int pos = 0;
while (++string < end - 1)
{
if (*string == '\\')
{
if (*(++string) == 'x')
{
int hex = 0;
for (int j = 0; j < 2; j++)
{
hex <<= 4U;
char c = *(++string);
if (c < 'A')
{
hex += c - '0';
}
else if (c < 'a')
{
hex += c - 'A' + 10;
}
else
{
hex += c - 'a' + 10;
}
}
bytes.b[pos++] = hex;
continue;
}
}
bytes.b[pos++] = (unsigned)*string;
}
switch (pos)
{
case 1:
expr_const_set_int(&expr_int->const_expr, bytes.u8, TYPE_U8);
expr_int->type = type_byte;
break;
case 2:
expr_const_set_int(&expr_int->const_expr, bytes.u8, TYPE_U16);
expr_int->type = type_ushort;
break;
case 4:
expr_const_set_int(&expr_int->const_expr, bytes.u8, TYPE_U32);
expr_int->type = type_uint;
break;
case 8:
expr_const_set_int(&expr_int->const_expr, bytes.u8, TYPE_U64);
expr_int->type = type_ulong;
break;
default:
UNREACHABLE
}
expr_int->resolve_status = RESOLVE_DONE;
advance(context);
return expr_int;
}
BigInt *i = &expr_int->const_expr.i;
bigint_init_unsigned(i, 0);
BigInt diff;
@@ -636,6 +560,38 @@ static Expr *parse_integer(Context *context, Expr *left)
return expr_int;
}
static Expr *parse_char_lit(Context *context, Expr *left)
{
assert(!left && "Had left hand side");
Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST, context->tok);
TokenData *data = tokendata_from_id(context->tok.id);
switch (data->width)
{
case 1:
expr_const_set_int(&expr_int->const_expr, data->char_lit.u8, TYPE_IXX);
expr_int->type = type_compint;
break;
case 2:
expr_const_set_int(&expr_int->const_expr, data->char_lit.u16, TYPE_IXX);
expr_int->type = type_compint;
break;
case 4:
expr_const_set_int(&expr_int->const_expr, data->char_lit.u32, TYPE_IXX);
expr_int->type = type_compint;
break;
case 8:
expr_const_set_int(&expr_int->const_expr, data->char_lit.u64, TYPE_U64);
expr_int->type = type_ulong;
break;
default:
UNREACHABLE
}
expr_int->resolve_status = RESOLVE_DONE;
advance(context);
return expr_int;
}
static Expr *parse_double(Context *context, Expr *left)
{
@@ -932,6 +888,7 @@ ParseRule rules[TOKEN_EOF + 1] = {
[TOKEN_FALSE] = { parse_bool, NULL, PREC_NONE },
[TOKEN_NIL] = { parse_nil, NULL, PREC_NONE },
[TOKEN_INTEGER] = { parse_integer, NULL, PREC_NONE },
[TOKEN_CHAR_LITERAL] = { parse_char_lit, NULL, PREC_NONE },
[TOKEN_IDENT] = { parse_maybe_scope, NULL, PREC_NONE },
[TOKEN_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE },
[TOKEN_CT_IDENT] = { parse_identifier, NULL, PREC_NONE },

View File

@@ -884,6 +884,7 @@ Ast *parse_stmt(Context *context)
case TOKEN_STAR:
case TOKEN_AMP:
case TOKEN_INTEGER:
case TOKEN_CHAR_LITERAL:
case TOKEN_BIT_NOT:
case TOKEN_BIT_OR:
case TOKEN_BIT_XOR:

View File

@@ -196,6 +196,8 @@ static void recover_top_level(Context *context)
case TOKEN_UNION:
case TOKEN_MACRO:
case TOKEN_EXTERN:
case TOKEN_ENUM:
case TOKEN_ERR:
return;
default:
advance(context);
@@ -209,6 +211,7 @@ static void recover_top_level(Context *context)
static inline Path *parse_module_path(Context *context)
{
assert(TOKEN_IS(TOKEN_IDENT));
char *scratch_ptr = context->path_scratch;
size_t offset = 0;
@@ -1791,6 +1794,12 @@ static inline void parse_module(Context *context)
return;
}
if (!TOKEN_IS(TOKEN_IDENT))
{
SEMA_TOKEN_ERROR(context->tok, "Module statement should be followed by the name of the module to import.");
return;
}
Path *path = parse_module_path(context);
// Expect the module name
@@ -1948,6 +1957,7 @@ static inline void parse_current(Context *context)
void parse_file(Context *context)
{
lexer_init_with_file(&context->lexer, context->file);
if (diagnostics.errors) return;
parse_current(context);
}

View File

@@ -641,7 +641,7 @@ static inline bool sema_analyse_error(Context *context __unused, Decl *decl)
sema_set_struct_size(decl);
if (decl->strukt.size > type_size(type_usize))
{
SEMA_ERROR(decl, "Error type may exceed pointer size (%d bytes) it was %d bytes.", type_size(type_usize), error_size);
SEMA_ERROR(decl, "Error type may not exceed pointer size (%d bytes) it was %d bytes.", type_size(type_usize), error_size);
return false;
}
decl->strukt.abi_alignment = type_abi_alignment(type_voidptr);

View File

@@ -249,8 +249,13 @@ static inline bool find_possible_inferred_identifier(Type *to, Expr *expr)
}
static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr *expr)
{
Decl *ambiguous_decl;
Decl *decl = sema_resolve_symbol(context, expr->identifier_expr.identifier, expr->identifier_expr.path, &ambiguous_decl);
Decl *ambiguous_decl = NULL;
Decl *private_symbol = NULL;
Decl *decl = sema_resolve_symbol(context,
expr->identifier_expr.identifier,
expr->identifier_expr.path,
&ambiguous_decl,
&private_symbol);
if (!decl && !expr->identifier_expr.path && to)
{
if (find_possible_inferred_identifier(to, expr)) return true;
@@ -258,7 +263,18 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr
if (!decl)
{
SEMA_ERROR(expr, "The symbol '%s' could not be found.", expr->identifier_expr.identifier);
if (private_symbol)
{
SEMA_ERROR(expr, "'%s' is not visible from this module.", expr->identifier_expr.identifier);
}
else if (ambiguous_decl)
{
SEMA_ERROR(expr, "The name '%s' ambiguous, please add a path.", expr->identifier_expr.identifier);
}
else
{
SEMA_ERROR(expr, "Identifier '%s' could not be found.", expr->identifier_expr.identifier);
}
return false;
}
@@ -1411,45 +1427,6 @@ static inline bool sema_expr_analyse_cast(Context *context, Type *to, Expr *expr
return true;
}
static inline bool sema_expr_analyse_fail_check(Context *context, Expr *expr)
{
Expr *inner = expr->fail_check_expr;
switch (inner->expr_kind)
{
case EXPR_IDENTIFIER:
case EXPR_CALL:
case EXPR_CAST:
case EXPR_EXPR_BLOCK:
case EXPR_GROUP:
case EXPR_TYPE_ACCESS:
case EXPR_SUBSCRIPT:
case EXPR_ACCESS:
case EXPR_TYPEID:
case EXPR_TYPEOF:
break;
default:
SEMA_ERROR(expr, "Ambiguous use of unwrapping operator '?', use '()' around the expression to indicate intent.");
return false;
}
if (!sema_analyse_expr(context, NULL, inner)) return false;
if (!inner->failable)
{
SEMA_ERROR(expr, "You can only check a failable type e.g. '%s!' not '%s'.",
type_to_error_string(inner->type), type_to_error_string(inner->type));
return false;
}
if (inner->expr_kind == EXPR_IDENTIFIER && (context->current_scope->flags & SCOPE_COND))
{
Decl *var = inner->identifier_expr.decl;
Decl *decl = COPY(var);
decl->var.kind = VARDECL_ALIAS;
decl->var.alias = var;
sema_unwrap_var(context, decl);
}
expr->type = type_bool;
return true;
}
bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *left_type, Expr *right, ExprFailableStatus lhs_is_failable)
{
// 1. Evaluate right side to required type.
@@ -2766,6 +2743,26 @@ static inline bool sema_expr_analyse_else(Context *context, Type *to, Expr *expr
return cast_implicit(context, expr->else_expr.expr, common);
}
static inline bool sema_expr_analyse_guard(Context *context, Type *to, Expr *expr)
{
Expr *inner = expr->guard_expr.inner;
bool success = sema_analyse_expr(context, to, inner);
expr->guard_expr.defer = context->current_scope->defers.end;
if (!success) return false;
expr->type = inner->type;
if (!inner->failable)
{
SEMA_ERROR(inner, "No failable to rethrow before '!!' in the expression, please remove '!!'.");
return false;
}
if (!context->failable_return)
{
SEMA_ERROR(expr, "This expression implicitly returns with a failable result, but the function does not allow failable results. Did you mean to use 'else' instead?");
return false;
}
return true;
}
static Ast *ast_shallow_copy(Ast *source)
{
return COPY(source);
@@ -2837,9 +2834,6 @@ static Expr *expr_copy_from_macro(Context *context, Expr *source_expr)
case EXPR_DECL_LIST:
MACRO_COPY_AST_LIST(expr->dexpr_list_expr);
return expr;
case EXPR_FAIL_CHECK:
MACRO_COPY_EXPR(expr->fail_check_expr);
return expr;
case EXPR_FAILABLE:
MACRO_COPY_EXPR(expr->failable_expr);
return expr;
@@ -2876,7 +2870,7 @@ static Expr *expr_copy_from_macro(Context *context, Expr *source_expr)
case EXPR_POISONED:
return source_expr;
case EXPR_GUARD:
MACRO_COPY_EXPR(expr->guard_expr);
MACRO_COPY_EXPR(expr->guard_expr.inner);
return expr;
case EXPR_CONST:
return expr;
@@ -3234,8 +3228,6 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *
{
case EXPR_DECL_LIST:
UNREACHABLE
case EXPR_FAIL_CHECK:
return sema_expr_analyse_fail_check(context, expr);
case EXPR_FAILABLE:
return sema_expr_analyse_failable(context, to, expr);
case EXPR_POISONED:
@@ -3259,7 +3251,7 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *
case EXPR_EXPR_BLOCK:
return sema_expr_analyse_expr_block(context, to, expr);
case EXPR_GUARD:
return sema_expr_analyse_try(context, expr);
return sema_expr_analyse_guard(context, to, expr);
case EXPR_RANGE:
SEMA_ERROR(expr, "Range expression was not expected here.");
return false;

View File

@@ -26,7 +26,8 @@ static inline bool matches_subpath(Path *path_to_check, Path *path_to_find)
return 0 == memcmp(path_to_check->module + compare_start, path_to_find->module, path_to_find->len);
}
static Decl *sema_resolve_path_symbol(Context *context, const char *symbol, Path *path, Decl **ambiguous_other_decl)
static Decl *sema_resolve_path_symbol(Context *context, const char *symbol, Path *path, Decl **ambiguous_other_decl,
Decl **private_decl)
{
assert(path && "Expected path.");
*ambiguous_other_decl = NULL;
@@ -41,7 +42,7 @@ static Decl *sema_resolve_path_symbol(Context *context, const char *symbol, Path
if (path->len > import->import.path->len) continue;
if (!matches_subpath(import->import.path, path)) continue;
path_found = true;
Decl *found = module_find_symbol(import->module, symbol, MODULE_SYMBOL_SEARCH_EXTERNAL);
Decl *found = module_find_symbol(import->module, symbol, MODULE_SYMBOL_SEARCH_EXTERNAL, private_decl);
if (!found) continue;
if (decl)
{
@@ -63,11 +64,12 @@ static Decl *sema_resolve_path_symbol(Context *context, const char *symbol, Path
return decl;
}
Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl **ambiguous_other_decl)
Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl **ambiguous_other_decl,
Decl **private_decl)
{
if (path)
{
return sema_resolve_path_symbol(context, symbol, path, ambiguous_other_decl);
return sema_resolve_path_symbol(context, symbol, path, ambiguous_other_decl, private_decl);
}
*ambiguous_other_decl = NULL;
@@ -90,7 +92,7 @@ Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl
if (decl) return decl;
// Search in the module and child modules.
decl = module_find_symbol(context->module, symbol, MODULE_SYMBOL_SEARCH_THIS);
decl = module_find_symbol(context->module, symbol, MODULE_SYMBOL_SEARCH_THIS, private_decl);
if (decl)
{
@@ -103,7 +105,7 @@ Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl
{
Decl *import = context->imports[i];
if (!decl_ok(import)) continue;
Decl *found = module_find_symbol(import->module, symbol, MODULE_SYMBOL_SEARCH_EXTERNAL);
Decl *found = module_find_symbol(import->module, symbol, MODULE_SYMBOL_SEARCH_EXTERNAL, private_decl);
if (!found) continue;
if (decl)
{
@@ -135,7 +137,8 @@ static inline bool sema_append_local(Context *context, Decl *decl)
bool sema_add_local(Context *context, Decl *decl)
{
Decl *dummy;
Decl *other = sema_resolve_symbol(context, decl->name, NULL, &dummy);
Decl *dummy2;
Decl *other = sema_resolve_symbol(context, decl->name, NULL, &dummy, &dummy2);
if (other)
{
sema_shadow_error(decl, other);

View File

@@ -122,6 +122,13 @@ void sema_analysis_pass_decls(Context *context)
{
sema_analyse_decl(context, context->functions[i]);
}
DEBUG_LOG("Pass finished with %d error(s).", diagnostics.errors);
}
void sema_analysis_pass_functions(Context *context)
{
DEBUG_LOG("Pass: Function analysis %s", context->file->name);
VECEACH(context->methods, i)
{
analyse_func_body(context, context->methods[i]);

View File

@@ -535,7 +535,8 @@ static DynamicScope *context_find_scope_by_id(Context *context, unsigned scope_i
static inline Decl *sema_analyse_label(Context *context, Ast *stmt)
{
Decl *ambiguous;
Decl *target = sema_resolve_symbol(context, stmt->contbreak_stmt.label.name, NULL, &ambiguous);
Decl *dummy;
Decl *target = sema_resolve_symbol(context, stmt->contbreak_stmt.label.name, NULL, &ambiguous, &dummy);
if (!target)
{
SEMA_ERROR(stmt, "Cannot find a labelled statement with the name '%s'.", stmt->contbreak_stmt.label.name);
@@ -613,7 +614,8 @@ static bool sema_analyse_next_stmt(Context *context, Ast *statement)
if (statement->next_stmt.label.name)
{
Decl *ambiguous;
Decl *target = sema_resolve_symbol(context, statement->next_stmt.label.name, NULL, &ambiguous);
Decl *dummy;
Decl *target = sema_resolve_symbol(context, statement->next_stmt.label.name, NULL, &ambiguous, &dummy);
if (!target)
{
SEMA_ERROR(statement, "Cannot find a switch statement with the name '%s'.", statement->next_stmt.label.name);
@@ -1077,10 +1079,11 @@ static bool sema_analyse_catch_stmt(Context *context, Ast *statement)
if (left->expr_kind == EXPR_IDENTIFIER)
{
Decl *ambiguous_decl;
Decl *dummy;
Decl *error_var_decl = sema_resolve_symbol(context,
left->identifier_expr.identifier,
left->identifier_expr.path,
&ambiguous_decl);
&ambiguous_decl, &dummy);
if (!error_var_decl)
{
error_var = decl_new_var(left->span.loc, type_info_new_base(type_error, left->span), VARDECL_LOCAL,

View File

@@ -58,14 +58,26 @@ static inline bool sema_resolve_array_type(Context *context, TypeInfo *type)
static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
{
Decl *ambiguous_decl;
Decl *ambiguous_decl = NULL;
Decl *private_decl = NULL;
Decl *decl = sema_resolve_symbol(context,
TOKSTR(type_info->unresolved.name_loc),
type_info->unresolved.path,
&ambiguous_decl);
&ambiguous_decl, &private_decl);
if (!decl)
{
SEMA_TOKID_ERROR(type_info->unresolved.name_loc, "Unknown type '%s'.", TOKSTR(type_info->unresolved.name_loc));
if (private_decl)
{
SEMA_TOKID_ERROR(type_info->unresolved.name_loc, "Type '%s' is not visible from this module.", TOKSTR(type_info->unresolved.name_loc));
}
else if (ambiguous_decl)
{
SEMA_TOKID_ERROR(type_info->unresolved.name_loc, "The type '%s' ambiguous, please add a path.", TOKSTR(type_info->unresolved.name_loc));
}
else
{
SEMA_TOKID_ERROR(type_info->unresolved.name_loc, "Unknown type '%s'.", TOKSTR(type_info->unresolved.name_loc));
}
return type_info_poison(type_info);
}

View File

@@ -166,6 +166,8 @@ const char *token_type_to_string(TokenType type)
return "INTEGER";
case TOKEN_REAL:
return "FLOAT";
case TOKEN_CHAR_LITERAL:
return "CHAR_LITERAL";
// Comments
case TOKEN_COMMENT:

View File

@@ -5,7 +5,7 @@
#include "compiler_tests/tests.h"
#include "utils/lib.h"
static int version = 100;
static int version = 101;
int main(int argc, const char *argv[])
{

View File

@@ -32,7 +32,7 @@ void error_exit(const char *format, ...) __attribute__((noreturn));
#ifndef NDEBUG
#define DEBUG_LOG(_string, ...) eprintf("-- DEBUG: "); eprintf(_string, ##__VA_ARGS__); eprintf("\n")
#define DEBUG_LOG(_string, ...) printf("-- DEBUG: "); printf(_string, ##__VA_ARGS__); printf("\n")
#else
#define DEBUG_LOG(_string, ...)
#endif

View File

@@ -112,10 +112,31 @@ static inline bool is_digit(char c)
*/
static inline int char_to_nibble(char c)
{
if (c >= '0' && c <= '9') return c - '0';
if (c <= 'F') return c - 'A' + 10;
if (c <= 'f') return c - 'a' + 10;
return -1;
char conv[256] = {
['0'] = 1,
['1'] = 2,
['2'] = 3,
['3'] = 4,
['4'] = 5,
['5'] = 6,
['6'] = 7,
['7'] = 8,
['8'] = 9,
['9'] = 10,
['A'] = 11,
['B'] = 12,
['C'] = 13,
['D'] = 14,
['E'] = 15,
['F'] = 16,
['a'] = 11,
['b'] = 12,
['c'] = 13,
['d'] = 14,
['e'] = 15,
['f'] = 16,
};
return conv[(unsigned)c] - 1;
}
static inline bool is_hex_or_(char c)
@@ -135,6 +156,45 @@ static inline bool is_hex_or_(char c)
}
}
static inline char is_valid_escape(char c)
{
switch (c)
{
case 'a':
return '\a';
case 'b':
return '\b';
case 'e':
return 0x1B;
case 'f':
return '\f';
case 'n':
return '\n';
case 'r':
return '\r';
case 't':
return '\t';
case 'v':
return '\v';
case 'x':
return 'x';
case 'u':
return 'u';
case 'U':
return 'U';
case '\'':
return '\'';
case '"':
return '"';
case '\\':
return '\\';
case '0':
return '\0';
default:
return -1;
}
}
static inline bool is_hex(char c)
{
switch (c)

205
test/src/tester.py Normal file
View File

@@ -0,0 +1,205 @@
#!/usr/bin/python
import os, sys, shutil, subprocess
TEST_DIR = '/tmp/c3test/'
class Config:
run_skipped = False
cwd = "."
numtests = 0
class File:
def __init__(self, filepath):
with open(filepath) as reader:
self.content = reader.read().splitlines()
self.filename = filepath
class Issues:
def __init__(self, conf, file, single):
self.file = file
self.single = single
self.line = 0
self.file_start = 0
self.line_offset = 0
self.has_errors = False
self.error_message = "unknown"
self.skip = False
self.cur = 0
self.current_file = None
self.errors = {}
self.warnings = {}
if single:
self.current_file = conf.cwd + "/" + file.filename
def exit_error(self, message):
print('Error in file ' + self.file.filename + ': ' + message)
exit(-1)
def set_failed(self):
if not self.has_errors: print(" Failed.")
self.has_errors = True
def check_line(self, type, file, line, message):
if file == 'test.c3': file = self.file.filename
map = {}
if type == 'Error':
map = self.errors
elif type == 'Warning':
map = self.warnings
else:
self.exit_error("Unknown type: " + type)
key = file + ":" + line
value = map.get(key)
if value == None: return False
if value in message:
del map[key]
return True
else:
return False
def parse_result(self, lines):
for line in lines:
parts = line.split('|', maxsplit=4)
if len(parts) != 4: self.exit_error("Illegal error result: " + line);
if not self.check_line(parts[0], parts[1], parts[2], parts[3]):
self.set_failed()
print("Unexpected " + parts[0].lower() + " in " + parts[1] + " line " + parts[2] + ":", end="")
print('"' + parts[3] + '"')
if len(self.errors) > 0:
self.set_failed()
print("Expected errors that never occured:")
num = 1
for key, value in self.errors.items():
pos = key.split(":", 2)
print(str(num) + ". " + pos[0] + " line: " + pos[1] + " expected: " + value)
num += 1
def compile(self, args):
path = os.path.dirname(sys.argv[0]) + "/../../cmake-build-debug/"
code = subprocess.run(path + 'c3c ' + args, universal_newlines=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if code.returncode != 0 and code.returncode != 1:
self.set_failed()
print("Error: " + code.stderr)
self.has_errors = True
return
self.parse_result(code.stderr.splitlines(keepends=False))
def parse_single(self):
lines = len(self.file.content)
while self.line < lines:
line = self.file.content[self.line].strip()
if "// #" in line:
self.parse_trailing_directive(line)
self.line += 1
with open(TEST_DIR + 'test.c3', mode='w') as f:
f.write("\n".join(self.file.content))
f.write("\n")
print("- " + self.file.filename + ":", end="")
self.compile("--test compile " + TEST_DIR + 'test.c3')
if not self.has_errors:
print(" Passed.")
def parse_header_directive(self, line):
line = line[4:].strip()
if (line.startswith("warnings:")):
print("TODO" + line)
exit(-1)
elif (line.startswith("file:")):
line = line[5:].strip()
print("NEW FILE" + line)
exit(-1)
elif (line.startswith("expect:")):
line = line[7:].strip()
print("Expect " + line)
exit(-1)
else:
self.exit_error("unknown header directive " + line)
def parse_trailing_directive(self, line):
line = line.split('// #', 2)[1].strip()
if (line.startswith("warning:")):
print("TODO" + line)
exit(-1)
elif (line.startswith("error:")):
line = line[6:].strip()
self.errors[self.file.filename + ":%d" % (self.line + 1)] = line
else:
self.exit_error("unknown trailing directive " + line)
def parse_template(self):
lines = len(self.file.content)
while self.line < lines:
line = self.file.content[self.line].strip()
if line.startswith("// #"):
self.parse_header_directive(line)
elif "// #" in line:
self.parse_trailing_directive(line)
self.line += 1
print("parse mult")
def parse(self):
if len(self.file.content) == 0: self.exit_error("File was empty")
is_skip = self.file.content[0].startswith("// #skip")
if is_skip != self.skip: return
if is_skip: self.line += 1
if self.single:
self.parse_single()
else:
self.parse_template()
def usage():
print("Usage: " + sys.argv[0] + " <file/dir> [-s]")
print('')
print('Options:')
print(" -s, --skipped only run skipped tests")
exit(-1)
def handle_file(filepath, conf):
if filepath.endswith('.c3'):
single = True
elif filepath.endswith('.c3t'):
single = False
else:
return
shutil.rmtree(TEST_DIR, ignore_errors=True)
os.mkdir(TEST_DIR, mode = 0o777)
conf.numtests += 1
issues = Issues(conf, File(filepath), single)
issues.parse()
def handle_dir(filepath, conf):
for file in os.listdir(filepath):
file = filepath + "/" + file
if os.path.isdir(file):
handle_dir(file, conf)
elif os.path.isfile(file):
handle_file(file, conf)
def main():
args = len(sys.argv)
conf = Config()
if args != 1 and args > 3: usage()
if args == 3:
if (sys.argv[2] != '-s' and sys.argv[2] != '--skipped'): usage()
conf.run_skipped = True
filepath = sys.argv[1]
if filepath.endswith('/'): filepath = filepath[:-1]
conf.cwd = os.getcwd()
if os.path.isfile(filepath):
handle_file(filepath, conf)
elif os.path.isdir(filepath):
handle_dir(filepath, conf)
else:
usage()
main()

View File

@@ -0,0 +1,11 @@
enum EnumTestOverflow
{
VALUE = 0x80000000, // #error: does not fit into 'int'
}
enum EnumTestErrorType : float // #error: must be an integer type not 'float'
{
VALUE_BOOM
}

View File

@@ -0,0 +1,19 @@
enum EnumWithErrorWithMissingName : int (int) // #error: function parameter must be named
{
TEST
}
enum EnumWithErrorData : int (int // #error: end of the parameter list
{
TEST
}
error TheError
{
union // #error: A type name was expected here
{
int a;
int b;
}
}

View File

@@ -0,0 +1,9 @@
module foo;
error TooBig // #error: Error type may not exceed pointer
{
usize a;
char b;
}

View File

@@ -0,0 +1,9 @@
module errors;
error TheError
{
int a;
}
error OtherError;

View File

@@ -0,0 +1,38 @@
module arithmetics;
func void testAdd(int a, int b)
{
a = a + b;
a = a +% b;
a +%= b;
a += b;
a += 1;
a +%= 1;
}
func void testSub(int a, int b)
{
a = a - b;
a = a -% b;
a -%= b;
a -= b;
a -%= 1;
a -= 1;
}
func void testMult(int a, int b)
{
a = a * b;
a = a *% b;
a *%= b;
a *= b;
a *%= 1;
a *= 1;
}
func void testDiv(int a, int b)
{
a = a / b;
a /= b;
a /= 1;
}

View File

@@ -0,0 +1,6 @@
module bob;
func void hello()
{
int i = 0;
}

View File

@@ -0,0 +1,3 @@
// Hello
func int foo() {} // #error: Missing return statement

View File

@@ -0,0 +1,58 @@
// @recipe bin
$warnings no-unused
$generate-c
// #file: file1
module test1;
import test2;
public func void pub1() {}
func void nonpub1() {}
public func i32 main(i32 argc, const i8*[] argv) {
return 0;
}
// #file: file2
module test2;
public func void pub2() {}
func void nonpub2() {}
// #expect: test1.h
void test1_pub1();
// #expect: test1.c
#include "test1.h"
static void test1_nonpub1();
void test1_pub1() {
}
static void test1_nonpub1() {
}
int32_t main(int32_t argc, const char* argv[]) {
return 0;
}
// @expect{atleast, build/test2.h}
void test2_pub2();
// @expect{atleast, build/test2.c}
#include "test2.h"
static void test2_nonpub2();
void test2_pub2() {
}
static void test2_nonpub2() {
}

View File

@@ -0,0 +1,13 @@
char bar = '\xaf';
char bar = '\x0F';
char bar = '\xgh'; // #error: Expected a two character
char baz = '\ueeof'; // #error: Expected a four char
char eofk = '\u233'; // #error: Expected a four char
char zab = '\Uaokdokok'; // #error: Expected an eight
char zab = '\Uaokdooekfoekfekfkeofkekok'; // #error: Expected an eight
char eofk = '\UaUfko'; // #error: Expected an eight
char foo = ' // #error: Character literal did not terminate

View File

@@ -0,0 +1,3 @@
byte bar1 = '\xaf';
char bar2 = '\x0F';
ushort bar4 = '\u0FaF';