418 lines
12 KiB
C++
418 lines
12 KiB
C++
|
|
function void
|
|
parsing_error(Token *token, const char *str, ...){
|
|
Scratch scratch;
|
|
STRING_FMT(scratch, str, string);
|
|
|
|
// @Note(Krzosa): Print nice error message
|
|
printf("\nError: %s", string.str);
|
|
if(token){
|
|
printf(" %s:%d\n", token->file.str, (S32)token->line + 1);
|
|
|
|
// @Note(Krzosa): Print error line
|
|
{
|
|
int i = 0;
|
|
while(token->line_begin[i]!='\n' && token->line_begin[i]!=0) i++;
|
|
printf("%.*s\n", i, token->line_begin);
|
|
|
|
// @Note(Krzosa): Print error marker
|
|
int token_i = token->str - token->line_begin;
|
|
for(int i = 0; i < token_i-2; i++) printf(" ");
|
|
printf("^^^^^^\n");
|
|
}
|
|
}
|
|
|
|
__debugbreak();
|
|
}
|
|
|
|
function Token *
|
|
token_get(S64 i = 0){
|
|
i += pctx->token_iter;
|
|
if(i >= pctx->tokens.len){
|
|
return &pctx->empty_token;
|
|
}
|
|
Token *result = &pctx->tokens[i];
|
|
return result;
|
|
}
|
|
|
|
function Token *
|
|
token_is_scope(){
|
|
Token *token = token_get();
|
|
if(lex_is_scope(token)) return token;
|
|
return 0;
|
|
}
|
|
|
|
function Token *
|
|
token_next(){
|
|
Token *token = token_get();
|
|
if(lex_is_scope(token)) pctx->indent = token->indent;
|
|
pctx->token_iter++;
|
|
return token;
|
|
}
|
|
|
|
function Token *
|
|
token_is(Token_Kind kind, S64 lookahead = 0){
|
|
Token *token = token_get(lookahead);
|
|
if(token->kind == kind){
|
|
return token;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
function Token *
|
|
token_match(Token_Kind kind){
|
|
Token *token = token_get();
|
|
if(token->kind == kind){
|
|
return token_next();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
function Token *
|
|
token_match_keyword(Intern_String string){
|
|
Token *token = token_get();
|
|
if(token->kind == TK_Keyword){
|
|
if(string.str == token->intern_val.str){
|
|
token = token_next();
|
|
return token;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
function Token *
|
|
token_expect(Token_Kind kind){
|
|
Token *token = token_get();
|
|
if(token->kind == kind) return token_next();
|
|
parsing_error(token, "Expected token of kind: [%s], got instead token of kind: [%s]", token_kind_string(kind).str, token_kind_string(token->kind).str);
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Expression parsing
|
|
//-----------------------------------------------------------------------------
|
|
/*
|
|
add = [+-]
|
|
mul = [/%*]
|
|
compare = == | != | >= | > | <= | <
|
|
logical = [&|^] | && | ||
|
|
unary = [&*-!~+] | ++ | --
|
|
|
|
atom_expr = Int
|
|
| Float
|
|
| String
|
|
| Identifier
|
|
| 'cast' '(' typespec ',' expr ')'
|
|
| 'size_type' '(' typespec ')'
|
|
| 'size_expr' '(' expr ')'
|
|
| '{' compound_expr '}'
|
|
| '(' expr ')'
|
|
| '(' ':' typespec ')' '{' compound_expr '}'
|
|
postfix_expr = atom_expr ('[' expr ']' | '.' Identifier | ++ | -- | '(' expr_list ')')*
|
|
unary_expr = unary ? unary_expr : atom_expr
|
|
mul_expr = atom_expr (mul atom_expr)*
|
|
add_expr = mul_expr (add mul_expr)*
|
|
logical_expr = add_expr (logical add_expr)*
|
|
compare_expr = logical_expr (compare logical_expr)*
|
|
ternary_expr = compare_expr ('?' ternary_expr ':' ternary_expr)?
|
|
expr = logical_expr
|
|
|
|
Compound literals
|
|
- (:[23]*Type){}
|
|
- Type{}
|
|
- { }
|
|
*/
|
|
function Ast_Expr *parse_expr(S64 rbp = 0);
|
|
function Ast_Typespec *parse_typespec();
|
|
|
|
function Ast_Compound *
|
|
parse_expr_compound(){
|
|
Scratch scratch;
|
|
Token *pos = token_get();
|
|
Array<Ast_Compound_Item *> exprs = {scratch};
|
|
while(!token_is(TK_CloseBrace)){
|
|
Token *token = token_get();
|
|
Ast_Expr *index = 0;
|
|
Ast_Expr *name = 0;
|
|
if(token_match(TK_OpenBracket)){
|
|
index = parse_expr();
|
|
token_expect(TK_CloseBracket);
|
|
token_expect(TK_Assign);
|
|
}
|
|
else if(token_is(TK_Identifier)){
|
|
token = token_next();
|
|
name = ast_expr_identifier(token, token->intern_val);
|
|
token_expect(TK_Assign);
|
|
}
|
|
|
|
Ast_Expr *item = parse_expr();
|
|
Ast_Compound_Item *item_comp = ast_expr_compound_item(token, index, name, item);
|
|
exprs.add(item_comp);
|
|
|
|
if(!token_match(TK_Comma)){
|
|
break;
|
|
}
|
|
}
|
|
token_expect(TK_CloseBrace);
|
|
|
|
Ast_Compound *result = ast_expr_compound(pos, 0, exprs);
|
|
return result;
|
|
}
|
|
|
|
function Ast_Typespec *
|
|
parse_optional_type(){
|
|
Ast_Typespec *result = 0;
|
|
if(token_match(TK_Colon)) result = parse_typespec();
|
|
return result;
|
|
}
|
|
|
|
function Ast_Decl *parse_decl(B32);
|
|
function Ast_Block *
|
|
parse_block(){
|
|
Ast_Block *block = 0;
|
|
if(token_match(OPEN_SCOPE)){
|
|
Token *token_block = token_get();
|
|
|
|
Scratch scratch;
|
|
Array<Ast *> stmts = {scratch};
|
|
do{
|
|
Token *token = token_get();
|
|
if(token_match_keyword(keyword_return)){
|
|
AST_NEW(Return, AST_RETURN, token);
|
|
if(!token_is_scope()) result->expr = parse_expr();
|
|
stmts.add(result);
|
|
}
|
|
else{
|
|
Ast_Decl *result = parse_decl(false);
|
|
if(result) stmts.add(result);
|
|
else parsing_error(token, "Unexpected token while parsing statement");
|
|
}
|
|
} while(token_match(SAME_SCOPE));
|
|
token_expect(CLOSE_SCOPE);
|
|
block = ast_block(token_block, stmts);
|
|
}
|
|
return block;
|
|
}
|
|
|
|
function Ast_Lambda *
|
|
parse_lambda(Token *token, B32 is_typespec = false){
|
|
Scratch scratch;
|
|
Array<Ast_Lambda_Arg *> params = {scratch};
|
|
if(!token_is(TK_CloseParen)){
|
|
for(;;){
|
|
Token *name = token_expect(TK_Identifier);
|
|
token_expect(TK_Colon);
|
|
Ast_Typespec *typespec = parse_typespec();
|
|
Ast_Lambda_Arg *param = ast_expr_lambda_arg(name, name->intern_val, typespec);
|
|
params.add(param);
|
|
|
|
if(!token_match(TK_Comma)){
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
token_expect(TK_CloseParen);
|
|
|
|
Ast_Typespec *ret = parse_optional_type();
|
|
Ast_Block *block = is_typespec ? 0 : parse_block();
|
|
Ast_Lambda *result = ast_lambda(token, params, ret, block);
|
|
return result;
|
|
}
|
|
|
|
function Ast_Expr *
|
|
null_denotation(Token *token){
|
|
switch(token->kind){
|
|
case TK_StringLit : return ast_expr_string(token, token->intern_val);
|
|
case TK_Identifier: return ast_expr_identifier(token, token->intern_val);
|
|
case TK_Integer : return ast_expr_integer(token, token->int_val);
|
|
case TK_Pointer : return ast_expr_unary(token, TK_Dereference, parse_expr());
|
|
case TK_Keyword: {
|
|
if(token->intern_val == keyword_cast){
|
|
token_expect(TK_OpenParen);
|
|
Ast_Expr *expr = parse_expr();
|
|
token_expect(TK_Colon);
|
|
Ast_Typespec *typespec = parse_typespec();
|
|
token_expect(TK_CloseParen);
|
|
return ast_expr_cast(token, expr, typespec);
|
|
}
|
|
else {
|
|
parsing_error(token, "Unexpected keyword: [%s], expected keyword [cast]", token->intern_val.str);
|
|
return 0;
|
|
}
|
|
}break;
|
|
case TK_OpenBrace: return parse_expr_compound();
|
|
case TK_OpenParen: {
|
|
if (token_is(TK_CloseParen)) return parse_lambda(token);
|
|
else if(token_is(TK_Identifier) && token_is(TK_Colon, 1)) return parse_lambda(token);
|
|
else{
|
|
Ast_Expr *result = parse_expr();
|
|
token_expect(TK_CloseParen);
|
|
return result;
|
|
}
|
|
}
|
|
default: parsing_error(token, "Unexpected token of kind: [%s] in expression", token_kind_string(token->kind).str); return 0;
|
|
}
|
|
}
|
|
|
|
function S64
|
|
left_binding_power(Token_Kind kind){
|
|
switch(kind){
|
|
case TK_Sub: case TK_Add: return 1;
|
|
case TK_Mul: case TK_Div: return 2;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
function S64
|
|
postfix_binding_power(Token_Kind kind){
|
|
switch(kind){
|
|
case TK_Increment: case TK_Decrement: case TK_Pointer: case TK_OpenBracket: return 1;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
function Ast_Expr *
|
|
left_denotation(Token *op, Ast_Expr *left){
|
|
enum{ Left_Associative, Right_Associative };
|
|
S64 assoc = Left_Associative;
|
|
Ast_Expr *right = parse_expr(left_binding_power(op->kind) - assoc);
|
|
switch(op->kind){
|
|
case TK_Add: case TK_Mul: case TK_Sub: case TK_Div: return ast_expr_binary(left, right, op);
|
|
default: parsing_error(op, "Unexpected token of kind: [%s] in expression", token_kind_string(op->kind).str); return 0;
|
|
}
|
|
}
|
|
|
|
function Ast_Expr *
|
|
parse_expr(S64 rbp){
|
|
Token *token = token_next();
|
|
Ast_Expr *left = null_denotation(token);
|
|
for(;;){
|
|
token = token_get();
|
|
|
|
if((rbp == 0) && (token->kind == TK_Increment || token->kind == TK_Decrement || token->kind == TK_Pointer || token->kind == TK_OpenBracket)){
|
|
token_next();
|
|
if(token->kind == TK_OpenBracket){
|
|
Ast_Expr *index = parse_expr();
|
|
left = ast_expr_index(token, left, index);
|
|
token_expect(TK_CloseBracket);
|
|
}
|
|
else{
|
|
if(token->kind == TK_Increment) token->kind = TK_PostIncrement;
|
|
else if(token->kind == TK_Decrement) token->kind = TK_PostDecrement;
|
|
left = ast_expr_unary(token, token->kind, left);
|
|
}
|
|
}
|
|
|
|
else if(rbp < left_binding_power(token->kind)){
|
|
token = token_next();
|
|
left = left_denotation(token, left);
|
|
}
|
|
else break;
|
|
|
|
}
|
|
return left;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Parsing declarations
|
|
//-----------------------------------------------------------------------------
|
|
// [10]*int - Array of 10 pointers to ints
|
|
function Ast_Typespec *
|
|
parse_typespec_recurse(){
|
|
Token *token = token_get();
|
|
if(token_match(TK_Pointer)){
|
|
Ast_Typespec *result = parse_typespec_recurse();
|
|
result = ast_typespec_pointer(token, result);
|
|
return result;
|
|
}
|
|
else if(token_match(TK_OpenBracket)){
|
|
Ast_Expr *expr = parse_expr();
|
|
token_expect(TK_CloseBracket);
|
|
Ast_Typespec *result = parse_typespec_recurse();
|
|
result = ast_typespec_array(token, result, expr);
|
|
return result;
|
|
}
|
|
else if(token_match(TK_OpenParen)){
|
|
Ast_Lambda *result = parse_lambda(token, true);
|
|
return ast_typespec_lambda(token, result);
|
|
}
|
|
else if(token_match(TK_Identifier)){
|
|
Ast_Typespec *result = ast_typespec_name(token, token->intern_val);
|
|
return result;
|
|
}
|
|
else{
|
|
parsing_error(token, "Failed to parse type, unexpected token of kind", token_kind_string(token->kind).str);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
function Ast_Typespec *
|
|
parse_typespec(){
|
|
Ast_Typespec *result = parse_typespec_recurse();
|
|
return result;
|
|
}
|
|
|
|
function Ast_Expr *
|
|
parse_assign_expr(){
|
|
Ast_Expr *result = 0;
|
|
if(token_match(TK_Assign)) result = parse_expr();
|
|
return result;
|
|
}
|
|
|
|
function Ast_Decl *
|
|
parse_decl(B32 is_global){
|
|
Ast_Decl *result = 0;
|
|
if(is_global) token_match(SAME_SCOPE);
|
|
if(token_is(TK_Identifier)){
|
|
if(is_global && pctx->indent != 0) parsing_error(token_get(), "Top level declarations shouldn't be indented");
|
|
Token *name = token_next();
|
|
if(token_match(TK_DoubleColon)){ // Constant
|
|
Ast_Expr *expr = parse_expr();
|
|
result = ast_decl_const(name, name->intern_val, expr);
|
|
}
|
|
else if(token_match(TK_Colon)){
|
|
Ast_Typespec *typespec = 0;
|
|
Ast_Expr *expr = 0;
|
|
if(!token_is(TK_Assign)) typespec = parse_typespec();
|
|
if(token_match(TK_Assign)) expr = parse_expr();
|
|
if(!expr && !typespec) parsing_error(name, "invalid declaration, no type or value");
|
|
|
|
result = ast_decl_var(name, typespec, name->intern_val, expr);
|
|
}
|
|
else{
|
|
Token *token = token_get();
|
|
parsing_error(token, "Unexpected token: [%s] when parsing a declaration", token_kind_string(token->kind).str);
|
|
}
|
|
}
|
|
else if(!token_is(TK_End)){
|
|
if(is_global){
|
|
Token *token = token_get();
|
|
parsing_error(token, "Unexpected token: [%s] when parsing a declaration", token_kind_string(token->kind).str);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function Ast_Package *
|
|
parse_file(){
|
|
Scratch scratch;
|
|
|
|
//
|
|
// @note: pop the first token which which always should be an indentation token,
|
|
// it updates the indent info on the parser, making sure that indentation on
|
|
// the first line is properly updated
|
|
//
|
|
Token *token = token_get();
|
|
Array<Ast_Decl *>decls = {scratch};
|
|
while(!token_is(TK_End)){
|
|
token_expect(SAME_SCOPE);
|
|
Ast_Decl *decl = parse_decl(true);
|
|
if(!decl) break;
|
|
decls.add(decl);
|
|
}
|
|
Ast_Package *result = ast_package(token, token->file, decls);
|
|
return result;
|
|
}
|
|
|
|
|