mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 23:04:08 +10:00
Added a few characters that can be escaped
This commit is contained in:
@@ -1,21 +1,3 @@
|
|||||||
//test basic truth ternaries
|
|
||||||
{
|
|
||||||
assert true ? true : false, "Basic true ternary failed";
|
|
||||||
assert false ? false : true, "Basic false ternary failed";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//test nesting
|
print " foo \n bar";
|
||||||
{
|
|
||||||
fn least(a, b, c) {
|
|
||||||
return a < b ? a : b < c ? b : c;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert least(1, 2, 3) == 1, "Least 1, 2, 3 failed";
|
|
||||||
assert least(10, 5, 7) == 5, "Least 10, 5, 7 failed";
|
|
||||||
assert least(9, 7, 5) == 5, "Least 9, 7, 5 failed";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
print "All good";
|
|
||||||
|
|
||||||
@@ -171,11 +171,23 @@ static Toy_Token makeIntegerOrFloat(Toy_Lexer* lexer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
|
static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
|
||||||
while (!isAtEnd(lexer) && peek(lexer) != terminator) {
|
while (!isAtEnd(lexer)) {
|
||||||
|
//skip escaped terminators
|
||||||
|
if (peek(lexer) == '\\' && peekNext(lexer) == terminator) {
|
||||||
advance(lexer);
|
advance(lexer);
|
||||||
|
advance(lexer);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//actually escape if you've hit the terminator
|
||||||
|
if (peek(lexer) == terminator) {
|
||||||
advance(lexer); //eat terminator
|
advance(lexer); //eat terminator
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//otherwise
|
||||||
|
advance(lexer);
|
||||||
|
}
|
||||||
|
|
||||||
if (isAtEnd(lexer)) {
|
if (isAtEnd(lexer)) {
|
||||||
return makeErrorToken(lexer, "Unterminated string");
|
return makeErrorToken(lexer, "Unterminated string");
|
||||||
|
|||||||
@@ -261,17 +261,51 @@ static Toy_Opcode string(Toy_Parser* parser, Toy_ASTNode** nodeHandle) {
|
|||||||
//handle strings
|
//handle strings
|
||||||
switch(parser->previous.type) {
|
switch(parser->previous.type) {
|
||||||
case TOY_TOKEN_LITERAL_STRING: {
|
case TOY_TOKEN_LITERAL_STRING: {
|
||||||
int length = parser->previous.length;
|
//unescape valid escaped characters
|
||||||
|
int strLength = 0;
|
||||||
|
char* buffer = TOY_ALLOCATE(char, parser->previous.length);
|
||||||
|
|
||||||
//for safety
|
for (int i = 0; i < parser->previous.length; i++) {
|
||||||
if (length > TOY_MAX_STRING_LENGTH) {
|
if (parser->previous.lexeme[i] != '\\') { //copy normally
|
||||||
length = TOY_MAX_STRING_LENGTH;
|
buffer[strLength++] = parser->previous.lexeme[i];
|
||||||
char buffer[256];
|
continue;
|
||||||
snprintf(buffer, 256, TOY_CC_ERROR "Strings can only be a maximum of %d characters long" TOY_CC_RESET, TOY_MAX_STRING_LENGTH);
|
|
||||||
error(parser, parser->previous, buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_Literal literal = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(parser->previous.lexeme, length));
|
//unescape based on the character
|
||||||
|
switch(parser->previous.lexeme[++i]) {
|
||||||
|
case 'r':
|
||||||
|
buffer[strLength++] = '\r';
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
buffer[strLength++] = '\n';
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
buffer[strLength++] = '\t';
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
buffer[strLength++] = '\\';
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
buffer[strLength++] = '"';
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
char msg[256];
|
||||||
|
snprintf(msg, 256, TOY_CC_ERROR "Unrecognized escape character %c in string" TOY_CC_RESET, parser->previous.lexeme[++i]);
|
||||||
|
error(parser, parser->previous, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//for length safety
|
||||||
|
if (strLength > TOY_MAX_STRING_LENGTH) {
|
||||||
|
strLength = TOY_MAX_STRING_LENGTH;
|
||||||
|
char msg[256];
|
||||||
|
snprintf(msg, 256, TOY_CC_ERROR "Strings can only be a maximum of %d characters long" TOY_CC_RESET, TOY_MAX_STRING_LENGTH);
|
||||||
|
error(parser, parser->previous, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Literal literal = TOY_TO_STRING_LITERAL(Toy_createRefStringLength(buffer, strLength));
|
||||||
|
TOY_FREE_ARRAY(char, buffer, parser->previous.length);
|
||||||
Toy_emitASTNodeLiteral(nodeHandle, literal);
|
Toy_emitASTNodeLiteral(nodeHandle, literal);
|
||||||
Toy_freeLiteral(literal);
|
Toy_freeLiteral(literal);
|
||||||
return TOY_OP_EOF;
|
return TOY_OP_EOF;
|
||||||
|
|||||||
@@ -36,17 +36,17 @@ int main() {
|
|||||||
|
|
||||||
//inspect the node
|
//inspect the node
|
||||||
if (node == NULL) {
|
if (node == NULL) {
|
||||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode is null" TOY_CC_RESET);
|
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode is null\n" TOY_CC_RESET);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->type != TOY_AST_NODE_UNARY || node->unary.opcode != TOY_OP_PRINT) {
|
if (node->type != TOY_AST_NODE_UNARY || node->unary.opcode != TOY_OP_PRINT) {
|
||||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode is not a unary print instruction" TOY_CC_RESET);
|
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode is not a unary print instruction\n" TOY_CC_RESET);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->unary.child->type != TOY_AST_NODE_LITERAL || !TOY_IS_NULL(node->unary.child->atomic.literal)) {
|
if (node->unary.child->type != TOY_AST_NODE_LITERAL || !TOY_IS_NULL(node->unary.child->atomic.literal)) {
|
||||||
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode to be printed is not a null literal" TOY_CC_RESET);
|
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode to be printed is not a null literal\n" TOY_CC_RESET);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ int main() {
|
|||||||
|
|
||||||
while (node != NULL) {
|
while (node != NULL) {
|
||||||
if (node->type == TOY_AST_NODE_ERROR) {
|
if (node->type == TOY_AST_NODE_ERROR) {
|
||||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Error node detected" TOY_CC_RESET);
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Error node detected\n" TOY_CC_RESET);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +83,44 @@ int main() {
|
|||||||
free((void*)source);
|
free((void*)source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//test parsing of escaped characters
|
||||||
|
char* source = "print \"\\\"\";"; //NOTE: this string goes through two layers of escaping
|
||||||
|
|
||||||
|
//test parsing
|
||||||
|
Toy_Lexer lexer;
|
||||||
|
Toy_Parser parser;
|
||||||
|
Toy_initLexer(&lexer, source);
|
||||||
|
Toy_initParser(&parser, &lexer);
|
||||||
|
|
||||||
|
Toy_ASTNode* node = Toy_scanParser(&parser);
|
||||||
|
|
||||||
|
//inspect the node
|
||||||
|
if (node == NULL) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode is null\n" TOY_CC_RESET);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->type != TOY_AST_NODE_UNARY || node->unary.opcode != TOY_OP_PRINT) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode is not a unary print instruction\n" TOY_CC_RESET);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->unary.child->type != TOY_AST_NODE_LITERAL || !TOY_IS_STRING(node->unary.child->atomic.literal)) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode to be printed is not a string literal\n" TOY_CC_RESET);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Toy_equalsRefStringCString(TOY_AS_STRING(node->unary.child->atomic.literal), "\"")) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: ASTNode to be printed is not an escaped character, found: %s\n" TOY_CC_RESET, Toy_toCString(TOY_AS_STRING(node->unary.child->atomic.literal)));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//cleanup
|
||||||
|
Toy_freeASTNode(node);
|
||||||
|
Toy_freeParser(&parser);
|
||||||
|
}
|
||||||
|
|
||||||
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user