Merge branch 'development' into weirdtsbug

This commit is contained in:
AzaezelX 2023-09-13 08:46:47 -05:00
commit a746957cd9
89 changed files with 1943 additions and 1733 deletions

View file

@ -0,0 +1,618 @@
%{
// bison --defines=cmdgram.h --verbose -o cmdgram.cpp -p CMD CMDgram.y
// Make sure we don't get gram.h twice.
#define _CMDGRAM_H_
#include <stdlib.h>
#include <stdio.h>
#include "console/console.h"
#include "console/compiler.h"
#include "console/consoleInternal.h"
#include "core/strings/stringFunctions.h"
#ifndef YYDEBUG
#define YYDEBUG 0
#endif
#define YYSSIZE 350
int outtext(char *fmt, ...);
extern int serrors;
#define nil 0
#undef YY_ARGS
#define YY_ARGS(x) x
int CMDlex();
void CMDerror(const char *, ...);
#ifdef alloca
#undef alloca
#endif
#define alloca dMalloc
template< typename T >
struct Token
{
T value;
U32 lineNumber;
};
%}
%{
/* Reserved Word Definitions */
%}
%token <i> rwDEFINE rwENDDEF rwDECLARE rwDECLARESINGLETON
%token <i> rwBREAK rwELSE rwCONTINUE rwGLOBAL
%token <i> rwIF rwNIL rwRETURN rwWHILE rwDO
%token <i> rwENDIF rwENDWHILE rwENDFOR rwDEFAULT
%token <i> rwFOR rwFOREACH rwFOREACHSTR rwIN rwDATABLOCK rwSWITCH rwCASE rwSWITCHSTR
%token <i> rwCASEOR rwPACKAGE rwNAMESPACE rwCLASS
%token <i> rwASSERT
%token ILLEGAL_TOKEN
%{
/* Constants and Identifier Definitions */
%}
%token <c> CHRCONST
%token <i> INTCONST
%token <s> TTAG
%token <s> VAR
%token <s> IDENT
%token <i> TYPEIDENT
%token <str> DOCBLOCK
%token <str> STRATOM
%token <str> TAGATOM
%token <f> FLTCONST
%{
/* Operator Definitions */
%}
%token <i> '+' '-' '*' '/' '<' '>' '=' '.' '|' '&' '%'
%token <i> '(' ')' ',' ':' ';' '{' '}' '^' '~' '!' '@'
%token <i> opINTNAME opINTNAMER
%token <i> opMINUSMINUS opPLUSPLUS
%token <i> STMT_SEP
%token <i> opSHL opSHR opPLASN opMIASN opMLASN opDVASN opMODASN opANDASN
%token <i> opXORASN opORASN opSLASN opSRASN opCAT
%token <i> opEQ opNE opGE opLE opAND opOR opSTREQ
%token <i> opCOLONCOLON
%union {
Token< char > c;
Token< int > i;
Token< const char* > s;
Token< char* > str;
Token< double > f;
StmtNode* stmt;
ExprNode* expr;
SlotAssignNode* slist;
VarNode* var;
SlotDecl slot;
InternalSlotDecl intslot;
ObjectBlockDecl odcl;
ObjectDeclNode* od;
AssignDecl asn;
IfStmtNode* ifnode;
}
%type <s> parent_block
%type <ifnode> case_block
%type <stmt> switch_stmt
%type <stmt> decl
%type <stmt> decl_list
%type <stmt> package_decl
%type <stmt> fn_decl_stmt
%type <stmt> fn_decl_list
%type <stmt> statement_list
%type <stmt> stmt
%type <expr> expr_list
%type <expr> expr_list_decl
%type <expr> aidx_expr
%type <expr> funcall_expr
%type <expr> assert_expr
%type <expr> object_name
%type <expr> object_args
%type <expr> stmt_expr
%type <expr> case_expr
%type <expr> class_name_expr
%type <stmt> if_stmt
%type <stmt> while_stmt
%type <stmt> for_stmt
%type <stmt> foreach_stmt
%type <stmt> stmt_block
%type <stmt> datablock_decl
%type <od> object_decl
%type <od> object_decl_list
%type <odcl> object_declare_block
%type <expr> expr
%type <slist> slot_assign_list_opt
%type <slist> slot_assign_list
%type <slist> slot_assign
%type <slot> slot_acc
%type <intslot> intslot_acc
%type <stmt> expression_stmt
%type <var> var_list
%type <var> var_list_decl
%type <asn> assign_op_struct
%left '['
%right opMODASN opANDASN opXORASN opPLASN opMIASN opMLASN opDVASN opMDASN opNDASN opNTASN opORASN opSLASN opSRASN '='
%left '?' ':'
%left opOR
%left opAND
%left '|'
%left '^'
%left '&'
%left opEQ opNE
%left '<' opLE '>' opGE
%left '@' opCAT opSTREQ opSTRNE
%left opSHL opSHR
%left '+' '-'
%left '*' '/' '%'
%right '!' '~' opPLUSPLUS opMINUSMINUS UNARY
%left '.'
%left opINTNAME opINTNAMER
%%
start
: decl_list
{ }
;
decl_list
:
{ $$ = nil; }
| decl_list decl
{ if(!gStatementList) { gStatementList = $2; } else { gStatementList->append($2); } }
;
decl
: stmt
{ $$ = $1; }
| fn_decl_stmt
{ $$ = $1; }
| package_decl
{ $$ = $1; }
;
package_decl
: rwPACKAGE IDENT '{' fn_decl_list '}' ';'
{ $$ = $4; for(StmtNode *walk = ($4);walk;walk = walk->getNext() ) walk->setPackage($2.value); }
;
fn_decl_list
: fn_decl_stmt
{ $$ = $1; }
| fn_decl_list fn_decl_stmt
{ $$ = $1; ($1)->append($2); }
;
statement_list
:
{ $$ = nil; }
| statement_list stmt
{ if(!$1) { $$ = $2; } else { ($1)->append($2); $$ = $1; } }
;
stmt
: if_stmt
| while_stmt
| for_stmt
| foreach_stmt
| datablock_decl
| switch_stmt
| rwBREAK ';'
{ $$ = BreakStmtNode::alloc( $1.lineNumber ); }
| rwCONTINUE ';'
{ $$ = ContinueStmtNode::alloc( $1.lineNumber ); }
| rwRETURN ';'
{ $$ = ReturnStmtNode::alloc( $1.lineNumber, NULL ); }
| rwRETURN expr ';'
{ $$ = ReturnStmtNode::alloc( $1.lineNumber, $2 ); }
| expression_stmt ';'
{ $$ = $1; }
| TTAG '=' expr ';'
{ $$ = TTagSetStmtNode::alloc( $1.lineNumber, $1.value, $3, NULL ); }
| TTAG '=' expr ',' expr ';'
{ $$ = TTagSetStmtNode::alloc( $1.lineNumber, $1.value, $3, $5 ); }
| DOCBLOCK
{ $$ = StrConstNode::alloc( $1.lineNumber, $1.value, false, true ); }
;
fn_decl_stmt
: rwDEFINE IDENT '(' var_list_decl ')' '{' statement_list '}'
{ $$ = FunctionDeclStmtNode::alloc( $1.lineNumber, $2.value, NULL, $4, $7 ); }
| rwDEFINE IDENT opCOLONCOLON IDENT '(' var_list_decl ')' '{' statement_list '}'
{ $$ = FunctionDeclStmtNode::alloc( $1.lineNumber, $4.value, $2.value, $6, $9 ); }
;
var_list_decl
:
{ $$ = NULL; }
| var_list
{ $$ = $1; }
;
var_list
: VAR
{ $$ = VarNode::alloc( $1.lineNumber, $1.value, NULL ); }
| var_list ',' VAR
{ $$ = $1; ((StmtNode*)($1))->append((StmtNode*)VarNode::alloc( $3.lineNumber, $3.value, NULL ) ); }
;
datablock_decl
: rwDATABLOCK class_name_expr '(' expr parent_block ')' '{' slot_assign_list_opt '}' ';'
{ $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $4, NULL, $5.value, $8, NULL, true, false, false); }
;
object_decl
: rwDECLARE class_name_expr '(' object_name parent_block object_args ')' '{' object_declare_block '}'
{ $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $4, $6, $5.value, $9.slots, $9.decls, false, false, false); }
| rwDECLARE class_name_expr '(' object_name parent_block object_args ')'
{ $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $4, $6, $5.value, NULL, NULL, false, false, false); }
| rwDECLARE class_name_expr '(' '[' object_name ']' parent_block object_args ')' '{' object_declare_block '}'
{ $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $5, $8, $7.value, $11.slots, $11.decls, false, true, false); }
| rwDECLARE class_name_expr '(' '[' object_name ']' parent_block object_args ')'
{ $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $5, $8, $7.value, NULL, NULL, false, true, false); }
| rwDECLARESINGLETON class_name_expr '(' object_name parent_block object_args ')' '{' object_declare_block '}'
{ $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $4, $6, $5.value, $9.slots, $9.decls, false, false, true); }
| rwDECLARESINGLETON class_name_expr '(' object_name parent_block object_args ')'
{ $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $4, $6, $5.value, NULL, NULL, false, false, true); }
;
parent_block
:
{ $$.value = NULL; }
| ':' IDENT
{ $$ = $2; }
;
object_name
:
{ $$ = StrConstNode::alloc( CodeBlock::smCurrentParser->getCurrentLine(), "", false); }
| expr
{ $$ = $1; }
;
object_args
:
{ $$ = NULL; }
| ',' expr_list
{ $$ = $2; }
;
object_declare_block
:
{ $$.slots = NULL; $$.decls = NULL; }
| slot_assign_list
{ $$.slots = $1; $$.decls = NULL; }
| object_decl_list
{ $$.slots = NULL; $$.decls = $1; }
| slot_assign_list object_decl_list
{ $$.slots = $1; $$.decls = $2; }
;
object_decl_list
: object_decl ';'
{ $$ = $1; }
| object_decl_list object_decl ';'
{ $1->append($2); $$ = $1; }
;
stmt_block
: '{' statement_list '}'
{ $$ = $2; }
| stmt
{ $$ = $1; }
;
switch_stmt
: rwSWITCH '(' expr ')' '{' case_block '}'
{ $$ = $6; $6->propagateSwitchExpr($3, false); }
| rwSWITCHSTR '(' expr ')' '{' case_block '}'
{ $$ = $6; $6->propagateSwitchExpr($3, true); }
;
case_block
: rwCASE case_expr ':' statement_list
{ $$ = IfStmtNode::alloc( $1.lineNumber, $2, $4, NULL, false); }
| rwCASE case_expr ':' statement_list rwDEFAULT ':' statement_list
{ $$ = IfStmtNode::alloc( $1.lineNumber, $2, $4, $7, false); }
| rwCASE case_expr ':' statement_list case_block
{ $$ = IfStmtNode::alloc( $1.lineNumber, $2, $4, $5, true); }
;
case_expr
: expr
{ $$ = $1;}
| case_expr rwCASEOR expr
{ ($1)->append($3); $$=$1; }
;
if_stmt
: rwIF '(' expr ')' stmt_block
{ $$ = IfStmtNode::alloc($1.lineNumber, $3, $5, NULL, false); }
| rwIF '(' expr ')' stmt_block rwELSE stmt_block
{ $$ = IfStmtNode::alloc($1.lineNumber, $3, $5, $7, false); }
;
while_stmt
: rwWHILE '(' expr ')' stmt_block
{ $$ = LoopStmtNode::alloc($1.lineNumber, nil, $3, nil, $5, false); }
| rwDO stmt_block rwWHILE '(' expr ')'
{ $$ = LoopStmtNode::alloc($3.lineNumber, nil, $5, nil, $2, true); }
;
for_stmt
: rwFOR '(' expr ';' expr ';' expr ')' stmt_block
{ $$ = LoopStmtNode::alloc($1.lineNumber, $3, $5, $7, $9, false); }
| rwFOR '(' expr ';' expr ';' ')' stmt_block
{ $$ = LoopStmtNode::alloc($1.lineNumber, $3, $5, NULL, $8, false); }
| rwFOR '(' expr ';' ';' expr ')' stmt_block
{ $$ = LoopStmtNode::alloc($1.lineNumber, $3, NULL, $6, $8, false); }
| rwFOR '(' expr ';' ';' ')' stmt_block
{ $$ = LoopStmtNode::alloc($1.lineNumber, $3, NULL, NULL, $7, false); }
| rwFOR '(' ';' expr ';' expr ')' stmt_block
{ $$ = LoopStmtNode::alloc($1.lineNumber, NULL, $4, $6, $8, false); }
| rwFOR '(' ';' expr ';' ')' stmt_block
{ $$ = LoopStmtNode::alloc($1.lineNumber, NULL, $4, NULL, $7, false); }
| rwFOR '(' ';' ';' expr ')' stmt_block
{ $$ = LoopStmtNode::alloc($1.lineNumber, NULL, NULL, $5, $7, false); }
| rwFOR '(' ';' ';' ')' stmt_block
{ $$ = LoopStmtNode::alloc($1.lineNumber, NULL, NULL, NULL, $6, false); }
;
foreach_stmt
: rwFOREACH '(' VAR rwIN expr ')' stmt_block
{ $$ = IterStmtNode::alloc( $1.lineNumber, $3.value, $5, $7, false ); }
| rwFOREACHSTR '(' VAR rwIN expr ')' stmt_block
{ $$ = IterStmtNode::alloc( $1.lineNumber, $3.value, $5, $7, true ); }
;
expression_stmt
: stmt_expr
{ $$ = $1; }
;
expr
: stmt_expr
{ $$ = $1; }
| '(' expr ')'
{ $$ = $2; }
| expr '^' expr
{ $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr '%' expr
{ $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr '&' expr
{ $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr '|' expr
{ $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr '+' expr
{ $$ = FloatBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr '-' expr
{ $$ = FloatBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr '*' expr
{ $$ = FloatBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr '/' expr
{ $$ = FloatBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| '-' expr %prec UNARY
{ $$ = FloatUnaryExprNode::alloc( $1.lineNumber, $1.value, $2); }
| '*' expr %prec UNARY
{ $$ = TTagDerefNode::alloc( $1.lineNumber, $2 ); }
| TTAG
{ $$ = TTagExprNode::alloc( $1.lineNumber, $1.value ); }
| expr '?' expr ':' expr
{ $$ = ConditionalExprNode::alloc( $1->dbgLineNumber, $1, $3, $5); }
| expr '<' expr
{ $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr '>' expr
{ $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr opGE expr
{ $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr opLE expr
{ $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr opEQ expr
{ $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr opNE expr
{ $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr opOR expr
{ $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr opSHL expr
{ $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr opSHR expr
{ $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr opAND expr
{ $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); }
| expr opSTREQ expr
{ $$ = StreqExprNode::alloc( $1->dbgLineNumber, $1, $3, true); }
| expr opSTRNE expr
{ $$ = StreqExprNode::alloc( $1->dbgLineNumber, $1, $3, false); }
| expr '@' expr
{ $$ = StrcatExprNode::alloc( $1->dbgLineNumber, $1, $3, $2.value); }
| '!' expr
{ $$ = IntUnaryExprNode::alloc($1.lineNumber, $1.value, $2); }
| '~' expr
{ $$ = IntUnaryExprNode::alloc($1.lineNumber, $1.value, $2); }
| TAGATOM
{ $$ = StrConstNode::alloc( $1.lineNumber, $1.value, true); }
| FLTCONST
{ $$ = FloatNode::alloc( $1.lineNumber, $1.value ); }
| INTCONST
{ $$ = IntNode::alloc( $1.lineNumber, $1.value ); }
| rwBREAK
{ $$ = ConstantNode::alloc( $1.lineNumber, StringTable->insert("break")); }
| slot_acc
{ $$ = SlotAccessNode::alloc( $1.lineNumber, $1.object, $1.array, $1.slotName ); }
| intslot_acc
{ $$ = InternalSlotAccessNode::alloc( $1.lineNumber, $1.object, $1.slotExpr, $1.recurse); }
| IDENT
{ $$ = ConstantNode::alloc( $1.lineNumber, $1.value ); }
| STRATOM
{ $$ = StrConstNode::alloc( $1.lineNumber, $1.value, false); }
| VAR
{ $$ = (ExprNode*)VarNode::alloc( $1.lineNumber, $1.value, NULL); }
| VAR '[' aidx_expr ']'
{ $$ = (ExprNode*)VarNode::alloc( $1.lineNumber, $1.value, $3 ); }
;
/*
| rwDEFINE '(' var_list_decl ')' '{' statement_list '}'
{
const U32 bufLen = 64;
UTF8 buffer[bufLen];
dSprintf(buffer, bufLen, "__anonymous_function%d", gAnonFunctionID++);
StringTableEntry fName = StringTable->insert(buffer);
StmtNode *fndef = FunctionDeclStmtNode::alloc($1.lineNumber, fName, NULL, $3, $6);
if(!gAnonFunctionList)
gAnonFunctionList = fndef;
else
gAnonFunctionList->append(fndef);
$$ = StrConstNode::alloc( $1.lineNumber, (UTF8*)fName, false );
}
*/
slot_acc
: expr '.' IDENT
{ $$.lineNumber = $1->dbgLineNumber; $$.object = $1; $$.slotName = $3.value; $$.array = NULL; }
| expr '.' IDENT '[' aidx_expr ']'
{ $$.lineNumber = $1->dbgLineNumber; $$.object = $1; $$.slotName = $3.value; $$.array = $5; }
;
intslot_acc
: expr opINTNAME class_name_expr
{ $$.lineNumber = $1->dbgLineNumber; $$.object = $1; $$.slotExpr = $3; $$.recurse = false; }
| expr opINTNAMER class_name_expr
{ $$.lineNumber = $1->dbgLineNumber; $$.object = $1; $$.slotExpr = $3; $$.recurse = true; }
;
class_name_expr
: IDENT
{ $$ = ConstantNode::alloc( $1.lineNumber, $1.value ); }
| '(' expr ')'
{ $$ = $2; }
;
assign_op_struct
: opPLUSPLUS
{ $$.lineNumber = $1.lineNumber; $$.token = opPLUSPLUS; $$.expr = FloatNode::alloc( $1.lineNumber, 1 ); }
| opMINUSMINUS
{ $$.lineNumber = $1.lineNumber; $$.token = opMINUSMINUS; $$.expr = FloatNode::alloc( $1.lineNumber, 1 ); }
| opPLASN expr
{ $$.lineNumber = $1.lineNumber; $$.token = '+'; $$.expr = $2; }
| opMIASN expr
{ $$.lineNumber = $1.lineNumber; $$.token = '-'; $$.expr = $2; }
| opMLASN expr
{ $$.lineNumber = $1.lineNumber; $$.token = '*'; $$.expr = $2; }
| opDVASN expr
{ $$.lineNumber = $1.lineNumber; $$.token = '/'; $$.expr = $2; }
| opMODASN expr
{ $$.lineNumber = $1.lineNumber; $$.token = '%'; $$.expr = $2; }
| opANDASN expr
{ $$.lineNumber = $1.lineNumber; $$.token = '&'; $$.expr = $2; }
| opXORASN expr
{ $$.lineNumber = $1.lineNumber; $$.token = '^'; $$.expr = $2; }
| opORASN expr
{ $$.lineNumber = $1.lineNumber; $$.token = '|'; $$.expr = $2; }
| opSLASN expr
{ $$.lineNumber = $1.lineNumber; $$.token = opSHL; $$.expr = $2; }
| opSRASN expr
{ $$.lineNumber = $1.lineNumber; $$.token = opSHR; $$.expr = $2; }
;
stmt_expr
: funcall_expr
{ $$ = $1; }
| assert_expr
{ $$ = $1; }
| object_decl
{ $$ = $1; }
| VAR '=' expr
{ $$ = AssignExprNode::alloc( $1.lineNumber, $1.value, NULL, $3); }
| VAR '[' aidx_expr ']' '=' expr
{ $$ = AssignExprNode::alloc( $1.lineNumber, $1.value, $3, $6); }
| VAR assign_op_struct
{ $$ = AssignOpExprNode::alloc( $1.lineNumber, $1.value, NULL, $2.expr, $2.token); }
| VAR '[' aidx_expr ']' assign_op_struct
{ $$ = AssignOpExprNode::alloc( $1.lineNumber, $1.value, $3, $5.expr, $5.token); }
| slot_acc assign_op_struct
{ $$ = SlotAssignOpNode::alloc( $1.lineNumber, $1.object, $1.slotName, $1.array, $2.token, $2.expr); }
| slot_acc '=' expr
{ $$ = SlotAssignNode::alloc( $1.lineNumber, $1.object, $1.array, $1.slotName, $3); }
| slot_acc '=' '{' expr_list '}'
{ $$ = SlotAssignNode::alloc( $1.lineNumber, $1.object, $1.array, $1.slotName, $4); }
;
funcall_expr
: IDENT '(' expr_list_decl ')'
{ $$ = FuncCallExprNode::alloc( $1.lineNumber, $1.value, NULL, $3, false); }
| IDENT opCOLONCOLON IDENT '(' expr_list_decl ')'
{ $$ = FuncCallExprNode::alloc( $1.lineNumber, $3.value, $1.value, $5, false); }
| expr '.' IDENT '(' expr_list_decl ')'
{ $1->append($5); $$ = FuncCallExprNode::alloc( $1->dbgLineNumber, $3.value, NULL, $1, true); }
;
/*
| expr '(' expr_list_decl ')'
{ $$ = FuncPointerCallExprNode::alloc( $1->dbgLineNumber, $1, $3); }
;
*/
assert_expr
: rwASSERT '(' expr ')'
{ $$ = AssertCallExprNode::alloc( $1.lineNumber, $3, NULL ); }
| rwASSERT '(' expr ',' STRATOM ')'
{ $$ = AssertCallExprNode::alloc( $1.lineNumber, $3, $5.value ); }
;
expr_list_decl
:
{ $$ = NULL; }
| expr_list
{ $$ = $1; }
;
expr_list
: expr
{ $$ = $1; }
| expr_list ',' expr
{ ($1)->append($3); $$ = $1; }
;
slot_assign_list_opt
:
{ $$ = NULL; }
| slot_assign_list
{ $$ = $1; }
;
slot_assign_list
: slot_assign
{ $$ = $1; }
| slot_assign_list slot_assign
{ $1->append($2); $$ = $1; }
;
slot_assign
: IDENT '=' expr ';'
{ $$ = SlotAssignNode::alloc( $1.lineNumber, NULL, NULL, $1.value, $3); }
| TYPEIDENT IDENT '=' expr ';'
{ $$ = SlotAssignNode::alloc( $1.lineNumber, NULL, NULL, $2.value, $4, $1.value); }
| rwDATABLOCK '=' expr ';'
{ $$ = SlotAssignNode::alloc( $1.lineNumber, NULL, NULL, StringTable->insert("datablock"), $3); }
| IDENT '[' aidx_expr ']' '=' expr ';'
{ $$ = SlotAssignNode::alloc( $1.lineNumber, NULL, $3, $1.value, $6); }
| TYPEIDENT IDENT '[' aidx_expr ']' '=' expr ';'
{ $$ = SlotAssignNode::alloc( $1.lineNumber, NULL, $4, $2.value, $7, $1.value); }
;
aidx_expr
: expr
{ $$ = $1; }
| aidx_expr ',' expr
{ $$ = CommaCatExprNode::alloc( $1->dbgLineNumber, $1, $3); }
;
%%

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,630 @@
%{
// flex --nounput -o CMDscan.cpp -P CMD CMDscan.l
#define YYLMAX 4096
#define YY_NO_UNISTD_H
#include <stdio.h>
#include "platform/platform.h"
#include "core/stringTable.h"
#include "console/console.h"
#include "console/compiler.h"
#include "console/dynamicTypes.h"
#include "core/strings/stringFunctions.h"
template< typename T >
struct Token
{
T value;
S32 lineNumber;
};
// Can't have ctors in structs used in unions, so we have this.
template< typename T >
inline Token< T > MakeToken( T value, U32 lineNumber )
{
Token< T > result;
result.value = value;
result.lineNumber = lineNumber;
return result;
}
#include "console/cmdgram.h"
// HACK: C++17 and beyond can't use register keyword
#define register
using namespace Compiler;
#define YY_NEVER_INTERACTIVE 1
// Some basic parsing primitives...
static int Sc_ScanDocBlock();
static int Sc_ScanString(int ret);
static int Sc_ScanNum();
static int Sc_ScanVar();
static int Sc_ScanHex();
static int Sc_ScanIdent();
// Deal with debuggability of FLEX.
#ifdef TORQUE_DEBUG
#define FLEX_DEBUG 1
#else
#define FLEX_DEBUG 0
#endif
// Install our own input code...
#undef CMDgetc
int CMDgetc();
// Hack to make windows lex happy.
#ifndef isatty
inline int isatty(int) { return 0; }
#endif
// Wrap our getc, so that lex doesn't try to do its own buffering/file IO.
#define YY_INPUT(buf,result,max_size) \
{ \
int c = '*', n; \
for ( n = 0; n < max_size && \
(c = CMDgetc()) != EOF && c != '\n'; ++n ) \
buf[n] = (char) c; \
if ( c == '\n' ) \
buf[n++] = (char) c; \
result = n; \
}
// General helper stuff.
static int lineIndex;
// File state
void CMDSetScanBuffer(const char *sb, const char *fn);
const char * CMDgetFileLine(int &lineNumber);
// Error reporting
void CMDerror(const char * s, ...);
// Reset the parser.
void CMDrestart(FILE *in);
%}
DIGIT [0-9]
INTEGER {DIGIT}+
FLOAT ({INTEGER}?\.{INTEGER})|({INTEGER}(\.{INTEGER})?[eE][+-]?{INTEGER})
LETTER [A-Za-z_]
FILECHAR [A-Za-z_\.]
VARMID [:A-Za-z0-9_]
IDTAIL [A-Za-z0-9_]
VARTAIL {VARMID}*{IDTAIL}
VAR [$%]{LETTER}{VARTAIL}*
ID {LETTER}{IDTAIL}*
ILID [$%]{DIGIT}+{LETTER}{VARTAIL}*
FILENAME {FILECHAR}+
SPACE [ \t\v\f]
HEXDIGIT [a-fA-F0-9]
%%
;
{SPACE}+ { }
("///"([^/\n\r][^\n\r]*)?[\n\r]+)+ { return(Sc_ScanDocBlock()); }
"//"[^\n\r]* ;
[\r] ;
[\n] {lineIndex++;}
\"(\\.|[^\\"\n\r])*\" { return(Sc_ScanString(STRATOM)); }
\'(\\.|[^\\'\n\r])*\' { return(Sc_ScanString(TAGATOM)); }
"==" { CMDlval.i = MakeToken< int >( opEQ, lineIndex ); return opEQ; }
"!=" { CMDlval.i = MakeToken< int >( opNE, lineIndex ); return opNE; }
">=" { CMDlval.i = MakeToken< int >( opGE, lineIndex ); return opGE; }
"<=" { CMDlval.i = MakeToken< int >( opLE, lineIndex ); return opLE; }
"&&" { CMDlval.i = MakeToken< int >( opAND, lineIndex ); return opAND; }
"||" { CMDlval.i = MakeToken< int >( opOR, lineIndex ); return opOR; }
"::" { CMDlval.i = MakeToken< int >( opCOLONCOLON, lineIndex ); return opCOLONCOLON; }
"--" { CMDlval.i = MakeToken< int >( opMINUSMINUS, lineIndex ); return opMINUSMINUS; }
"++" { CMDlval.i = MakeToken< int >( opPLUSPLUS, lineIndex ); return opPLUSPLUS; }
"$=" { CMDlval.i = MakeToken< int >( opSTREQ, lineIndex ); return opSTREQ; }
"!$=" { CMDlval.i = MakeToken< int >( opSTRNE, lineIndex ); return opSTRNE; }
"<<" { CMDlval.i = MakeToken< int >( opSHL, lineIndex ); return opSHL; }
">>" { CMDlval.i = MakeToken< int >( opSHR, lineIndex ); return opSHR; }
"+=" { CMDlval.i = MakeToken< int >( opPLASN, lineIndex ); return opPLASN; }
"-=" { CMDlval.i = MakeToken< int >( opMIASN, lineIndex ); return opMIASN; }
"*=" { CMDlval.i = MakeToken< int >( opMLASN, lineIndex ); return opMLASN; }
"/=" { CMDlval.i = MakeToken< int >( opDVASN, lineIndex ); return opDVASN; }
"%=" { CMDlval.i = MakeToken< int >( opMODASN, lineIndex ); return opMODASN; }
"&=" { CMDlval.i = MakeToken< int >( opANDASN, lineIndex ); return opANDASN; }
"^=" { CMDlval.i = MakeToken< int >( opXORASN, lineIndex ); return opXORASN; }
"|=" { CMDlval.i = MakeToken< int >( opORASN, lineIndex ); return opORASN; }
"<<=" { CMDlval.i = MakeToken< int >( opSLASN, lineIndex ); return opSLASN; }
">>=" { CMDlval.i = MakeToken< int >( opSRASN, lineIndex ); return opSRASN; }
"->" { CMDlval.i = MakeToken< int >( opINTNAME, lineIndex ); return opINTNAME; }
"-->" { CMDlval.i = MakeToken< int >( opINTNAMER, lineIndex ); return opINTNAMER; }
"NL" { CMDlval.i = MakeToken< int >( '\n', lineIndex ); return '@'; }
"TAB" { CMDlval.i = MakeToken< int >( '\t', lineIndex ); return '@'; }
"SPC" { CMDlval.i = MakeToken< int >( ' ', lineIndex ); return '@'; }
"@" { CMDlval.i = MakeToken< int >( 0, lineIndex ); return '@'; }
"/*" { /* this comment stops syntax highlighting from getting messed up when editing the lexer in TextPad */
int c = 0, l;
for ( ; ; )
{
l = c;
c = yyinput();
// Is this an open comment?
if ( c == EOF )
{
CMDerror( "unexpected end of file found in comment" );
break;
}
// Increment line numbers.
else if ( c == '\n' )
lineIndex++;
// Did we find the end of the comment?
else if ( l == '*' && c == '/' )
break;
}
}
"?" |
"[" |
"]" |
"(" |
")" |
"+" |
"-" |
"*" |
"/" |
"<" |
">" |
"|" |
"." |
"!" |
":" |
";" |
"{" |
"}" |
"," |
"&" |
"%" |
"^" |
"~" |
"=" { CMDlval.i = MakeToken< int >( CMDtext[ 0 ], lineIndex ); return CMDtext[ 0 ]; }
"in" { CMDlval.i = MakeToken< int >( rwIN, lineIndex ); return(rwIN); }
"or" { CMDlval.i = MakeToken< int >( rwCASEOR, lineIndex ); return(rwCASEOR); }
"break" { CMDlval.i = MakeToken< int >( rwBREAK, lineIndex ); return(rwBREAK); }
"return" { CMDlval.i = MakeToken< int >( rwRETURN, lineIndex ); return(rwRETURN); }
"else" { CMDlval.i = MakeToken< int >( rwELSE, lineIndex ); return(rwELSE); }
"assert" { CMDlval.i = MakeToken< int >( rwASSERT, lineIndex ); return(rwASSERT); }
"while" { CMDlval.i = MakeToken< int >( rwWHILE, lineIndex ); return(rwWHILE); }
"do" { CMDlval.i = MakeToken< int >( rwDO, lineIndex ); return(rwDO); }
"if" { CMDlval.i = MakeToken< int >( rwIF, lineIndex ); return(rwIF); }
"foreach$" { CMDlval.i = MakeToken< int >( rwFOREACHSTR, lineIndex ); return(rwFOREACHSTR); }
"foreach" { CMDlval.i = MakeToken< int >( rwFOREACH, lineIndex ); return(rwFOREACH); }
"for" { CMDlval.i = MakeToken< int >( rwFOR, lineIndex ); return(rwFOR); }
"continue" { CMDlval.i = MakeToken< int >( rwCONTINUE, lineIndex ); return(rwCONTINUE); }
"function" { CMDlval.i = MakeToken< int >( rwDEFINE, lineIndex ); return(rwDEFINE); }
"new" { CMDlval.i = MakeToken< int >( rwDECLARE, lineIndex ); return(rwDECLARE); }
"singleton" { CMDlval.i = MakeToken< int >( rwDECLARESINGLETON, lineIndex ); return(rwDECLARESINGLETON); }
"datablock" { CMDlval.i = MakeToken< int >( rwDATABLOCK, lineIndex ); return(rwDATABLOCK); }
"case" { CMDlval.i = MakeToken< int >( rwCASE, lineIndex ); return(rwCASE); }
"switch$" { CMDlval.i = MakeToken< int >( rwSWITCHSTR, lineIndex ); return(rwSWITCHSTR); }
"switch" { CMDlval.i = MakeToken< int >( rwSWITCH, lineIndex ); return(rwSWITCH); }
"default" { CMDlval.i = MakeToken< int >( rwDEFAULT, lineIndex ); return(rwDEFAULT); }
"package" { CMDlval.i = MakeToken< int >( rwPACKAGE, lineIndex ); return(rwPACKAGE); }
"namespace" { CMDlval.i = MakeToken< int >( rwNAMESPACE, lineIndex ); return(rwNAMESPACE); }
"true" { CMDlval.i = MakeToken< int >( 1, lineIndex ); return INTCONST; }
"false" { CMDlval.i = MakeToken< int >( 0, lineIndex ); return INTCONST; }
{VAR} { return(Sc_ScanVar()); }
{ID} { return Sc_ScanIdent(); }
0[xX]{HEXDIGIT}+ return(Sc_ScanHex());
{INTEGER} { CMDtext[CMDleng] = 0; CMDlval.i = MakeToken< int >( dAtoi(CMDtext), lineIndex ); return INTCONST; }
{FLOAT} return Sc_ScanNum();
{ILID} return(ILLEGAL_TOKEN);
. return(ILLEGAL_TOKEN);
%%
static const char *scanBuffer;
static const char *fileName;
static int scanIndex;
const char * CMDGetCurrentFile()
{
return fileName;
}
int CMDGetCurrentLine()
{
return lineIndex;
}
extern bool gConsoleSyntaxError;
void CMDerror(const char *format, ...)
{
Compiler::gSyntaxError = true;
const int BUFMAX = 1024;
char tempBuf[BUFMAX];
va_list args;
va_start( args, format );
#ifdef TORQUE_OS_WIN
_vsnprintf( tempBuf, BUFMAX, format, args );
#else
vsnprintf( tempBuf, BUFMAX, format, args );
#endif
va_end(args);
if(fileName)
{
Con::errorf(ConsoleLogEntry::Script, "%s Line: %d - %s", fileName, lineIndex, tempBuf);
#ifndef NO_ADVANCED_ERROR_REPORT
// dhc - lineIndex is bogus. let's try to add some sanity back in.
int i,j,n;
char c;
int linediv = 1;
// first, walk the buffer, trying to detect line ending type.
// this is imperfect, if inconsistant line endings exist...
for (i=0; i<scanIndex; i++)
{
c = scanBuffer[i];
if (c=='\r' && scanBuffer[i+1]=='\n') linediv = 2; // crlf detected
if (c=='\r' || c=='\n' || c==0) break; // enough for us to stop.
}
// grab some of the chars starting at the error location - lineending.
i = 1; j = 0; n = 1;
// find prev lineending
while (n<BUFMAX-8 && i<scanIndex) // cap at file start
{
c = scanBuffer[scanIndex-i];
if ((c=='\r' || c=='\n') && i>BUFMAX>>2) break; // at least get a little data
n++; i++;
}
// find next lineending
while (n<BUFMAX-8 && j<BUFMAX>>1) // cap at half-buf-size forward
{
c = scanBuffer[scanIndex+j];
if (c==0) break;
if ((c=='\r' || c=='\n') && j>BUFMAX>>2) break; // at least get a little data
n++; j++;
}
if (i) i--; // chop off extra linefeed.
if (j) j--; // chop off extra linefeed.
// build our little text block
if (i) dStrncpy(tempBuf,scanBuffer+scanIndex-i,i);
dStrncpy(tempBuf+i,"##", 2); // bracketing.
tempBuf[i+2] = scanBuffer[scanIndex]; // copy the halt character.
dStrncpy(tempBuf+i+3,"##", 2); // bracketing.
if (j) dStrncpy(tempBuf+i+5,scanBuffer+scanIndex+1,j); // +1 to go past current char.
tempBuf[i+j+5] = 0; // null terminate
for(n=0; n<i+j+5; n++) // convert CR to LF if alone...
if (tempBuf[n]=='\r' && tempBuf[n+1]!='\n') tempBuf[n] = '\n';
// write out to console the advanced error report
Con::warnf(ConsoleLogEntry::Script, ">>> Advanced script error report. Line %d.", lineIndex);
Con::warnf(ConsoleLogEntry::Script, ">>> Some error context, with ## on sides of error halt:");
Con::errorf(ConsoleLogEntry::Script, "%s", tempBuf);
Con::warnf(ConsoleLogEntry::Script, ">>> Error report complete.\n");
#endif
// Update the script-visible error buffer.
const char *prevStr = Con::getVariable("$ScriptError");
if (prevStr[0])
dSprintf(tempBuf, sizeof(tempBuf), "%s\n%s Line: %d - Syntax error.", prevStr, fileName, lineIndex);
else
dSprintf(tempBuf, sizeof(tempBuf), "%s Line: %d - Syntax error.", fileName, lineIndex);
Con::setVariable("$ScriptError", tempBuf);
// We also need to mark that we came up with a new error.
static S32 sScriptErrorHash=1000;
Con::setIntVariable("$ScriptErrorHash", sScriptErrorHash++);
}
else
Con::errorf(ConsoleLogEntry::Script, tempBuf);
}
void CMDSetScanBuffer(const char *sb, const char *fn)
{
scanBuffer = sb;
fileName = fn;
scanIndex = 0;
lineIndex = 1;
}
int CMDgetc()
{
int ret = scanBuffer[scanIndex];
if(ret)
scanIndex++;
else
ret = -1;
return ret;
}
int CMDwrap()
{
return 1;
}
static int Sc_ScanVar()
{
// Truncate the temp buffer...
CMDtext[CMDleng] = 0;
// Make it a stringtable string!
CMDlval.s = MakeToken< StringTableEntry >( StringTable->insert(CMDtext), lineIndex );
return(VAR);
}
static int charConv(int in)
{
switch(in)
{
case 'r':
return '\r';
case 'n':
return '\n';
case 't':
return '\t';
default:
return in;
}
}
static int getHexDigit(char c)
{
if(c >= '0' && c <= '9')
return c - '0';
if(c >= 'A' && c <= 'F')
return c - 'A' + 10;
if(c >= 'a' && c <= 'f')
return c - 'a' + 10;
return -1;
}
static int Sc_ScanDocBlock()
{
S32 len = dStrlen(CMDtext);
char* text = (char *) consoleAlloc(len + 1);
S32 line = lineIndex;
for( S32 i = 0, j = 0; j <= len; j++ )
{
if( ( j <= (len - 2) ) && ( CMDtext[j] == '/' ) && ( CMDtext[j + 1] == '/' ) && ( CMDtext[j + 2] == '/' ) )
{
j += 2;
continue;
}
if( CMDtext[j] == '\r' )
continue;
if( CMDtext[j] == '\n' )
lineIndex++;
text[i++] = CMDtext[j];
}
CMDlval.str = MakeToken< char* >( text, line );
return(DOCBLOCK);
}
static int Sc_ScanString(int ret)
{
CMDtext[CMDleng - 1] = 0;
if(!collapseEscape(CMDtext+1))
return -1;
dsize_t bufferLen = dStrlen( CMDtext );
char* buffer = ( char* ) consoleAlloc( bufferLen );
dStrcpy( buffer, CMDtext + 1, bufferLen );
CMDlval.str = MakeToken< char* >( buffer, lineIndex );
return ret;
}
static int Sc_ScanIdent()
{
ConsoleBaseType *type;
CMDtext[CMDleng] = 0;
if((type = ConsoleBaseType::getTypeByName(CMDtext)) != NULL)
{
/* It's a type */
CMDlval.i = MakeToken< int >( type->getTypeID(), lineIndex );
return TYPEIDENT;
}
/* It's an identifier */
CMDlval.s = MakeToken< StringTableEntry >( StringTable->insert(CMDtext), lineIndex );
return IDENT;
}
void expandEscape(char *dest, const char *src)
{
U8 c;
while((c = (U8) *src++) != 0)
{
if(c == '\"')
{
*dest++ = '\\';
*dest++ = '\"';
}
else if(c == '\\')
{
*dest++ = '\\';
*dest++ = '\\';
}
else if(c == '\r')
{
*dest++ = '\\';
*dest++ = 'r';
}
else if(c == '\n')
{
*dest++ = '\\';
*dest++ = 'n';
}
else if(c == '\t')
{
*dest++ = '\\';
*dest++ = 't';
}
else if(c == '\'')
{
*dest++ = '\\';
*dest++ = '\'';
}
else if((c >= 1 && c <= 7) ||
(c >= 11 && c <= 12) ||
(c >= 14 && c <= 15))
{
/* Remap around: \b = 0x8, \t = 0x9, \n = 0xa, \r = 0xd */
static U8 expandRemap[15] = { 0x0,
0x0,
0x1,
0x2,
0x3,
0x4,
0x5,
0x6,
0x0,
0x0,
0x0,
0x7,
0x8,
0x0,
0x9 };
*dest++ = '\\';
*dest++ = 'c';
if(c == 15)
*dest++ = 'r';
else if(c == 16)
*dest++ = 'p';
else if(c == 17)
*dest++ = 'o';
else
*dest++ = expandRemap[c] + '0';
}
else if(c < 32)
{
*dest++ = '\\';
*dest++ = 'x';
S32 dig1 = c >> 4;
S32 dig2 = c & 0xf;
if(dig1 < 10)
dig1 += '0';
else
dig1 += 'A' - 10;
if(dig2 < 10)
dig2 += '0';
else
dig2 += 'A' - 10;
*dest++ = dig1;
*dest++ = dig2;
}
else
*dest++ = c;
}
*dest = '\0';
}
bool collapseEscape(char *buf)
{
S32 len = dStrlen(buf) + 1;
for(S32 i = 0; i < len;)
{
if(buf[i] == '\\')
{
if(buf[i+1] == 'x')
{
S32 dig1 = getHexDigit(buf[i+2]);
if(dig1 == -1)
return false;
S32 dig2 = getHexDigit(buf[i+3]);
if(dig2 == -1)
return false;
buf[i] = dig1 * 16 + dig2;
dMemmove(buf + i + 1, buf + i + 4, len - i - 3);
len -= 3;
i++;
}
else if(buf[i+1] == 'c')
{
/* Remap around: \b = 0x8, \t = 0x9, \n = 0xa, \r = 0xd */
static U8 collapseRemap[10] = { 0x1,
0x2,
0x3,
0x4,
0x5,
0x6,
0x7,
0xb,
0xc,
0xe };
if(buf[i+2] == 'r')
buf[i] = 15;
else if(buf[i+2] == 'p')
buf[i] = 16;
else if(buf[i+2] == 'o')
buf[i] = 17;
else
{
int dig1 = buf[i+2] - '0';
if(dig1 < 0 || dig1 > 9)
return false;
buf[i] = collapseRemap[dig1];
}
// Make sure we don't put 0x1 at the beginning of the string.
if ((buf[i] == 0x1) && (i == 0))
{
buf[i] = 0x2;
buf[i+1] = 0x1;
dMemmove(buf + i + 2, buf + i + 3, len - i - 1);
len -= 1;
}
else
{
dMemmove(buf + i + 1, buf + i + 3, len - i - 2);
len -= 2;
}
i++;
}
else
{
buf[i] = charConv(buf[i+1]);
dMemmove(buf + i + 1, buf + i + 2, len - i - 1);
len--;
i++;
}
}
else
i++;
}
return true;
}
static int Sc_ScanNum()
{
CMDtext[CMDleng] = 0;
CMDlval.f = MakeToken< double >( dAtof(CMDtext), lineIndex );
return(FLTCONST);
}
static int Sc_ScanHex()
{
S32 val = 0;
dSscanf(CMDtext, "%x", &val);
CMDlval.i = MakeToken< int >( val, lineIndex );
return INTCONST;
}
void CMD_reset()
{
CMDrestart(NULL);
}

View file

@ -0,0 +1,606 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _AST_H_
#define _AST_H_
#include "evalState.h"
#include "platform/types.h"
class SimObject;
class SimGroup;
class CodeStream;
/// Enable this #define if you are seeing the message "precompile size mismatch" in the console.
/// This will help track down which node type is causing the error. It could be
/// due to incorrect compiler optimization.
//#define DEBUG_AST_NODES
enum TypeReq
{
TypeReqNone,
TypeReqUInt,
TypeReqFloat,
TypeReqString
};
enum ExprNodeName
{
NameExprNode,
NameFloatNode,
NameIntNode,
NameVarNode
};
/// Representation of a node for the scripting language parser.
///
/// When the scripting language is evaluated, it is turned from a string representation,
/// into a parse tree, thence into byte code, which is ultimately interpreted by the VM.
///
/// This is the base class for the nodes in the parse tree. There are a great many subclasses,
/// each representing a different language construct.
struct StmtNode
{
StmtNode* next; ///< Next entry in parse tree.
StmtNode();
virtual ~StmtNode() {}
/// @name next Accessors
/// @{
///
void append(StmtNode* next);
StmtNode* getNext() const { return next; }
/// @}
/// @name Debug Info
/// @{
StringTableEntry dbgFileName; ///< Name of file this node is associated with.
S32 dbgLineNumber; ///< Line number this node is associated with.
#ifdef DEBUG_AST_NODES
virtual String dbgStmtType() const = 0;
#endif
/// @}
/// @name Breaking
/// @{
void addBreakLine(CodeStream& codeStream);
/// @}
/// @name Compilation
/// @{
virtual U32 compileStmt(CodeStream& codeStream, U32 ip) = 0;
virtual void setPackage(StringTableEntry packageName);
/// @}
};
/// Helper macro
#ifndef DEBUG_AST_NODES
# define DBG_STMT_TYPE(s) virtual const char* dbgStmtType() const { return "#s"; }
#else
# define DBG_STMT_TYPE(s)
#endif
struct BreakStmtNode : StmtNode
{
static BreakStmtNode* alloc(S32 lineNumber);
U32 compileStmt(CodeStream& codeStream, U32 ip);
DBG_STMT_TYPE(BreakStmtNode);
};
struct ContinueStmtNode : StmtNode
{
static ContinueStmtNode* alloc(S32 lineNumber);
U32 compileStmt(CodeStream& codeStream, U32 ip);
DBG_STMT_TYPE(ContinueStmtNode);
};
/// A mathematical expression.
struct ExprNode : StmtNode
{
ExprNode* optimizedNode;
U32 compileStmt(CodeStream& codeStream, U32 ip);
virtual U32 compile(CodeStream& codeStream, U32 ip, TypeReq type) = 0;
virtual TypeReq getPreferredType() = 0;
virtual ExprNodeName getExprNodeNameEnum() const { return NameExprNode; }
};
struct ReturnStmtNode : StmtNode
{
ExprNode* expr;
static ReturnStmtNode* alloc(S32 lineNumber, ExprNode* expr);
U32 compileStmt(CodeStream& codeStream, U32 ip);
DBG_STMT_TYPE(ReturnStmtNode);
};
struct IfStmtNode : StmtNode
{
ExprNode* testExpr;
StmtNode* ifBlock, * elseBlock;
U32 endifOffset;
U32 elseOffset;
bool integer;
bool propagate;
static IfStmtNode* alloc(S32 lineNumber, ExprNode* testExpr, StmtNode* ifBlock, StmtNode* elseBlock, bool propagateThrough);
void propagateSwitchExpr(ExprNode* left, bool string);
ExprNode* getSwitchOR(ExprNode* left, ExprNode* list, bool string);
U32 compileStmt(CodeStream& codeStream, U32 ip);
DBG_STMT_TYPE(IfStmtNode);
};
struct LoopStmtNode : StmtNode
{
ExprNode* testExpr;
ExprNode* initExpr;
ExprNode* endLoopExpr;
StmtNode* loopBlock;
bool isDoLoop;
U32 breakOffset;
U32 continueOffset;
U32 loopBlockStartOffset;
bool integer;
static LoopStmtNode* alloc(S32 lineNumber, ExprNode* testExpr, ExprNode* initExpr, ExprNode* endLoopExpr, StmtNode* loopBlock, bool isDoLoop);
U32 compileStmt(CodeStream& codeStream, U32 ip);
DBG_STMT_TYPE(LoopStmtNode);
};
/// A "foreach" statement.
struct IterStmtNode : StmtNode
{
/// Local variable name to use for the container element.
StringTableEntry varName;
/// Expression evaluating to a SimSet object.
ExprNode* containerExpr;
/// The statement body.
StmtNode* body;
/// If true, this is a 'foreach$'.
bool isStringIter;
/// Bytecode size of body statement. Set by precompileStmt.
U32 bodySize;
static IterStmtNode* alloc(S32 lineNumber, StringTableEntry varName, ExprNode* containerExpr, StmtNode* body, bool isStringIter);
U32 compileStmt(CodeStream& codeStream, U32 ip);
};
/// A binary mathematical expression (ie, left op right).
struct BinaryExprNode : ExprNode
{
S32 op;
ExprNode* left;
ExprNode* right;
};
struct FloatBinaryExprNode : BinaryExprNode
{
static FloatBinaryExprNode* alloc(S32 lineNumber, S32 op, ExprNode* left, ExprNode* right);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
bool optimize();
TypeReq getPreferredType();
DBG_STMT_TYPE(FloatBinaryExprNode);
};
struct ConditionalExprNode : ExprNode
{
ExprNode* testExpr;
ExprNode* trueExpr;
ExprNode* falseExpr;
bool integer;
static ConditionalExprNode* alloc(S32 lineNumber, ExprNode* testExpr, ExprNode* trueExpr, ExprNode* falseExpr);
virtual U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
virtual TypeReq getPreferredType();
DBG_STMT_TYPE(ConditionalExprNode);
};
struct IntBinaryExprNode : BinaryExprNode
{
TypeReq subType;
U32 operand;
static IntBinaryExprNode* alloc(S32 lineNumber, S32 op, ExprNode* left, ExprNode* right);
void getSubTypeOperand();
bool optimize();
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(IntBinaryExprNode);
};
struct StreqExprNode : BinaryExprNode
{
bool eq;
static StreqExprNode* alloc(S32 lineNumber, ExprNode* left, ExprNode* right, bool eq);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(StreqExprNode);
};
struct StrcatExprNode : BinaryExprNode
{
S32 appendChar;
static StrcatExprNode* alloc(S32 lineNumber, ExprNode* left, ExprNode* right, S32 appendChar);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(StrcatExprNode);
};
struct CommaCatExprNode : BinaryExprNode
{
static CommaCatExprNode* alloc(S32 lineNumber, ExprNode* left, ExprNode* right);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(CommaCatExprNode);
};
struct IntUnaryExprNode : ExprNode
{
S32 op;
ExprNode* expr;
bool integer;
static IntUnaryExprNode* alloc(S32 lineNumber, S32 op, ExprNode* expr);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(IntUnaryExprNode);
};
struct FloatUnaryExprNode : ExprNode
{
S32 op;
ExprNode* expr;
static FloatUnaryExprNode* alloc(S32 lineNumber, S32 op, ExprNode* expr);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(FloatUnaryExprNode);
};
struct VarNode : ExprNode
{
StringTableEntry varName;
ExprNode* arrayIndex;
static VarNode* alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
virtual ExprNodeName getExprNodeNameEnum() const { return NameVarNode; }
DBG_STMT_TYPE(VarNode);
};
struct IntNode : ExprNode
{
S32 value;
U32 index; // if it's converted to float/string
static IntNode* alloc(S32 lineNumber, S32 value);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
virtual ExprNodeName getExprNodeNameEnum() const { return NameIntNode; }
DBG_STMT_TYPE(IntNode);
};
struct FloatNode : ExprNode
{
F64 value;
U32 index;
static FloatNode* alloc(S32 lineNumber, F64 value);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
virtual ExprNodeName getExprNodeNameEnum() const { return NameFloatNode; }
DBG_STMT_TYPE(FloatNode);
};
struct StrConstNode : ExprNode
{
char* str;
F64 fVal;
U32 index;
bool tag;
bool doc; // Specifies that this string is a documentation block.
static StrConstNode* alloc(S32 lineNumber, const char* str, bool tag, bool doc = false);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(StrConstNode);
};
struct ConstantNode : ExprNode
{
StringTableEntry value;
F64 fVal;
U32 index;
static ConstantNode* alloc(S32 lineNumber, StringTableEntry value);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(ConstantNode);
};
struct AssignExprNode : ExprNode
{
StringTableEntry varName;
ExprNode* expr;
ExprNode* arrayIndex;
TypeReq subType;
static AssignExprNode* alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex, ExprNode* expr);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(AssignExprNode);
};
struct AssignDecl
{
S32 lineNumber;
S32 token;
ExprNode* expr;
bool integer;
};
struct AssignOpExprNode : ExprNode
{
StringTableEntry varName;
ExprNode* expr;
ExprNode* arrayIndex;
S32 op;
U32 operand;
TypeReq subType;
static AssignOpExprNode* alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex, ExprNode* expr, S32 op);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(AssignOpExprNode);
};
struct TTagSetStmtNode : StmtNode
{
StringTableEntry tag;
ExprNode* valueExpr;
ExprNode* stringExpr;
static TTagSetStmtNode* alloc(S32 lineNumber, StringTableEntry tag, ExprNode* valueExpr, ExprNode* stringExpr);
U32 compileStmt(CodeStream& codeStream, U32 ip);
DBG_STMT_TYPE(TTagSetStmtNode);
};
struct TTagDerefNode : ExprNode
{
ExprNode* expr;
static TTagDerefNode* alloc(S32 lineNumber, ExprNode* expr);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(TTagDerefNode);
};
struct TTagExprNode : ExprNode
{
StringTableEntry tag;
static TTagExprNode* alloc(S32 lineNumber, StringTableEntry tag);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(TTagExprNode);
};
struct FuncCallExprNode : ExprNode
{
StringTableEntry funcName;
StringTableEntry nameSpace;
ExprNode* args;
U32 callType;
enum {
FunctionCall,
StaticCall,
MethodCall,
ParentCall
};
static FuncCallExprNode* alloc(S32 lineNumber, StringTableEntry funcName, StringTableEntry nameSpace, ExprNode* args, bool dot);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(FuncCallExprNode);
};
struct AssertCallExprNode : ExprNode
{
ExprNode* testExpr;
const char* message;
U32 messageIndex;
static AssertCallExprNode* alloc(S32 lineNumber, ExprNode* testExpr, const char* message);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(AssertCallExprNode);
};
struct SlotDecl
{
S32 lineNumber;
ExprNode* object;
StringTableEntry slotName;
ExprNode* array;
};
struct SlotAccessNode : ExprNode
{
ExprNode* objectExpr, * arrayExpr;
StringTableEntry slotName;
static SlotAccessNode* alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* arrayExpr, StringTableEntry slotName);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(SlotAccessNode);
};
struct InternalSlotDecl
{
S32 lineNumber;
ExprNode* object;
ExprNode* slotExpr;
bool recurse;
};
struct InternalSlotAccessNode : ExprNode
{
ExprNode* objectExpr, * slotExpr;
bool recurse;
static InternalSlotAccessNode* alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* slotExpr, bool recurse);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(InternalSlotAccessNode);
};
struct SlotAssignNode : ExprNode
{
ExprNode* objectExpr, * arrayExpr;
StringTableEntry slotName;
ExprNode* valueExpr;
U32 typeID;
static SlotAssignNode* alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* arrayExpr, StringTableEntry slotName, ExprNode* valueExpr, U32 typeID = -1);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(SlotAssignNode);
};
struct SlotAssignOpNode : ExprNode
{
ExprNode* objectExpr, * arrayExpr;
StringTableEntry slotName;
S32 op;
ExprNode* valueExpr;
U32 operand;
TypeReq subType;
static SlotAssignOpNode* alloc(S32 lineNumber, ExprNode* objectExpr, StringTableEntry slotName, ExprNode* arrayExpr, S32 op, ExprNode* valueExpr);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
DBG_STMT_TYPE(SlotAssignOpNode);
};
struct ObjectDeclNode : ExprNode
{
ExprNode* classNameExpr;
StringTableEntry parentObject;
ExprNode* objectNameExpr;
ExprNode* argList;
SlotAssignNode* slotDecls;
ObjectDeclNode* subObjects;
bool isDatablock;
U32 failOffset;
bool isClassNameInternal;
bool isSingleton;
static ObjectDeclNode* alloc(S32 lineNumber, ExprNode* classNameExpr, ExprNode* objectNameExpr, ExprNode* argList, StringTableEntry parentObject, SlotAssignNode* slotDecls, ObjectDeclNode* subObjects, bool isDatablock, bool classNameInternal, bool isSingleton);
U32 precompileSubObject(bool);
U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
U32 compileSubObject(CodeStream& codeStream, U32 ip, bool);
TypeReq getPreferredType();
DBG_STMT_TYPE(ObjectDeclNode);
};
struct ObjectBlockDecl
{
SlotAssignNode* slots;
ObjectDeclNode* decls;
};
struct FunctionDeclStmtNode : StmtNode
{
StringTableEntry fnName;
VarNode* args;
StmtNode* stmts;
StringTableEntry nameSpace;
StringTableEntry package;
U32 endOffset;
U32 argc;
static FunctionDeclStmtNode* alloc(S32 lineNumber, StringTableEntry fnName, StringTableEntry nameSpace, VarNode* args, StmtNode* stmts);
U32 compileStmt(CodeStream& codeStream, U32 ip);
void setPackage(StringTableEntry packageName);
DBG_STMT_TYPE(FunctionDeclStmtNode);
};
namespace Script
{
inline ExprEvalState gEvalState;
inline StmtNode *gStatementList;
inline StmtNode *gAnonFunctionList;
inline U32 gAnonFunctionID = 0;
}
#endif

View file

@ -0,0 +1,456 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2013 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "compiler.h"
#include "console/consoleInternal.h"
using namespace Compiler;
/// @file
///
/// TorqueScript AST node allocators.
///
/// These static methods exist to allocate new AST node for the compiler. They
/// all allocate memory from the consoleAllocator for efficiency, and often take
/// arguments relating to the state of the nodes. They are called from gram.y
/// (really gram.c) as the lexer analyzes the script code.
//------------------------------------------------------------
BreakStmtNode* BreakStmtNode::alloc(S32 lineNumber)
{
BreakStmtNode* ret = (BreakStmtNode*)consoleAlloc(sizeof(BreakStmtNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
return ret;
}
ContinueStmtNode* ContinueStmtNode::alloc(S32 lineNumber)
{
ContinueStmtNode* ret = (ContinueStmtNode*)consoleAlloc(sizeof(ContinueStmtNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
return ret;
}
ReturnStmtNode* ReturnStmtNode::alloc(S32 lineNumber, ExprNode* expr)
{
ReturnStmtNode* ret = (ReturnStmtNode*)consoleAlloc(sizeof(ReturnStmtNode));
constructInPlace(ret);
ret->expr = expr;
ret->dbgLineNumber = lineNumber;
return ret;
}
IfStmtNode* IfStmtNode::alloc(S32 lineNumber, ExprNode* testExpr, StmtNode* ifBlock, StmtNode* elseBlock, bool propagate)
{
IfStmtNode* ret = (IfStmtNode*)consoleAlloc(sizeof(IfStmtNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->testExpr = testExpr;
ret->ifBlock = ifBlock;
ret->elseBlock = elseBlock;
ret->propagate = propagate;
return ret;
}
LoopStmtNode* LoopStmtNode::alloc(S32 lineNumber, ExprNode* initExpr, ExprNode* testExpr, ExprNode* endLoopExpr, StmtNode* loopBlock, bool isDoLoop)
{
LoopStmtNode* ret = (LoopStmtNode*)consoleAlloc(sizeof(LoopStmtNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->testExpr = testExpr;
ret->initExpr = initExpr;
ret->endLoopExpr = endLoopExpr;
ret->loopBlock = loopBlock;
ret->isDoLoop = isDoLoop;
// Deal with setting some dummy constant nodes if we weren't provided with
// info... This allows us to play nice with missing parts of for(;;) for
// instance.
if (!ret->testExpr) ret->testExpr = IntNode::alloc(lineNumber, 1);
return ret;
}
IterStmtNode* IterStmtNode::alloc(S32 lineNumber, StringTableEntry varName, ExprNode* containerExpr, StmtNode* body, bool isStringIter)
{
IterStmtNode* ret = (IterStmtNode*)consoleAlloc(sizeof(IterStmtNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->varName = varName;
ret->containerExpr = containerExpr;
ret->body = body;
ret->isStringIter = isStringIter;
return ret;
}
FloatBinaryExprNode* FloatBinaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode* left, ExprNode* right)
{
FloatBinaryExprNode* ret = (FloatBinaryExprNode*)consoleAlloc(sizeof(FloatBinaryExprNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->op = op;
ret->left = left;
ret->right = right;
return ret;
}
IntBinaryExprNode* IntBinaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode* left, ExprNode* right)
{
IntBinaryExprNode* ret = (IntBinaryExprNode*)consoleAlloc(sizeof(IntBinaryExprNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->op = op;
ret->left = left;
ret->right = right;
return ret;
}
StreqExprNode* StreqExprNode::alloc(S32 lineNumber, ExprNode* left, ExprNode* right, bool eq)
{
StreqExprNode* ret = (StreqExprNode*)consoleAlloc(sizeof(StreqExprNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->left = left;
ret->right = right;
ret->eq = eq;
return ret;
}
StrcatExprNode* StrcatExprNode::alloc(S32 lineNumber, ExprNode* left, ExprNode* right, int appendChar)
{
StrcatExprNode* ret = (StrcatExprNode*)consoleAlloc(sizeof(StrcatExprNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->left = left;
ret->right = right;
ret->appendChar = appendChar;
return ret;
}
CommaCatExprNode* CommaCatExprNode::alloc(S32 lineNumber, ExprNode* left, ExprNode* right)
{
CommaCatExprNode* ret = (CommaCatExprNode*)consoleAlloc(sizeof(CommaCatExprNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->left = left;
ret->right = right;
return ret;
}
IntUnaryExprNode* IntUnaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode* expr)
{
IntUnaryExprNode* ret = (IntUnaryExprNode*)consoleAlloc(sizeof(IntUnaryExprNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->op = op;
ret->expr = expr;
return ret;
}
FloatUnaryExprNode* FloatUnaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode* expr)
{
FloatUnaryExprNode* ret = (FloatUnaryExprNode*)consoleAlloc(sizeof(FloatUnaryExprNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->op = op;
ret->expr = expr;
return ret;
}
VarNode* VarNode::alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex)
{
VarNode* ret = (VarNode*)consoleAlloc(sizeof(VarNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->varName = varName;
ret->arrayIndex = arrayIndex;
return ret;
}
IntNode* IntNode::alloc(S32 lineNumber, S32 value)
{
IntNode* ret = (IntNode*)consoleAlloc(sizeof(IntNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->value = value;
return ret;
}
ConditionalExprNode* ConditionalExprNode::alloc(S32 lineNumber, ExprNode* testExpr, ExprNode* trueExpr, ExprNode* falseExpr)
{
ConditionalExprNode* ret = (ConditionalExprNode*)consoleAlloc(sizeof(ConditionalExprNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->testExpr = testExpr;
ret->trueExpr = trueExpr;
ret->falseExpr = falseExpr;
ret->integer = false;
return ret;
}
FloatNode* FloatNode::alloc(S32 lineNumber, F64 value)
{
FloatNode* ret = (FloatNode*)consoleAlloc(sizeof(FloatNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->value = value;
return ret;
}
StrConstNode* StrConstNode::alloc(S32 lineNumber, const char* str, bool tag, bool doc)
{
StrConstNode* ret = (StrConstNode*)consoleAlloc(sizeof(StrConstNode));
constructInPlace(ret);
S32 len = dStrlen(str);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->str = (char*)consoleAlloc(len + 1);
ret->tag = tag;
ret->doc = doc;
dStrcpy(ret->str, str, len + 1);
ret->str[len] = '\0';
return ret;
}
ConstantNode* ConstantNode::alloc(S32 lineNumber, StringTableEntry value)
{
ConstantNode* ret = (ConstantNode*)consoleAlloc(sizeof(ConstantNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->value = value;
return ret;
}
AssignExprNode* AssignExprNode::alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex, ExprNode* expr)
{
AssignExprNode* ret = (AssignExprNode*)consoleAlloc(sizeof(AssignExprNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->varName = varName;
ret->expr = expr;
ret->arrayIndex = arrayIndex;
return ret;
}
AssignOpExprNode* AssignOpExprNode::alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex, ExprNode* expr, S32 op)
{
AssignOpExprNode* ret = (AssignOpExprNode*)consoleAlloc(sizeof(AssignOpExprNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->varName = varName;
ret->expr = expr;
ret->arrayIndex = arrayIndex;
ret->op = op;
return ret;
}
TTagSetStmtNode* TTagSetStmtNode::alloc(S32 lineNumber, StringTableEntry tag, ExprNode* valueExpr, ExprNode* stringExpr)
{
TTagSetStmtNode* ret = (TTagSetStmtNode*)consoleAlloc(sizeof(TTagSetStmtNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->tag = tag;
ret->valueExpr = valueExpr;
ret->stringExpr = stringExpr;
return ret;
}
TTagDerefNode* TTagDerefNode::alloc(S32 lineNumber, ExprNode* expr)
{
TTagDerefNode* ret = (TTagDerefNode*)consoleAlloc(sizeof(TTagDerefNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->expr = expr;
return ret;
}
TTagExprNode* TTagExprNode::alloc(S32 lineNumber, StringTableEntry tag)
{
TTagExprNode* ret = (TTagExprNode*)consoleAlloc(sizeof(TTagExprNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->tag = tag;
return ret;
}
FuncCallExprNode* FuncCallExprNode::alloc(S32 lineNumber, StringTableEntry funcName, StringTableEntry nameSpace, ExprNode* args, bool dot)
{
FuncCallExprNode* ret = (FuncCallExprNode*)consoleAlloc(sizeof(FuncCallExprNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->funcName = funcName;
ret->nameSpace = nameSpace;
ret->args = args;
if (dot)
ret->callType = MethodCall;
else if (nameSpace != NULL)
{
if (dStricmp(nameSpace, "Parent") == 0)
ret->callType = ParentCall;
else
ret->callType = StaticCall;
}
else
ret->callType = FunctionCall;
return ret;
}
AssertCallExprNode* AssertCallExprNode::alloc(S32 lineNumber, ExprNode* testExpr, const char* message)
{
#ifdef TORQUE_ENABLE_SCRIPTASSERTS
AssertCallExprNode* ret = (AssertCallExprNode*)consoleAlloc(sizeof(FuncCallExprNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->testExpr = testExpr;
ret->message = message ? message : "TorqueScript assert!";
return ret;
#else
return NULL;
#endif
}
SlotAccessNode* SlotAccessNode::alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* arrayExpr, StringTableEntry slotName)
{
SlotAccessNode* ret = (SlotAccessNode*)consoleAlloc(sizeof(SlotAccessNode));
constructInPlace(ret);
ret->optimizedNode = NULL;
ret->dbgLineNumber = lineNumber;
ret->objectExpr = objectExpr;
ret->arrayExpr = arrayExpr;
ret->slotName = slotName;
return ret;
}
InternalSlotAccessNode* InternalSlotAccessNode::alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* slotExpr, bool recurse)
{
InternalSlotAccessNode* ret = (InternalSlotAccessNode*)consoleAlloc(sizeof(InternalSlotAccessNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->objectExpr = objectExpr;
ret->slotExpr = slotExpr;
ret->recurse = recurse;
return ret;
}
SlotAssignNode* SlotAssignNode::alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* arrayExpr, StringTableEntry slotName, ExprNode* valueExpr, U32 typeID /* = -1 */)
{
SlotAssignNode* ret = (SlotAssignNode*)consoleAlloc(sizeof(SlotAssignNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->objectExpr = objectExpr;
ret->arrayExpr = arrayExpr;
ret->slotName = slotName;
ret->valueExpr = valueExpr;
ret->typeID = typeID;
return ret;
}
SlotAssignOpNode* SlotAssignOpNode::alloc(S32 lineNumber, ExprNode* objectExpr, StringTableEntry slotName, ExprNode* arrayExpr, S32 op, ExprNode* valueExpr)
{
SlotAssignOpNode* ret = (SlotAssignOpNode*)consoleAlloc(sizeof(SlotAssignOpNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->objectExpr = objectExpr;
ret->arrayExpr = arrayExpr;
ret->slotName = slotName;
ret->op = op;
ret->valueExpr = valueExpr;
return ret;
}
ObjectDeclNode* ObjectDeclNode::alloc(S32 lineNumber, ExprNode* classNameExpr, ExprNode* objectNameExpr, ExprNode* argList, StringTableEntry parentObject, SlotAssignNode* slotDecls, ObjectDeclNode* subObjects, bool isDatablock, bool classNameInternal, bool isSingleton)
{
ObjectDeclNode* ret = (ObjectDeclNode*)consoleAlloc(sizeof(ObjectDeclNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->optimizedNode = NULL;
ret->classNameExpr = classNameExpr;
ret->objectNameExpr = objectNameExpr;
ret->argList = argList;
ret->slotDecls = slotDecls;
ret->subObjects = subObjects;
ret->isDatablock = isDatablock;
ret->isClassNameInternal = classNameInternal;
ret->isSingleton = isSingleton;
ret->failOffset = 0;
if (parentObject)
ret->parentObject = parentObject;
else
ret->parentObject = StringTable->insert("");
return ret;
}
FunctionDeclStmtNode* FunctionDeclStmtNode::alloc(S32 lineNumber, StringTableEntry fnName, StringTableEntry nameSpace, VarNode* args, StmtNode* stmts)
{
FunctionDeclStmtNode* ret = (FunctionDeclStmtNode*)consoleAlloc(sizeof(FunctionDeclStmtNode));
constructInPlace(ret);
ret->dbgLineNumber = lineNumber;
ret->fnName = fnName;
ret->args = args;
ret->stmts = stmts;
ret->nameSpace = nameSpace;
ret->package = NULL;
return ret;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
echo Changing to %4 ...
cd %4
echo Generating %2 and %3 with prefix %1.
..\..\bin\bison\bison.exe -o %2 %3 --defines -p %1
echo Renaming %2 to %5 .
move /Y %2 %5

View file

@ -0,0 +1,686 @@
/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
#line 3 "bison.simple"
/* Skeleton output parser for bison,
Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* As a special exception, when this file is copied by Bison into a
Bison output file, you may use that output file without restriction.
This special exception was added by the Free Software Foundation
in version 1.24 of Bison. */
#ifndef alloca
#ifdef __GNUC__
#define alloca __builtin_alloca
#else /* not GNU C. */
#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
#include <alloca.h>
#else /* not sparc */
#if defined (MSDOS) && !defined (__TURBOC__)
#include <malloc.h>
#else /* not MSDOS, or __TURBOC__ */
#if defined(_AIX)
#include <malloc.h>
#pragma alloca
#else /* not MSDOS, __TURBOC__, or _AIX */
#ifdef __hpux
#ifdef __cplusplus
extern "C" {
void *alloca (unsigned int);
};
#else /* not __cplusplus */
void *alloca ();
#endif /* not __cplusplus */
#endif /* __hpux */
#endif /* not _AIX */
#endif /* not MSDOS, or __TURBOC__ */
#endif /* not sparc. */
#endif /* not GNU C. */
#endif /* alloca not defined. */
/* This is the parser code that is written into each bison parser
when the %semantic_parser declaration is not specified in the grammar.
It was written by Richard Stallman by simplifying the hairy parser
used when %semantic_parser is specified. */
/* Note: there must be only one dollar sign in this file.
It is replaced by the list of actions, each action
as one case of the switch. */
#define yyerrok (yyerrstatus = 0)
#define yyclearin (yychar = YYEMPTY)
#define YYEMPTY -2
#define YYEOF 0
#define YYACCEPT return(0)
#define YYABORT return(1)
#define YYERROR goto yyerrlab1
/* Like YYERROR except do call yyerror.
This remains here temporarily to ease the
transition to the new meaning of YYERROR, for GCC.
Once GCC version 2 has supplanted version 1, this can go. */
#define YYFAIL goto yyerrlab
#define YYRECOVERING() (!!yyerrstatus)
#define YYBACKUP(token, value) \
do \
if (yychar == YYEMPTY && yylen == 1) \
{ yychar = (token), yylval = (value); \
yychar1 = YYTRANSLATE (yychar); \
YYPOPSTACK; \
goto yybackup; \
} \
else \
{ yyerror ("syntax error: cannot back up"); YYERROR; } \
while (0)
#define YYTERROR 1
#define YYERRCODE 256
#ifndef YYPURE
#define YYLEX yylex()
#endif
#ifdef YYPURE
#ifdef YYLSP_NEEDED
#ifdef YYLEX_PARAM
#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM)
#else
#define YYLEX yylex(&yylval, &yylloc)
#endif
#else /* not YYLSP_NEEDED */
#ifdef YYLEX_PARAM
#define YYLEX yylex(&yylval, YYLEX_PARAM)
#else
#define YYLEX yylex(&yylval)
#endif
#endif /* not YYLSP_NEEDED */
#endif
/* If nonreentrant, generate the variables here */
#ifndef YYPURE
int yychar; /* the lookahead symbol */
YYSTYPE yylval; /* the semantic value of the */
/* lookahead symbol */
#ifdef YYLSP_NEEDED
YYLTYPE yylloc; /* location data for the lookahead */
/* symbol */
#endif
int yynerrs; /* number of parse errors so far */
#endif /* not YYPURE */
#if YYDEBUG != 0
int yydebug; /* nonzero means print parse trace */
/* Since this is uninitialized, it does not stop multiple parsers
from coexisting. */
#endif
/* YYINITDEPTH indicates the initial size of the parser's stacks */
#ifndef YYINITDEPTH
#define YYINITDEPTH 200
#endif
/* YYMAXDEPTH is the maximum size the stacks can grow to
(effective only if the built-in stack extension method is used). */
#if YYMAXDEPTH == 0
#undef YYMAXDEPTH
#endif
#ifndef YYMAXDEPTH
#define YYMAXDEPTH 10000
#endif
/* Prevent warning if -Wstrict-prototypes. */
#ifdef __GNUC__
int yyparse (void);
#endif
#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
#define __yy_memcpy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
#else /* not GNU C or C++ */
#ifndef __cplusplus
/* This is the most reliable way to avoid incompatibilities
in available built-in functions on various systems. */
static void
__yy_memcpy (from, to, count)
char *from;
char *to;
int count;
{
char *f = from;
char *t = to;
int i = count;
while (i-- > 0)
*t++ = *f++;
}
#else /* __cplusplus */
/* This is the most reliable way to avoid incompatibilities
in available built-in functions on various systems. */
static void
__yy_memcpy (char *from, char *to, int count)
{
char *f = from;
char *t = to;
int i = count;
while (i-- > 0)
*t++ = *f++;
}
#endif
#endif
#line 192 "bison.simple"
/* The user can define YYPARSE_PARAM as the name of an argument to be passed
into yyparse. The argument should have type void *.
It should actually point to an object.
Grammar actions can access the variable by casting it
to the proper pointer type. */
#ifdef YYPARSE_PARAM
#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
#else
#define YYPARSE_PARAM
#define YYPARSE_PARAM_DECL
#endif
int
yyparse(YYPARSE_PARAM)
YYPARSE_PARAM_DECL
{
int yystate;
int yyn;
short *yyssp;
YYSTYPE *yyvsp;
int yyerrstatus; /* number of tokens to shift before error messages enabled */
int yychar1 = 0; /* lookahead token as an internal (translated) token number */
short yyssa[YYINITDEPTH]; /* the state stack */
YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
short *yyss = yyssa; /* refer to the stacks thru separate pointers */
YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
#ifdef YYLSP_NEEDED
YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
YYLTYPE *yyls = yylsa;
YYLTYPE *yylsp;
#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
#else
#define YYPOPSTACK (yyvsp--, yyssp--)
#endif
int yystacksize = YYINITDEPTH;
#ifdef YYPURE
int yychar;
YYSTYPE yylval;
int yynerrs;
#ifdef YYLSP_NEEDED
YYLTYPE yylloc;
#endif
#endif
YYSTYPE yyval; /* the variable used to return */
/* semantic values from the action */
/* routines */
int yylen;
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Starting parse\n");
#endif
yystate = 0;
yyerrstatus = 0;
yynerrs = 0;
yychar = YYEMPTY; /* Cause a token to be read. */
/* Initialize stack pointers.
Waste one element of value and location stack
so that they stay on the same level as the state stack.
The wasted elements are never initialized. */
yyssp = yyss - 1;
yyvsp = yyvs;
#ifdef YYLSP_NEEDED
yylsp = yyls;
#endif
/* Push a new state, which is found in yystate . */
/* In all cases, when you get here, the value and location stacks
have just been pushed. so pushing a state here evens the stacks. */
yynewstate:
*++yyssp = yystate;
if (yyssp >= yyss + yystacksize - 1)
{
/* Give user a chance to reallocate the stack */
/* Use copies of these so that the &'s don't force the real ones into memory. */
YYSTYPE *yyvs1 = yyvs;
short *yyss1 = yyss;
#ifdef YYLSP_NEEDED
YYLTYPE *yyls1 = yyls;
#endif
/* Get the current used size of the three stacks, in elements. */
int size = yyssp - yyss + 1;
#ifdef yyoverflow
/* Each stack pointer address is followed by the size of
the data in use in that stack, in bytes. */
#ifdef YYLSP_NEEDED
/* This used to be a conditional around just the two extra args,
but that might be undefined if yyoverflow is a macro. */
yyoverflow("parser stack overflow",
&yyss1, size * sizeof (*yyssp),
&yyvs1, size * sizeof (*yyvsp),
&yyls1, size * sizeof (*yylsp),
&yystacksize);
#else
yyoverflow("parser stack overflow",
&yyss1, size * sizeof (*yyssp),
&yyvs1, size * sizeof (*yyvsp),
&yystacksize);
#endif
yyss = yyss1; yyvs = yyvs1;
#ifdef YYLSP_NEEDED
yyls = yyls1;
#endif
#else /* no yyoverflow */
/* Extend the stack our own way. */
if (yystacksize >= YYMAXDEPTH)
{
yyerror("parser stack overflow");
return 2;
}
yystacksize *= 2;
if (yystacksize > YYMAXDEPTH)
yystacksize = YYMAXDEPTH;
yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
__yy_memcpy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
__yy_memcpy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
#ifdef YYLSP_NEEDED
yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
__yy_memcpy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
#endif
#endif /* no yyoverflow */
yyssp = yyss + size - 1;
yyvsp = yyvs + size - 1;
#ifdef YYLSP_NEEDED
yylsp = yyls + size - 1;
#endif
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Stack size increased to %d\n", yystacksize);
#endif
if (yyssp >= yyss + yystacksize - 1)
YYABORT;
}
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Entering state %d\n", yystate);
#endif
goto yybackup;
yybackup:
/* Do appropriate processing given the current state. */
/* Read a lookahead token if we need one and don't already have one. */
/* yyresume: */
/* First try to decide what to do without reference to lookahead token. */
yyn = yypact[yystate];
if (yyn == YYFLAG)
goto yydefault;
/* Not known => get a lookahead token if don't already have one. */
/* yychar is either YYEMPTY or YYEOF
or a valid token in external form. */
if (yychar == YYEMPTY)
{
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Reading a token: ");
#endif
yychar = YYLEX;
}
/* Convert token to internal form (in yychar1) for indexing tables with */
if (yychar <= 0) /* This means end of input. */
{
yychar1 = 0;
yychar = YYEOF; /* Don't call YYLEX any more */
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Now at end of input.\n");
#endif
}
else
{
yychar1 = YYTRANSLATE(yychar);
#if YYDEBUG != 0
if (yydebug)
{
fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
/* Give the individual parser a way to print the precise meaning
of a token, for further debugging info. */
#ifdef YYPRINT
YYPRINT (stderr, yychar, yylval);
#endif
fprintf (stderr, ")\n");
}
#endif
}
yyn += yychar1;
if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
goto yydefault;
yyn = yytable[yyn];
/* yyn is what to do for this token type in this state.
Negative => reduce, -yyn is rule number.
Positive => shift, yyn is new state.
New state is final state => don't bother to shift,
just return success.
0, or most negative number => error. */
if (yyn < 0)
{
if (yyn == YYFLAG)
goto yyerrlab;
yyn = -yyn;
goto yyreduce;
}
else if (yyn == 0)
goto yyerrlab;
if (yyn == YYFINAL)
YYACCEPT;
/* Shift the lookahead token. */
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
#endif
/* Discard the token being shifted unless it is eof. */
if (yychar != YYEOF)
yychar = YYEMPTY;
*++yyvsp = yylval;
#ifdef YYLSP_NEEDED
*++yylsp = yylloc;
#endif
/* count tokens shifted since error; after three, turn off error status. */
if (yyerrstatus) yyerrstatus--;
yystate = yyn;
goto yynewstate;
/* Do the default action for the current state. */
yydefault:
yyn = yydefact[yystate];
if (yyn == 0)
goto yyerrlab;
/* Do a reduction. yyn is the number of a rule to reduce with. */
yyreduce:
yylen = yyr2[yyn];
if (yylen > 0)
yyval = yyvsp[1-yylen]; /* implement default value of the action */
#if YYDEBUG != 0
if (yydebug)
{
int i;
fprintf (stderr, "Reducing via rule %d (line %d), ",
yyn, yyrline[yyn]);
/* Print the symbols being reduced, and their result. */
for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
fprintf (stderr, "%s ", yytname[yyrhs[i]]);
fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
}
#endif
$ /* the action file gets copied in in place of this dollarsign */
#line 487 "bison.simple"
yyvsp -= yylen;
yyssp -= yylen;
#ifdef YYLSP_NEEDED
yylsp -= yylen;
#endif
#if YYDEBUG != 0
if (yydebug)
{
short *ssp1 = yyss - 1;
fprintf (stderr, "state stack now");
while (ssp1 != yyssp)
fprintf (stderr, " %d", *++ssp1);
fprintf (stderr, "\n");
}
#endif
*++yyvsp = yyval;
#ifdef YYLSP_NEEDED
yylsp++;
if (yylen == 0)
{
yylsp->first_line = yylloc.first_line;
yylsp->first_column = yylloc.first_column;
yylsp->last_line = (yylsp-1)->last_line;
yylsp->last_column = (yylsp-1)->last_column;
yylsp->text = 0;
}
else
{
yylsp->last_line = (yylsp+yylen-1)->last_line;
yylsp->last_column = (yylsp+yylen-1)->last_column;
}
#endif
/* Now "shift" the result of the reduction.
Determine what state that goes to,
based on the state we popped back to
and the rule number reduced by. */
yyn = yyr1[yyn];
yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
yystate = yytable[yystate];
else
yystate = yydefgoto[yyn - YYNTBASE];
goto yynewstate;
yyerrlab: /* here on detecting error */
if (! yyerrstatus)
/* If not already recovering from an error, report this error. */
{
++yynerrs;
#ifdef YYERROR_VERBOSE
yyn = yypact[yystate];
if (yyn > YYFLAG && yyn < YYLAST)
{
int size = 0;
char *msg;
int x, count;
count = 0;
/* Start X at -yyn if nec to avoid negative indexes in yycheck. */
for (x = (yyn < 0 ? -yyn : 0);
x < (sizeof(yytname) / sizeof(char *)); x++)
if (yycheck[x + yyn] == x)
size += strlen(yytname[x]) + 15, count++;
msg = (char *) malloc(size + 15);
if (msg != 0)
{
strcpy(msg, "parse error");
if (count < 5)
{
count = 0;
for (x = (yyn < 0 ? -yyn : 0);
x < (sizeof(yytname) / sizeof(char *)); x++)
if (yycheck[x + yyn] == x)
{
strcat(msg, count == 0 ? ", expecting `" : " or `");
strcat(msg, yytname[x]);
strcat(msg, "'");
count++;
}
}
yyerror(msg);
free(msg);
}
else
yyerror ("parse error; also virtual memory exceeded");
}
else
#endif /* YYERROR_VERBOSE */
yyerror("parse error");
}
goto yyerrlab1;
yyerrlab1: /* here on error raised explicitly by an action */
if (yyerrstatus == 3)
{
/* if just tried and failed to reuse lookahead token after an error, discard it. */
/* return failure if at end of input */
if (yychar == YYEOF)
YYABORT;
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
#endif
yychar = YYEMPTY;
}
/* Else will try to reuse lookahead token
after shifting the error token. */
yyerrstatus = 3; /* Each real token shifted decrements this */
goto yyerrhandle;
yyerrdefault: /* current state does not do anything special for the error token. */
#if 0
/* This is wrong; only states that explicitly want error tokens
should shift them. */
yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
if (yyn) goto yydefault;
#endif
yyerrpop: /* pop the current state because it cannot handle the error token */
if (yyssp == yyss) YYABORT;
yyvsp--;
yystate = *--yyssp;
#ifdef YYLSP_NEEDED
yylsp--;
#endif
#if YYDEBUG != 0
if (yydebug)
{
short *ssp1 = yyss - 1;
fprintf (stderr, "Error: state stack now");
while (ssp1 != yyssp)
fprintf (stderr, " %d", *++ssp1);
fprintf (stderr, "\n");
}
#endif
yyerrhandle:
yyn = yypact[yystate];
if (yyn == YYFLAG)
goto yyerrdefault;
yyn += YYTERROR;
if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
goto yyerrdefault;
yyn = yytable[yyn];
if (yyn < 0)
{
if (yyn == YYFLAG)
goto yyerrpop;
yyn = -yyn;
goto yyreduce;
}
else if (yyn == 0)
goto yyerrpop;
if (yyn == YYFINAL)
YYACCEPT;
#if YYDEBUG != 0
if (yydebug)
fprintf(stderr, "Shifting error token, ");
#endif
*++yyvsp = yylval;
#ifdef YYLSP_NEEDED
*++yylsp = yylloc;
#endif
yystate = yyn;
goto yynewstate;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,97 @@
#ifndef _CMDGRAM_H_
#define _CMDGRAM_H_
typedef union {
Token< char > c;
Token< int > i;
Token< const char* > s;
Token< char* > str;
Token< double > f;
StmtNode* stmt;
ExprNode* expr;
SlotAssignNode* slist;
VarNode* var;
SlotDecl slot;
InternalSlotDecl intslot;
ObjectBlockDecl odcl;
ObjectDeclNode* od;
AssignDecl asn;
IfStmtNode* ifnode;
} YYSTYPE;
#define rwDEFINE 258
#define rwENDDEF 259
#define rwDECLARE 260
#define rwDECLARESINGLETON 261
#define rwBREAK 262
#define rwELSE 263
#define rwCONTINUE 264
#define rwGLOBAL 265
#define rwIF 266
#define rwNIL 267
#define rwRETURN 268
#define rwWHILE 269
#define rwDO 270
#define rwENDIF 271
#define rwENDWHILE 272
#define rwENDFOR 273
#define rwDEFAULT 274
#define rwFOR 275
#define rwFOREACH 276
#define rwFOREACHSTR 277
#define rwIN 278
#define rwDATABLOCK 279
#define rwSWITCH 280
#define rwCASE 281
#define rwSWITCHSTR 282
#define rwCASEOR 283
#define rwPACKAGE 284
#define rwNAMESPACE 285
#define rwCLASS 286
#define rwASSERT 287
#define ILLEGAL_TOKEN 288
#define CHRCONST 289
#define INTCONST 290
#define TTAG 291
#define VAR 292
#define IDENT 293
#define TYPEIDENT 294
#define DOCBLOCK 295
#define STRATOM 296
#define TAGATOM 297
#define FLTCONST 298
#define opINTNAME 299
#define opINTNAMER 300
#define opMINUSMINUS 301
#define opPLUSPLUS 302
#define STMT_SEP 303
#define opSHL 304
#define opSHR 305
#define opPLASN 306
#define opMIASN 307
#define opMLASN 308
#define opDVASN 309
#define opMODASN 310
#define opANDASN 311
#define opXORASN 312
#define opORASN 313
#define opSLASN 314
#define opSRASN 315
#define opCAT 316
#define opEQ 317
#define opNE 318
#define opGE 319
#define opLE 320
#define opAND 321
#define opOR 322
#define opSTREQ 323
#define opCOLONCOLON 324
#define opMDASN 325
#define opNDASN 326
#define opNTASN 327
#define opSTRNE 328
#define UNARY 329
extern YYSTYPE CMDlval;
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,170 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _CODEBLOCK_H_
#define _CODEBLOCK_H_
#include <vector>
#include <unordered_map>
#include "parser.h"
#include "console/runtime.h"
struct CompilerLocalVariableToRegisterMappingTable
{
struct RemappingTable
{
std::vector<StringTableEntry> varList;
};
std::unordered_map<StringTableEntry, RemappingTable> localVarToRegister;
void add(StringTableEntry functionName, StringTableEntry namespaceName, StringTableEntry varName);
S32 lookup(StringTableEntry namespaceName, StringTableEntry functionName, StringTableEntry varName);
CompilerLocalVariableToRegisterMappingTable copy();
void reset();
void write(Stream& stream);
};
#include "compiler.h"
class Stream;
class ConsoleValue;
/// Core TorqueScript code management class.
///
/// This class represents a block of code, usually mapped directly to a file.
class CodeBlock : Con::Module
{
private:
static CodeBlock* smCodeBlockList;
public:
static bool smInFunction;
static TorqueScriptParser * smCurrentParser;
static CodeBlock *getCodeBlockList()
{
return smCodeBlockList;
}
static CodeBlock *find(StringTableEntry);
CodeBlock();
~CodeBlock() override;
StringTableEntry name;
StringTableEntry fullPath;
StringTableEntry modPath;
char *globalStrings;
char *functionStrings;
U32 functionStringsMaxLen;
U32 globalStringsMaxLen;
F64 *globalFloats;
F64 *functionFloats;
U32 codeSize;
U32 *code;
CompilerLocalVariableToRegisterMappingTable variableRegisterTable;
U32 refCount;
U32 lineBreakPairCount;
U32 *lineBreakPairs;
Vector<U32> breakList;
CodeBlock *nextFile;
void addToCodeList();
void removeFromCodeList();
void calcBreakList();
void clearAllBreaks() override;
void setAllBreaks() override;
void dumpInstructions(U32 startIp = 0, bool upToReturn = false);
/// Returns the first breakable line or 0 if none was found.
/// @param lineNumber The one based line number.
U32 findFirstBreakLine(U32 lineNumber) override;
void clearBreakpoint(U32 lineNumber) override;
/// Set a OP_BREAK instruction on a line. If a break
/// is not possible on that line it returns false.
/// @param lineNumber The one based line number.
bool setBreakpoint(U32 lineNumber) override;
void findBreakLine(U32 ip, U32 &line, U32 &instruction) override;
const char *getFileLine(U32 ip) override;
///
String getFunctionArgs(U32 offset);
bool read(StringTableEntry fileName, Stream &st);
bool compile(const char *dsoName, StringTableEntry fileName, const char *script, bool overrideNoDso = false);
/// Compiles and executes a block of script storing the compiled code in this
/// CodeBlock. If there is no filename breakpoints will not be generated and
/// the CodeBlock will not be added to the linked list of loaded CodeBlocks.
/// Note that if the script contains no executable statements the CodeBlock
/// will delete itself on return an empty string. The return string is any
/// result of the code executed, if any, or an empty string.
///
/// @param fileName The file name, including path and extension, for the
/// block of code or an empty string.
/// @param script The script code to compile and execute.
/// @param noCalls Skips calling functions from the script.
/// @param setFrame A zero based index of the stack frame to execute the code
/// with, zero being the top of the stack. If the the index is
/// -1 a new frame is created. If the index is out of range the
/// top stack frame is used.
Con::EvalResult compileExec(StringTableEntry fileName, const char *script,
bool noCalls, S32 setFrame = -1);
/// Executes the existing code in the CodeBlock. The return string is any
/// result of the code executed, if any, or an empty string.
///
/// @param offset The instruction offset to start executing from.
/// @param fnName The name of the function to execute or null.
/// @param ns The namespace of the function to execute or null.
/// @param argc The number of parameters passed to the function or
/// zero to execute code outside of a function.
/// @param argv The function parameter list.
/// @param noCalls Skips calling functions from the script.
/// @param setFrame A zero based index of the stack frame to execute the code
/// with, zero being the top of the stack. If the the index is
/// -1 a new frame is created. If the index is out of range the
/// top stack frame is used.
/// @param packageName The code package name or null.
Con::EvalResult exec(U32 offset, const char* fnName, Namespace* ns, U32 argc,
ConsoleValue* argv, bool noCalls, StringTableEntry packageName,
S32 setFrame = -1) override;
const char* getFunctionArgs(StringTableEntry functionName, U32 functionOffset) override { return getFunctionArgs(functionOffset); }
const char* getPath() override { return fullPath; }
const char* getName() override { return name; }
Vector<U32> getBreakableLines() override { return breakList; }
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,561 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "console/console.h"
#include "compiler.h"
#include "console/simBase.h"
extern FuncVars gEvalFuncVars;
extern FuncVars gGlobalScopeFuncVars;
extern FuncVars *gFuncVars;
namespace Con
{
extern bool scriptWarningsAsAsserts;
};
namespace Compiler
{
F64 consoleStringToNumber(const char *str, StringTableEntry file, U32 line)
{
F64 val = dAtof(str);
if (val != 0)
return val;
else if (!dStricmp(str, "true"))
return 1;
else if (!dStricmp(str, "false"))
return 0;
else if (file)
{
Con::warnf(ConsoleLogEntry::General, "%s (%d): string always evaluates to 0.", file, line);
return 0;
}
return 0;
}
//------------------------------------------------------------
CompilerStringTable *gCurrentStringTable, gGlobalStringTable, gFunctionStringTable;
CompilerFloatTable *gCurrentFloatTable, gGlobalFloatTable, gFunctionFloatTable;
DataChunker gConsoleAllocator;
CompilerIdentTable gIdentTable;
CompilerLocalVariableToRegisterMappingTable gFunctionVariableMappingTable;
//------------------------------------------------------------
void evalSTEtoCode(StringTableEntry ste, U32 ip, U32 *ptr)
{
#if defined(TORQUE_CPU_X64) || defined(TORQUE_CPU_ARM64)
*(U64*)(ptr) = (U64)ste;
#else
*ptr = (U32)ste;
#endif
}
void compileSTEtoCode(StringTableEntry ste, U32 ip, U32 *ptr)
{
if (ste)
getIdentTable().add(ste, ip);
*ptr = 0;
*(ptr + 1) = 0;
}
void(*STEtoCode)(StringTableEntry ste, U32 ip, U32 *ptr) = evalSTEtoCode;
//------------------------------------------------------------
bool gSyntaxError = false;
bool gIsEvalCompile = false;
//------------------------------------------------------------
CompilerStringTable *getCurrentStringTable() { return gCurrentStringTable; }
CompilerStringTable &getGlobalStringTable() { return gGlobalStringTable; }
CompilerStringTable &getFunctionStringTable() { return gFunctionStringTable; }
CompilerLocalVariableToRegisterMappingTable& getFunctionVariableMappingTable() { return gFunctionVariableMappingTable; }
void setCurrentStringTable(CompilerStringTable* cst) { gCurrentStringTable = cst; }
CompilerFloatTable *getCurrentFloatTable() { return gCurrentFloatTable; }
CompilerFloatTable &getGlobalFloatTable() { return gGlobalFloatTable; }
CompilerFloatTable &getFunctionFloatTable() { return gFunctionFloatTable; }
void setCurrentFloatTable(CompilerFloatTable* cst) { gCurrentFloatTable = cst; }
CompilerIdentTable &getIdentTable() { return gIdentTable; }
void precompileIdent(StringTableEntry ident)
{
if (ident)
gGlobalStringTable.add(ident);
}
void resetTables()
{
setCurrentStringTable(&gGlobalStringTable);
setCurrentFloatTable(&gGlobalFloatTable);
getGlobalFloatTable().reset();
getGlobalStringTable().reset();
getFunctionFloatTable().reset();
getFunctionStringTable().reset();
getIdentTable().reset();
getFunctionVariableMappingTable().reset();
gGlobalScopeFuncVars.clear();
gFuncVars = gIsEvalCompile ? &gEvalFuncVars : &gGlobalScopeFuncVars;
}
void *consoleAlloc(U32 size) { return gConsoleAllocator.alloc(size); }
void consoleAllocReset() { gConsoleAllocator.freeBlocks(); }
void scriptErrorHandler(const char* str)
{
if (Con::scriptWarningsAsAsserts)
{
AssertISV(false, str);
}
else
{
Con::warnf(ConsoleLogEntry::Type::Script, "%s", str);
}
}
}
//-------------------------------------------------------------------------
using namespace Compiler;
S32 FuncVars::assign(StringTableEntry var, TypeReq currentType, S32 lineNumber, bool isConstant)
{
std::unordered_map<StringTableEntry, Var>::iterator found = vars.find(var);
if (found != vars.end())
{
// if we are calling assign more than once AND it changes type, we don't know what the variable type is as this is a
// dynamically typed language. So we will assign to None and bail. None will be taken care of by the code to always
// load what the default type is (What Globals and arrays use, type None).
if (currentType != found->second.currentType && found->second.currentType != TypeReqNone)
found->second.currentType = TypeReqNone;
if (found->second.isConstant)
{
const char* str = avar("Script Warning: Reassigning variable %s when it is a constant. File: %s Line : %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber);
scriptErrorHandler(str);
}
return found->second.reg;
}
S32 id = counter++;
vars[var] = { id, currentType, var, isConstant };
variableNameMap[id] = var;
return id;
}
S32 FuncVars::lookup(StringTableEntry var, S32 lineNumber)
{
std::unordered_map<StringTableEntry, Var>::iterator found = vars.find(var);
if (found == vars.end())
{
const char* str = avar("Script Warning: Variable %s referenced before used when compiling script. File: %s Line: %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber);
scriptErrorHandler(str);
return assign(var, TypeReqString, lineNumber, false);
}
return found->second.reg;
}
TypeReq FuncVars::lookupType(StringTableEntry var, S32 lineNumber)
{
std::unordered_map<StringTableEntry, Var>::iterator found = vars.find(var);
if (found == vars.end())
{
const char* str = avar("Script Warning: Variable %s referenced before used when compiling script. File: %s Line: %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber);
scriptErrorHandler(str);
assign(var, TypeReqString, lineNumber, false);
return vars.find(var)->second.currentType;
}
return found->second.currentType;
}
void FuncVars::clear()
{
vars.clear();
variableNameMap.clear();
counter = 0;
}
//-------------------------------------------------------------------------
U32 CompilerStringTable::add(const char *str, bool caseSens, bool tag)
{
// Is it already in?
Entry **walk;
for (walk = &list; *walk; walk = &((*walk)->next))
{
if ((*walk)->tag != tag)
continue;
if (caseSens)
{
if (!String::compare((*walk)->string, str))
return (*walk)->start;
}
else
{
if (!dStricmp((*walk)->string, str))
return (*walk)->start;
}
}
// Write it out.
Entry *newStr = (Entry *)consoleAlloc(sizeof(Entry));
*walk = newStr;
newStr->next = NULL;
newStr->start = totalLen;
U32 len = dStrlen(str) + 1;
if (tag && len < 7) // alloc space for the numeric tag 1 for tag, 5 for # and 1 for nul
len = 7;
totalLen += len;
newStr->string = (char *)consoleAlloc(len);
newStr->len = len;
newStr->tag = tag;
dStrcpy(newStr->string, str, len);
// Put into the hash table.
hashTable[str] = newStr;
return newStr->start;
}
U32 CompilerStringTable::addIntString(U32 value)
{
dSprintf(buf, sizeof(buf), "%d", value);
return add(buf);
}
U32 CompilerStringTable::addFloatString(F64 value)
{
dSprintf(buf, sizeof(buf), "%g", value);
return add(buf);
}
void CompilerStringTable::reset()
{
list = NULL;
totalLen = 0;
}
char *CompilerStringTable::build()
{
char *ret = new char[totalLen];
dMemset(ret, 0, totalLen);
for (Entry *walk = list; walk; walk = walk->next)
dStrcpy(ret + walk->start, walk->string, totalLen - walk->start);
return ret;
}
void CompilerStringTable::write(Stream &st)
{
st.write(totalLen);
for (Entry *walk = list; walk; walk = walk->next)
st.write(walk->len, walk->string);
}
//------------------------------------------------------------
void CompilerLocalVariableToRegisterMappingTable::add(StringTableEntry functionName, StringTableEntry namespaceName, StringTableEntry varName)
{
StringTableEntry funcLookupTableName = StringTable->insert(avar("%s::%s", namespaceName, functionName));
localVarToRegister[funcLookupTableName].varList.push_back(varName);;
}
S32 CompilerLocalVariableToRegisterMappingTable::lookup(StringTableEntry namespaceName, StringTableEntry functionName, StringTableEntry varName)
{
StringTableEntry funcLookupTableName = StringTable->insert(avar("%s::%s", namespaceName, functionName));
auto functionPosition = localVarToRegister.find(funcLookupTableName);
if (functionPosition != localVarToRegister.end())
{
const auto& table = localVarToRegister[funcLookupTableName].varList;
auto varPosition = std::find(table.begin(), table.end(), varName);
if (varPosition != table.end())
{
return std::distance(table.begin(), varPosition);
}
}
Con::errorf("Unable to find local variable %s in function name %s", varName, funcLookupTableName);
return -1;
}
CompilerLocalVariableToRegisterMappingTable CompilerLocalVariableToRegisterMappingTable::copy()
{
// Trivilly copyable as its all plain old data and using STL containers... (We want a deep copy though!)
CompilerLocalVariableToRegisterMappingTable table;
table.localVarToRegister = localVarToRegister;
return table;
}
void CompilerLocalVariableToRegisterMappingTable::reset()
{
localVarToRegister.clear();
}
void CompilerLocalVariableToRegisterMappingTable::write(Stream& stream)
{
stream.write((U32)localVarToRegister.size());
for (const auto& pair : localVarToRegister)
{
StringTableEntry functionName = pair.first;
stream.writeString(functionName);
const auto& localVariableTableForFunction = localVarToRegister[functionName].varList;
stream.write((U32)localVariableTableForFunction.size());
for (const StringTableEntry& varName : localVariableTableForFunction)
{
stream.writeString(varName);
}
}
}
//------------------------------------------------------------
U32 CompilerFloatTable::add(F64 value)
{
Entry **walk;
U32 i = 0;
for (walk = &list; *walk; walk = &((*walk)->next), i++)
if (value == (*walk)->val)
return i;
Entry *newFloat = (Entry *)consoleAlloc(sizeof(Entry));
newFloat->val = value;
newFloat->next = NULL;
count++;
*walk = newFloat;
return count - 1;
}
void CompilerFloatTable::reset()
{
list = NULL;
count = 0;
}
F64 *CompilerFloatTable::build()
{
F64 *ret = new F64[count];
U32 i = 0;
for (Entry *walk = list; walk; walk = walk->next, i++)
ret[i] = walk->val;
return ret;
}
void CompilerFloatTable::write(Stream &st)
{
st.write(count);
for (Entry *walk = list; walk; walk = walk->next)
st.write(walk->val);
}
//------------------------------------------------------------
void CompilerIdentTable::reset()
{
list = NULL;
}
void CompilerIdentTable::add(StringTableEntry ste, U32 ip)
{
U32 index = gGlobalStringTable.add(ste, false);
Entry *newEntry = (Entry *)consoleAlloc(sizeof(Entry));
newEntry->offset = index;
newEntry->ip = ip;
for (Entry *walk = list; walk; walk = walk->next)
{
if (walk->offset == index)
{
newEntry->nextIdent = walk->nextIdent;
walk->nextIdent = newEntry;
return;
}
}
newEntry->next = list;
list = newEntry;
newEntry->nextIdent = NULL;
}
void CompilerIdentTable::write(Stream &st)
{
U32 count = 0;
Entry * walk;
for (walk = list; walk; walk = walk->next)
count++;
st.write(count);
for (walk = list; walk; walk = walk->next)
{
U32 ec = 0;
Entry * el;
for (el = walk; el; el = el->nextIdent)
ec++;
st.write(walk->offset);
st.write(ec);
for (el = walk; el; el = el->nextIdent)
st.write(el->ip);
}
}
//-------------------------------------------------------------------------
U8 *CodeStream::allocCode(U32 sz)
{
U8 *ptr = NULL;
if (mCodeHead)
{
const U32 bytesLeft = BlockSize - mCodeHead->size;
if (bytesLeft > sz)
{
ptr = mCodeHead->data + mCodeHead->size;
mCodeHead->size += sz;
return ptr;
}
}
CodeData *data = new CodeData;
data->data = (U8*)dMalloc(BlockSize);
data->size = sz;
data->next = NULL;
if (mCodeHead)
mCodeHead->next = data;
mCodeHead = data;
if (mCode == NULL)
mCode = data;
return data->data;
}
//-------------------------------------------------------------------------
void CodeStream::fixLoop(U32 loopBlockStart, U32 breakPoint, U32 continuePoint)
{
AssertFatal(mFixStack.size() > 0, "Fix stack mismatch");
U32 fixStart = mFixStack[mFixStack.size() - 1];
for (U32 i = fixStart; i<mFixList.size(); i += 2)
{
FixType type = (FixType)mFixList[i + 1];
U32 fixedIp = 0;
bool valid = true;
switch (type)
{
case FIXTYPE_LOOPBLOCKSTART:
fixedIp = loopBlockStart;
break;
case FIXTYPE_BREAK:
fixedIp = breakPoint;
break;
case FIXTYPE_CONTINUE:
fixedIp = continuePoint;
break;
default:
//Con::warnf("Address %u fixed as %u", mFixList[i], mFixList[i+1]);
valid = false;
break;
}
if (valid)
{
patch(mFixList[i], fixedIp);
}
}
}
//-------------------------------------------------------------------------
void CodeStream::emitCodeStream(U32 *size, U32 **stream, U32 **lineBreaks)
{
// Alloc stream
U32 numLineBreaks = getNumLineBreaks();
*stream = new U32[mCodePos + (numLineBreaks * 2)];
dMemset(*stream, '\0', mCodePos + (numLineBreaks * 2));
*size = mCodePos;
// Dump chunks & line breaks
U32 outBytes = mCodePos * sizeof(U32);
U8 *outPtr = *((U8**)stream);
for (CodeData *itr = mCode; itr != NULL; itr = itr->next)
{
U32 bytesToCopy = itr->size > outBytes ? outBytes : itr->size;
dMemcpy(outPtr, itr->data, bytesToCopy);
outPtr += bytesToCopy;
outBytes -= bytesToCopy;
}
*lineBreaks = *stream + mCodePos;
dMemcpy(*lineBreaks, mBreakLines.address(), sizeof(U32) * mBreakLines.size());
// Apply patches on top
for (U32 i = 0; i<mPatchList.size(); i++)
{
PatchEntry &e = mPatchList[i];
(*stream)[e.addr] = e.value;
}
}
//-------------------------------------------------------------------------
void CodeStream::reset()
{
mCodePos = 0;
mFixStack.clear();
mFixLoopStack.clear();
mFixList.clear();
mBreakLines.clear();
// Pop down to one code block
CodeData *itr = mCode ? mCode->next : NULL;
while (itr != NULL)
{
CodeData *next = itr->next;
dFree(itr->data);
delete(itr);
itr = next;
}
if (mCode)
{
mCode->size = 0;
mCode->next = NULL;
mCodeHead = mCode;
}
}

View file

@ -0,0 +1,480 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _COMPILER_H_
#define _COMPILER_H_
//#define DEBUG_CODESTREAM
#ifdef DEBUG_CODESTREAM
#include <stdio.h>
#endif
#include <string>
#include <unordered_map>
class CodeStream;
struct StmtNode;
class Stream;
class DataChunker;
#include "platform/platform.h"
#include "ast.h"
#include "codeBlock.h"
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
//------------------------------------------------------------
namespace Compiler
{
/// The opcodes for the TorqueScript VM.
enum CompiledInstructions
{
OP_FUNC_DECL,
OP_CREATE_OBJECT,
OP_ADD_OBJECT,
OP_END_OBJECT,
// Added to fix the stack issue [7/9/2007 Black]
OP_FINISH_OBJECT,
OP_JMPIFFNOT,
OP_JMPIFNOT,
OP_JMPIFF,
OP_JMPIF,
OP_JMPIFNOT_NP,
OP_JMPIF_NP, // 10
OP_JMP,
OP_RETURN,
// fixes a bug when not explicitly returning a value
OP_RETURN_VOID,
OP_RETURN_FLT,
OP_RETURN_UINT,
OP_CMPEQ,
OP_CMPGR,
OP_CMPGE,
OP_CMPLT,
OP_CMPLE,
OP_CMPNE,
OP_XOR, // 20
OP_MOD,
OP_BITAND,
OP_BITOR,
OP_NOT,
OP_NOTF,
OP_ONESCOMPLEMENT,
OP_SHR,
OP_SHL,
OP_AND,
OP_OR, // 30
OP_ADD,
OP_SUB,
OP_MUL,
OP_DIV,
OP_NEG,
OP_INC,
OP_SETCURVAR,
OP_SETCURVAR_CREATE,
OP_SETCURVAR_ARRAY,
OP_SETCURVAR_ARRAY_CREATE,
OP_LOADVAR_UINT,// 40
OP_LOADVAR_FLT,
OP_LOADVAR_STR,
OP_SAVEVAR_UINT,
OP_SAVEVAR_FLT,
OP_SAVEVAR_STR,
OP_LOAD_LOCAL_VAR_UINT,
OP_LOAD_LOCAL_VAR_FLT,
OP_LOAD_LOCAL_VAR_STR,
OP_SAVE_LOCAL_VAR_UINT,
OP_SAVE_LOCAL_VAR_FLT,
OP_SAVE_LOCAL_VAR_STR,
OP_SETCUROBJECT,
OP_SETCUROBJECT_NEW,
OP_SETCUROBJECT_INTERNAL,
OP_SETCURFIELD,
OP_SETCURFIELD_ARRAY, // 50
OP_SETCURFIELD_TYPE,
OP_LOADFIELD_UINT,
OP_LOADFIELD_FLT,
OP_LOADFIELD_STR,
OP_SAVEFIELD_UINT,
OP_SAVEFIELD_FLT,
OP_SAVEFIELD_STR,
OP_POP_STK,
OP_LOADIMMED_UINT,
OP_LOADIMMED_FLT,
OP_TAG_TO_STR,
OP_LOADIMMED_STR, // 70
OP_DOCBLOCK_STR, // 76
OP_LOADIMMED_IDENT,
OP_CALLFUNC,
OP_ADVANCE_STR_APPENDCHAR,
OP_REWIND_STR,
OP_TERMINATE_REWIND_STR,
OP_COMPARE_STR,
OP_PUSH,
OP_PUSH_FRAME,
OP_ASSERT,
OP_BREAK,
OP_ITER_BEGIN, ///< Prepare foreach iterator.
OP_ITER_BEGIN_STR, ///< Prepare foreach$ iterator.
OP_ITER, ///< Enter foreach loop.
OP_ITER_END, ///< End foreach loop.
OP_INVALID, // 90
MAX_OP_CODELEN ///< The amount of op codes.
};
//------------------------------------------------------------
F64 consoleStringToNumber(const char *str, StringTableEntry file = 0, U32 line = 0);
U32 compileBlock(StmtNode *block, CodeStream &codeStream, U32 ip);
//------------------------------------------------------------
struct CompilerIdentTable
{
struct Entry
{
U32 offset;
U32 ip;
Entry *next;
Entry *nextIdent;
};
Entry *list;
void add(StringTableEntry ste, U32 ip);
void reset();
void write(Stream &st);
};
//------------------------------------------------------------
struct CompilerStringTable
{
U32 totalLen;
struct Entry
{
char *string;
U32 start;
U32 len;
bool tag;
Entry *next;
};
Entry *list;
char buf[256];
std::unordered_map<std::string, Entry*> hashTable;
U32 add(const char *str, bool caseSens = true, bool tag = false);
U32 addIntString(U32 value);
U32 addFloatString(F64 value);
void reset();
char *build();
void write(Stream &st);
};
//------------------------------------------------------------
struct CompilerFloatTable
{
struct Entry
{
F64 val;
Entry *next;
};
U32 count;
Entry *list;
U32 add(F64 value);
void reset();
F64 *build();
void write(Stream &st);
};
//------------------------------------------------------------
inline StringTableEntry CodeToSTE(U32 *code, U32 ip)
{
#if defined(TORQUE_CPU_X64) || defined(TORQUE_CPU_ARM64)
return (StringTableEntry)(*((U64*)(code + ip)));
#else
return (StringTableEntry)(*(code + ip));
#endif
}
extern void(*STEtoCode)(StringTableEntry ste, U32 ip, U32 *ptr);
void evalSTEtoCode(StringTableEntry ste, U32 ip, U32 *ptr);
void compileSTEtoCode(StringTableEntry ste, U32 ip, U32 *ptr);
CompilerStringTable *getCurrentStringTable();
CompilerStringTable &getGlobalStringTable();
CompilerStringTable &getFunctionStringTable();
CompilerLocalVariableToRegisterMappingTable& getFunctionVariableMappingTable();
void setCurrentStringTable(CompilerStringTable* cst);
CompilerFloatTable *getCurrentFloatTable();
CompilerFloatTable &getGlobalFloatTable();
CompilerFloatTable &getFunctionFloatTable();
void setCurrentFloatTable(CompilerFloatTable* cst);
CompilerIdentTable &getIdentTable();
void precompileIdent(StringTableEntry ident);
/// Helper function to reset the float, string, and ident tables to a base
/// starting state.
void resetTables();
void *consoleAlloc(U32 size);
void consoleAllocReset();
void scriptErrorHandler(const char* str);
extern bool gSyntaxError;
extern bool gIsEvalCompile;
};
class FuncVars
{
struct Var
{
S32 reg;
TypeReq currentType;
StringTableEntry name;
bool isConstant;
};
public:
S32 assign(StringTableEntry var, TypeReq currentType, S32 lineNumber, bool isConstant = false);
S32 lookup(StringTableEntry var, S32 lineNumber);
TypeReq lookupType(StringTableEntry var, S32 lineNumber);
inline S32 count() { return counter; }
std::unordered_map<S32, StringTableEntry> variableNameMap;
void clear();
private:
std::unordered_map<StringTableEntry, Var> vars;
S32 counter = 0;
};
/// Utility class to emit and patch bytecode
class CodeStream
{
public:
enum FixType
{
// For loops
FIXTYPE_LOOPBLOCKSTART,
FIXTYPE_BREAK,
FIXTYPE_CONTINUE
};
enum Constants
{
BlockSize = 16384,
};
protected:
typedef struct PatchEntry
{
U32 addr; ///< Address to patch
U32 value; ///< Value to place at addr
PatchEntry(): addr(0), value(0) { ; }
PatchEntry(U32 a, U32 v) : addr(a), value(v) { ; }
} PatchEntry;
typedef struct CodeData
{
U8 *data; ///< Allocated data (size is BlockSize)
U32 size; ///< Bytes used in data
CodeData *next; ///< Next block
} CodeData;
/// @name Emitted code
/// {
CodeData *mCode;
CodeData *mCodeHead;
U32 mCodePos;
/// }
/// @name Code fixing stacks
/// {
Vector<U32> mFixList;
Vector<U32> mFixStack;
Vector<bool> mFixLoopStack;
Vector<PatchEntry> mPatchList;
/// }
Vector<U32> mBreakLines; ///< Line numbers
public:
CodeStream() : mCode(0), mCodeHead(NULL), mCodePos(0)
{
}
~CodeStream()
{
reset();
if (mCode)
{
dFree(mCode->data);
delete mCode;
}
}
U8 *allocCode(U32 sz);
inline U32 emit(U32 code)
{
U32 *ptr = (U32*)allocCode(4);
*ptr = code;
#ifdef DEBUG_CODESTREAM
printf("code[%u] = %u\n", mCodePos, code);
#endif
return mCodePos++;
}
inline void patch(U32 addr, U32 code)
{
#ifdef DEBUG_CODESTREAM
printf("patch[%u] = %u\n", addr, code);
#endif
mPatchList.push_back(PatchEntry(addr, code));
}
inline U32 emitSTE(const char *code)
{
U64 *ptr = (U64*)allocCode(8);
*ptr = 0;
Compiler::STEtoCode(code, mCodePos, (U32*)ptr);
#ifdef DEBUG_CODESTREAM
printf("code[%u] = %s\n", mCodePos, code);
#endif
mCodePos += 2;
return mCodePos - 2;
}
inline U32 tell()
{
return mCodePos;
}
inline bool inLoop()
{
for (U32 i = 0; i<mFixLoopStack.size(); i++)
{
if (mFixLoopStack[i])
return true;
}
return false;
}
inline U32 emitFix(FixType type)
{
U32 *ptr = (U32*)allocCode(4);
*ptr = (U32)type;
#ifdef DEBUG_CODESTREAM
printf("code[%u] = [FIX:%u]\n", mCodePos, (U32)type);
#endif
mFixList.push_back(mCodePos);
mFixList.push_back((U32)type);
return mCodePos++;
}
inline void pushFixScope(bool isLoop)
{
mFixStack.push_back(mFixList.size());
mFixLoopStack.push_back(isLoop);
}
inline void popFixScope()
{
AssertFatal(mFixStack.size() > 0, "Fix stack mismatch");
U32 newSize = mFixStack[mFixStack.size() - 1];
while (mFixList.size() > newSize)
mFixList.pop_back();
mFixStack.pop_back();
mFixLoopStack.pop_back();
}
void fixLoop(U32 loopBlockStart, U32 breakPoint, U32 continuePoint);
inline void addBreakLine(U32 lineNumber, U32 ip)
{
mBreakLines.push_back(lineNumber);
mBreakLines.push_back(ip);
}
inline U32 getNumLineBreaks()
{
return mBreakLines.size() / 2;
}
void emitCodeStream(U32 *size, U32 **stream, U32 **lineBreaks);
void reset();
};
#endif

View file

@ -0,0 +1,157 @@

#include "evalState.h"
void ExprEvalState::pushFrame(StringTableEntry frameName, Namespace *ns, S32 registerCount)
{
#ifdef DEBUG_SPEW
validate();
Platform::outputDebugString("[ConsoleInternal] Pushing new frame for '%s' at %i",
frameName, mStackDepth);
#endif
if (mStackDepth + 1 > stack.size())
{
#ifdef DEBUG_SPEW
Platform::outputDebugString("[ConsoleInternal] Growing stack by one frame");
#endif
stack.push_back(new Dictionary);
}
Dictionary& newFrame = *(stack[mStackDepth]);
newFrame.setState();
newFrame.scopeName = frameName;
newFrame.scopeNamespace = ns;
Con::pushStackFrame(stack[mStackDepth]);
mStackDepth++;
currentVariable = NULL;
AssertFatal(!newFrame.getCount(), "ExprEvalState::pushFrame - Dictionary not empty!");
ConsoleValue* consoleValArray = new ConsoleValue[registerCount]();
localStack.push_back(ConsoleValueFrame(consoleValArray, false));
currentRegisterArray = &localStack.last();
AssertFatal(mStackDepth == localStack.size(), avar("Stack sizes do not match. mStackDepth = %d, localStack = %d", mStackDepth, localStack.size()));
#ifdef DEBUG_SPEW
validate();
#endif
}
void ExprEvalState::popFrame()
{
AssertFatal(mStackDepth > 0, "ExprEvalState::popFrame - Stack Underflow!");
#ifdef DEBUG_SPEW
validate();
Platform::outputDebugString("[ConsoleInternal] Popping %sframe at %i",
getCurrentFrame().isOwner() ? "" : "shared ", mStackDepth - 1);
#endif
Con::popStackFrame();
mStackDepth--;
stack[mStackDepth]->reset();
currentVariable = NULL;
const ConsoleValueFrame& frame = localStack.last();
localStack.pop_back();
if (!frame.isReference)
delete[] frame.values;
currentRegisterArray = localStack.size() ? &localStack.last() : NULL;
AssertFatal(mStackDepth == localStack.size(), avar("Stack sizes do not match. mStackDepth = %d, localStack = %d", mStackDepth, localStack.size()));
#ifdef DEBUG_SPEW
validate();
#endif
}
void ExprEvalState::pushFrameRef(S32 stackIndex)
{
AssertFatal(stackIndex >= 0 && stackIndex < mStackDepth, "You must be asking for a valid frame!");
#ifdef DEBUG_SPEW
validate();
Platform::outputDebugString("[ConsoleInternal] Cloning frame from %i to %i",
stackIndex, mStackDepth);
#endif
if (mStackDepth + 1 > stack.size())
{
#ifdef DEBUG_SPEW
Platform::outputDebugString("[ConsoleInternal] Growing stack by one frame");
#endif
stack.push_back(new Dictionary);
}
Dictionary& newFrame = *(stack[mStackDepth]);
newFrame.setState(stack[stackIndex]);
Con::pushStackFrame(stack[mStackDepth]);
mStackDepth++;
currentVariable = NULL;
ConsoleValue* values = localStack[stackIndex].values;
localStack.push_back(ConsoleValueFrame(values, true));
currentRegisterArray = &localStack.last();
AssertFatal(mStackDepth == localStack.size(), avar("Stack sizes do not match. mStackDepth = %d, localStack = %d", mStackDepth, localStack.size()));
#ifdef DEBUG_SPEW
validate();
#endif
}
void ExprEvalState::pushDebugFrame(S32 stackIndex)
{
pushFrameRef(stackIndex);
Dictionary& newFrame = *(stack[mStackDepth - 1]);
// debugger needs to know this info...
newFrame.scopeName = stack[stackIndex]->scopeName;
newFrame.scopeNamespace = stack[stackIndex]->scopeNamespace;
newFrame.module = stack[stackIndex]->module;
newFrame.ip = stack[stackIndex]->ip;
}
ExprEvalState::ExprEvalState()
{
VECTOR_SET_ASSOCIATION(stack);
currentVariable = NULL;
mStackDepth = 0;
stack.reserve(64);
mShouldReset = false;
mResetLocked = false;
copyVariable = NULL;
currentRegisterArray = NULL;
}
ExprEvalState::~ExprEvalState()
{
// Delete callframes.
while (!stack.empty())
{
delete stack.last();
stack.decrement();
}
}
void ExprEvalState::validate()
{
AssertFatal(mStackDepth <= stack.size(),
"ExprEvalState::validate() - Stack depth pointing beyond last stack frame!");
for (U32 i = 0; i < stack.size(); ++i)
stack[i]->validate();
}

View file

@ -0,0 +1,119 @@
#ifndef _EVALSTATE_H
#define _EVALSTATE_H
#include "console/consoleInternal.h"
class ExprEvalState
{
public:
/// @name Expression Evaluation
/// @{
///
Dictionary::Entry *currentVariable;
Dictionary::Entry *copyVariable;
U32 mStackDepth;
bool mShouldReset; ///< Designates if the value stack should be reset
bool mResetLocked; ///< mShouldReset will be set at the end
ExprEvalState();
~ExprEvalState();
/// @}
/// @name Stack Management
/// @{
/// The stack of callframes. The extra redirection is necessary since Dictionary holds
/// an interior pointer that will become invalid when the object changes address.
Vector< Dictionary* > stack;
S32 getTopOfStack() { return (S32)mStackDepth; }
Vector< ConsoleValueFrame > localStack;
ConsoleValueFrame* currentRegisterArray; // contains array at to top of localStack
///
void setCurVarName(StringTableEntry name);
void setCurVarNameCreate(StringTableEntry name);
S32 getIntVariable();
F64 getFloatVariable();
const char *getStringVariable();
void setIntVariable(S32 val);
void setFloatVariable(F64 val);
void setStringVariable(const char *str);
TORQUE_FORCEINLINE S32 getLocalIntVariable(S32 reg)
{
return currentRegisterArray->values[reg].getInt();
}
TORQUE_FORCEINLINE F64 getLocalFloatVariable(S32 reg)
{
return currentRegisterArray->values[reg].getFloat();
}
TORQUE_FORCEINLINE const char* getLocalStringVariable(S32 reg)
{
return currentRegisterArray->values[reg].getString();
}
TORQUE_FORCEINLINE void setLocalIntVariable(S32 reg, S64 val)
{
currentRegisterArray->values[reg].setInt(val);
}
TORQUE_FORCEINLINE void setLocalFloatVariable(S32 reg, F64 val)
{
currentRegisterArray->values[reg].setFloat(val);
}
TORQUE_FORCEINLINE void setLocalStringVariable(S32 reg, const char* val, S32 len)
{
currentRegisterArray->values[reg].setString(val, len);
}
TORQUE_FORCEINLINE void setLocalStringTableEntryVariable(S32 reg, StringTableEntry val)
{
currentRegisterArray->values[reg].setStringTableEntry(val);
}
TORQUE_FORCEINLINE void moveConsoleValue(S32 reg, ConsoleValue val)
{
currentRegisterArray->values[reg] = std::move(val);
}
void pushFrame(StringTableEntry frameName, Namespace *ns, S32 regCount);
void popFrame();
/// Puts a reference to an existing stack frame
/// on the top of the stack.
void pushFrameRef(S32 stackIndex);
void pushDebugFrame(S32 stackIndex);
U32 getStackDepth() const
{
return mStackDepth;
}
Dictionary& getCurrentFrame()
{
return *(stack[mStackDepth - 1]);
}
Dictionary& getFrameAt(S32 depth)
{
return *(stack[depth]);
}
/// @}
/// Run integrity checks for debugging.
void validate();
};
#endif

View file

@ -0,0 +1,103 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2013 GarageGames, LLC
// Copyright (c) 2021 TGEMIT Authors & Contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "codeBlock.h"
static bool isLiteralNumber(ExprNode* node)
{
ExprNodeName name = node->getExprNodeNameEnum();
return name == NameFloatNode || name == NameIntNode;
}
static F64 getFloatValue(ExprNode* node)
{
if (node->getExprNodeNameEnum() == NameFloatNode)
return static_cast<FloatNode*>(node)->value;
return (F64)static_cast<IntNode*>(node)->value;
}
static S32 getIntValue(ExprNode* node)
{
if (node->getExprNodeNameEnum() == NameFloatNode)
return (S32)static_cast<FloatNode*>(node)->value;
return static_cast<IntNode*>(node)->value;
}
bool FloatBinaryExprNode::optimize()
{
// Perform constant folding
if (isLiteralNumber(right) && isLiteralNumber(left))
{
F64 rightValue = getFloatValue(right);
F64 leftValue = getFloatValue(left);
F64 result = 0.0;
switch (op)
{
case '+':
result = leftValue + rightValue;
break;
case '-':
result = leftValue - rightValue;
break;
case '*':
result = leftValue * rightValue;
break;
case '/':
if (rightValue != 0.0)
result = leftValue / rightValue;
break;
}
optimizedNode = FloatNode::alloc(dbgLineNumber, result);
return true;
}
return false;
}
bool IntBinaryExprNode::optimize()
{
if (op == '%' && left->getExprNodeNameEnum() == NameVarNode && isLiteralNumber(right))
{
// %a % intconst
S32 val = getIntValue(right);
switch (val)
{
case 2:
op = '&';
optimizedNode = IntNode::alloc(dbgLineNumber, 1);
return true;
case 4:
op = '&';
optimizedNode = IntNode::alloc(dbgLineNumber, 3);
return true;
case 8:
op = '&';
optimizedNode = IntNode::alloc(dbgLineNumber, 7);
return true;
}
}
return false;
}

View file

@ -0,0 +1,49 @@
#ifndef _TORQUESCRIPT_PARSER_H_
#define _TORQUESCRIPT_PARSER_H_
#include <cstdio>
#include "platform/types.h"
const char* CMDGetCurrentFile();
S32 CMDGetCurrentLine();
S32 CMDparse();
void CMDrestart(FILE* in);
void CMDSetScanBuffer(const char *sb, const char *fn);
extern void expandEscape(char *dest, const char *src);
extern bool collapseEscape(char *buf);
class TorqueScriptParser
{
public:
TorqueScriptParser() = default;
const char* mExtension;
//-----------------------------------------------------------------------------
/// \brief Function for GetCurrentFile from the lexer
//-----------------------------------------------------------------------------
const char* getCurrentFile() { return CMDGetCurrentFile(); }
//-----------------------------------------------------------------------------
/// \brief Function for GetCurrentLine from the lexer
//-----------------------------------------------------------------------------
S32 getCurrentLine() { return CMDGetCurrentLine(); }
//-----------------------------------------------------------------------------
/// \brief Function for Parse from the lexer
//-----------------------------------------------------------------------------
S32 parse() { return CMDparse(); }
//-----------------------------------------------------------------------------
/// \brief Function for Restart from the lexer
//-----------------------------------------------------------------------------
void restart(FILE *pInputFile) { CMDrestart(pInputFile); }
//-----------------------------------------------------------------------------
/// \brief Function for SetScanBuffer from the lexer
//-----------------------------------------------------------------------------
void setScanBuffer(const char* sb, const char* fn) { CMDSetScanBuffer(sb, fn); }
};
#endif

View file

@ -0,0 +1,486 @@
#include "runtime.h"
#include "codeBlock.h"
#include "console/script.h"
#include "console/runtime.h"
#include "core/volume.h"
#include "core/stream/fileStream.h"
#include "core/util/timeClass.h"
namespace TorqueScript
{
// Buffer for expanding script filenames.
static char scriptFilenameBuffer[1024];
TorqueScriptRuntime::TorqueScriptRuntime()
{
Con::registerRuntime(0, this);
}
TorqueScriptRuntime::~TorqueScriptRuntime()
{
}
Con::EvalResult TorqueScriptRuntime::evaluate(const char* string, bool echo, const char* fileName)
{
ConsoleStackFrameSaver stackSaver;
stackSaver.save();
if (echo)
{
if (string[0] == '%')
Con::printf("%s", string);
else
Con::printf("%s%s", Con::getVariable("$Con::Prompt"), string);
}
if (fileName)
fileName = StringTable->insert(fileName);
CodeBlock* newCodeBlock = new CodeBlock();
return std::move(newCodeBlock->compileExec(fileName, string, false, fileName ? -1 : 0));
}
Con::EvalResult TorqueScriptRuntime::evaluate(const char* script, S32 frame, bool echo, const char* fileName)
{
// Make sure we're passing a valid frame to the eval.
if (frame > Script::gEvalState.getStackDepth())
frame = Script::gEvalState.getStackDepth() - 1;
if (frame < 0)
frame = 0;
// Local variables use their own memory management and can't be queried by just executing
// TorqueScript, we have to go digging into the interpreter.
S32 evalBufferLen = dStrlen(script);
bool isEvaluatingLocalVariable = evalBufferLen > 0 && script[0] == '%';
if (isEvaluatingLocalVariable)
{
// See calculation of current frame in pushing a reference frame for console exec, we need access
// to the proper scope.
//frame = gEvalState.getTopOfStack() - frame - 1;
S32 stackIndex = Script::gEvalState.getStackDepth() - frame - 1;
Script::gEvalState.pushDebugFrame(stackIndex);
Dictionary& stackFrame = Script::gEvalState.getCurrentFrame();
StringTableEntry functionName = stackFrame.scopeName;
StringTableEntry namespaceName = stackFrame.scopeNamespace->mName;
StringTableEntry varToLookup = StringTable->insert(script);
S32 registerId = ((CodeBlock*)stackFrame.module)->variableRegisterTable.lookup(namespaceName, functionName, varToLookup);
if (registerId == -1)
{
// ERROR, can't read the variable!
return Con::EvalResult("variable not found");
}
const char* varResult = Script::gEvalState.getLocalStringVariable(registerId);
Script::gEvalState.popFrame();
ConsoleValue val;
val.setString(varResult);
return Con::EvalResult("variable not found");
}
// Execute the eval.
CodeBlock* newCodeBlock = new CodeBlock();
Con::EvalResult result = newCodeBlock->compileExec(NULL, script, false, frame);
return result;
}
//------------------------------------------------------------------------------
Con::EvalResult TorqueScriptRuntime::evaluatef(const char* string, ...)
{
char buffer[4096];
va_list args;
va_start(args, string);
dVsprintf(buffer, sizeof(buffer), string, args);
va_end(args);
return evaluate(buffer);
}
bool TorqueScriptRuntime::executeFile(const char* fileName, bool noCalls, bool journalScript)
{
bool journal = false;
U32 execDepth = 0;
U32 journalDepth = 1;
execDepth++;
if (journalDepth >= execDepth)
journalDepth = execDepth + 1;
else
journal = true;
bool ret = false;
if (journalScript && !journal)
{
journal = true;
journalDepth = execDepth;
}
// Determine the filename we actually want...
Con::expandScriptFilename(scriptFilenameBuffer, sizeof(scriptFilenameBuffer), fileName);
// since this function expects a script file reference, if it's a .dso
// lets terminate the string before the dso so it will act like a .tscript
if (dStrEndsWith(scriptFilenameBuffer, ".dso"))
{
scriptFilenameBuffer[dStrlen(scriptFilenameBuffer) - dStrlen(".dso")] = '\0';
}
// Figure out where to put DSOs
StringTableEntry dsoPath = Con::getDSOPath(scriptFilenameBuffer);
const char* ext = dStrrchr(scriptFilenameBuffer, '.');
if (!ext)
{
// Try appending the default script extension and see if that succeeds
if (executeFile(fileName + String("." TORQUE_SCRIPT_EXTENSION), noCalls, journalScript))
{
return true;
}
// We need an extension!
Con::errorf(ConsoleLogEntry::Script, "exec: invalid script file name %s.", scriptFilenameBuffer);
execDepth--;
return false;
}
// Check Editor Extensions
bool isEditorScript = false;
// If the script file extension is '.ed.tscript' then compile it to a different compiled extension
if (dStricmp(ext, "." TORQUE_SCRIPT_EXTENSION) == 0)
{
const char* ext2 = ext - 3;
if (dStricmp(ext2, ".ed." TORQUE_SCRIPT_EXTENSION) == 0)
isEditorScript = true;
}
else if (dStricmp(ext, ".gui") == 0)
{
const char* ext2 = ext - 3;
if (dStricmp(ext2, ".ed.gui") == 0)
isEditorScript = true;
}
StringTableEntry scriptFileName = StringTable->insert(scriptFilenameBuffer);
// Is this a file we should compile? (anything in the prefs path should not be compiled)
StringTableEntry prefsPath = Platform::getPrefsPath();
bool compiled = dStricmp(ext, ".mis") && !journal && !Con::getBoolVariable("Scripts::ignoreDSOs");
// [tom, 12/5/2006] stripBasePath() fucks up if the filename is not in the exe
// path, current directory or prefs path. Thus, getDSOFilename() will also screw
// up and so this allows the scripts to still load but without a DSO.
if (Platform::isFullPath(Platform::stripBasePath(scriptFilenameBuffer)))
compiled = false;
// [tom, 11/17/2006] It seems to make sense to not compile scripts that are in the
// prefs directory. However, getDSOPath() can handle this situation and will put
// the dso along with the script to avoid name clashes with tools/game dsos.
if ((dsoPath && *dsoPath == 0) || (prefsPath && prefsPath[0] && dStrnicmp(
scriptFileName, prefsPath, dStrlen(prefsPath)) == 0))
compiled = false;
// If we're in a journaling mode, then we will read the script
// from the journal file.
if (journal && Journal::IsPlaying())
{
char fileNameBuf[256];
bool fileRead = false;
U32 fileSize;
Journal::ReadString(fileNameBuf);
Journal::Read(&fileRead);
if (!fileRead)
{
Con::errorf(ConsoleLogEntry::Script, "Journal script read (failed) for %s", fileNameBuf);
execDepth--;
return false;
}
Journal::Read(&fileSize);
char* script = new char[fileSize + 1];
Journal::Read(fileSize, script);
script[fileSize] = 0;
Con::printf("Executing (journal-read) %s.", scriptFileName);
CodeBlock *newCodeBlock = new CodeBlock();
newCodeBlock->compileExec(scriptFileName, script, noCalls, 0);
delete newCodeBlock;
delete[] script;
execDepth--;
return true;
}
// Ok, we let's try to load and compile the script.
Torque::FS::FileNodeRef scriptFile = Torque::FS::GetFileNode(scriptFileName);
Torque::FS::FileNodeRef dsoFile;
// ResourceObject *rScr = gResourceManager->find(scriptFileName);
// ResourceObject *rCom = NULL;
char nameBuffer[512];
char* script = NULL;
U32 version;
Stream* compiledStream = NULL;
Torque::Time scriptModifiedTime, dsoModifiedTime;
// Check here for .edso
bool edso = false;
if (dStricmp(ext, ".edso") == 0 && scriptFile != NULL)
{
edso = true;
dsoFile = scriptFile;
scriptFile = NULL;
dsoModifiedTime = dsoFile->getModifiedTime();
dStrcpy(nameBuffer, scriptFileName, 512);
}
// If we're supposed to be compiling this file, check to see if there's a DSO
if (compiled && !edso)
{
const char* filenameOnly = dStrrchr(scriptFileName, '/');
if (filenameOnly)
++filenameOnly;
else
filenameOnly = scriptFileName;
char pathAndFilename[1024];
Platform::makeFullPathName(filenameOnly, pathAndFilename, sizeof(pathAndFilename), dsoPath);
if (isEditorScript)
dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".edso", NULL);
else
dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".dso", NULL);
dsoFile = Torque::FS::GetFileNode(nameBuffer);
if (scriptFile != NULL)
scriptModifiedTime = scriptFile->getModifiedTime();
if (dsoFile != NULL)
dsoModifiedTime = dsoFile->getModifiedTime();
}
// Let's do a sanity check to complain about DSOs in the future.
//
// MM: This doesn't seem to be working correctly for now so let's just not issue
// the warning until someone knows how to resolve it.
//
//if(compiled && rCom && rScr && Platform::compareFileTimes(comModifyTime, scrModifyTime) < 0)
//{
//Con::warnf("exec: Warning! Found a DSO from the future! (%s)", nameBuffer);
//}
// If we had a DSO, let's check to see if we should be reading from it.
//MGT: fixed bug with dsos not getting recompiled correctly
//Note: Using Nathan Martin's version from the forums since its easier to read and understand
if (compiled && dsoFile != NULL && (scriptFile == NULL || (dsoModifiedTime >= scriptModifiedTime)))
{
//MGT: end
compiledStream = FileStream::createAndOpen(nameBuffer, Torque::FS::File::Read);
if (compiledStream)
{
// Check the version!
compiledStream->read(&version);
if (version != Con::DSOVersion)
{
Con::warnf("exec: Found an old DSO (%s, ver %d < %d), ignoring.", nameBuffer, version, Con::DSOVersion);
delete compiledStream;
compiledStream = NULL;
}
}
}
// If we're journalling, let's write some info out.
if (journal && Journal::IsRecording())
Journal::WriteString(scriptFileName);
if (scriptFile != NULL && !compiledStream)
{
// If we have source but no compiled version, then we need to compile
// (and journal as we do so, if that's required).
void* data;
U32 dataSize = 0;
Torque::FS::ReadFile(scriptFileName, data, dataSize, true);
if (journal && Journal::IsRecording())
Journal::Write(bool(data != NULL));
if (data == NULL)
{
Con::errorf(ConsoleLogEntry::Script, "exec: invalid script file %s.", scriptFileName);
execDepth--;
return false;
}
else
{
if (!dataSize)
{
execDepth--;
return false;
}
script = (char*)data;
if (journal && Journal::IsRecording())
{
Journal::Write(dataSize);
Journal::Write(dataSize, data);
}
}
#ifndef TORQUE_NO_DSO_GENERATION
if (compiled)
{
// compile this baddie.
#ifdef TORQUE_DEBUG
Con::printf("Compiling %s...", scriptFileName);
#endif
CodeBlock *code = new CodeBlock();
code->compile(nameBuffer, scriptFileName, script);
delete code;
code = NULL;
compiledStream = FileStream::createAndOpen(nameBuffer, Torque::FS::File::Read);
if (compiledStream)
{
compiledStream->read(&version);
}
else
{
// We have to exit out here, as otherwise we get double error reports.
delete[] script;
execDepth--;
return false;
}
}
#endif
}
else
{
if (journal && Journal::IsRecording())
Journal::Write(bool(false));
}
if (compiledStream)
{
// Delete the script object first to limit memory used
// during recursive execs.
delete[] script;
script = 0;
// We're all compiled, so let's run it.
#ifdef TORQUE_DEBUG
Con::printf("Loading compiled script %s.", scriptFileName);
#endif
CodeBlock* code = new CodeBlock;
code->read(scriptFileName, *compiledStream);
delete compiledStream;
code->exec(0, scriptFileName, NULL, 0, NULL, noCalls, NULL, 0);
delete code;
ret = true;
}
else if (scriptFile)
{
// No compiled script, let's just try executing it
// directly... this is either a mission file, or maybe
// we're on a readonly volume.
#ifdef TORQUE_DEBUG
Con::printf("Executing %s.", scriptFileName);
#endif
CodeBlock *newCodeBlock = new CodeBlock();
StringTableEntry name = StringTable->insert(scriptFileName);
newCodeBlock->compileExec(name, script, noCalls, 0);
ret = true;
}
else
{
// Don't have anything.
Con::warnf(ConsoleLogEntry::Script, "Missing file: %s!", scriptFileName);
ret = false;
}
delete[] script;
execDepth--;
return ret;
}
bool TorqueScriptRuntime::compile(const char* fileName, bool overrideNoDso)
{
Con::expandScriptFilename( scriptFilenameBuffer, sizeof( scriptFilenameBuffer ), fileName );
// Figure out where to put DSOs
StringTableEntry dsoPath = Con::getDSOPath(scriptFilenameBuffer);
if(dsoPath && *dsoPath == 0)
return false;
// If the script file extention is '.ed.tscript' then compile it to a different compiled extention
bool isEditorScript = false;
const char *ext = dStrrchr( scriptFilenameBuffer, '.' );
if( ext && ( dStricmp( ext, "." TORQUE_SCRIPT_EXTENSION) == 0 ) )
{
const char* ext2 = ext - 3;
if( dStricmp( ext2, ".ed." TORQUE_SCRIPT_EXTENSION) == 0 )
isEditorScript = true;
}
else if( ext && ( dStricmp( ext, ".gui" ) == 0 ) )
{
const char* ext2 = ext - 3;
if( dStricmp( ext2, ".ed.gui" ) == 0 )
isEditorScript = true;
}
const char *filenameOnly = dStrrchr(scriptFilenameBuffer, '/');
if(filenameOnly)
++filenameOnly;
else
filenameOnly = scriptFilenameBuffer;
char nameBuffer[512];
if( isEditorScript )
dStrcpyl(nameBuffer, sizeof(nameBuffer), dsoPath, "/", filenameOnly, ".edso", NULL);
else
dStrcpyl(nameBuffer, sizeof(nameBuffer), dsoPath, "/", filenameOnly, ".dso", NULL);
void *data = NULL;
U32 dataSize = 0;
Torque::FS::ReadFile(scriptFilenameBuffer, data, dataSize, true);
if(data == NULL)
{
Con::errorf(ConsoleLogEntry::Script, "compile: invalid script file %s.", scriptFilenameBuffer);
return false;
}
const char *script = static_cast<const char *>(data);
#ifdef TORQUE_DEBUG
Con::printf("Compiling %s...", scriptFilenameBuffer);
#endif
CodeBlock *code = new CodeBlock();
code->compile(nameBuffer, scriptFilenameBuffer, script, overrideNoDso);
delete code;
delete[] script;
return true;
}
}

View file

@ -0,0 +1,27 @@
#ifndef _TORQUESCRIPT_RUNTIME_H_
#define _TORQUESCRIPT_RUNTIME_H_
#include "ast.h"
#include "console/runtime.h"
namespace TorqueScript
{
class TorqueScriptRuntime : public Con::Runtime
{
public:
TorqueScriptRuntime();
~TorqueScriptRuntime() override;
void expandEscapedCharacters(char* dest, const char* src) override { expandEscape(dest, src); }
bool collapseEscapedCharacters(char* buf) override { return collapseEscape(buf); }
Con::EvalResult evaluate(const char* string, bool echo = false, const char* fileName = NULL) override;
Con::EvalResult evaluate(const char* script, S32 frame, bool echo = false, const char *fileName = NULL) override;
Con::EvalResult evaluatef(const char* string, ...) override;
bool executeFile(const char* fileName, bool noCalls, bool journalScript) override;
bool compile(const char* fileName, bool overrideNoDso);
};
inline TorqueScriptRuntime* gRuntime = new TorqueScriptRuntime();
inline TorqueScriptRuntime* getRuntime() { return gRuntime; }
}
#endif