diff --git a/src/analyze.cpp b/src/analyze.cpp index b7a4bd388f..7b21a43606 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -714,6 +714,50 @@ static TypeTableEntry *analyze_variable_name(CodeGen *g, BlockContext *context, } } +static bool is_op_allowed(TypeTableEntry *type, BinOpType op) { + switch (op) { + case BinOpTypeAssign: + return true; + case BinOpTypeAssignTimes: + case BinOpTypeAssignDiv: + case BinOpTypeAssignMod: + case BinOpTypeAssignPlus: + case BinOpTypeAssignMinus: + return type->id == TypeTableEntryIdInt || type->id == TypeTableEntryIdFloat; + case BinOpTypeAssignBitShiftLeft: + case BinOpTypeAssignBitShiftRight: + case BinOpTypeAssignBitAnd: + case BinOpTypeAssignBitXor: + case BinOpTypeAssignBitOr: + return type->id == TypeTableEntryIdInt; + case BinOpTypeAssignBoolAnd: + case BinOpTypeAssignBoolOr: + return type->id == TypeTableEntryIdBool; + + case BinOpTypeInvalid: + case BinOpTypeBoolOr: + case BinOpTypeBoolAnd: + case BinOpTypeCmpEq: + case BinOpTypeCmpNotEq: + case BinOpTypeCmpLessThan: + case BinOpTypeCmpGreaterThan: + case BinOpTypeCmpLessOrEq: + case BinOpTypeCmpGreaterOrEq: + case BinOpTypeBinOr: + case BinOpTypeBinXor: + case BinOpTypeBinAnd: + case BinOpTypeBitShiftLeft: + case BinOpTypeBitShiftRight: + case BinOpTypeAdd: + case BinOpTypeSub: + case BinOpTypeMult: + case BinOpTypeDiv: + case BinOpTypeMod: + zig_unreachable(); + } + zig_unreachable(); +} + static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -842,6 +886,18 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, { switch (node->data.bin_op_expr.bin_op) { case BinOpTypeAssign: + case BinOpTypeAssignTimes: + case BinOpTypeAssignDiv: + case BinOpTypeAssignMod: + case BinOpTypeAssignPlus: + case BinOpTypeAssignMinus: + case BinOpTypeAssignBitShiftLeft: + case BinOpTypeAssignBitShiftRight: + case BinOpTypeAssignBitAnd: + case BinOpTypeAssignBitXor: + case BinOpTypeAssignBitOr: + case BinOpTypeAssignBoolAnd: + case BinOpTypeAssignBoolOr: { AstNode *lhs_node = node->data.bin_op_expr.op1; TypeTableEntry *expected_rhs_type = nullptr; @@ -853,7 +909,12 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, add_node_error(g, lhs_node, buf_sprintf("cannot assign to constant variable")); } else { - expected_rhs_type = var->type; + if (!is_op_allowed(var->type, node->data.bin_op_expr.bin_op)) { + add_node_error(g, lhs_node, + buf_sprintf("operator not allowed for type '%s'", buf_ptr(&var->type->name))); + } else { + expected_rhs_type = var->type; + } } } else { add_node_error(g, lhs_node, diff --git a/src/codegen.cpp b/src/codegen.cpp index 881ef2be18..c7dca2e9b6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -409,6 +409,18 @@ static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) { case BinOpTypeCmpGreaterOrEq: case BinOpTypeInvalid: case BinOpTypeAssign: + case BinOpTypeAssignTimes: + case BinOpTypeAssignDiv: + case BinOpTypeAssignMod: + case BinOpTypeAssignPlus: + case BinOpTypeAssignMinus: + case BinOpTypeAssignBitShiftLeft: + case BinOpTypeAssignBitShiftRight: + case BinOpTypeAssignBitAnd: + case BinOpTypeAssignBitXor: + case BinOpTypeAssignBitOr: + case BinOpTypeAssignBoolAnd: + case BinOpTypeAssignBoolOr: zig_unreachable(); } zig_unreachable(); @@ -543,6 +555,11 @@ static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) { AstNode *lhs_node = node->data.bin_op_expr.op1; + bool is_read_first = node->data.bin_op_expr.bin_op != BinOpTypeAssign; + if (is_read_first) { + zig_panic("TODO: implement modify assignment ops"); + } + if (lhs_node->type == NodeTypeSymbol) { LocalVariableTableEntry *var = find_local_variable(node->codegen_node->expr_node.block_context, &lhs_node->data.symbol); @@ -577,15 +594,26 @@ static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) { } else { zig_panic("bad assign target"); } - } static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { switch (node->data.bin_op_expr.bin_op) { - case BinOpTypeAssign: - return gen_assign_expr(g, node); case BinOpTypeInvalid: zig_unreachable(); + case BinOpTypeAssign: + case BinOpTypeAssignTimes: + case BinOpTypeAssignDiv: + case BinOpTypeAssignMod: + case BinOpTypeAssignPlus: + case BinOpTypeAssignMinus: + case BinOpTypeAssignBitShiftLeft: + case BinOpTypeAssignBitShiftRight: + case BinOpTypeAssignBitAnd: + case BinOpTypeAssignBitXor: + case BinOpTypeAssignBitOr: + case BinOpTypeAssignBoolAnd: + case BinOpTypeAssignBoolOr: + return gen_assign_expr(g, node); case BinOpTypeBoolOr: return gen_bool_or_expr(g, node); case BinOpTypeBoolAnd: diff --git a/src/parser.cpp b/src/parser.cpp index 8b7ae50ae4..9ccaf6f20b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -14,26 +14,38 @@ static const char *bin_op_str(BinOpType bin_op) { switch (bin_op) { - case BinOpTypeInvalid: return "(invalid)"; - case BinOpTypeBoolOr: return "||"; - case BinOpTypeBoolAnd: return "&&"; - case BinOpTypeCmpEq: return "=="; - case BinOpTypeCmpNotEq: return "!="; - case BinOpTypeCmpLessThan: return "<"; - case BinOpTypeCmpGreaterThan: return ">"; - case BinOpTypeCmpLessOrEq: return "<="; - case BinOpTypeCmpGreaterOrEq: return ">="; - case BinOpTypeBinOr: return "|"; - case BinOpTypeBinXor: return "^"; - case BinOpTypeBinAnd: return "&"; - case BinOpTypeBitShiftLeft: return "<<"; - case BinOpTypeBitShiftRight: return ">>"; - case BinOpTypeAdd: return "+"; - case BinOpTypeSub: return "-"; - case BinOpTypeMult: return "*"; - case BinOpTypeDiv: return "/"; - case BinOpTypeMod: return "%"; - case BinOpTypeAssign: return "="; + case BinOpTypeInvalid: return "(invalid)"; + case BinOpTypeBoolOr: return "||"; + case BinOpTypeBoolAnd: return "&&"; + case BinOpTypeCmpEq: return "=="; + case BinOpTypeCmpNotEq: return "!="; + case BinOpTypeCmpLessThan: return "<"; + case BinOpTypeCmpGreaterThan: return ">"; + case BinOpTypeCmpLessOrEq: return "<="; + case BinOpTypeCmpGreaterOrEq: return ">="; + case BinOpTypeBinOr: return "|"; + case BinOpTypeBinXor: return "^"; + case BinOpTypeBinAnd: return "&"; + case BinOpTypeBitShiftLeft: return "<<"; + case BinOpTypeBitShiftRight: return ">>"; + case BinOpTypeAdd: return "+"; + case BinOpTypeSub: return "-"; + case BinOpTypeMult: return "*"; + case BinOpTypeDiv: return "/"; + case BinOpTypeMod: return "%"; + case BinOpTypeAssign: return "="; + case BinOpTypeAssignTimes: return "*="; + case BinOpTypeAssignDiv: return "/="; + case BinOpTypeAssignMod: return "%="; + case BinOpTypeAssignPlus: return "+="; + case BinOpTypeAssignMinus: return "-="; + case BinOpTypeAssignBitShiftLeft: return "<<="; + case BinOpTypeAssignBitShiftRight: return ">>="; + case BinOpTypeAssignBitAnd: return "&="; + case BinOpTypeAssignBitXor: return "^="; + case BinOpTypeAssignBitOr: return "|="; + case BinOpTypeAssignBoolAnd: return "&&="; + case BinOpTypeAssignBoolOr: return "||="; } zig_unreachable(); } @@ -103,7 +115,7 @@ const char *node_type_str(NodeType node_type) { case NodeTypeLabel: return "Label"; case NodeTypeGoto: - return "Label"; + return "Goto"; case NodeTypeAsmExpr: return "AsmExpr"; case NodeTypeFieldAccessExpr: @@ -1448,24 +1460,60 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool ma return nullptr; } +static BinOpType tok_to_ass_op(Token *token) { + switch (token->id) { + case TokenIdEq: return BinOpTypeAssign; + case TokenIdTimesEq: return BinOpTypeAssignTimes; + case TokenIdDivEq: return BinOpTypeAssignDiv; + case TokenIdModEq: return BinOpTypeAssignMod; + case TokenIdPlusEq: return BinOpTypeAssignPlus; + case TokenIdMinusEq: return BinOpTypeAssignMinus; + case TokenIdBitShiftLeftEq: return BinOpTypeAssignBitShiftLeft; + case TokenIdBitShiftRightEq: return BinOpTypeAssignBitShiftRight; + case TokenIdBitAndEq: return BinOpTypeAssignBitAnd; + case TokenIdBitXorEq: return BinOpTypeAssignBitXor; + case TokenIdBitOrEq: return BinOpTypeAssignBitOr; + case TokenIdBoolAndEq: return BinOpTypeAssignBoolAnd; + case TokenIdBoolOrEq: return BinOpTypeAssignBoolOr; + default: return BinOpTypeInvalid; + } +} + /* -AssignmentExpression : BoolOrExpression token(Equal) BoolOrExpression | BoolOrExpression +AssignmentOperator : token(Eq) | token(TimesEq) | token(DivEq) | token(ModEq) | token(PlusEq) | token(MinusEq) | token(BitShiftLeftEq) | token(BitShiftRightEq) | token(BitAndEq) | token(BitXorEq) | token(BitOrEq) | token(BoolAndEq) | token(BoolOrEq) +*/ +static BinOpType ast_parse_ass_op(ParseContext *pc, int *token_index, bool mandatory) { + Token *token = &pc->tokens->at(*token_index); + BinOpType result = tok_to_ass_op(token); + if (result == BinOpTypeInvalid) { + if (mandatory) { + ast_invalid_token_error(pc, token); + } else { + return BinOpTypeInvalid; + } + } + *token_index += 1; + return result; +} + +/* +AssignmentExpression : BoolOrExpression AssignmentOperator BoolOrExpression | BoolOrExpression */ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mandatory) { AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory); if (!lhs) - return lhs; + return nullptr; Token *token = &pc->tokens->at(*token_index); - if (token->id != TokenIdEq) + BinOpType ass_op = ast_parse_ass_op(pc, token_index, false); + if (ass_op == BinOpTypeInvalid) return lhs; - *token_index += 1; AstNode *rhs = ast_parse_bool_or_expr(pc, token_index, true); AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = lhs; - node->data.bin_op_expr.bin_op = BinOpTypeAssign; + node->data.bin_op_expr.bin_op = ass_op; node->data.bin_op_expr.op2 = rhs; return node; diff --git a/src/parser.hpp b/src/parser.hpp index 7d3b42d9e5..1468d2a9e1 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -119,6 +119,18 @@ struct AstNodeVariableDeclaration { enum BinOpType { BinOpTypeInvalid, BinOpTypeAssign, + BinOpTypeAssignTimes, + BinOpTypeAssignDiv, + BinOpTypeAssignMod, + BinOpTypeAssignPlus, + BinOpTypeAssignMinus, + BinOpTypeAssignBitShiftLeft, + BinOpTypeAssignBitShiftRight, + BinOpTypeAssignBitAnd, + BinOpTypeAssignBitXor, + BinOpTypeAssignBitOr, + BinOpTypeAssignBoolAnd, + BinOpTypeAssignBoolOr, BinOpTypeBoolOr, BinOpTypeBoolAnd, BinOpTypeCmpEq,