Files
sslc/parse.c
T
Vasilii RoginandGitHub 5a939a8ff2 Add Linux and Nodejs build targets (#14)
* Add CI build

* fix

* Update toolset

* Use v141_xp

* Run on win2019

* Fixes

* Link libs statically

* Use vcxproj

* Experiment: no spaces in configuration

* Experiments with build

* Commented Fallout1 builds

* Comment Win2k builds

* Exclude ./out/ from git

* Add compat headers

* Use one compat file

* Fixes for build

* Include compat

* Add guard for compat.c

* Add CMakeLists

* Add linux x32 build

* Small improvements to build steps

* Do not install build-essential

* gRevert "Do not install build-essential"

This reverts commit 434d8e9e98.

* Build wasi

* Fixing CI pipeline

* Fix wasi-sdk download url

* Use "" for version

* Fix wasm build

* Fix wasi build

* Disable WASI build due to setjmp/longjmp

* Adding emscripten support

* Fixes for builds

* Include nodefs

* Add emscripten build into CI

* Build release version from github

* Emit mjs file

* Add environment node

* Use emscripten 4.0.8

* Add comment

* Use PATH_MAX

* Move all builds into one file

* Use bash for linux builds only

* Rename artifacts names

* Add auto-releases

* Add debug ls

* Fix artifacts names

* Remove folder from tar archive

* Fix artifact name

* Enable release only on push

* Release other builds too

* fix names

* Fix names

* Enable deploy only on push

* Remove useless line

* Use NODERAWFS & format CMakeLists

* Add node-rawfs into emscripten build name

* Delete tmp file on non-windows platforms

* Rename tmp_file_name

* Remove temp file on posix platforms

* Revert "Remove temp file on non windows platforms"

* Use nodefs instead of noderawfs

* Fix ChatGPT code

* Add simple tests

* Fixes to tests

* Fixes for tests run

* Small improvements

* Rename variable

* Small refactor of test runner

* Remove warnings which shows folder name

* Add minimal package.json

* fix package.json main

* Fixes for package.json

* Fixes for npm packaging

* Use bin for all in cmake

* Use package subfolder in emscripten ci build

* Update CI

* Add compiler.mjs as binary

* Add test into CI

* Bump version

* Thinking about running on fallout2 rpu

* Add some tests of Fallout RPU

* Adding tests for RPU

* Debugging

* Small improvements

* Fixes

* Remove comment (there is no problem with asb paths because it runs on the same system)

* Experimental: Add RPU test into CI pipeline

* CI fixes

* Fixes

* Use set -e

* Add debug for windows build

* Add install 7z

* Fixes for test runner

* Fixes for test runner

* Fix symlink creation

* Fixing emscripten runner

* Add comments

* Skip running long test on debug build

* Add comment

* Use relative symlink for emscripten builds

* Show stderr/stdout on wasm crash

* Increase stack size to fix emscripten crashes

* Add option to skip rpu check by using label

* Add additinal label to skip wasm check because they are really really slow

* Remove useless line

* Add daemon mode for compiler

* Use nodejs daemon mode in CI

* Add debug for daemon start

* Fix daemon start in CI

* Add some errors check for daemon mode

* Fix daemon cwd

* Reduce logging for daemon

* use node 22 for emscripten tests runner

* Split RPU setup and test run, use cache

* Fix order: first try cache

* Fix type in CI action

* Fix dependencies in CI

* Add fail-on-cache-miss

* Install wine on test setup

* Bump cache version

* Add comment

* Fix for different caches

* Fix bash script

* Bump cache version

* Check that snapshot file exists

* Add "bin" option into package.json

* Add comment

* Fix checks for RPU

* Improve testing logic

* Do not install wine if it is already installed

* Add some sanity checks

* Remove useless check

* Check compiler on setup stage

* Bump cache version to check setup script on all platforms

* Add set -u

* Fix env varibales, check wine in bash

* Remove unused variable now

* Bump cache version

* Suppress warning on Windows when checking for apt

* Disable asyncify, reduce memory growth

* Remove export all

* Use O2 for release

* Revert exports back because we need FS

* Remove emscripted daemon mode from CI

* Remove CI daemon mode from emscripten compiler

* Adding utils.bash file

* Move utils

* Move embedded test files

* Move gitignore

* Use utils for embedded tests too

* Remove useless assignment

* Fix Windows regular tests

* Rename folder name

* Add some test files

* Add failing .ssl files

* Replace paths for embedded tests too

* Add expected results for failing compilations

* Remove spaces in bash script

* Use new line for reports

* Remove unused variable in test run

* Use new line for test runner too

* Use windows-latest

* Try windows 2022

* Revert "Try windows 2022"

This reverts commit 62f6036fae.

* Try to use latest but install vs2017

* Do vcvars from vs2017

* Use vs2019

* Change step name

* List directories

* Use VsDevCmd

* Add cmd as shell for dir

* use multi-line

* Fix multiline

* remove duplicate

* Use 2017

* Try to install directly

* Use cmd as shell

* Add sdk to install

* Use call

* Add VC.Tools.v141;

* Add includeRecommended

* Add dir command

* More components

* Use choco

* Into one line

* Use windows-2025

* Use 2019

* Trying to use vs2019

* Remove choco cache because it does not work properly

* Disable ES6 export

* Remove wrong commit

* Enforce EXPORT_ES6=0

* Revert back to 0ef49cbb01

* Add separate mjs file with __filename
2025-06-12 14:22:24 +02:00

2098 lines
55 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <setjmp.h>
#include "parse.h"
#include "parselib.h"
#include "extra.h"
#include "opcodes.h"
#include "oplib.h"
#include "opextra.h"
#include "gencode.h"
#include "lex.h"
#include "parseext.h"
#ifndef _WIN32
#include "compat.h"
#endif
int compilerErrorTotal;
char compilerSyntaxError;
extern int backwardcompat;
extern int warnings;
extern int optimize;
extern int debug;
extern int dumpTree;
extern int shortCircuit;
void optimizeTree(Program *program);
/*
* Parser for SSL (Startrek Scripting Language).
*
* All variables are "typeless"; their type is defined by how
* they are used. All types are promoted to the "highest" type
* in an expression, where the order is from lowest to highest:
* int, float, string.
*
* So,
* if you do 2 + "foo", you get back "2foo".
* if you do 2 + 2.4, you get 4.4
* if you do 2 + 4, you get 6 (hopefully :)
* if you do 2 + 2.4 + "foo", you get "4.4foo", since
* expressions are parsed left to right.
* if you do 2 + (2.4 + "foo") you get "22.4foo", due
* to the parentheses.
* etc.
*/
Program *currentProgram;
static Procedure *currentProcedure;
static InputStream *currentInputStream;
static InputStream *includes[32];
static int tois = -1;
static int tmpCounter = 0;
int loopNesting = 0;
int expressionNesting = 0;
static void freeVariableList(VariableList *v);
static void freeVariable(Variable *v);
static void parseWhile(Procedure *p, NodeList *n);
static int variable(VariableList *v, char **names, int type, char allowArrays, int allowMulti);
extern int writeBlock(NodeList *n, int i, FILE *f);
extern FILE* parseroutput;
#ifdef BUILDING_DLL
int outputStr(const char *s) {
if (parseroutput)
fprintf(parseroutput, "%s", s);
return 1;
}
int vparseOutput(const char *format, va_list args) {
int v = 0;
if (parseroutput)
vfprintf(parseroutput, format, args);
return v;
}
int parseOutput(const char *format, ...) {
int i=0;
if (parseroutput) {
va_list arg;
va_start(arg, format);
i = vfprintf(parseroutput, format, arg);
va_end(arg);
}
return i;
}
#else
int outputStr(const char *s) {
fprintf(stdout, "%s", s);
return 1;
}
int vparseOutput(const char *format, va_list args) {
return vfprintf(stdout, format, args);
}
int parseOutput(const char *format, ...) {
int i;
va_list arg;
va_start(arg, format);
i = vfprintf(stdout, format, arg);
va_end(arg);
return i;
}
#endif
void parseMessageAtNode(const Node* node, const char *format, ...) {
char buf[256];
int i;
va_list arg;
if (!debug) return;
va_start(arg, format);
i = vsprintf(buf, format, arg);
if (node)
parseOutput("[Message] <%s>:%d:%d: %s\n", lexGetFilename(currentInputStream), node->lineNum, node->column, buf);
else
parseOutput("[Message] <none>:%d: %s\n", -1, buf);
va_end(arg);
}
void parseWarningAtNode(const Node* node, const char *format, ...) {
char buf[256];
int i;
va_list arg;
if (!warnings) return;
va_start(arg, format);
i = vsprintf(buf, format, arg);
if (node)
parseOutput("[Warning] <%s>:%d:%d: %s\n", lexGetFilename(currentInputStream), node->lineNum, node->column, buf);
else
parseOutput("[Warning] <none>:%d: %s\n", -1, buf);
va_end(arg);
}
/*
void parseOutputWithOrigin(const char* type, int lineNo, int column, const char *format, ...) {
char buf[256];
int i;
va_list arg;
va_start(arg, format);
i = vsprintf(buf, format, arg);
if (lineNo == -1) {
parseOutput("[%s] <none>:-1: Warning during code generation: %s\n", type, buf);
} else {
parseOutput("[%s] <%s>:%d:%d: %s\n", type, lexGetFilename(currentInputStream), lineNo, column, buf);
}
va_end(arg);
}
*/
void parseWarning(const char *format, ...) {
char buf[256];
int i;
va_list arg;
if (!warnings) return;
va_start(arg, format);
i = vsprintf(buf, format, arg);
if (currentInputStream->lineno == -1)
parseOutput("[Warning] <none>:-1: Warning during code generation: %s\n", buf);
else
parseOutput("[Warning] <%s>:%d:%d: %s\n", lexGetFilename(currentInputStream), lexGetLineno(currentInputStream), lexGetColumn(currentInputStream), buf);
va_end(arg);
}
void parseErrorAtNode(const Node* node, const char *format, ...) {
char buf[256];
int i;
va_list arg;
va_start(arg, format);
i = vsprintf(buf, format, arg);
if (node)
parseOutput("[Error] <%s>:%d:%d: %s\n", lexGetFilename(currentInputStream), node->lineNum, node->column, buf);
else
parseOutput("[Error] <none>:%d: %s\n", -1, buf);
va_end(arg);
compilerErrorTotal++;
longjmp(currentProgram->env, 1);
}
void parseError(const char *format, ...) {
char buf[256];
int i;
va_list arg;
va_start(arg, format);
i = vsprintf(buf, format, arg);
if (currentInputStream->lineno == -1)
parseOutput("[Error] <none>:-1: Error during code generation: %s\n", buf);
else
parseOutput("[Error] <%s>:%d:%d: %s\n", lexGetFilename(currentInputStream), lexGetLineno(currentInputStream), lexGetColumn(currentInputStream), buf);
va_end(arg);
compilerErrorTotal++;
longjmp(currentProgram->env, 1);
}
void parseSemanticError(const char *format, ...) {
char buf[256];
int i;
va_list arg;
va_start(arg, format);
i = vsprintf(buf, format, arg);
if (currentInputStream->lineno == -1)
parseOutput("[Error] <Semantic> <none>:-1: Error during code generation: %s\n", buf);
else
parseOutput("[Error] <Semantic> <%s>:%d:%d: %s\n", lexGetFilename(currentInputStream), lexGetLineno(currentInputStream), lexGetColumn(currentInputStream), buf);
va_end(arg);
#ifndef BUILDING_DLL
compilerErrorTotal++;
longjmp(currentProgram->env, 1);
#else
compilerSyntaxError = 1;
#endif
}
static void freeVariableList(VariableList *v) {
int i;
if (v->variables) {
for (i = 0; i < v->numVariables; ++i)
freeVariable(v->variables + i);
free(v->variables);
}
}
static void freeProcedure(Procedure *p) {
if (p->namelist) free(p->namelist);
freeVariableList(&p->variables);
}
static void freeProcedureList(ProcedureList *p) {
int i;
if (p->procedures) {
for (i = 0; i < p->numProcedures; ++i)
freeProcedure(p->procedures + i);
free(p->procedures);
}
}
int expectToken(int expectToken) {
int token;
token = lex();
if (token != expectToken) {
ungetToken();
return -1;
}
return token;
}
static int findName(char *namelist, char *name) {
char *n;
if (!namelist) return -1;
n = namelist + 4;
while (*(unsigned short *)n != 0xffff) {
if (_stricmp(n + 2, name) == 0)
return n + 2 - namelist;
n += *(unsigned short *)n + 2;
}
return -1;
}
static int findString(char *namelist, char *name) {
char *n;
if (!namelist) return -1;
n = namelist + 4;
while (*(unsigned short *)n != 0xffff) {
if (strcmp(n + 2, name) == 0)
return n + 2 - namelist;
n += *(unsigned short *)n + 2;
}
return -1;
}
char *getName(int offset, char *namelist) {
return namelist + offset;
}
/*
* Add a string to a namelist list.
* namelists are defined by <4-byte total len><string><string> ...
* where a <string> is <2-byte len><string data><one or 2 zero>.
* lengths do not include the headers; the total len *does*
* include the headers for each particular string.
* A length field of 0xffff indicates end of the namelist.
*
* To get the string for any name (be that the name of a variable,
* or a string in a string variable, since I store both as 'names'),
* you have to have the namelist defining that name.
*/
static int addName(char **namelist, char *name) {
unsigned short slen = strlen(name);
long tlen;
char *n = *namelist;
char *c;
int odd = 0;
slen++; /* increment one for the null */
if (slen & 1) {/* odd? */
slen++;
odd = 1;
}
if (!n) {
n = (char*)malloc(4 + 2 + slen + 2);
*(long *)n = 2 + slen;
c = n + 4;
}
else {
int i;
tlen = *(long *)n;
i = findName(n, name);
if (i != -1)
return i;
/* 4 for main header
tlen for old total length
2 for this string's header
slen for this string's length
2 for ending length
*/
n = (char*)realloc(n, 4 + tlen + 2 + slen + 2);
*(long *)n = tlen + 2 + slen;
c = n + 4 + tlen;
}
*(unsigned short *)c = slen;
*(unsigned short *)(c + 2 + slen) = 0xffff;
*namelist = n;
strcpy(c + 2, name);
if (odd) *(c + 2 + slen - 1) = 0;
return c + 2 - n;
}
static int addString(char **namelist, char *name) {
unsigned short slen = strlen(name);
long tlen;
char *n = *namelist;
char *c;
int odd = 0;
slen++; /* increment one for the null */
if (slen & 1) {/* odd? */
slen++;
odd = 1;
}
if (!n) {
n = (char*)malloc(4 + 2 + slen + 2);
*(long *)n = 2 + slen;
c = n + 4;
}
else {
int i;
tlen = *(long *)n;
i = findString(n, name);
if (i != -1)
return i;
/* 4 for main header
tlen for old total length
2 for this string's header
slen for this string's length
2 for ending length
*/
n = (char*)realloc(n, 4 + tlen + 2 + slen + 2);
*(long *)n = tlen + 2 + slen;
c = n + 4 + tlen;
}
*(unsigned short *)c = slen;
*(unsigned short *)(c + 2 + slen) = 0xffff;
*namelist = n;
strcpy(c + 2, name);
if (odd) *(c + 2 + slen - 1) = 0;
return c + 2 - n;
}
static void assignValue(Value *v, LexData *what) {
switch (what->type) {
case T_INT:
v->type = V_INT;
v->intData = what->intData;
break;
case T_FLOAT:
v->type = V_FLOAT;
v->floatData = what->floatData;
break;
case T_STRING:
v->type = V_STRING;
v->stringData = addString(&currentProgram->stringspace, what->stringData);
break;
}
}
static void constantExpression(LexData* result) {
int parens = 0;
int unaryOperator = 0;
while (expectToken('(') != -1) parens++;
if (expectToken('-') != -1 || expectToken(T_NOT) != -1 || expectToken(T_BWNOT) != -1) {
unaryOperator = lexData.token;
}
if (expectToken(T_CONSTANT) == -1) {
parseError("Constant expected.");
}
switch (unaryOperator) {
case '-':
switch (lexData.type) {
case T_INT:
lexData.intData = -lexData.intData;
break;
case T_FLOAT:
lexData.floatData = -lexData.floatData;
break;
}
break;
case T_NOT:
switch (lexData.type) {
case T_INT:
lexData.intData = !lexData.intData;
break;
case T_FLOAT:
lexData.type = T_INT;
lexData.intData = !(int)lexData.floatData;
break;
}
break;
case T_BWNOT:
switch (lexData.type) {
case T_INT:
lexData.intData = ~lexData.intData;
break;
case T_FLOAT:
lexData.type = T_INT;
lexData.intData = ~(int)lexData.floatData;
break;
}
break;
}
*result = lexData;
for (; parens > 0; --parens) {
if (expectToken(')') == -1) {
parseError("')' expected.");
}
}
}
static void assignVariable(VariableList *v, int which, LexData *what) {
assignValue(&v->variables[which].value, what);
v->variables[which].initialized = 1;
}
static void reference(int* numrefs, int** refs) {
if (!*refs) {
*refs = (int*)malloc(8 * 8);
//#ifndef BUILDING_DLL // Fakels: fixes list of references for sfall Script Editor Ext
} else if (refs[0][numrefs[0] * 2 - 2] == lexGetLineno(currentInputStream) && refs[0][numrefs[0] * 2 - 1] == (int)lexGetFilename(currentInputStream)) {
return;
//#endif
} else if (!(numrefs[0] % 8)) {
*refs = (int*)realloc(*refs, (numrefs[0] + 9) * 8);
}
refs[0][numrefs[0] * 2] = lexGetLineno(currentInputStream);
refs[0][numrefs[0] * 2 + 1] = (int)lexGetFilename(currentInputStream);
numrefs[0]++;
}
static void referenceVariable(VariableList *v, int which) {
v = v; which = which;
//v->variables[which].uses++;
reference(&v->variables[which].numRefs, &v->variables[which].references);
}
static void referenceProcedure(ProcedureList *p, int which) {
p->procedures[which].uses++;
reference(&p->procedures[which].numRefs, &p->procedures[which].references);
}
int addVariable(VariableList *var, char **namelist, int type, char *name) {
Variable *v = var->variables;
int i;
if (!v) {
v = (Variable*)malloc(sizeof(Variable));
i = 0;
}
else {
for (i = 0; i < var->numVariables; ++i)
if (_stricmp(getName(v[i].name, *namelist), name) == 0)
break;
if (i == var->numVariables)
v = (Variable*)realloc(v, sizeof(Variable) * (i + 1));
else {
parseWarning("Redefinition of variable %s\n", name);
return i;
}
}
v[i].name = addName(namelist, name);
v[i].type = type;
v[i].value.type = V_INT;
v[i].value.intData = 0;
v[i].uses = 0;
v[i].numRefs = 0;
v[i].references = 0;
v[i].initialized = 0;
v[i].declared = lexGetLineno(currentInputStream);
v[i].fdeclared = lexGetFilename(currentInputStream);
var->variables = v;
var->numVariables++;
return i;
}
void GenTmpVar(Procedure *p, LexData* lex) {
lex->stringData = (char*)malloc(16);
lex->token = T_SYMBOL;
sprintf_s(lex->stringData, 16, "tmp.%d", tmpCounter++);
addVariable(&p->variables, &p->namelist, V_LOCAL, lex->stringData);
}
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)
parseError("Expecting name symbol.");
symbol = lexData;
if (findName(*namelist, lexData.stringData) != -1)
parseSemanticError("Redefinition of %s.", lexData.stringData);
i = addVariable(v, namelist, type, lexData.stringData);
if (i == -1)
parseSemanticError("Couldn't add variable %s.", lexData.stringData);
if (expectToken('[') != -1) {
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.");
arraySize = lexData.intData;
if (expectToken(',') != -1) {
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 ']'");
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);
strcpy(buf, symbol.stringData);
symbol.stringData = buf;
if (allowExpr) {
NodeList tmpN = { 0, 0 };
p = currentProcedure;
parseExpression(p, &tmpN);
// If expression has just one constant, simply assign it as variable value, otherwise write assign statement.
if (tmpN.numNodes == 3 && tmpN.nodes[1].token == T_CONSTANT) {
v->variables[i].value = tmpN.nodes[1].value;
v->variables[i].initialized = 1;
}
else {
emitOp(p, &p->nodes, T_START_STATEMENT);
emitNode(p, &p->nodes, &symbol);
emitOp(p, &p->nodes, T_ASSIGN);
appendNodeList(&p->nodes, &tmpN);
emitOp(p, &p->nodes, T_END_STATEMENT);
}
free(tmpN.nodes);
} else {
LexData assignConstant;
constantExpression(&assignConstant);
assignVariable(v, i, &assignConstant);
}
}
} while (allowMulti && expectToken(',') != -1);
if (expectToken(';') == -1) {
if (allowArrays && backwardcompat == 0) parseError("Expected ';' at end of variable declaration.");
}
return 0;
}
static Procedure *findProcedure(ProcedureList *procs, char **namelist, char *name) {
int i;
if (!*namelist || !procs || !procs->procedures) return 0;
for (i = 0; i < procs->numProcedures; ++i) {
if (_stricmp(getName(procs->procedures[i].name, *namelist), name) == 0)
return procs->procedures + i;
}
return 0;
}
static int findProcedureIndex(ProcedureList *procs, char **namelist, char *name) {
int i;
if (!*namelist || !procs || !procs->procedures) return 0;
for (i = 0; i < procs->numProcedures; ++i) {
if (_stricmp(getName(procs->procedures[i].name, *namelist), name) == 0)
return i;
}
return -1;
}
static Procedure *addProcedure(ProcedureList *procs, char **namelist, char *name) {
int i;
Procedure *p;
if (!procs->procedures) {
procs->procedures = (Procedure*)malloc(sizeof(Procedure));
procs->numProcedures = 1;
i = 0;
}
else if ((p = findProcedure(procs, namelist, name)) != 0)
return p;
else {
i = procs->numProcedures++;
procs->procedures = (Procedure*)realloc(procs->procedures, sizeof(Procedure) * (i + 1));
}
procs->procedures[i].name = addName(namelist, name);
procs->procedures[i].type = 0;
procs->procedures[i].namelist = 0;
procs->procedures[i].numArgs = 0;
procs->procedures[i].minArgs = 0;
procs->procedures[i].uses = 0;
procs->procedures[i].deftype = 0;
procs->procedures[i].variables.variables = 0;
procs->procedures[i].variables.numVariables = 0;
procs->procedures[i].nodes.numNodes = 0;
procs->procedures[i].nodes.nodes = 0;
procs->procedures[i].condition.numNodes = 0;
procs->procedures[i].condition.nodes = 0;
procs->procedures[i].numRefs = 0;
procs->procedures[i].references = 0;
procs->procedures[i].declared = -1;
procs->procedures[i].fdeclared = 0;
procs->procedures[i].fstart = 0;
procs->procedures[i].start = -1;
procs->procedures[i].end = -1;
procs->procedures[i].defined = -1;
procs->procedures[i].stringifiedName = 0;
return procs->procedures + i;
}
static void copyVariables(VariableList *to, char **toNames, VariableList *from, char *fromNames) {
int i, varI;
for (i = 0; i < from->numVariables; ++i) {
varI = addVariable(to, toNames, from->variables[i].type,
getName(from->variables[i].name, fromNames));
to->variables[i].value = from->variables[i].value;
to->variables[i].initialized = from->variables[i].initialized;
}
}
static int externProcedure(ProcedureList *pl, char **namelist, int type) {
Procedure *p;
int numArgs = 0, minArgs = 0;
char *argNames = 0, *tmpNames = 0;
VariableList args;
args.numVariables = 0;
args.variables = 0;
if (expectToken(T_SYMBOL) == -1)
return 1;
p = findProcedure(pl, namelist, lexData.stringData);
if (p) {
if (p->deftype == 2)
parseSemanticError("Redefinition of procedure %s.", lexData.stringData);
}
else {
if (findName(*namelist, lexData.stringData) != -1)
parseSemanticError("%s already defined.", lexData.stringData);
p = addProcedure(pl, namelist, lexData.stringData);
}
if (p->declared == -1) {
p->declared = lexGetLineno(currentInputStream);
p->fdeclared = lexGetFilename(currentInputStream);
}
if (expectToken('(') != -1) {
int i, optionalPart = 0;
i = lex();
while (i != ')') {
if (i != T_VARIABLE)
parseError("'variable' expected.");
ungetToken();
variable(&args, &argNames, V_LOCAL, 0, 0);
if (args.variables[args.numVariables - 1].initialized) {
optionalPart = 1;
} else {
if (optionalPart)
parseSemanticError("Optional arguments must not precede required arguments.");
minArgs++;
}
i = lex();
if (i != ')') {
if (i != ',')
parseError("',' expected.");
i = lex();
}
numArgs++;
}
}
if (expectToken(';') == -1)
parseError("Can't define procedures in an import or export block");
p->numArgs = numArgs;
p->minArgs = minArgs;
p->deftype = 1;
p->type |= type;
if (type | P_EXPORT)
p->uses = 1;
copyVariables(&p->variables, &tmpNames, &args, argNames);
freeVariableList(&args);
if (argNames) free(argNames);
if (tmpNames) free(tmpNames);
return 0;
}
static int externVariable(VariableList *v, char **namelist, int type, int flag) {
int i;
if (expectToken(T_SYMBOL) == -1)
parseError("Expecting symbol.");
if (findName(*namelist, lexData.stringData) != -1)
parseSemanticError("Redefinition of %s.", lexData.stringData);
i = addVariable(v, namelist, type, lexData.stringData);
v->variables[i].declared = lexGetLineno(currentInputStream);
v->variables[i].fdeclared = lexGetFilename(currentInputStream);
if (i == -1)
parseSemanticError("Couldn't add variable %s.", lexData.stringData);
if (expectToken(T_ASSIGN) != -1) {
if (flag) {
LexData assignConstant;
constantExpression(&assignConstant);
assignVariable(v, i, &assignConstant);
}
else parseError("Can't assign in initialization of imported variables.");
}
if (expectToken(';') == -1)
;
return 0;
}
int findVariableIndex(char *var, VariableList *v, char *namelist) {
int i;
for (i = 0; i < v->numVariables; ++i) {
if (_stricmp(getName(v->variables[i].name, namelist), var) == 0)
return i;
}
return -1;
}
static int import(Program *p, char **names) {
if (expectToken(T_IMPORT) == -1) return 1;
do {
if (expectToken(T_VARIABLE) != -1) {
if (expectToken(T_BEGIN) != -1) {
while (expectToken(T_END) == -1)
if (externVariable(&p->externals, names, V_IMPORT, 0))
return 1;
}
else if (expectToken(T_SYMBOL) != -1) {
ungetToken();
if (externVariable(&p->externals, names, V_IMPORT, 0))
return 1;
}
else parseError("Expected symbol or 'begin' block");
}
else if (expectToken(T_PROCEDURE) != -1) {
if (expectToken(T_BEGIN) != -1) {
while (expectToken(T_END) == -1) {
if (externProcedure(&p->procedures, names, P_IMPORT))
return 1;
}
}
else if (expectToken(T_SYMBOL) != -1) {
ungetToken();
if (externProcedure(&p->procedures, names, P_IMPORT))
return 1;
}
else parseError("Expected symbol or 'begin' block");
}
else parseError("Expected 'procedure' or 'variable'");
} while (expectToken(T_IMPORT) != -1);
ungetToken();
return 0;
}
static int export(Program *p, char **names) {
if (expectToken(T_EXPORT) == -1) return 1;
do {
if (expectToken(T_VARIABLE) != -1) {
if (expectToken(T_BEGIN) != -1) {
while (expectToken(T_END) == -1)
if (externVariable(&p->externals, names, V_EXPORT, 1))
return 1;
}
else if (expectToken(T_SYMBOL) != -1) {
ungetToken();
if (externVariable(&p->externals, names, V_EXPORT, 1))
return 1;
}
else parseError("Expected symbol or 'begin' block");
}
else if (expectToken(T_PROCEDURE) != -1) {
if (expectToken(T_BEGIN) != -1) {
while (expectToken(T_END) == -1) {
if (externProcedure(&p->procedures, names, P_EXPORT))
return 1;
}
}
else if (expectToken(T_SYMBOL) != -1) {
ungetToken();
if (externProcedure(&p->procedures, names, P_EXPORT))
return 1;
}
else parseError("Expected symbol or 'begin' block");
}
else parseError("Expected 'procedure' or 'variable'");
} while (expectToken(T_EXPORT) != -1);
ungetToken();
return 0;
}
/* Parse the syntax for declaring global and local variables of the procedures */
static int variable(VariableList *v, char **names, int type, char allowArrays, int allowMulti) {
if (expectToken(T_VARIABLE) == -1) return 1;
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 if (expectToken(T_SYMBOL) != -1) {
ungetToken();
if (defineVariable(v, names, type, allowArrays, allowMulti))
return 1;
} else
parseError("Expected variable name symbol or 'begin' block.");
return 0;
}
/*
* A node is just an instruction.
* the token field is set to the token returned from the lexical
* analyzer. If it's a constant, the value's type is set to the
* type of variable it is. If it's a symbol, the type field is
* set to the type of symbol (global or local, variable or procedure)
* and the intData field is set to the offset for that variable
* or procedure.
*/
void emitNode(Procedure *p, NodeList *n, LexData *data) {
int i;
if (!n->nodes) {
n->nodes = (Node*)malloc(8 * sizeof(Node));
n->numNodes = 0;
} else if (!(n->numNodes % 8)) {
n->nodes = (Node*)realloc(n->nodes, sizeof(Node) * (n->numNodes + 8));
}
i = n->numNodes++;
n->nodes[i].token = data->token;
n->nodes[i].lineNum = lexGetLineno(currentInputStream);
n->nodes[i].column = lexGetColumn(currentInputStream);
switch (data->token) {
case T_CONSTANT:
assignValue(&n->nodes[i].value, data);
break;
case T_SYMBOL: {
int v;
int type = 0;
//
// search order is:
// local variables
// global procedures
// global variables
// external variables
//
v = findVariableIndex(data->stringData, &p->variables, p->namelist);
type = P_LOCAL;
if (v == -1) {
v = findProcedureIndex(&currentProgram->procedures, &currentProgram->namelist, data->stringData);
if (v == -1) {
v = findVariableIndex(data->stringData, &currentProgram->variables, currentProgram->namelist);
type = P_GLOBAL;
if (v == -1) {
v = findVariableIndex(data->stringData, &currentProgram->externals, currentProgram->namelist);
if (v == -1) {
parseSemanticError("Undefined symbol %s.", data->stringData);
break;
}
//v = findName(currentProgram->namelist, data->stringData);
type = P_EXTERN;
referenceVariable(&currentProgram->externals, v);
}
else referenceVariable(&currentProgram->variables, v);
}
else {
Procedure *proc;
type |= P_PROCEDURE;
referenceProcedure(&currentProgram->procedures, v);
if (data->type == T_STRING) {
type |= P_STRINGIFY;
proc = &currentProgram->procedures.procedures[v];
// Add stringified procedure name (because program has separate namelist of string constants)
if (!proc->stringifiedName) {
proc->stringifiedName = addString(&currentProgram->stringspace, data->stringData);
}
}
}
}
else referenceVariable(&p->variables, v);
n->nodes[i].value.type = type;
n->nodes[i].value.intData = v;
break;
}
}
}
void emitInt(Procedure *p, NodeList *nodes, int i) {
LexData tmp;
tmp.token = T_CONSTANT;
tmp.type = T_INT;
tmp.intData = i;
emitNode(p, nodes, &tmp);
}
void emitOp(Procedure *p, NodeList *nodes, int token) {
LexData tmp;
tmp.token = token;
emitNode(p, nodes, &tmp);
}
static void logical_expression(Procedure *p, NodeList *nodes);
static void parseFuncArgs(Procedure *p, NodeList *nodes, Procedure *q);
static int isExpectingProcArg();
static void parseVariableRef(Procedure *p, NodeList *nodes, LexData* d, int refSyntax) {
if (refSyntax)
parseSemanticError("Can only stringify procedures.");
if (expectToken('(') != -1) { // function call
ungetToken();
emitOp(p, nodes, T_CALL_FUNC);
emitNode(p, nodes, d);
emitOp(p, nodes, T_START_EVENT);
parseFuncArgs(p, nodes, 0);
emitOp(p, nodes, T_END_EVENT);
}
else {
if (expectToken('[') != -1 || expectToken('.') != -1) {
parseArrayDereference(p, nodes, *d, 0);
}
else {
emitNode(p, nodes, d);
}
}
}
static void factor(Procedure *p, NodeList *nodes) {
int i, refSyntax = 0, numNodes = nodes->numNodes;
i = lex();
if (i == '@') {
i = lex();
if (i != T_SYMBOL)
parseError("Expected symbol");
refSyntax = 1;
}
switch (i) {
case T_FLOOR:
case T_NOT:
case T_BWNOT:
case '-':
factor(p, nodes);
if (i == '-') i = T_NEGATE;
emitOp(p, nodes, i);
break;
case T_SYMBOL: {
// local?
LexData d = lexData;
if (findVariableIndex(lexData.stringData, &p->variables, p->namelist) != -1) {
parseVariableRef(p, nodes, &d, refSyntax);
}
else {
Procedure *q;
q = findProcedure(&currentProgram->procedures, &currentProgram->namelist, lexData.stringData);
if (q) {
if (q->type&P_INLINE) parseSemanticError("Cannot use an inline procedure in an expression");
if (refSyntax) {
d.type = T_STRING; // this will make node stringify when generating code
emitNode(p, nodes, &d);
} else if (isExpectingProcArg() && (expectToken('(') == -1)) {
ungetToken();
emitNode(p, nodes, &d); // emit procedure ID directly
} else {
emitOp(p, nodes, T_CALL_FUNC);
emitNode(p, nodes, &d);
emitOp(p, nodes, T_START_EVENT);
parseFuncArgs(p, nodes, q);
emitOp(p, nodes, T_END_EVENT);
}
}
else if (findVariableIndex(lexData.stringData, &currentProgram->variables, currentProgram->namelist) != -1) {
parseVariableRef(p, nodes, &d, refSyntax);
}
else if (findVariableIndex(lexData.stringData, &currentProgram->externals, currentProgram->namelist) != -1) {
parseVariableRef(p, nodes, &d, refSyntax);
}
else {
parseSemanticError("Undefined symbol %s in factor\n", lexData.stringData);
// for parser:
if (expectToken('(') != -1) {
ungetToken();
parseFuncArgs(p, nodes, 0);
}
}
}
break;
}
case T_CONSTANT: {
if (lexData.type == T_STRING) { // could be a function call
LexData d = lexData;
if (expectToken('(') != -1) {
ungetToken();
emitOp(p, nodes, T_CALL_FUNC);
emitNode(p, nodes, &d);
emitOp(p, nodes, T_START_EVENT);
parseFuncArgs(p, nodes, 0);
emitOp(p, nodes, T_END_EVENT);
}
else
emitNode(p, nodes, &d);
}
else
emitNode(p, nodes, &lexData);
break;
}
case '(':
logical_expression(p, nodes);
i = lex();
if (i != ')') {
parseError("Mismatched parenthesis.");
ungetToken();
}
break;
case '[':
parseArrayConstant(p, nodes);
break;
case '{':
parseAssocArrayConstant(p, nodes);
break;
default:
parseLibExpression(p, nodes, i);
if (nodes->numNodes == numNodes) {
parseError("Expression expected.");
}
break;
}
}
static void term_prime(Procedure *p, NodeList *nodes) {
int i = lex();
if (i == '*' || i == '/' || i == '%' || i == T_DIV2 || i == '^') { // sfall: added '^', 'div'
Node *node;
// term(p, nodes);
factor(p, nodes);
if (nodes->numNodes > 0) { // fix crash for DLL
node = &nodes->nodes[nodes->numNodes - 1];
if ((i == '/' || i == '%' || i == T_DIV2) && node->token == T_CONSTANT && node->value.type != V_STRING && node->value.intData == 0) {
parseSemanticError("Division by zero!");
}
emitOp(p, nodes, i);
term_prime(p, nodes);
}
}
else ungetToken();
}
static void term(Procedure *p, NodeList *nodes) {
factor(p, nodes);
term_prime(p, nodes);
}
static void expr_prime(Procedure *p, NodeList *nodes) {
int i = lex();
if (i == '+' || i == '-' || i == T_BWAND || i == T_BWOR || i == T_BWXOR) {
term(p, nodes);
emitOp(p, nodes, i);
expr_prime(p, nodes);
}
else
ungetToken();
}
static void expression(Procedure *p, NodeList *nodes) {
term(p, nodes);
expr_prime(p, nodes);
}
static void compare_prime(Procedure *p, NodeList *nodes) {
int i = lex();
switch (i) {
case '>':
case '<':
case T_GREATER_EQUAL:
case T_LESS_EQUAL:
case T_EQUAL:
case T_NOT_EQUAL: break;
default: ungetToken(); return;
}
expression(p, nodes);
emitOp(p, nodes, i);
}
static void compare_expression(Procedure *p, NodeList *nodes) {
expression(p, nodes);
compare_prime(p, nodes);
}
static void logical_prime(Procedure *p, NodeList *nodes) {
int i;
i = lex();
if (i == T_AND_ALSO || i == T_OR_ELSE || i == T_AND || i == T_OR) {
/* old logic:
compare_expression(p, nodes);
emitOp(p, nodes, i);
logical_prime(p, nodes);*/
// phobos2077 - new boolean expressions begin
emitOp(p, nodes, i);
emitOp(p, nodes, T_START_EXPRESSION);
compare_expression(p, nodes);
emitOp(p, nodes, T_END_EXPRESSION);
logical_prime(p, nodes);
// boolean end
}
else ungetToken();
}
static void logical_expression(Procedure *p, NodeList *nodes) {
NodeList tmpNodes;
tmpNodes.numNodes = 0;
tmpNodes.nodes = 0;
compare_expression(p, &tmpNodes);
logical_prime(p, &tmpNodes);
if (expectToken(T_IF) != -1) { // phobos2077 - ternary operator, python-like
emitOp(p, nodes, T_IF);
parseExpression(p, nodes); // condition
emitOp(p, nodes, T_START_EXPRESSION);
appendNodeList(nodes, &tmpNodes); // expr if true
emitOp(p, nodes, T_END_EXPRESSION);
if (expectToken(T_ELSE) == -1)
parseError("'else' expected.");
parseExpression(p, nodes); // expr if false
} else {
appendNodeList(nodes, &tmpNodes);
}
/*compare_expression(p, nodes);
logical_prime(p, nodes);*/
}
void parseExpression(Procedure *p, NodeList *nodes) {
emitOp(p, nodes, T_START_EXPRESSION);
expressionNesting++;
logical_expression(p, nodes);
expressionNesting--;
emitOp(p, nodes, T_END_EXPRESSION);
}
static int argsListNestLevel = 0;
static int argsListCurArg[300]; // current argument on each level, used only for lib args
static int expectProcArgStack[300]; // each bit specifies if this argument should be a proc reference
// used to treat procedures differently when passed without argument list (passed by reference, and not called)
static int isExpectingProcArg() {
return (expectProcArgStack[argsListNestLevel] != 0
&& (expectProcArgStack[argsListNestLevel] & (1 << argsListCurArg[argsListNestLevel])) != 0);
}
void parseLibArgs(Procedure *p, NodeList* nodes, int n, int procArgs) {
if (n) {
int *argNum;
if (expectToken('(') == -1) parseError("'(' expected");
expectProcArgStack[++argsListNestLevel] = procArgs; // lib func may expect procedure as one of the arguments
argNum = &argsListCurArg[argsListNestLevel];
for (*argNum = 0; *argNum < n; (*argNum)++) {
if (*argNum > 0 && expectToken(',') == -1)
parseError("',' expected.");
parseExpression(p, nodes);
}
argsListNestLevel--;
if (expectToken(')') == -1) parseError("')' expected after args list");
}
}
static void parseFuncArgs(Procedure *p, NodeList *nodes, Procedure *q) {
int i;
int args = 0, argBlock = 0;
if (expectToken('(') != -1) {
argBlock = 1;
expectProcArgStack[++argsListNestLevel] = 0; // not a lib func, for expressions below
emitOp(p, nodes, T_START_ARG);
i = lex();
if (i != ')') {
ungetToken();
do {
parseExpression(p, nodes);
args++;
if (expectToken(')') != -1)
break;
if (expectToken(',') == -1)
parseError("',' expected.");
} while (1);
}
emitOp(p, nodes, T_END_ARG);
argsListNestLevel--;
}
if (q) {
if (q->numArgs != -1) {
if (args > q->numArgs || args < q->minArgs) {
parseSemanticError("Wrong number of arguments to procedure %s, must be from %d to %d.",
getName(q->name, currentProgram->namelist), q->minArgs, q->numArgs);
}
// phobos2077 - optional arguments:
if (q->numArgs > 0 && args < q->numArgs) {
if (argBlock) {
nodes->numNodes--; // erase T_END_ARG
} else {
emitOp(p, nodes, T_START_ARG);
}
for (i = args; i < q->numArgs; i++) {
emitOp(p, nodes, T_START_EXPRESSION);
emitInt(p, nodes, 0);
nodes->nodes[nodes->numNodes - 1].value = q->variables.variables[i].value;
emitOp(p, nodes, T_END_EXPRESSION);
}
emitOp(p, nodes, T_END_ARG);
}
}
}
else
emitOp(p, nodes, T_CHECK_ARG_COUNT);
}
static int parseEvent(Procedure *p, NodeList *nodes) {
int i;
Procedure *other;
int calloffset = nodes->numNodes;
if (expectToken(T_SYMBOL) == -1) {
if (expectToken(T_CONSTANT) == -1 || lexData.type != T_STRING) {
parseSemanticError("Symbol or string expected.");
return 0;
}
}
if (lexData.token == T_SYMBOL) {
other = findProcedure(&currentProgram->procedures, &currentProgram->namelist, lexData.stringData);
if (!other) { // not a procedure, see if it's a variable
int v;
v = findVariableIndex(lexData.stringData, &p->variables, p->namelist);
if (v == -1) {
v = findVariableIndex(lexData.stringData, &currentProgram->variables, currentProgram->namelist);
if (v == -1)
v = findVariableIndex(lexData.stringData, &currentProgram->externals, currentProgram->namelist);
if (v == -1)
parseSemanticError("Undefined procedure %s.", lexData.stringData);
}
} else if (other->type&P_PURE) {
parseSemanticError("Cannot call a pure function");
} else if (other->type & P_INLINE && optimize >= 1) {
int firstVar = p->variables.numVariables;
LexData ld;
nodes->numNodes -= 2; // eat "start statement" and "call"
if (other->numArgs) {
if (expectToken('(') == -1) parseError("Expected '('.");
for (i = 0; i < other->numArgs; i++) {
GenTmpVar(p, &ld);
emitOp(p, nodes, T_START_STATEMENT);
emitNode(p, nodes, &ld);
emitOp(p, nodes, T_ASSIGN);
parseExpression(p, nodes);
emitOp(p, nodes, T_END_STATEMENT);
if (i != other->numArgs - 1 && expectToken(',') == -1) parseError("Expected ','");
}
if (expectToken(')') == -1) parseError("Expected ')'.");
}
for(i = other->numArgs; i < other->variables.numVariables; i++) {
GenTmpVar(p, &ld);
if (other->variables.variables[i].value.intData || other->variables.variables[i].type != V_INT) {
emitOp(p, nodes, T_START_STATEMENT);
emitNode(p, nodes, &ld);
emitOp(p, nodes, T_ASSIGN);
ld.token = T_CONSTANT;
ld.intData = other->variables.variables[i].value.intData;
ld.type = other->variables.variables[i].value.type;
//TODO: Check this is valid for strings
emitNode(p, nodes, &ld);
emitOp(p, nodes, T_END_STATEMENT);
}
}
p->variables.variables = realloc(p->variables.variables, sizeof(Variable) * p->variables.numVariables);
nodes->nodes = realloc(nodes->nodes, sizeof(Node) * (nodes->numNodes + other->nodes.numNodes + 9));
memcpy(&nodes->nodes[nodes->numNodes], &other->nodes.nodes[1], (other->nodes.numNodes - 2) * sizeof(Node));
for(i = nodes->numNodes; i < nodes->numNodes + other->nodes.numNodes - 2; i++) {
if (nodes->nodes[i].token == T_SYMBOL && nodes->nodes[i].value.type == P_LOCAL) {
nodes->nodes[i].value.intData += firstVar;
}
}
nodes->numNodes += (other->nodes.numNodes - 2);
return 1;
}
}
else other = 0;
emitNode(p, nodes, &lexData);
emitOp(p, nodes, T_START_EVENT);
parseFuncArgs(p, nodes, other);
i = lex();
switch (i) {
case T_IN:
case T_WHEN:
if (i == T_IN)
nodes->nodes[calloffset - 1].token = T_CALL_AT;
else
nodes->nodes[calloffset - 1].token = T_CALL_CONDITION;
emitNode(p, nodes, &lexData);
parseExpression(p, nodes);
break;
default: ungetToken(); break;
}
emitOp(p, nodes, T_END_EVENT);
return 0;
}
static void parseIf(Procedure *p, NodeList *nodes);
static void parseBlock(Procedure *p);
void parseExpGroup(Procedure *p, NodeList *nodes, int num) {
if (num > 0) {
parseExpression(p, nodes);
num--;
}
while (num-- > 0) {
if (expectToken(',') == -1)
parseError("',' expected.");
parseExpression(p, nodes);
}
}
// search order is:
// local variables
// global procedures
// global variables
// external variables
static int FindSymbolName(Procedure *p, LexData *data) {
int type = P_LOCAL;
if (findVariableIndex(data->stringData, &p->variables, p->namelist) == -1) {
if (findProcedureIndex(&currentProgram->procedures, &currentProgram->namelist, data->stringData) == -1) {
if (findVariableIndex(data->stringData, &currentProgram->variables, currentProgram->namelist) == -1) {
type = P_GLOBAL;
if (findVariableIndex(data->stringData, &currentProgram->externals, currentProgram->namelist) == -1) {
return 0;
}
}
}
}
return 1; // found
}
// Parse a statement from the input stream.
static void parseStatementInternal(Procedure *p, char requireSemicolon) {
int i, inlinedEvent = 0;
NodeList *nodes;
nodes = &p->nodes;
i = lex();
emitOp(p, &p->nodes, T_START_STATEMENT);
if (i == T_BEGIN) {
ungetToken();
parseBlock(p);
}
else {
if (i != T_FOR && i != T_FOREACH && i != T_SWITCH && i != T_SYMBOL) emitNode(p, &p->nodes, &lexData);
switch (i) {
case T_SWITCH:
parseSwitch(p, &p->nodes);
break;
case T_IF:
parseIf(p, &p->nodes);
break;
case T_WHILE:
parseWhile(p, &p->nodes);
break;
case T_FOR:
parseFor(p, &p->nodes);
break;
case T_FOREACH:
parseForEach(p, &p->nodes);
break;
default: {
switch (i) {
default:
parseLib(p, &p->nodes, i);
break;
PARSE(DETACH, 0);
PARSE(EXIT, 0);
PARSE(SPAWN, 1);
PARSE(CALLSTART, 1);
PARSE(EXEC, 1);
PARSE(FORK, 1);
PARSE(WAIT, 1);
PARSE(STARTCRITICAL, 0);
PARSE(ENDCRITICAL, 0);
case T_NOOP: break;
case T_SYMBOL: {
int t, op, setArray = 0;
LexData d, symb;
symb = lexData;
t = lex();
if (t == '[' || t == '.') {
parseArrayAssignment(p, &p->nodes, symb);
break;
}
if (t == T_INC || t == T_DEC) { // pretend it's += or -=
t = (t == T_INC) ? T_ASSIGN_ADD : T_ASSIGN_SUB;
d.token = T_CONSTANT;
d.type = T_INT;
d.intData = 1;
setNextToken(&d);
}
switch (t) {
case T_ASSIGN:
emitNode(p, &p->nodes, &symb); // lvalue
emitNode(p, &p->nodes, &lexData); // :=
parseExpression(p, &p->nodes); // expr
break;
case T_ASSIGN_ADD:
case T_ASSIGN_SUB:
case T_ASSIGN_DIV:
case T_ASSIGN_MUL:
switch (t) {
case T_ASSIGN_ADD: op = '+'; break;
case T_ASSIGN_MUL: op = '*'; break;
case T_ASSIGN_SUB: op = '-'; break;
case T_ASSIGN_DIV: op = '/'; break;
}
emitNode(p, &p->nodes, &symb); // lvalue
emitOp(p, &p->nodes, T_ASSIGN);
emitOp(p, &p->nodes, T_START_EXPRESSION);
emitOp(p, &p->nodes, T_START_EXPRESSION);
emitNode(p, &p->nodes, &symb);
emitOp(p, &p->nodes, T_END_EXPRESSION);
parseExpression(p, &p->nodes);
emitOp(p, &p->nodes, op);
emitOp(p, &p->nodes, T_END_EXPRESSION);
break;
default:
if (FindSymbolName(p, &lexData))
parseError("Assignment operator expected.");
else
parseSemanticError("Unknown name identifier %s.", lexData.stringData);
}
break;
}
case T_CALL:
inlinedEvent = parseEvent(p, &p->nodes);
break;
case T_CANCEL:
if (expectToken('(') == -1)
parseError("'(' expected");
if (expectToken(T_SYMBOL) == -1)
parseError("Symbol expected");
emitNode(p, &p->nodes, &lexData);
if (expectToken(')') == -1)
parseError("')' expected");
break;
case T_CANCELALL:
emitNode(p, &p->nodes, &lexData);
break;
case T_RETURN:
if (p->type&P_INLINE) parseSemanticError("Cannot use return in an inline procedure");
if (expectToken(';') == -1)
parseExpression(p, &p->nodes);
else {
ungetToken();
emitOp(p, &p->nodes, T_START_EXPRESSION);
emitInt(p, &p->nodes, 0);
emitOp(p, &p->nodes, T_END_EXPRESSION);
}
break;
case T_BREAK:
case T_CONTINUE:
if (loopNesting == 0) parseSemanticError("Cannot use break or continue outside of loop");
break;
}
if (requireSemicolon && expectToken(';') == -1) parseError("expecting ';'.");
break;
}
}
}
if (!inlinedEvent)
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) {
parseStatementOrLocalVariables(p, 1);
}
// sfall addition
void parseStatementNoSemicolon(Procedure *p) {
parseStatementOrLocalVariables(p, 0);
}
static void parseBlock(Procedure *p) {
int i;
if (expectToken(T_BEGIN) == -1)
parseError("expected 'begin'.");
emitNode(p, &p->nodes, &lexData); // emit the begin
while ((i = lex()) != T_END) {
if (i == T_EOF) {
parseError("Premature EOF encountered.");
} else {
ungetToken();
parseStatement(p);
}
}
emitNode(p, &p->nodes, &lexData); // emit the end
}
static void parseIf(Procedure *p, NodeList *n) {
int emitend = 0;
parseExpression(p, n);
if (expectToken(T_THEN) == -1) {
parseError("Expected 'then'.");
return;
}
emitNode(p, n, &lexData);
if (expectToken(T_BEGIN) == -1) {
emitend = 1;
emitOp(p, n, T_START_STATEMENT);
emitOp(p, n, T_BEGIN);
} else ungetToken();
parseStatement(p);
if (emitend) {
emitend = 0;
emitOp(p, n, T_END);
emitOp(p, n, T_END_STATEMENT);
}
if (expectToken(T_ELSE) != -1) {
emitNode(p, n, &lexData);
if (expectToken(T_BEGIN) == -1) {
emitend = 1;
emitOp(p, n, T_START_STATEMENT);
emitOp(p, n, T_BEGIN);
} else ungetToken();
parseStatement(p);
if (emitend) {
emitOp(p, n, T_END);
emitOp(p, n, T_END_STATEMENT);
}
}
}
static void parseWhile(Procedure *p, NodeList *n) {
int emitend = 0;
parseExpression(p, n);
if (expectToken(T_DO) == -1) {
parseError("Expected 'do'.");
return;
}
emitNode(p, n, &lexData);
if (expectToken(T_BEGIN) == -1) {
emitend=1;
emitOp(p, n, T_START_STATEMENT);
emitOp(p, n, T_BEGIN);
} else ungetToken();
loopNesting++;
parseStatement(p);
loopNesting--;
if (emitend) {
emitOp(p, n, T_END);
emitOp(p, n, T_END_STATEMENT);
}
}
void CloneLexData(LexData *dest, LexData *source) {
*dest = *source;
dest->stringData = malloc(strlen(source->stringData) + 1);
strcpy_s(dest->stringData, strlen(source->stringData) + 1, source->stringData);
}
int procedure(void) {
Procedure *p;
int numArgs = 0, minArgs = 0;
char *argNames = 0;
VariableList args;
int critical = 0, pure = 0, inlined = 0;
args.numVariables = 0;
args.variables = 0;
if (expectToken(T_CRITICAL) != -1)
critical = 1;
if (expectToken(T_PURE) != -1)
pure = 1;
if (expectToken(T_INLINE) != -1)
inlined = 1;
if (expectToken(T_PROCEDURE) == -1)
return 1;
if (expectToken(T_SYMBOL) == -1)
return 1;
p = findProcedure(&currentProgram->procedures, &currentProgram->namelist, lexData.stringData);
if (p) {
if (p->type & P_IMPORT)
parseSemanticError("Can't define imported procedures");
if (p->deftype == 2)
parseSemanticError("Redefinition of procedure %s.", lexData.stringData);
}
else {
if (findName(currentProgram->namelist, lexData.stringData) != -1)
parseSemanticError("%s already defined.", lexData.stringData);
p = addProcedure(&currentProgram->procedures, &currentProgram->namelist, lexData.stringData);
}
if (p->declared == -1) {
p->declared = lexGetLineno(currentInputStream);
p->fdeclared = lexGetFilename(currentInputStream);
} else {
p->defined = lexGetLineno(currentInputStream);
}
if (critical) p->type |= P_CRITICAL;
if (pure) p->type |= P_PURE;
if (inlined) p->type |= P_INLINE;
if (pure && inlined) parseSemanticError("A procedure cannot be both pure and inline");
currentProcedure = p;
if (expectToken('(') != -1) {
int i, optionalPart = 0;
i = lex();
while (i != ')') {
if (i != T_VARIABLE)
parseError("'variable' expected.");
ungetToken();
variable(&args, &argNames, V_LOCAL, 0, 0);
if (args.variables[args.numVariables - 1].initialized) {
optionalPart = 1;
} else {
if (optionalPart)
parseSemanticError("Optional arguments must not precede required arguments.");
minArgs++;
}
i = lex();
if (i != ')') {
if (i != ',')
parseError("',' expected.");
i = lex();
}
numArgs++;
}
}
if (p->deftype == 1) {
if (p->numArgs != numArgs) {
parseError("Wrong number of arguments to procedure %s\n",
getName(p->name, currentProgram->namelist));
} else if (numArgs != minArgs) {
parseSemanticError("Default argument values are not allowed for a forward-declared procedure: %s\n",
getName(p->name, currentProgram->namelist));
}
} else if (p->deftype == 0) {
p->numArgs = numArgs;
p->minArgs = minArgs;
}
if (expectToken(';') != -1) {
char* tmpNames = 0;
copyVariables(&p->variables, &tmpNames, &args, argNames);
freeVariableList(&args);
if (argNames) free(argNames);
if (tmpNames) free(tmpNames);
if (p->type&P_INLINE) parseSemanticError("Cannot forward declare in inline procedure");
p->deftype = 1;
return 0;
}
if (p->deftype == 1) {
int i;
for (i = 0; i < args.numVariables; ++i) {
p->variables.variables[i].name = addName(&p->namelist, getName(args.variables[i].name, argNames));
}
} else {
copyVariables(&p->variables, &p->namelist, &args, argNames);
}
freeVariableList(&args);
if (argNames) free(argNames);
p->start = lexGetLineno(currentInputStream);
p->fstart = lexGetFilename(currentInputStream);
if (expectToken(T_IN) != -1) {
if (expectToken(T_CONSTANT) == -1)
parseError("Constant expected.");
p->type |= P_TIMED;
p->time = lexData.intData;
}
else if (expectToken(T_WHEN) != -1) {
p->type |= P_CONDITIONAL;
parseExpression(p, &p->condition);
}
if (p->type & (P_TIMED | P_CONDITIONAL) && (pure || inlined)) {
parseSemanticError("Timed or conditional procedures cannot be marked pure or inline");
}
if (p->type & (P_IMPORT | P_EXPORT) && inlined) {
parseSemanticError("inline procedures cannot be imported or exported");
}
parseBlock(p);
if (!optimize || !(p->type & P_INLINE)) {
p->nodes.numNodes--; // Avoid double begin/end
emitOp(p, &p->nodes, T_START_STATEMENT);
emitOp(p, &p->nodes, T_RETURN);
emitOp(p, &p->nodes, T_START_EXPRESSION);
emitInt(p, &p->nodes, 0);
emitOp(p, &p->nodes, T_END_EXPRESSION);
emitOp(p, &p->nodes, T_END_STATEMENT);
emitOp(p, &p->nodes, T_END);
}
p->deftype = 2;
p->end = lexGetLineno(currentInputStream);
p->fend = lexGetFilename(currentInputStream);
return 0;
}
int top(void) {
tmpCounter = 0;
while (1) {
if (expectToken(T_EOF) != -1) {
if (tois != -1) {
fclose(currentInputStream->file);
free(currentInputStream);
currentInputStream = includes[tois];
includes[tois--] = 0;
continueLex(currentInputStream);
}
else break;
}
if (variable(&currentProgram->variables, &currentProgram->namelist, V_GLOBAL, 0, 1) &&
import(currentProgram, &currentProgram->namelist) &&
export(currentProgram, &currentProgram->namelist) &&
procedure()) {
if (expectToken(T_INCLUDE) != -1) {
InputStream *tmp;
if (tois == sizeof(includes) / sizeof(includes[0]))
parseError("Too many includes!");
includes[++tois] = currentInputStream;
tmp = malloc(sizeof(InputStream));
lex();
if (lexData.type != T_STRING)
parseError("Invalid type given to include");
tmp->name = AddFileName(lexData.stringData);
tmp->file = fopen(lexData.stringData, "r");
if (!tmp->file)
parseError("Couldn't find include file %s\n", lexData.stringData);
currentInputStream = tmp;
startLex(tmp);
}
else {
parseError("Expecting top-level statement");
return 1;
}
}
}
return 0;
}
static void freeVariable(Variable *v) {
v = v;
}
static void freeProgram(Program *s) {
freeProcedureList(&s->procedures);
freeVariableList(&s->variables);
if (s->stringspace) free(s->stringspace);
if (s->namelist) free(s->namelist);
free(s);
}
void freeCurrentProgram(void) {
if (currentProgram)
freeProgram(currentProgram);
}
void dumpNodes(Procedure* p, NodeList* nodes, FILE* f) {
int i, indent = 0;
char indentstr[100];
indentstr[0] = 0;
for (i = 0; i < nodes->numNodes; i++) {
Node node;
node = nodes->nodes[i];
fprintf(f, "\n%s", indentstr);
switch (node.token) {
case T_START_STATEMENT:
case T_START_EXPRESSION:
if (nodes->nodes[i].token == T_START_EXPRESSION)
fprintf(f, "EXPRESSION:");
else
fprintf(f, "STATEMENT:");
indentstr[indent] = '\t';
indentstr[++indent] = 0;
break;
case T_END_STATEMENT:
indentstr[--indent] = 0;
fprintf(f, "END STATEMENT");
break;
case T_END_EXPRESSION:
indentstr[--indent] = 0;
fprintf(f, "END EXPRESSION");
break;
case T_SYMBOL:
fprintf(f, "symbol %d", node.value.intData);
break;
case T_CONSTANT:
if (node.value.type == T_STRING)
fprintf(f, "\"%s\"", currentProgram->stringspace + node.value.stringData);
else if (nodes->nodes[i].value.type == T_FLOAT)
fprintf(f, "%.5f", node.value.floatData);
else
fprintf(f, "%d", node.value.intData);
break;
default:
if (node.token < 127)
fprintf(f, "'%c'", node.token);
else if (lexGetToken(node.token))
fprintf(f, "%s", lexGetToken(node.token));
else
fprintf(f, "%d", node.token);
}
}
}
void dumpAllNodes(const char* fileName) {
FILE* f = fopen(fileName, "wt");
int i;
if (!f)
return;
for (i = 1; i < currentProgram->procedures.numProcedures; i++) {
fprintf(f, "\nPROCEDURE %d:", i);
dumpNodes(&currentProgram->procedures.procedures[i], &currentProgram->procedures.procedures[i].nodes, f);
fprintf(f, "\n\n");
}
fclose(f);
}
void parse(InputStream *stream, const char *output) {
initLex();
startLex(stream);
currentProgram = (Program*)malloc(sizeof(Program));
currentProgram->procedures.procedures = 0;
currentProgram->procedures.numProcedures = 0;
currentProgram->variables.variables = 0;
currentProgram->variables.numVariables = 0;
currentProgram->externals.variables = 0;
currentProgram->externals.numVariables = 0;
currentProgram->stringspace = 0;
currentProgram->namelist = 0;
currentInputStream = stream;
// bogus procedure so we never have a zero procedure offset
addProcedure(&currentProgram->procedures, &currentProgram->namelist, "..............");
if (setjmp(currentProgram->env)) {
freeProgram(currentProgram);
currentProgram = 0;
lexClose();
if (output)
remove( output);
return;
}
if (top()) {
freeProgram(currentProgram);
currentProgram = 0;
lexClose();
if (output)
remove( output);
return;
}
lexClose();
currentInputStream->lineno = -1;
if (optimize)
optimizeTree(currentProgram);
if (output) {
if (dumpTree) {
char name[260] = "";
char *c;
strcpy_s(name, 260, output);
c = strrchr(name, '.');
if (c) {
*c = 0;
}
strcat_s(name, 260, "_tree.txt");
dumpAllNodes(name);
}
generateCode(currentProgram, output);
}
}
#ifdef BUILDING_DLL
int _stdcall numProcs() { return currentProgram->procedures.numProcedures - 1; }
void _stdcall getProc(int i, Procedure* proc) {
*proc = currentProgram->procedures.procedures[i + 1];
}
int _stdcall getProcNamespaceSize(int i) {
if (!currentProgram->procedures.procedures[i + 1].namelist) return -1;
return *(unsigned int*)currentProgram->procedures.procedures[i + 1].namelist;
}
void _stdcall getProcNamespace(int i, char* data) {
unsigned int size = *(unsigned int*)currentProgram->procedures.procedures[i + 1].namelist;
memcpy(data, currentProgram->procedures.procedures[i + 1].namelist + 4, size);
}
int _stdcall numVars() {
return (currentProgram->variables.numVariables + currentProgram->externals.numVariables);
}
void _stdcall getVar(int i, Variable* var) {
int numNormalVars = currentProgram->variables.numVariables;
if (i >= numNormalVars) {
*var = currentProgram->externals.variables[i - numNormalVars];
} else {
*var = currentProgram->variables.variables[i];
}
}
/*int _stdcall numExternals() { return currentProgram->externals.numVariables; }
void _stdcall getExternal(int i, Variable* var) {
*var = currentProgram->externals.variables[i];
}*/
void _stdcall getProcVar(int i, int j, Variable* var) {
*var = currentProgram->procedures.procedures[i + 1].variables.variables[j];
}
int _stdcall namespaceSize() {return *(unsigned int*)currentProgram->namelist; }
void _stdcall getNamespace(char* data) {
unsigned int size = *(unsigned int*)currentProgram->namelist;
memcpy(data, currentProgram->namelist + 4, size);
}
int _stdcall stringspaceSize() {
return currentProgram->stringspace
? *(unsigned int*)currentProgram->stringspace
: 0;
}
void _stdcall getStringspace(char* data) {
unsigned int size = *(unsigned int*)currentProgram->stringspace;
memcpy(data, currentProgram->stringspace + 4, size);
}
void _stdcall getProcRefs(int i, Reference* refs) {
memcpy(refs, currentProgram->procedures.procedures[i + 1].references, currentProgram->procedures.procedures[i + 1].numRefs * 8);
}
void _stdcall getVarRefs(int i, Reference* refs) {
int numNormalVars = currentProgram->variables.numVariables;
VariableList* varlist;
if (i >= numNormalVars) {
i = i - numNormalVars;
varlist = &currentProgram->externals;
} else {
varlist = &currentProgram->variables;
}
memcpy(refs, varlist->variables[i].references, varlist->variables[i].numRefs * 8);
}
void _stdcall getProcVarRefs(int i, int j, Reference* refs) {
memcpy(refs, currentProgram->procedures.procedures[i + 1].variables.variables[j].references, currentProgram->procedures.procedures[i + 1].variables.variables[j].numRefs * 8);
}
#endif