Disallow empty bodies in control flow statements, added 'pass' keyword

Fixed #170
This commit is contained in:
2025-01-09 11:03:03 +11:00
parent 9de9c85bea
commit 90ffe9b40e
4 changed files with 28 additions and 17 deletions

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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;

View File

@@ -0,0 +1,9 @@
//these are allowed
/* EMPTY */;
if (true) { }
if (true) pass;
//these are not allowed
// if (true) /* EMPTY */;