mirror of
https://github.com/sfall-team/sslc.git
synced 2026-07-27 16:52:49 -07:00
Syntax improvements: foreach, for, variables
- ForEach: allow to declare variables for key & value header - ForEach: allow to use expression after `in` that start with a symbol without mandatory parantheses - Variables: allow to declare variables anywhere where statement is expected - Variables: simplify declaration code & array vars - Expressions: display useful error message when expression was expected but nothing was parsed
This commit is contained in:
@@ -288,6 +288,7 @@ Syntax which requires sfall for compiled scripts to be interpreted is marked by
|
||||
__NOTE:__ When using incremental operators like `+=`, `*=`, `++`, `--` compiler will use additional temp variable to get an array at penultimate level in order to avoid making the same chain of `get_array` calls twice.
|
||||
|
||||
- (*) `foreach` loops: A shorthand method of looping over all elements in an array. Syntax is `foreach (<symbol> in <expression>)`.
|
||||
- You can declare varaibles in-place by adding `variable` keyword before symbol name.
|
||||
- new:
|
||||
```
|
||||
procedure bingle begin
|
||||
|
||||
@@ -59,7 +59,6 @@ static void freeVariableList(VariableList *v);
|
||||
static void freeVariable(Variable *v);
|
||||
static void parseWhile(Procedure *p, NodeList *n);
|
||||
static int writeBlock(NodeList *n, int i, FILE *f);
|
||||
static int variable(VariableList *v, char **names, int type, ArrayVarList* arrays, int allowMulti);
|
||||
|
||||
extern FILE* parseroutput;
|
||||
|
||||
@@ -498,7 +497,7 @@ static void referenceProcedure(ProcedureList *p, int which) {
|
||||
reference(&p->procedures[which].numRefs, &p->procedures[which].references);
|
||||
}
|
||||
|
||||
static int addVariable(VariableList *var, char **namelist, int type, char *name) {
|
||||
int addVariable(VariableList *var, char **namelist, int type, char *name) {
|
||||
Variable *v = var->variables;
|
||||
int i;
|
||||
|
||||
@@ -526,7 +525,6 @@ static int addVariable(VariableList *var, char **namelist, int type, char *name)
|
||||
v[i].uses = 0;
|
||||
v[i].numRefs = 0;
|
||||
v[i].references = 0;
|
||||
v[i].arrayLen = -1;
|
||||
v[i].initialized = 0;
|
||||
v[i].declared = lexGetLineno(currentInputStream);
|
||||
v[i].fdeclared = lexGetFilename(currentInputStream);
|
||||
@@ -542,23 +540,10 @@ void GenTmpVar(Procedure *p, LexData* lex) {
|
||||
addVariable(&p->variables, &p->namelist, V_LOCAL, lex->stringData);
|
||||
}
|
||||
|
||||
static void AddArrayVar(ArrayVarList* arrays, ArrayVar* var) {
|
||||
if (arrays->size == 0) {
|
||||
arrays->vars = (ArrayVar*)malloc(sizeof(ArrayVar) * 4);
|
||||
arrays->size = 4;
|
||||
} else if (arrays->size == arrays->count) {
|
||||
arrays->size += 4;
|
||||
arrays->vars = (ArrayVar*)malloc(sizeof(ArrayVar) * arrays->size);
|
||||
}
|
||||
arrays->vars[arrays->count].name = (char*)malloc(strlen(var->name) + 1);
|
||||
strcpy(arrays->vars[arrays->count].name, var->name);
|
||||
arrays->vars[arrays->count].len = var->len;
|
||||
arrays->vars[arrays->count++].datasize = var->datasize;
|
||||
}
|
||||
static int defineVariable(VariableList *v, char **namelist, int type, ArrayVarList* arrays, int allowMulti) {
|
||||
int i;
|
||||
ArrayVar av;
|
||||
static int defineVariable(VariableList *v, char **namelist, int type, char allowArrays, int allowMulti) {
|
||||
int i, arraySize, arrayFlags;
|
||||
LexData symbol;
|
||||
Procedure* p;
|
||||
|
||||
do {
|
||||
if (expectToken(T_SYMBOL) == -1)
|
||||
@@ -575,19 +560,33 @@ static int defineVariable(VariableList *v, char **namelist, int type, ArrayVarLi
|
||||
parseSemanticError("Couldn't add variable %s.", lexData.stringData);
|
||||
|
||||
if (expectToken('[') != -1) {
|
||||
if (!arrays) parseSemanticError("Array variable declarations not allowed here.");
|
||||
av.name = lexData.stringData;
|
||||
av.datasize = 4;
|
||||
if (!allowArrays) parseSemanticError("Array variable declarations not allowed here.");
|
||||
if (expectToken(T_CONSTANT) == -1) parseError("Initialization of array bounds with non-constant.");
|
||||
if (lexData.type != T_INT) parseError("Initialization of array bounds with non-integer.");
|
||||
av.len = lexData.intData;
|
||||
arraySize = lexData.intData;
|
||||
if (expectToken(',') != -1) {
|
||||
if (expectToken(T_CONSTANT) == -1) parseError("Initialization of array data size with non-constant.");
|
||||
if (lexData.type != T_INT) parseError("Initialization of array data size with non-integer.");
|
||||
av.datasize = lexData.intData;
|
||||
if (expectToken(T_CONSTANT) == -1) parseError("Initialization of array flags with non-constant.");
|
||||
if (lexData.type != T_INT) parseError("Initialization of array flags with non-integer.");
|
||||
arrayFlags = lexData.intData;
|
||||
}
|
||||
else arrayFlags = 4;
|
||||
|
||||
if (expectToken(']') == -1) parseError("Expected ']'");
|
||||
AddArrayVar(arrays, &av);
|
||||
|
||||
p = currentProcedure;
|
||||
emitOp(p, &p->nodes, T_START_STATEMENT);
|
||||
emitNode(p, &p->nodes, &symbol);
|
||||
emitOp(p, &p->nodes, T_ASSIGN);
|
||||
emitOp(p, &p->nodes, T_START_EXPRESSION);
|
||||
emitOp(p, &p->nodes, T_TS_TEMP_ARRAY);
|
||||
emitOp(p, &p->nodes, T_START_EXPRESSION);
|
||||
emitInt(p, &p->nodes, arraySize);
|
||||
emitOp(p, &p->nodes, T_END_EXPRESSION);
|
||||
emitOp(p, &p->nodes, T_START_EXPRESSION);
|
||||
emitInt(p, &p->nodes, arrayFlags);
|
||||
emitOp(p, &p->nodes, T_END_EXPRESSION);
|
||||
emitOp(p, &p->nodes, T_END_EXPRESSION);
|
||||
emitOp(p, &p->nodes, T_END_STATEMENT);
|
||||
} else if (expectToken(T_ASSIGN) != -1) {
|
||||
char buf[1024];
|
||||
int allowExpr = (allowMulti && type == V_LOCAL);
|
||||
@@ -610,7 +609,7 @@ static int defineVariable(VariableList *v, char **namelist, int type, ArrayVarLi
|
||||
}
|
||||
} while (allowMulti && expectToken(',') != -1);
|
||||
if (expectToken(';') == -1) {
|
||||
if (arrays && backwardcompat == 0) parseError("Expected ';' at end of variable declaration.");
|
||||
if (allowArrays && backwardcompat == 0) parseError("Expected ';' at end of variable declaration.");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -780,7 +779,6 @@ static int externVariable(VariableList *v, char **namelist, int type, int flag)
|
||||
|
||||
i = addVariable(v, namelist, type, lexData.stringData);
|
||||
|
||||
v->variables[i].arrayLen = -1;
|
||||
v->variables[i].declared = lexGetLineno(currentInputStream);
|
||||
v->variables[i].fdeclared = lexGetFilename(currentInputStream);
|
||||
|
||||
@@ -800,7 +798,7 @@ static int externVariable(VariableList *v, char **namelist, int type, int flag)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int findVariableIndex(char *var, VariableList *v, char *namelist) {
|
||||
int findVariableIndex(char *var, VariableList *v, char *namelist) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < v->numVariables; ++i) {
|
||||
@@ -885,41 +883,22 @@ static int export(Program *p, char **names) {
|
||||
}
|
||||
|
||||
/* Parse the syntax for declaring global and local variables of the procedures */
|
||||
static int variable(VariableList *v, char **names, int type, ArrayVarList* arrays, int allowMulti) {
|
||||
static int variable(VariableList *v, char **names, int type, char allowArrays, int allowMulti) {
|
||||
if (expectToken(T_VARIABLE) == -1) return 1;
|
||||
|
||||
do {
|
||||
if (expectToken(T_BEGIN) != -1) { // sfall addition
|
||||
while (expectToken(T_END) == -1) {
|
||||
if (defineVariable(v, names, type, arrays, allowMulti))
|
||||
return 1;
|
||||
}
|
||||
} else if (expectToken(T_SYMBOL) != -1) {
|
||||
ungetToken();
|
||||
if (defineVariable(v, names, type, arrays, allowMulti))
|
||||
if (expectToken(T_BEGIN) != -1) { // sfall addition
|
||||
if (!allowMulti) parseError("Unexpected 'begin' block.");
|
||||
while (expectToken(T_END) == -1) {
|
||||
if (defineVariable(v, names, type, allowArrays, allowMulti))
|
||||
return 1;
|
||||
} else
|
||||
parseError("Expected variable name symbol or 'begin' block.");
|
||||
} while (expectToken(T_VARIABLE) != -1);
|
||||
}
|
||||
} else if (expectToken(T_SYMBOL) != -1) {
|
||||
ungetToken();
|
||||
if (defineVariable(v, names, type, allowArrays, allowMulti))
|
||||
return 1;
|
||||
} else
|
||||
parseError("Expected variable name symbol or 'begin' block.");
|
||||
|
||||
ungetToken(); // put back what was there
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Parse the syntax for declaring local variables in the procedure body without the 'begin...end' block
|
||||
added sfall 4.2.7
|
||||
*/
|
||||
static int VariableParse(VariableList *v, char **names, int type, ArrayVarList* arrays) {
|
||||
do {
|
||||
if (expectToken(T_SYMBOL) != -1) {
|
||||
ungetToken();
|
||||
if (defineVariable(v, names, type, arrays, 1)) return 1;
|
||||
} else
|
||||
parseError("Expected variable name symbol.");
|
||||
} while (expectToken(T_VARIABLE) != -1);
|
||||
|
||||
ungetToken(); // put back what was there
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1044,7 +1023,7 @@ static void parseVariableRef(Procedure *p, NodeList *nodes, LexData* d, int refS
|
||||
}
|
||||
|
||||
static void factor(Procedure *p, NodeList *nodes) {
|
||||
int i, refSyntax = 0;
|
||||
int i, refSyntax = 0, numNodes = nodes->numNodes;
|
||||
|
||||
i = lex();
|
||||
if (i == '@') {
|
||||
@@ -1140,6 +1119,9 @@ static void factor(Procedure *p, NodeList *nodes) {
|
||||
break;
|
||||
default:
|
||||
parseLibExpression(p, nodes, i);
|
||||
if (nodes->numNodes == numNodes) {
|
||||
parseError("Expression expected.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1602,66 +1584,37 @@ static void parseStatementInternal(Procedure *p, char requireSemicolon) {
|
||||
emitOp(p, &p->nodes, T_END_STATEMENT);
|
||||
}
|
||||
|
||||
static void parseStatementOrLocalVariables(Procedure *p, char requireSemicolon) {
|
||||
if (variable(&p->variables, &p->namelist, V_LOCAL, 1, 1))
|
||||
parseStatementInternal(p, requireSemicolon);
|
||||
}
|
||||
|
||||
void parseStatement(Procedure *p) {
|
||||
parseStatementInternal(p, 1);
|
||||
parseStatementOrLocalVariables(p, 1);
|
||||
}
|
||||
|
||||
// sfall addition
|
||||
void parseStatementNoSemicolon(Procedure *p) {
|
||||
parseStatementInternal(p, 0);
|
||||
parseStatementOrLocalVariables(p, 0);
|
||||
}
|
||||
|
||||
static void parseBlock(Procedure *p) {
|
||||
int i;
|
||||
ArrayVarList arrays;
|
||||
LexData tlex;
|
||||
arrays.vars = 0;
|
||||
arrays.size = 0;
|
||||
arrays.count = 0;
|
||||
|
||||
if (expectToken(T_BEGIN) == -1)
|
||||
parseError("expected 'begin'.");
|
||||
|
||||
emitNode(p, &p->nodes, &lexData); // emit the begin
|
||||
|
||||
variable(&p->variables, &p->namelist, V_LOCAL, &arrays, 1);
|
||||
|
||||
for(i = 0; i < arrays.count; i++) {
|
||||
emitOp(p, &p->nodes, T_START_STATEMENT);
|
||||
tlex.token = T_SYMBOL;
|
||||
tlex.stringData = arrays.vars[i].name;
|
||||
emitNode(p, &p->nodes, &tlex);
|
||||
emitOp(p, &p->nodes, T_ASSIGN);
|
||||
emitOp(p, &p->nodes, T_START_EXPRESSION);
|
||||
emitOp(p, &p->nodes, T_TS_TEMP_ARRAY);
|
||||
emitOp(p, &p->nodes, T_START_EXPRESSION);
|
||||
emitInt(p, &p->nodes, arrays.vars[i].len);
|
||||
emitOp(p, &p->nodes, T_END_EXPRESSION);
|
||||
emitOp(p, &p->nodes, T_START_EXPRESSION);
|
||||
emitInt(p, &p->nodes, arrays.vars[i].datasize);
|
||||
emitOp(p, &p->nodes, T_END_EXPRESSION);
|
||||
emitOp(p, &p->nodes, T_END_EXPRESSION);
|
||||
emitOp(p, &p->nodes, T_END_STATEMENT);
|
||||
}
|
||||
|
||||
while ((i = lex()) != T_END) {
|
||||
if (i == T_EOF)
|
||||
if (i == T_EOF) {
|
||||
parseError("Premature EOF encountered.");
|
||||
else if (i == T_VARIABLE) { // sfall addition (Fakels)
|
||||
VariableParse(&p->variables, &p->namelist, V_LOCAL, &arrays);
|
||||
} else {
|
||||
ungetToken();
|
||||
parseStatement(p);
|
||||
}
|
||||
}
|
||||
emitNode(p, &p->nodes, &lexData); // emit the end
|
||||
|
||||
if (arrays.vars) {
|
||||
for(i = 0; i < arrays.count; i++) {
|
||||
free(arrays.vars[i].name);
|
||||
}
|
||||
free(arrays.vars);
|
||||
}
|
||||
}
|
||||
|
||||
static void parseIf(Procedure *p, NodeList *n) {
|
||||
|
||||
@@ -45,7 +45,6 @@ typedef struct {
|
||||
int numRefs;
|
||||
Value value;
|
||||
int type; // this type is where it was declared
|
||||
int arrayLen;
|
||||
int declared;
|
||||
const char* fdeclared;
|
||||
int uses;
|
||||
@@ -154,6 +153,9 @@ extern int expectToken(int expectToken);
|
||||
extern void freeCurrentProgram(void);
|
||||
extern char *getName(int offset, char *namelist);
|
||||
|
||||
extern int addVariable(VariableList *var, char **namelist, int type, char *name);
|
||||
extern int findVariableIndex(char *var, VariableList *v, char *namelist);
|
||||
|
||||
#define P_GLOBAL 0x80000000
|
||||
#define P_LOCAL 0x40000000
|
||||
#define P_PROCEDURE 0x20000000
|
||||
|
||||
+16
-5
@@ -205,34 +205,45 @@ void parseFor(Procedure *p, NodeList *n) {
|
||||
|
||||
void parseForEach(Procedure *p, NodeList *n) {
|
||||
LexData symbolKey, symbolVal, a, len, count;
|
||||
char hasKey = 0, emitEnd = 0, hasParan = 0;
|
||||
char hasKey = 0, emitEnd = 0, hasParan = 0, isSymbol = 0, addVars = 0;
|
||||
if (expectToken('(') != -1) {
|
||||
hasParan = 1;
|
||||
}
|
||||
if (expectToken(T_VARIABLE) != -1) {
|
||||
addVars = 1;
|
||||
}
|
||||
if (expectToken(T_SYMBOL) == -1) parseError("Expected symbol");
|
||||
CloneLexData(&symbolVal, &lexData);
|
||||
if (addVars && addVariable(&p->variables, &p->namelist, V_LOCAL, lexData.stringData) == -1) {
|
||||
parseSemanticError("Couldn't add variable %s.", lexData.stringData);
|
||||
}
|
||||
if (expectToken(':') != -1) {
|
||||
symbolKey = symbolVal;
|
||||
if(expectToken(T_SYMBOL) == -1) parseError("Expected symbol for value");
|
||||
CloneLexData(&symbolVal, &lexData);
|
||||
if (addVars && addVariable(&p->variables, &p->namelist, V_LOCAL, lexData.stringData) == -1) {
|
||||
parseSemanticError("Couldn't add variable %s.", lexData.stringData);
|
||||
}
|
||||
hasKey = 1;
|
||||
}
|
||||
|
||||
if (expectToken(T_IN) == -1) parseError("Expected 'in'");
|
||||
if (expectToken(T_SYMBOL) == -1) {
|
||||
isSymbol = expectToken(T_SYMBOL) != -1;
|
||||
if (isSymbol && findVariableIndex(lexData.stringData, &p->variables, p->namelist) != -1) {
|
||||
CloneLexData(&a, &lexData);
|
||||
} else {
|
||||
if (isSymbol) ungetToken();
|
||||
GenTmpVar(p, &a);
|
||||
emitOp(p, n, T_START_STATEMENT);
|
||||
emitNode(p, n, &a);
|
||||
emitOp(p, n, T_ASSIGN);
|
||||
parseExpression(p, n);
|
||||
emitOp(p, n, T_END_STATEMENT);
|
||||
} else {
|
||||
CloneLexData(&a, &lexData);
|
||||
}
|
||||
|
||||
GenTmpVar(p, &len);
|
||||
GenTmpVar(p, &count);
|
||||
if (!hasKey) GenTmpVar(p, &symbolKey);
|
||||
if (!hasKey && !addVars) GenTmpVar(p, &symbolKey);
|
||||
|
||||
//count:=0;
|
||||
emitOp(p, n, T_START_STATEMENT);
|
||||
|
||||
-11
@@ -5,17 +5,6 @@
|
||||
Extended SSL syntax for sfall
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
int len;
|
||||
int datasize;
|
||||
} ArrayVar;
|
||||
typedef struct {
|
||||
ArrayVar* vars;
|
||||
int count;
|
||||
int size;
|
||||
} ArrayVarList;
|
||||
|
||||
void appendNodeListPart(NodeList* dst, const NodeList* src, int offset, int length);
|
||||
void appendNodeList(NodeList* dst, const NodeList* src);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user