mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
parse and codegen for math expressions
This commit is contained in:
parent
f6529341a2
commit
e5d1f0eea5
7 changed files with 1398 additions and 185 deletions
66
README.md
66
README.md
|
|
@ -30,6 +30,16 @@ readable, safe, optimal, and concise code to solve any computing problem.
|
|||
This mode should automatically provide test coverage.
|
||||
* Memory zeroed by default, unless you initialize with "uninitialized".
|
||||
|
||||
### Building
|
||||
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
./run_tests
|
||||
```
|
||||
|
||||
## Roadmap
|
||||
|
||||
* Math expression
|
||||
|
|
@ -93,25 +103,63 @@ PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type
|
|||
|
||||
Block : token(LBrace) many(Statement) token(RBrace)
|
||||
|
||||
Statement : ExpressionStatement | ReturnStatement
|
||||
Statement : ExpressionStatement
|
||||
|
||||
ExpressionStatement : Expression token(Semicolon)
|
||||
|
||||
ReturnStatement : token(Return) option(Expression) token(Semicolon)
|
||||
Expression : BoolOrExpression | ReturnExpression
|
||||
|
||||
Expression : token(Number) | token(String) | token(Unreachable) | FnCall
|
||||
BoolOrExpression : BoolAndExpression token(BoolOr) BoolAndExpression | BoolAndExpression
|
||||
|
||||
ReturnExpression : token(Return) option(Expression)
|
||||
|
||||
BoolAndExpression : ComparisonExpression token(BoolAnd) ComparisonExpression | ComparisonExpression
|
||||
|
||||
ComparisonExpression : BinaryOrExpression ComparisonOperator BinaryOrExpression | BinaryOrExpression
|
||||
|
||||
ComparisonOperator : token(BoolEq) | token(BoolNotEq) | token(BoolLessThan) | token(BoolGreaterThan) | token(BoolLessEqual) | token(BoolGreaterEqual)
|
||||
|
||||
BinaryOrExpression : BinaryXorExpression token(BinOr) BinaryXorExpression | BinaryXorExpression
|
||||
|
||||
BinaryXorExpression : BinaryAndExpression token(BinXor) BinaryAndExpression | BinaryAndExpression
|
||||
|
||||
BinaryAndExpression : BitShiftExpression token(BinAnd) BitShiftExpression | BitShiftExpression
|
||||
|
||||
BitShiftExpression : AdditionExpression BitShiftOperator AdditionExpression | AdditionExpression
|
||||
|
||||
BitShiftOperator : token(BitShiftLeft | token(BitShiftRight)
|
||||
|
||||
AdditionExpression : MultiplyExpression AdditionOperator MultiplyExpression | MultiplyExpression
|
||||
|
||||
AdditionOperator : token(Plus) | token(Minus)
|
||||
|
||||
MultiplyExpression : CastExpression MultiplyOperator CastExpression | CastExpression
|
||||
|
||||
MultiplyOperator : token(Star) | token(Slash) | token(Percent)
|
||||
|
||||
CastExpression : PrimaryExpression token(as) Type | PrimaryExpression
|
||||
|
||||
PrimaryExpression : token(Number) | token(String) | token(Unreachable) | FnCall | GroupedExpression | Block
|
||||
|
||||
GroupedExpression : token(LParen) Expression token(RParen)
|
||||
|
||||
FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen)
|
||||
|
||||
Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen)
|
||||
```
|
||||
|
||||
### Building
|
||||
### Binary Operator Precedence
|
||||
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
./run_tests
|
||||
as
|
||||
* / %
|
||||
+ -
|
||||
<< >>
|
||||
&
|
||||
^
|
||||
|
|
||||
== != < > <= >=
|
||||
&&
|
||||
||
|
||||
=
|
||||
```
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ if exists("b:current_syntax")
|
|||
finish
|
||||
endif
|
||||
|
||||
syn keyword zigKeyword fn return mut const extern unreachable export pub
|
||||
syn keyword zigKeyword fn return mut const extern unreachable export pub as
|
||||
syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 void
|
||||
|
||||
syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell
|
||||
|
|
|
|||
441
src/codegen.cpp
441
src/codegen.cpp
|
|
@ -77,6 +77,7 @@ struct CodeGen {
|
|||
ZigList<FnTableEntry *> fn_defs;
|
||||
Buf *out_name;
|
||||
OutType out_type;
|
||||
LLVMValueRef cur_fn;
|
||||
};
|
||||
|
||||
struct TypeNode {
|
||||
|
|
@ -301,12 +302,23 @@ static void find_declarations(CodeGen *g, AstNode *node) {
|
|||
// we handled directives in the parent function
|
||||
break;
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeStatementReturn:
|
||||
case NodeTypeReturnExpr:
|
||||
case NodeTypeRoot:
|
||||
case NodeTypeBlock:
|
||||
case NodeTypeExpression:
|
||||
case NodeTypeBoolOrExpr:
|
||||
case NodeTypeFnCall:
|
||||
case NodeTypeRootExportDecl:
|
||||
case NodeTypeBoolAndExpr:
|
||||
case NodeTypeComparisonExpr:
|
||||
case NodeTypeBinOrExpr:
|
||||
case NodeTypeBinXorExpr:
|
||||
case NodeTypeBinAndExpr:
|
||||
case NodeTypeBitShiftExpr:
|
||||
case NodeTypeAddExpr:
|
||||
case NodeTypeMultExpr:
|
||||
case NodeTypeCastExpr:
|
||||
case NodeTypePrimaryExpr:
|
||||
case NodeTypeGroupedExpr:
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
|
@ -341,7 +353,7 @@ static void check_fn_def_control_flow(CodeGen *g, AstNode *node) {
|
|||
bool prev_statement_return = false;
|
||||
for (int i = 0; i < body_node->data.block.statements.length; i += 1) {
|
||||
AstNode *statement_node = body_node->data.block.statements.at(i);
|
||||
if (statement_node->type == NodeTypeStatementReturn) {
|
||||
if (statement_node->type == NodeTypeReturnExpr) {
|
||||
if (type_id == TypeIdUnreachable) {
|
||||
add_node_error(g, statement_node,
|
||||
buf_sprintf("return statement in function with unreachable return type"));
|
||||
|
|
@ -464,23 +476,15 @@ static void analyze_node(CodeGen *g, AstNode *node) {
|
|||
analyze_node(g, child);
|
||||
}
|
||||
break;
|
||||
case NodeTypeStatementReturn:
|
||||
if (node->data.statement_return.expression) {
|
||||
analyze_node(g, node->data.statement_return.expression);
|
||||
case NodeTypeReturnExpr:
|
||||
if (node->data.return_expr.expr) {
|
||||
analyze_node(g, node->data.return_expr.expr);
|
||||
}
|
||||
break;
|
||||
case NodeTypeExpression:
|
||||
switch (node->data.expression.type) {
|
||||
case AstNodeExpressionTypeNumber:
|
||||
break;
|
||||
case AstNodeExpressionTypeString:
|
||||
break;
|
||||
case AstNodeExpressionTypeFnCall:
|
||||
analyze_node(g, node->data.expression.data.fn_call);
|
||||
break;
|
||||
case AstNodeExpressionTypeUnreachable:
|
||||
break;
|
||||
}
|
||||
case NodeTypeBoolOrExpr:
|
||||
analyze_node(g, node->data.bool_or_expr.op1);
|
||||
if (node->data.bool_or_expr.op2)
|
||||
analyze_node(g, node->data.bool_or_expr.op2);
|
||||
break;
|
||||
case NodeTypeFnCall:
|
||||
{
|
||||
|
|
@ -511,6 +515,54 @@ static void analyze_node(CodeGen *g, AstNode *node) {
|
|||
case NodeTypeDirective:
|
||||
// we looked at directives in the parent node
|
||||
break;
|
||||
case NodeTypeBoolAndExpr:
|
||||
zig_panic("TODO");
|
||||
break;
|
||||
case NodeTypeComparisonExpr:
|
||||
zig_panic("TODO");
|
||||
break;
|
||||
case NodeTypeBinOrExpr:
|
||||
zig_panic("TODO");
|
||||
break;
|
||||
case NodeTypeBinXorExpr:
|
||||
zig_panic("TODO");
|
||||
break;
|
||||
case NodeTypeBinAndExpr:
|
||||
zig_panic("TODO");
|
||||
break;
|
||||
case NodeTypeBitShiftExpr:
|
||||
zig_panic("TODO");
|
||||
break;
|
||||
case NodeTypeAddExpr:
|
||||
zig_panic("TODO");
|
||||
break;
|
||||
case NodeTypeMultExpr:
|
||||
zig_panic("TODO");
|
||||
break;
|
||||
case NodeTypeCastExpr:
|
||||
zig_panic("TODO");
|
||||
break;
|
||||
case NodeTypePrimaryExpr:
|
||||
switch (node->data.primary_expr.type) {
|
||||
case PrimaryExprTypeNumber:
|
||||
case PrimaryExprTypeString:
|
||||
case PrimaryExprTypeUnreachable:
|
||||
// nothing to do
|
||||
break;
|
||||
case PrimaryExprTypeFnCall:
|
||||
analyze_node(g, node->data.primary_expr.data.fn_call);
|
||||
break;
|
||||
case PrimaryExprTypeGroupedExpr:
|
||||
analyze_node(g, node->data.primary_expr.data.grouped_expr);
|
||||
break;
|
||||
case PrimaryExprTypeBlock:
|
||||
analyze_node(g, node->data.primary_expr.data.block);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NodeTypeGroupedExpr:
|
||||
zig_panic("TODO");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -649,34 +701,326 @@ static LLVMValueRef find_or_create_string(CodeGen *g, Buf *str) {
|
|||
return global_value;
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_expr(CodeGen *g, AstNode *expr_node) {
|
||||
assert(expr_node->type == NodeTypeExpression);
|
||||
switch (expr_node->data.expression.type) {
|
||||
case AstNodeExpressionTypeNumber:
|
||||
static LLVMValueRef gen_primary_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypePrimaryExpr);
|
||||
|
||||
AstNodePrimaryExpr *prim_expr = &node->data.primary_expr;
|
||||
|
||||
switch (node->data.primary_expr.type) {
|
||||
case PrimaryExprTypeNumber:
|
||||
{
|
||||
Buf *number_str = &expr_node->data.expression.data.number;
|
||||
Buf *number_str = &prim_expr->data.number;
|
||||
LLVMTypeRef number_type = LLVMInt32Type();
|
||||
LLVMValueRef number_val = LLVMConstIntOfStringAndSize(number_type,
|
||||
buf_ptr(number_str), buf_len(number_str), 10);
|
||||
return number_val;
|
||||
}
|
||||
case AstNodeExpressionTypeString:
|
||||
case PrimaryExprTypeString:
|
||||
{
|
||||
Buf *str = &expr_node->data.expression.data.string;
|
||||
Buf *str = &prim_expr->data.string;
|
||||
LLVMValueRef str_val = find_or_create_string(g, str);
|
||||
LLVMValueRef indices[] = {
|
||||
LLVMConstInt(LLVMInt32Type(), 0, false),
|
||||
LLVMConstInt(LLVMInt32Type(), 0, false)
|
||||
};
|
||||
LLVMValueRef ptr_val = LLVMBuildInBoundsGEP(g->builder, str_val,
|
||||
indices, 2, "");
|
||||
|
||||
LLVMValueRef ptr_val = LLVMBuildInBoundsGEP(g->builder, str_val, indices, 2, "");
|
||||
return ptr_val;
|
||||
}
|
||||
case AstNodeExpressionTypeFnCall:
|
||||
return gen_fn_call(g, expr_node->data.expression.data.fn_call);
|
||||
case AstNodeExpressionTypeUnreachable:
|
||||
case PrimaryExprTypeUnreachable:
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildUnreachable(g->builder);
|
||||
case PrimaryExprTypeFnCall:
|
||||
return gen_fn_call(g, prim_expr->data.fn_call);
|
||||
case PrimaryExprTypeGroupedExpr:
|
||||
return gen_expr(g, prim_expr->data.grouped_expr);
|
||||
case PrimaryExprTypeBlock:
|
||||
break;
|
||||
}
|
||||
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeCastExpr);
|
||||
|
||||
LLVMValueRef expr = gen_primary_expr(g, node->data.cast_expr.primary_expr);
|
||||
|
||||
if (!node->data.cast_expr.type)
|
||||
return expr;
|
||||
|
||||
zig_panic("TODO cast expression");
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_mult_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeMultExpr);
|
||||
|
||||
LLVMValueRef val1 = gen_cast_expr(g, node->data.mult_expr.op1);
|
||||
|
||||
if (!node->data.mult_expr.op2)
|
||||
return val1;
|
||||
|
||||
LLVMValueRef val2 = gen_cast_expr(g, node->data.mult_expr.op2);
|
||||
|
||||
switch (node->data.mult_expr.mult_op) {
|
||||
case MultOpMult:
|
||||
// TODO types so we know float vs int
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildMul(g->builder, val1, val2, "");
|
||||
case MultOpDiv:
|
||||
// TODO types so we know float vs int and signed vs unsigned
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildSDiv(g->builder, val1, val2, "");
|
||||
case MultOpMod:
|
||||
// TODO types so we know float vs int and signed vs unsigned
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildSRem(g->builder, val1, val2, "");
|
||||
case MultOpInvalid:
|
||||
zig_unreachable();
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_add_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeAddExpr);
|
||||
|
||||
LLVMValueRef val1 = gen_mult_expr(g, node->data.add_expr.op1);
|
||||
|
||||
if (!node->data.add_expr.op2)
|
||||
return val1;
|
||||
|
||||
LLVMValueRef val2 = gen_mult_expr(g, node->data.add_expr.op2);
|
||||
|
||||
switch (node->data.add_expr.add_op) {
|
||||
case AddOpAdd:
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildAdd(g->builder, val1, val2, "");
|
||||
case AddOpSub:
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildSub(g->builder, val1, val2, "");
|
||||
case AddOpInvalid:
|
||||
zig_unreachable();
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_bit_shift_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeBitShiftExpr);
|
||||
|
||||
LLVMValueRef val1 = gen_add_expr(g, node->data.bit_shift_expr.op1);
|
||||
|
||||
if (!node->data.bit_shift_expr.op2)
|
||||
return val1;
|
||||
|
||||
LLVMValueRef val2 = gen_add_expr(g, node->data.bit_shift_expr.op2);
|
||||
|
||||
switch (node->data.bit_shift_expr.bit_shift_op) {
|
||||
case BitShiftOpLeft:
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildShl(g->builder, val1, val2, "");
|
||||
case BitShiftOpRight:
|
||||
// TODO implement type system so that we know whether to do
|
||||
// logical or arithmetic shifting here.
|
||||
// signed -> arithmetic, unsigned -> logical
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildLShr(g->builder, val1, val2, "");
|
||||
case BitShiftOpInvalid:
|
||||
zig_unreachable();
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_bin_and_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeBinAndExpr);
|
||||
|
||||
LLVMValueRef val1 = gen_bit_shift_expr(g, node->data.bin_and_expr.op1);
|
||||
|
||||
if (!node->data.bin_and_expr.op2)
|
||||
return val1;
|
||||
|
||||
LLVMValueRef val2 = gen_bit_shift_expr(g, node->data.bin_and_expr.op2);
|
||||
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildAnd(g->builder, val1, val2, "");
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_bin_xor_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeBinXorExpr);
|
||||
|
||||
LLVMValueRef val1 = gen_bin_and_expr(g, node->data.bin_xor_expr.op1);
|
||||
|
||||
if (!node->data.bin_xor_expr.op2)
|
||||
return val1;
|
||||
|
||||
LLVMValueRef val2 = gen_bin_and_expr(g, node->data.bin_xor_expr.op2);
|
||||
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildXor(g->builder, val1, val2, "");
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_bin_or_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeBinOrExpr);
|
||||
|
||||
LLVMValueRef val1 = gen_bin_xor_expr(g, node->data.bin_or_expr.op1);
|
||||
|
||||
if (!node->data.bin_or_expr.op2)
|
||||
return val1;
|
||||
|
||||
LLVMValueRef val2 = gen_bin_xor_expr(g, node->data.bin_or_expr.op2);
|
||||
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildOr(g->builder, val1, val2, "");
|
||||
}
|
||||
|
||||
static LLVMIntPredicate cmp_op_to_int_predicate(CmpOp cmp_op, bool is_signed) {
|
||||
switch (cmp_op) {
|
||||
case CmpOpInvalid:
|
||||
zig_unreachable();
|
||||
case CmpOpEq:
|
||||
return LLVMIntEQ;
|
||||
case CmpOpNotEq:
|
||||
return LLVMIntNE;
|
||||
case CmpOpLessThan:
|
||||
return is_signed ? LLVMIntSLT : LLVMIntULT;
|
||||
case CmpOpGreaterThan:
|
||||
return is_signed ? LLVMIntSGT : LLVMIntUGT;
|
||||
case CmpOpLessOrEq:
|
||||
return is_signed ? LLVMIntSLE : LLVMIntULE;
|
||||
case CmpOpGreaterOrEq:
|
||||
return is_signed ? LLVMIntSGE : LLVMIntUGE;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_cmp_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeComparisonExpr);
|
||||
|
||||
LLVMValueRef val1 = gen_bin_or_expr(g, node->data.comparison_expr.op1);
|
||||
|
||||
if (!node->data.comparison_expr.op2)
|
||||
return val1;
|
||||
|
||||
LLVMValueRef val2 = gen_bin_or_expr(g, node->data.comparison_expr.op2);
|
||||
|
||||
// TODO implement type system so that we know whether to do signed or unsigned comparison here
|
||||
LLVMIntPredicate pred = cmp_op_to_int_predicate(node->data.comparison_expr.cmp_op, true);
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildICmp(g->builder, pred, val1, val2, "");
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeBoolAndExpr);
|
||||
|
||||
LLVMValueRef val1 = gen_cmp_expr(g, node->data.bool_and_expr.op1);
|
||||
|
||||
if (!node->data.bool_and_expr.op2)
|
||||
return val1;
|
||||
|
||||
// block for when val1 == true
|
||||
LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn, "BoolAndTrue");
|
||||
// block for when val1 == false (don't even evaluate the second part)
|
||||
LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn, "BoolAndFalse");
|
||||
|
||||
LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(val1));
|
||||
add_debug_source_node(g, node);
|
||||
LLVMValueRef val1_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, zero, "");
|
||||
LLVMBuildCondBr(g->builder, val1_i1, false_block, true_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, true_block);
|
||||
LLVMValueRef val2 = gen_cmp_expr(g, node->data.bool_and_expr.op2);
|
||||
add_debug_source_node(g, node);
|
||||
LLVMValueRef val2_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, false_block);
|
||||
add_debug_source_node(g, node);
|
||||
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
|
||||
LLVMValueRef one_i1 = LLVMConstAllOnes(LLVMInt1Type());
|
||||
LLVMValueRef incoming_values[2] = {one_i1, val2_i1};
|
||||
LLVMBasicBlockRef incoming_blocks[2] = {LLVMGetInsertBlock(g->builder), true_block};
|
||||
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
|
||||
|
||||
return phi;
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
|
||||
assert(expr_node->type == NodeTypeBoolOrExpr);
|
||||
|
||||
LLVMValueRef val1 = gen_bool_and_expr(g, expr_node->data.bool_or_expr.op1);
|
||||
|
||||
if (!expr_node->data.bool_or_expr.op2)
|
||||
return val1;
|
||||
|
||||
// block for when val1 == false
|
||||
LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn, "BoolOrFalse");
|
||||
// block for when val1 == true (don't even evaluate the second part)
|
||||
LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn, "BoolOrTrue");
|
||||
|
||||
LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(val1));
|
||||
add_debug_source_node(g, expr_node);
|
||||
LLVMValueRef val1_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, zero, "");
|
||||
LLVMBuildCondBr(g->builder, val1_i1, false_block, true_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, false_block);
|
||||
LLVMValueRef val2 = gen_bool_and_expr(g, expr_node->data.bool_or_expr.op2);
|
||||
add_debug_source_node(g, expr_node);
|
||||
LLVMValueRef val2_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, true_block);
|
||||
add_debug_source_node(g, expr_node);
|
||||
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
|
||||
LLVMValueRef one_i1 = LLVMConstAllOnes(LLVMInt1Type());
|
||||
LLVMValueRef incoming_values[2] = {one_i1, val2_i1};
|
||||
LLVMBasicBlockRef incoming_blocks[2] = {LLVMGetInsertBlock(g->builder), false_block};
|
||||
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
|
||||
|
||||
return phi;
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeReturnExpr);
|
||||
AstNode *param_node = node->data.return_expr.expr;
|
||||
if (param_node) {
|
||||
LLVMValueRef value = gen_expr(g, param_node);
|
||||
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildRet(g->builder, value);
|
||||
} else {
|
||||
add_debug_source_node(g, node);
|
||||
return LLVMBuildRetVoid(g->builder);
|
||||
}
|
||||
}
|
||||
/*
|
||||
Expression : BoolOrExpression | ReturnExpression
|
||||
*/
|
||||
static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
||||
switch (node->type) {
|
||||
case NodeTypeBoolOrExpr:
|
||||
return gen_bool_or_expr(g, node);
|
||||
case NodeTypeReturnExpr:
|
||||
return gen_return_expr(g, node);
|
||||
case NodeTypeRoot:
|
||||
case NodeTypeRootExportDecl:
|
||||
case NodeTypeFnProto:
|
||||
case NodeTypeFnDef:
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeType:
|
||||
case NodeTypeBlock:
|
||||
case NodeTypeFnCall:
|
||||
case NodeTypeExternBlock:
|
||||
case NodeTypeDirective:
|
||||
case NodeTypeBoolAndExpr:
|
||||
case NodeTypeComparisonExpr:
|
||||
case NodeTypeBinOrExpr:
|
||||
case NodeTypeBinXorExpr:
|
||||
case NodeTypeBinAndExpr:
|
||||
case NodeTypeBitShiftExpr:
|
||||
case NodeTypeAddExpr:
|
||||
case NodeTypeMultExpr:
|
||||
case NodeTypeCastExpr:
|
||||
case NodeTypePrimaryExpr:
|
||||
return gen_primary_expr(g, node);
|
||||
case NodeTypeGroupedExpr:
|
||||
zig_unreachable();
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
|
@ -692,39 +1036,7 @@ static void gen_block(CodeGen *g, AstNode *block_node, bool add_implicit_return)
|
|||
|
||||
for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
|
||||
AstNode *statement_node = block_node->data.block.statements.at(i);
|
||||
switch (statement_node->type) {
|
||||
case NodeTypeStatementReturn:
|
||||
{
|
||||
AstNode *expr_node = statement_node->data.statement_return.expression;
|
||||
if (expr_node) {
|
||||
LLVMValueRef value = gen_expr(g, expr_node);
|
||||
|
||||
add_debug_source_node(g, statement_node);
|
||||
LLVMBuildRet(g->builder, value);
|
||||
} else {
|
||||
add_debug_source_node(g, statement_node);
|
||||
LLVMBuildRetVoid(g->builder);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeTypeExpression:
|
||||
{
|
||||
gen_expr(g, statement_node);
|
||||
break;
|
||||
}
|
||||
case NodeTypeRoot:
|
||||
case NodeTypeFnProto:
|
||||
case NodeTypeFnDef:
|
||||
case NodeTypeFnDecl:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeType:
|
||||
case NodeTypeBlock:
|
||||
case NodeTypeFnCall:
|
||||
case NodeTypeExternBlock:
|
||||
case NodeTypeDirective:
|
||||
case NodeTypeRootExportDecl:
|
||||
zig_unreachable();
|
||||
}
|
||||
gen_expr(g, statement_node);
|
||||
}
|
||||
|
||||
if (add_implicit_return) {
|
||||
|
|
@ -810,6 +1122,7 @@ void code_gen(CodeGen *g) {
|
|||
FnTableEntry *fn_table_entry = g->fn_defs.at(i);
|
||||
AstNode *fn_def_node = fn_table_entry->fn_def_node;
|
||||
LLVMValueRef fn = fn_table_entry->fn_value;
|
||||
g->cur_fn = fn;
|
||||
|
||||
AstNode *proto_node = fn_table_entry->proto_node;
|
||||
assert(proto_node->type == NodeTypeFnProto);
|
||||
|
|
|
|||
718
src/parser.cpp
718
src/parser.cpp
|
|
@ -10,6 +10,47 @@
|
|||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static const char *mult_op_str(MultOp mult_op) {
|
||||
switch (mult_op) {
|
||||
case MultOpInvalid: return "(invalid)";
|
||||
case MultOpMult: return "*";
|
||||
case MultOpDiv: return "/";
|
||||
case MultOpMod: return "%";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static const char *add_op_str(AddOp add_op) {
|
||||
switch (add_op) {
|
||||
case AddOpInvalid: return "(invalid)";
|
||||
case AddOpAdd: return "+";
|
||||
case AddOpSub: return "-";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static const char *bit_shift_op_str(BitShiftOp bit_shift_op) {
|
||||
switch (bit_shift_op) {
|
||||
case BitShiftOpInvalid: return "(invalid)";
|
||||
case BitShiftOpLeft: return "<<";
|
||||
case BitShiftOpRight: return ">>";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static const char *cmp_op_str(CmpOp cmp_op) {
|
||||
switch (cmp_op) {
|
||||
case CmpOpInvalid: return "(invalid)";
|
||||
case CmpOpEq: return "=";
|
||||
case CmpOpNotEq: return "!=";
|
||||
case CmpOpLessThan: return "<";
|
||||
case CmpOpGreaterThan: return ">";
|
||||
case CmpOpLessOrEq: return "<=";
|
||||
case CmpOpGreaterOrEq: return ">=";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
__attribute__ ((format (printf, 2, 3)))
|
||||
__attribute__ ((noreturn))
|
||||
static void ast_error(Token *token, const char *format, ...) {
|
||||
|
|
@ -43,16 +84,38 @@ const char *node_type_str(NodeType node_type) {
|
|||
return "Type";
|
||||
case NodeTypeBlock:
|
||||
return "Block";
|
||||
case NodeTypeStatementReturn:
|
||||
return "StatementReturn";
|
||||
case NodeTypeExpression:
|
||||
return "Expression";
|
||||
case NodeTypeBoolOrExpr:
|
||||
return "BoolOrExpr";
|
||||
case NodeTypeFnCall:
|
||||
return "FnCall";
|
||||
case NodeTypeExternBlock:
|
||||
return "ExternBlock";
|
||||
case NodeTypeDirective:
|
||||
return "Directive";
|
||||
case NodeTypeReturnExpr:
|
||||
return "ReturnExpr";
|
||||
case NodeTypeBoolAndExpr:
|
||||
return "BoolAndExpr";
|
||||
case NodeTypeComparisonExpr:
|
||||
return "ComparisonExpr";
|
||||
case NodeTypeBinOrExpr:
|
||||
return "BinOrExpr";
|
||||
case NodeTypeBinXorExpr:
|
||||
return "BinXorExpr";
|
||||
case NodeTypeBinAndExpr:
|
||||
return "BinAndExpr";
|
||||
case NodeTypeBitShiftExpr:
|
||||
return "BitShiftExpr";
|
||||
case NodeTypeAddExpr:
|
||||
return "AddExpr";
|
||||
case NodeTypeMultExpr:
|
||||
return "MultExpr";
|
||||
case NodeTypeCastExpr:
|
||||
return "CastExpr";
|
||||
case NodeTypePrimaryExpr:
|
||||
return "PrimaryExpr";
|
||||
case NodeTypeGroupedExpr:
|
||||
return "GroupedExpr";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
|
@ -133,10 +196,10 @@ void ast_print(AstNode *node, int indent) {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case NodeTypeStatementReturn:
|
||||
fprintf(stderr, "ReturnStatement\n");
|
||||
if (node->data.statement_return.expression)
|
||||
ast_print(node->data.statement_return.expression, indent + 2);
|
||||
case NodeTypeReturnExpr:
|
||||
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||
if (node->data.return_expr.expr)
|
||||
ast_print(node->data.return_expr.expr, indent + 2);
|
||||
break;
|
||||
case NodeTypeExternBlock:
|
||||
{
|
||||
|
|
@ -151,22 +214,11 @@ void ast_print(AstNode *node, int indent) {
|
|||
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||
ast_print(node->data.fn_decl.fn_proto, indent + 2);
|
||||
break;
|
||||
case NodeTypeExpression:
|
||||
switch (node->data.expression.type) {
|
||||
case AstNodeExpressionTypeNumber:
|
||||
fprintf(stderr, "NumberLiteralExpression %s\n", buf_ptr(&node->data.expression.data.number));
|
||||
break;
|
||||
case AstNodeExpressionTypeString:
|
||||
fprintf(stderr, "StringLiteralExpression '%s'\n", buf_ptr(&node->data.expression.data.string));
|
||||
break;
|
||||
case AstNodeExpressionTypeFnCall:
|
||||
fprintf(stderr, "FnCallExpression\n");
|
||||
ast_print(node->data.expression.data.fn_call, indent + 2);
|
||||
break;
|
||||
case AstNodeExpressionTypeUnreachable:
|
||||
fprintf(stderr, "UnreachableExpression\n");
|
||||
break;
|
||||
}
|
||||
case NodeTypeBoolOrExpr:
|
||||
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||
ast_print(node->data.bool_or_expr.op1, indent + 2);
|
||||
if (node->data.bool_or_expr.op2)
|
||||
ast_print(node->data.bool_or_expr.op2, indent + 2);
|
||||
break;
|
||||
case NodeTypeFnCall:
|
||||
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.fn_call.name));
|
||||
|
|
@ -178,6 +230,95 @@ void ast_print(AstNode *node, int indent) {
|
|||
case NodeTypeDirective:
|
||||
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||
break;
|
||||
case NodeTypeBoolAndExpr:
|
||||
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||
ast_print(node->data.bool_and_expr.op1, indent + 2);
|
||||
if (node->data.bool_and_expr.op2)
|
||||
ast_print(node->data.bool_and_expr.op2, indent + 2);
|
||||
break;
|
||||
case NodeTypeComparisonExpr:
|
||||
fprintf(stderr, "%s %s\n", node_type_str(node->type),
|
||||
cmp_op_str(node->data.comparison_expr.cmp_op));
|
||||
ast_print(node->data.comparison_expr.op1, indent + 2);
|
||||
if (node->data.comparison_expr.op2)
|
||||
ast_print(node->data.comparison_expr.op2, indent + 2);
|
||||
break;
|
||||
case NodeTypeBinOrExpr:
|
||||
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||
ast_print(node->data.bin_or_expr.op1, indent + 2);
|
||||
if (node->data.bin_or_expr.op2)
|
||||
ast_print(node->data.bin_or_expr.op2, indent + 2);
|
||||
break;
|
||||
case NodeTypeBinXorExpr:
|
||||
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||
ast_print(node->data.bin_xor_expr.op1, indent + 2);
|
||||
if (node->data.bin_xor_expr.op2)
|
||||
ast_print(node->data.bin_xor_expr.op2, indent + 2);
|
||||
break;
|
||||
case NodeTypeBinAndExpr:
|
||||
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||
ast_print(node->data.bin_and_expr.op1, indent + 2);
|
||||
if (node->data.bin_and_expr.op2)
|
||||
ast_print(node->data.bin_and_expr.op2, indent + 2);
|
||||
break;
|
||||
case NodeTypeBitShiftExpr:
|
||||
fprintf(stderr, "%s %s\n", node_type_str(node->type),
|
||||
bit_shift_op_str(node->data.bit_shift_expr.bit_shift_op));
|
||||
ast_print(node->data.bit_shift_expr.op1, indent + 2);
|
||||
if (node->data.bit_shift_expr.op2)
|
||||
ast_print(node->data.bit_shift_expr.op2, indent + 2);
|
||||
break;
|
||||
case NodeTypeAddExpr:
|
||||
fprintf(stderr, "%s %s\n", node_type_str(node->type),
|
||||
add_op_str(node->data.add_expr.add_op));
|
||||
ast_print(node->data.add_expr.op1, indent + 2);
|
||||
if (node->data.add_expr.op2)
|
||||
ast_print(node->data.add_expr.op2, indent + 2);
|
||||
break;
|
||||
case NodeTypeMultExpr:
|
||||
fprintf(stderr, "%s %s\n", node_type_str(node->type),
|
||||
mult_op_str(node->data.mult_expr.mult_op));
|
||||
ast_print(node->data.mult_expr.op1, indent + 2);
|
||||
if (node->data.mult_expr.op2)
|
||||
ast_print(node->data.mult_expr.op2, indent + 2);
|
||||
break;
|
||||
case NodeTypeCastExpr:
|
||||
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||
ast_print(node->data.cast_expr.primary_expr, indent + 2);
|
||||
if (node->data.cast_expr.type)
|
||||
ast_print(node->data.cast_expr.type, indent + 2);
|
||||
break;
|
||||
case NodeTypePrimaryExpr:
|
||||
switch (node->data.primary_expr.type) {
|
||||
case PrimaryExprTypeNumber:
|
||||
fprintf(stderr, "PrimaryExpr Number %s\n",
|
||||
buf_ptr(&node->data.primary_expr.data.number));
|
||||
break;
|
||||
case PrimaryExprTypeString:
|
||||
fprintf(stderr, "PrimaryExpr String '%s'\n",
|
||||
buf_ptr(&node->data.primary_expr.data.string));
|
||||
break;
|
||||
case PrimaryExprTypeUnreachable:
|
||||
fprintf(stderr, "PrimaryExpr Unreachable\n");
|
||||
break;
|
||||
case PrimaryExprTypeFnCall:
|
||||
fprintf(stderr, "PrimaryExpr FnCall\n");
|
||||
ast_print(node->data.primary_expr.data.fn_call, indent + 2);
|
||||
break;
|
||||
case PrimaryExprTypeGroupedExpr:
|
||||
fprintf(stderr, "PrimaryExpr GroupedExpr\n");
|
||||
ast_print(node->data.primary_expr.data.grouped_expr, indent + 2);
|
||||
break;
|
||||
case PrimaryExprTypeBlock:
|
||||
fprintf(stderr, "PrimaryExpr Block\n");
|
||||
ast_print(node->data.primary_expr.data.block, indent + 2);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NodeTypeGroupedExpr:
|
||||
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||
ast_print(node->data.grouped_expr.expr, indent + 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -267,6 +408,7 @@ void ast_invalid_token_error(ParseContext *pc, Token *token) {
|
|||
}
|
||||
|
||||
static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory);
|
||||
static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory);
|
||||
|
||||
|
||||
static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {
|
||||
|
|
@ -444,109 +586,515 @@ static void ast_parse_fn_call_param_list(ParseContext *pc, int token_index, int
|
|||
zig_unreachable();
|
||||
}
|
||||
|
||||
/*
|
||||
GroupedExpression : token(LParen) Expression token(RParen)
|
||||
*/
|
||||
static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
Token *l_paren = &pc->tokens->at(*token_index);
|
||||
if (l_paren->id != TokenIdLParen) {
|
||||
if (mandatory) {
|
||||
ast_invalid_token_error(pc, l_paren);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeGroupedExpr, l_paren);
|
||||
|
||||
node->data.grouped_expr.expr = ast_parse_expression(pc, token_index, true);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen) ;
|
||||
*/
|
||||
static AstNode *ast_parse_fn_call(ParseContext *pc, int token_index, int *new_token_index) {
|
||||
Token *fn_name = &pc->tokens->at(token_index);
|
||||
token_index += 1;
|
||||
ast_expect_token(pc, fn_name, TokenIdSymbol);
|
||||
static AstNode *ast_parse_fn_call(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
Token *fn_name = &pc->tokens->at(*token_index);
|
||||
if (fn_name->id != TokenIdSymbol) {
|
||||
if (mandatory) {
|
||||
ast_invalid_token_error(pc, fn_name);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeFnCall, fn_name);
|
||||
|
||||
|
||||
ast_buf_from_token(pc, fn_name, &node->data.fn_call.name);
|
||||
|
||||
ast_parse_fn_call_param_list(pc, token_index, &token_index, &node->data.fn_call.params);
|
||||
ast_parse_fn_call_param_list(pc, *token_index, token_index, &node->data.fn_call.params);
|
||||
|
||||
*new_token_index = token_index;
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
Expression : token(Number) | token(String) | token(Unreachable) | FnCall
|
||||
PrimaryExpression : token(Number) | token(String) | token(Unreachable) | FnCall | GroupedExpression | Block
|
||||
*/
|
||||
static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
AstNode *node = ast_create_node(NodeTypeExpression, token);
|
||||
if (token->id == TokenIdKeywordUnreachable) {
|
||||
node->data.expression.type = AstNodeExpressionTypeUnreachable;
|
||||
*token_index += 1;
|
||||
} else if (token->id == TokenIdSymbol) {
|
||||
node->data.expression.type = AstNodeExpressionTypeFnCall;
|
||||
node->data.expression.data.fn_call = ast_parse_fn_call(pc, *token_index, token_index);
|
||||
} else if (token->id == TokenIdNumberLiteral) {
|
||||
node->data.expression.type = AstNodeExpressionTypeNumber;
|
||||
ast_buf_from_token(pc, token, &node->data.expression.data.number);
|
||||
|
||||
if (token->id == TokenIdNumberLiteral) {
|
||||
AstNode *node = ast_create_node(NodeTypePrimaryExpr, token);
|
||||
node->data.primary_expr.type = PrimaryExprTypeNumber;
|
||||
ast_buf_from_token(pc, token, &node->data.primary_expr.data.number);
|
||||
*token_index += 1;
|
||||
return node;
|
||||
} else if (token->id == TokenIdStringLiteral) {
|
||||
node->data.expression.type = AstNodeExpressionTypeString;
|
||||
parse_string_literal(pc, token, &node->data.expression.data.string);
|
||||
AstNode *node = ast_create_node(NodeTypePrimaryExpr, token);
|
||||
node->data.primary_expr.type = PrimaryExprTypeString;
|
||||
parse_string_literal(pc, token, &node->data.primary_expr.data.string);
|
||||
*token_index += 1;
|
||||
return node;
|
||||
} else if (token->id == TokenIdKeywordUnreachable) {
|
||||
AstNode *node = ast_create_node(NodeTypePrimaryExpr, token);
|
||||
node->data.primary_expr.type = PrimaryExprTypeUnreachable;
|
||||
*token_index += 1;
|
||||
return node;
|
||||
}
|
||||
|
||||
AstNode *block_node = ast_parse_block(pc, token_index, false);
|
||||
if (block_node) {
|
||||
AstNode *node = ast_create_node(NodeTypePrimaryExpr, token);
|
||||
node->data.primary_expr.type = PrimaryExprTypeBlock;
|
||||
node->data.primary_expr.data.block = block_node;
|
||||
return node;
|
||||
}
|
||||
|
||||
AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);
|
||||
if (grouped_expr_node) {
|
||||
AstNode *node = ast_create_node(NodeTypePrimaryExpr, token);
|
||||
node->data.primary_expr.type = PrimaryExprTypeGroupedExpr;
|
||||
node->data.primary_expr.data.grouped_expr = grouped_expr_node;
|
||||
return node;
|
||||
}
|
||||
|
||||
AstNode *fn_call_node = ast_parse_fn_call(pc, token_index, false);
|
||||
if (fn_call_node) {
|
||||
AstNode *node = ast_create_node(NodeTypePrimaryExpr, token);
|
||||
node->data.primary_expr.type = PrimaryExprTypeFnCall;
|
||||
node->data.primary_expr.data.fn_call = fn_call_node;
|
||||
return node;
|
||||
}
|
||||
|
||||
if (!mandatory)
|
||||
return nullptr;
|
||||
|
||||
ast_invalid_token_error(pc, token);
|
||||
}
|
||||
|
||||
/*
|
||||
CastExpression : PrimaryExpression token(As) Type | PrimaryExpression
|
||||
*/
|
||||
static AstNode *ast_parse_cast_expression(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
AstNode *primary_expr = ast_parse_primary_expr(pc, token_index, mandatory);
|
||||
if (!primary_expr)
|
||||
return nullptr;
|
||||
|
||||
Token *as_kw = &pc->tokens->at(*token_index);
|
||||
if (as_kw->id != TokenIdKeywordAs)
|
||||
return primary_expr;
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeCastExpr, as_kw);
|
||||
node->data.cast_expr.primary_expr = primary_expr;
|
||||
|
||||
node->data.cast_expr.type = ast_parse_type(pc, *token_index, token_index);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static MultOp tok_to_mult_op(Token *token) {
|
||||
switch (token->id) {
|
||||
case TokenIdStar: return MultOpMult;
|
||||
case TokenIdSlash: return MultOpDiv;
|
||||
case TokenIdPercent: return MultOpMod;
|
||||
default: return MultOpInvalid;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MultiplyOperator : token(Star) | token(Slash) | token(Percent)
|
||||
*/
|
||||
static MultOp ast_parse_mult_op(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
MultOp result = tok_to_mult_op(token);
|
||||
if (result == MultOpInvalid) {
|
||||
if (mandatory) {
|
||||
ast_invalid_token_error(pc, token);
|
||||
} else {
|
||||
return MultOpInvalid;
|
||||
}
|
||||
}
|
||||
*token_index += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
MultiplyExpression : CastExpression MultiplyOperator CastExpression | CastExpression
|
||||
*/
|
||||
static AstNode *ast_parse_mult_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
AstNode *operand_1 = ast_parse_cast_expression(pc, token_index, mandatory);
|
||||
if (!operand_1)
|
||||
return nullptr;
|
||||
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
MultOp mult_op = ast_parse_mult_op(pc, token_index, false);
|
||||
if (mult_op == MultOpInvalid)
|
||||
return operand_1;
|
||||
|
||||
AstNode *operand_2 = ast_parse_cast_expression(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeMultExpr, token);
|
||||
node->data.mult_expr.op1 = operand_1;
|
||||
node->data.mult_expr.mult_op = mult_op;
|
||||
node->data.mult_expr.op2 = operand_2;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static AddOp tok_to_add_op(Token *token) {
|
||||
switch (token->id) {
|
||||
case TokenIdPlus: return AddOpAdd;
|
||||
case TokenIdDash: return AddOpSub;
|
||||
default: return AddOpInvalid;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
AdditionOperator : token(Plus) | token(Minus)
|
||||
*/
|
||||
static AddOp ast_parse_add_op(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
AddOp result = tok_to_add_op(token);
|
||||
if (result == AddOpInvalid) {
|
||||
if (mandatory) {
|
||||
ast_invalid_token_error(pc, token);
|
||||
} else {
|
||||
return AddOpInvalid;
|
||||
}
|
||||
}
|
||||
*token_index += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
AdditionExpression : MultiplyExpression AdditionOperator MultiplyExpression | MultiplyExpression
|
||||
*/
|
||||
static AstNode *ast_parse_add_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
AstNode *operand_1 = ast_parse_mult_expr(pc, token_index, mandatory);
|
||||
if (!operand_1)
|
||||
return nullptr;
|
||||
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
AddOp add_op = ast_parse_add_op(pc, token_index, false);
|
||||
if (add_op == AddOpInvalid)
|
||||
return operand_1;
|
||||
|
||||
AstNode *operand_2 = ast_parse_mult_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeAddExpr, token);
|
||||
node->data.add_expr.op1 = operand_1;
|
||||
node->data.add_expr.add_op = add_op;
|
||||
node->data.add_expr.op2 = operand_2;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static BitShiftOp tok_to_bit_shift_op(Token *token) {
|
||||
switch (token->id) {
|
||||
case TokenIdBitShiftLeft: return BitShiftOpLeft;
|
||||
case TokenIdBitShiftRight: return BitShiftOpRight;
|
||||
default: return BitShiftOpInvalid;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
BitShiftOperator : token(BitShiftLeft | token(BitShiftRight)
|
||||
*/
|
||||
static BitShiftOp ast_parse_bit_shift_op(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
BitShiftOp result = tok_to_bit_shift_op(token);
|
||||
if (result == BitShiftOpInvalid) {
|
||||
if (mandatory) {
|
||||
ast_invalid_token_error(pc, token);
|
||||
} else {
|
||||
return BitShiftOpInvalid;
|
||||
}
|
||||
}
|
||||
*token_index += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
BitShiftExpression : AdditionExpression BitShiftOperator AdditionExpression | AdditionExpression
|
||||
*/
|
||||
static AstNode *ast_parse_bit_shift_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
AstNode *operand_1 = ast_parse_add_expr(pc, token_index, mandatory);
|
||||
if (!operand_1)
|
||||
return nullptr;
|
||||
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
BitShiftOp bit_shift_op = ast_parse_bit_shift_op(pc, token_index, false);
|
||||
if (bit_shift_op == BitShiftOpInvalid)
|
||||
return operand_1;
|
||||
|
||||
AstNode *operand_2 = ast_parse_add_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBitShiftExpr, token);
|
||||
node->data.bit_shift_expr.op1 = operand_1;
|
||||
node->data.bit_shift_expr.bit_shift_op = bit_shift_op;
|
||||
node->data.bit_shift_expr.op2 = operand_2;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
BinaryAndExpression : BitShiftExpression token(BinAnd) BitShiftExpression | BitShiftExpression
|
||||
*/
|
||||
static AstNode *ast_parse_bin_and_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
AstNode *operand_1 = ast_parse_bit_shift_expr(pc, token_index, mandatory);
|
||||
if (!operand_1)
|
||||
return nullptr;
|
||||
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
if (token->id != TokenIdBinAnd)
|
||||
return operand_1;
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *operand_2 = ast_parse_bit_shift_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBinAndExpr, token);
|
||||
node->data.bin_and_expr.op1 = operand_1;
|
||||
node->data.bin_and_expr.op2 = operand_2;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
BinaryXorExpression : BinaryAndExpression token(BinXor) BinaryAndExpression | BinaryAndExpression
|
||||
*/
|
||||
static AstNode *ast_parse_bin_xor_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
AstNode *operand_1 = ast_parse_bin_and_expr(pc, token_index, mandatory);
|
||||
if (!operand_1)
|
||||
return nullptr;
|
||||
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
if (token->id != TokenIdBinXor)
|
||||
return operand_1;
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *operand_2 = ast_parse_bin_and_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBinXorExpr, token);
|
||||
node->data.bin_xor_expr.op1 = operand_1;
|
||||
node->data.bin_xor_expr.op2 = operand_2;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
BinaryOrExpression : BinaryXorExpression token(BinOr) BinaryXorExpression | BinaryXorExpression
|
||||
*/
|
||||
static AstNode *ast_parse_bin_or_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
AstNode *operand_1 = ast_parse_bin_xor_expr(pc, token_index, mandatory);
|
||||
if (!operand_1)
|
||||
return nullptr;
|
||||
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
if (token->id != TokenIdBinOr)
|
||||
return operand_1;
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *operand_2 = ast_parse_bin_xor_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBinOrExpr, token);
|
||||
node->data.bin_or_expr.op1 = operand_1;
|
||||
node->data.bin_or_expr.op2 = operand_2;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static CmpOp tok_to_cmp_op(Token *token) {
|
||||
switch (token->id) {
|
||||
case TokenIdCmpEq: return CmpOpEq;
|
||||
case TokenIdCmpNotEq: return CmpOpNotEq;
|
||||
case TokenIdCmpLessThan: return CmpOpLessThan;
|
||||
case TokenIdCmpGreaterThan: return CmpOpGreaterThan;
|
||||
case TokenIdCmpLessOrEq: return CmpOpLessOrEq;
|
||||
case TokenIdCmpGreaterOrEq: return CmpOpGreaterOrEq;
|
||||
default: return CmpOpInvalid;
|
||||
}
|
||||
}
|
||||
|
||||
static CmpOp ast_parse_comparison_operator(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
CmpOp result = tok_to_cmp_op(token);
|
||||
if (result == CmpOpInvalid) {
|
||||
if (mandatory) {
|
||||
ast_invalid_token_error(pc, token);
|
||||
} else {
|
||||
return CmpOpInvalid;
|
||||
}
|
||||
}
|
||||
*token_index += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
ComparisonExpression : BinaryOrExpression ComparisonOperator BinaryOrExpression | BinaryOrExpression
|
||||
*/
|
||||
static AstNode *ast_parse_comparison_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
AstNode *operand_1 = ast_parse_bin_or_expr(pc, token_index, mandatory);
|
||||
if (!operand_1)
|
||||
return nullptr;
|
||||
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
CmpOp cmp_op = ast_parse_comparison_operator(pc, token_index, false);
|
||||
if (cmp_op == CmpOpInvalid)
|
||||
return operand_1;
|
||||
|
||||
AstNode *operand_2 = ast_parse_bin_or_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeComparisonExpr, token);
|
||||
node->data.comparison_expr.op1 = operand_1;
|
||||
node->data.comparison_expr.cmp_op = cmp_op;
|
||||
node->data.comparison_expr.op2 = operand_2;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
BoolAndExpression : ComparisonExpression token(BoolAnd) ComparisonExpression | ComparisonExpression
|
||||
*/
|
||||
static AstNode *ast_parse_bool_and_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
AstNode *operand_1 = ast_parse_comparison_expr(pc, token_index, mandatory);
|
||||
if (!operand_1)
|
||||
return nullptr;
|
||||
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
if (token->id != TokenIdBoolAnd)
|
||||
return operand_1;
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *operand_2 = ast_parse_comparison_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBoolAndExpr, token);
|
||||
node->data.bool_and_expr.op1 = operand_1;
|
||||
node->data.bool_and_expr.op2 = operand_2;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
ReturnExpression : token(Return) option(Expression)
|
||||
*/
|
||||
static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
Token *return_tok = &pc->tokens->at(*token_index);
|
||||
if (return_tok->id == TokenIdKeywordReturn) {
|
||||
*token_index += 1;
|
||||
AstNode *node = ast_create_node(NodeTypeReturnExpr, return_tok);
|
||||
node->data.return_expr.expr = ast_parse_expression(pc, token_index, false);
|
||||
return node;
|
||||
} else if (mandatory) {
|
||||
ast_invalid_token_error(pc, token);
|
||||
ast_invalid_token_error(pc, return_tok);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
BoolOrExpression : BoolAndExpression token(BoolOr) BoolAndExpression | BoolAndExpression
|
||||
*/
|
||||
static AstNode *ast_parse_bool_or_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
AstNode *operand_1 = ast_parse_bool_and_expr(pc, token_index, mandatory);
|
||||
if (!operand_1)
|
||||
return nullptr;
|
||||
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
if (token->id != TokenIdBoolOr)
|
||||
return operand_1;
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *operand_2 = ast_parse_bool_and_expr(pc, token_index, true);
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBoolOrExpr, token);
|
||||
node->data.bool_or_expr.op1 = operand_1;
|
||||
node->data.bool_or_expr.op2 = operand_2;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
Statement : ExpressionStatement | ReturnStatement ;
|
||||
|
||||
ExpressionStatement : Expression token(Semicolon) ;
|
||||
|
||||
ReturnStatement : token(Return) option(Expression) token(Semicolon) ;
|
||||
Expression : BoolOrExpression | ReturnExpression
|
||||
*/
|
||||
static AstNode *ast_parse_statement(ParseContext *pc, int token_index, int *new_token_index) {
|
||||
Token *token = &pc->tokens->at(token_index);
|
||||
if (token->id == TokenIdKeywordReturn) {
|
||||
AstNode *node = ast_create_node(NodeTypeStatementReturn, token);
|
||||
token_index += 1;
|
||||
node->data.statement_return.expression = ast_parse_expression(pc, &token_index, false);
|
||||
static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
|
||||
Token *semicolon = &pc->tokens->at(token_index);
|
||||
token_index += 1;
|
||||
ast_expect_token(pc, semicolon, TokenIdSemicolon);
|
||||
*new_token_index = token_index;
|
||||
return node;
|
||||
} else if (token->id == TokenIdSymbol ||
|
||||
token->id == TokenIdStringLiteral ||
|
||||
token->id == TokenIdKeywordUnreachable ||
|
||||
token->id == TokenIdNumberLiteral)
|
||||
{
|
||||
AstNode *node = ast_parse_expression(pc, &token_index, true);
|
||||
AstNode *return_expr = ast_parse_return_expr(pc, token_index, false);
|
||||
if (return_expr)
|
||||
return return_expr;
|
||||
|
||||
Token *semicolon = &pc->tokens->at(token_index);
|
||||
token_index += 1;
|
||||
ast_expect_token(pc, semicolon, TokenIdSemicolon);
|
||||
*new_token_index = token_index;
|
||||
return node;
|
||||
} else {
|
||||
ast_invalid_token_error(pc, token);
|
||||
}
|
||||
AstNode *bool_or_expr = ast_parse_bool_or_expr(pc, token_index, false);
|
||||
if (bool_or_expr)
|
||||
return bool_or_expr;
|
||||
|
||||
if (!mandatory)
|
||||
return nullptr;
|
||||
|
||||
ast_invalid_token_error(pc, token);
|
||||
}
|
||||
|
||||
/*
|
||||
ExpressionStatement : Expression token(Semicolon)
|
||||
*/
|
||||
static AstNode *ast_parse_expression_statement(ParseContext *pc, int *token_index) {
|
||||
AstNode *expr_node = ast_parse_expression(pc, token_index, true);
|
||||
|
||||
Token *semicolon = &pc->tokens->at(*token_index);
|
||||
*token_index += 1;
|
||||
ast_expect_token(pc, semicolon, TokenIdSemicolon);
|
||||
|
||||
return expr_node;
|
||||
}
|
||||
|
||||
/*
|
||||
Statement : ExpressionStatement
|
||||
*/
|
||||
static AstNode *ast_parse_statement(ParseContext *pc, int *token_index) {
|
||||
return ast_parse_expression_statement(pc, token_index);
|
||||
}
|
||||
|
||||
/*
|
||||
Block : token(LBrace) many(Statement) token(RBrace);
|
||||
*/
|
||||
static AstNode *ast_parse_block(ParseContext *pc, int token_index, int *new_token_index) {
|
||||
Token *l_brace = &pc->tokens->at(token_index);
|
||||
token_index += 1;
|
||||
ast_expect_token(pc, l_brace, TokenIdLBrace);
|
||||
static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) {
|
||||
Token *l_brace = &pc->tokens->at(*token_index);
|
||||
|
||||
if (l_brace->id != TokenIdLBrace) {
|
||||
if (mandatory) {
|
||||
ast_invalid_token_error(pc, l_brace);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *node = ast_create_node(NodeTypeBlock, l_brace);
|
||||
|
||||
|
||||
for (;;) {
|
||||
Token *token = &pc->tokens->at(token_index);
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
if (token->id == TokenIdRBrace) {
|
||||
token_index += 1;
|
||||
*new_token_index = token_index;
|
||||
*token_index += 1;
|
||||
return node;
|
||||
} else {
|
||||
AstNode *statement_node = ast_parse_statement(pc, token_index, &token_index);
|
||||
AstNode *statement_node = ast_parse_statement(pc, token_index);
|
||||
node->data.block.statements.append(statement_node);
|
||||
}
|
||||
}
|
||||
|
|
@ -620,7 +1168,7 @@ static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandat
|
|||
AstNode *node = ast_create_node_with_node(NodeTypeFnDef, fn_proto);
|
||||
|
||||
node->data.fn_def.fn_proto = fn_proto;
|
||||
node->data.fn_def.body = ast_parse_block(pc, *token_index, token_index);
|
||||
node->data.fn_def.body = ast_parse_block(pc, token_index, true);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
|
|
|||
164
src/parser.hpp
164
src/parser.hpp
|
|
@ -24,11 +24,22 @@ enum NodeType {
|
|||
NodeTypeParamDecl,
|
||||
NodeTypeType,
|
||||
NodeTypeBlock,
|
||||
NodeTypeExpression,
|
||||
NodeTypeFnCall,
|
||||
NodeTypeExternBlock,
|
||||
NodeTypeDirective,
|
||||
NodeTypeStatementReturn,
|
||||
NodeTypeReturnExpr,
|
||||
NodeTypeBoolOrExpr,
|
||||
NodeTypeBoolAndExpr,
|
||||
NodeTypeComparisonExpr,
|
||||
NodeTypeBinOrExpr,
|
||||
NodeTypeBinXorExpr,
|
||||
NodeTypeBinAndExpr,
|
||||
NodeTypeBitShiftExpr,
|
||||
NodeTypeAddExpr,
|
||||
NodeTypeMultExpr,
|
||||
NodeTypeCastExpr,
|
||||
NodeTypePrimaryExpr,
|
||||
NodeTypeGroupedExpr,
|
||||
};
|
||||
|
||||
struct AstNodeRoot {
|
||||
|
|
@ -80,24 +91,15 @@ struct AstNodeBlock {
|
|||
ZigList<AstNode *> statements;
|
||||
};
|
||||
|
||||
struct AstNodeStatementReturn {
|
||||
AstNode *expression;
|
||||
struct AstNodeReturnExpr {
|
||||
// might be null in case of return void;
|
||||
AstNode *expr;
|
||||
};
|
||||
|
||||
enum AstNodeExpressionType {
|
||||
AstNodeExpressionTypeNumber,
|
||||
AstNodeExpressionTypeString,
|
||||
AstNodeExpressionTypeFnCall,
|
||||
AstNodeExpressionTypeUnreachable,
|
||||
};
|
||||
|
||||
struct AstNodeExpression {
|
||||
AstNodeExpressionType type;
|
||||
union {
|
||||
Buf number;
|
||||
Buf string;
|
||||
AstNode *fn_call;
|
||||
} data;
|
||||
struct AstNodeBoolOrExpr {
|
||||
AstNode *op1;
|
||||
// if op2 is non-null, do boolean or, otherwise nothing
|
||||
AstNode *op2;
|
||||
};
|
||||
|
||||
struct AstNodeFnCall {
|
||||
|
|
@ -120,6 +122,117 @@ struct AstNodeRootExportDecl {
|
|||
Buf name;
|
||||
};
|
||||
|
||||
struct AstNodeBoolAndExpr {
|
||||
AstNode *op1;
|
||||
// if op2 is non-null, do boolean and, otherwise nothing
|
||||
AstNode *op2;
|
||||
};
|
||||
|
||||
enum CmpOp {
|
||||
CmpOpInvalid,
|
||||
CmpOpEq,
|
||||
CmpOpNotEq,
|
||||
CmpOpLessThan,
|
||||
CmpOpGreaterThan,
|
||||
CmpOpLessOrEq,
|
||||
CmpOpGreaterOrEq,
|
||||
};
|
||||
|
||||
struct AstNodeComparisonExpr {
|
||||
AstNode *op1;
|
||||
CmpOp cmp_op;
|
||||
// if op2 is non-null, do cmp_op, otherwise nothing
|
||||
AstNode *op2;
|
||||
};
|
||||
|
||||
struct AstNodeBinOrExpr {
|
||||
AstNode *op1;
|
||||
// if op2 is non-null, do binary or, otherwise nothing
|
||||
AstNode *op2;
|
||||
};
|
||||
|
||||
struct AstNodeBinXorExpr {
|
||||
AstNode *op1;
|
||||
// if op2 is non-null, do binary xor, otherwise nothing
|
||||
AstNode *op2;
|
||||
};
|
||||
|
||||
struct AstNodeBinAndExpr {
|
||||
AstNode *op1;
|
||||
// if op2 is non-null, do binary and, otherwise nothing
|
||||
AstNode *op2;
|
||||
};
|
||||
|
||||
enum BitShiftOp {
|
||||
BitShiftOpInvalid,
|
||||
BitShiftOpLeft,
|
||||
BitShiftOpRight,
|
||||
};
|
||||
|
||||
struct AstNodeBitShiftExpr {
|
||||
AstNode *op1;
|
||||
BitShiftOp bit_shift_op;
|
||||
// if op2 is non-null, do bit_shift_op, otherwise nothing
|
||||
AstNode *op2;
|
||||
};
|
||||
|
||||
enum AddOp {
|
||||
AddOpInvalid,
|
||||
AddOpAdd,
|
||||
AddOpSub,
|
||||
};
|
||||
|
||||
struct AstNodeAddExpr {
|
||||
AstNode *op1;
|
||||
AddOp add_op;
|
||||
// if op2 is non-null, do add_op, otherwise nothing
|
||||
AstNode *op2;
|
||||
};
|
||||
|
||||
enum MultOp {
|
||||
MultOpInvalid,
|
||||
MultOpMult,
|
||||
MultOpDiv,
|
||||
MultOpMod,
|
||||
};
|
||||
|
||||
struct AstNodeMultExpr {
|
||||
AstNode *op1;
|
||||
MultOp mult_op;
|
||||
// if op2 is non-null, do mult_op, otherwise nothing
|
||||
AstNode *op2;
|
||||
};
|
||||
|
||||
struct AstNodeCastExpr {
|
||||
AstNode *primary_expr;
|
||||
// if type is non-null, do cast, otherwise nothing
|
||||
AstNode *type;
|
||||
};
|
||||
|
||||
enum PrimaryExprType {
|
||||
PrimaryExprTypeNumber,
|
||||
PrimaryExprTypeString,
|
||||
PrimaryExprTypeUnreachable,
|
||||
PrimaryExprTypeFnCall,
|
||||
PrimaryExprTypeGroupedExpr,
|
||||
PrimaryExprTypeBlock,
|
||||
};
|
||||
|
||||
struct AstNodePrimaryExpr {
|
||||
PrimaryExprType type;
|
||||
union {
|
||||
Buf number;
|
||||
Buf string;
|
||||
AstNode *fn_call;
|
||||
AstNode *grouped_expr;
|
||||
AstNode *block;
|
||||
} data;
|
||||
};
|
||||
|
||||
struct AstNodeGroupedExpr {
|
||||
AstNode *expr;
|
||||
};
|
||||
|
||||
struct AstNode {
|
||||
enum NodeType type;
|
||||
AstNode *parent;
|
||||
|
|
@ -135,11 +248,22 @@ struct AstNode {
|
|||
AstNodeType type;
|
||||
AstNodeParamDecl param_decl;
|
||||
AstNodeBlock block;
|
||||
AstNodeStatementReturn statement_return;
|
||||
AstNodeExpression expression;
|
||||
AstNodeReturnExpr return_expr;
|
||||
AstNodeBoolOrExpr bool_or_expr;
|
||||
AstNodeFnCall fn_call;
|
||||
AstNodeExternBlock extern_block;
|
||||
AstNodeDirective directive;
|
||||
AstNodeBoolAndExpr bool_and_expr;
|
||||
AstNodeComparisonExpr comparison_expr;
|
||||
AstNodeBinOrExpr bin_or_expr;
|
||||
AstNodeBinXorExpr bin_xor_expr;
|
||||
AstNodeBinAndExpr bin_and_expr;
|
||||
AstNodeBitShiftExpr bit_shift_expr;
|
||||
AstNodeAddExpr add_expr;
|
||||
AstNodeMultExpr mult_expr;
|
||||
AstNodeCastExpr cast_expr;
|
||||
AstNodePrimaryExpr primary_expr;
|
||||
AstNodeGroupedExpr grouped_expr;
|
||||
} data;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -98,6 +98,12 @@ enum TokenizeState {
|
|||
TokenizeStateMultiLineComment,
|
||||
TokenizeStateMultiLineCommentSlash,
|
||||
TokenizeStateMultiLineCommentStar,
|
||||
TokenizeStatePipe,
|
||||
TokenizeStateAmpersand,
|
||||
TokenizeStateEq,
|
||||
TokenizeStateBang,
|
||||
TokenizeStateLessThan,
|
||||
TokenizeStateGreaterThan,
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -144,6 +150,11 @@ static void begin_token(Tokenize *t, TokenId id) {
|
|||
t->cur_tok = token;
|
||||
}
|
||||
|
||||
static void cancel_token(Tokenize *t) {
|
||||
t->tokens->pop();
|
||||
t->cur_tok = nullptr;
|
||||
}
|
||||
|
||||
static void end_token(Tokenize *t) {
|
||||
assert(t->cur_tok);
|
||||
t->cur_tok->end_pos = t->pos + 1;
|
||||
|
|
@ -167,6 +178,8 @@ static void end_token(Tokenize *t) {
|
|||
t->cur_tok->id = TokenIdKeywordPub;
|
||||
} else if (mem_eql_str(token_mem, token_len, "export")) {
|
||||
t->cur_tok->id = TokenIdKeywordExport;
|
||||
} else if (mem_eql_str(token_mem, token_len, "as")) {
|
||||
t->cur_tok->id = TokenIdKeywordAs;
|
||||
}
|
||||
|
||||
t->cur_tok = nullptr;
|
||||
|
|
@ -212,6 +225,10 @@ ZigList<Token> *tokenize(Buf *buf) {
|
|||
begin_token(&t, TokenIdStar);
|
||||
end_token(&t);
|
||||
break;
|
||||
case '%':
|
||||
begin_token(&t, TokenIdPercent);
|
||||
end_token(&t);
|
||||
break;
|
||||
case '{':
|
||||
begin_token(&t, TokenIdLBrace);
|
||||
end_token(&t);
|
||||
|
|
@ -240,24 +257,149 @@ ZigList<Token> *tokenize(Buf *buf) {
|
|||
begin_token(&t, TokenIdNumberSign);
|
||||
end_token(&t);
|
||||
break;
|
||||
case '^':
|
||||
begin_token(&t, TokenIdBinXor);
|
||||
end_token(&t);
|
||||
break;
|
||||
case '/':
|
||||
begin_token(&t, TokenIdSlash);
|
||||
t.state = TokenizeStateSawSlash;
|
||||
break;
|
||||
case '|':
|
||||
begin_token(&t, TokenIdBinOr);
|
||||
t.state = TokenizeStatePipe;
|
||||
break;
|
||||
case '&':
|
||||
begin_token(&t, TokenIdBinAnd);
|
||||
t.state = TokenizeStateAmpersand;
|
||||
break;
|
||||
case '=':
|
||||
begin_token(&t, TokenIdEq);
|
||||
t.state = TokenizeStateEq;
|
||||
break;
|
||||
case '!':
|
||||
begin_token(&t, TokenIdNot);
|
||||
t.state = TokenizeStateBang;
|
||||
break;
|
||||
case '<':
|
||||
begin_token(&t, TokenIdCmpLessThan);
|
||||
t.state = TokenizeStateLessThan;
|
||||
break;
|
||||
case '>':
|
||||
begin_token(&t, TokenIdCmpGreaterThan);
|
||||
t.state = TokenizeStateGreaterThan;
|
||||
break;
|
||||
default:
|
||||
tokenize_error(&t, "invalid character: '%c'", c);
|
||||
}
|
||||
break;
|
||||
case TokenizeStateGreaterThan:
|
||||
switch (c) {
|
||||
case '=':
|
||||
t.cur_tok->id = TokenIdCmpGreaterOrEq;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
break;
|
||||
case '>':
|
||||
t.cur_tok->id = TokenIdBitShiftRight;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
break;
|
||||
default:
|
||||
t.pos -= 1;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case TokenizeStateLessThan:
|
||||
switch (c) {
|
||||
case '=':
|
||||
t.cur_tok->id = TokenIdCmpLessOrEq;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
case '<':
|
||||
t.cur_tok->id = TokenIdBitShiftLeft;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
break;
|
||||
default:
|
||||
t.pos -= 1;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case TokenizeStateBang:
|
||||
switch (c) {
|
||||
case '=':
|
||||
t.cur_tok->id = TokenIdCmpNotEq;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
break;
|
||||
default:
|
||||
t.pos -= 1;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case TokenizeStateEq:
|
||||
switch (c) {
|
||||
case '=':
|
||||
t.cur_tok->id = TokenIdCmpEq;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
break;
|
||||
default:
|
||||
t.pos -= 1;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case TokenizeStateAmpersand:
|
||||
switch (c) {
|
||||
case '&':
|
||||
t.cur_tok->id = TokenIdBoolAnd;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
break;
|
||||
default:
|
||||
t.pos -= 1;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case TokenizeStatePipe:
|
||||
switch (c) {
|
||||
case '|':
|
||||
t.cur_tok->id = TokenIdBoolOr;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
break;
|
||||
default:
|
||||
t.pos -= 1;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case TokenizeStateSawSlash:
|
||||
switch (c) {
|
||||
case '/':
|
||||
cancel_token(&t);
|
||||
t.state = TokenizeStateLineComment;
|
||||
break;
|
||||
case '*':
|
||||
cancel_token(&t);
|
||||
t.state = TokenizeStateMultiLineComment;
|
||||
t.multi_line_comment_count = 1;
|
||||
break;
|
||||
default:
|
||||
tokenize_error(&t, "invalid character: '%c'", c);
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
@ -371,16 +513,18 @@ ZigList<Token> *tokenize(Buf *buf) {
|
|||
switch (t.state) {
|
||||
case TokenizeStateStart:
|
||||
break;
|
||||
case TokenizeStateSymbol:
|
||||
end_token(&t);
|
||||
break;
|
||||
case TokenizeStateString:
|
||||
tokenize_error(&t, "unterminated string");
|
||||
break;
|
||||
case TokenizeStateSymbol:
|
||||
case TokenizeStateNumber:
|
||||
end_token(&t);
|
||||
break;
|
||||
case TokenizeStateSawDash:
|
||||
case TokenizeStatePipe:
|
||||
case TokenizeStateAmpersand:
|
||||
case TokenizeStateEq:
|
||||
case TokenizeStateBang:
|
||||
case TokenizeStateLessThan:
|
||||
case TokenizeStateGreaterThan:
|
||||
end_token(&t);
|
||||
break;
|
||||
case TokenizeStateSawSlash:
|
||||
|
|
@ -413,6 +557,7 @@ static const char * token_name(Token *token) {
|
|||
case TokenIdKeywordUnreachable: return "Unreachable";
|
||||
case TokenIdKeywordPub: return "Pub";
|
||||
case TokenIdKeywordExport: return "Export";
|
||||
case TokenIdKeywordAs: return "As";
|
||||
case TokenIdLParen: return "LParen";
|
||||
case TokenIdRParen: return "RParen";
|
||||
case TokenIdComma: return "Comma";
|
||||
|
|
@ -427,6 +572,23 @@ static const char * token_name(Token *token) {
|
|||
case TokenIdArrow: return "Arrow";
|
||||
case TokenIdDash: return "Dash";
|
||||
case TokenIdNumberSign: return "NumberSign";
|
||||
case TokenIdBinOr: return "BinOr";
|
||||
case TokenIdBinAnd: return "BinAnd";
|
||||
case TokenIdBinXor: return "BinXor";
|
||||
case TokenIdBoolOr: return "BoolOr";
|
||||
case TokenIdBoolAnd: return "BoolAnd";
|
||||
case TokenIdEq: return "Eq";
|
||||
case TokenIdNot: return "Not";
|
||||
case TokenIdCmpEq: return "CmpEq";
|
||||
case TokenIdCmpNotEq: return "CmpNotEq";
|
||||
case TokenIdCmpLessThan: return "CmpLessThan";
|
||||
case TokenIdCmpGreaterThan: return "CmpGreaterThan";
|
||||
case TokenIdCmpLessOrEq: return "CmpLessOrEq";
|
||||
case TokenIdCmpGreaterOrEq: return "CmpGreaterOrEq";
|
||||
case TokenIdBitShiftLeft: return "BitShiftLeft";
|
||||
case TokenIdBitShiftRight: return "BitShiftRight";
|
||||
case TokenIdSlash: return "Slash";
|
||||
case TokenIdPercent: return "Percent";
|
||||
}
|
||||
return "(invalid token)";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ enum TokenId {
|
|||
TokenIdKeywordUnreachable,
|
||||
TokenIdKeywordPub,
|
||||
TokenIdKeywordExport,
|
||||
TokenIdKeywordAs,
|
||||
TokenIdLParen,
|
||||
TokenIdRParen,
|
||||
TokenIdComma,
|
||||
|
|
@ -35,6 +36,23 @@ enum TokenId {
|
|||
TokenIdArrow,
|
||||
TokenIdDash,
|
||||
TokenIdNumberSign,
|
||||
TokenIdBoolOr,
|
||||
TokenIdBoolAnd,
|
||||
TokenIdBinOr,
|
||||
TokenIdBinAnd,
|
||||
TokenIdBinXor,
|
||||
TokenIdEq,
|
||||
TokenIdCmpEq,
|
||||
TokenIdNot,
|
||||
TokenIdCmpNotEq,
|
||||
TokenIdCmpLessThan,
|
||||
TokenIdCmpGreaterThan,
|
||||
TokenIdCmpLessOrEq,
|
||||
TokenIdCmpGreaterOrEq,
|
||||
TokenIdBitShiftLeft,
|
||||
TokenIdBitShiftRight,
|
||||
TokenIdSlash,
|
||||
TokenIdPercent,
|
||||
};
|
||||
|
||||
struct Token {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue