Author: mesyeti <mesyeti@mesyeti.uk>
add bitwise operators
basic/source/frontend/c89.c | 41 +++++++---- basic/source/lexer.c | 117 ++++++++++++++++++---------------- basic/source/lexer.h | 12 ++- basic/source/parser.c | 38 +++++++++- basic/source/semanticAnalysis.c | 60 +++++++++++++++++ basic/test.bas | 2
diff --git a/basic/source/frontend/c89.c b/basic/source/frontend/c89.c index 638c918ec87e07bd80566659e5cea48ca614527b..c6cb13fcaeaa9f8a82c709ab0d229db6d303e1fc 100644 --- a/basic/source/frontend/c89.c +++ b/basic/source/frontend/c89.c @@ -8,6 +8,7 @@ #include "../semanticAnalysis.h" static FILE* out; static bool inMain; +static bool inFunc; static void Finish(bool program) { if (program) { @@ -78,15 +79,19 @@ static void CompileFuncDef(FuncDefNode* node) { CompileDec(node->dec); fprintf(out, " {\n"); + inFunc = true; + for (size_t i = 0; i < node->bodyLen; ++ i) { Compiler_CompileBodyNode(&node->body[i]); } fprintf(out, "}\n"); + + inFunc = false; } static void CompileDim(DimNode* node) { - if (inMain) return; + if (!inFunc) return; UsedType usedType = SemanticAnalysis_NodeAsUsedType(node->varType); Type* type = &state.types[usedType.typeIdx]; @@ -143,16 +148,19 @@ fprintf(out, "("); CompileExprNode(node->bin.left); switch (node->bin.op) { - case TOKEN_ADD: fprintf(out, "+"); break; - case TOKEN_SUBTRACT: fprintf(out, "-"); break; - case TOKEN_MULTIPLY: fprintf(out, "*"); break; - case TOKEN_DIVIDE: fprintf(out, "/"); break; - case TOKEN_MOD: fprintf(out, "%%"); break; - case TOKEN_EQUAL: fprintf(out, "=="); break; - case TOKEN_LESS: fprintf(out, "<"); break; - case TOKEN_GREATER: fprintf(out, ">"); break; - case TOKEN_ASSIGN: fprintf(out, "="); break; - case TOKEN_NOT_EQUAL: fprintf(out, "!="); break; + case TOKEN_ADD: fprintf(out, "+"); break; + case TOKEN_SUBTRACT: fprintf(out, "-"); break; + case TOKEN_MULTIPLY: fprintf(out, "*"); break; + case TOKEN_DIVIDE: fprintf(out, "/"); break; + case TOKEN_MOD: fprintf(out, "%%"); break; + case TOKEN_EQUAL: fprintf(out, "=="); break; + case TOKEN_LESS: fprintf(out, "<"); break; + case TOKEN_GREATER: fprintf(out, ">"); break; + case TOKEN_ASSIGN: fprintf(out, "="); break; + case TOKEN_NOT_EQUAL: fprintf(out, "!="); break; + case TOKEN_ACCESS_XOR: fprintf(out, "^"); break; + case TOKEN_ADDRESS_AND: fprintf(out, "&"); break; + case TOKEN_BIT_OR: fprintf(out, "|"); break; case TOKEN_DOT: { UsedType type = SemanticAnalysis_NodeAsUsedType(node->bin.left); @@ -182,11 +190,12 @@ case NODE_UNARY_OP: { fprintf(out, "("); switch (node->unary.op) { - case TOKEN_ADD: fprintf(out, "+"); break; - case TOKEN_SUBTRACT: fprintf(out, "-"); break; - case TOKEN_ADDRESS: fprintf(out, "&"); break; - case TOKEN_ACCESS: fprintf(out, "*"); break; - case TOKEN_NOT: fprintf(out, "!"); break; + case TOKEN_ADD: fprintf(out, "+"); break; + case TOKEN_SUBTRACT: fprintf(out, "-"); break; + case TOKEN_ADDRESS_AND: fprintf(out, "&"); break; + case TOKEN_ACCESS_XOR: fprintf(out, "*"); break; + case TOKEN_NOT: fprintf(out, "!"); break; + case TOKEN_BIT_NOT: fprintf(out, "~"); break; default: assert(0); } diff --git a/basic/source/lexer.c b/basic/source/lexer.c index 1a385bd5a17d1a8f766f19be16866d59ebf55b6f..cf4355ddf929d399241d489a12283a9d928cd4c7 100644 --- a/basic/source/lexer.c +++ b/basic/source/lexer.c @@ -118,12 +118,8 @@ if (next == '=') { *token = TOKEN(TOKEN_NOT_EQUAL, NULL); } else { - *token = TOKEN(TOKEN_NOT, NULL); - - if (next == '\n') { - -- lexer->line; - } - fseek(lexer->file, -1, SEEK_CUR); + ErrorInfo err = {lexer->fileName, lexer->line}; + PrintError(err, "Unexpected '%c'", next); } return LEXER_TOKEN; } @@ -144,13 +140,15 @@ fseek(lexer->file, -1, SEEK_CUR); } return LEXER_TOKEN; } - case '<': *token = TOKEN(TOKEN_LESS, NULL); return LEXER_TOKEN; - case '>': *token = TOKEN(TOKEN_GREATER, NULL); return LEXER_TOKEN; - case '&': *token = TOKEN(TOKEN_ADDRESS, NULL); return LEXER_TOKEN; - case '^': *token = TOKEN(TOKEN_ACCESS, NULL); return LEXER_TOKEN; - case '\n': *token = TOKEN(TOKEN_LINE, NULL); return LEXER_TOKEN; - case '[': *token = TOKEN(TOKEN_LSQUARE, NULL); return LEXER_TOKEN; - case ']': *token = TOKEN(TOKEN_RSQUARE, NULL); return LEXER_TOKEN; + case '<': *token = TOKEN(TOKEN_LESS, NULL); return LEXER_TOKEN; + case '>': *token = TOKEN(TOKEN_GREATER, NULL); return LEXER_TOKEN; + case '&': *token = TOKEN(TOKEN_ADDRESS_AND, NULL); return LEXER_TOKEN; + case '^': *token = TOKEN(TOKEN_ACCESS_XOR, NULL); return LEXER_TOKEN; + case '\n': *token = TOKEN(TOKEN_LINE, NULL); return LEXER_TOKEN; + case '[': *token = TOKEN(TOKEN_LSQUARE, NULL); return LEXER_TOKEN; + case ']': *token = TOKEN(TOKEN_RSQUARE, NULL); return LEXER_TOKEN; + case '|': *token = TOKEN(TOKEN_BIT_OR, NULL); return LEXER_TOKEN; + case '~': *token = TOKEN(TOKEN_BIT_NOT, NULL); return LEXER_TOKEN; case '\'': { // comment while (true) { GET_CHAR(ch); @@ -202,7 +200,10 @@ {"type", TOKEN_TYPE}, {"union", TOKEN_UNION}, {"program", TOKEN_PROGRAM}, {"import", TOKEN_IMPORT}, - {"return", TOKEN_RETURN} + {"return", TOKEN_RETURN}, + {"not", TOKEN_NOT}, + {"and", TOKEN_AND}, + {"or", TOKEN_OR} }; for (size_t i = 0; i < sizeof(keywords) / sizeof(Keyword); ++ i) { @@ -221,48 +222,52 @@ } const char* Lexer_TypeVAsString(TokenType type) { switch (type) { - case TOKEN_NULL: return "null"; - case TOKEN_EXTERN: return "extern"; - case TOKEN_FUNC: return "func"; - case TOKEN_SUB: return "sub"; - case TOKEN_DIM: return "dim"; - case TOKEN_AS: return "as"; - case TOKEN_QUIT: return "quit"; - case TOKEN_PTR: return "ptr"; - case TOKEN_END: return "end"; - case TOKEN_IF: return "if"; - case TOKEN_THEN: return "then"; - case TOKEN_ELSE: return "else"; - case TOKEN_WHILE: return "while"; - case TOKEN_DO: return "do"; - case TOKEN_TYPE: return "type"; - case TOKEN_UNION: return "union"; - case TOKEN_PROGRAM: return "program"; - case TOKEN_IMPORT: return "import"; - case TOKEN_RETURN: return "return"; - case TOKEN_IDENTIFIER: return "identifier"; - case TOKEN_STRING: return "string"; - case TOKEN_INT: return "int"; - case TOKEN_LINE: return "line"; - case TOKEN_LPAREN: return "lparen"; - case TOKEN_RPAREN: return "rparen"; - case TOKEN_DOT: return "dot"; - case TOKEN_COMMA: return "comma"; - case TOKEN_LSQUARE: return "left square"; - case TOKEN_RSQUARE: return "right square"; - case TOKEN_ADD: return "add"; - case TOKEN_SUBTRACT: return "subtract"; - case TOKEN_MULTIPLY: return "multiply"; - case TOKEN_DIVIDE: return "divide"; - case TOKEN_MOD: return "mod"; - case TOKEN_EQUAL: return "equal"; - case TOKEN_LESS: return "less"; - case TOKEN_GREATER: return "greater"; - case TOKEN_ADDRESS: return "address"; - case TOKEN_ACCESS: return "access"; - case TOKEN_NOT: return "not"; - case TOKEN_NOT_EQUAL: return "not equal"; - default: assert(0); + case TOKEN_NULL: return "null"; + case TOKEN_EXTERN: return "extern"; + case TOKEN_FUNC: return "func"; + case TOKEN_SUB: return "sub"; + case TOKEN_DIM: return "dim"; + case TOKEN_AS: return "as"; + case TOKEN_QUIT: return "quit"; + case TOKEN_PTR: return "ptr"; + case TOKEN_END: return "end"; + case TOKEN_IF: return "if"; + case TOKEN_THEN: return "then"; + case TOKEN_ELSE: return "else"; + case TOKEN_WHILE: return "while"; + case TOKEN_DO: return "do"; + case TOKEN_TYPE: return "type"; + case TOKEN_UNION: return "union"; + case TOKEN_PROGRAM: return "program"; + case TOKEN_IMPORT: return "import"; + case TOKEN_RETURN: return "return"; + case TOKEN_NOT: return "not"; + case TOKEN_AND: return "and"; + case TOKEN_OR: return "or"; + case TOKEN_IDENTIFIER: return "identifier"; + case TOKEN_STRING: return "string"; + case TOKEN_INT: return "int"; + case TOKEN_LINE: return "line"; + case TOKEN_LPAREN: return "lparen"; + case TOKEN_RPAREN: return "rparen"; + case TOKEN_DOT: return "dot"; + case TOKEN_COMMA: return "comma"; + case TOKEN_LSQUARE: return "left square"; + case TOKEN_RSQUARE: return "right square"; + case TOKEN_ADD: return "add"; + case TOKEN_SUBTRACT: return "subtract"; + case TOKEN_MULTIPLY: return "multiply"; + case TOKEN_DIVIDE: return "divide"; + case TOKEN_MOD: return "mod"; + case TOKEN_EQUAL: return "equal"; + case TOKEN_LESS: return "less"; + case TOKEN_GREATER: return "greater"; + case TOKEN_ACCESS_XOR: return "access/bitwise xor"; + case TOKEN_ADDRESS_AND: return "address/bitwise and"; + case TOKEN_NOT_EQUAL: return "not equal"; + case TOKEN_BIT_OR: return "bitwise or"; + case TOKEN_BIT_NOT: return "bitwise not"; + default: assert(0); } } diff --git a/basic/source/lexer.h b/basic/source/lexer.h index 3e69d894e04c7addcd8704162ca225d804184f27..13c65731d6b8dec21001e81d54eac723105584a1 100644 --- a/basic/source/lexer.h +++ b/basic/source/lexer.h @@ -27,6 +27,9 @@ TOKEN_UNION, TOKEN_PROGRAM, TOKEN_IMPORT, TOKEN_RETURN, + TOKEN_NOT, + TOKEN_AND, + TOKEN_OR, // misc TOKEN_IDENTIFIER, @@ -52,10 +55,11 @@ TOKEN_EQUAL, TOKEN_LESS, TOKEN_GREATER, TOKEN_ASSIGN, - TOKEN_ADDRESS, - TOKEN_ACCESS, - TOKEN_NOT, - TOKEN_NOT_EQUAL + TOKEN_ACCESS_XOR, + TOKEN_ADDRESS_AND, + TOKEN_NOT_EQUAL, + TOKEN_BIT_OR, + TOKEN_BIT_NOT } TokenType; enum { diff --git a/basic/source/parser.c b/basic/source/parser.c index 9b52e190c69d437e8ba0e61b72ee3ac00f667554..cb7ce0fb159f75ed5430e4cee67cdfbd1c15a76f 100644 --- a/basic/source/parser.c +++ b/basic/source/parser.c @@ -122,9 +122,10 @@ static bool IsUnaryOperator(Token* tok) { switch (tok->type) { case TOKEN_ADD: case TOKEN_SUBTRACT: - case TOKEN_ADDRESS: - case TOKEN_ACCESS: - case TOKEN_NOT: return true; + case TOKEN_ADDRESS_AND: + case TOKEN_ACCESS_XOR: + case TOKEN_NOT: + case TOKEN_BIT_NOT: return true; default: return false; } } @@ -246,8 +247,33 @@ return left; } +static Node ParseBitwise(Parser* p) { + Node left = ParseAddSub(p); + + while ( + (p->tokens[p->i].type == TOKEN_ACCESS_XOR) || + (p->tokens[p->i].type == TOKEN_ADDRESS_AND) || + (p->tokens[p->i].type == TOKEN_BIT_OR) + ) { + TokenType op = p->tokens[p->i].type; + Advance(p); + Node right = ParseAddSub(p); + + Node* leftPtr = SafeMalloc(sizeof(Node)); + Node* rightPtr = SafeMalloc(sizeof(Node)); + *leftPtr = left; + *rightPtr = right; + + left.bin = (BinaryNode) { + INFO(NODE_BINARY_OP), leftPtr, rightPtr, op + }; + } + + return left; +} + static Node ParseComparison(Parser* p) { - Node left = ParseAddSub(p); + Node left = ParseBitwise(p); while ( (p->tokens[p->i].type == TOKEN_EQUAL) || @@ -257,7 +283,7 @@ (p->tokens[p->i].type == TOKEN_NOT_EQUAL) ) { TokenType op = p->tokens[p->i].type; Advance(p); - Node right = ParseAddSub(p); + Node right = ParseBitwise(p); Node* leftPtr = SafeMalloc(sizeof(Node)); Node* rightPtr = SafeMalloc(sizeof(Node)); @@ -745,7 +771,7 @@ case TOKEN_PROGRAM: return ParseProgram(p); case TOKEN_IMPORT: return ParseImport(p); case TOKEN_RETURN: return ParseReturn(p); case TOKEN_LPAREN: - case TOKEN_ACCESS: + case TOKEN_ACCESS_XOR: case TOKEN_IDENTIFIER: { if (p->i == p->tokenNum - 1) { PrintError(p->tokens[p->i].err, "Unexpected EOF"); diff --git a/basic/source/semanticAnalysis.c b/basic/source/semanticAnalysis.c index 610d709c48dffe66a8316c13f56401ba5685a47a..9eb9f24829a08d064b4b1e59d41ca94eb3143539 100644 --- a/basic/source/semanticAnalysis.c +++ b/basic/source/semanticAnalysis.c @@ -275,6 +275,18 @@ PrintError(node->i.err, "Cannot add float to pointer"); } return left; } + case TOKEN_ACCESS_XOR: + case TOKEN_ADDRESS_AND: + case TOKEN_BIT_OR: { + if (leftType->type != TYPE_PRIM) { + PrintError(node->i.err, "Using non-primitive type in bitwise operation"); + } + + if ((rightType->primType != PRIM_UINT) && (rightType->primType != PRIM_INT)) { + PrintError(node->i.err, "Can only use integers for bitwise operations"); + } + return right; + } default: { PrintError( node->i.err, "Operation '%s' not allowed here", @@ -293,6 +305,18 @@ PrintError(node->i.err, "Cannot add float to pointer"); } return right; } + case TOKEN_ACCESS_XOR: + case TOKEN_ADDRESS_AND: + case TOKEN_BIT_OR: { + if (leftType->type != TYPE_PRIM) { + PrintError(node->i.err, "Using non-primitive type in bitwise operation"); + } + + if ((leftType->primType != PRIM_UINT) && (leftType->primType != PRIM_INT)) { + PrintError(node->i.err, "Can only use integers for bitwise operations"); + } + return right; + } default: { PrintError( node->i.err, "Operation '%s' not allowed here", @@ -322,7 +346,7 @@ case TOKEN_DIVIDE: case TOKEN_MOD: { if ((leftType->type != TYPE_PRIM) || (rightType->type != TYPE_PRIM)) { PrintError( - node->i.err, "Operation '%s' only works on primitives", + node->i.err, "Operation '%s' only works on primitive types", Lexer_TypeVAsString(node->bin.op) ); } @@ -342,6 +366,23 @@ Lexer_TypeVAsString(node->bin.op) ); } } + case TOKEN_ACCESS_XOR: + case TOKEN_ADDRESS_AND: + case TOKEN_BIT_OR: { + if ((leftType->type != TYPE_PRIM) || (rightType->type != TYPE_PRIM)) { + PrintError(node->i.err, "Bitwise operations only work with primitive types"); + } + if ((leftType->primType == PRIM_FLOAT) || (rightType->type == PRIM_FLOAT)) { + PrintError(node->i.err, "Bitwise operations do not work on floats"); + } + + if (leftType->size > rightType->size) { + return left; + } + else { + return right; + } + } default: break; } @@ -413,16 +454,29 @@ } return operand; } - case TOKEN_ADDRESS: { + case TOKEN_ADDRESS_AND: { ++ operand.ptr; return operand; } - case TOKEN_ACCESS: { + case TOKEN_ACCESS_XOR: { if (operand.ptr <= 0) { PrintError(node->i.err, "Used access operator on non-pointer type"); } -- operand.ptr; + return operand; + } + case TOKEN_BIT_NOT: { + if (type->type != TYPE_PRIM) { + PrintError(node->i.err, + "Only primitive types can be used with the +/- unary operators" + ); + } + + if ((type->primType != PRIM_UINT) && (type->primType != PRIM_INT)) { + PrintError(node->i.err, "Can only perform bitwise not operation on integer types"); + } + return operand; } default: assert(0); diff --git a/basic/test.bas b/basic/test.bas index a42885304b30f991aedef977e1263ff2acb719e9..f97a4e6ee087a0b54c594adc79818f59c0105506 100644 --- a/basic/test.bas +++ b/basic/test.bas @@ -5,7 +5,7 @@ extern func puts(str as ptr(char)) i32 extern func printf(str as ptr(char), val as int) i32 -func add(a as int, b as int) i32 +func add(a as int, b as int) int return a + b end