mirror of https://gitlab.com/nakst/essence
scripting engine: tuples for multiple return values
This commit is contained in:
parent
eebba8a873
commit
c7daea3dc5
356
util/script.c
356
util/script.c
|
@ -36,6 +36,10 @@
|
|||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// #define TRACE_COMMAND_EXECUTION
|
||||
|
||||
#define FUNCTION_MAX_ARGUMENTS (20) // Also the maximum number of return values in a tuple.
|
||||
|
||||
#define T_ERROR (0)
|
||||
#define T_EOF (1)
|
||||
#define T_IDENTIFIER (2)
|
||||
|
@ -88,6 +92,10 @@
|
|||
#define T_IMPORT_PATH (93)
|
||||
#define T_LIST_LITERAL (94)
|
||||
#define T_REPL_RESULT (95)
|
||||
#define T_RETURN_TUPLE (96)
|
||||
#define T_DECLARE_GROUP (97)
|
||||
#define T_DECL_GROUP_AND_SET (98)
|
||||
#define T_SET_GROUP (99)
|
||||
|
||||
#define T_EXIT_SCOPE (100)
|
||||
#define T_END_FUNCTION (101)
|
||||
|
@ -162,6 +170,7 @@
|
|||
#define T_IMPORT (180)
|
||||
#define T_INLINE (181)
|
||||
#define T_AWAIT (182)
|
||||
#define T_TUPLE (183)
|
||||
|
||||
#define STACK_READ_STRING(textVariable, bytesVariable, stackIndex) \
|
||||
if (context->c->stackPointer < stackIndex) return -1; \
|
||||
|
@ -768,6 +777,7 @@ uint8_t TokenLookupPrecedence(uint8_t t) {
|
|||
if (t == T_MINUS_EQUALS) return 10;
|
||||
if (t == T_ASTERISK_EQUALS) return 10;
|
||||
if (t == T_SLASH_EQUALS) return 10;
|
||||
if (t == T_COMMA) return 12;
|
||||
if (t == T_LOGICAL_OR) return 14;
|
||||
if (t == T_LOGICAL_AND) return 15;
|
||||
if (t == T_GREATER_THAN) return 20;
|
||||
|
@ -890,6 +900,7 @@ Token TokenNext(Tokenizer *tokenizer) {
|
|||
else if KEYWORD("str") token.type = T_STR;
|
||||
else if KEYWORD("struct") token.type = T_STRUCT;
|
||||
else if KEYWORD("true") token.type = T_TRUE;
|
||||
else if KEYWORD("tuple") token.type = T_TUPLE;
|
||||
else if KEYWORD("void") token.type = T_VOID;
|
||||
else if KEYWORD("while") token.type = T_WHILE;
|
||||
|
||||
|
@ -984,7 +995,7 @@ Token TokenPeek(Tokenizer *tokenizer) {
|
|||
return TokenNext(©);
|
||||
}
|
||||
|
||||
Node *ParseType(Tokenizer *tokenizer, bool maybe, bool allowVoid) {
|
||||
Node *ParseType(Tokenizer *tokenizer, bool maybe, bool allowVoid, bool allowTuple) {
|
||||
Node *node = (Node *) AllocateFixed(sizeof(Node));
|
||||
node->token = TokenNext(tokenizer);
|
||||
|
||||
|
@ -993,6 +1004,7 @@ Node *ParseType(Tokenizer *tokenizer, bool maybe, bool allowVoid) {
|
|||
|| node->token.type == T_STR
|
||||
|| node->token.type == T_BOOL
|
||||
|| node->token.type == T_VOID
|
||||
|| node->token.type == T_TUPLE
|
||||
|| node->token.type == T_IDENTIFIER) {
|
||||
node->type = node->token.type;
|
||||
|
||||
|
@ -1004,6 +1016,60 @@ Node *ParseType(Tokenizer *tokenizer, bool maybe, bool allowVoid) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (!allowTuple && node->type == T_TUPLE) {
|
||||
if (!maybe) {
|
||||
PrintError2(tokenizer, node, "The 'tuple' type is not allowed here.\n");
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (node->type == T_TUPLE) {
|
||||
Token token = TokenNext(tokenizer);
|
||||
|
||||
if (token.type != T_LEFT_SQUARE) {
|
||||
if (!maybe) PrintError2(tokenizer, node, "Expected a '[' after 'tuple'.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node **link = &node->firstChild;
|
||||
bool needComma = false;
|
||||
int count = 0;
|
||||
|
||||
while (true) {
|
||||
token = TokenPeek(tokenizer);
|
||||
|
||||
if (token.type == T_RIGHT_SQUARE) {
|
||||
TokenNext(tokenizer);
|
||||
break;
|
||||
} else if (needComma) {
|
||||
if (token.type == T_COMMA) {
|
||||
TokenNext(tokenizer);
|
||||
} else {
|
||||
if (!maybe) PrintError2(tokenizer, node, "Expected a ',' or ']' in the type list for 'tuple'.\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == FUNCTION_MAX_ARGUMENTS) {
|
||||
if (!maybe) PrintError2(tokenizer, node, "Too many values in the tuple (maximum is %d).\n", FUNCTION_MAX_ARGUMENTS);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node *n = ParseType(tokenizer, maybe, false, false);
|
||||
if (!n) return NULL;
|
||||
*link = n;
|
||||
link = &n->sibling;
|
||||
needComma = true;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (!node->firstChild || !node->firstChild->sibling) {
|
||||
if (!maybe) PrintError2(tokenizer, node, "A tuple must have at least two types in its list.\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
|
||||
while (true) {
|
||||
|
@ -1045,7 +1111,7 @@ Node *ParseType(Tokenizer *tokenizer, bool maybe, bool allowVoid) {
|
|||
|
||||
return node;
|
||||
} else if (!maybe) {
|
||||
PrintError2(tokenizer, node, "Expected a type. This can be 'int', 'float', 'bool', 'void', 'str', or an identifier.\n");
|
||||
PrintError2(tokenizer, node, "Expected a type. This can be 'int', 'float', 'bool', 'void', 'str', 'tuple', or an identifier.\n");
|
||||
return NULL;
|
||||
} else {
|
||||
return NULL;
|
||||
|
@ -1173,7 +1239,7 @@ Node *ParseExpression(Tokenizer *tokenizer, bool allowAssignment, uint8_t preced
|
|||
}
|
||||
} else if (node->token.type == T_NEW) {
|
||||
node->type = T_NEW;
|
||||
node->firstChild = ParseType(tokenizer, false, false);
|
||||
node->firstChild = ParseType(tokenizer, false, false /* allow void */, false /* allow tuple */);
|
||||
node->expressionType = node->firstChild;
|
||||
if (!node->firstChild) return NULL;
|
||||
} else if (node->token.type == T_LEFT_SQUARE) {
|
||||
|
@ -1299,6 +1365,14 @@ Node *ParseExpression(Tokenizer *tokenizer, bool allowAssignment, uint8_t preced
|
|||
PrintError2(tokenizer, &n, "Expected a matching closing bracket.\n");
|
||||
return NULL;
|
||||
}
|
||||
} else if (token.type == T_COMMA && TokenLookupPrecedence(token.type) >= precedence && allowAssignment) {
|
||||
Node *operation = (Node *) AllocateFixed(sizeof(Node));
|
||||
operation->token = TokenNext(tokenizer);
|
||||
operation->type = T_SET_GROUP;
|
||||
operation->firstChild = node;
|
||||
node->sibling = ParseExpression(tokenizer, true, TokenLookupPrecedence(token.type));
|
||||
if (!node->sibling) return NULL;
|
||||
node = operation;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -1367,12 +1441,9 @@ Node *ParseVariableDeclarationOrExpression(Tokenizer *tokenizer, bool replMode)
|
|||
Tokenizer copy = *tokenizer;
|
||||
bool isVariableDeclaration = false;
|
||||
|
||||
if (ParseType(tokenizer, true, false) && TokenNext(tokenizer).type == T_IDENTIFIER) {
|
||||
Token equalsOrSemicolon = TokenNext(tokenizer);
|
||||
|
||||
if (equalsOrSemicolon.type == T_EQUALS || equalsOrSemicolon.type == T_SEMICOLON) {
|
||||
isVariableDeclaration = true;
|
||||
}
|
||||
if (ParseType(tokenizer, true, false /* allow void */, false /* allow tuple */) && TokenNext(tokenizer).type == T_IDENTIFIER) {
|
||||
Token token = TokenNext(tokenizer);
|
||||
isVariableDeclaration = token.type == T_EQUALS || token.type == T_SEMICOLON || token.type == T_COMMA;
|
||||
}
|
||||
|
||||
if (tokenizer->error) {
|
||||
|
@ -1390,14 +1461,15 @@ Node *ParseVariableDeclarationOrExpression(Tokenizer *tokenizer, bool replMode)
|
|||
|
||||
Node *declaration = (Node *) AllocateFixed(sizeof(Node));
|
||||
declaration->type = T_DECLARE;
|
||||
declaration->firstChild = ParseType(tokenizer, false, false);
|
||||
declaration->firstChild = ParseType(tokenizer, false, false /* allow void */, false /* allow tuple */);
|
||||
Assert(declaration->firstChild);
|
||||
declaration->token = TokenNext(tokenizer);
|
||||
Assert(declaration->token.type == T_IDENTIFIER);
|
||||
Token equalsOrSemicolon = TokenNext(tokenizer);
|
||||
Assert(equalsOrSemicolon.type == T_EQUALS || equalsOrSemicolon.type == T_SEMICOLON);
|
||||
|
||||
if (equalsOrSemicolon.type == T_EQUALS) {
|
||||
Token token = TokenNext(tokenizer);
|
||||
Assert(token.type == T_EQUALS || token.type == T_SEMICOLON || token.type == T_COMMA);
|
||||
|
||||
if (token.type == T_EQUALS) {
|
||||
declaration->firstChild->sibling = ParseExpression(tokenizer, false, 0);
|
||||
if (!declaration->firstChild->sibling) return NULL;
|
||||
Token semicolon = TokenNext(tokenizer);
|
||||
|
@ -1406,6 +1478,53 @@ Node *ParseVariableDeclarationOrExpression(Tokenizer *tokenizer, bool replMode)
|
|||
PrintError2(tokenizer, declaration, "Expected a semicolon at the end of the variable declaration.\n");
|
||||
return NULL;
|
||||
}
|
||||
} else if (token.type == T_COMMA) {
|
||||
Node *group = (Node *) AllocateFixed(sizeof(Node));
|
||||
group->type = T_DECLARE_GROUP;
|
||||
group->firstChild = declaration;
|
||||
group->token = declaration->token;
|
||||
Node **link = &declaration->sibling;
|
||||
declaration = group;
|
||||
|
||||
while (true) {
|
||||
Node *declaration = (Node *) AllocateFixed(sizeof(Node));
|
||||
*link = declaration;
|
||||
link = &declaration->sibling;
|
||||
declaration->type = T_DECLARE;
|
||||
declaration->firstChild = ParseType(tokenizer, false, false /* allow void */, false /* allow tuple */);
|
||||
if (!declaration->firstChild) return NULL;
|
||||
declaration->token = TokenNext(tokenizer);
|
||||
|
||||
if (declaration->token.type != T_IDENTIFIER) {
|
||||
PrintError2(tokenizer, declaration, "Expected an identifier for the variable name.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Token token = TokenNext(tokenizer);
|
||||
|
||||
if (token.type == T_ERROR) {
|
||||
return NULL;
|
||||
} else if (token.type == T_COMMA) {
|
||||
// Keep going...
|
||||
} else if (token.type == T_SEMICOLON) {
|
||||
break;
|
||||
} else if (token.type == T_EQUALS) {
|
||||
group->type = T_DECL_GROUP_AND_SET;
|
||||
*link = ParseExpression(tokenizer, false, 0);
|
||||
if (!(*link)) return NULL;
|
||||
Token semicolon = TokenNext(tokenizer);
|
||||
|
||||
if (semicolon.type != T_SEMICOLON) {
|
||||
PrintError2(tokenizer, declaration, "Expected a semicolon at the end of the variable declaration group.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
break;
|
||||
} else {
|
||||
PrintError2(tokenizer, declaration, "Expected one of ',', '=' or ';' in the variable declaration group.\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return declaration;
|
||||
|
@ -1534,14 +1653,31 @@ Node *ParseBlock(Tokenizer *tokenizer, bool replMode) {
|
|||
Node *node = (Node *) AllocateFixed(sizeof(Node));
|
||||
node->type = token.type;
|
||||
node->token = TokenNext(tokenizer);
|
||||
*link = node;
|
||||
link = &node->sibling;
|
||||
|
||||
if (token.type == T_ASSERT || TokenPeek(tokenizer).type != T_SEMICOLON) {
|
||||
node->firstChild = ParseExpression(tokenizer, false, 0);
|
||||
if (!node->firstChild) return NULL;
|
||||
|
||||
if (token.type == T_RETURN && TokenPeek(tokenizer).type == T_COMMA) {
|
||||
Node *n = node;
|
||||
node = (Node *) AllocateFixed(sizeof(Node));
|
||||
node->type = T_RETURN_TUPLE;
|
||||
node->firstChild = n->firstChild;
|
||||
Node **argumentLink = &n->firstChild->sibling;
|
||||
|
||||
while (TokenPeek(tokenizer).type == T_COMMA) {
|
||||
TokenNext(tokenizer);
|
||||
n = ParseExpression(tokenizer, false, 0);
|
||||
if (!n) return NULL;
|
||||
*argumentLink = n;
|
||||
argumentLink = &n->sibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*link = node;
|
||||
link = &node->sibling;
|
||||
|
||||
Token semicolon = TokenNext(tokenizer);
|
||||
|
||||
if (semicolon.type != T_SEMICOLON) {
|
||||
|
@ -1564,7 +1700,7 @@ Node *ParseBlock(Tokenizer *tokenizer, bool replMode) {
|
|||
}
|
||||
|
||||
Node *ParseGlobalVariableOrFunctionDefinition(Tokenizer *tokenizer, bool allowGlobalVariables, bool parseFunctionBody) {
|
||||
Node *type = ParseType(tokenizer, false, true);
|
||||
Node *type = ParseType(tokenizer, false, true /* allow void */, true /* allow tuple */);
|
||||
|
||||
if (!type) {
|
||||
return NULL;
|
||||
|
@ -1620,14 +1756,13 @@ Node *ParseGlobalVariableOrFunctionDefinition(Tokenizer *tokenizer, bool allowGl
|
|||
|
||||
Node *argument = (Node *) AllocateFixed(sizeof(Node));
|
||||
argument->type = T_ARGUMENT;
|
||||
argument->firstChild = ParseType(tokenizer, false, false);
|
||||
argument->firstChild = ParseType(tokenizer, false, false /* allow void */, false /* allow tuple */);
|
||||
if (!argument->firstChild) return NULL;
|
||||
argument->token = TokenNext(tokenizer);
|
||||
*link = argument;
|
||||
link = &argument->sibling;
|
||||
argumentCount++;
|
||||
|
||||
#define FUNCTION_MAX_ARGUMENTS (20)
|
||||
if (argumentCount > FUNCTION_MAX_ARGUMENTS) {
|
||||
PrintError2(tokenizer, argument, "Functions cannot have more than %d arguments.\n", FUNCTION_MAX_ARGUMENTS);
|
||||
return NULL;
|
||||
|
@ -1767,6 +1902,7 @@ Node *ParseRoot(Tokenizer *tokenizer) {
|
|||
if (token.type == T_ERROR) {
|
||||
return NULL;
|
||||
} else if (token.type == T_EOF) {
|
||||
// ScriptPrintNode(root, 0);
|
||||
return root;
|
||||
} else if (token.type == T_FUNCTYPE) {
|
||||
TokenNext(tokenizer);
|
||||
|
@ -1806,7 +1942,7 @@ Node *ParseRoot(Tokenizer *tokenizer) {
|
|||
Token peek = TokenPeek(tokenizer);
|
||||
if (peek.type == T_ERROR) return NULL;
|
||||
if (peek.type == T_RIGHT_FANCY) break;
|
||||
Node *type = ParseType(tokenizer, false, true);
|
||||
Node *type = ParseType(tokenizer, false, false /* allow void */, false /* allow tuple */);
|
||||
if (!type) return NULL;
|
||||
Node *field = (Node *) AllocateFixed(sizeof(Node));
|
||||
field->token = TokenNext(tokenizer);
|
||||
|
@ -2211,6 +2347,17 @@ bool ASTIsManagedType(Node *node) {
|
|||
return node->type == T_STR || node->type == T_LIST || node->type == T_STRUCT || node->type == T_FUNCPTR || node->type == T_FUNCPTR;
|
||||
}
|
||||
|
||||
int ASTGetTypePopCount(Node *node) {
|
||||
if (node->type == T_TUPLE) {
|
||||
int count = 0;
|
||||
Node *child = node->firstChild;
|
||||
while (child) { count++; child = child->sibling; }
|
||||
return count;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool ASTLookupTypeIdentifiers(Tokenizer *tokenizer, Node *node) {
|
||||
Node *child = node->firstChild;
|
||||
|
||||
|
@ -2276,12 +2423,12 @@ bool ASTSetTypes(Tokenizer *tokenizer, Node *node) {
|
|||
}
|
||||
|
||||
if (node->type == T_ROOT || node->type == T_BLOCK
|
||||
|| node->type == T_INT || node->type == T_FLOAT || node->type == T_STR || node->type == T_LIST
|
||||
|| node->type == T_INT || node->type == T_FLOAT || node->type == T_STR || node->type == T_LIST || node->type == T_TUPLE
|
||||
|| node->type == T_BOOL || node->type == T_VOID || node->type == T_IDENTIFIER
|
||||
|| node->type == T_ARGUMENTS || node->type == T_ARGUMENT
|
||||
|| node->type == T_STRUCT || node->type == T_FUNCTYPE || node->type == T_IMPORT || node->type == T_IMPORT_PATH
|
||||
|| node->type == T_FUNCPTR || node->type == T_FUNCBODY || node->type == T_FUNCTION
|
||||
|| node->type == T_REPL_RESULT) {
|
||||
|| node->type == T_REPL_RESULT || node->type == T_DECLARE_GROUP) {
|
||||
} else if (node->type == T_NUMERIC_LITERAL) {
|
||||
size_t dotCount = 0;
|
||||
|
||||
|
@ -2375,12 +2522,66 @@ bool ASTSetTypes(Tokenizer *tokenizer, Node *node) {
|
|||
}
|
||||
} else if (node->type == T_DECLARE) {
|
||||
if (node->firstChild->sibling && !ASTMatching(node->firstChild, node->firstChild->sibling->expressionType)) {
|
||||
ScriptPrintNode(node->firstChild, 0);
|
||||
ScriptPrintNode(node->firstChild->sibling->expressionType, 0);
|
||||
|
||||
PrintError2(tokenizer, node, "The type of the variable being assigned does not match the expression.\n");
|
||||
return false;
|
||||
}
|
||||
} else if (node->type == T_DECL_GROUP_AND_SET) {
|
||||
Node *lastChild = node->firstChild;
|
||||
while (lastChild->sibling) lastChild = lastChild->sibling;
|
||||
Node *tuple = lastChild->expressionType;
|
||||
|
||||
if (tuple->type != T_TUPLE) {
|
||||
PrintError2(tokenizer, node, "You can only use '=' in the declaration group with a function that returns a tuple.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
Node *child = node->firstChild;
|
||||
Node *item = tuple->firstChild;
|
||||
int index = 1;
|
||||
|
||||
while (child->sibling && item) {
|
||||
if (!ASTMatching(child->expressionType, item)) {
|
||||
PrintError2(tokenizer, node, "The type of value %d in the tuple does not match the declaration type.\n", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
child = child->sibling;
|
||||
item = item->sibling;
|
||||
index++;
|
||||
}
|
||||
|
||||
if ((!item && child->sibling) || (item && !child->sibling)) {
|
||||
PrintError2(tokenizer, node, "The number of declarations in the group does not match the number of values in the tuple.\n");
|
||||
return false;
|
||||
}
|
||||
} else if (node->type == T_SET_GROUP) {
|
||||
if (node->parent->type == T_SET_GROUP) {
|
||||
// Reattach our children at the end of the parent's children.
|
||||
Assert(!node->sibling);
|
||||
Node *child = node->parent->firstChild;
|
||||
while (child->sibling != node) child = child->sibling;
|
||||
child->sibling = node->firstChild;
|
||||
} else if (node->parent->type == T_EQUALS) {
|
||||
Node *expressionType = (Node *) AllocateFixed(sizeof(Node));
|
||||
expressionType->type = T_TUPLE;
|
||||
node->expressionType = expressionType;
|
||||
|
||||
Node *child = node->firstChild;
|
||||
Node **link = &expressionType->firstChild;
|
||||
|
||||
while (child) {
|
||||
Node *copy = (Node *) AllocateFixed(sizeof(Node));
|
||||
*copy = *child->expressionType;
|
||||
*link = copy;
|
||||
link = &(*link)->sibling;
|
||||
child = child->sibling;
|
||||
}
|
||||
|
||||
*link = NULL;
|
||||
} else {
|
||||
PrintError2(tokenizer, node, "A comma separated list of expressions can only be used to assign a tuple return value to variables.\n");
|
||||
return false;
|
||||
}
|
||||
} else if (node->type == T_EQUALS) {
|
||||
if (!ASTMatching(node->firstChild->expressionType, node->firstChild->sibling->expressionType)) {
|
||||
PrintError2(tokenizer, node, "The type of the variable being assigned does not match the expression.\n");
|
||||
|
@ -2438,6 +2639,40 @@ bool ASTSetTypes(Tokenizer *tokenizer, Node *node) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!ASTMatching(expressionType, returnType)) {
|
||||
PrintError2(tokenizer, node, "The type of the expression does not match the declared return type of the function.\n");
|
||||
return false;
|
||||
}
|
||||
} else if (node->type == T_RETURN_TUPLE) {
|
||||
Node *expressionType = (Node *) AllocateFixed(sizeof(Node));
|
||||
expressionType->type = T_TUPLE;
|
||||
|
||||
Node *child = node->firstChild;
|
||||
Node **link = &expressionType->firstChild;
|
||||
|
||||
while (child) {
|
||||
Node *copy = (Node *) AllocateFixed(sizeof(Node));
|
||||
*copy = *child->expressionType;
|
||||
*link = copy;
|
||||
link = &(*link)->sibling;
|
||||
child = child->sibling;
|
||||
}
|
||||
|
||||
*link = NULL;
|
||||
|
||||
Node *function = node->parent;
|
||||
|
||||
while (function->type != T_FUNCTION) {
|
||||
function = function->parent;
|
||||
}
|
||||
|
||||
Node *returnType = function->firstChild->firstChild->sibling;
|
||||
|
||||
if (ASTMatching(returnType, &globalExpressionTypeVoid)) {
|
||||
PrintError2(tokenizer, node, "The function does not return a value ('void'), but the return statement has a return value.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ASTMatching(expressionType, returnType)) {
|
||||
PrintError2(tokenizer, node, "The type of the expression does not match the declared return type of the function.\n");
|
||||
return false;
|
||||
|
@ -2575,6 +2810,12 @@ bool ASTSetTypes(Tokenizer *tokenizer, Node *node) {
|
|||
PrintError2(tokenizer, node, "The return value cannot be discarded from a function that already returns void.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expressionType->firstChild->sibling && expressionType->firstChild->sibling->type == T_TUPLE) {
|
||||
// TODO Remove this restriction?
|
||||
PrintError2(tokenizer, node, "The discard operation cannot be used on a function returning a tuple.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
node->expressionType = (Node *) AllocateFixed(sizeof(Node));
|
||||
|
@ -2729,7 +2970,7 @@ bool ASTCheckForReturnStatements(Tokenizer *tokenizer, Node *node) {
|
|||
lastStatement = lastStatement->sibling;
|
||||
}
|
||||
|
||||
if (lastStatement && lastStatement->type == T_RETURN) {
|
||||
if (lastStatement && (lastStatement->type == T_RETURN || lastStatement->type == T_RETURN_TUPLE)) {
|
||||
return true;
|
||||
} else if (lastStatement && (lastStatement->type == T_IF || lastStatement->type == T_BLOCK)) {
|
||||
return ASTCheckForReturnStatements(tokenizer, lastStatement);
|
||||
|
@ -2888,22 +3129,50 @@ bool FunctionBuilderRecurse(Tokenizer *tokenizer, Node *node, FunctionBuilder *b
|
|||
FunctionBuilderAppend(builder, &isManaged, sizeof(isManaged));
|
||||
}
|
||||
}
|
||||
} else if (node->type == T_EQUALS || node->type == T_DECLARE) {
|
||||
} else if (node->type == T_EQUALS || node->type == T_DECLARE || node->type == T_DECL_GROUP_AND_SET) {
|
||||
if (node->firstChild->sibling) {
|
||||
if (!FunctionBuilderRecurse(tokenizer, node->firstChild->sibling, builder, false)) return false;
|
||||
builder->isPersistentVariable = false;
|
||||
Node *variables[FUNCTION_MAX_ARGUMENTS];
|
||||
uintptr_t variableCount = 0;
|
||||
bool setGroup = node->type == T_EQUALS && node->firstChild->type == T_SET_GROUP;
|
||||
Node *lastChild = setGroup ? node->firstChild->firstChild : node->firstChild;
|
||||
|
||||
if (node->type == T_DECLARE) {
|
||||
if (!FunctionBuilderVariable(tokenizer, builder, node, true)) return false;
|
||||
while (setGroup ? lastChild : lastChild->sibling) {
|
||||
Assert(variableCount != FUNCTION_MAX_ARGUMENTS);
|
||||
variables[variableCount++] = lastChild;
|
||||
lastChild = lastChild->sibling;
|
||||
}
|
||||
|
||||
else if (!FunctionBuilderRecurse(tokenizer, node->firstChild, builder, true)) return false;
|
||||
FunctionBuilderAddLineNumber(builder, node);
|
||||
uint8_t b = builder->isListAssignment ? T_EQUALS_LIST : builder->isDotAssignment ? T_EQUALS_DOT : T_EQUALS;
|
||||
FunctionBuilderAppend(builder, &b, sizeof(b));
|
||||
if (!builder->isListAssignment) FunctionBuilderAppend(builder, &builder->scopeIndex, sizeof(builder->scopeIndex));
|
||||
b = T_PERSIST;
|
||||
if (builder->isPersistentVariable) FunctionBuilderAppend(builder, &b, sizeof(b));
|
||||
if (!FunctionBuilderRecurse(tokenizer, setGroup ? node->firstChild->sibling : lastChild, builder, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (variableCount) {
|
||||
Node *child = variables[--variableCount];
|
||||
builder->isPersistentVariable = false;
|
||||
|
||||
if (node->type == T_DECLARE || node->type == T_DECL_GROUP_AND_SET) {
|
||||
if (!FunctionBuilderVariable(tokenizer, builder, node->type == T_DECL_GROUP_AND_SET ? child : node, true)) {
|
||||
return false;
|
||||
}
|
||||
} else if (!FunctionBuilderRecurse(tokenizer, child, builder, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FunctionBuilderAddLineNumber(builder, node);
|
||||
uint8_t b = builder->isListAssignment ? T_EQUALS_LIST : builder->isDotAssignment ? T_EQUALS_DOT : T_EQUALS;
|
||||
FunctionBuilderAppend(builder, &b, sizeof(b));
|
||||
|
||||
if (!builder->isListAssignment) {
|
||||
FunctionBuilderAppend(builder, &builder->scopeIndex, sizeof(builder->scopeIndex));
|
||||
}
|
||||
|
||||
if (builder->isPersistentVariable) {
|
||||
b = T_PERSIST;
|
||||
FunctionBuilderAppend(builder, &b, sizeof(b));
|
||||
}
|
||||
|
||||
child = child->sibling;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2970,7 +3239,10 @@ bool FunctionBuilderRecurse(Tokenizer *tokenizer, Node *node, FunctionBuilder *b
|
|||
if (increment->expressionType && increment->expressionType->type != T_VOID) {
|
||||
if (increment->type == T_CALL) {
|
||||
uint8_t b = T_POP;
|
||||
FunctionBuilderAppend(builder, &b, sizeof(b));
|
||||
|
||||
for (int i = 0; i < ASTGetTypePopCount(increment->expressionType); i++) {
|
||||
FunctionBuilderAppend(builder, &b, sizeof(b));
|
||||
}
|
||||
} else {
|
||||
PrintError2(tokenizer, increment, "The result of the expression is unused.\n");
|
||||
return false;
|
||||
|
@ -3113,7 +3385,10 @@ bool FunctionBuilderRecurse(Tokenizer *tokenizer, Node *node, FunctionBuilder *b
|
|||
|| (child->type == T_COLON && child->operationType == T_OP_FIND_AND_DELETE)
|
||||
|| (child->type == T_COLON && child->operationType == T_OP_FIND_AND_DEL_STR)) {
|
||||
uint8_t b = T_POP;
|
||||
FunctionBuilderAppend(builder, &b, sizeof(b));
|
||||
|
||||
for (int i = 0; i < ASTGetTypePopCount(child->expressionType); i++) {
|
||||
FunctionBuilderAppend(builder, &b, sizeof(b));
|
||||
}
|
||||
} else if (child->type == T_DECLARE || child->type == T_EQUALS) {
|
||||
} else {
|
||||
PrintError2(tokenizer, child, "The result of the expression is unused.\n");
|
||||
|
@ -3132,7 +3407,8 @@ bool FunctionBuilderRecurse(Tokenizer *tokenizer, Node *node, FunctionBuilder *b
|
|||
FunctionBuilderAppend(builder, &entryCount, sizeof(entryCount));
|
||||
b = T_END_FUNCTION;
|
||||
if (node->type == T_FUNCBODY) FunctionBuilderAppend(builder, &b, sizeof(b));
|
||||
} else if (node->type == T_RETURN) {
|
||||
} else if (node->type == T_DECLARE_GROUP) {
|
||||
} else if (node->type == T_RETURN || node->type == T_RETURN_TUPLE) {
|
||||
uint8_t b = T_END_FUNCTION;
|
||||
FunctionBuilderAddLineNumber(builder, node);
|
||||
FunctionBuilderAppend(builder, &b, sizeof(b));
|
||||
|
|
Loading…
Reference in New Issue