lci/parser.c

4434 lines
105 KiB
C

#include "parser.h"
#ifdef DEBUG
static unsigned int shiftwidth = 0;
void shiftout(void) { shiftwidth += 4; }
void shiftin(void) { shiftwidth -= 4; }
void debug(const char *info)
{
int n;
for (n = 0; n < shiftwidth; n++)
fprintf(stderr, " ");
fprintf(stderr, "%s\n", info);
}
#endif
/**
* Creates the main code block of a program.
*
* \param [in] block The first code block to execute.
*
* \return A pointer to the main code block with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
MainNode *createMainNode(BlockNode *block)
{
MainNode *p = malloc(sizeof(MainNode));
if (!p) {
perror("malloc");
return NULL;
}
p->block = block;
return p;
}
/**
* Deletes the main code block of a program.
*
* \param [in,out] node The main code block to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteMainNode(MainNode *node)
{
if (!node) return;
deleteBlockNode(node->block);
free(node);
}
/**
* Creates a code block.
*
* \param [in] stmts The list of statements which comprise the code block.
*
* \return A pointer to the code block with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
BlockNode *createBlockNode(StmtNodeList *stmts)
{
BlockNode *p = malloc(sizeof(BlockNode));
if (!p) {
perror("malloc");
return NULL;
}
p->stmts = stmts;
return p;
}
/**
* Deletes a code block.
*
* \param [in,out] node The code block to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteBlockNode(BlockNode *node)
{
if (!node) return;
deleteStmtNodeList(node->stmts);
free(node);
}
/**
* Creates an empty code block list.
*
* \return A pointer to an empty code block list.
*
* \retval NULL Memory allocation failed.
*/
BlockNodeList *createBlockNodeList(void)
{
BlockNodeList *p = malloc(sizeof(BlockNodeList));
if (!p) {
perror("malloc");
return NULL;
}
p->num = 0;
p->blocks = NULL;
return p;
}
/**
* Adds a code block to a list.
*
* \param [in,out] list The code block list to add \a node to.
*
* \param [in] node The code block to add to \a list.
*
* \post \a node will be added to \a list and its size will be updated.
*
* \retval 0 Memory allocation failed.
*
* \retval 1 \a node was added to \a list.
*/
int addBlockNode(BlockNodeList *list,
BlockNode *node)
{
unsigned int newsize = list->num + 1;
void *mem = realloc(list->blocks, sizeof(BlockNode *) * newsize);
if (!mem) {
perror("realloc");
return 0;
}
list->blocks = mem;
list->blocks[list->num] = node;
list->num = newsize;
return 1;
}
/**
* Deletes a code block list.
*
* \param [in,out] list The code block list to delete.
*
* \post The memory at \a list and all of its members will be freed.
*/
void deleteBlockNodeList(BlockNodeList *list)
{
unsigned int n;
if (!list) return;
for (n = 0; n < list->num; n++)
deleteBlockNode(list->blocks[n]);
free(list->blocks);
free(list);
}
/**
* Creates a boolean constant.
*
* \param [in] data The boolean constant value.
*
* \return A pointer to a boolean constant which will be FALSE if \a data is \c
* 0 and TRUE otherwise.
*
* \retval NULL Memory allocation failed.
*/
ConstantNode *createBooleanConstantNode(int data)
{
ConstantNode *p = malloc(sizeof(ConstantNode));
if (!p) {
perror("malloc");
return NULL;
}
p->type = CT_BOOLEAN;
p->data.i = (data != 0);
return p;
}
/**
* Creates an integer constant.
*
* \param [in] data The integer constant value.
*
* \return A pointer to an integer constant whose value is \a data.
*
* \retval NULL Memory allocation failed.
*/
ConstantNode *createIntegerConstantNode(long long data)
{
ConstantNode *p = malloc(sizeof(ConstantNode));
if (!p) {
perror("malloc");
return NULL;
}
p->type = CT_INTEGER;
p->data.i = data;
return p;
}
/**
* Creates a float constant.
*
* \param [in] data The float constant value.
*
* \return A pointer to a float constant whose value is \a data.
*
* \retval NULL Memory allocation failed.
*/
ConstantNode *createFloatConstantNode(float data)
{
ConstantNode *p = malloc(sizeof(ConstantNode));
if (!p) {
perror("malloc");
return NULL;
}
p->type = CT_FLOAT;
p->data.f = data;
return p;
}
/**
* Creates a string constant.
*
* \param [in] data The string constant value.
*
* \return A pointer to the string constant whose value is \a data.
*
* \retval NULL Memory allocation failed.
*/
ConstantNode *createStringConstantNode(char *data)
{
ConstantNode *p = malloc(sizeof(ConstantNode));
if (!p) {
perror("malloc");
return NULL;
}
p->type = CT_STRING;
p->data.s = data;
return p;
}
/**
* Deletes a constant.
*
* \param [in,out] node The constant to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteConstantNode(ConstantNode *node)
{
if (!node) return;
if (node->type == CT_STRING) free(node->data.s);
free(node);
}
/**
* Creates an indentifier.
*
* \param [in] type The type of the identifier \a id.
*
* \param [in] id The identifier data.
*
* \param [in] slot An optional slot to index.
*
* \param [in] fname The file containing the identifier.
*
* \param [in] line The line the identifier occurred on.
*
* \return A pointer to the identifier with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
IdentifierNode *createIdentifierNode(IdentifierType type,
void *id,
IdentifierNode *slot,
const char *fname,
unsigned int line)
{
IdentifierNode *p = malloc(sizeof(IdentifierNode));
if (!p) {
perror("malloc");
return NULL;
}
p->type = type;
p->id = id;
p->slot = slot;
if (fname) {
p->fname = malloc(sizeof(char) * (strlen(fname) + 1));
strcpy(p->fname, fname);
}
else {
p->fname = NULL;
}
p->line = line;
return p;
}
/**
* Deletes an identifier.
*
* \param [in,out] node The identifier to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteIdentifierNode(IdentifierNode *node)
{
if (!node) return;
switch (node->type) {
case IT_DIRECT: {
free(node->id);
break;
}
case IT_INDIRECT: {
deleteExprNode(node->id);
break;
}
default:
error(PR_UNKNOWN_IDENTIFIER_TYPE, node->fname, node->line);
break;
}
if (node->slot) deleteIdentifierNode(node->slot);
if (node->fname) free(node->fname);
free(node);
}
/**
* Creates an identifier list.
*
* \return A pointer to an identifier list.
*
* \retval NULL Memory allocation failed.
*/
IdentifierNodeList *createIdentifierNodeList(void)
{
IdentifierNodeList *p = malloc(sizeof(IdentifierNodeList));
if (!p) {
perror("malloc");
return NULL;
}
p->num = 0;
p->ids = NULL;
return p;
}
/**
* Adds an identifier to a list.
*
* \param [in,out] list The list of identifiers to add \a node to.
*
* \param [in] node The identifier to add to \a list.
*
* \post \a token will be added to the end of \a list and the size of \a list
* will be updated.
*
* \retval 0 Memory allocation failed.
*
* \retval 1 \a node was added to \a list.
*/
int addIdentifierNode(IdentifierNodeList *list,
IdentifierNode *node)
{
unsigned int newsize = list->num + 1;
void *mem = realloc(list->ids, sizeof(IdentifierNode *) * newsize);
if (!mem) {
perror("realloc");
return 0;
}
list->ids = mem;
list->ids[list->num] = node;
list->num = newsize;
return 1;
}
/**
* Deletes an identifier list.
*
* \param [in,out] list The list of identifiers to delete.
*
* \post The memory at \a list and all of its members will be freed.
*/
void deleteIdentifierNodeList(IdentifierNodeList *list)
{
unsigned int n;
if (!list) return;
for (n = 0; n < list->num; n++)
deleteIdentifierNode(list->ids[n]);
free(list->ids);
free(list);
}
/**
* Creates a type.
*
* \param [in] type The type to create.
*
* \return A pointer to a new type with the desired properties.
*
* \retval NULL Memory allocatin failed.
*/
TypeNode *createTypeNode(ConstantType type)
{
TypeNode *p = malloc(sizeof(TypeNode));
if (!p) {
perror("malloc");
return NULL;
}
p->type = type;
return p;
}
/**
* Deletes a type.
*
* \param [in,out] node The type to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteTypeNode(TypeNode *node)
{
if (!node) return;
free(node);
}
/**
* Creates a statement.
*
* \param [in] type The type of statement.
*
* \param [in] stmt The statement data.
*
* \return A pointer to a statement with the desired properties.
*
* \retval NULL malloc was unable to allocate memory.
*/
StmtNode *createStmtNode(StmtType type,
void *stmt)
{
StmtNode *p = malloc(sizeof(StmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->type = type;
p->stmt = stmt;
return p;
}
/**
* Deletes a statement.
*
* \param [in,out] node The statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteStmtNode(StmtNode *node)
{
if (!node) return;
switch (node->type) {
case ST_CAST: {
CastStmtNode *stmt = (CastStmtNode *)node->stmt;
deleteCastStmtNode(stmt);
break;
}
case ST_PRINT: {
PrintStmtNode *stmt = (PrintStmtNode *)node->stmt;
deletePrintStmtNode(stmt);
break;
}
case ST_INPUT: {
InputStmtNode *stmt = (InputStmtNode *)node->stmt;
deleteInputStmtNode(stmt);
break;
}
case ST_ASSIGNMENT: {
AssignmentStmtNode *stmt = (AssignmentStmtNode *)node->stmt;
deleteAssignmentStmtNode(stmt);
break;
}
case ST_DECLARATION: {
DeclarationStmtNode *stmt = (DeclarationStmtNode *)node->stmt;
deleteDeclarationStmtNode(stmt);
break;
}
case ST_IFTHENELSE: {
IfThenElseStmtNode *stmt = (IfThenElseStmtNode *)node->stmt;
deleteIfThenElseStmtNode(stmt);
break;
}
case ST_SWITCH: {
SwitchStmtNode *stmt = (SwitchStmtNode *)node->stmt;
deleteSwitchStmtNode(stmt);
break;
}
case ST_BREAK:
break; /* This statement type does not have any content */
case ST_RETURN: {
ReturnStmtNode *stmt = (ReturnStmtNode *)node->stmt;
deleteReturnStmtNode(stmt);
break;
}
case ST_LOOP: {
LoopStmtNode *stmt = (LoopStmtNode *)node->stmt;
deleteLoopStmtNode(stmt);
break;
}
case ST_DEALLOCATION: {
DeallocationStmtNode *stmt = (DeallocationStmtNode *)node->stmt;
deleteDeallocationStmtNode(stmt);
break;
}
case ST_FUNCDEF: {
FuncDefStmtNode *stmt = (FuncDefStmtNode *)node->stmt;
deleteFuncDefStmtNode(stmt);
break;
}
case ST_EXPR: {
ExprNode *expr = (ExprNode *)node->stmt;
deleteExprNode(expr);
break;
}
case ST_ALTARRAYDEF: {
AltArrayDefStmtNode *stmt = (AltArrayDefStmtNode *)node->stmt;
deleteAltArrayDefStmtNode(stmt);
break;
}
case ST_BINDING: {
BindingStmtNode *stmt = (BindingStmtNode *)node->stmt;
deleteBindingStmtNode(stmt);
break;
}
case ST_IMPORT: {
ImportStmtNode *stmt = (ImportStmtNode *)node->stmt;
deleteImportStmtNode(stmt);
break;
}
default:
error(PR_UNKNOWN_STATEMENT_TYPE);
break;
}
free(node);
}
/**
* Creates an empty statement list.
*
* \return A pointer to an empty statement list.
*
* \retval NULL Memory allocation failed.
*/
StmtNodeList *createStmtNodeList(void)
{
StmtNodeList *p = malloc(sizeof(StmtNodeList));
if (!p) {
perror("malloc");
return NULL;
}
p->num = 0;
p->stmts = NULL;
return p;
}
/**
* Adds a statement to a list.
*
* \param [in,out] list The statement list to add \a node to.
*
* \param [in] node The statement to add to \a list.
*
* \post \a node will be added to \a list and its size will be updated.
*
* \retval 0 Memory allocation failed.
*
* \retval 1 \a node was added to \a list.
*/
int addStmtNode(StmtNodeList *list,
StmtNode *node)
{
unsigned int newsize = list->num + 1;
void *mem = realloc(list->stmts, sizeof(StmtNode *) * newsize);
if (!mem) {
perror("realloc");
return 0;
}
list->stmts = mem;
list->stmts[list->num] = node;
list->num = newsize;
return 1;
}
/**
* Deletes a statement list.
*
* \param [in,out] list The statement list to delete.
*
* \post The memory at \a list and all of its members will be freed.
*/
void deleteStmtNodeList(StmtNodeList *list)
{
unsigned int n;
if (!list) return;
for (n = 0; n < list->num; n++)
deleteStmtNode(list->stmts[n]);
free(list->stmts);
free(list);
}
/**
* Creates a cast statement.
*
* \param [in] target The variable to cast to \a newtype.
*
* \param [in] newtype The type to cast \a target to.
*
* \return A pointer to a cast statement with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
CastStmtNode *createCastStmtNode(IdentifierNode *target,
TypeNode *newtype)
{
CastStmtNode *p = malloc(sizeof(CastStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->target = target;
p->newtype = newtype;
return p;
}
/**
* Deletes a cast statement.
*
* \param [in,out] node The cast statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteCastStmtNode(CastStmtNode *node)
{
if (!node) return;
deleteIdentifierNode(node->target);
deleteTypeNode(node->newtype);
free(node);
}
/**
* Creates a print statement.
*
* \param [in] args The expressions to print.
*
* \param [in] nonl Whether an ending newline should be surpressed.
*
* \return A pointer to the print statement with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
PrintStmtNode *createPrintStmtNode(ExprNodeList *args,
int nonl)
{
PrintStmtNode *p = malloc(sizeof(PrintStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->args = args;
p->nonl = nonl;
return p;
}
/**
* Deletes a print statement.
*
* \param [in,out] node The print statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deletePrintStmtNode(PrintStmtNode *node)
{
if (!node) return;
deleteExprNodeList(node->args);
free(node);
}
/**
* Creates an input statement.
*
* \param [in] target The variable to store input in.
*
* \return A pointer to an input statement with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
InputStmtNode *createInputStmtNode(IdentifierNode *target)
{
InputStmtNode *p = malloc(sizeof(InputStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->target = target;
return p;
}
/**
* Deletes an input statement.
*
* \param [in,out] node The input statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteInputStmtNode(InputStmtNode *node)
{
if (!node) return;
deleteIdentifierNode(node->target);
free(node);
}
/**
* Creates an assignment statement.
*
* \param [in] target The variable to store \a expr in.
*
* \param [in] expr The expression to store in \a target.
*
* \return A pointer to an assignment statement with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
AssignmentStmtNode *createAssignmentStmtNode(IdentifierNode *target,
ExprNode *expr)
{
AssignmentStmtNode *p = malloc(sizeof(AssignmentStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->target = target;
p->expr = expr;
return p;
}
/**
* Deletes an assignment statement.
*
* \param [in,out] node The assignment statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteAssignmentStmtNode(AssignmentStmtNode *node)
{
if (!node) return;
deleteIdentifierNode(node->target);
deleteExprNode(node->expr);
free(node);
}
/**
* Creates a declaration statement.
*
* \param [in] scope The scope to create the variable in.
*
* \param [in] target The variable to create.
*
* \param [in] expr An optional expression to initialize \a target to.
*
* \param [in] type An optional type to initialize \a target to.
*
* \param [in] parent The optional parent to inherit from.
*
* \return A pointer to a declaration statement with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
DeclarationStmtNode *createDeclarationStmtNode(IdentifierNode *scope,
IdentifierNode *target,
ExprNode *expr,
TypeNode *type,
IdentifierNode *parent)
{
DeclarationStmtNode *p = malloc(sizeof(DeclarationStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->scope = scope;
p->target = target;
p->expr = expr;
p->type = type;
p->parent = parent;
return p;
}
/**
* Deletes a declaration statement.
*
* \param [in,out] node The declaration statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteDeclarationStmtNode(DeclarationStmtNode *node)
{
if (!node) return;
deleteIdentifierNode(node->scope);
deleteIdentifierNode(node->target);
deleteExprNode(node->expr);
deleteTypeNode(node->type);
deleteIdentifierNode(node->parent);
free(node);
}
/**
* Creates an if/then/else statement.
*
* \param [in] yes The code block to execute if the \ref impvar "implicit
* variable" is \c true.
*
* \param [in] no The code block to execute if the \ref impvar "implicit
* variable" is \c false and all \a guards are \c false.
*
* \param [in] guards The expressions to test if the \ref impvar "implicit
* variable" is \c false.
*
* \param [in] blocks The code blocks to execute if the respective guard is \c
* true.
*
* \return A pointer to the if/then/else statement with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
IfThenElseStmtNode *createIfThenElseStmtNode(BlockNode *yes,
BlockNode *no,
ExprNodeList *guards,
BlockNodeList *blocks)
{
IfThenElseStmtNode *p = malloc(sizeof(IfThenElseStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->yes = yes;
p->no = no;
p->guards = guards;
p->blocks = blocks;
return p;
}
/**
* Deletes an if/then/else statement.
*
* \param [in,out] node The if/then/else statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteIfThenElseStmtNode(IfThenElseStmtNode *node)
{
if (!node) return;
deleteBlockNode(node->yes);
deleteBlockNode(node->no);
deleteExprNodeList(node->guards);
deleteBlockNodeList(node->blocks);
free(node);
}
/**
* Creates a switch statement.
*
* \param [in] guards The expressions to compare the the \ref impvar "implicit
* variable".
*
* \param [in] blocks The code blocks to execute if a respective guard matches
* the \ref impvar "implicit variable".
*
* \param [in] def The default code block to execute if none of the \a guards
* match the \ref impvar "implicit variable".
*
* \return A pointer to a switch statement with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
SwitchStmtNode *createSwitchStmtNode(ExprNodeList *guards,
BlockNodeList *blocks,
BlockNode *def)
{
SwitchStmtNode *p = malloc(sizeof(SwitchStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->guards = guards;
p->blocks = blocks;
p->def = def;
return p;
}
/**
* Deletes a switch statement.
*
* \param [in,out] node The switch statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteSwitchStmtNode(SwitchStmtNode *node)
{
if (!node) return;
deleteExprNodeList(node->guards);
deleteBlockNodeList(node->blocks);
deleteBlockNode(node->def);
free(node);
}
/**
* Creates a return statement.
*
* \param [in] value The return value.
*
* \return A pointer to a return statement of the desired properties.
*
* \retval NULL Memory allocation failed.
*/
ReturnStmtNode *createReturnStmtNode(ExprNode *value)
{
ReturnStmtNode *p = malloc(sizeof(ReturnStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->value = value;
return p;
}
/**
* Deletes a return statement.
*
* \param [in,out] node The return statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteReturnStmtNode(ReturnStmtNode *node)
{
if (!node) return;
deleteExprNode(node->value);
free(node);
}
/**
* Creates a loop statement.
*
* \param [in] name The name of the loop.
*
* \param [in] var The induction variable.
*
* \param [in] guard The expression to determine if the loop continues.
*
* \param [in] update The expression to update \a var using.
*
* \param [in] body The code block to execute with each loop iteration.
*
* \return A pointer to a loop statement with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
LoopStmtNode *createLoopStmtNode(IdentifierNode *name,
IdentifierNode *var,
ExprNode *guard,
ExprNode *update,
BlockNode *body)
{
LoopStmtNode *p = malloc(sizeof(LoopStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->name = name;
p->var = var;
p->guard = guard;
p->update = update;
p->body = body;
return p;
}
/**
* Deletes a loop statement.
*
* \param [in,out] node The loop statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteLoopStmtNode(LoopStmtNode *node)
{
if (!node) return;
deleteIdentifierNode(node->name);
deleteIdentifierNode(node->var);
deleteExprNode(node->guard);
deleteExprNode(node->update);
deleteBlockNode(node->body);
free(node);
}
/**
* Creates a deallocation statement.
*
* \param [in] target The variable to deallocate.
*
* \return A pointer to a deallocation statement with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
DeallocationStmtNode *createDeallocationStmtNode(IdentifierNode *target)
{
DeallocationStmtNode *p = malloc(sizeof(DeallocationStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->target = target;
return p;
}
/**
* Deletes a deallocation statement.
*
* \param [in,out] node The deallocation statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteDeallocationStmtNode(DeallocationStmtNode *node)
{
if (!node) return;
deleteIdentifierNode(node->target);
free(node);
}
/**
* Creates a function definition statement.
*
* \param [in] scope The scope to define the function in.
*
* \param [in] name The name of the function.
*
* \param [in] args The function arguments names.
*
* \param [in] body The function's code block.
*
* \return A pointer to a function definition statement with the desired
* properties.
*
* \retval NULL Memory allocation failed.
*/
FuncDefStmtNode *createFuncDefStmtNode(IdentifierNode *scope,
IdentifierNode *name,
IdentifierNodeList *args,
BlockNode *body)
{
FuncDefStmtNode *p = malloc(sizeof(FuncDefStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->scope = scope;
p->name = name;
p->args = args;
p->body = body;
return p;
}
/**
* Deletes a function definition statement.
*
* \param [in,out] node The function definition statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteFuncDefStmtNode(FuncDefStmtNode *node)
{
if (!node) return;
deleteIdentifierNode(node->scope);
deleteIdentifierNode(node->name);
deleteIdentifierNodeList(node->args);
deleteBlockNode(node->body);
free(node);
}
/**
* Creates an alternate array definition statement.
*
* \param [in] name The name of the array to define.
*
* \param [in] body The body of the array to define.
*
* \param [in] parent The optional parent to inherit from.
*
* \return A pointer to an array definition statement with the desired
* properties.
*
* \retval NULL Memory allocation failed.
*/
AltArrayDefStmtNode *createAltArrayDefStmtNode(IdentifierNode *name,
BlockNode *body,
IdentifierNode *parent)
{
AltArrayDefStmtNode *p = malloc(sizeof(AltArrayDefStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->name = name;
p->body = body;
p->parent = parent;
return p;
}
/**
* Deletes an alternate array definition statement.
*
* \param [in,out] node The alternate array definition statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteAltArrayDefStmtNode(AltArrayDefStmtNode *node)
{
if (!node) return;
deleteIdentifierNode(node->name);
deleteBlockNode(node->body);
free(node);
}
/**
* Creates a library import statement.
*
* \param [in] name The name of the library to import.
*
* \return A pointer to a library import statement with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
ImportStmtNode *createImportStmtNode(IdentifierNode *name)
{
ImportStmtNode *p = malloc(sizeof(ImportStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->name = name;
return p;
}
/**
* Deletes a library import statement.
*
* \param [in,out] node The library import statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteImportStmtNode(ImportStmtNode *node)
{
if (!node) return;
deleteIdentifierNode(node->name);
free(node);
}
/**
* Creates a binding statement.
*
* \param [in] binding A pointer to the function that defines the binding.
*
* \return A pointer to a binding statement with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
BindingStmtNode *createBindingStmtNode(struct returnobject *(*binding)(struct scopeobject *))
{
BindingStmtNode *p = malloc(sizeof(BindingStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->binding = binding;
return p;
}
/**
* Deletes a binding statement.
*
* \param [in,out] node The binding statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteBindingStmtNode(BindingStmtNode *node)
{
if (!node) return;
free(node);
}
/**
* Creates an expression.
*
* \param [in] type The type of expression.
*
* \param [in] expr The expression data.
*
* \return A pointer to an expression with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
ExprNode *createExprNode(ExprType type,
void *expr)
{
ExprNode *p = malloc(sizeof(ExprNode));
if (!p) {
perror("malloc");
return NULL;
}
p->type = type;
p->expr = expr;
return p;
}
/**
* Deletes an expression.
*
* \param [in,out] node The expression to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteExprNode(ExprNode *node)
{
if (!node) return;
switch (node->type) {
case ET_CAST:
deleteCastExprNode((CastExprNode *)node->expr);
break;
case ET_CONSTANT:
deleteConstantNode((ConstantNode *)node->expr);
break;
case ET_IDENTIFIER:
deleteIdentifierNode((IdentifierNode *)node->expr);
break;
case ET_FUNCCALL:
deleteFuncCallExprNode((FuncCallExprNode *)node->expr);
break;
case ET_OP:
deleteOpExprNode((OpExprNode *)node->expr);
break;
case ET_IMPVAR:
break; /* This expression type does not have any content */
case ET_SYSTEMCOMMAND:
deleteSystemCommandExprNode((SystemCommandExprNode *)node->expr);
break;
default:
error(PR_UNKNOWN_EXPRESSION_TYPE);
break;
}
free(node);
}
/**
* Creates an empty expression list.
*
* \return A pointer to an empty expression list.
*
* \retval NULL Memory allocation failed.
*/
ExprNodeList *createExprNodeList(void)
{
ExprNodeList *p = malloc(sizeof(ExprNodeList));
if (!p) {
perror("malloc");
return NULL;
}
p->num = 0;
p->exprs = NULL;
return p;
}
/**
* Adds an expression to a list.
*
* \param [in,out] list The expression list to add \a node to.
*
* \param [in] node The expression to add to \a list.
*
* \post \a node will be added to \a list and its size will be updated.
*
* \retval 0 Memory allocation failed.
*
* \retval 1 \a node was added to \a list.
*/
int addExprNode(ExprNodeList *list,
ExprNode *node)
{
unsigned int newsize = list->num + 1;
void *mem = realloc(list->exprs, sizeof(ExprNode *) * newsize);
if (!mem) {
perror("realloc");
return 0;
}
list->exprs = mem;
list->exprs[list->num] = node;
list->num = newsize;
return 1;
}
/**
* Deletes an expression list.
*
* \param [in,out] list The expression list to delete.
*
* \post The memory at \a list and all of its members will be freed.
*/
void deleteExprNodeList(ExprNodeList *list)
{
unsigned int n;
if (!list) return;
for (n = 0; n < list->num; n++)
deleteExprNode(list->exprs[n]);
free(list->exprs);
free(list);
}
/**
* Creates a cast expression.
*
* \param [in] target The expression to cast.
*
* \param [in] newtype The type to cast \a target to.
*
* \return A pointer to a cast expression with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
CastExprNode *createCastExprNode(ExprNode *target,
TypeNode *newtype)
{
CastExprNode *p = malloc(sizeof(CastExprNode));
if (!p) {
perror("malloc");
return NULL;
}
p->target = target;
p->newtype = newtype;
return p;
}
/**
* Deletes a cast expression.
*
* \param [in,out] node The cast expression to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteCastExprNode(CastExprNode *node)
{
if (!node) return;
deleteExprNode(node->target);
deleteTypeNode(node->newtype);
free(node);
}
/**
* Creates a function call expression.
*
* \param [in] scope The scope to lookup the function in.
*
* \param [in] name The name of the function.
*
* \param [in] args The arguments to supply the function.
*
* \return A pointer to a function call expression with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
FuncCallExprNode *createFuncCallExprNode(IdentifierNode *scope,
IdentifierNode *name,
ExprNodeList *args)
{
FuncCallExprNode *p = malloc(sizeof(FuncCallExprNode));
if (!p) {
perror("malloc");
return NULL;
}
p->scope = scope;
p->name = name;
p->args = args;
return p;
}
/**
* Creates a system command expression.
*
* \param [in] cmd The command to execute.
*
* \return A pointer to a system command expression with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
SystemCommandExprNode *createSystemCommandExprNode(ExprNode *cmd)
{
SystemCommandExprNode *p = malloc(sizeof(SystemCommandExprNode));
if (!p) {
perror("malloc");
return NULL;
}
p->cmd = cmd;
return p;
}
/**
* Deletes a function call expression.
*
* \param [in,out] node The function call expression to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteFuncCallExprNode(FuncCallExprNode *node)
{
if (!node) return;
deleteIdentifierNode(node->scope);
deleteIdentifierNode(node->name);
deleteExprNodeList(node->args);
free(node);
}
/**
* Deletes a system command expression.
*
* \param [in,out] node The system command expression to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteSystemCommandExprNode(SystemCommandExprNode *node)
{
if (!node) return;
deleteExprNode(node->cmd);
free(node);
}
/**
* Creates an operation expression.
*
* \param [in] type The type of operation to perform.
*
* \param [in] args The operands.
*
* \return A pointer to an operation expression with the desired properties.
*
* \retval NULL Memroy allocation failed.
*/
OpExprNode *createOpExprNode(OpType type,
ExprNodeList *args)
{
OpExprNode *p = malloc(sizeof(OpExprNode));
if (!p) {
perror("malloc");
return NULL;
}
p->type = type;
p->args = args;
return p;
}
/**
* Deletes an operation expression.
*
* \param [in,out] node The operation expression to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteOpExprNode(OpExprNode *node)
{
if (!node) return;
deleteExprNodeList(node->args);
free(node);
}
/**
* Checks if a type of token is at a position in a token list, and if so,
* advances the position.
*
* \param [in,out] tokenp The position in a token list to check.
*
* \param [in] token The type of token to check for.
*
* \post If the type of \a tokenp is not \a token, \a tokenp will remain
* unchanged, else, it will point to the token after the one matched.
*
* \retval 0 The type of \a tokenp is \a token.
*
* \retval 1 The type of \a tokenp is not \a token.
*/
int acceptToken(Token ***tokenp,
TokenType token)
{
Token **tokens = *tokenp;
if ((*tokens)->type != token) return 0;
tokens++;
*tokenp = tokens;
return 1;
}
/**
* Checks if a type of token is at a position in a token list.
*
* \param [in] tokenp The position in a token list to check.
*
* \param [in] token The type of token to check for.
*
* \post \a tokenp will remain unchanged.
*
* \retval 0 The type of \a tokenp is \a token.
*
* \retval 1 The type of \a tokenp is not \a token.
*/
int peekToken(Token ***tokenp,
TokenType token)
{
Token **tokens = *tokenp;
if ((*tokens)->type != token) return 0;
return 1;
}
/**
* Checks if a type of token is after a position in a token list.
*
* \param [in] tokenp The position in a token list to check after.
*
* \param [in] token The type of token to check for.
*
* \post \a tokenp will remain unchanged.
*
* \retval 0 The type of the token after \a tokenp is \a token.
*
* \retval 1 The type of the token after \a tokenp is not \a token.
*/
int nextToken(Token ***tokenp,
TokenType token)
{
Token **tokens = *tokenp;
if ((*(tokens + 1))->type != token) return 0;
return 1;
}
/**
* A simple wrapper around the global error printing function tailored to
* general parser errors.
*
* \param [in] type The type of error.
*
* \param [in] tokens The tokens being parsed when the error occurred.
*/
void parser_error(ErrorType type,
Token **tokens)
{
error(type, (*tokens)->fname, (*tokens)->line, (*tokens)->image);
}
/**
* A simple wrapper around the global error printing function tailored to
* expected token parser errors.
*
* \param [in] token The expected token's image.
*
* \param [in] tokens The tokens being parsed when the error occurred.
*/
void parser_error_expected_token(TokenType token,
Token **tokens)
{
error(PR_EXPECTED_TOKEN,
(*tokens)->fname,
(*tokens)->line,
keywords[token],
(*tokens)->image);
}
/**
* A simple wrapper around the global error printing function tailored to
* expected two token parser errors.
*
* \param [in] token1 The first expected token's image.
*
* \param [in] token2 The second expected token's image.
*
* \param [in] tokens The tokens being parsed when the error occurred.
*/
void parser_error_expected_either_token(TokenType token1,
TokenType token2,
Token **tokens)
{
error(PR_EXPECTED_TOKEN,
(*tokens)->fname,
(*tokens)->line,
keywords[token1],
keywords[token2],
(*tokens)->image);
}
/**
* Parses tokens into a constant.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a constant.
*
* \retval NULL Unable to parse.
*/
ConstantNode *parseConstantNode(Token ***tokenp)
{
ConstantNode *ret = NULL;
char *data = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
shiftout();
#endif
/* Boolean */
if (peekToken(&tokens, TT_BOOLEAN)) {
#ifdef DEBUG
debug("CT_BOOLEAN");
#endif
/* Create the ConstantNode structure */
ret = createBooleanConstantNode((*tokens)->data.i);
if (!ret) goto parseConstantNodeAbort;
/* This should succeed; it was checked for above */
status = acceptToken(&tokens, TT_BOOLEAN);
if (!status) {
parser_error(PR_EXPECTED_BOOLEAN, tokens);
goto parseConstantNodeAbort;
}
}
/* Integer */
else if (peekToken(&tokens, TT_INTEGER)) {
#ifdef DEBUG
debug("CT_INTEGER");
#endif
/* Create the ConstantNode structure */
ret = createIntegerConstantNode((*tokens)->data.i);
if (!ret) goto parseConstantNodeAbort;
/* This should succeed; it was checked for above */
status = acceptToken(&tokens, TT_INTEGER);
if (!status) {
parser_error(PR_EXPECTED_INTEGER, tokens);
goto parseConstantNodeAbort;
}
}
/* Float */
else if (peekToken(&tokens, TT_FLOAT)) {
#ifdef DEBUG
debug("CT_FLOAT");
#endif
/* Create the ConstantNode structure */
ret = createFloatConstantNode((*tokens)->data.f);
if (!ret) goto parseConstantNodeAbort;
/* This should succeed; it was checked for above */
status = acceptToken(&tokens, TT_FLOAT);
if (!status) {
parser_error(PR_EXPECTED_FLOAT, tokens);
goto parseConstantNodeAbort;
}
}
/* String */
else if (peekToken(&tokens, TT_STRING)) {
size_t len = strlen((*tokens)->image);
data = malloc(sizeof(char) * (len - 1));
if (!data) {
perror("malloc");
goto parseConstantNodeAbort;
}
strncpy(data, (*tokens)->image + 1, len - 2);
data[len - 2] = '\0';
#ifdef DEBUG
debug("CT_STRING");
#endif
/* Create the ConstantNode structure */
ret = createStringConstantNode(data);
if (!ret) goto parseConstantNodeAbort;
data = NULL;
/* This should succeed; it was checked for above */
status = acceptToken(&tokens, TT_STRING);
if (!status) {
parser_error(PR_EXPECTED_STRING, tokens);
goto parseConstantNodeAbort;
}
}
else {
parser_error(PR_EXPECTED_CONSTANT, tokens);
}
#ifdef DEBUG
shiftin();
#endif
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseConstantNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (data) free(data);
if (ret) deleteConstantNode(ret);
return NULL;
}
/**
* Parses tokens into a type.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a type.
*
* \retval NULL Unable to parse.
*/
TypeNode *parseTypeNode(Token ***tokenp)
{
TypeNode *ret = NULL;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
shiftout();
#endif
/* Nil */
if (acceptToken(&tokens, TT_NOOB)) {
#ifdef DEBUG
debug("CT_NIL");
#endif
ret = createTypeNode(CT_NIL);
if (!ret) goto parseTypeNodeAbort;
}
/* Boolean */
else if (acceptToken(&tokens, TT_TROOF)) {
#ifdef DEBUG
debug("CT_BOOLEAN");
#endif
ret = createTypeNode(CT_BOOLEAN);
if (!ret) goto parseTypeNodeAbort;
}
/* Integer */
else if (acceptToken(&tokens, TT_NUMBR)) {
#ifdef DEBUG
debug("CT_INTEGER");
#endif
ret = createTypeNode(CT_INTEGER);
if (!ret) goto parseTypeNodeAbort;
}
/* Float */
else if (acceptToken(&tokens, TT_NUMBAR)) {
#ifdef DEBUG
debug("CT_FLOAT");
#endif
ret = createTypeNode(CT_FLOAT);
if (!ret) goto parseTypeNodeAbort;
}
/* String */
else if (acceptToken(&tokens, TT_YARN)) {
#ifdef DEBUG
debug("CT_STRING");
#endif
ret = createTypeNode(CT_STRING);
if (!ret) goto parseTypeNodeAbort;
}
/* Associative array */
else if (acceptToken(&tokens, TT_BUKKIT)) {
#ifdef DEBUG
debug("CT_ARRAY");
#endif
ret = createTypeNode(CT_ARRAY);
if (!ret) goto parseTypeNodeAbort;
}
else {
parser_error(PR_EXPECTED_TYPE, tokens);
}
#ifdef DEBUG
shiftin();
#endif
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseTypeNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteTypeNode(ret);
return NULL;
}
/**
* Parses tokens into an identifier.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to an identifier.
*
* \retval NULL Unable to parse.
*/
IdentifierNode *parseIdentifierNode(Token ***tokenp)
{
IdentifierType type;
void *data = NULL;
IdentifierNode *slot = NULL;
const char *fname = NULL;
unsigned int line;
/* For direct identifier */
char *temp = NULL;
/* For indirect identifier */
ExprNode *expr = NULL;
IdentifierNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
shiftout();
#endif
fname = (*tokens)->fname;
line = (*tokens)->line;
/* Direct identifier */
if (peekToken(&tokens, TT_IDENTIFIER)) {
type = IT_DIRECT;
#ifdef DEBUG
debug("IT_DIRECT");
#endif
/* Copy the token image */
temp = malloc(sizeof(char) * (strlen((*tokens)->image) + 1));
if (!temp) goto parseIdentifierNodeAbort;
strcpy(temp, (*tokens)->image);
data = temp;
/* This should succeed; it was checked for above */
status = acceptToken(&tokens, TT_IDENTIFIER);
if (!status) {
parser_error(PR_EXPECTED_IDENTIFIER, tokens);
goto parseIdentifierNodeAbort;
}
}
else if (acceptToken(&tokens, TT_SRS)) {
type = IT_INDIRECT;
#ifdef DEBUG
debug("IT_INDIRECT");
#endif
/* Parse the expression representing the identifier */
expr = parseExprNode(&tokens);
if (!expr) goto parseIdentifierNodeAbort;
data = expr;
}
else {
parser_error(PR_EXPECTED_IDENTIFIER, tokens);
}
/* Check if there is a slot access */
if (acceptToken(&tokens, TT_APOSTROPHEZ)) {
slot = parseIdentifierNode(&tokens);
if (!slot) goto parseIdentifierNodeAbort;
}
#ifdef DEBUG
shiftin();
#endif
/* Create the new IdentifierNode structure */
ret = createIdentifierNode(type, data, slot, fname, line);
if (!ret) goto parseIdentifierNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseIdentifierNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteIdentifierNode(ret);
else {
/* For indirect identifier */
if (expr) deleteExprNode(expr);
/* For direct identifier */
if (temp) free(temp);
if (slot) deleteIdentifierNode(slot);
}
return NULL;
}
/**
* Parses tokens into a cast expression.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a cast expression.
*
* \retval NULL Unable to parse.
*/
ExprNode *parseCastExprNode(Token ***tokenp)
{
ExprNode *target = NULL;
TypeNode *newtype = NULL;
CastExprNode *expr = NULL;
ExprNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ET_CAST");
#endif
/* Parse the cast token */
status = acceptToken(&tokens, TT_MAEK);
if (!status) {
parser_error_expected_token(TT_MAEK, tokens);
goto parseCastExprNodeAbort;
}
/* Parse the expression to cast */
target = parseExprNode(&tokens);
if (!target) goto parseCastExprNodeAbort;
/* Optionally parse the cast-to token */
status = acceptToken(&tokens, TT_A);
/* Parse the type to cast to */
newtype = parseTypeNode(&tokens);
if (!newtype) goto parseCastExprNodeAbort;
/* Create the new CastExprNode structure */
expr = createCastExprNode(target, newtype);
if (!expr) goto parseCastExprNodeAbort;
/* Create the new ExprNode structure */
ret = createExprNode(ET_CAST, expr);
if (!ret) goto parseCastExprNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseCastExprNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteExprNode(ret);
else if (expr) deleteCastExprNode(expr);
else {
if (newtype) deleteTypeNode(newtype);
if (target) deleteExprNode(target);
}
return NULL;
}
/**
* Parses tokens into a constant expression.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a constant expression.
*
* \retval NULL Unable to parse.
*/
ExprNode *parseConstantExprNode(Token ***tokenp)
{
ConstantNode *node = NULL;
ExprNode *ret = NULL;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ET_CONSTANT");
#endif
/* Parse the constant */
node = parseConstantNode(&tokens);
if (!node) goto parseConstantExprNodeAbort;
/* Create the new ExprNode structure */
ret = createExprNode(ET_CONSTANT, node);
if (!ret) goto parseConstantExprNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseConstantExprNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteExprNode(ret);
else {
if (node) deleteConstantNode(node);
}
return NULL;
}
/**
* Parses tokens into an identifier expression.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to an identifier expression.
*
* \retval NULL Unable to parse.
*/
ExprNode *parseIdentifierExprNode(Token ***tokenp)
{
IdentifierNode *node = NULL;
ExprNode *ret = NULL;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ET_IDENTIFIER");
#endif
/* Parse the identifier node */
node = parseIdentifierNode(&tokens);
if (!node) goto parseIdentifierExprNodeAbort;
/* Create the new ExprNode structure */
ret = createExprNode(ET_IDENTIFIER, node);
if (!ret) goto parseIdentifierExprNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseIdentifierExprNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteExprNode(ret);
else {
if (node) deleteIdentifierNode(node);
}
return NULL;
}
/**
* Parses tokens into a function call expression.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a Tokenp will point to the next unparsed token.
*
* \return A pointer to a function call expression.
*
* \retval NULL Unable to parse.
*/
ExprNode *parseFuncCallExprNode(Token ***tokenp)
{
IdentifierNode *scope = NULL;
IdentifierNode *name = NULL;
ExprNodeList *args = NULL;
ExprNode *arg = NULL;
FuncCallExprNode *expr = NULL;
ExprNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ET_FUNCCALL");
#endif
/* Parse the scope name */
scope = parseIdentifierNode(&tokens);
if (!scope) goto parseFuncCallExprNodeAbort;
/* Parse the function call token */
status = acceptToken(&tokens, TT_IZ);
if (!status) {
parser_error_expected_token(TT_IZ, tokens);
goto parseFuncCallExprNodeAbort;
}
/* Parse the function name */
name = parseIdentifierNode(&tokens);
if (!name) goto parseFuncCallExprNodeAbort;
/* Create an argument list */
args = createExprNodeList();
if (!args) goto parseFuncCallExprNodeAbort;
/* Parse the first argument token */
if (acceptToken(&tokens, TT_YR)) {
/* Parse the first argument */
arg = parseExprNode(&tokens);
if (!arg) goto parseFuncCallExprNodeAbort;
/* Add the first argument to the argument list */
status = addExprNode(args, arg);
if (!status) goto parseFuncCallExprNodeAbort;
arg = NULL;
/* Parse additional arguments */
while (acceptToken(&tokens, TT_ANYR)) {
/* Parse the argument */
arg = parseExprNode(&tokens);
if (!arg) goto parseFuncCallExprNodeAbort;
/* Add the argument to the argument list */
status = addExprNode(args, arg);
if (!status) goto parseFuncCallExprNodeAbort;
arg = NULL;
}
}
/* Parse the end-of-argument token */
status = acceptToken(&tokens, TT_MKAY);
if (!status) {
parser_error_expected_token(TT_MKAY, tokens);
goto parseFuncCallExprNodeAbort;
}
/* Create the new FuncCallExprNode structure */
expr = createFuncCallExprNode(scope, name, args);
if (!expr) goto parseFuncCallExprNodeAbort;
/* Create the new ExprNode structure */
ret = createExprNode(ET_FUNCCALL, expr);
if (!ret) goto parseFuncCallExprNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseFuncCallExprNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteExprNode(ret);
else if (expr) deleteFuncCallExprNode(expr);
else {
if (args) deleteExprNodeList(args);
if (arg) deleteExprNode(arg);
if (name) deleteIdentifierNode(name);
if (scope) deleteIdentifierNode(scope);
}
return NULL;
}
/**
* Parses tokens into a system command expression.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a Tokenp will point to the next unparsed token.
*
* \return A pointer to a system command expression.
*
* \retval NULL Unable to parse.
*/
ExprNode *parseSystemCommandExprNode(Token ***tokenp)
{
ExprNode *name = NULL;
SystemCommandExprNode *expr = NULL;
ExprNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ET_SYSTEMCOMMAND");
#endif
/* Parse the system command token */
status = acceptToken(&tokens, TT_IDUZ);
if (!status) {
parser_error_expected_token(TT_IDUZ, tokens);
goto parseSystemCommandExprNodeAbort;
}
/* Parse the expression name */
name = parseExprNode(&tokens);
if (!name) goto parseSystemCommandExprNodeAbort;
/* Create the new SystemCommandExprNode structure */
expr = createSystemCommandExprNode(name);
if (!expr) goto parseSystemCommandExprNodeAbort;
/* Create the new ExprNode structure */
ret = createExprNode(ET_SYSTEMCOMMAND, expr);
if (!ret) goto parseSystemCommandExprNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseSystemCommandExprNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteExprNode(ret);
else if (expr) deleteSystemCommandExprNode(expr);
else if (name) deleteExprNode(name);
return NULL;
}
/**
* Parses tokens into an operation expression.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a operation expression.
*
* \retval NULL Unable to parse.
*/
ExprNode *parseOpExprNode(Token ***tokenp)
{
enum ArityType {
AT_UNARY,
AT_BINARY,
AT_NARY
};
enum ArityType arity;
ExprNodeList *args = NULL;
ExprNode *arg = NULL;
OpExprNode *expr = NULL;
OpType type;
ExprNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
/* Unary operations */
if (acceptToken(&tokens, TT_NOT)) {
type = OP_NOT;
arity = AT_UNARY;
#ifdef DEBUG
debug("ET_OP (OP_NOT)");
#endif
}
/* Binary operations */
else if (acceptToken(&tokens, TT_SUMOF)) {
type = OP_ADD;
arity = AT_BINARY;
#ifdef DEBUG
debug("ET_OP (OP_ADD)");
#endif
}
else if(acceptToken(&tokens, TT_DIFFOF)) {
type = OP_SUB;
arity = AT_BINARY;
#ifdef DEBUG
debug("ET_OP (OP_SUB)");
#endif
}
else if(acceptToken(&tokens, TT_PRODUKTOF)) {
type = OP_MULT;
arity = AT_BINARY;
#ifdef DEBUG
debug("ET_OP (OP_MULT)");
#endif
}
else if(acceptToken(&tokens, TT_QUOSHUNTOF)) {
type = OP_DIV;
arity = AT_BINARY;
#ifdef DEBUG
debug("ET_OP (OP_DIV)");
#endif
}
else if(acceptToken(&tokens, TT_MODOF)) {
type = OP_MOD;
arity = AT_BINARY;
#ifdef DEBUG
debug("ET_OP (OP_MOD)");
#endif
}
else if(acceptToken(&tokens, TT_BIGGROF)) {
type = OP_MAX;
arity = AT_BINARY;
#ifdef DEBUG
debug("ET_OP (OP_MAX)");
#endif
}
else if(acceptToken(&tokens, TT_SMALLROF)) {
type = OP_MIN;
arity = AT_BINARY;
#ifdef DEBUG
debug("ET_OP (OP_MIN)");
#endif
}
else if(acceptToken(&tokens, TT_BOTHOF)) {
type = OP_AND;
arity = AT_BINARY;
#ifdef DEBUG
debug("ET_OP (OP_AND)");
#endif
}
else if(acceptToken(&tokens, TT_EITHEROF)) {
type = OP_OR;
arity = AT_BINARY;
#ifdef DEBUG
debug("ET_OP (OP_OR)");
#endif
}
else if(acceptToken(&tokens, TT_WONOF)) {
type = OP_XOR;
arity = AT_BINARY;
#ifdef DEBUG
debug("ET_OP (OP_XOR)");
#endif
}
else if(acceptToken(&tokens, TT_BOTHSAEM)) {
type = OP_EQ;
arity = AT_BINARY;
#ifdef DEBUG
debug("ET_OP (OP_EQ)");
#endif
}
else if(acceptToken(&tokens, TT_DIFFRINT)) {
type = OP_NEQ;
arity = AT_BINARY;
#ifdef DEBUG
debug("ET_OP (OP_NEQ)");
#endif
}
/* N-ary operators */
else if (acceptToken(&tokens, TT_ALLOF)) {
type = OP_AND;
arity = AT_NARY;
#ifdef DEBUG
debug("ET_OP (OP_AND)");
#endif
}
else if(acceptToken(&tokens, TT_ANYOF)) {
type = OP_OR;
arity = AT_NARY;
#ifdef DEBUG
debug("ET_OP (OP_OR)");
#endif
}
else if(acceptToken(&tokens, TT_SMOOSH)) {
type = OP_CAT;
arity = AT_NARY;
#ifdef DEBUG
debug("ET_OP (OP_CAT)");
#endif
}
else {
parser_error(PR_INVALID_OPERATOR, tokens);
return NULL;
}
/* Create the argument list */
args = createExprNodeList();
if (!args) goto parseOpExprNodeAbort;
/* Parse the operation arguments */
if (arity == AT_UNARY) {
/* Parse the argument */
arg = parseExprNode(&tokens);
if (!arg) goto parseOpExprNodeAbort;
/* Add the argument to the argument list */
status = addExprNode(args, arg);
if (!status) goto parseOpExprNodeAbort;
arg = NULL;
}
else if (arity == AT_BINARY) {
/* Parse the first argument */
arg = parseExprNode(&tokens);
if (!arg) goto parseOpExprNodeAbort;
/* Add the first argument to the argument list */
status = addExprNode(args, arg);
if (!status) goto parseOpExprNodeAbort;
arg = NULL;
/* Optionally parse the argument-separator token */
status = acceptToken(&tokens, TT_AN);
/* Parse the second argument */
arg = parseExprNode(&tokens);
if (!arg) goto parseOpExprNodeAbort;
/* Add the second argument to the argument list */
status = addExprNode(args, arg);
if (!status) goto parseOpExprNodeAbort;
arg = NULL;
}
else if (arity == AT_NARY) {
/* Parse as many arguments as possible */
while (1) {
/* Parse an argument */
arg = parseExprNode(&tokens);
if (!arg) goto parseOpExprNodeAbort;
/* Add the argument to the argument list */
status = addExprNode(args, arg);
if (!status) goto parseOpExprNodeAbort;
arg = NULL;
/* Stop if the end-of-arguments token is present */
if (acceptToken(&tokens, TT_MKAY)) break;
/* Optionally parse the argument-separator token */
status = acceptToken(&tokens, TT_AN);
}
}
/* Create the new OpExprNode structure */
expr = createOpExprNode(type, args);
if (!expr) goto parseOpExprNodeAbort;
/* Create the new ExprNode structure */
ret = createExprNode(ET_OP, expr);
if (!ret) goto parseOpExprNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseOpExprNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteExprNode(ret);
else if (expr) deleteOpExprNode(expr);
else {
if (arg) deleteExprNode(arg);
if (args) deleteExprNodeList(args);
}
return NULL;
}
/**
* Parses tokens into an expression.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to an expression.
*
* \retval NULL Unable to parse.
*/
ExprNode *parseExprNode(Token ***tokenp)
{
Token **tokens = *tokenp;
ExprNode *ret = NULL;
#ifdef DEBUG
shiftout();
#endif
/* Parse context-sensitive expressions */
if (peekToken(&tokens, TT_IDENTIFIER)
|| peekToken(&tokens, TT_SRS)) {
IdentifierNode *id = NULL;
/* Remove the identifier from the token stream */
id = parseIdentifierNode(&tokens);
if (!id) return NULL;
/* We do not need to hold onto it */
deleteIdentifierNode(id);
/* Function call (must come before identifier) */
if (peekToken(&tokens, TT_IZ)) {
ret = parseFuncCallExprNode(tokenp);
}
/* Identifier */
else {
ret = parseIdentifierExprNode(tokenp);
}
}
/* Cast */
else if (peekToken(&tokens, TT_MAEK)) {
ret = parseCastExprNode(tokenp);
}
/* Constant value */
else if (peekToken(&tokens, TT_BOOLEAN)
|| peekToken(&tokens, TT_INTEGER)
|| peekToken(&tokens, TT_FLOAT)
|| peekToken(&tokens, TT_STRING))
ret = parseConstantExprNode(tokenp);
/* Operation */
else if (peekToken(&tokens, TT_SUMOF)
|| peekToken(&tokens, TT_DIFFOF)
|| peekToken(&tokens, TT_PRODUKTOF)
|| peekToken(&tokens, TT_QUOSHUNTOF)
|| peekToken(&tokens, TT_MODOF)
|| peekToken(&tokens, TT_BIGGROF)
|| peekToken(&tokens, TT_SMALLROF)
|| peekToken(&tokens, TT_BOTHOF)
|| peekToken(&tokens, TT_EITHEROF)
|| peekToken(&tokens, TT_WONOF)
|| peekToken(&tokens, TT_BOTHSAEM)
|| peekToken(&tokens, TT_DIFFRINT)
|| peekToken(&tokens, TT_ANYOF)
|| peekToken(&tokens, TT_ALLOF)
|| peekToken(&tokens, TT_SMOOSH)
|| peekToken(&tokens, TT_NOT)) {
ret = parseOpExprNode(tokenp);
}
/* Implicit variable */
else if (acceptToken(&tokens, TT_IT)) {
#ifdef DEBUG
debug("ET_IMPVAR");
#endif
/* Create the new ExprNode structure */
ret = createExprNode(ET_IMPVAR, NULL);
if (!ret) return NULL;
/* Since we're successful, update the token stream */
*tokenp = tokens;
}
else if (peekToken(&tokens, TT_IDUZ)) {
/* System command */
ret = parseSystemCommandExprNode(tokenp);
}
else {
parser_error(PR_EXPECTED_EXPRESSION, tokens);
}
#ifdef DEBUG
shiftin();
#endif
return ret;
}
/**
* Parses tokens into a cast statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a cast statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parseCastStmtNode(Token ***tokenp)
{
IdentifierNode *target = NULL;
TypeNode *newtype = NULL;
CastStmtNode *stmt = NULL;
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_CAST");
#endif
/* Parse the target of the cast statement */
target = parseIdentifierNode(&tokens);
if (!target) goto parseCastStmtNodeAbort;
/* Remove the cast keywords from the token stream */
status = acceptToken(&tokens, TT_ISNOWA);
if (!status) {
parser_error_expected_token(TT_ISNOWA, tokens);
goto parseCastStmtNodeAbort;
}
/* Parse the type to cast to */
newtype = parseTypeNode(&tokens);
if (!newtype) goto parseCastStmtNodeAbort;
/* Make sure the statement ends with a newline */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseCastStmtNodeAbort;
}
/* Create the new CastStmtNode structure */
stmt = createCastStmtNode(target, newtype);
if (!stmt) goto parseCastStmtNodeAbort;
/* Create the new StmtNode structure */
ret = createStmtNode(ST_CAST, stmt);
if (!ret) goto parseCastStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseCastStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
else if (stmt) deleteCastStmtNode(stmt);
else {
if (newtype) deleteTypeNode(newtype);
if (target) deleteIdentifierNode(target);
}
return NULL;
}
/**
* Parses tokens into a print statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a print statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parsePrintStmtNode(Token ***tokenp)
{
ExprNode *arg = NULL;
ExprNodeList *args = NULL;
int nonl = 0;
PrintStmtNode *stmt = NULL;
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_PRINT");
#endif
/* Remove the print keyword from the token stream */
status = acceptToken(&tokens, TT_VISIBLE);
if (!status) {
parser_error_expected_token(TT_VISIBLE, tokens);
goto parsePrintStmtNodeAbort;
}
/* Parse the arguments to the print statement */
args = createExprNodeList();
if (!args) goto parsePrintStmtNodeAbort;
do {
/* Parse an argument; it should be an expression */
arg = parseExprNode(&tokens);
if (!arg) goto parsePrintStmtNodeAbort;
/* Add it to the list of arguments */
status = addExprNode(args, arg);
if (!status) goto parsePrintStmtNodeAbort;
arg = NULL;
/* Arguments can optionally be separated by an AN keyword */
status = acceptToken(&tokens, TT_AN);
} while (!peekToken(&tokens, TT_NEWLINE)
&& !peekToken(&tokens, TT_BANG));
/* Check for the no-newline token */
if(acceptToken(&tokens, TT_BANG)) nonl = 1;
/* Make sure the statement ends with a newline */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parsePrintStmtNodeAbort;
}
/* Create the new PrintStmtNode structure */
stmt = createPrintStmtNode(args, nonl);
if (!stmt) goto parsePrintStmtNodeAbort;
/* Create the new StmtNode structure */
ret = createStmtNode(ST_PRINT, stmt);
if (!ret) goto parsePrintStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parsePrintStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
else if (stmt) deletePrintStmtNode(stmt);
else {
if (arg) deleteExprNode(arg);
if (args) deleteExprNodeList(args);
}
return NULL;
}
/**
* Parses tokens into an input statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to an input statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parseInputStmtNode(Token ***tokenp)
{
IdentifierNode *target = NULL;
InputStmtNode *stmt = NULL;
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_INPUT");
#endif
/* Remove the input keyword from the token stream */
status = acceptToken(&tokens, TT_GIMMEH);
if (!status) {
parser_error_expected_token(TT_GIMMEH, tokens);
goto parseInputStmtNodeAbort;
}
/* Parse the identifier to store the input into */
target = parseIdentifierNode(&tokens);
if (!target) goto parseInputStmtNodeAbort;
/* Make sure the statement ends with a newline */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseInputStmtNodeAbort;
}
/* Create the new InputStmtNode structure */
stmt = createInputStmtNode(target);
if (!stmt) goto parseInputStmtNodeAbort;
/* Create the new StmtNode structure */
ret = createStmtNode(ST_INPUT, stmt);
if (!ret) goto parseInputStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseInputStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
else if (stmt) deleteInputStmtNode(stmt);
else {
if (target) deleteIdentifierNode(target);
}
return NULL;
}
/**
* Parses tokens into an assignment statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to an assignment statement.
*
* \retval NULL unable to parse.
*/
StmtNode *parseAssignmentStmtNode(Token ***tokenp)
{
IdentifierNode *target = NULL;
ExprNode *expr = NULL;
AssignmentStmtNode *stmt = NULL;
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_ASSIGNMENT");
#endif
/* Parse the target of the assignment */
target = parseIdentifierNode(&tokens);
if (!target) goto parseAssignmentStmtNodeAbort;
/* Remove the assignment keyword from the token stream */
if (!acceptToken(&tokens, TT_R)) {
parser_error_expected_token(TT_R, tokens);
goto parseAssignmentStmtNodeAbort;
}
/* Parse the expression to assign */
expr = parseExprNode(&tokens);
if (!expr) goto parseAssignmentStmtNodeAbort;
/* Make sure the statement ends with a newline */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_STATEMENT, tokens);
goto parseAssignmentStmtNodeAbort;
}
/* Create the new AssignmentStmtNode structure */
stmt = createAssignmentStmtNode(target, expr);
if (!stmt) goto parseAssignmentStmtNodeAbort;
/* Create the new StmtNode structure */
ret = createStmtNode(ST_ASSIGNMENT, stmt);
if (!ret) goto parseAssignmentStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseAssignmentStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
else if (stmt) deleteAssignmentStmtNode(stmt);
else {
if (expr) deleteExprNode(expr);
if (target) deleteIdentifierNode(target);
}
return NULL;
}
/**
* Parses tokens into a declaration statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a declaration statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parseDeclarationStmtNode(Token ***tokenp)
{
IdentifierNode *scope = NULL;
IdentifierNode *target = NULL;
ExprNode *expr = NULL;
TypeNode *type = NULL;
IdentifierNode *parent = NULL;
DeclarationStmtNode *stmt = NULL;
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_DECLARATION");
#endif
/* Parse the scope to declare the variable in */
scope = parseIdentifierNode(&tokens);
if (!scope) goto parseDeclarationStmtNodeAbort;
/* Remove the declaration keywords from the token stream */
if (!acceptToken(&tokens, TT_HASA)) {
parser_error_expected_token(TT_HASA, tokens);
goto parseDeclarationStmtNodeAbort;
}
/* Parse the identifier to declare */
target = parseIdentifierNode(&tokens);
if (!target) goto parseDeclarationStmtNodeAbort;
/* Check for an optional initialization */
if (acceptToken(&tokens, TT_ITZ)) {
/* Parse the expression to initialize to */
expr = parseExprNode(&tokens);
if (!expr) goto parseDeclarationStmtNodeAbort;
}
/* Check for an optional type initialization */
else if (acceptToken(&tokens, TT_ITZA)) {
/* Parse the type to initialize to */
type = parseTypeNode(&tokens);
if (!type) goto parseDeclarationStmtNodeAbort;
}
/* Check for an optional array inheritance */
else if (acceptToken(&tokens, TT_ITZLIEKA)) {
/* Parse the parent to inherit from */
parent = parseIdentifierNode(&tokens);
if (!parent) goto parseDeclarationStmtNodeAbort;
}
/* Make sure the statement ends with a newline */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_STATEMENT, tokens);
goto parseDeclarationStmtNodeAbort;
}
/* Create the new DeclarationStmtNode structure */
stmt = createDeclarationStmtNode(scope, target, expr, type, parent);
if (!stmt) goto parseDeclarationStmtNodeAbort;
/* Create the new StmtNode structure */
ret = createStmtNode(ST_DECLARATION, stmt);
if (!ret) goto parseDeclarationStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseDeclarationStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
else if (stmt) deleteDeclarationStmtNode(stmt);
else {
if (type) deleteTypeNode(type);
if (expr) deleteExprNode(expr);
if (target) deleteIdentifierNode(target);
if (scope) deleteIdentifierNode(scope);
}
return NULL;
}
/**
* Parses tokens into an if/then/else statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to an if/then/else statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parseIfThenElseStmtNode(Token ***tokenp)
{
BlockNode *yes = NULL;
ExprNodeList *guards = NULL;
BlockNodeList *blocks = NULL;
ExprNode *guard = NULL;
BlockNode *block = NULL;
BlockNode *no = NULL;
IfThenElseStmtNode *stmt = NULL;
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_CONDITIONAL");
#endif
/* Remove the if keyword from the token stream */
status = acceptToken(&tokens, TT_ORLY);
if (!status) {
parser_error_expected_token(TT_ORLY, tokens);
goto parseIfThenElseStmtNodeAbort;
}
/* Remove the question mark from the token stream */
status = acceptToken(&tokens, TT_QUESTION);
if (!status) {
parser_error_expected_token(TT_QUESTION, tokens);
goto parseIfThenElseStmtNodeAbort;
}
/* The if keyword must appear on its own line */
if (!acceptToken(&tokens, TT_NEWLINE)) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseIfThenElseStmtNodeAbort;
}
/* Parse the true branch keyword */
status = acceptToken(&tokens, TT_YARLY);
if (!status) {
parser_error_expected_token(TT_YARLY, tokens);
goto parseIfThenElseStmtNodeAbort;
}
/* The true branch keyword must appear on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseIfThenElseStmtNodeAbort;
}
/* Parse the true part of the branch */
yes = parseBlockNode(&tokens);
if (!yes) goto parseIfThenElseStmtNodeAbort;
/* Set up lists of guards and blocks */
guards = createExprNodeList();
if (!guards) goto parseIfThenElseStmtNodeAbort;
blocks = createBlockNodeList();
if (!blocks) goto parseIfThenElseStmtNodeAbort;
/* Parse the else-if keyword */
while (acceptToken(&tokens, TT_MEBBE)) {
/* Parse an else-if guard */
guard = parseExprNode(&tokens);
if (!guard) goto parseIfThenElseStmtNodeAbort;
/* Add the else-if guard to the guard list */
status = addExprNode(guards, guard);
if (!status) goto parseIfThenElseStmtNodeAbort;
guard = NULL;
/* The else-if keyword and guard must be on their own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseIfThenElseStmtNodeAbort;
}
/* Parse the else-if block */
block = parseBlockNode(&tokens);
if (!block) goto parseIfThenElseStmtNodeAbort;
/* Add the else-if block to the block list */
status = addBlockNode(blocks, block);
if (!status) goto parseIfThenElseStmtNodeAbort;
block = NULL;
}
/* Parse the else keyword */
if (acceptToken(&tokens, TT_NOWAI)) {
/* The else keyword must appear on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseIfThenElseStmtNodeAbort;
}
/* Parse the else block */
no = parseBlockNode(&tokens);
if (!no) goto parseIfThenElseStmtNodeAbort;
}
/* Parse the end-if-block keyword */
status = acceptToken(&tokens, TT_OIC);
if (!status) {
parser_error_expected_token(TT_OIC, tokens);
goto parseIfThenElseStmtNodeAbort;
}
/* The end-if-block keyword must appear on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseIfThenElseStmtNodeAbort;
}
/* Create the new IfThenElseStmtNode structure */
stmt = createIfThenElseStmtNode(yes, no, guards, blocks);
if (!stmt) goto parseIfThenElseStmtNodeAbort;
/* Create the new StmtNode structure */
ret = createStmtNode(ST_IFTHENELSE, stmt);
if (!ret) goto parseIfThenElseStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseIfThenElseStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
else if (stmt) deleteIfThenElseStmtNode(stmt);
else {
if (no) deleteBlockNode(no);
if (blocks) deleteBlockNodeList(blocks);
if (guards) deleteExprNodeList(guards);
if (block) deleteBlockNode(block);
if (guard) deleteExprNode(guard);
if (yes) deleteBlockNode(yes);
}
return NULL;
}
/**
* Parses tokens into a switch statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a switch statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parseSwitchStmtNode(Token ***tokenp)
{
ExprNodeList *guards = NULL;
BlockNodeList *blocks = NULL;
ConstantNode *c = NULL;
ExprNode *guard = NULL;
BlockNode *block = NULL;
BlockNode *def = NULL;
SwitchStmtNode *stmt = NULL;
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_SWITCH");
#endif
/* Remove the switch keyword from the token stream */
status = acceptToken(&tokens, TT_WTF);
if (!status) {
parser_error_expected_token(TT_WTF, tokens);
goto parseSwitchStmtNodeAbort;
}
/* Remove the question mark from the token stream */
status = acceptToken(&tokens, TT_QUESTION);
if (!status) {
parser_error_expected_token(TT_QUESTION, tokens);
goto parseSwitchStmtNodeAbort;
}
/* The switch keyword must appear on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseSwitchStmtNodeAbort;
}
/* Set up lists of guards and blocks */
guards = createExprNodeList();
if (!guards) goto parseSwitchStmtNodeAbort;
blocks = createBlockNodeList();
if (!blocks) goto parseSwitchStmtNodeAbort;
/* Parse at least one case */
do {
unsigned int n = 0;
/* Parse the case keyword */
status = acceptToken(&tokens, TT_OMG);
if (!status) {
parser_error_expected_token(TT_OMG, tokens);
goto parseSwitchStmtNodeAbort;
}
/* Parse a constant for the case */
/** \note The 1.2 specification only allows constant
* values for OMG guards thus this function
* explicitly checks for them. */
c = parseConstantNode(&tokens);
if (!c) goto parseSwitchStmtNodeAbort;
/* String interpolation is not allowed */
if (c->type == CT_STRING && strstr(c->data.s, ":{")) {
parser_error(PR_CANNOT_USE_STR_AS_LITERAL, tokens);
goto parseSwitchStmtNodeAbort;
}
/* Make sure the constant is unique to this switch statement */
for (n = 0; n < guards->num; n++) {
ConstantNode *test = guards->exprs[n]->expr;
if (c->type != test->type) continue;
/* Check for equivalent types and values */
if (((c->type == CT_BOOLEAN || c->type == CT_INTEGER)
&& c->data.i == test->data.i)
|| (c->type == CT_FLOAT
&& fabs(c->data.f - test->data.f) < FLT_EPSILON)
|| (c->type == CT_STRING
&& !strcmp(c->data.s, test->data.s))) {
parser_error(PR_LITERAL_MUST_BE_UNIQUE, tokens);
goto parseSwitchStmtNodeAbort;
}
}
/* Package the constant in an expression */
guard = createExprNode(ET_CONSTANT, c);
if (!guard) goto parseSwitchStmtNodeAbort;
/* Add the guard to the list of guards */
status = addExprNode(guards, guard);
if (!status) goto parseSwitchStmtNodeAbort;
guard = NULL;
/* Make sure the guard appears on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseSwitchStmtNodeAbort;
}
/* Parse the block associated with the guard */
block = parseBlockNode(&tokens);
if (!block) goto parseSwitchStmtNodeAbort;
/* Add the block to the list of blocks */
status = addBlockNode(blocks, block);
if (!status) goto parseSwitchStmtNodeAbort;
block = NULL;
} while (peekToken(&tokens, TT_OMG));
/* Check for the default case */
if (acceptToken(&tokens, TT_OMGWTF)) {
/* Make sure the default case keyword appears on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseSwitchStmtNodeAbort;
}
/* Parse the default case block */
def = parseBlockNode(&tokens);
if (!def) goto parseSwitchStmtNodeAbort;
}
/* Parse the end-switch keyword */
status = acceptToken(&tokens, TT_OIC);
if (!status) {
parser_error_expected_token(TT_OIC, tokens);
goto parseSwitchStmtNodeAbort;
}
/* Make sure the end-switch keyword appears on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseSwitchStmtNodeAbort;
}
/* Create the new SwitchStmtNode structure */
stmt = createSwitchStmtNode(guards, blocks, def);
if (!stmt) goto parseSwitchStmtNodeAbort;
/* Create the new StmtNode structure */
ret = createStmtNode(ST_SWITCH, stmt);
if (!ret) goto parseSwitchStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseSwitchStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
else if (stmt) deleteSwitchStmtNode(stmt);
else {
if (def) deleteBlockNode(def);
if (block) deleteBlockNode(block);
if (guard) deleteExprNode(guard);
if (c) deleteConstantNode(c);
if (blocks) deleteBlockNodeList(blocks);
if (guards) deleteExprNodeList(guards);
}
return NULL;
}
/**
* Parses tokens into a break statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a break statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parseBreakStmtNode(Token ***tokenp)
{
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_BREAK");
#endif
/* Remove the break keyword from the token stream */
status = acceptToken(&tokens, TT_GTFO);
if (!status) {
parser_error_expected_token(TT_GTFO, tokens);
goto parseBreakStmtNodeAbort;
}
/* The break keyword must appear on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseBreakStmtNodeAbort;
}
/* Create the new StmtNode structure */
ret = createStmtNode(ST_BREAK, NULL);
if (!ret) goto parseBreakStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseBreakStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
return NULL;
}
/**
* Parses tokens into a return statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a return statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parseReturnStmtNode(Token ***tokenp)
{
ExprNode *value = NULL;
ReturnStmtNode *stmt = NULL;
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_RETURN");
#endif
/* Remove the return keyword from the token stream */
status = acceptToken(&tokens, TT_FOUNDYR);
if (!status) {
parser_error_expected_token(TT_FOUNDYR, tokens);
goto parseReturnStmtNodeAbort;
}
/* Parse the return value */
value = parseExprNode(&tokens);
if (!value) goto parseReturnStmtNodeAbort;
/* The return statement must reside on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseReturnStmtNodeAbort;
}
/* Create the new ReturnStmtNode structure */
stmt = createReturnStmtNode(value);
if (!stmt) goto parseReturnStmtNodeAbort;
/* Create the new StmtNode structure */
ret = createStmtNode(ST_RETURN, stmt);
if (!ret) goto parseReturnStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseReturnStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
else if (stmt) deleteReturnStmtNode(stmt);
else {
if (value) deleteExprNode(value);
}
return NULL;
}
/**
* Parses tokens into a loop statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a loop statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parseLoopStmtNode(Token ***tokenp)
{
IdentifierNode *name1 = NULL;
IdentifierNode *var = NULL;
ExprNode *update = NULL;
ExprNode *guard = NULL;
BlockNode *body = NULL;
FuncDefStmtNode *def = NULL;
IdentifierNode *name2 = NULL;
LoopStmtNode *stmt = NULL;
ExprNodeList *args = NULL;
char *id = NULL;
/* For increment and decrement loops */
IdentifierNode *varcopy = NULL;
ExprNode *arg1 = NULL;
ConstantNode *one = NULL;
ExprNode *arg2 = NULL;
OpExprNode *op = NULL;
/* For function loops */
IdentifierNode *scope = NULL;
IdentifierNode *name = NULL;
FuncCallExprNode *node = NULL;
ExprNode *arg = NULL;
/* For loop predicates */
ExprNode *predarg = NULL;
ExprNodeList *predargs = NULL;
OpExprNode *predop = NULL;
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_LOOP");
#endif
/* Remove the loop-start keyword from the token stream */
status = acceptToken(&tokens, TT_IMINYR);
if (!status) {
parser_error_expected_token(TT_IMINYR, tokens);
goto parseLoopStmtNodeAbort;
}
/* Parse the loop name */
name1 = parseIdentifierNode(&tokens);
if (!name1) goto parseLoopStmtNodeAbort;
else if (name1->type != IT_DIRECT) {
parser_error(PR_EXPECTED_LOOP_NAME, tokens);
goto parseLoopStmtNodeAbort;
}
/* Check if an increment or decrement loop */
if (peekToken(&tokens, TT_UPPIN) || peekToken(&tokens, TT_NERFIN)) {
OpType type;
/* Parse the increment token or decrement token */
if (acceptToken(&tokens, TT_UPPIN)) {
type = OP_ADD;
#ifdef DEBUG
shiftout();
debug("ET_OP (OP_ADD)");
#endif
}
else if (acceptToken(&tokens, TT_NERFIN)) {
type = OP_SUB;
#ifdef DEBUG
shiftout();
debug("ET_OP (OP_SUB)");
#endif
}
else {
parser_error_expected_either_token(TT_UPPIN, TT_NERFIN, tokens);
goto parseLoopStmtNodeAbort;
}
/* Parse the required syntax */
if (!acceptToken(&tokens, TT_YR)) {
parser_error_expected_token(TT_YR, tokens);
goto parseLoopStmtNodeAbort;
}
/* Parse the variable to operate on */
var = parseIdentifierNode(&tokens);
if (!var) goto parseLoopStmtNodeAbort;
#ifdef DEBUG
shiftout();
debug("ET_CONSTANT (CT_INTEGER)");
shiftin();
shiftin();
#endif
/* Make a copy of the variable for use as a function argument */
id = malloc(sizeof(char) * (strlen(var->id) + 1));
if (!id) goto parseLoopStmtNodeAbort;
strcpy(id, var->id);
varcopy = createIdentifierNode(IT_DIRECT, id, NULL, var->fname, var->line);
if (!varcopy) goto parseLoopStmtNodeAbort;
id = NULL;
/* Package the variable into an identifier expression */
arg1 = createExprNode(ET_IDENTIFIER, varcopy);
if (!arg1) goto parseLoopStmtNodeAbort;
/* Create a constant integer "1" */
one = createIntegerConstantNode(1);
if (!one) goto parseLoopStmtNodeAbort;
/* Package the constant into an expression */
arg2 = createExprNode(ET_CONSTANT, one);
if (!arg2) goto parseLoopStmtNodeAbort;
/* Create a list of arguments */
args = createExprNodeList();
if (!args) goto parseLoopStmtNodeAbort;
/* Add the packaged arguments to the argument list */
status = addExprNode(args, arg1);
if (!status) goto parseLoopStmtNodeAbort;
arg1 = NULL;
status = addExprNode(args, arg2);
if (!status) goto parseLoopStmtNodeAbort;
arg2 = NULL;
/* Package the arguments and operation type in an expression */
op = createOpExprNode(type, args);
if (!op) goto parseLoopStmtNodeAbort;
/* Create the update expression */
update = createExprNode(ET_OP, op);
if (!update) goto parseLoopStmtNodeAbort;
}
/* Check if a function loop */
else if (nextToken(&tokens, TT_IZ)) {
IdentifierNode *temp = NULL;
#ifdef DEBUG
debug("ET_FUNCCALL");
#endif
/* Parse the function scope */
scope = parseIdentifierNode(&tokens);
if (!scope) goto parseLoopStmtNodeAbort;
/* Parse the function indicator */
status = acceptToken(&tokens, TT_IZ);
if (!status) {
parser_error_expected_token(TT_IZ, tokens);
goto parseLoopStmtNodeAbort;
}
/* Parse the function name */
name = parseIdentifierNode(&tokens);
if (!name) goto parseLoopStmtNodeAbort;
/* Create a list of arguments */
args = createExprNodeList();
if (!args) goto parseLoopStmtNodeAbort;
/* Check for unary function */
status = acceptToken(&tokens, TT_YR);
if (!status) {
parser_error(PR_EXPECTED_UNARY_FUNCTION, tokens);
goto parseLoopStmtNodeAbort;
}
/* Parse the unary function's single argument */
arg = parseExprNode(&tokens);
if (!arg) goto parseLoopStmtNodeAbort;
/* Make sure the argument is an identifier */
if (arg->type != ET_IDENTIFIER) {
parser_error(PR_EXPECTED_IDENTIFIER, tokens);
goto parseLoopStmtNodeAbort;
}
/* Add the argument to the argument list */
status = addExprNode(args, arg);
if (!status) goto parseLoopStmtNodeAbort;
/* (Save a pointer to its expression for use, below) */
temp = (IdentifierNode *)(arg->expr);
arg = NULL;
/* Copy the identifier to make it the loop variable */
id = malloc(sizeof(char) * (strlen(temp->id) + 1));
if (!id) goto parseLoopStmtNodeAbort;
strcpy(id, temp->id);
var = createIdentifierNode(IT_DIRECT, id, NULL, temp->fname, temp->line);
if (!var) goto parseLoopStmtNodeAbort;
id = NULL;
/* Check for unary function */
status = acceptToken(&tokens, TT_MKAY);
if (!status) {
parser_error_expected_token(TT_MKAY, tokens);
goto parseLoopStmtNodeAbort;
}
/* Create a function call expression */
node = createFuncCallExprNode(scope, name, args);
if (!node) goto parseLoopStmtNodeAbort;
/* Package the function call in an expression */
update = createExprNode(ET_FUNCCALL, node);
if (!update) goto parseLoopStmtNodeAbort;
}
/* If there is an update, parse any predicates */
if (update) {
/* Check for a while token */
if (acceptToken(&tokens, TT_WILE)) {
/* Parse the while predicate */
guard = parseExprNode(&tokens);
if (!guard) goto parseLoopStmtNodeAbort;
}
/* Check for an until token */
else if (acceptToken(&tokens, TT_TIL)) {
#ifdef DEBUG
shiftout();
debug("ET_OP (OP_NOT)");
#endif
/* Parse the until predicate */
predarg = parseExprNode(&tokens);
if (!predarg) goto parseLoopStmtNodeAbort;
/* Create a list for predicate arguments */
predargs = createExprNodeList();
if (!predargs) goto parseLoopStmtNodeAbort;
/* Add the until predicate to the list */
status = addExprNode(predargs, predarg);
if (!status) goto parseLoopStmtNodeAbort;
predarg = NULL;
#ifdef DEBUG
shiftin();
#endif
/* Create a NOT operation with the predicate */
predop = createOpExprNode(OP_NOT, predargs);
if (!predop) goto parseLoopStmtNodeAbort;
/* Package the NOT operation in an expression */
guard = createExprNode(ET_OP, predop);
if (!guard) goto parseLoopStmtNodeAbort;
}
}
/* All of the above should be on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseLoopStmtNodeAbort;
}
/* Parse the body of the loop */
body = parseBlockNode(&tokens);
if (!body) goto parseLoopStmtNodeAbort;
/* Parse the end-loop keywords */
status = acceptToken(&tokens, TT_IMOUTTAYR);
if (!status) {
parser_error_expected_token(TT_IMOUTTAYR, tokens);
goto parseLoopStmtNodeAbort;
}
/* Parse the end-of-loop name */
name2 = parseIdentifierNode(&tokens);
if (!name2) goto parseLoopStmtNodeAbort;
else if (name2->type != IT_DIRECT) {
parser_error(PR_EXPECTED_LOOP_NAME, tokens);
goto parseLoopStmtNodeAbort;
}
/* Make sure the beginning-of-loop and end-of-loop names match */
if (strcmp((char *)(name1->id), (char *)(name2->id))) {
parser_error(PR_EXPECTED_MATCHING_LOOP_NAME, tokens);
goto parseLoopStmtNodeAbort;
}
/* We no longer need the end-of-loop name */
deleteIdentifierNode(name2);
/* The end-of-loop structure should appear on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseLoopStmtNodeAbort;
}
/* Create the new LoopStmtNode structure */
stmt = createLoopStmtNode(name1, var, guard, update, body);
if (!stmt) goto parseLoopStmtNodeAbort;
/* Create the new StmtNode structure */
ret = createStmtNode(ST_LOOP, stmt);
if (!ret) goto parseLoopStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseLoopStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
else if (stmt) deleteLoopStmtNode(stmt);
else {
if (name2) deleteIdentifierNode(name2);
if (def) deleteFuncDefStmtNode(def);
if (body) deleteBlockNode(body);
if (guard) deleteExprNode(guard);
if (update) deleteExprNode(update);
if (var) deleteIdentifierNode(var);
if (name1) deleteIdentifierNode(name1);
if (id) free(id);
/* For increment and decrement loops */
if (op) deleteOpExprNode(op);
if (args) deleteExprNodeList(args);
if (arg2) deleteExprNode(arg2);
if (one) deleteConstantNode(one);
if (arg1) deleteExprNode(arg1);
if (varcopy) deleteIdentifierNode(varcopy);
/* For function loops */
if (arg) deleteExprNode(arg);
if (node) deleteFuncCallExprNode(node);
if (name) deleteIdentifierNode(name);
if (scope) deleteIdentifierNode(scope);
/* For loop predicates */
if (predarg) deleteExprNode(predarg);
if (predargs) deleteExprNodeList(predargs);
if (predop) deleteOpExprNode(predop);
}
return NULL;
}
/**
* Parses tokens into a deallocation statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a deallocation statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parseDeallocationStmtNode(Token ***tokenp)
{
IdentifierNode *target = NULL;
DeallocationStmtNode *stmt = NULL;
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_DEALLOCATION");
#endif
/* Parse the variable to deallocate */
target = parseIdentifierNode(&tokens);
if (!target) goto parseDeallocationStmtNodeAbort;
/* Parse the deallocation token */
status = acceptToken(&tokens, TT_RNOOB);
if (!status) {
parser_error_expected_token(TT_RNOOB, tokens);
goto parseDeallocationStmtNodeAbort;
}
/* The deallocation statement must reside on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_STATEMENT, tokens);
goto parseDeallocationStmtNodeAbort;
}
/* Create the new DeallocationStmtNode structure */
stmt = createDeallocationStmtNode(target);
if (!stmt) goto parseDeallocationStmtNodeAbort;
/* Create the new StmtNode structure */
ret = createStmtNode(ST_DEALLOCATION, stmt);
if (!ret) goto parseDeallocationStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseDeallocationStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
else if (stmt) deleteDeallocationStmtNode(stmt);
else {
if (target) deleteIdentifierNode(target);
}
return NULL;
}
/**
* Parses tokens into a function definition statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a function definition statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parseFuncDefStmtNode(Token ***tokenp)
{
IdentifierNode *scope = NULL;
IdentifierNode *name = NULL;
IdentifierNodeList *args = NULL;
IdentifierNode *arg = NULL;
BlockNode *body = NULL;
FuncDefStmtNode *stmt = NULL;
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_FUNCDEF");
#endif
/* Parse the function definition token */
status = acceptToken(&tokens, TT_HOWIZ);
if (!status) {
parser_error_expected_token(TT_HOWIZ, tokens);
goto parseFuncDefStmtNodeAbort;
}
/* Parse the scope to define the function in */
scope = parseIdentifierNode(&tokens);
if (!scope) goto parseFuncDefStmtNodeAbort;
/* Parse the name of the function */
name = parseIdentifierNode(&tokens);
if (!name) goto parseFuncDefStmtNodeAbort;
/* Create a list of arguments */
args = createIdentifierNodeList();
if (!args) goto parseFuncDefStmtNodeAbort;
/* Parse the first argument indicator */
if (acceptToken(&tokens, TT_YR)) {
/* Parse the first argument name */
arg = parseIdentifierNode(&tokens);
if (!arg) goto parseFuncDefStmtNodeAbort;
/* Add the first argument to the arguments list */
status = addIdentifierNode(args, arg);
if (!status) goto parseFuncDefStmtNodeAbort;
arg = NULL;
/* Continue parsing argument indicators */
while (acceptToken(&tokens, TT_ANYR)) {
/* Parse the argument */
arg = parseIdentifierNode(&tokens);
if (!arg) goto parseFuncDefStmtNodeAbort;
/* Add the argument to the arguments list */
status = addIdentifierNode(args, arg);
if (!status) goto parseFuncDefStmtNodeAbort;
arg = NULL;
}
}
/* The function declaration should appear on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_STATEMENT, tokens);
goto parseFuncDefStmtNodeAbort;
}
/* Parse the body of the function */
body = parseBlockNode(&tokens);
if (!body) goto parseFuncDefStmtNodeAbort;
/* Parse the end-function token */
status = acceptToken(&tokens, TT_IFUSAYSO);
if (!status) {
parser_error_expected_token(TT_IFUSAYSO, tokens);
goto parseFuncDefStmtNodeAbort;
}
/* The end-function token should appear on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_STATEMENT, tokens);
goto parseFuncDefStmtNodeAbort;
}
/* Create the new FuncDefStmtNode structure */
stmt = createFuncDefStmtNode(scope, name, args, body);
if (!stmt) goto parseFuncDefStmtNodeAbort;
/* Create the new StmtNode structure */
ret = createStmtNode(ST_FUNCDEF, stmt);
if (!ret) goto parseFuncDefStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseFuncDefStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
else if (stmt) deleteFuncDefStmtNode(stmt);
else {
if (body) deleteBlockNode(body);
if (args) deleteIdentifierNodeList(args);
if (arg) deleteIdentifierNode(arg);
if (name) deleteIdentifierNode(name);
if (scope) deleteIdentifierNode(scope);
}
return NULL;
}
/**
* Parses tokens into an alternate array definition statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to an alternate array definition statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parseAltArrayDefStmtNode(Token ***tokenp)
{
IdentifierNode *name = NULL;
BlockNode *body = NULL;
IdentifierNode *parent = NULL;
AltArrayDefStmtNode *stmt = NULL;
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_ALTARRAYDEF");
#endif
/* Parse the alternate array definition token */
status = acceptToken(&tokens, TT_OHAIIM);
if (!status) {
parser_error_expected_token(TT_OHAIIM, tokens);
goto parseAltArrayDefStmtNodeAbort;
}
/* Parse the array name */
name = parseIdentifierNode(&tokens);
if (!name) goto parseAltArrayDefStmtNodeAbort;
/* Check for an optional array inheritance */
if (acceptToken(&tokens, TT_IMLIEK)) {
/* Parse the parent to inherit from */
parent = parseIdentifierNode(&tokens);
if (!parent) goto parseAltArrayDefStmtNodeAbort;
}
/* The alternate array definition token and name should appear on their
* own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_STATEMENT, tokens);
goto parseAltArrayDefStmtNodeAbort;
}
/* Parse the array definition body */
body = parseBlockNode(&tokens);
if (!body) goto parseAltArrayDefStmtNodeAbort;
/* The end-alternate array definition token should appear on its own
* line */
status = acceptToken(&tokens, TT_KTHX);
if (!status) {
parser_error_expected_token(TT_KTHX, tokens);
goto parseAltArrayDefStmtNodeAbort;
}
/* The end-function token should appear on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_STATEMENT, tokens);
goto parseAltArrayDefStmtNodeAbort;
}
/* Create the new AltArrayDefStmtNode structure */
stmt = createAltArrayDefStmtNode(name, body, parent);
if (!stmt) goto parseAltArrayDefStmtNodeAbort;
/* Create the new StmtNode structure */
ret = createStmtNode(ST_ALTARRAYDEF, stmt);
if (!ret) goto parseAltArrayDefStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseAltArrayDefStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
else if (stmt) deleteAltArrayDefStmtNode(stmt);
else {
if (name) deleteIdentifierNode(name);
if (body) deleteBlockNode(body);
}
return NULL;
}
/**
* Parses tokens into a library import statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a return statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parseImportStmtNode(Token ***tokenp)
{
IdentifierNode *value = NULL;
ImportStmtNode *stmt = NULL;
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_IMPORT");
#endif
/* Remove the library import keyword from the token stream */
status = acceptToken(&tokens, TT_CANHAS);
if (!status) {
parser_error_expected_token(TT_CANHAS, tokens);
goto parseImportStmtNodeAbort;
}
/* Parse the library name */
value = parseIdentifierNode(&tokens);
if (!value) goto parseImportStmtNodeAbort;
/* Check for the question mark token (currently optional) */
acceptToken(&tokens, TT_QUESTION);
/* The library import statement must reside on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseImportStmtNodeAbort;
}
/* Create the new ImportStmtNode structure */
stmt = createImportStmtNode(value);
if (!stmt) goto parseImportStmtNodeAbort;
/* Create the new StmtNode structure */
ret = createStmtNode(ST_IMPORT, stmt);
if (!ret) goto parseImportStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseImportStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
else if (stmt) deleteImportStmtNode(stmt);
else {
if (value) deleteIdentifierNode(value);
}
return NULL;
}
/**
* Parses tokens into a statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parseStmtNode(Token ***tokenp)
{
StmtNode *ret = NULL;
ExprNode *expr = NULL;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
shiftout();
#endif
/* Parse context-sensitive expressions */
if (peekToken(&tokens, TT_IDENTIFIER)
|| peekToken(&tokens, TT_SRS)) {
IdentifierNode *id = NULL;
/* Remove the identifier from the token stream */
id = parseIdentifierNode(&tokens);
if (!id) return NULL;
/* We do not need to hold onto it */
deleteIdentifierNode(id);
/* Casting */
if (peekToken(&tokens, TT_ISNOWA)) {
ret = parseCastStmtNode(tokenp);
}
/* Assignment */
else if (peekToken(&tokens, TT_R)) {
ret = parseAssignmentStmtNode(tokenp);
}
/* Variable declaration */
else if (peekToken(&tokens, TT_HASA)) {
ret = parseDeclarationStmtNode(tokenp);
}
/* Deallocation */
else if (peekToken(&tokens, TT_RNOOB)) {
ret = parseDeallocationStmtNode(tokenp);
}
/* Bare identifier expression */
else {
/* Reset state and continue parsing */
tokens = *tokenp;
#ifdef DEBUG
debug("ST_EXPR");
#endif
/* Parse the expression */
expr = parseExprNode(&tokens);
if (!expr) return NULL;
/* The expression should appear on its own line */
if (!acceptToken(&tokens, TT_NEWLINE)) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
deleteExprNode(expr);
return NULL;
}
/* Create the new StmtNode structure */
ret = createStmtNode(ST_EXPR, expr);
if (!ret) {
deleteExprNode(expr);
return NULL;
}
/* Since we're successful, update the token stream */
*tokenp = tokens;
}
}
/* Print */
else if (peekToken(&tokens, TT_VISIBLE)) {
ret = parsePrintStmtNode(tokenp);
}
/* Input */
else if (peekToken(&tokens, TT_GIMMEH)) {
ret = parseInputStmtNode(tokenp);
}
/* If/then/else */
else if (peekToken(&tokens, TT_ORLY)) {
ret = parseIfThenElseStmtNode(tokenp);
}
/* Switch */
else if (peekToken(&tokens, TT_WTF)) {
ret = parseSwitchStmtNode(tokenp);
}
/* Break */
else if (peekToken(&tokens, TT_GTFO)) {
ret = parseBreakStmtNode(tokenp);
}
/* Return */
else if (peekToken(&tokens, TT_FOUNDYR)) {
ret = parseReturnStmtNode(tokenp);
}
/* Loop */
else if (peekToken(&tokens, TT_IMINYR)) {
ret = parseLoopStmtNode(tokenp);
}
/* Function definition */
else if (peekToken(&tokens, TT_HOWIZ)) {
ret = parseFuncDefStmtNode(tokenp);
}
/* Alternate array definition */
else if (peekToken(&tokens, TT_OHAIIM)) {
ret = parseAltArrayDefStmtNode(tokenp);
}
/* Library import statement */
else if (peekToken(&tokens, TT_CANHAS)) {
ret = parseImportStmtNode(tokenp);
}
/* Bare expression */
else if ((expr = parseExprNode(&tokens))) {
int status;
#ifdef DEBUG
debug("ST_EXPR");
#endif
/* The expression should appear on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
deleteExprNode(expr);
return NULL;
}
/* Create the new StmtNode structure */
ret = createStmtNode(ST_EXPR, expr);
if (!ret) {
deleteExprNode(expr);
return NULL;
}
/* Since we're successful, update the token stream */
*tokenp = tokens;
}
else {
parser_error(PR_EXPECTED_STATEMENT, tokens);
}
#ifdef DEBUG
shiftin();
#endif
return ret;
}
/**
* Parses tokens into a code block.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a code block.
*
* \retval NULL Unable to parse.
*/
BlockNode *parseBlockNode(Token ***tokenp)
{
StmtNodeList *stmts = NULL;
StmtNode *stmt = NULL;
BlockNode *block = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
shiftout();
debug(">ET_BLOCK");
#endif
/* Create a list of statements */
stmts = createStmtNodeList();
if (!stmts) goto parseBlockNodeAbort;
/* Parse block until certain tokens are found */
while (!peekToken(&tokens, TT_EOF)
&& !peekToken(&tokens, TT_KTHXBYE)
&& !peekToken(&tokens, TT_OIC)
&& !peekToken(&tokens, TT_YARLY)
&& !peekToken(&tokens, TT_NOWAI)
&& !peekToken(&tokens, TT_MEBBE)
&& !peekToken(&tokens, TT_OMG)
&& !peekToken(&tokens, TT_OMGWTF)
&& !peekToken(&tokens, TT_IMOUTTAYR)
&& !peekToken(&tokens, TT_IFUSAYSO)
&& !peekToken(&tokens, TT_KTHX)) {
/* Parse the next statement */
stmt = parseStmtNode(&tokens);
if (!stmt) goto parseBlockNodeAbort;
/* Add the statement to the list */
status = addStmtNode(stmts, stmt);
if (!status) goto parseBlockNodeAbort;
stmt = NULL;
}
#ifdef DEBUG
debug("<ET_BLOCK");
shiftin();
#endif
/* Create the BlockNode structure */
block = createBlockNode(stmts);
if (!block) goto parseBlockNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return block;
parseBlockNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (block) deleteBlockNode(block);
else {
if (stmt) deleteStmtNode(stmt);
if (stmts) deleteStmtNodeList(stmts);
}
return NULL;
}
/**
* Parses tokens into a main code block.
*
* \param [in] tokens The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a main node block.
*
* \retval NULL Unable to parse.
*/
MainNode *parseMainNode(Token **tokens)
{
BlockNode *block = NULL;
MainNode *_main = NULL;
int status;
/* All programs must start with the HAI token */
status = acceptToken(&tokens, TT_HAI);
if (!status) {
parser_error_expected_token(TT_HAI, tokens);
goto parseMainNodeAbort;
}
/* Accept any version */
tokens++;
#ifdef DEBUG
debug("ET_MAINBLOCK");
#endif
/* Make sure the header line ends with a newline */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_STATEMENT, tokens);
goto parseMainNodeAbort;
}
/* Parse the main block of code */
block = parseBlockNode(&tokens);
if (!block) goto parseMainNodeAbort;
/* Make sure the program ends with KTHXBYE */
status = acceptToken(&tokens, TT_KTHXBYE);
if (!status) {
parser_error_expected_token(TT_KTHXBYE, tokens);
goto parseMainNodeAbort;
}
/* Create the MainBlockNode structure */
_main = createMainNode(block);
if (!_main) goto parseMainNodeAbort;
return _main;
parseMainNodeAbort: /* In case something goes wrong... */
/* Clean up any allocated structures */
if (_main) deleteMainNode(_main);
else if (block) deleteBlockNode(block);
return NULL;
}