mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
Disallow empty bodies in control flow statements, added 'pass' keyword
Fixed #170
This commit is contained in:
@@ -44,6 +44,7 @@ const Toy_KeywordTypeTuple keywordTuples[] = {
|
|||||||
{TOY_TOKEN_KEYWORD_IMPORT, "import"},
|
{TOY_TOKEN_KEYWORD_IMPORT, "import"},
|
||||||
{TOY_TOKEN_KEYWORD_IN, "in"},
|
{TOY_TOKEN_KEYWORD_IN, "in"},
|
||||||
{TOY_TOKEN_KEYWORD_OF, "of"},
|
{TOY_TOKEN_KEYWORD_OF, "of"},
|
||||||
|
{TOY_TOKEN_KEYWORD_PASS, "pass"},
|
||||||
{TOY_TOKEN_KEYWORD_PRINT, "print"},
|
{TOY_TOKEN_KEYWORD_PRINT, "print"},
|
||||||
{TOY_TOKEN_KEYWORD_RETURN, "return"},
|
{TOY_TOKEN_KEYWORD_RETURN, "return"},
|
||||||
{TOY_TOKEN_KEYWORD_TYPEAS, "typeas"},
|
{TOY_TOKEN_KEYWORD_TYPEAS, "typeas"},
|
||||||
@@ -404,12 +405,6 @@ void Toy_private_printToken(Toy_Token* token) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//read pass token, even though it isn't generated
|
|
||||||
if (token->type == TOY_TOKEN_PASS) {
|
|
||||||
printf(TOY_CC_NOTICE "PASS: \t%d\t%.*s\n" TOY_CC_RESET, (int)token->line, (int)token->length, token->lexeme);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//print the line number
|
//print the line number
|
||||||
printf("\t%d\t%d\t", token->type, (int)token->line);
|
printf("\t%d\t%d\t", token->type, (int)token->line);
|
||||||
|
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ static ParsingTuple parsingRulesetTable[] = {
|
|||||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_IMPORT,
|
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_IMPORT,
|
||||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_IN,
|
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_IN,
|
||||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_OF,
|
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_OF,
|
||||||
|
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_PASS,
|
||||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_PRINT,
|
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_PRINT,
|
||||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_RETURN,
|
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_RETURN,
|
||||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_TYPEAS,
|
{PREC_NONE,NULL,NULL},// TOY_TOKEN_KEYWORD_TYPEAS,
|
||||||
@@ -221,7 +222,6 @@ static ParsingTuple parsingRulesetTable[] = {
|
|||||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_PIPE, // |
|
{PREC_NONE,NULL,NULL},// TOY_TOKEN_OPERATOR_PIPE, // |
|
||||||
|
|
||||||
//meta tokens
|
//meta tokens
|
||||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_PASS,
|
|
||||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_ERROR,
|
{PREC_NONE,NULL,NULL},// TOY_TOKEN_ERROR,
|
||||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_EOF,
|
{PREC_NONE,NULL,NULL},// TOY_TOKEN_EOF,
|
||||||
};
|
};
|
||||||
@@ -730,7 +730,7 @@ static void makeExpr(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
|
|||||||
|
|
||||||
//forward declarations
|
//forward declarations
|
||||||
static void makeBlockStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
static void makeBlockStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
||||||
static void makeDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
static void makeDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle, bool errorOnEmpty);
|
||||||
|
|
||||||
static void makeAssertStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
static void makeAssertStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||||
Toy_Ast* ast = NULL; //assert's emit function is a bit different
|
Toy_Ast* ast = NULL; //assert's emit function is a bit different
|
||||||
@@ -764,11 +764,11 @@ static void makeIfThenElseStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, To
|
|||||||
consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'if' condition");
|
consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'if' condition");
|
||||||
|
|
||||||
// { thenBranch }
|
// { thenBranch }
|
||||||
makeDeclarationStmt(bucketHandle, parser, &thenBranch);
|
makeDeclarationStmt(bucketHandle, parser, &thenBranch, true);
|
||||||
|
|
||||||
//else { elseBranch }
|
//else { elseBranch }
|
||||||
if (match(parser, TOY_TOKEN_KEYWORD_ELSE)) {
|
if (match(parser, TOY_TOKEN_KEYWORD_ELSE)) {
|
||||||
makeDeclarationStmt(bucketHandle, parser, &elseBranch);
|
makeDeclarationStmt(bucketHandle, parser, &elseBranch, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_private_emitAstIfThenElse(bucketHandle, rootHandle, condBranch, thenBranch, elseBranch);
|
Toy_private_emitAstIfThenElse(bucketHandle, rootHandle, condBranch, thenBranch, elseBranch);
|
||||||
@@ -784,7 +784,7 @@ static void makeWhileStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast
|
|||||||
consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'while' condition");
|
consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'while' condition");
|
||||||
|
|
||||||
// { thenBranch }
|
// { thenBranch }
|
||||||
makeDeclarationStmt(bucketHandle, parser, &thenBranch);
|
makeDeclarationStmt(bucketHandle, parser, &thenBranch, true);
|
||||||
|
|
||||||
Toy_private_emitAstWhileThen(bucketHandle, rootHandle, condBranch, thenBranch);
|
Toy_private_emitAstWhileThen(bucketHandle, rootHandle, condBranch, thenBranch);
|
||||||
}
|
}
|
||||||
@@ -899,8 +899,8 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//empty lines
|
//empty lines, or pass statements
|
||||||
else if (match(parser, TOY_TOKEN_OPERATOR_SEMICOLON)) {
|
else if (match(parser, TOY_TOKEN_OPERATOR_SEMICOLON) || match(parser, TOY_TOKEN_KEYWORD_PASS)) {
|
||||||
Toy_private_emitAstPass(bucketHandle, rootHandle);
|
Toy_private_emitAstPass(bucketHandle, rootHandle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -913,9 +913,16 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void makeDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
static void makeDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle, bool errorOnEmpty) {
|
||||||
|
//disallow empty control flow bodies
|
||||||
|
if (errorOnEmpty && match(parser, TOY_TOKEN_OPERATOR_SEMICOLON)) {
|
||||||
|
printError(parser, parser->previous, "Empty control flow bodies are disallowed, use the 'pass' keyword");
|
||||||
|
Toy_private_emitAstError(bucketHandle, rootHandle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//variable declarations
|
//variable declarations
|
||||||
if (match(parser, TOY_TOKEN_KEYWORD_VAR)) {
|
else if (match(parser, TOY_TOKEN_KEYWORD_VAR)) {
|
||||||
makeVariableDeclarationStmt(bucketHandle, parser, rootHandle);
|
makeVariableDeclarationStmt(bucketHandle, parser, rootHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -938,7 +945,7 @@ static void makeBlockStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast
|
|||||||
while (parser->current.type != TOY_TOKEN_OPERATOR_BRACE_RIGHT && !match(parser, TOY_TOKEN_EOF)) {
|
while (parser->current.type != TOY_TOKEN_OPERATOR_BRACE_RIGHT && !match(parser, TOY_TOKEN_EOF)) {
|
||||||
//process the grammar rules
|
//process the grammar rules
|
||||||
Toy_Ast* stmt = NULL;
|
Toy_Ast* stmt = NULL;
|
||||||
makeDeclarationStmt(bucketHandle, parser, &stmt);
|
makeDeclarationStmt(bucketHandle, parser, &stmt, false);
|
||||||
|
|
||||||
//if something went wrong
|
//if something went wrong
|
||||||
if (parser->panic) {
|
if (parser->panic) {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ typedef enum Toy_TokenType {
|
|||||||
TOY_TOKEN_KEYWORD_IMPORT,
|
TOY_TOKEN_KEYWORD_IMPORT,
|
||||||
TOY_TOKEN_KEYWORD_IN,
|
TOY_TOKEN_KEYWORD_IN,
|
||||||
TOY_TOKEN_KEYWORD_OF,
|
TOY_TOKEN_KEYWORD_OF,
|
||||||
|
TOY_TOKEN_KEYWORD_PASS,
|
||||||
TOY_TOKEN_KEYWORD_PRINT,
|
TOY_TOKEN_KEYWORD_PRINT,
|
||||||
TOY_TOKEN_KEYWORD_RETURN,
|
TOY_TOKEN_KEYWORD_RETURN,
|
||||||
TOY_TOKEN_KEYWORD_TYPEAS,
|
TOY_TOKEN_KEYWORD_TYPEAS,
|
||||||
@@ -102,7 +103,6 @@ typedef enum Toy_TokenType {
|
|||||||
TOY_TOKEN_OPERATOR_PIPE, // |
|
TOY_TOKEN_OPERATOR_PIPE, // |
|
||||||
|
|
||||||
//meta tokens
|
//meta tokens
|
||||||
TOY_TOKEN_PASS,
|
|
||||||
TOY_TOKEN_ERROR,
|
TOY_TOKEN_ERROR,
|
||||||
TOY_TOKEN_EOF,
|
TOY_TOKEN_EOF,
|
||||||
} Toy_TokenType;
|
} Toy_TokenType;
|
||||||
|
|||||||
9
tests/integrations/test_control_flow.toy
Normal file
9
tests/integrations/test_control_flow.toy
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
//these are allowed
|
||||||
|
/* EMPTY */;
|
||||||
|
if (true) { }
|
||||||
|
if (true) pass;
|
||||||
|
|
||||||
|
|
||||||
|
//these are not allowed
|
||||||
|
// if (true) /* EMPTY */;
|
||||||
|
|
||||||
Reference in New Issue
Block a user