diff --git a/docs/TODO.txt b/docs/TODO.txt index 72118ac..eb82529 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -1,7 +1,10 @@ DONE: rework type system DONE: var decl with a type, but no value +DONE: type casting -TODO: type casting +TODO: string concat with the + operator +TODO: empty string as falsy? +TODO: remove optimization option TODO: increment & decrement operators TODO: a = b = c = 1; TODO: are compounds shallow or deep copies? diff --git a/scripts/casting.toy b/scripts/casting.toy new file mode 100644 index 0000000..bc68744 --- /dev/null +++ b/scripts/casting.toy @@ -0,0 +1,30 @@ +//boolean origin +var b: bool = true; + +print (int)b; +print (float)b; +print (string)b; + + +//integer origin +var i: int = 42; + +print (bool)i; +print (float)i; +print (string)i; + + +//float origin +var f: float = 3.14; + +print (bool)f; +print (int)f; +print (string)f; + + +//string origin +var s: string = "78.9"; + +print (bool)s; +print (int)s; +print (float)s; diff --git a/scripts/small.toy b/scripts/small.toy deleted file mode 100644 index f2e4d27..0000000 --- a/scripts/small.toy +++ /dev/null @@ -1,6 +0,0 @@ - -var arr : [int] = [1, 2, 3, 42]; -var dict : [string, int] = ["hello": 1, "world":2]; - -print arr; -print dict; \ No newline at end of file diff --git a/source/compiler.c b/source/compiler.c index 3a35449..3522e0f 100644 --- a/source/compiler.c +++ b/source/compiler.c @@ -140,7 +140,7 @@ static int writeLiteralTypeToCache(LiteralArray* literalCache, Literal literal) //push the store to the cache, tweaking the type Literal lit = TO_ARRAY_LITERAL(store); - lit.type = LITERAL_TYPE; //NOTE: tweaking the type usually isn't a good idea + lit.type = LITERAL_TYPE_INTERMEDIATE; //NOTE: tweaking the type usually isn't a good idea return pushLiteralArray(literalCache, lit); } @@ -463,8 +463,20 @@ unsigned char* collateCompiler(Compiler* compiler, int* size) { break; case LITERAL_TYPE: { + //push a raw type emitByte(&collation, &capacity, &count, LITERAL_TYPE); + Literal typeLiteral = compiler->literalCache.literals[i]; + + //what type this literal represents + emitByte(&collation, &capacity, &count, AS_TYPE(typeLiteral).typeOf); + emitByte(&collation, &capacity, &count, AS_TYPE(typeLiteral).constant); //if it's constant + } + break; + + case LITERAL_TYPE_INTERMEDIATE: { + emitByte(&collation, &capacity, &count, LITERAL_TYPE_INTERMEDIATE); + LiteralArray* ptr = AS_ARRAY(compiler->literalCache.literals[i]); //used an array for storage above //the base literal diff --git a/source/interpreter.c b/source/interpreter.c index 9e69bdd..a5d979d 100644 --- a/source/interpreter.c +++ b/source/interpreter.c @@ -361,6 +361,80 @@ static bool execVarAssign(Interpreter* interpreter) { return true; } +static bool execValCast(Interpreter* interpreter) { + Literal value = popLiteralArray(&interpreter->stack); + Literal type = popLiteralArray(&interpreter->stack); + + parseIdentifierToValue(interpreter, &value); + + Literal result = TO_NULL_LITERAL; + + //cast the rhs to the type represented by lhs + switch(AS_TYPE(type).typeOf) { + case LITERAL_BOOLEAN: + result = TO_BOOLEAN_LITERAL(IS_TRUTHY(value)); + break; + + case LITERAL_INTEGER: + if (IS_BOOLEAN(value)) { + result = TO_INTEGER_LITERAL(AS_BOOLEAN(value) ? 1 : 0); + } + + if (IS_FLOAT(value)) { + result = TO_INTEGER_LITERAL(AS_FLOAT(value)); + } + + if (IS_STRING(value)) { + int val = 0; + sscanf(AS_STRING(value), "%d", &val); + result = TO_INTEGER_LITERAL(val); + } + break; + + case LITERAL_FLOAT: + if (IS_BOOLEAN(value)) { + result = TO_FLOAT_LITERAL(AS_BOOLEAN(value) ? 1 : 0); + } + + if (IS_INTEGER(value)) { + result = TO_FLOAT_LITERAL(AS_INTEGER(value)); + } + + if (IS_STRING(value)) { + float val = 0; + sscanf(AS_STRING(value), "%f", &val); + result = TO_FLOAT_LITERAL(val); + } + break; + + case LITERAL_STRING: + if (IS_BOOLEAN(value)) { + result = TO_STRING_LITERAL(AS_BOOLEAN(value) ? "true" : "false"); + } + + if (IS_INTEGER(value)) { + char buffer[128]; + snprintf(buffer, 128, "%d", AS_INTEGER(value)); + result = TO_STRING_LITERAL(buffer); + } + + if (IS_FLOAT(value)) { + char buffer[128]; + snprintf(buffer, 128, "%g", AS_FLOAT(value)); + result = TO_STRING_LITERAL(buffer); + } + break; + + default: + printf("Unknown cast type found %d, terminating\n", AS_TYPE(type).typeOf); + return false; + } + + //leave the new value on the stack + pushLiteralArray(&interpreter->stack, result); + return true; +} + //the heart of toy static void execInterpreter(Interpreter* interpreter) { unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count); @@ -433,6 +507,12 @@ static void execInterpreter(Interpreter* interpreter) { } break; + case OP_TYPE_CAST: + if (!execValCast(interpreter)) { + return; + } + break; + default: printf("Unknown opcode found %d, terminating\n", opcode); printLiteralArray(&interpreter->stack, "\n"); @@ -602,6 +682,24 @@ void runInterpreter(Interpreter* interpreter, unsigned char* bytecode, int lengt break; case LITERAL_TYPE: { + //what the literal is + LiteralType literalType = (LiteralType)readByte(interpreter->bytecode, &interpreter->count); + unsigned char constant = readByte(interpreter->bytecode, &interpreter->count); + + Literal typeLiteral = TO_TYPE_LITERAL(literalType, constant); + + //save the type + pushLiteralArray(&interpreter->literalCache, typeLiteral); + + if (command.verbose) { + printf("(type "); + printLiteral(typeLiteral); + printf(")\n"); + } + } + break; + + case LITERAL_TYPE_INTERMEDIATE: { //what the literal represents LiteralType literalType = (LiteralType)readByte(interpreter->bytecode, &interpreter->count); unsigned char constant = readByte(interpreter->bytecode, &interpreter->count); diff --git a/source/literal.c b/source/literal.c index 94305b4..1472c85 100644 --- a/source/literal.c +++ b/source/literal.c @@ -322,7 +322,25 @@ void freeLiteral(Literal literal) { } bool _isTruthy(Literal x) { - return (IS_NULL(x) || (IS_BOOLEAN(x) && AS_BOOLEAN(x)) || (IS_INTEGER(x) && AS_INTEGER(x) != 0) || (IS_FLOAT(x) && AS_FLOAT(x) != 0)); + if (IS_NULL(x)) { + return false; + } + + if (IS_BOOLEAN(x)) { + return AS_BOOLEAN(x); + } + + if (IS_INTEGER(x)) { + return AS_INTEGER(x) != 0; + } + + if (IS_FLOAT(x)) { + return AS_FLOAT(x) != 0; + } + + //TODO: empty string as falsy? + + return true; } Literal _toStringLiteral(char* str) { diff --git a/source/literal.h b/source/literal.h index 7170945..7649267 100644 --- a/source/literal.h +++ b/source/literal.h @@ -17,6 +17,7 @@ typedef enum { //these are meta-level types LITERAL_IDENTIFIER, LITERAL_TYPE, + LITERAL_TYPE_INTERMEDIATE, //used to process types in the compiler only LITERAL_ANY, //used by the type system only } LiteralType; diff --git a/source/opcodes.h b/source/opcodes.h index 4c00619..9b3d46f 100644 --- a/source/opcodes.h +++ b/source/opcodes.h @@ -33,6 +33,8 @@ typedef enum Opcode { OP_VAR_ASSIGN, //assign to a literal + OP_TYPE_CAST, //temporarily change a type of an atomic value + //meta OP_SECTION_END, //TODO: add more diff --git a/source/parser.c b/source/parser.c index df64333..83ad4f2 100644 --- a/source/parser.c +++ b/source/parser.c @@ -266,12 +266,6 @@ static Opcode grouping(Parser* parser, Node** nodeHandle) { parsePrecedence(parser, &tmpNode, PREC_TERNARY); consume(parser, TOKEN_PAREN_RIGHT, "Expected ')' at end of grouping"); - //if it's just a literal, don't need a grouping - if (command.optimize >= 1 && tmpNode->type == NODE_LITERAL) { - (*nodeHandle) = tmpNode; - return OP_EOF; - } - //process the result without optimisations emitNodeGrouping(nodeHandle); nodeHandle = &((*nodeHandle)->unary.child); //re-align after append @@ -424,13 +418,72 @@ static Opcode identifier(Parser* parser, Node** nodeHandle) { return OP_EOF; } +static Opcode castingPrefix(Parser* parser, Node** nodeHandle) { + switch(parser->previous.type) { + case TOKEN_BOOLEAN: + emitNodeLiteral(nodeHandle, TO_TYPE_LITERAL(LITERAL_BOOLEAN, false)); + break; + + case TOKEN_INTEGER: + emitNodeLiteral(nodeHandle, TO_TYPE_LITERAL(LITERAL_INTEGER, false)); + break; + + case TOKEN_FLOAT: + emitNodeLiteral(nodeHandle, TO_TYPE_LITERAL(LITERAL_FLOAT, false)); + break; + + case TOKEN_STRING: + emitNodeLiteral(nodeHandle, TO_TYPE_LITERAL(LITERAL_STRING, false)); + break; + + default: + error(parser, parser->previous, "Unexpected token passed to casting precedence rule"); + return OP_EOF; + } + + return OP_EOF; +} + +static Opcode castingInfix(Parser* parser, Node** nodeHandle) { + advance(parser); + + switch(parser->previous.type) { + case TOKEN_IDENTIFIER: + identifier(parser, nodeHandle); + break; + + case TOKEN_LITERAL_TRUE: + case TOKEN_LITERAL_FALSE: + atomic(parser, nodeHandle); + break; + + case TOKEN_LITERAL_INTEGER: + atomic(parser, nodeHandle); + break; + + case TOKEN_LITERAL_FLOAT: + atomic(parser, nodeHandle); + break; + + case TOKEN_LITERAL_STRING: + atomic(parser, nodeHandle); + break; + + default: + error(parser, parser->previous, "Unexpected token passed to casting infix precedence rule"); + return OP_EOF; + } + + return OP_TYPE_CAST; +} + ParseRule parseRules[] = { //must match the token types //types {atomic, NULL, PREC_PRIMARY},// TOKEN_NULL, - {NULL, NULL, PREC_NONE},// TOKEN_BOOLEAN, - {NULL, NULL, PREC_NONE},// TOKEN_INTEGER, - {NULL, NULL, PREC_NONE},// TOKEN_FLOAT, - {NULL, NULL, PREC_NONE},// TOKEN_STRING, + {castingPrefix, NULL, PREC_CALL},// TOKEN_BOOLEAN, + {castingPrefix, NULL, PREC_CALL},// TOKEN_INTEGER, + {castingPrefix, NULL, PREC_CALL},// TOKEN_FLOAT, + {castingPrefix, NULL, PREC_CALL},// TOKEN_STRING, {NULL, NULL, PREC_NONE},// TOKEN_ARRAY, {NULL, NULL, PREC_NONE},// TOKEN_DICTIONARY, {NULL, NULL, PREC_NONE},// TOKEN_FUNCTION, @@ -459,12 +512,12 @@ ParseRule parseRules[] = { //must match the token types {NULL, NULL, PREC_NONE},// TOKEN_WHILE, //literal values - {identifier, NULL, PREC_PRIMARY},// TOKEN_IDENTIFIER, - {atomic, NULL, PREC_PRIMARY},// TOKEN_LITERAL_TRUE, - {atomic, NULL, PREC_PRIMARY},// TOKEN_LITERAL_FALSE, - {atomic, NULL, PREC_PRIMARY},// TOKEN_LITERAL_INTEGER, - {atomic, NULL, PREC_PRIMARY},// TOKEN_LITERAL_FLOAT, - {string, NULL, PREC_PRIMARY},// TOKEN_LITERAL_STRING, + {identifier, castingInfix, PREC_PRIMARY},// TOKEN_IDENTIFIER, + {atomic, castingInfix, PREC_PRIMARY},// TOKEN_LITERAL_TRUE, + {atomic, castingInfix, PREC_PRIMARY},// TOKEN_LITERAL_FALSE, + {atomic, castingInfix, PREC_PRIMARY},// TOKEN_LITERAL_INTEGER, + {atomic, castingInfix, PREC_PRIMARY},// TOKEN_LITERAL_FLOAT, + {string, castingInfix, PREC_PRIMARY},// TOKEN_LITERAL_STRING, //math operators {NULL, binary, PREC_TERM},// TOKEN_PLUS,