diff --git a/README.md b/README.md index c65a694d47..ec6798cfbd 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,11 @@ MultiplyExpression : CastExpression MultiplyOperator CastExpression | CastExpres MultiplyOperator : token(Star) | token(Slash) | token(Percent) -CastExpression : PrimaryExpression token(as) Type | PrimaryExpression +CastExpression : UnaryExpression token(as) Type | UnaryExpression + +UnaryExpression : UnaryOp PrimaryExpression | PrimaryExpression + +UnaryOp : token(Not) | token(Dash) | token(Tilde) PrimaryExpression : token(Number) | token(String) | token(Unreachable) | FnCall | GroupedExpression | Block @@ -149,9 +153,11 @@ FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen) ``` -### Binary Operator Precedence +### Operator Precedence ``` +x() +!x -x ~x as * / % + - diff --git a/src/codegen.cpp b/src/codegen.cpp index dd1f8a4722..4ecceaabdd 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -317,6 +317,7 @@ static void find_declarations(CodeGen *g, AstNode *node) { case NodeTypeAddExpr: case NodeTypeMultExpr: case NodeTypeCastExpr: + case NodeTypeUnaryExpr: case NodeTypePrimaryExpr: case NodeTypeGroupedExpr: zig_unreachable(); @@ -542,6 +543,9 @@ static void analyze_node(CodeGen *g, AstNode *node) { case NodeTypeCastExpr: zig_panic("TODO"); break; + case NodeTypeUnaryExpr: + zig_panic("TODO"); + break; case NodeTypePrimaryExpr: switch (node->data.primary_expr.type) { case PrimaryExprTypeNumber: @@ -740,10 +744,35 @@ static LLVMValueRef gen_primary_expr(CodeGen *g, AstNode *node) { zig_unreachable(); } +static LLVMValueRef gen_unary_expr(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeUnaryExpr); + assert(node->data.unary_expr.primary_expr); + + LLVMValueRef expr = gen_expr(g, node->data.unary_expr.primary_expr); + + switch (node->data.unary_expr.unary_op) { + case UnaryOpNegation: + add_debug_source_node(g, node); + return LLVMBuildNeg(g->builder, expr, ""); + case UnaryOpBoolNot: + { + LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr)); + add_debug_source_node(g, node); + return LLVMBuildICmp(g->builder, LLVMIntEQ, expr, zero, ""); + } + case UnaryOpBinNot: + add_debug_source_node(g, node); + return LLVMBuildNot(g->builder, expr, ""); + case UnaryOpInvalid: + 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); + LLVMValueRef expr = gen_expr(g, node->data.cast_expr.unary_expr); if (!node->data.cast_expr.type) return expr; @@ -997,6 +1026,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { return gen_bool_or_expr(g, node); case NodeTypeReturnExpr: return gen_return_expr(g, node); + case NodeTypeUnaryExpr: + return gen_unary_expr(g, node); case NodeTypeRoot: case NodeTypeRootExportDecl: case NodeTypeFnProto: diff --git a/src/parser.cpp b/src/parser.cpp index 07ade572f0..00a180dadc 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -10,6 +10,16 @@ #include #include +static const char *unary_op_str(UnaryOp unary_op) { + switch (unary_op) { + case UnaryOpInvalid: return "(invalid)"; + case UnaryOpNegation: return "-"; + case UnaryOpBoolNot: return "!"; + case UnaryOpBinNot: return "~"; + } + zig_unreachable(); +} + static const char *mult_op_str(MultOp mult_op) { switch (mult_op) { case MultOpInvalid: return "(invalid)"; @@ -116,6 +126,8 @@ const char *node_type_str(NodeType node_type) { return "PrimaryExpr"; case NodeTypeGroupedExpr: return "GroupedExpr"; + case NodeTypeUnaryExpr: + return "UnaryExpr"; } zig_unreachable(); } @@ -284,10 +296,15 @@ void ast_print(AstNode *node, int indent) { break; case NodeTypeCastExpr: fprintf(stderr, "%s\n", node_type_str(node->type)); - ast_print(node->data.cast_expr.primary_expr, indent + 2); + ast_print(node->data.cast_expr.unary_expr, indent + 2); if (node->data.cast_expr.type) ast_print(node->data.cast_expr.type, indent + 2); break; + case NodeTypeUnaryExpr: + fprintf(stderr, "%s %s\n", node_type_str(node->type), + unary_op_str(node->data.unary_expr.unary_op)); + ast_print(node->data.unary_expr.primary_expr, indent + 2); + break; case NodeTypePrimaryExpr: switch (node->data.primary_expr.type) { case PrimaryExprTypeNumber: @@ -688,21 +705,65 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool ast_invalid_token_error(pc, token); } +static UnaryOp tok_to_unary_op(Token *token) { + switch (token->id) { + case TokenIdBang: return UnaryOpBoolNot; + case TokenIdDash: return UnaryOpNegation; + case TokenIdTilde: return UnaryOpBinNot; + default: return UnaryOpInvalid; + } +} + /* -CastExpression : PrimaryExpression token(As) Type | PrimaryExpression +UnaryOp : token(Not) | token(Dash) | token(Tilde) +*/ +static UnaryOp ast_parse_unary_op(ParseContext *pc, int *token_index, bool mandatory) { + Token *token = &pc->tokens->at(*token_index); + UnaryOp result = tok_to_unary_op(token); + if (result == UnaryOpInvalid) { + if (mandatory) { + ast_invalid_token_error(pc, token); + } else { + return UnaryOpInvalid; + } + } + *token_index += 1; + return result; +} + +/* +UnaryExpression : UnaryOp PrimaryExpression | PrimaryExpression +*/ +static AstNode *ast_parse_unary_expr(ParseContext *pc, int *token_index, bool mandatory) { + Token *token = &pc->tokens->at(*token_index); + UnaryOp unary_op = ast_parse_unary_op(pc, token_index, false); + if (unary_op == UnaryOpInvalid) + return ast_parse_primary_expr(pc, token_index, mandatory); + + AstNode *primary_expr = ast_parse_primary_expr(pc, token_index, true); + AstNode *node = ast_create_node(NodeTypeUnaryExpr, token); + node->data.unary_expr.primary_expr = primary_expr; + node->data.unary_expr.unary_op = unary_op; + + return node; +} + + +/* +CastExpression : UnaryExpression token(as) Type | UnaryExpression */ 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) + AstNode *unary_expr = ast_parse_unary_expr(pc, token_index, mandatory); + if (!unary_expr) return nullptr; Token *as_kw = &pc->tokens->at(*token_index); if (as_kw->id != TokenIdKeywordAs) - return primary_expr; + return unary_expr; *token_index += 1; AstNode *node = ast_create_node(NodeTypeCastExpr, as_kw); - node->data.cast_expr.primary_expr = primary_expr; + node->data.cast_expr.unary_expr = unary_expr; node->data.cast_expr.type = ast_parse_type(pc, *token_index, token_index); diff --git a/src/parser.hpp b/src/parser.hpp index 626152a973..da933c6dd4 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -40,6 +40,7 @@ enum NodeType { NodeTypeCastExpr, NodeTypePrimaryExpr, NodeTypeGroupedExpr, + NodeTypeUnaryExpr, }; struct AstNodeRoot { @@ -204,7 +205,7 @@ struct AstNodeMultExpr { }; struct AstNodeCastExpr { - AstNode *primary_expr; + AstNode *unary_expr; // if type is non-null, do cast, otherwise nothing AstNode *type; }; @@ -233,6 +234,18 @@ struct AstNodeGroupedExpr { AstNode *expr; }; +enum UnaryOp { + UnaryOpInvalid, + UnaryOpBoolNot, + UnaryOpBinNot, + UnaryOpNegation, +}; + +struct AstNodeUnaryExpr { + UnaryOp unary_op; + AstNode *primary_expr; +}; + struct AstNode { enum NodeType type; AstNode *parent; @@ -264,6 +277,7 @@ struct AstNode { AstNodeCastExpr cast_expr; AstNodePrimaryExpr primary_expr; AstNodeGroupedExpr grouped_expr; + AstNodeUnaryExpr unary_expr; } data; }; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 1852dbf142..3cfab3288a 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -249,6 +249,10 @@ ZigList *tokenize(Buf *buf) { begin_token(&t, TokenIdPlus); end_token(&t); break; + case '~': + begin_token(&t, TokenIdTilde); + end_token(&t); + break; case '-': begin_token(&t, TokenIdDash); t.state = TokenizeStateSawDash; @@ -278,7 +282,7 @@ ZigList *tokenize(Buf *buf) { t.state = TokenizeStateEq; break; case '!': - begin_token(&t, TokenIdNot); + begin_token(&t, TokenIdBang); t.state = TokenizeStateBang; break; case '<': @@ -578,7 +582,8 @@ static const char * token_name(Token *token) { case TokenIdBoolOr: return "BoolOr"; case TokenIdBoolAnd: return "BoolAnd"; case TokenIdEq: return "Eq"; - case TokenIdNot: return "Not"; + case TokenIdBang: return "Bang"; + case TokenIdTilde: return "Tilde"; case TokenIdCmpEq: return "CmpEq"; case TokenIdCmpNotEq: return "CmpNotEq"; case TokenIdCmpLessThan: return "CmpLessThan"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 725429a18e..2c9ad61957 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -43,7 +43,8 @@ enum TokenId { TokenIdBinXor, TokenIdEq, TokenIdCmpEq, - TokenIdNot, + TokenIdBang, + TokenIdTilde, TokenIdCmpNotEq, TokenIdCmpLessThan, TokenIdCmpGreaterThan,