added everything

This commit is contained in:
Metario
2017-04-17 06:17:10 -06:00
commit 9c6ff74f19
6121 changed files with 1625704 additions and 0 deletions

1951
engine/console/BASgram.cc Executable file

File diff suppressed because it is too large Load Diff

533
engine/console/BASgram.y Executable file
View File

@ -0,0 +1,533 @@
%{
// Make sure we don't get gram.h twice.
#define _BASGRAM_H_
#define _CMDGRAM_H_
#include <stdlib.h>
#include <stdio.h>
#include "console/console.h"
#include "console/compiler.h"
#include "console/consoleInternal.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 BASlex();
void BASerror(char *a, ...);
#define alloca dMalloc
%}
%{
/* Reserved Word Definitions */
/* IMPORTANT: SEE COMMENTS BELOW IF YOU ARE THINKING ABOUT MODIFYING TOKENS */
/*
ALL TOKENS BETWEEN HERE AND ADDITIONAL TOKENS BELOW MUST BE IDENTICAL TO THOSE
IN CS_CMD.y OTHERWISE BAD STUFF HAPPENS
*/
%}
%token <i> rwDEFINE rwENDDEF rwDECLARE
%token <i> rwBREAK rwELSE rwCONTINUE rwGLOBAL
%token <i> rwIF rwNIL rwRETURN rwWHILE
%token <i> rwENDIF rwENDWHILE rwENDFOR rwDEFAULT
%token <i> rwFOR rwDATABLOCK rwSWITCH rwCASE rwSWITCHSTR
%token <i> rwCASEOR rwPACKAGE
%token ILLEGAL_TOKEN
%{
/* Constants and Identifier Definitions */
%}
%token <c> CHRCONST
%token <i> INTCONST
%token <s> TTAG
%token <s> VAR
%token <s> IDENT
%token <str> STRATOM
%token <str> TAGATOM
%token <f> FLTCONST
%{
/* Operator Definitions */
%}
%token <i> '+' '-' '*' '/' '<' '>' '=' '.' '|' '&' '%'
%token <i> '(' ')' ',' ':' ';' '{' '}' '^' '~' '!' '@'
%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 {
char c;
int i;
const char *s;
char *str;
double f;
StmtNode *stmt;
ExprNode *expr;
SlotAssignNode *slist;
VarNode *var;
SlotDecl slot;
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> 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> 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
%type <slist> slot_assign
%type <slot> slot_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 '.'
%{
/*
Additional tokens
NOTE: These MUST be here, otherwise the definitions of the usual TorqueScript tokens changes
which will break the compiler. Double check the standard TS tokens arent being redefined before
testing a build.
*/
%}
%token <i> rwTHEN rwEND rwBEGIN rwCFOR rwTO rwSTEP
%%
start
: decl_list
{ }
;
decl_list
:
{ $$ = nil; }
| decl_list decl
{ if(!statementList) { statementList = $2; } else { statementList->append($2); } }
;
decl
: stmt
{ $$ = $1; }
| fn_decl_stmt
{ $$ = $1; }
| package_decl
{ $$ = $1; }
;
package_decl
: rwPACKAGE IDENT fn_decl_list rwEND
{ $$ = $3; for(StmtNode *walk = ($3);walk;walk = walk->getNext() ) walk->setPackage($2); }
;
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
| datablock_decl
| switch_stmt
| rwBREAK semicolon
{ $$ = BreakStmtNode::alloc(); }
| rwCONTINUE semicolon
{ $$ = ContinueStmtNode::alloc(); }
| rwRETURN semicolon
{ $$ = ReturnStmtNode::alloc(NULL); }
| rwRETURN expr semicolon
{ $$ = ReturnStmtNode::alloc($2); }
| expression_stmt semicolon
{ $$ = $1; }
| TTAG '=' expr semicolon
{ $$ = TTagSetStmtNode::alloc($1, $3, NULL); }
| TTAG '=' expr ',' expr semicolon
{ $$ = TTagSetStmtNode::alloc($1, $3, $5); }
;
fn_decl_stmt
: rwDEFINE IDENT '(' var_list_decl ')' statement_list rwEND
{ $$ = FunctionDeclStmtNode::alloc($2, NULL, $4, $6); }
| rwDEFINE IDENT opCOLONCOLON IDENT '(' var_list_decl ')' statement_list rwEND
{ $$ = FunctionDeclStmtNode::alloc($4, $2, $6, $8); }
;
var_list_decl
:
{ $$ = NULL; }
| var_list
{ $$ = $1; }
;
var_list
: VAR
{ $$ = VarNode::alloc($1, NULL); }
| var_list ',' VAR
{ $$ = $1; ((StmtNode*)($1))->append((StmtNode*)VarNode::alloc($3, NULL)); }
;
datablock_decl
: rwDATABLOCK IDENT '(' IDENT parent_block ')' slot_assign_list rwEND
{ $$ = ObjectDeclNode::alloc(ConstantNode::alloc($2), ConstantNode::alloc($4), NULL, $5, $7, NULL, true); }
;
object_decl
: rwDECLARE class_name_expr '(' object_name parent_block object_args ')' object_declare_block rwEND
{ $$ = ObjectDeclNode::alloc($2, $4, $6, $5, $8.slots, $8.decls, false); }
| rwDECLARE class_name_expr '(' object_name parent_block object_args ')' rwEND
{ $$ = ObjectDeclNode::alloc($2, $4, $6, $5, NULL, NULL, false); }
;
parent_block
:
{ $$ = NULL; }
| ':' IDENT
{ $$ = $2; }
;
object_name
:
{ $$ = StrConstNode::alloc("", 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 semicolon
{ $$ = $1; }
| object_decl_list object_decl semicolon
{ $1->append($2); $$ = $1; }
;
stmt_block
: rwBEGIN statement_list rwEND
{ $$ = $2; }
| stmt
{ $$ = $1; }
;
switch_stmt
: rwSWITCH expr case_block rwEND
{ $$ = $3; $3->propagateSwitchExpr($2, false); }
| rwSWITCHSTR expr case_block rwEND
{ $$ = $3; $3->propagateSwitchExpr($2, true); }
;
case_block
: rwCASE case_expr ':' statement_list
{ $$ = IfStmtNode::alloc($1, $2, $4, NULL, false); }
| rwCASE case_expr ':' statement_list rwDEFAULT ':' statement_list
{ $$ = IfStmtNode::alloc($1, $2, $4, $7, false); }
| rwCASE case_expr ':' statement_list case_block
{ $$ = IfStmtNode::alloc($1, $2, $4, $5, true); }
;
case_expr
: expr
{ $$ = $1;}
| case_expr rwCASEOR expr
{ ($1)->append($3); $$=$1; }
;
if_stmt
: rwIF expr rwTHEN statement_list rwEND
{ $$ = IfStmtNode::alloc($1, $2, $4, NULL, false); }
| rwIF expr rwTHEN statement_list rwELSE statement_list rwEND
{ $$ = IfStmtNode::alloc($1, $2, $4, $6, false); }
;
while_stmt
: rwWHILE expr rwBEGIN statement_list rwEND
{ $$ = LoopStmtNode::alloc($1, nil, $2, nil, $4, false); }
;
for_stmt
: rwFOR expr ',' expr ',' expr rwBEGIN statement_list rwEND
{ $$ = LoopStmtNode::alloc($1, $2, $4, $6, $8, false); }
;
expression_stmt
: stmt_expr
{ $$ = $1; }
;
expr
: stmt_expr
{ $$ = $1; }
| '(' expr ')'
{ $$ = $2; }
| expr '^' expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr '%' expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr '&' expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr '|' expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr '+' expr
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
| expr '-' expr
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
| expr '*' expr
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
| expr '/' expr
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
| '-' expr %prec UNARY
{ $$ = FloatUnaryExprNode::alloc($1, $2); }
| '*' expr %prec UNARY
{ $$ = TTagDerefNode::alloc($2); }
| TTAG
{ $$ = TTagExprNode::alloc($1); }
| expr '?' expr ':' expr
{ $$ = ConditionalExprNode::alloc($1, $3, $5); }
| expr '<' expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr '>' expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opGE expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opLE expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opEQ expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opNE expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opOR expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opSHL expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opSHR expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opAND expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opSTREQ expr
{ $$ = StreqExprNode::alloc($1, $3, true); }
| expr opSTRNE expr
{ $$ = StreqExprNode::alloc($1, $3, false); }
| expr '@' expr
{ $$ = StrcatExprNode::alloc($1, $3, $2); }
| '!' expr
{ $$ = IntUnaryExprNode::alloc($1, $2); }
| '~' expr
{ $$ = IntUnaryExprNode::alloc($1, $2); }
| TAGATOM
{ $$ = StrConstNode::alloc($1, true); }
| FLTCONST
{ $$ = FloatNode::alloc($1); }
| INTCONST
{ $$ = IntNode::alloc($1); }
| rwBREAK
{ $$ = ConstantNode::alloc(StringTable->insert("break")); }
| slot_acc
{ $$ = SlotAccessNode::alloc($1.object, $1.array, $1.slotName); }
| IDENT
{ $$ = ConstantNode::alloc($1); }
| STRATOM
{ $$ = StrConstNode::alloc($1, false); }
| VAR
{ $$ = (ExprNode*)VarNode::alloc($1, NULL); }
| VAR '[' aidx_expr ']'
{ $$ = (ExprNode*)VarNode::alloc($1, $3); }
;
slot_acc
: expr '.' IDENT
{ $$.object = $1; $$.slotName = $3; $$.array = NULL; }
| expr '.' IDENT '[' aidx_expr ']'
{ $$.object = $1; $$.slotName = $3; $$.array = $5; }
;
class_name_expr
: IDENT
{ $$ = ConstantNode::alloc($1); }
| '(' expr ')'
{ $$ = $2; }
;
assign_op_struct
: opPLUSPLUS
{ $$.token = '+'; $$.expr = FloatNode::alloc(1); }
| opMINUSMINUS
{ $$.token = '-'; $$.expr = FloatNode::alloc(1); }
| opPLASN expr
{ $$.token = '+'; $$.expr = $2; }
| opMIASN expr
{ $$.token = '-'; $$.expr = $2; }
| opMLASN expr
{ $$.token = '*'; $$.expr = $2; }
| opDVASN expr
{ $$.token = '/'; $$.expr = $2; }
| opMODASN expr
{ $$.token = '%'; $$.expr = $2; }
| opANDASN expr
{ $$.token = '&'; $$.expr = $2; }
| opXORASN expr
{ $$.token = '^'; $$.expr = $2; }
| opORASN expr
{ $$.token = '|'; $$.expr = $2; }
| opSLASN expr
{ $$.token = opSHL; $$.expr = $2; }
| opSRASN expr
{ $$.token = opSHR; $$.expr = $2; }
;
stmt_expr
: funcall_expr
{ $$ = $1; }
| object_decl
{ $$ = $1; }
| VAR '=' expr
{ $$ = AssignExprNode::alloc($1, NULL, $3); }
| VAR '[' aidx_expr ']' '=' expr
{ $$ = AssignExprNode::alloc($1, $3, $6); }
| VAR assign_op_struct
{ $$ = AssignOpExprNode::alloc($1, NULL, $2.expr, $2.token); }
| VAR '[' aidx_expr ']' assign_op_struct
{ $$ = AssignOpExprNode::alloc($1, $3, $5.expr, $5.token); }
| slot_acc assign_op_struct
{ $$ = SlotAssignOpNode::alloc($1.object, $1.slotName, $1.array, $2.token, $2.expr); }
| slot_acc '=' expr
{ $$ = SlotAssignNode::alloc($1.object, $1.array, $1.slotName, $3); }
| slot_acc '=' '{' expr_list '}'
{ $$ = SlotAssignNode::alloc($1.object, $1.array, $1.slotName, $4); }
;
funcall_expr
: IDENT '(' expr_list_decl ')'
{ $$ = FuncCallExprNode::alloc($1, NULL, $3, false); }
| IDENT opCOLONCOLON IDENT '(' expr_list_decl ')'
{ $$ = FuncCallExprNode::alloc($3, $1, $5, false); }
| expr '.' IDENT '(' expr_list_decl ')'
{ $1->append($5); $$ = FuncCallExprNode::alloc($3, NULL, $1, true); }
;
expr_list_decl
:
{ $$ = NULL; }
| expr_list
{ $$ = $1; }
;
expr_list
: expr
{ $$ = $1; }
| expr_list ',' expr
{ ($1)->append($3); $$ = $1; }
;
slot_assign_list
: slot_assign
{ $$ = $1; }
| slot_assign_list slot_assign
{ $1->append($2); $$ = $1; }
;
slot_assign
: IDENT '=' expr semicolon
{ $$ = SlotAssignNode::alloc(NULL, NULL, $1, $3); }
| rwDATABLOCK '=' expr semicolon
{ $$ = SlotAssignNode::alloc(NULL, NULL, StringTable->insert("datablock"), $3); }
| IDENT '[' aidx_expr ']' '=' expr semicolon
{ $$ = SlotAssignNode::alloc(NULL, $3, $1, $6); }
;
aidx_expr
: expr
{ $$ = $1; }
| aidx_expr ',' expr
{ $$ = CommaCatExprNode::alloc($1, $3); }
;
semicolon:
| semicolon ';'
;
%%

2190
engine/console/BASscan.cc Executable file

File diff suppressed because it is too large Load Diff

289
engine/console/BASscan.l Executable file
View File

@ -0,0 +1,289 @@
%{
#define YYLMAX 4096
#include <stdio.h>
#include "platform/platform.h"
#include "core/stringTable.h"
#include "console/console.h"
#define _CMDGRAM_H_
#include "console/compiler.h"
#include "console/basgram.h"
using namespace Compiler;
#define YY_NEVER_INTERACTIVE 1
// Some basic parsing primitives...
static int Sc_ScanString(int ret);
static int Sc_ScanNum();
static int Sc_ScanVar();
static int Sc_ScanHex();
// Deal with debuggability of FLEX.
#ifdef TORQUE_DEBUG
#define FLEX_DEBUG 1
#else
#define FLEX_DEBUG 0
#endif
//#undef input
//#undef unput
#undef BASgetc
int BASgetc();
#define YY_INPUT(buf,result,max_size) \
{ \
int c = '*', n; \
for ( n = 0; n < max_size && \
(c = BASgetc()) != EOF && c != '\n'; ++n ) \
buf[n] = (char) c; \
if ( c == '\n' ) \
buf[n++] = (char) c; \
result = n; \
}
static int lineIndex;
// Prototypes
void BASSetScanBuffer(const char *sb, const char *fn);
void BASerror(char * s, ...);
// Error reporting
void CMDerror(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]* ;
"rem"{SPACE}+[^\n\r]* ;
[\r] ;
[\n] { lineIndex++; }
\"(\\.|[^\\"\n\r])*\" { return(Sc_ScanString(STRATOM)); }
\'(\\.|[^\\'\n\r])*\' { return(Sc_ScanString(TAGATOM)); }
"==" return(BASlval.i = opEQ);
"!=" return(BASlval.i = opNE);
">=" return(BASlval.i = opGE);
"<=" return(BASlval.i = opLE);
"&&" return(BASlval.i = opAND);
"||" return(BASlval.i = opOR);
"::" return(BASlval.i = opCOLONCOLON);
"--" return(BASlval.i = opMINUSMINUS);
"++" return(BASlval.i = opPLUSPLUS);
"$=" return(BASlval.i = opSTREQ);
"!$=" return(BASlval.i = opSTRNE);
"<<" return(BASlval.i = opSHL);
">>" return(BASlval.i = opSHR);
"+=" return(BASlval.i = opPLASN);
"-=" return(BASlval.i = opMIASN);
"*=" return(BASlval.i = opMLASN);
"/=" return(BASlval.i = opDVASN);
"%=" return(BASlval.i = opMODASN);
"&=" return(BASlval.i = opANDASN);
"^=" return(BASlval.i = opXORASN);
"|=" return(BASlval.i = opORASN);
"<<=" return(BASlval.i = opSLASN);
">>=" return(BASlval.i = opSRASN);
"NL" {BASlval.i = '\n'; return '@'; }
"TAB" {BASlval.i = '\t'; return '@'; }
"SPC" {BASlval.i = ' '; return '@'; }
"@" {BASlval.i = 0; return '@'; }
"?" |
"[" |
"]" |
"(" |
")" |
"+" |
"-" |
"*" |
"/" |
"<" |
">" |
"|" |
"." |
"!" |
":" |
";" |
"{" |
"}" |
"," |
"&" |
"%" |
"^" |
"~" |
"=" { return(BASlval.i = BAStext[0]); }
"or" { BASlval.i = lineIndex; return(rwCASEOR); }
"break" { BASlval.i = lineIndex; return(rwBREAK); }
"return" { BASlval.i = lineIndex; return(rwRETURN); }
"else" { BASlval.i = lineIndex; return(rwELSE); }
"while" { BASlval.i = lineIndex; return(rwWHILE); }
"if" { BASlval.i = lineIndex; return(rwIF); }
"then" { BASlval.i = lineIndex; return(rwTHEN); }
"do" { BASlval.i = lineIndex; return(rwBEGIN); }
"begin" { BASlval.i = lineIndex; return(rwBEGIN); }
"end" { BASlval.i = lineIndex; return(rwEND); }
"for" { BASlval.i = lineIndex; return(rwFOR); }
"cfor" { BASlval.i = lineIndex; return(rwCFOR); }
"to" { BASlval.i = lineIndex; return(rwTO); }
"step" { BASlval.i = lineIndex; return(rwSTEP); }
"continue" { BASlval.i = lineIndex; return(rwCONTINUE); }
"function" { BASlval.i = lineIndex; return(rwDEFINE); }
"sub" { BASlval.i = lineIndex; return(rwDEFINE); }
"new" { BASlval.i = lineIndex; return(rwDECLARE); }
"datablock" { BASlval.i = lineIndex; return(rwDATABLOCK); }
"case" { BASlval.i = lineIndex; return(rwCASE); }
"switch$" { BASlval.i = lineIndex; return(rwSWITCHSTR); }
"switch" { BASlval.i = lineIndex; return(rwSWITCH); }
"default" { BASlval.i = lineIndex; return(rwDEFAULT); }
"package" { BASlval.i = lineIndex; return(rwPACKAGE); }
"true" { BASlval.i = 1; return INTCONST; }
"false" { BASlval.i = 0; return INTCONST; }
{VAR} return(Sc_ScanVar());
{ID} { BAStext[BASleng] = 0; BASlval.s = StringTable->insert(BAStext); return(IDENT); }
0[xX]{HEXDIGIT}+ return(Sc_ScanHex());
{INTEGER} { BAStext[BASleng] = 0; BASlval.i = atoi(BAStext); return INTCONST; }
{FLOAT} return Sc_ScanNum();
{ILID} return(ILLEGAL_TOKEN);
. return(ILLEGAL_TOKEN);
%%
/*
* Scan character constant.
*/
/*
* Scan identifier.
*/
static const char *scanBuffer;
static const char *fileName;
static int scanIndex;
const char * BASGetCurrentFile()
{
return fileName;
}
int BASGetCurrentLine()
{
return lineIndex;
}
void BASerror(char *, ...)
{
gSyntaxError = true;
if(fileName)
Con::errorf(ConsoleLogEntry::Script, "%s Line: %d - Syntax error.",
fileName, lineIndex);
else
Con::errorf(ConsoleLogEntry::Script, "Syntax error in input.");
}
void BASSetScanBuffer(const char *sb, const char *fn)
{
scanBuffer = sb;
fileName = fn;
scanIndex = 0;
lineIndex = 1;
}
int BASgetc()
{
int ret = scanBuffer[scanIndex];
if(ret)
scanIndex++;
else
ret = -1;
return ret;
}
int BASwrap()
{
return 1;
}
static int Sc_ScanVar()
{
BAStext[BASleng] = 0;
BASlval.s = StringTable->insert(BAStext);
return(VAR);
}
/*
* Scan string constant.
*/
// Defined in CS_CMD.l
extern void expandEscape(char *dest, const char *src);
extern bool collapseEscape(char *buf);
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_ScanString(int ret)
{
BAStext[BASleng - 1] = 0;
if(!collapseEscape(BAStext+1))
return -1;
BASlval.str = (char *) consoleAlloc(dStrlen(BAStext));
dStrcpy(BASlval.str, BAStext + 1);
return(ret);
}
static int Sc_ScanNum()
{
BAStext[BASleng] = 0;
BASlval.f = atof(BAStext);
return(FLTCONST);
}
static int Sc_ScanHex()
{
int val = 0;
dSscanf(BAStext, "%x", &val);
BASlval.i = val;
return INTCONST;
}

2140
engine/console/CMDgram.cc Executable file

File diff suppressed because it is too large Load Diff

526
engine/console/CMDgram.y Executable file
View File

@ -0,0 +1,526 @@
%{
// 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"
#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(char *, ...);
#define alloca dMalloc
%}
%{
/* Reserved Word Definitions */
%}
%token <i> rwDEFINE rwENDDEF rwDECLARE
%token <i> rwBREAK rwELSE rwCONTINUE rwGLOBAL
%token <i> rwIF rwNIL rwRETURN rwWHILE rwDO
%token <i> rwENDIF rwENDWHILE rwENDFOR rwDEFAULT
%token <i> rwFOR rwDATABLOCK rwSWITCH rwCASE rwSWITCHSTR
%token <i> rwCASEOR rwPACKAGE rwNAMESPACE rwCLASS
%token ILLEGAL_TOKEN
%{
/* Constants and Identifier Definitions */
%}
%token <c> CHRCONST
%token <i> INTCONST
%token <s> TTAG
%token <s> VAR
%token <s> IDENT
%token <str> STRATOM
%token <str> TAGATOM
%token <f> FLTCONST
%{
/* Operator Definitions */
%}
%token <i> '+' '-' '*' '/' '<' '>' '=' '.' '|' '&' '%'
%token <i> '(' ')' ',' ':' ';' '{' '}' '^' '~' '!' '@'
%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 {
char c;
int i;
const char * s;
char * str;
double f;
StmtNode * stmt;
ExprNode * expr;
SlotAssignNode * slist;
VarNode * var;
SlotDecl slot;
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> 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> 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
%type <slist> slot_assign
%type <slot> slot_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 '.'
%%
start
: decl_list
{ }
;
decl_list
:
{ $$ = nil; }
| decl_list decl
{ if(!statementList) { statementList = $2; } else { statementList->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); }
;
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
| datablock_decl
| switch_stmt
| rwBREAK ';'
{ $$ = BreakStmtNode::alloc(); }
| rwCONTINUE ';'
{ $$ = ContinueStmtNode::alloc(); }
| rwRETURN ';'
{ $$ = ReturnStmtNode::alloc(NULL); }
| rwRETURN expr ';'
{ $$ = ReturnStmtNode::alloc($2); }
| expression_stmt ';'
{ $$ = $1; }
| TTAG '=' expr ';'
{ $$ = TTagSetStmtNode::alloc($1, $3, NULL); }
| TTAG '=' expr ',' expr ';'
{ $$ = TTagSetStmtNode::alloc($1, $3, $5); }
;
fn_decl_stmt
: rwDEFINE IDENT '(' var_list_decl ')' '{' statement_list '}'
{ $$ = FunctionDeclStmtNode::alloc($2, NULL, $4, $7); }
| rwDEFINE IDENT opCOLONCOLON IDENT '(' var_list_decl ')' '{' statement_list '}'
{ $$ = FunctionDeclStmtNode::alloc($4, $2, $6, $9); }
;
var_list_decl
:
{ $$ = NULL; }
| var_list
{ $$ = $1; }
;
var_list
: VAR
{ $$ = VarNode::alloc($1, NULL); }
| var_list ',' VAR
{ $$ = $1; ((StmtNode*)($1))->append((StmtNode*)VarNode::alloc($3, NULL)); }
;
datablock_decl
: rwDATABLOCK IDENT '(' IDENT parent_block ')' '{' slot_assign_list '}' ';'
{ $$ = ObjectDeclNode::alloc(ConstantNode::alloc($2), ConstantNode::alloc($4), NULL, $5, $8, NULL, true); }
;
object_decl
: rwDECLARE class_name_expr '(' object_name parent_block object_args ')' '{' object_declare_block '}'
{ $$ = ObjectDeclNode::alloc($2, $4, $6, $5, $9.slots, $9.decls, false); }
| rwDECLARE class_name_expr '(' object_name parent_block object_args ')'
{ $$ = ObjectDeclNode::alloc($2, $4, $6, $5, NULL, NULL, false); }
;
parent_block
:
{ $$ = NULL; }
| ':' IDENT
{ $$ = $2; }
;
object_name
:
{ $$ = StrConstNode::alloc("", 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, $2, $4, NULL, false); }
| rwCASE case_expr ':' statement_list rwDEFAULT ':' statement_list
{ $$ = IfStmtNode::alloc($1, $2, $4, $7, false); }
| rwCASE case_expr ':' statement_list case_block
{ $$ = IfStmtNode::alloc($1, $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, $3, $5, NULL, false); }
| rwIF '(' expr ')' stmt_block rwELSE stmt_block
{ $$ = IfStmtNode::alloc($1, $3, $5, $7, false); }
;
while_stmt
: rwWHILE '(' expr ')' stmt_block
{ $$ = LoopStmtNode::alloc($1, nil, $3, nil, $5, false); }
| rwDO stmt_block rwWHILE '(' expr ')'
{ $$ = LoopStmtNode::alloc($3, nil, $5, nil, $2, true); }
;
for_stmt
: rwFOR '(' expr ';' expr ';' expr ')' stmt_block
{ $$ = LoopStmtNode::alloc($1, $3, $5, $7, $9, false); }
| rwFOR '(' expr ';' expr ';' ')' stmt_block
{ $$ = LoopStmtNode::alloc($1, $3, $5, NULL, $8, false); }
| rwFOR '(' expr ';' ';' expr ')' stmt_block
{ $$ = LoopStmtNode::alloc($1, $3, NULL, $6, $8, false); }
| rwFOR '(' expr ';' ';' ')' stmt_block
{ $$ = LoopStmtNode::alloc($1, $3, NULL, NULL, $7, false); }
| rwFOR '(' ';' expr ';' expr ')' stmt_block
{ $$ = LoopStmtNode::alloc($1, NULL, $4, $6, $8, false); }
| rwFOR '(' ';' expr ';' ')' stmt_block
{ $$ = LoopStmtNode::alloc($1, NULL, $4, NULL, $7, false); }
| rwFOR '(' ';' ';' expr ')' stmt_block
{ $$ = LoopStmtNode::alloc($1, NULL, NULL, $5, $7, false); }
| rwFOR '(' ';' ';' ')' stmt_block
{ $$ = LoopStmtNode::alloc($1, NULL, NULL, NULL, $6, false); }
;
expression_stmt
: stmt_expr
{ $$ = $1; }
;
expr
: stmt_expr
{ $$ = $1; }
| '(' expr ')'
{ $$ = $2; }
| expr '^' expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr '%' expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr '&' expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr '|' expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr '+' expr
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
| expr '-' expr
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
| expr '*' expr
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
| expr '/' expr
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
| '-' expr %prec UNARY
{ $$ = FloatUnaryExprNode::alloc($1, $2); }
| '*' expr %prec UNARY
{ $$ = TTagDerefNode::alloc($2); }
| TTAG
{ $$ = TTagExprNode::alloc($1); }
| expr '?' expr ':' expr
{ $$ = ConditionalExprNode::alloc($1, $3, $5); }
| expr '<' expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr '>' expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opGE expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opLE expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opEQ expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opNE expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opOR expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opSHL expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opSHR expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opAND expr
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
| expr opSTREQ expr
{ $$ = StreqExprNode::alloc($1, $3, true); }
| expr opSTRNE expr
{ $$ = StreqExprNode::alloc($1, $3, false); }
| expr '@' expr
{ $$ = StrcatExprNode::alloc($1, $3, $2); }
| '!' expr
{ $$ = IntUnaryExprNode::alloc($1, $2); }
| '~' expr
{ $$ = IntUnaryExprNode::alloc($1, $2); }
| TAGATOM
{ $$ = StrConstNode::alloc($1, true); }
| FLTCONST
{ $$ = FloatNode::alloc($1); }
| INTCONST
{ $$ = IntNode::alloc($1); }
| rwBREAK
{ $$ = ConstantNode::alloc(StringTable->insert("break")); }
| slot_acc
{ $$ = SlotAccessNode::alloc($1.object, $1.array, $1.slotName); }
| IDENT
{ $$ = ConstantNode::alloc($1); }
| STRATOM
{ $$ = StrConstNode::alloc($1, false); }
| VAR
{ $$ = (ExprNode*)VarNode::alloc($1, NULL); }
| VAR '[' aidx_expr ']'
{ $$ = (ExprNode*)VarNode::alloc($1, $3); }
;
slot_acc
: expr '.' IDENT
{ $$.object = $1; $$.slotName = $3; $$.array = NULL; }
| expr '.' IDENT '[' aidx_expr ']'
{ $$.object = $1; $$.slotName = $3; $$.array = $5; }
;
class_name_expr
: IDENT
{ $$ = ConstantNode::alloc($1); }
| '(' expr ')'
{ $$ = $2; }
;
assign_op_struct
: opPLUSPLUS
{ $$.token = '+'; $$.expr = FloatNode::alloc(1); }
| opMINUSMINUS
{ $$.token = '-'; $$.expr = FloatNode::alloc(1); }
| opPLASN expr
{ $$.token = '+'; $$.expr = $2; }
| opMIASN expr
{ $$.token = '-'; $$.expr = $2; }
| opMLASN expr
{ $$.token = '*'; $$.expr = $2; }
| opDVASN expr
{ $$.token = '/'; $$.expr = $2; }
| opMODASN expr
{ $$.token = '%'; $$.expr = $2; }
| opANDASN expr
{ $$.token = '&'; $$.expr = $2; }
| opXORASN expr
{ $$.token = '^'; $$.expr = $2; }
| opORASN expr
{ $$.token = '|'; $$.expr = $2; }
| opSLASN expr
{ $$.token = opSHL; $$.expr = $2; }
| opSRASN expr
{ $$.token = opSHR; $$.expr = $2; }
;
stmt_expr
: funcall_expr
{ $$ = $1; }
| object_decl
{ $$ = $1; }
| VAR '=' expr
{ $$ = AssignExprNode::alloc($1, NULL, $3); }
| VAR '[' aidx_expr ']' '=' expr
{ $$ = AssignExprNode::alloc($1, $3, $6); }
| VAR assign_op_struct
{ $$ = AssignOpExprNode::alloc($1, NULL, $2.expr, $2.token); }
| VAR '[' aidx_expr ']' assign_op_struct
{ $$ = AssignOpExprNode::alloc($1, $3, $5.expr, $5.token); }
| slot_acc assign_op_struct
{ $$ = SlotAssignOpNode::alloc($1.object, $1.slotName, $1.array, $2.token, $2.expr); }
| slot_acc '=' expr
{ $$ = SlotAssignNode::alloc($1.object, $1.array, $1.slotName, $3); }
| slot_acc '=' '{' expr_list '}'
{ $$ = SlotAssignNode::alloc($1.object, $1.array, $1.slotName, $4); }
;
funcall_expr
: IDENT '(' expr_list_decl ')'
{ $$ = FuncCallExprNode::alloc($1, NULL, $3, false); }
| IDENT opCOLONCOLON IDENT '(' expr_list_decl ')'
{ $$ = FuncCallExprNode::alloc($3, $1, $5, false); }
| expr '.' IDENT '(' expr_list_decl ')'
{ $1->append($5); $$ = FuncCallExprNode::alloc($3, NULL, $1, true); }
;
expr_list_decl
:
{ $$ = NULL; }
| expr_list
{ $$ = $1; }
;
expr_list
: expr
{ $$ = $1; }
| expr_list ',' expr
{ ($1)->append($3); $$ = $1; }
;
slot_assign_list
: slot_assign
{ $$ = $1; }
| slot_assign_list slot_assign
{ $1->append($2); $$ = $1; }
;
slot_assign
: IDENT '=' expr ';'
{ $$ = SlotAssignNode::alloc(NULL, NULL, $1, $3); }
| rwDATABLOCK '=' expr ';'
{ $$ = SlotAssignNode::alloc(NULL, NULL, StringTable->insert("datablock"), $3); }
| IDENT '[' aidx_expr ']' '=' expr ';'
{ $$ = SlotAssignNode::alloc(NULL, $3, $1, $6); }
;
aidx_expr
: expr
{ $$ = $1; }
| aidx_expr ',' expr
{ $$ = CommaCatExprNode::alloc($1, $3); }
;
%%

2426
engine/console/CMDscan.cc Executable file

File diff suppressed because it is too large Load Diff

541
engine/console/CMDscan.l Executable file
View File

@ -0,0 +1,541 @@
%{
#define YYLMAX 4096
#include <stdio.h>
#include "platform/platform.h"
#include "core/stringTable.h"
#include "console/console.h"
#include "console/compiler.h"
using namespace Compiler;
#define YY_NEVER_INTERACTIVE 1
// Some basic parsing primitives...
static int Sc_ScanString(int ret);
static int Sc_ScanNum();
static int Sc_ScanVar();
static int Sc_ScanHex();
// 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(char * s, ...);
// Reset the parser.
void CMDrestart(FILE *in);
%}
DIGIT [0-9]
INTEGER {DIGIT}+
FLOAT ({INTEGER}\.{INTEGER})|({INTEGER}(\.{INTEGER})?[eE][+-]?{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]* ;
[\r] ;
[\n] {lineIndex++;}
\"(\\.|[^\\"\n\r])*\" { return(Sc_ScanString(STRATOM)); }
\'(\\.|[^\\'\n\r])*\' { return(Sc_ScanString(TAGATOM)); }
"==" return(CMDlval.i = opEQ);
"!=" return(CMDlval.i = opNE);
">=" return(CMDlval.i = opGE);
"<=" return(CMDlval.i = opLE);
"&&" return(CMDlval.i = opAND);
"||" return(CMDlval.i = opOR);
"::" return(CMDlval.i = opCOLONCOLON);
"--" return(CMDlval.i = opMINUSMINUS);
"++" return(CMDlval.i = opPLUSPLUS);
"$=" return(CMDlval.i = opSTREQ);
"!$=" return(CMDlval.i = opSTRNE);
"<<" return(CMDlval.i = opSHL);
">>" return(CMDlval.i = opSHR);
"+=" return(CMDlval.i = opPLASN);
"-=" return(CMDlval.i = opMIASN);
"*=" return(CMDlval.i = opMLASN);
"/=" return(CMDlval.i = opDVASN);
"%=" return(CMDlval.i = opMODASN);
"&=" return(CMDlval.i = opANDASN);
"^=" return(CMDlval.i = opXORASN);
"|=" return(CMDlval.i = opORASN);
"<<=" return(CMDlval.i = opSLASN);
">>=" return(CMDlval.i = opSRASN);
"NL" {CMDlval.i = '\n'; return '@'; }
"TAB" {CMDlval.i = '\t'; return '@'; }
"SPC" {CMDlval.i = ' '; return '@'; }
"@" {CMDlval.i = 0; return '@'; }
"/*" {
register 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;
}
}
"?" |
"[" |
"]" |
"(" |
")" |
"+" |
"-" |
"*" |
"/" |
"<" |
">" |
"|" |
"." |
"!" |
":" |
";" |
"{" |
"}" |
"," |
"&" |
"%" |
"^" |
"~" |
"=" { return(CMDlval.i = CMDtext[0]); }
"or" { CMDlval.i = lineIndex; return(rwCASEOR); }
"break" { CMDlval.i = lineIndex; return(rwBREAK); }
"return" { CMDlval.i = lineIndex; return(rwRETURN); }
"else" { CMDlval.i = lineIndex; return(rwELSE); }
"while" { CMDlval.i = lineIndex; return(rwWHILE); }
"do" { CMDlval.i = lineIndex; return(rwDO); }
"if" { CMDlval.i = lineIndex; return(rwIF); }
"for" { CMDlval.i = lineIndex; return(rwFOR); }
"continue" { CMDlval.i = lineIndex; return(rwCONTINUE); }
"function" { CMDlval.i = lineIndex; return(rwDEFINE); }
"new" { CMDlval.i = lineIndex; return(rwDECLARE); }
"datablock" { CMDlval.i = lineIndex; return(rwDATABLOCK); }
"case" { CMDlval.i = lineIndex; return(rwCASE); }
"switch$" { CMDlval.i = lineIndex; return(rwSWITCHSTR); }
"switch" { CMDlval.i = lineIndex; return(rwSWITCH); }
"default" { CMDlval.i = lineIndex; return(rwDEFAULT); }
"package" { CMDlval.i = lineIndex; return(rwPACKAGE); }
"namespace" { CMDlval.i = lineIndex; return(rwNAMESPACE); }
"true" { CMDlval.i = 1; return INTCONST; }
"false" { CMDlval.i = 0; return INTCONST; }
{VAR} return(Sc_ScanVar());
{ID} { CMDtext[CMDleng] = 0; CMDlval.s = StringTable->insert(CMDtext); return(IDENT); }
0[xX]{HEXDIGIT}+ return(Sc_ScanHex());
{INTEGER} { CMDtext[CMDleng] = 0; CMDlval.i = dAtoi(CMDtext); 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(char *format, ...)
{
Compiler::gSyntaxError = true;
const int BUFMAX = 1024;
char tempBuf[BUFMAX];
va_list args;
va_start( args, format );
#ifdef TORQUE_OS_WIN32
_vsnprintf( tempBuf, BUFMAX, format, args );
#else
vsnprintf( tempBuf, BUFMAX, format, args );
#endif
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 = StringTable->insert(CMDtext);
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_ScanString(int ret)
{
CMDtext[CMDleng - 1] = 0;
if(!collapseEscape(CMDtext+1))
return -1;
CMDlval.str = (char *) consoleAlloc(dStrlen(CMDtext));
dStrcpy(CMDlval.str, CMDtext + 1);
return(ret);
}
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 = dAtof(CMDtext);
return(FLTCONST);
}
static int Sc_ScanHex()
{
S32 val = 0;
dSscanf(CMDtext, "%x", &val);
CMDlval.i = val;
return INTCONST;
}
void CMD_reset()
{
CMDrestart(NULL);
}

461
engine/console/ast.h Executable file
View File

@ -0,0 +1,461 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _AST_H_
#define _AST_H_
class ExprEvalState;
class Namespace;
class SimObject;
class SimGroup;
enum TypeReq {
TypeReqNone,
TypeReqUInt,
TypeReqFloat,
TypeReqString
};
/// 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();
/// @name next Accessors
/// @{
///
void append(StmtNode *next);
StmtNode *getNext() { return next; }
/// @}
/// @name Debug Info
/// @{
StringTableEntry dbgFileName; ///< Name of file this node is associated with.
S32 dbgLineNumber; ///< Line number this node is associated with.
/// @}
/// @name Breaking
/// @{
void addBreakCount();
void addBreakLine(U32 ip);
/// @}
/// @name Compilation
/// @{
virtual U32 precompileStmt(U32 loopCount) = 0;
virtual U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint) = 0;
virtual void setPackage(StringTableEntry packageName);
/// @}
};
struct BreakStmtNode : StmtNode
{
static BreakStmtNode *alloc();
U32 precompileStmt(U32 loopCount);
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
};
struct ContinueStmtNode : StmtNode
{
static ContinueStmtNode *alloc();
U32 precompileStmt(U32 loopCount);
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
};
/// A mathematical expression.
struct ExprNode : StmtNode
{
U32 precompileStmt(U32 loopCount);
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
virtual U32 precompile(TypeReq type) = 0;
virtual U32 compile(U32 *codeStream, U32 ip, TypeReq type) = 0;
virtual TypeReq getPreferredType() = 0;
};
struct ReturnStmtNode : StmtNode
{
ExprNode *expr;
static ReturnStmtNode *alloc(ExprNode *expr);
U32 precompileStmt(U32 loopCount);
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
};
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 precompileStmt(U32 loopCount);
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
};
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 precompileStmt(U32 loopCount);
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
};
/// A binary mathematical expression (ie, left op right).
struct BinaryExprNode : ExprNode
{
S32 op;
ExprNode *left;
ExprNode *right;
};
struct FloatBinaryExprNode : BinaryExprNode
{
static FloatBinaryExprNode *alloc(S32 op, ExprNode *left, ExprNode *right);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct ConditionalExprNode : ExprNode
{
ExprNode *testExpr;
ExprNode *trueExpr;
ExprNode *falseExpr;
bool integer;
static ConditionalExprNode *alloc(ExprNode *testExpr, ExprNode *trueExpr, ExprNode *falseExpr);
virtual U32 precompile(TypeReq type);
virtual U32 compile(U32 *codeStream, U32 ip, TypeReq type);
virtual TypeReq getPreferredType();
};
struct IntBinaryExprNode : BinaryExprNode
{
TypeReq subType;
U32 operand;
static IntBinaryExprNode *alloc(S32 op, ExprNode *left, ExprNode *right);
void getSubTypeOperand();
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct StreqExprNode : BinaryExprNode
{
bool eq;
static StreqExprNode *alloc(ExprNode *left, ExprNode *right, bool eq);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct StrcatExprNode : BinaryExprNode
{
int appendChar;
static StrcatExprNode *alloc(ExprNode *left, ExprNode *right, int appendChar);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct CommaCatExprNode : BinaryExprNode
{
static CommaCatExprNode *alloc(ExprNode *left, ExprNode *right);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct IntUnaryExprNode : ExprNode
{
S32 op;
ExprNode *expr;
bool integer;
static IntUnaryExprNode *alloc(S32 op, ExprNode *expr);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct FloatUnaryExprNode : ExprNode
{
S32 op;
ExprNode *expr;
static FloatUnaryExprNode *alloc(S32 op, ExprNode *expr);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct VarNode : ExprNode
{
StringTableEntry varName;
ExprNode *arrayIndex;
static VarNode *alloc(StringTableEntry varName, ExprNode *arrayIndex);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct IntNode : ExprNode
{
S32 value;
U32 index; // if it's converted to float/string
static IntNode *alloc(S32 value);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct FloatNode : ExprNode
{
F64 value;
U32 index;
static FloatNode *alloc(F64 value);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct StrConstNode : ExprNode
{
char *str;
F64 fVal;
U32 index;
bool tag;
static StrConstNode *alloc(char *str, bool tag);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct ConstantNode : ExprNode
{
StringTableEntry value;
F64 fVal;
U32 index;
static ConstantNode *alloc(StringTableEntry value);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct AssignExprNode : ExprNode
{
StringTableEntry varName;
ExprNode *expr;
ExprNode *arrayIndex;
TypeReq subType;
static AssignExprNode *alloc(StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct AssignDecl
{
S32 token;
ExprNode *expr;
bool integer;
};
struct AssignOpExprNode : ExprNode
{
StringTableEntry varName;
ExprNode *expr;
ExprNode *arrayIndex;
S32 op;
U32 operand;
TypeReq subType;
static AssignOpExprNode *alloc(StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr, S32 op);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct TTagSetStmtNode : StmtNode
{
StringTableEntry tag;
ExprNode *valueExpr;
ExprNode *stringExpr;
static TTagSetStmtNode *alloc(StringTableEntry tag, ExprNode *valueExpr, ExprNode *stringExpr);
U32 precompileStmt(U32 loopCount);
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
};
struct TTagDerefNode : ExprNode
{
ExprNode *expr;
static TTagDerefNode *alloc(ExprNode *expr);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct TTagExprNode : ExprNode
{
StringTableEntry tag;
static TTagExprNode *alloc(StringTableEntry tag);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct FuncCallExprNode : ExprNode
{
StringTableEntry funcName;
StringTableEntry nameSpace;
ExprNode *args;
U32 callType;
enum {
FunctionCall,
MethodCall,
ParentCall
};
static FuncCallExprNode *alloc(StringTableEntry funcName, StringTableEntry nameSpace, ExprNode *args, bool dot);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct SlotDecl
{
ExprNode *object;
StringTableEntry slotName;
ExprNode *array;
};
struct SlotAccessNode : ExprNode
{
ExprNode *objectExpr, *arrayExpr;
StringTableEntry slotName;
static SlotAccessNode *alloc(ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct SlotAssignNode : ExprNode
{
ExprNode *objectExpr, *arrayExpr;
StringTableEntry slotName;
ExprNode *valueExpr;
static SlotAssignNode *alloc(ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName, ExprNode *valueExpr);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct SlotAssignOpNode : ExprNode
{
ExprNode *objectExpr, *arrayExpr;
StringTableEntry slotName;
S32 op;
ExprNode *valueExpr;
U32 operand;
TypeReq subType;
static SlotAssignOpNode *alloc(ExprNode *objectExpr, StringTableEntry slotName, ExprNode *arrayExpr, S32 op, ExprNode *valueExpr);
U32 precompile(TypeReq type);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
TypeReq getPreferredType();
};
struct ObjectDeclNode : ExprNode
{
ExprNode *classNameExpr;
StringTableEntry parentObject;
ExprNode *objectNameExpr;
ExprNode *argList;
SlotAssignNode *slotDecls;
ObjectDeclNode *subObjects;
bool structDecl;
U32 failOffset;
static ObjectDeclNode *alloc(ExprNode *classNameExpr, ExprNode *objectNameExpr, ExprNode *argList, StringTableEntry parentObject, SlotAssignNode *slotDecls, ObjectDeclNode *subObjects, bool structDecl);
U32 precompile(TypeReq type);
U32 precompileSubObject(bool);
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
U32 compileSubObject(U32 *codeStream, U32 ip, bool);
TypeReq getPreferredType();
};
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(StringTableEntry fnName, StringTableEntry nameSpace, VarNode *args, StmtNode *stmts);
U32 precompileStmt(U32 loopCount);
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
void setPackage(StringTableEntry packageName);
};
extern StmtNode *statementList;
extern void createFunction(const char *fnName, VarNode *args, StmtNode *statements);
extern ExprEvalState gEvalState;
extern bool lookupFunction(const char *fnName, VarNode **args, StmtNode **statements);
typedef const char *(*cfunc)(S32 argc, char **argv);
extern bool lookupCFunction(const char *fnName, cfunc *f);
#endif

335
engine/console/astAlloc.cc Executable file
View File

@ -0,0 +1,335 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/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()
{
BreakStmtNode *ret = (BreakStmtNode *) consoleAlloc(sizeof(BreakStmtNode));
constructInPlace(ret);
return ret;
}
ContinueStmtNode *ContinueStmtNode::alloc()
{
ContinueStmtNode *ret = (ContinueStmtNode *) consoleAlloc(sizeof(ContinueStmtNode));
constructInPlace(ret);
return ret;
}
ReturnStmtNode *ReturnStmtNode::alloc(ExprNode *expr)
{
ReturnStmtNode *ret = (ReturnStmtNode *) consoleAlloc(sizeof(ReturnStmtNode));
constructInPlace(ret);
ret->expr = expr;
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(1);
return ret;
}
FloatBinaryExprNode *FloatBinaryExprNode::alloc(S32 op, ExprNode *left, ExprNode *right)
{
FloatBinaryExprNode *ret = (FloatBinaryExprNode *) consoleAlloc(sizeof(FloatBinaryExprNode));
constructInPlace(ret);
ret->op = op;
ret->left = left;
ret->right = right;
return ret;
}
IntBinaryExprNode *IntBinaryExprNode::alloc(S32 op, ExprNode *left, ExprNode *right)
{
IntBinaryExprNode *ret = (IntBinaryExprNode *) consoleAlloc(sizeof(IntBinaryExprNode));
constructInPlace(ret);
ret->op = op;
ret->left = left;
ret->right = right;
return ret;
}
StreqExprNode *StreqExprNode::alloc(ExprNode *left, ExprNode *right, bool eq)
{
StreqExprNode *ret = (StreqExprNode *) consoleAlloc(sizeof(StreqExprNode));
constructInPlace(ret);
ret->left = left;
ret->right = right;
ret->eq = eq;
return ret;
}
StrcatExprNode *StrcatExprNode::alloc(ExprNode *left, ExprNode *right, int appendChar)
{
StrcatExprNode *ret = (StrcatExprNode *) consoleAlloc(sizeof(StrcatExprNode));
constructInPlace(ret);
ret->left = left;
ret->right = right;
ret->appendChar = appendChar;
return ret;
}
CommaCatExprNode *CommaCatExprNode::alloc(ExprNode *left, ExprNode *right)
{
CommaCatExprNode *ret = (CommaCatExprNode *) consoleAlloc(sizeof(CommaCatExprNode));
constructInPlace(ret);
ret->left = left;
ret->right = right;
return ret;
}
IntUnaryExprNode *IntUnaryExprNode::alloc(S32 op, ExprNode *expr)
{
IntUnaryExprNode *ret = (IntUnaryExprNode *) consoleAlloc(sizeof(IntUnaryExprNode));
constructInPlace(ret);
ret->op = op;
ret->expr = expr;
return ret;
}
FloatUnaryExprNode *FloatUnaryExprNode::alloc(S32 op, ExprNode *expr)
{
FloatUnaryExprNode *ret = (FloatUnaryExprNode *) consoleAlloc(sizeof(FloatUnaryExprNode));
constructInPlace(ret);
ret->op = op;
ret->expr = expr;
return ret;
}
VarNode *VarNode::alloc(StringTableEntry varName, ExprNode *arrayIndex)
{
VarNode *ret = (VarNode *) consoleAlloc(sizeof(VarNode));
constructInPlace(ret);
ret->varName = varName;
ret->arrayIndex = arrayIndex;
return ret;
}
IntNode *IntNode::alloc(S32 value)
{
IntNode *ret = (IntNode *) consoleAlloc(sizeof(IntNode));
constructInPlace(ret);
ret->value = value;
return ret;
}
ConditionalExprNode *ConditionalExprNode::alloc(ExprNode *testExpr, ExprNode *trueExpr, ExprNode *falseExpr)
{
ConditionalExprNode *ret = (ConditionalExprNode *) consoleAlloc(sizeof(ConditionalExprNode));
constructInPlace(ret);
ret->testExpr = testExpr;
ret->trueExpr = trueExpr;
ret->falseExpr = falseExpr;
ret->integer = false;
return ret;
}
FloatNode *FloatNode::alloc(F64 value)
{
FloatNode *ret = (FloatNode *) consoleAlloc(sizeof(FloatNode));
constructInPlace(ret);
ret->value = value;
return ret;
}
StrConstNode *StrConstNode::alloc(char *str, bool tag)
{
StrConstNode *ret = (StrConstNode *) consoleAlloc(sizeof(StrConstNode));
constructInPlace(ret);
ret->str = (char *) consoleAlloc(dStrlen(str) + 1);
ret->tag = tag;
dStrcpy(ret->str, str);
return ret;
}
ConstantNode *ConstantNode::alloc(StringTableEntry value)
{
ConstantNode *ret = (ConstantNode *) consoleAlloc(sizeof(ConstantNode));
constructInPlace(ret);
ret->value = value;
return ret;
}
AssignExprNode *AssignExprNode::alloc(StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr)
{
AssignExprNode *ret = (AssignExprNode *) consoleAlloc(sizeof(AssignExprNode));
constructInPlace(ret);
ret->varName = varName;
ret->expr = expr;
ret->arrayIndex = arrayIndex;
return ret;
}
AssignOpExprNode *AssignOpExprNode::alloc(StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr, S32 op)
{
AssignOpExprNode *ret = (AssignOpExprNode *) consoleAlloc(sizeof(AssignOpExprNode));
constructInPlace(ret);
ret->varName = varName;
ret->expr = expr;
ret->arrayIndex = arrayIndex;
ret->op = op;
return ret;
}
TTagSetStmtNode *TTagSetStmtNode::alloc(StringTableEntry tag, ExprNode *valueExpr, ExprNode *stringExpr)
{
TTagSetStmtNode *ret = (TTagSetStmtNode *) consoleAlloc(sizeof(TTagSetStmtNode));
constructInPlace(ret);
ret->tag = tag;
ret->valueExpr = valueExpr;
ret->stringExpr = stringExpr;
return ret;
}
TTagDerefNode *TTagDerefNode::alloc(ExprNode *expr)
{
TTagDerefNode *ret = (TTagDerefNode *) consoleAlloc(sizeof(TTagDerefNode));
constructInPlace(ret);
ret->expr = expr;
return ret;
}
TTagExprNode *TTagExprNode::alloc(StringTableEntry tag)
{
TTagExprNode *ret = (TTagExprNode *) consoleAlloc(sizeof(TTagExprNode));
constructInPlace(ret);
ret->tag = tag;
return ret;
}
FuncCallExprNode *FuncCallExprNode::alloc(StringTableEntry funcName, StringTableEntry nameSpace, ExprNode *args, bool dot)
{
FuncCallExprNode *ret = (FuncCallExprNode *) consoleAlloc(sizeof(FuncCallExprNode));
constructInPlace(ret);
ret->funcName = funcName;
ret->nameSpace = nameSpace;
ret->args = args;
if(dot)
ret->callType = MethodCall;
else
{
if(nameSpace && !dStricmp(nameSpace, "Parent"))
ret->callType = ParentCall;
else
ret->callType = FunctionCall;
}
return ret;
}
SlotAccessNode *SlotAccessNode::alloc(ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName)
{
SlotAccessNode *ret = (SlotAccessNode *) consoleAlloc(sizeof(SlotAccessNode));
constructInPlace(ret);
ret->objectExpr = objectExpr;
ret->arrayExpr = arrayExpr;
ret->slotName = slotName;
return ret;
}
SlotAssignNode *SlotAssignNode::alloc(ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName, ExprNode *valueExpr)
{
SlotAssignNode *ret = (SlotAssignNode *) consoleAlloc(sizeof(SlotAssignNode));
constructInPlace(ret);
ret->objectExpr = objectExpr;
ret->arrayExpr = arrayExpr;
ret->slotName = slotName;
ret->valueExpr = valueExpr;
return ret;
}
SlotAssignOpNode *SlotAssignOpNode::alloc(ExprNode *objectExpr, StringTableEntry slotName, ExprNode *arrayExpr, S32 op, ExprNode *valueExpr)
{
SlotAssignOpNode *ret = (SlotAssignOpNode *) consoleAlloc(sizeof(SlotAssignOpNode));
constructInPlace(ret);
ret->objectExpr = objectExpr;
ret->arrayExpr = arrayExpr;
ret->slotName = slotName;
ret->op = op;
ret->valueExpr = valueExpr;
return ret;
}
ObjectDeclNode *ObjectDeclNode::alloc(ExprNode *classNameExpr, ExprNode *objectNameExpr, ExprNode *argList, StringTableEntry parentObject, SlotAssignNode *slotDecls, ObjectDeclNode *subObjects, bool structDecl)
{
ObjectDeclNode *ret = (ObjectDeclNode *) consoleAlloc(sizeof(ObjectDeclNode));
constructInPlace(ret);
ret->classNameExpr = classNameExpr;
ret->objectNameExpr = objectNameExpr;
ret->argList = argList;
ret->slotDecls = slotDecls;
ret->subObjects = subObjects;
ret->structDecl = structDecl;
if(parentObject)
ret->parentObject = parentObject;
else
ret->parentObject = StringTable->insert("");
return ret;
}
FunctionDeclStmtNode *FunctionDeclStmtNode::alloc(StringTableEntry fnName, StringTableEntry nameSpace, VarNode *args, StmtNode *stmts)
{
FunctionDeclStmtNode *ret = (FunctionDeclStmtNode *) consoleAlloc(sizeof(FunctionDeclStmtNode));
constructInPlace(ret);
ret->fnName = fnName;
ret->args = args;
ret->stmts = stmts;
ret->nameSpace = nameSpace;
ret->package = NULL;
return ret;
}

1680
engine/console/astNodes.cc Executable file

File diff suppressed because it is too large Load Diff

85
engine/console/basgram.h Executable file
View File

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

6
engine/console/bison.bat Executable file
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

686
engine/console/bison.simple Executable file
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;
{
register char *f = from;
register char *t = to;
register 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)
{
register char *f = from;
register char *t = to;
register 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
{
register int yystate;
register int yyn;
register short *yyssp;
register 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;
}

82
engine/console/cmdgram.h Executable file
View File

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

565
engine/console/codeBlock.cc Executable file
View File

@ -0,0 +1,565 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/compiler.h"
#include "console/codeBlock.h"
#include "console/telnetDebugger.h"
#include "core/resManager.h"
using namespace Compiler;
bool CodeBlock::smInFunction = false;
U32 CodeBlock::smBreakLineCount = 0;
CodeBlock * CodeBlock::smCodeBlockList = NULL;
CodeBlock * CodeBlock::smCurrentCodeBlock = NULL;
ConsoleParser *CodeBlock::smCurrentParser = NULL;
//-------------------------------------------------------------------------
CodeBlock::CodeBlock()
{
globalStrings = NULL;
functionStrings = NULL;
globalFloats = NULL;
functionFloats = NULL;
lineBreakPairs = NULL;
breakList = NULL;
breakListSize = 0;
refCount = 0;
code = NULL;
name = NULL;
mRoot = StringTable->insert("");
}
CodeBlock::~CodeBlock()
{
// Make sure we aren't lingering in the current code block...
AssertFatal(smCurrentCodeBlock != this, "CodeBlock::~CodeBlock - Caught lingering in smCurrentCodeBlock!")
if(name)
removeFromCodeList();
delete[] const_cast<char*>(globalStrings);
delete[] const_cast<char*>(functionStrings);
delete[] globalFloats;
delete[] functionFloats;
delete[] code;
delete[] breakList;
}
//-------------------------------------------------------------------------
StringTableEntry CodeBlock::getCurrentCodeBlockName()
{
if (CodeBlock::getCurrentBlock())
return CodeBlock::getCurrentBlock()->name;
else
return NULL;
}
CodeBlock *CodeBlock::find(StringTableEntry name)
{
for(CodeBlock *walk = CodeBlock::getCodeBlockList(); walk; walk = walk->nextFile)
if(walk->name == name)
return walk;
return NULL;
}
//-------------------------------------------------------------------------
void CodeBlock::addToCodeList()
{
// remove any code blocks with my name
for(CodeBlock **walk = &smCodeBlockList; *walk;walk = &((*walk)->nextFile))
{
if((*walk)->name == name)
{
*walk = (*walk)->nextFile;
break;
}
}
nextFile = smCodeBlockList;
smCodeBlockList = this;
}
void CodeBlock::clearAllBreaks()
{
if(!lineBreakPairs)
return;
for(U32 i = 0; i < lineBreakPairCount; i++)
{
U32 *p = lineBreakPairs + i * 2;
code[p[1]] = p[0] & 0xFF;
}
}
void CodeBlock::clearBreakpoint(U32 lineNumber)
{
if(!lineBreakPairs)
return;
for(U32 i = 0; i < lineBreakPairCount; i++)
{
U32 *p = lineBreakPairs + i * 2;
if((p[0] >> 8) == lineNumber)
{
code[p[1]] = p[0] & 0xFF;
return;
}
}
}
void CodeBlock::setAllBreaks()
{
if(!lineBreakPairs)
return;
for(U32 i = 0; i < lineBreakPairCount; i++)
{
U32 *p = lineBreakPairs + i * 2;
code[p[1]] = OP_BREAK;
}
}
bool CodeBlock::setBreakpoint(U32 lineNumber)
{
if(!lineBreakPairs)
return false;
for(U32 i = 0; i < lineBreakPairCount; i++)
{
U32 *p = lineBreakPairs + i * 2;
if((p[0] >> 8) == lineNumber)
{
code[p[1]] = OP_BREAK;
return true;
}
}
return false;
}
U32 CodeBlock::findFirstBreakLine(U32 lineNumber)
{
if(!lineBreakPairs)
return 0;
for(U32 i = 0; i < lineBreakPairCount; i++)
{
U32 *p = lineBreakPairs + i * 2;
U32 line = (p[0] >> 8);
if( lineNumber <= line )
return line;
}
return 0;
}
struct LinePair
{
U32 instLine;
U32 ip;
};
void CodeBlock::findBreakLine(U32 ip, U32 &line, U32 &instruction)
{
U32 min = 0;
U32 max = lineBreakPairCount - 1;
LinePair *p = (LinePair *) lineBreakPairs;
U32 found;
if(!lineBreakPairCount || p[min].ip > ip || p[max].ip < ip)
{
line = 0;
instruction = OP_INVALID;
return;
}
else if(p[min].ip == ip)
found = min;
else if(p[max].ip == ip)
found = max;
else
{
for(;;)
{
if(min == max - 1)
{
found = min;
break;
}
U32 mid = (min + max) >> 1;
if(p[mid].ip == ip)
{
found = mid;
break;
}
else if(p[mid].ip > ip)
max = mid;
else
min = mid;
}
}
instruction = p[found].instLine & 0xFF;
line = p[found].instLine >> 8;
}
const char *CodeBlock::getFileLine(U32 ip)
{
static char nameBuffer[256];
U32 line, inst;
findBreakLine(ip, line, inst);
dSprintf(nameBuffer, sizeof(nameBuffer), "%s (%d)", name ? name : "<input>", line);
return nameBuffer;
}
void CodeBlock::removeFromCodeList()
{
for(CodeBlock **walk = &smCodeBlockList; *walk; walk = &((*walk)->nextFile))
{
if(*walk == this)
{
*walk = nextFile;
// clear out all breakpoints
clearAllBreaks();
return;
}
}
// Let the telnet debugger know that this code
// block has been unloaded and that it needs to
// remove references to it.
if ( TelDebugger )
TelDebugger->clearCodeBlockPointers( this );
}
void CodeBlock::calcBreakList()
{
U32 size = 0;
S32 line = -1;
U32 seqCount = 0;
U32 i;
for(i = 0; i < lineBreakPairCount; i++)
{
U32 lineNumber = lineBreakPairs[i * 2];
if(lineNumber == U32(line + 1))
seqCount++;
else
{
if(seqCount)
size++;
size++;
seqCount = 1;
}
line = lineNumber;
}
if(seqCount)
size++;
breakList = new U32[size];
breakListSize = size;
line = -1;
seqCount = 0;
size = 0;
for(i = 0; i < lineBreakPairCount; i++)
{
U32 lineNumber = lineBreakPairs[i * 2];
if(lineNumber == U32(line + 1))
seqCount++;
else
{
if(seqCount)
breakList[size++] = seqCount;
breakList[size++] = lineNumber - getMax(0, line) - 1;
seqCount = 1;
}
line = lineNumber;
}
if(seqCount)
breakList[size++] = seqCount;
for(i = 0; i < lineBreakPairCount; i++)
{
U32 *p = lineBreakPairs + i * 2;
p[0] = (p[0] << 8) | code[p[1]];
}
// Let the telnet debugger know that this code
// block has been loaded and that it can add break
// points it has for it.
if ( TelDebugger )
TelDebugger->addAllBreakpoints( this );
}
bool CodeBlock::read(StringTableEntry fileName, Stream &st)
{
name = fileName;
//
if (name)
{
if (const char *slash = dStrchr(this->name, '/'))
{
char root[512];
dStrncpy(root, this->name, slash-this->name);
root[slash-this->name] = 0;
mRoot = StringTable->insert(root);
}
}
//
addToCodeList();
U32 globalSize,size,i;
st.read(&size);
if(size)
{
globalSize = size;
globalStrings = new char[size];
st.read(size, globalStrings);
}
st.read(&size);
if(size)
{
functionStrings = new char[size];
st.read(size, functionStrings);
}
st.read(&size);
if(size)
{
globalFloats = new F64[size];
for(U32 i = 0; i < size; i++)
st.read(&globalFloats[i]);
}
st.read(&size);
if(size)
{
functionFloats = new F64[size];
for(U32 i = 0; i < size; i++)
st.read(&functionFloats[i]);
}
U32 codeSize;
st.read(&codeSize);
st.read(&lineBreakPairCount);
U32 totSize = codeSize + lineBreakPairCount * 2;
code = new U32[totSize];
for(i = 0; i < codeSize; i++)
{
U8 b;
st.read(&b);
if(b == 0xFF)
st.read(&code[i]);
else
code[i] = b;
}
for(i = codeSize; i < totSize; i++)
st.read(&code[i]);
lineBreakPairs = code + codeSize;
// StringTable-ize our identifiers.
U32 identCount;
st.read(&identCount);
while(identCount--)
{
U32 offset;
st.read(&offset);
StringTableEntry ste;
if(offset < globalSize)
ste = StringTable->insert(globalStrings + offset);
else
ste = StringTable->insert("");
U32 count;
st.read(&count);
while(count--)
{
U32 ip;
st.read(&ip);
code[ip] = *((U32 *) &ste);
}
}
if(lineBreakPairCount)
calcBreakList();
return true;
}
bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, const char *script)
{
gSyntaxError = false;
consoleAllocReset();
STEtoU32 = compileSTEtoU32;
statementList = NULL;
// Set up the parser.
smCurrentParser = getParserForFile(fileName);
AssertISV(smCurrentParser, avar("CodeBlock::compile - no parser available for '%s'!", fileName));
// Now do some parsing.
smCurrentParser->setScanBuffer(script, fileName);
smCurrentParser->restart(NULL);
smCurrentParser->parse();
if(gSyntaxError)
{
consoleAllocReset();
return false;
}
FileStream st;
if(!ResourceManager->openFileForWrite(st, codeFileName))
return false;
st.write(U32(Con::DSOVersion));
// Reset all our value tables...
resetTables();
smInFunction = false;
smBreakLineCount = 0;
setBreakCodeBlock(this);
if(statementList)
codeSize = precompileBlock(statementList, 0) + 1;
else
codeSize = 1;
lineBreakPairCount = smBreakLineCount;
code = new U32[codeSize + smBreakLineCount * 2];
lineBreakPairs = code + codeSize;
// Write string table data...
getGlobalStringTable().write(st);
getFunctionStringTable().write(st);
// Write float table data...
getGlobalFloatTable().write(st);
getFunctionFloatTable().write(st);
smBreakLineCount = 0;
U32 lastIp;
if(statementList)
lastIp = compileBlock(statementList, code, 0, 0, 0);
else
lastIp = 0;
if(lastIp != codeSize - 1)
Con::errorf(ConsoleLogEntry::General, "CodeBlock::compile - precompile size mismatch, a precompile/compile function pair is probably mismatched.");
code[lastIp++] = OP_RETURN;
U32 totSize = codeSize + smBreakLineCount * 2;
st.write(codeSize);
st.write(lineBreakPairCount);
// Write out our bytecode, doing a bit of compression for low numbers.
U32 i;
for(i = 0; i < codeSize; i++)
{
if(code[i] < 0xFF)
st.write(U8(code[i]));
else
{
st.write(U8(0xFF));
st.write(code[i]);
}
}
// Write the break info...
for(i = codeSize; i < totSize; i++)
st.write(code[i]);
getIdentTable().write(st);
consoleAllocReset();
st.close();
return true;
}
const char *CodeBlock::compileExec(StringTableEntry fileName, const char *string, bool noCalls, int setFrame)
{
STEtoU32 = evalSTEtoU32;
consoleAllocReset();
name = fileName;
if(name)
addToCodeList();
statementList = NULL;
// Set up the parser.
smCurrentParser = getParserForFile(fileName);
AssertISV(smCurrentParser, avar("CodeBlock::compile - no parser available for '%s'!", fileName));
// Now do some parsing.
smCurrentParser->setScanBuffer(string, fileName);
smCurrentParser->restart(NULL);
smCurrentParser->parse();
if(!statementList)
{
delete this;
return "";
}
resetTables();
smInFunction = false;
smBreakLineCount = 0;
setBreakCodeBlock(this);
codeSize = precompileBlock(statementList, 0) + 1;
lineBreakPairCount = smBreakLineCount;
globalStrings = getGlobalStringTable().build();
functionStrings = getFunctionStringTable().build();
globalFloats = getGlobalFloatTable().build();
functionFloats = getFunctionFloatTable().build();
code = new U32[codeSize + lineBreakPairCount * 2];
lineBreakPairs = code + codeSize;
smBreakLineCount = 0;
U32 lastIp = compileBlock(statementList, code, 0, 0, 0);
code[lastIp++] = OP_RETURN;
consoleAllocReset();
if(lineBreakPairCount && fileName)
calcBreakList();
if(lastIp != codeSize)
Con::warnf(ConsoleLogEntry::General, "precompile size mismatch");
return exec(0, fileName, NULL, 0, 0, noCalls, NULL, setFrame);
}
//-------------------------------------------------------------------------
void CodeBlock::incRefCount()
{
refCount++;
}
void CodeBlock::decRefCount()
{
refCount--;
if(!refCount)
delete this;
}

129
engine/console/codeBlock.h Executable file
View File

@ -0,0 +1,129 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _CODEBLOCK_H_
#define _CODEBLOCK_H_
#include "console/compiler.h"
#include "console/consoleParser.h"
class Stream;
/// Core TorqueScript code management class.
///
/// This class represents a block of code, usually mapped directly to a file.
class CodeBlock
{
private:
static CodeBlock* smCodeBlockList;
static CodeBlock* smCurrentCodeBlock;
public:
static U32 smBreakLineCount;
static bool smInFunction;
static Compiler::ConsoleParser * smCurrentParser;
static CodeBlock* getCurrentBlock()
{
return smCurrentCodeBlock;
}
static CodeBlock *getCodeBlockList()
{
return smCodeBlockList;
}
static StringTableEntry getCurrentCodeBlockName();
static CodeBlock *find(StringTableEntry);
CodeBlock();
~CodeBlock();
StringTableEntry name;
char *globalStrings;
char *functionStrings;
F64 *globalFloats;
F64 *functionFloats;
U32 codeSize;
U32 *code;
U32 refCount;
U32 lineBreakPairCount;
U32 *lineBreakPairs;
U32 breakListSize;
U32 *breakList;
CodeBlock *nextFile;
StringTableEntry mRoot;
void addToCodeList();
void removeFromCodeList();
void calcBreakList();
void clearAllBreaks();
void setAllBreaks();
/// Returns the first breakable line or 0 if none was found.
/// @param lineNumber The one based line number.
U32 findFirstBreakLine(U32 lineNumber);
void clearBreakpoint(U32 lineNumber);
/// 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);
void findBreakLine(U32 ip, U32 &line, U32 &instruction);
void getFunctionArgs(char buffer[1024], U32 offset);
const char *getFileLine(U32 ip);
bool read(StringTableEntry fileName, Stream &st);
bool compile(const char *dsoName, StringTableEntry fileName, const char *script);
void incRefCount();
void decRefCount();
/// 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.
const char *compileExec(StringTableEntry fileName, const char *script,
bool noCalls, int 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.
const char *exec(U32 offset, const char *fnName, Namespace *ns, U32 argc,
const char **argv, bool noCalls, StringTableEntry packageName,
S32 setFrame = -1);
};
#endif

1205
engine/console/compiledEval.cc Executable file

File diff suppressed because it is too large Load Diff

271
engine/console/compiler.cc Executable file
View File

@ -0,0 +1,271 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "console/console.h"
#include "console/telnetDebugger.h"
#include "platform/event.h"
#include "console/ast.h"
#include "core/tAlgorithm.h"
#include "core/resManager.h"
#include "core/findMatch.h"
#include "console/consoleInternal.h"
#include "core/fileStream.h"
#include "console/compiler.h"
#include "console/simBase.h"
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;
CodeBlock *gCurBreakBlock;
//------------------------------------------------------------
CodeBlock *getBreakCodeBlock() { return gCurBreakBlock; }
void setBreakCodeBlock(CodeBlock *cb) { gCurBreakBlock = cb; }
//------------------------------------------------------------
U32 evalSTEtoU32(StringTableEntry ste, U32)
{
return *((U32 *) &ste);
}
U32 compileSTEtoU32(StringTableEntry ste, U32 ip)
{
if(ste)
getIdentTable().add(ste, ip);
return 0;
}
U32 (*STEtoU32)(StringTableEntry ste, U32 ip) = evalSTEtoU32;
//------------------------------------------------------------
bool gSyntaxError = false;
//------------------------------------------------------------
CompilerStringTable *getCurrentStringTable() { return gCurrentStringTable; }
CompilerStringTable &getGlobalStringTable() { return gGlobalStringTable; }
CompilerStringTable &getFunctionStringTable() { return gFunctionStringTable; }
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();
}
void *consoleAlloc(U32 size) { return gConsoleAllocator.alloc(size); }
void consoleAllocReset() { gConsoleAllocator.freeBlocks(); }
}
//-------------------------------------------------------------------------
using namespace Compiler;
//-------------------------------------------------------------------------
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(!dStrcmp((*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);
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];
for(Entry *walk = list; walk; walk = walk->next)
dStrcpy(ret + walk->start, walk->string);
return ret;
}
void CompilerStringTable::write(Stream &st)
{
st.write(totalLen);
for(Entry *walk = list; walk; walk = walk->next)
st.write(walk->len, walk->string);
}
//------------------------------------------------------------
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);
}
}

235
engine/console/compiler.h Executable file
View File

@ -0,0 +1,235 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _COMPILER_H_
#define _COMPILER_H_
class Stream;
class DataChunker;
#include "platform/platform.h"
#include "console/ast.h"
#include "console/codeBlock.h"
// Autogenerated, so we should only ever include from once place - here.
// (We can't stick include guards in it without patching bison.)
#ifndef _CMDGRAM_H_
#define _CMDGRAM_H_
#include "console/cmdgram.h"
#endif
namespace Compiler
{
/// The opcodes for the TorqueScript VM.
enum CompiledInstructions
{
OP_FUNC_DECL,
OP_CREATE_OBJECT,
OP_ADD_OBJECT,
OP_END_OBJECT,
OP_JMPIFFNOT,
OP_JMPIFNOT,
OP_JMPIFF,
OP_JMPIF,
OP_JMPIFNOT_NP,
OP_JMPIF_NP,
OP_JMP,
OP_RETURN,
OP_CMPEQ,
OP_CMPGR,
OP_CMPGE,
OP_CMPLT,
OP_CMPLE,
OP_CMPNE,
OP_XOR,
OP_MOD,
OP_BITAND,
OP_BITOR,
OP_NOT,
OP_NOTF,
OP_ONESCOMPLEMENT,
OP_SHR,
OP_SHL,
OP_AND,
OP_OR,
OP_ADD,
OP_SUB,
OP_MUL,
OP_DIV,
OP_NEG,
OP_SETCURVAR,
OP_SETCURVAR_CREATE,
OP_SETCURVAR_ARRAY,
OP_SETCURVAR_ARRAY_CREATE,
OP_LOADVAR_UINT,
OP_LOADVAR_FLT,
OP_LOADVAR_STR,
OP_SAVEVAR_UINT,
OP_SAVEVAR_FLT,
OP_SAVEVAR_STR,
OP_SETCUROBJECT,
OP_SETCUROBJECT_NEW,
OP_SETCURFIELD,
OP_SETCURFIELD_ARRAY,
OP_LOADFIELD_UINT,
OP_LOADFIELD_FLT,
OP_LOADFIELD_STR,
OP_SAVEFIELD_UINT,
OP_SAVEFIELD_FLT,
OP_SAVEFIELD_STR,
OP_STR_TO_UINT,
OP_STR_TO_FLT,
OP_STR_TO_NONE,
OP_FLT_TO_UINT,
OP_FLT_TO_STR,
OP_FLT_TO_NONE,
OP_UINT_TO_FLT,
OP_UINT_TO_STR,
OP_UINT_TO_NONE,
OP_LOADIMMED_UINT,
OP_LOADIMMED_FLT,
OP_TAG_TO_STR,
OP_LOADIMMED_STR,
OP_LOADIMMED_IDENT,
OP_CALLFUNC_RESOLVE,
OP_CALLFUNC,
OP_ADVANCE_STR,
OP_ADVANCE_STR_APPENDCHAR,
OP_ADVANCE_STR_COMMA,
OP_ADVANCE_STR_NUL,
OP_REWIND_STR,
OP_TERMINATE_REWIND_STR,
OP_COMPARE_STR,
OP_PUSH,
OP_PUSH_FRAME,
OP_BREAK,
OP_INVALID
};
//------------------------------------------------------------
F64 consoleStringToNumber(const char *str, StringTableEntry file = 0, U32 line = 0);
U32 precompileBlock(StmtNode *block, U32 loopCount);
U32 compileBlock(StmtNode *block, U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
//------------------------------------------------------------
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];
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 U32toSTE(U32 u)
{
return *((StringTableEntry *) &u);
}
extern U32 (*STEtoU32)(StringTableEntry ste, U32 ip);
U32 evalSTEtoU32(StringTableEntry ste, U32);
U32 compileSTEtoU32(StringTableEntry ste, U32 ip);
CompilerStringTable *getCurrentStringTable();
CompilerStringTable &getGlobalStringTable();
CompilerStringTable &getFunctionStringTable();
void setCurrentStringTable (CompilerStringTable* cst);
CompilerFloatTable *getCurrentFloatTable();
CompilerFloatTable &getGlobalFloatTable();
CompilerFloatTable &getFunctionFloatTable();
void setCurrentFloatTable (CompilerFloatTable* cst);
CompilerIdentTable &getIdentTable();
void precompileIdent(StringTableEntry ident);
CodeBlock *getBreakCodeBlock();
void setBreakCodeBlock(CodeBlock *cb);
/// Helper function to reset the float, string, and ident tables to a base
/// starting state.
void resetTables();
void *consoleAlloc(U32 size);
void consoleAllocReset();
extern bool gSyntaxError;
};
#endif

1043
engine/console/console.cc Executable file

File diff suppressed because it is too large Load Diff

808
engine/console/console.h Executable file
View File

@ -0,0 +1,808 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _CONSOLE_H_
#define _CONSOLE_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _BITSET_H_
#include "core/bitSet.h"
#endif
#include <stdarg.h>
class SimObject;
struct EnumTable;
class Namespace;
/// Indicates that warnings about undefined script variables should be displayed.
///
/// @note This is set and controlled by script.
extern bool gWarnUndefinedScriptVariables;
enum StringTableConstants
{
StringTagPrefixByte = 0x01 ///< Magic value prefixed to tagged strings.
};
/// Represents an entry in the log.
struct ConsoleLogEntry
{
/// This field indicates the severity of the log entry.
///
/// Log entries are filtered and displayed differently based on
/// their severity. Errors are highlighted red, while normal entries
/// are displayed as normal text. Often times, the engine will be
/// configured to hide all log entries except warnings or errors,
/// or to perform a special notification when it encounters an error.
enum Level
{
Normal = 0,
Warning,
Error,
NUM_CLASS
} mLevel;
/// Used to associate a log entry with a module.
///
/// Log entries can come from different sources; for instance,
/// the scripting engine, or the network code. This allows the
/// logging system to be aware of where different log entries
/// originated from.
enum Type
{
General = 0,
Assert,
Script,
GUI,
Network,
NUM_TYPE
} mType;
/// Indicates the actual log entry.
///
/// This contains a description of the event being logged.
/// For instance, "unable to access file", or "player connected
/// successfully", or nearly anything else you might imagine.
///
/// Typically, the description should contain a concise, descriptive
/// string describing whatever is being logged. Whenever possible,
/// include useful details like the name of the file being accessed,
/// or the id of the player or GuiControl, so that if a log needs
/// to be used to locate a bug, it can be done as painlessly as
/// possible.
const char *mString;
};
/// Scripting engine representation of an enum.
///
/// This data structure is used by the scripting engine
/// to expose enumerations to the scripting language. It
/// acts to relate named constants to integer values, just
/// like an enum in C++.
struct EnumTable
{
/// Number of enumerated items in the table.
S32 size;
/// This represents a specific item in the enumeration.
struct Enums
{
S32 index; ///< Index label maps to.
const char *label;///< Label for this index.
};
Enums *table;
/// Constructor.
///
/// This sets up the EnumTable with predefined data.
///
/// @param sSize Size of the table.
/// @param sTable Pointer to table of Enums.
///
/// @see gLiquidTypeTable
/// @see gAlignTable
EnumTable(S32 sSize, Enums *sTable)
{ size = sSize; table = sTable; }
};
typedef const char *StringTableEntry;
/// @defgroup console_callbacks Scripting Engine Callbacks
///
/// The scripting engine makes heavy use of callbacks to represent
/// function exposed to the scripting language. StringCallback,
/// IntCallback, FloatCallback, VoidCallback, and BoolCallback all
/// represent exposed script functions returning different types.
///
/// ConsumerCallback is used with the function Con::addConsumer; functions
/// registered with Con::addConsumer are called whenever something is outputted
/// to the console. For instance, the TelnetConsole registers itself with the
/// console so it can echo the console over the network.
///
/// @note Callbacks to the scripting language - for instance, onExit(), which is
/// a script function called when the engine is shutting down - are handled
/// using Con::executef() and kin.
/// @{
///
typedef const char * (*StringCallback)(SimObject *obj, S32 argc, const char *argv[]);
typedef S32 (*IntCallback)(SimObject *obj, S32 argc, const char *argv[]);
typedef F32 (*FloatCallback)(SimObject *obj, S32 argc, const char *argv[]);
typedef void (*VoidCallback)(SimObject *obj, S32 argc, const char *argv[]); // We have it return a value so things don't break..
typedef bool (*BoolCallback)(SimObject *obj, S32 argc, const char *argv[]);
typedef void (*ConsumerCallback)(ConsoleLogEntry::Level level, const char *consoleLine);
/// @}
/// @defgroup console_types Scripting Engine Type Functions
///
/// @see Con::registerType
/// @{
typedef const char* (*GetDataFunction)(void *dptr, EnumTable *tbl, BitSet32 flag);
typedef void (*SetDataFunction)(void *dptr, S32 argc, const char **argv, EnumTable *tbl, BitSet32 flag);
/// @}
/// This namespace contains the core of the console functionality.
///
/// @section con_intro Introduction
///
/// The console is a key part of Torque's architecture. It allows direct run-time control
/// of many aspects of the engine.
///
/// @nosubgrouping
namespace Con
{
/// Various configuration constants.
enum Constants
{
/// This is the version number associated with DSO files.
///
/// If you make any changes to the way the scripting language works
/// (such as DSO format changes, adding/removing op-codes) that would
/// break compatibility, then you should increment this.
///
/// If you make a really major change, increment it to the next multiple
/// of ten.
///
/// 12/29/04 - BJG - 33->34 Removed some opcodes, part of namespace upgrade.
/// 12/30/04 - BJG - 34->35 Reordered some things, further general shuffling.
/// 11/03/05 - BJG - 35->36 Integrated new debugger code.
DSOVersion = 36,
MaxLineLength = 512, ///< Maximum length of a line of console input.
MaxDataTypes = 256 ///< Maximum number of registered data types.
};
/// @name Control Functions
///
/// The console must be initialized and shutdown appropriately during the
/// lifetime of the app. These functions are used to manage this behavior.
///
/// @note Torque deals with this aspect of console management, so you don't need
/// to call these functions in normal usage of the engine.
/// @{
/// Initializes the console.
///
/// This performs the following steps:
/// - Calls Namespace::init() to initialize the scripting namespace hierarchy.
/// - Calls ConsoleConstructor::setup() to initialize globally defined console
/// methods and functions.
/// - Registers some basic global script variables.
/// - Calls AbstractClassRep::init() to initialize Torque's class database.
/// - Registers some basic global script functions that couldn't usefully
/// be defined anywhere else.
void init();
/// Shuts down the console.
///
/// This performs the following steps:
/// - Closes the console log file.
/// - Calls Namespace::shutdown() to shut down the scripting namespace hierarchy.
void shutdown();
/// Is the console active at this time?
bool isActive();
/// @}
/// @name Console Consumers
///
/// The console distributes its output through Torque by using
/// consumers. Every time a new line is printed to the console,
/// all the ConsumerCallbacks registered using addConsumer are
/// called, in order.
///
/// @note The GuiConsole control, which provides the on-screen
/// in-game console, uses a different technique to render
/// the console. It calls getLockLog() to lock the Vector
/// of on-screen console entries, then it renders them as
/// needed. While the Vector is locked, the console will
/// not change the Vector. When the GuiConsole control is
/// done with the console entries, it calls unlockLog()
/// to tell the console that it is again safe to modify
/// the Vector.
///
/// @see TelnetConsole
/// @see TelnetDebugger
/// @see WinConsole
/// @see MacCarbConsole
/// @see StdConsole
/// @see ConsoleLogger
///
/// @{
void addConsumer(ConsumerCallback cb);
void removeConsumer(ConsumerCallback cb);
/// @}
/// @name Miscellaneous
/// @{
/// Remove color marking information from a string.
///
/// @note It does this in-place, so be careful! It may
/// potentially blast data if you're not careful.
/// When in doubt, make a copy of the string first.
void stripColorChars(char* line);
/// Convert from a relative script path to an absolute script path.
///
/// This is used in (among other places) the exec() script function, which
/// takes a parameter indicating a script file and executes it. Script paths
/// can be one of:
/// - <b>Absolute:</b> <i>fps/foo/bar.cs</i> Paths of this sort are passed
/// through.
/// - <b>Mod-relative:</b> <i>~/foo/bar.cs</i> Paths of this sort have their
/// replaced with the name of the current mod.
/// - <b>File-relative:</b> <i>./baz/blip.cs</i> Paths of this sort are
/// calculated relative to the path of the current scripting file.
///
/// @note This function determines paths relative to the currently executing
/// CodeBlock. Calling it outside of script execution will result in
/// it directly copying src to filename, since it won't know to what the
/// path is relative!
///
/// @param filename Pointer to string buffer to fill with absolute path.
/// @param size Size of buffer pointed to by filename.
/// @param src Original, possibly relative script path.
bool expandScriptFilename(char *filename, U32 size, const char *src);
/// Returns true if fn is a global scripting function.
///
/// This looks in the global namespace. It also checks to see if fn
/// is in the StringTable; if not, it returns false.
bool isFunction(const char *fn);
/// This is the basis for tab completion in the console.
///
/// @note This is an internally used function. You probably don't
/// care much about how this works.
///
/// This function does some basic parsing to try to ascertain the namespace in which
/// we are attempting to do tab completion, then bumps control off to the appropriate
/// tabComplete function, either in SimObject or Namespace.
///
/// @param inputBuffer Pointer to buffer containing starting data, or last result.
/// @param cursorPos Location of cursor in this buffer. This is used to indicate
/// what part of the string should be kept and what part should
/// be advanced to the next match if any.
/// @param maxResultLength Maximum amount of result data to put into inputBuffer. This
/// is capped by MaxCompletionBufferSize.
/// @param forwardTab Should we go forward to next match or backwards to previous
/// match? True indicates forward.
U32 tabComplete(char* inputBuffer, U32 cursorPos, U32 maxResultLength, bool forwardTab);
/// @}
/// @name Variable Management
/// @{
/// Add a console variable that references the value of a variable in C++ code.
///
/// If a value is assigned to the console variable the C++ variable is updated,
/// and vice-versa.
///
/// @param name Global console variable name to create
/// @param type The type of the C++ variable; see the ConsoleDynamicTypes enum for a complete list.
/// @param pointer Pointer to the variable.
/// @see ConsoleDynamicTypes
bool addVariable(const char *name, S32 type, void *pointer);
/// Remove a console variable.
///
/// @param name Global console variable name to remove
/// @return true if variable existed before removal.
bool removeVariable(const char *name);
/// Assign a string value to a locally scoped console variable
///
/// @note The context of the variable is determined by gEvalState; that is,
/// by the currently executing code.
///
/// @param name Local console variable name to set
/// @param value String value to assign to name
void setLocalVariable(const char *name, const char *value);
/// Retrieve the string value to a locally scoped console variable
///
/// @note The context of the variable is determined by gEvalState; that is,
/// by the currently executing code.
///
/// @param name Local console variable name to get
const char* getLocalVariable(const char* name);
/// @}
/// @name Global Variable Accessors
/// @{
/// Assign a string value to a global console variable
/// @param name Global console variable name to set
/// @param value String value to assign to this variable.
void setVariable(const char *name, const char *value);
/// Retrieve the string value of a global console variable
/// @param name Global Console variable name to query
/// @return The string value of the variable or "" if the variable does not exist.
const char* getVariable(const char* name);
/// Same as setVariable(), but for bools.
void setBoolVariable (const char* name,bool var);
/// Same as getVariable(), but for bools.
///
/// @param name Name of the variable.
/// @param def Default value to supply if no matching variable is found.
bool getBoolVariable (const char* name,bool def = false);
/// Same as setVariable(), but for ints.
void setIntVariable (const char* name,S32 var);
/// Same as getVariable(), but for ints.
///
/// @param name Name of the variable.
/// @param def Default value to supply if no matching variable is found.
S32 getIntVariable (const char* name,S32 def = 0);
/// Same as setVariable(), but for floats.
void setFloatVariable(const char* name,F32 var);
/// Same as getVariable(), but for floats.
///
/// @param name Name of the variable.
/// @param def Default value to supply if no matching variable is found.
F32 getFloatVariable(const char* name,F32 def = .0f);
/// @}
/// @name Global Function Registration
/// @{
/// Register a C++ function with the console making it a global function callable from the scripting engine.
///
/// @param name Name of the new function.
/// @param cb Pointer to the function implementing the scripting call; a console callback function returning a specific type value.
/// @param usage Documentation for this function. @ref console_autodoc
/// @param minArgs Minimum number of arguments this function accepts
/// @param maxArgs Maximum number of arguments this function accepts
void addCommand(const char *name, StringCallback cb, const char *usage, S32 minArgs, S32 maxArgs);
void addCommand(const char *name, IntCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char *, StringCallback, const char *, S32, S32)
void addCommand(const char *name, FloatCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char *, StringCallback, const char *, S32, S32)
void addCommand(const char *name, VoidCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char *, StringCallback, const char *, S32, S32)
void addCommand(const char *name, BoolCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char *, StringCallback, const char *, S32, S32)
/// @}
/// @name Namespace Function Registration
/// @{
/// Register a C++ function with the console making it callable
/// as a method of the given namespace from the scripting engine.
///
/// @param nameSpace Name of the namespace to associate the new function with; this is usually the name of a class.
/// @param name Name of the new function.
/// @param cb Pointer to the function implementing the scripting call; a console callback function returning a specific type value.
/// @param usage Documentation for this function. @ref console_autodoc
/// @param minArgs Minimum number of arguments this function accepts
/// @param maxArgs Maximum number of arguments this function accepts
void addCommand(const char *nameSpace, const char *name,StringCallback cb, const char *usage, S32 minArgs, S32 maxArgs);
void addCommand(const char *nameSpace, const char *name,IntCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char*, const char *, StringCallback, const char *, S32, S32)
void addCommand(const char *nameSpace, const char *name,FloatCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char*, const char *, StringCallback, const char *, S32, S32)
void addCommand(const char *nameSpace, const char *name,VoidCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char*, const char *, StringCallback, const char *, S32, S32)
void addCommand(const char *nameSpace, const char *name,BoolCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char*, const char *, StringCallback, const char *, S32, S32)
/// @}
/// @name Special Purpose Registration
///
/// These are special-purpose functions that exist to allow commands to be grouped, so
/// that when we generate console docs, they can be more meaningfully presented.
///
/// @ref console_autodoc "Click here for more information about console docs and grouping."
///
/// @{
void markCommandGroup (const char * nsName, const char *name, const char* usage=NULL);
void beginCommandGroup(const char * nsName, const char *name, const char* usage);
void endCommandGroup (const char * nsName, const char *name);
/// @deprecated
void addOverload (const char * nsName, const char *name, const char *altUsage);
/// @}
/// @name Console Output
///
/// These functions process the formatted string and pass it to all the ConsumerCallbacks that are
/// currently registered. The console log file and the console window callbacks are installed by default.
///
/// @see addConsumer()
/// @see removeConsumer()
/// @{
/// @param _format A stdlib printf style formatted out put string
/// @param ... Variables to be written
void printf(const char *_format, ...);
/// @note The console window colors warning text as LIGHT GRAY.
/// @param _format A stdlib printf style formatted out put string
/// @param ... Variables to be written
void warnf(const char *_format, ...);
/// @note The console window colors warning text as RED.
/// @param _format A stdlib printf style formatted out put string
/// @param ... Variables to be written
void errorf(const char *_format, ...);
/// @note The console window colors warning text as LIGHT GRAY.
/// @param type Allows you to associate the warning message with an internal module.
/// @param _format A stdlib printf style formatted out put string
/// @param ... Variables to be written
/// @see Con::warnf()
void warnf(ConsoleLogEntry::Type type, const char *_format, ...);
/// @note The console window colors warning text as RED.
/// @param type Allows you to associate the warning message with an internal module.
/// @param _format A stdlib printf style formatted out put string
/// @param ... Variables to be written
/// @see Con::errorf()
void errorf(ConsoleLogEntry::Type type, const char *_format, ...);
/// @}
/// Returns true when called from the main thread, false otherwise
bool isMainThread();
/// @name Console Execution
///
/// These are functions relating to the execution of script code.
///
/// @{
/// Call a script function from C/C++ code.
///
/// @param argc Number of elements in the argv parameter
/// @param argv A character string array containing the name of the function
/// to call followed by the arguments to that function.
/// @code
/// // Call a Torque script function called mAbs, having one parameter.
/// char* argv[] = {"abs", "-9"};
/// char* result = execute(2, argv);
/// @endcode
const char *execute(S32 argc, const char* argv[]);
/// @see execute(S32 argc, const char* argv[])
const char *executef(S32 argc, ...);
/// Call a Torque Script member function of a SimObject from C/C++ code.
/// @param object Object on which to execute the method call.
/// @param argc Number of elements in the argv parameter (must be >2, see argv)
/// @param argv A character string array containing the name of the member function
/// to call followed by an empty parameter (gets filled with object ID)
/// followed by arguments to that function.
/// @code
/// // Call the method setMode() on an object, passing it one parameter.
///
/// char* argv[] = {"setMode", "", "2"};
/// char* result = execute(mysimobject, 3, argv);
/// @endcode
const char *execute(SimObject *object, S32 argc, const char *argv[]);
/// @see execute(SimObject *, S32 argc, const char *argv[])
const char *executef(SimObject *, S32 argc, ...);
/// Evaluate an arbitrary chunk of code.
///
/// @param string Buffer containing code to execute.
/// @param echo Should we echo the string to the console?
/// @param fileName Indicate what file this code is coming from; used in error reporting and such.
const char *evaluate(const char* string, bool echo = false, const char *fileName = NULL);
/// Evaluate an arbitrary line of script.
///
/// This wraps dVsprintf(), so you can substitute parameters into the code being executed.
const char *evaluatef(const char* string, ...);
/// @}
/// @name Console Function Implementation Helpers
///
/// The functions Con::getIntArg, Con::getFloatArg and Con::getArgBuffer(size) are used to
/// allocate on the console stack string variables that will be passed into the next console
// function called. This allows the console to avoid copying some data.
///
/// getReturnBuffer lets you allocate stack space to return data in.
/// @{
///
char *getReturnBuffer(U32 bufferSize);
char *getArgBuffer(U32 bufferSize);
char *getFloatArg(F64 arg);
char *getIntArg (S32 arg);
/// @}
/// @name Namespaces
/// @{
Namespace *lookupNamespace(const char *nsName);
bool linkNamespaces(const char *parentName, const char *childName);
bool unlinkNamespaces(const char *parentName, const char *childName);
/// @note This should only be called from consoleObject.h
bool classLinkNamespaces(Namespace *parent, Namespace *child);
/// @}
/// @name Logging
/// @{
void getLockLog(ConsoleLogEntry * &log, U32 &size);
void unlockLog(void);
void setLogMode(S32 mode);
/// @}
/// @name Dynamic Type System
/// @{
///
/* void registerType( const char *typeName, S32 type, S32 size, GetDataFunction gdf, SetDataFunction sdf, bool isDatablockType = false );
void registerType( const char* typeName, S32 type, S32 size, bool isDatablockType = false );
void registerTypeGet( S32 type, GetDataFunction gdf );
void registerTypeSet( S32 type, SetDataFunction sdf );
const char *getTypeName(S32 type);
bool isDatablockType( S32 type ); */
void setData(S32 type, void *dptr, S32 index, S32 argc, const char **argv, EnumTable *tbl = NULL, BitSet32 flag = 0);
const char *getData(S32 type, void *dptr, S32 index, EnumTable *tbl = NULL, BitSet32 flag = 0);
/// @}
};
extern void expandEscape(char *dest, const char *src);
extern bool collapseEscape(char *buf);
extern S32 HashPointer(StringTableEntry ptr);
/// This is the backend for the ConsoleMethod()/ConsoleFunction() macros.
///
/// See the group ConsoleConstructor Innards for specifics on how this works.
///
/// @see @ref console_autodoc
/// @nosubgrouping
class ConsoleConstructor
{
public:
/// @name Entry Type Fields
///
/// One of these is set based on the type of entry we want
/// inserted in the console.
///
/// @ref console_autodoc
/// @{
StringCallback sc; ///< A function/method that returns a string.
IntCallback ic; ///< A function/method that returns an int.
FloatCallback fc; ///< A function/method that returns a float.
VoidCallback vc; ///< A function/method that returns nothing.
BoolCallback bc; ///< A function/method that returns a bool.
bool group; ///< Indicates that this is a group marker.
bool overload; ///< Indicates that this is an overload marker.
/// @deprecated Unused.
/// @}
/// Minimum/maximum number of arguments for the function.
S32 mina, maxa;
const char *usage; ///< Usage string.
const char *funcName; ///< Function name.
const char *className; ///< Class name.
/// @name ConsoleConstructer Innards
///
/// The ConsoleConstructor class is used as the backend for the ConsoleFunction() and
/// ConsoleMethod() macros. The way it works takes advantage of several properties of
/// C++.
///
/// The ConsoleFunction()/ConsoleMethod() macros wrap the declaration of a ConsoleConstructor.
///
/// @code
/// // The definition of a ConsoleFunction using the macro
/// ConsoleFunction(ExpandFilename, const char*, 2, 2, "(string filename)")
/// {
/// argc;
/// char* ret = Con::getReturnBuffer( 1024 );
/// Con::expandScriptFilename(ret, 1024, argv[1]);
/// return ret;
/// }
///
/// // Resulting code
/// static const char* cExpandFilename(SimObject *, S32, const char **argv);
/// static ConsoleConstructor
/// gExpandFilenameobj(NULL,"ExpandFilename", cExpandFilename,
/// "(string filename)", 2, 2);
/// static const char* cExpandFilename(SimObject *, S32 argc, const char **argv)
/// {
/// argc;
/// char* ret = Con::getReturnBuffer( 1024 );
/// Con::expandScriptFilename(ret, 1024, argv[1]);
/// return ret;
/// }
///
/// // A similar thing happens when you do a ConsoleMethod.
/// @endcode
///
/// As you can see, several global items are defined when you use the ConsoleFunction method.
/// The macro constructs the name of these items from the parameters you passed it. Your
/// implementation of the console function is is placed in a function with a name based on
/// the actual name of the console funnction. In addition, a ConsoleConstructor is declared.
///
/// Because it is defined as a global, the constructor for the ConsoleConstructor is called
/// before execution of main() is started. The constructor is called once for each global
/// ConsoleConstructor variable, in the order in which they were defined (this property only holds true
/// within file scope).
///
/// We have ConsoleConstructor create a linked list at constructor time, by storing a static
/// pointer to the head of the list, and keeping a pointer to the next item in each instance
/// of ConsoleConstructor. init() is a helper function in this process, automatically filling
/// in commonly used fields and updating first and next as needed. In this way, a list of
/// items to add to the console is assemble in memory, ready for use, before we start
/// execution of the program proper.
///
/// In Con::init(), ConsoleConstructor::setup() is called to process this prepared list. Each
/// item in the list is iterated over, and the appropriate Con namespace functions (usually
/// Con::addCommand) are invoked to register the ConsoleFunctions and ConsoleMethods in
/// the appropriate namespaces.
///
/// @see Namespace
/// @see Con
/// @{
ConsoleConstructor *next;
static ConsoleConstructor *first;
void init(const char *cName, const char *fName, const char *usg, S32 minArgs, S32 maxArgs);
static void setup();
/// @}
/// @name Basic Console Constructors
/// @{
ConsoleConstructor(const char *className, const char *funcName, StringCallback sfunc, const char* usage, S32 minArgs, S32 maxArgs);
ConsoleConstructor(const char *className, const char *funcName, IntCallback ifunc, const char* usage, S32 minArgs, S32 maxArgs);
ConsoleConstructor(const char *className, const char *funcName, FloatCallback ffunc, const char* usage, S32 minArgs, S32 maxArgs);
ConsoleConstructor(const char *className, const char *funcName, VoidCallback vfunc, const char* usage, S32 minArgs, S32 maxArgs);
ConsoleConstructor(const char *className, const char *funcName, BoolCallback bfunc, const char* usage, S32 minArgs, S32 maxArgs);
/// @}
/// @name Magic Console Constructors
///
/// These perform various pieces of "magic" related to consoleDoc functionality.
/// @ref console_autodoc
/// @{
/// Indicates a group marker. (A doxygen illusion)
///
/// @see Con::markCommandGroup
/// @ref console_autodoc
ConsoleConstructor(const char *className, const char *groupName, const char* usage);
/// @}
};
/// @name Global Console Definition Macros
///
/// @note If TORQUE_DEBUG is defined, then we gather documentation information, and
/// do some extra sanity checks.
///
/// @see ConsoleConstructor
/// @ref console_autodoc
/// @{
// O hackery of hackeries
#define conmethod_return_const return (const
#define conmethod_return_S32 return (S32
#define conmethod_return_F32 return (F32
#define conmethod_nullify(val)
#define conmethod_return_void conmethod_nullify(void
#define conmethod_return_bool return (bool
#if !defined(TORQUE_SHIPPING)
// Console function macros
# define ConsoleFunctionGroupBegin(groupName, usage) \
static ConsoleConstructor gConsoleFunctionGroup##groupName##__GroupBegin(NULL,#groupName,usage);
# define ConsoleFunction(name,returnType,minArgs,maxArgs,usage1) \
static returnType c##name(SimObject *, S32, const char **argv); \
static ConsoleConstructor g##name##obj(NULL,#name,c##name,usage1,minArgs,maxArgs); \
static returnType c##name(SimObject *, S32 argc, const char **argv)
# define ConsoleFunctionGroupEnd(groupName) \
static ConsoleConstructor gConsoleFunctionGroup##groupName##__GroupEnd(NULL,#groupName,NULL);
// Console method macros
# define ConsoleMethodGroupBegin(className, groupName, usage) \
static ConsoleConstructor className##groupName##__GroupBegin(#className,#groupName,usage);
# define ConsoleMethod(className,name,returnType,minArgs,maxArgs,usage1) \
static inline returnType c##className##name(className *, S32, const char **argv); \
static returnType c##className##name##caster(SimObject *object, S32 argc, const char **argv) { \
AssertFatal( dynamic_cast<className*>( object ), "Object passed to " #name " is not a " #className "!" ); \
conmethod_return_##returnType ) c##className##name(static_cast<className*>(object),argc,argv); \
}; \
static ConsoleConstructor className##name##obj(#className,#name,c##className##name##caster,usage1,minArgs,maxArgs); \
static inline returnType c##className##name(className *object, S32 argc, const char **argv)
# define ConsoleStaticMethod(className,name,returnType,minArgs,maxArgs,usage1) \
static inline returnType c##className##name(S32, const char **); \
static returnType c##className##name##caster(SimObject *object, S32 argc, const char **argv) { \
conmethod_return_##returnType ) c##className##name(argc,argv); \
}; \
static ConsoleConstructor \
className##name##obj(#className,#name,c##className##name##caster,usage1,minArgs,maxArgs); \
static inline returnType c##className##name(S32 argc, const char **argv)
# define ConsoleMethodGroupEnd(className, groupName) \
static ConsoleConstructor className##groupName##__GroupEnd(#className,#groupName,NULL);
#else
// These do nothing if we don't want doc information.
# define ConsoleFunctionGroupBegin(groupName, usage)
# define ConsoleFunctionGroupEnd(groupName)
# define ConsoleMethodGroupBegin(className, groupName, usage)
# define ConsoleMethodGroupEnd(className, groupName)
// These are identical to what's above, we just want to null out the usage strings.
# define ConsoleFunction(name,returnType,minArgs,maxArgs,usage1) \
static returnType c##name(SimObject *, S32, const char **); \
static ConsoleConstructor g##name##obj(NULL,#name,c##name,"",minArgs,maxArgs);\
static returnType c##name(SimObject *, S32 argc, const char **argv)
# define ConsoleMethod(className,name,returnType,minArgs,maxArgs,usage1) \
static inline returnType c##className##name(className *, S32, const char **argv); \
static returnType c##className##name##caster(SimObject *object, S32 argc, const char **argv) { \
conmethod_return_##returnType ) c##className##name(static_cast<className*>(object),argc,argv); \
}; \
static ConsoleConstructor \
className##name##obj(#className,#name,c##className##name##caster,"",minArgs,maxArgs); \
static inline returnType c##className##name(className *object, S32 argc, const char **argv)
# define ConsoleStaticMethod(className,name,returnType,minArgs,maxArgs,usage1) \
static inline returnType c##className##name(S32, const char **); \
static returnType c##className##name##caster(SimObject *object, S32 argc, const char **argv) { \
conmethod_return_##returnType ) c##className##name(argc,argv); \
}; \
static ConsoleConstructor \
className##name##obj(#className,#name,c##className##name##caster,"",minArgs,maxArgs); \
static inline returnType c##className##name(S32 argc, const char **argv)
#endif
/// @}
#endif

378
engine/console/consoleDoc.cc Executable file
View File

@ -0,0 +1,378 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "console/console.h"
#include "console/ast.h"
#include "core/tAlgorithm.h"
#include "core/resManager.h"
#include "core/findMatch.h"
#include "console/consoleInternal.h"
#include "console/consoleObject.h"
#include "core/fileStream.h"
#include "console/compiler.h"
//--- Information pertaining to this page... ------------------
/// @file
///
/// For specifics on using the consoleDoc functionality, see @ref console_autodoc
ConsoleFunctionGroupBegin(ConsoleDoc, "Console self-documentation functions. These output psuedo C++ suitable for feeeding through Doxygen or another auto documentation tool.");
ConsoleFunction(dumpConsoleClasses, void, 1, 1, "() dumps all declared console classes to the console.")
{
Namespace::dumpClasses();
}
ConsoleFunction(dumpConsoleFunctions, void, 1, 1, "() dumps all declared console functions to the console.")
{
Namespace::dumpFunctions();
}
ConsoleFunctionGroupEnd(ConsoleDoc);
/// Helper table to convert type ids to human readable names.
const char *typeNames[] =
{
"Script",
"string",
"int",
"float",
"void",
"bool",
"",
"",
"unknown_overload"
};
void printClassHeader(const char * className, const char * superClassName, const bool stub)
{
if(stub)
{
Con::printf("/// Stub class");
Con::printf("/// ");
Con::printf("/// @note This is a stub class to ensure a proper class hierarchy. No ");
Con::printf("/// information was available for this class.");
}
// Print out appropriate class header
if(superClassName)
Con::printf("class %s : public %s {", className, superClassName ? superClassName : "");
else if(!className)
Con::printf("namespace Global {");
else
Con::printf("class %s {", className);
if(className)
Con::printf(" public:");
}
void printClassMethod(const bool isVirtual, const char *retType, const char *methodName, const char* args, const char*usage)
{
if(usage && usage[0] != ';' && usage[0] != 0)
Con::printf(" /*! %s */", usage);
Con::printf(" %s%s %s(%s) {}", isVirtual ? "virtual " : "", retType, methodName, args);
}
void printGroupStart(const char * aName, const char * aDocs)
{
Con::printf("");
Con::printf(" /*! @name %s", aName);
if(aDocs)
{
Con::printf(" ");
Con::printf(" %s", aDocs);
}
Con::printf(" @{ */");
}
void printClassMember(const bool isDeprec, const char * aType, const char * aName, const char * aDocs)
{
Con::printf(" /*!");
if(aDocs)
{
Con::printf(" %s", aDocs);
Con::printf(" ");
}
if(isDeprec)
Con::printf(" @deprecated This member is deprecated, which means that its value is always undefined.");
Con::printf(" */");
Con::printf(" %s %s;", isDeprec ? "deprecated" : aType, aName);
}
void printGroupEnd()
{
Con::printf(" /// @}");
Con::printf("");
}
void printClassFooter()
{
Con::printf("};");
Con::printf("");
}
void Namespace::printNamespaceEntries(Namespace * g)
{
static bool inGroup = false;
// Go through all the entries.
// Iterate through the methods of the namespace...
for(Entry *ewalk = g->mEntryList; ewalk; ewalk = ewalk->mNext)
{
char buffer[1024]; //< This will bite you in the butt someday.
int eType = ewalk->mType;
const char * funcName = ewalk->mFunctionName;
// If it's a function
if(eType >= Entry::ScriptFunctionType || eType == Entry::OverloadMarker)
{
if(eType==Entry::OverloadMarker)
{
// Deal with crap from the OverloadMarker case.
// It has no type information so we have to "correct" its type.
// Find the original
eType = 8;
for(Entry *eseek = g->mEntryList; eseek; eseek = eseek->mNext)
{
if(!dStrcmp(eseek->mFunctionName, ewalk->cb.mGroupName))
{
eType = eseek->mType;
break;
}
}
// And correct the name
funcName = ewalk->cb.mGroupName;
}
// A quick note - if your usage field starts with a (, then it's auto-integrated into
// the script docs! Use this HEAVILY!
// We add some heuristics here as well. If you're of the form:
// *.methodName(*)
// then we will also extract parameters.
const char *use = ewalk->mUsage ? ewalk->mUsage : "";
const char *bgn = dStrchr(use, '(');
const char *end = dStrchr(use, ')');
const char *dot = dStrchr(use, '.');
if(use[0] == '(')
{
if(!end)
end = use + 1;
use++;
U32 len = end - use;
dStrncpy(buffer, use, len);
buffer[len] = 0;
printClassMethod(true, typeNames[eType], funcName, buffer, end+1);
continue; // Skip to next one.
}
// We check to see if they're giving a prototype.
if(dot && bgn && end) // If there's two parentheses, and a dot...
if(dot < bgn && bgn < end) // And they're in the order dot, bgn, end...
{
use++;
U32 len = end - bgn - 1;
dStrncpy(buffer, bgn+1, len);
buffer[len] = 0;
// Then let's do the heuristic-trick
printClassMethod(true, typeNames[eType], funcName, buffer, end+1);
continue; // Get to next item.
}
// Finally, see if they did it foo(*) style.
char* func_pos = dStrstr(use, funcName);
if((func_pos) && (func_pos < bgn) && (end > bgn))
{
U32 len = end - bgn - 1;
dStrncpy(buffer, bgn+1, len);
buffer[len] = 0;
printClassMethod(true, typeNames[eType], funcName, buffer, end+1);
continue;
}
// Default...
printClassMethod(true, typeNames[eType], funcName, "", ewalk->mUsage);
}
else if(ewalk->mType == Entry::GroupMarker)
{
if(!inGroup)
printGroupStart(ewalk->cb.mGroupName, ewalk->mUsage);
else
printGroupEnd();
inGroup = !inGroup;
}
else if(ewalk->mFunctionOffset) // If it's a builtin function...
{
ewalk->mCode->getFunctionArgs(buffer, ewalk->mFunctionOffset);
printClassMethod(false, typeNames[ewalk->mType], ewalk->mFunctionName, buffer, "");
}
else
{
Con::printf(" // got an unknown thing?? %d", ewalk->mType );
}
}
}
void Namespace::dumpClasses()
{
Vector<Namespace *> vec;
trashCache();
// We use mHashSequence to mark if we have traversed...
// so mark all as zero to start.
for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
walk->mHashSequence = 0;
for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
{
Vector<Namespace *> stack;
// Get all the parents of this namespace... (and mark them as we go)
Namespace *parentWalk = walk;
while(parentWalk)
{
if(parentWalk->mHashSequence != 0)
break;
if(parentWalk->mPackage == 0)
{
parentWalk->mHashSequence = 1; // Mark as traversed.
stack.push_back(parentWalk);
}
parentWalk = parentWalk->mParent;
}
// Load stack into our results vector.
while(stack.size())
{
vec.push_back(stack[stack.size() - 1]);
stack.pop_back();
}
}
// Go through previously discovered classes
U32 i;
for(i = 0; i < vec.size(); i++)
{
const char *className = vec[i]->mName;
const char *superClassName = vec[i]->mParent ? vec[i]->mParent->mName : NULL;
// Skip the global namespace, that gets dealt with in dumpFunctions
if(!className) continue;
// If we hit a class with no members and no classRep, do clever filtering.
if(vec[i]->mEntryList == NULL && vec[i]->mClassRep == NULL)
{
// Print out a short stub so we get a proper class hierarchy.
if(superClassName) { // Filter hack; we don't want non-inheriting classes...
printClassHeader(className,superClassName, true);
printClassFooter();
}
continue;
}
// Print the header for the class..
printClassHeader(className, superClassName, false);
// Deal with entries.
printNamespaceEntries(vec[i]);
// Deal with the classRep (to get members)...
AbstractClassRep *rep = vec[i]->mClassRep;
AbstractClassRep::FieldList emptyList;
AbstractClassRep::FieldList *parentList = &emptyList;
AbstractClassRep::FieldList *fieldList = &emptyList;
if(rep)
{
// Get information about the parent's fields...
AbstractClassRep *parentRep = vec[i]->mParent ? vec[i]->mParent->mClassRep : NULL;
if(parentRep)
parentList = &(parentRep->mFieldList);
// Get information about our fields
fieldList = &(rep->mFieldList);
// Go through all our fields...
for(U32 j = 0; j < fieldList->size(); j++)
{
switch((*fieldList)[j].type)
{
case AbstractClassRep::StartGroupFieldType:
printGroupStart((*fieldList)[j].pGroupname, (*fieldList)[j].pFieldDocs);
break;
case AbstractClassRep::EndGroupFieldType:
printGroupEnd();
break;
default:
case AbstractClassRep::DepricatedFieldType:
{
bool isDeprecated = ((*fieldList)[j].type == AbstractClassRep::DepricatedFieldType);
if(isDeprecated)
{
printClassMember(
true,
"<deprecated>",
(*fieldList)[j].pFieldname,
(*fieldList)[j].pFieldDocs
);
}
else
{
ConsoleBaseType *cbt = ConsoleBaseType::getType((*fieldList)[j].type);
printClassMember(
false,
cbt ? cbt->getTypeClassName() : "<unknown>",
(*fieldList)[j].pFieldname,
(*fieldList)[j].pFieldDocs
);
}
}
}
}
}
// Close the class/namespace.
printClassFooter();
}
}
void Namespace::dumpFunctions()
{
// Get the global namespace.
Namespace* g = find(NULL); //->mParent;
printClassHeader(NULL,NULL, false);
while(g)
{
printNamespaceEntries(g);
g = g->mParent;
}
printClassFooter();
}

176
engine/console/consoleDoc.h Executable file
View File

@ -0,0 +1,176 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
// This file exists solely to document consoleDoc.cc
/// @page console_autodoc Console Auto-Documentation
///
/// @see consoleDoc.cc
///
/// @section console_autodoc_using Using Console Auto-Documentation
///
/// There are on the order of three hundred functions exposed to the script language
/// through the console. It is therefore extremely important that they be documented,
/// but due to their number, it is difficult to maintain a seperate reference document.
///
/// Therefore, a simple documentation system has been built into the scripting engine. It
/// was initially added by Mark Frohnmayer, and later enhanced by Ben Garney. The
/// scripting engine supports grouping functions and methods, to help organize the
/// several hundred functions, as well as associating a "usage string" with functions and
/// groups.
///
/// @note The results of a console doc dump will vary depending on when you run it. If
/// you run it, for example, while in the game menu, it won't output any data for
/// the script-defined classes which are defined for gameplay. To get comprehensive
/// documentation, you may need to write a special script that will get all your
/// classes loaded into the scripting engine.
///
/// The console documentation system is designed to output a dump of the current state
/// of the scripting engine in a format understandable by Doxygen. It does this by
/// traversing the namespace/class hierarchy in memory at the time of the dump, and
/// outputting psuedo-C++ code equivalent to this class hierarchy.
///
/// @subsection console_autodoc_using_script For the Scripter...
///
/// Currently, there is no way to associate usage strings or other documentation with script code
/// like you can with C++ code.
///
/// You can get a list of all the methods and fields of an object from any object which inherits
/// from SimObject (ie, every object), as well as the documentation on those objects by using the
/// dump() method from the console:
///
/// @code
/// ==>$foo = new SimObject();
/// ==>$foo.dump();
/// Member Fields:
/// Tagged Fields:
/// Methods:
/// delete() - obj.delete()
/// dump() - obj.dump()
/// getClassName() - obj.getClassName()
/// getGroup() - obj.getGroup()
/// getId() - obj.getId()
/// getName() - obj.getName()
/// getType() - obj.getType()
/// save() - obj.save(fileName, <selectedOnly>)
/// schedule() - object.schedule(time, command, <arg1...argN>);
/// setName() - obj.setName(newName)
/// @endcode
///
/// In the Torque example app, there are two functions defined in common\\client\\scriptDoc.cs
/// which automate the process of dumping the documentation. They make use of the ConsoleLogger
/// object to output the documentation to a file, and look like this:
///
/// @note You may want to add this code, or code like it, to your project if you have
/// rewritten the script code in common.
///
/// @code
/// // Writes out all script functions to a file
/// function writeOutFunctions() {
/// new ConsoleLogger( logger, "scriptFunctions.txt", false );
/// dumpConsoleFunctions();
/// logger.delete();
/// }
///
/// // Writes out all script classes to a file
/// function writeOutClasses() {
/// new ConsoleLogger( logger, "scriptClasses.txt", false );
/// dumpConsoleClasses();
/// logger.delete();
/// }
/// @endcode
///
/// @subsection console_autodoc_using_coder For the C++ Coder...
///
/// @note <b>It is of the utmost important that you keep your usage strings up to date!</b>
/// Usage strings are the only way that a scripter has to know how to use the methods,
/// functions, and variables you expose. Misleading, missing, or out of date documentation
/// will make their lives much harder - and yours, too, because you'll have to keep
/// explaining things to them! So make everyone's lives easier - keep your usage strings
/// clear, concise, and up to date.
///
/// There are four types of items which can be documented using the autodocumentation system:
/// - <b>Fields</b>, which are defined using the addField() calls. They are documented
/// by passing a string to the usage parameter.
/// - <b>Field groups</b>, which are defined using the beginGroup() and endGroup() calls.
/// They are documented by passing a descriptive string to the usage parameter.
/// - <b>Method groups</b>, which are defined using beginCommandGroup(), endCommandGroup(),
/// ConsoleMethodGroupEnd(), ConsoleMethodGroupBegin(), ConsoleFunctionGroupEnd(), and
/// ConsoleFunctionGroupBegin().
/// - <b>Methods and functions</b>, which are defined using either SimObject::addCommand(),
/// the ConsoleMethod() macro, or the ConsoleFunction() macro. Methods and functions are
/// special in that the usage strings should be in a specific format, so
/// that parameter information can be extracted from them and placed into the Doxygen
/// output.
///
/// You can use standard Doxygen commands in your comments, to make the documentation clearer.
/// Of particular use are \@returns, \@param, \@note, and \@deprecated.
///
/// <b>Examples using global definitions.</b>
///
/// @code
/// // Example of using Doxygen commands.
/// ConsoleFunction(alxGetWaveLen, S32, 2, 2, "(string filename)"
/// "Get length of a wave file\n\n"
/// "@param filename File to determine length of.\n"
/// "@returns Length in milliseconds.")
///
/// // A function group...
/// ConsoleFunctionGroupBegin(Example, "This is an example group! Notice that the name for the group"
/// "must be a valid identifier, due to limitations in the C preprocessor.");
///
/// // ConsoleFunction definitions go here.
///
/// ConsoleFunctionGroupEnd(Example);
///
/// // You can do similar things with methods...
/// ConsoleMethodGroupBegin(SimSet, UsefulFuncs, "Here are some useful functions involving a SimSet.");
/// ConsoleMethod(SimSet, listObjects, void, 2, 2, "set.listObjects();")
/// ConsoleMethodGroupEnd(SimSet, UsefulFuncs, "Here are some more useful functions involving a SimSet.");
/// @endcode
///
/// <b>Examples using addField</b>
///
/// @note Using addCommand is strongly deprecated.
///
/// @code
/// // Example of a field group.
/// addGroup( "Logging", "Things relating to logging." );
/// addField( "level", TypeEnum, Offset( mLevel, ConsoleLogger ), 1, &gLogLevelTable );
/// endGroup( "Logging" );
/// @endcode
///
/// @section console_autodoc_makingdocs How to Generate Console Docs
///
/// Console docs can be generated by running the dumpConsoleFunctions() and
/// dumpConsoleClasses(), then running the output through Doxygen. There is an
/// example Doxygen configuration file to do this in HEAD,
/// at doc\\doxygen\\html\\script_doxygen.html.cfg. Doxygen will parse the psuedo-C++
/// generated by the console doc code and produce a class hierarchy and documentation
/// of the global namespace. You may need to tweak the paths in the configuration file
/// slightly to reflect your individual setup.
///
/// @section console_autodoc_internals Console Auto-Documentation Internals
///
/// The consoleDoc system works by inserting "hidden" entries in Namespace and
/// AbstractClassRep; these hidden entries are assigned special type IDs so that
/// they aren't touched by the standard name resolution code. At documentation
/// creation time, the dumpConsole functions iterate through the Namespace hierarchy
/// and the AbstractClassRep data and extract this "hidden" information, outputting
/// it in a Doxygen-compatible format.
///
/// @note You can customize the output of the console documentation system by modifying
/// these functions:
/// - printClassHeader()
/// - printClassMethod()
/// - printGroupStart()
/// - printClassMember()
/// - printGroupEnd()
/// - printClassFooter()
///
/// @note There was once support for 'overloaded' script functions; ie, script functions
/// with multiple usage strings. Certain functions in the audio library used this.
/// However, it was deemed too complex, and removed from the scripting engine. There
/// are still some latent traces of it, however.

1512
engine/console/consoleFunctions.cc Executable file

File diff suppressed because it is too large Load Diff

1087
engine/console/consoleInternal.cc Executable file

File diff suppressed because it is too large Load Diff

311
engine/console/consoleInternal.h Executable file
View File

@ -0,0 +1,311 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _CONSOLEINTERNAL_H_
#define _CONSOLEINTERNAL_H_
#ifndef _STRINGTABLE_H_
#include "core/stringTable.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _CONSOLETYPES_H_
#include "console/consoleTypes.h"
#endif
class ExprEvalState;
struct FunctionDecl;
class CodeBlock;
class AbstractClassRep;
class Namespace
{
enum {
MaxActivePackages = 512,
};
static U32 mNumActivePackages;
static U32 mOldNumActivePackages;
static StringTableEntry mActivePackages[MaxActivePackages];
public:
StringTableEntry mName;
StringTableEntry mPackage;
Namespace *mParent;
Namespace *mNext;
AbstractClassRep *mClassRep;
U32 mRefCountToParent;
struct Entry
{
enum {
GroupMarker = -3,
OverloadMarker = -2,
InvalidFunctionType = -1,
ScriptFunctionType,
StringCallbackType,
IntCallbackType,
FloatCallbackType,
VoidCallbackType,
BoolCallbackType
};
Namespace *mNamespace;
Entry *mNext;
StringTableEntry mFunctionName;
S32 mType;
S32 mMinArgs;
S32 mMaxArgs;
const char *mUsage;
StringTableEntry mPackage;
CodeBlock *mCode;
U32 mFunctionOffset;
union {
StringCallback mStringCallbackFunc;
IntCallback mIntCallbackFunc;
VoidCallback mVoidCallbackFunc;
FloatCallback mFloatCallbackFunc;
BoolCallback mBoolCallbackFunc;
const char* mGroupName;
} cb;
Entry();
void clear();
const char *execute(S32 argc, const char **argv, ExprEvalState *state);
};
Entry *mEntryList;
Entry **mHashTable;
U32 mHashSize;
U32 mHashSequence; ///< @note The hash sequence is used by the autodoc console facility
/// as a means of testing reference state.
Namespace();
void addFunction(StringTableEntry name, CodeBlock *cb, U32 functionOffset);
void addCommand(StringTableEntry name,StringCallback, const char *usage, S32 minArgs, S32 maxArgs);
void addCommand(StringTableEntry name,IntCallback, const char *usage, S32 minArgs, S32 maxArgs);
void addCommand(StringTableEntry name,FloatCallback, const char *usage, S32 minArgs, S32 maxArgs);
void addCommand(StringTableEntry name,VoidCallback, const char *usage, S32 minArgs, S32 maxArgs);
void addCommand(StringTableEntry name,BoolCallback, const char *usage, S32 minArgs, S32 maxArgs);
void addOverload(const char *name, const char* altUsage);
void markGroup(const char* name, const char* usage);
char * lastUsage;
void getEntryList(Vector<Entry *> *);
Entry *lookup(StringTableEntry name);
Entry *lookupRecursive(StringTableEntry name);
Entry *createLocalEntry(StringTableEntry name);
void buildHashTable();
void clearEntries();
bool classLinkTo(Namespace *parent);
bool unlinkClass(Namespace *parent);
const char *tabComplete(const char *prevText, S32 baseLen, bool fForward);
static U32 mCacheSequence;
static DataChunker mCacheAllocator;
static DataChunker mAllocator;
static void trashCache();
static Namespace *mNamespaceList;
static Namespace *mGlobalNamespace;
static void init();
static void shutdown();
static Namespace *global();
static Namespace *find(StringTableEntry name, StringTableEntry package=NULL);
static void activatePackage(StringTableEntry name);
static void deactivatePackage(StringTableEntry name);
static void dumpClasses();
static void dumpFunctions();
static void printNamespaceEntries(Namespace * g);
static void unlinkPackages();
static void relinkPackages();
static bool isPackage(StringTableEntry name);
};
extern char *typeValueEmpty;
class Dictionary
{
public:
struct Entry
{
enum
{
TypeInternalInt = -3,
TypeInternalFloat = -2,
TypeInternalString = -1,
};
StringTableEntry name;
Entry *nextEntry;
S32 type;
char *sval;
U32 ival; // doubles as strlen when type = -1
F32 fval;
U32 bufferLen;
void *dataPtr;
Entry(StringTableEntry name);
~Entry();
U32 getIntValue()
{
if(type <= TypeInternalString)
return ival;
else
return dAtoi(Con::getData(type, dataPtr, 0));
}
F32 getFloatValue()
{
if(type <= TypeInternalString)
return fval;
else
return dAtof(Con::getData(type, dataPtr, 0));
}
const char *getStringValue()
{
if(type == TypeInternalString)
return sval;
if(type == TypeInternalFloat)
return Con::getData(TypeF32, &fval, 0);
else if(type == TypeInternalInt)
return Con::getData(TypeS32, &ival, 0);
else
return Con::getData(type, dataPtr, 0);
}
void setIntValue(U32 val)
{
if(type <= TypeInternalString)
{
fval = (F32)val;
ival = val;
if(sval != typeValueEmpty)
{
dFree(sval);
sval = typeValueEmpty;
}
type = TypeInternalInt;
return;
}
else
{
const char *dptr = Con::getData(TypeS32, &val, 0);
Con::setData(type, dataPtr, 0, 1, &dptr);
}
}
void setFloatValue(F32 val)
{
if(type <= TypeInternalString)
{
fval = val;
ival = static_cast<U32>(val);
if(sval != typeValueEmpty)
{
dFree(sval);
sval = typeValueEmpty;
}
type = TypeInternalFloat;
return;
}
else
{
const char *dptr = Con::getData(TypeF32, &val, 0);
Con::setData(type, dataPtr, 0, 1, &dptr);
}
}
void setStringValue(const char *value);
};
private:
struct HashTableData
{
Dictionary* owner;
S32 size;
S32 count;
Entry **data;
};
HashTableData *hashTable;
ExprEvalState *exprState;
public:
StringTableEntry scopeName;
Namespace *scopeNamespace;
CodeBlock *code;
U32 ip;
Dictionary();
Dictionary(ExprEvalState *state, Dictionary* ref=NULL);
~Dictionary();
Entry *lookup(StringTableEntry name);
Entry *add(StringTableEntry name);
void setState(ExprEvalState *state, Dictionary* ref=NULL);
void remove(Entry *);
void reset();
void exportVariables(const char *varString, const char *fileName, bool append);
void deleteVariables(const char *varString);
void setVariable(StringTableEntry name, const char *value);
const char *getVariable(StringTableEntry name, bool *valid = NULL);
void addVariable(const char *name, S32 type, void *dataPtr);
bool removeVariable(StringTableEntry name);
/// Return the best tab completion for prevText, with the length
/// of the pre-tab string in baseLen.
const char *tabComplete(const char *prevText, S32 baseLen, bool);
};
class ExprEvalState
{
public:
/// @name Expression Evaluation
/// @{
///
SimObject *thisObject;
Dictionary::Entry *currentVariable;
bool traceOn;
ExprEvalState();
~ExprEvalState();
/// @}
/// @name Stack Management
/// @{
///
Dictionary globalVars;
Vector<Dictionary *> stack;
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);
void pushFrame(StringTableEntry frameName, Namespace *ns);
void popFrame();
/// Puts a reference to an existing stack frame
/// on the top of the stack.
void pushFrameRef(S32 stackIndex);
/// @}
};
#endif

215
engine/console/consoleLogger.cc Executable file
View File

@ -0,0 +1,215 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/consoleLogger.h"
#include "console/consoleTypes.h"
Vector<ConsoleLogger *> ConsoleLogger::mActiveLoggers;
bool ConsoleLogger::smInitialized = false;
IMPLEMENT_CONOBJECT( ConsoleLogger );
//-----------------------------------------------------------------------------
ConsoleLogger::ConsoleLogger()
{
mFilename = NULL;
mLogging = false;
mAppend = false;
}
//-----------------------------------------------------------------------------
ConsoleLogger::ConsoleLogger( const char *fileName, bool append )
{
mLogging = false;
mLevel = ConsoleLogEntry::Normal;
mFilename = StringTable->insert( fileName );
mAppend = append;
init();
}
//-----------------------------------------------------------------------------
static EnumTable::Enums logLevelEnums[] =
{
{ ConsoleLogEntry::Normal, "normal" },
{ ConsoleLogEntry::Warning, "warning" },
{ ConsoleLogEntry::Error, "error" },
};
static EnumTable gLogLevelTable( 3, &logLevelEnums[0] );
void ConsoleLogger::initPersistFields()
{
Parent::initPersistFields();
addGroup( "Logging" );
addField( "level", TypeEnum, Offset( mLevel, ConsoleLogger ), 1, &gLogLevelTable );
endGroup( "Logging" );
}
//-----------------------------------------------------------------------------
bool ConsoleLogger::processArguments( S32 argc, const char **argv )
{
if( argc == 0 )
return false;
bool append = false;
if( argc == 2 )
append = dAtob( argv[1] );
mAppend = append;
mFilename = StringTable->insert( argv[0] );
if( init() )
{
attach();
return true;
}
return false;
}
//-----------------------------------------------------------------------------
ConsoleLogger::~ConsoleLogger()
{
detach();
}
//-----------------------------------------------------------------------------
bool ConsoleLogger::init()
{
if( smInitialized )
return true;
Con::addConsumer( ConsoleLogger::logCallback );
smInitialized = true;
return true;
}
//-----------------------------------------------------------------------------
bool ConsoleLogger::attach()
{
if( mFilename == NULL )
{
Con::errorf( "ConsoleLogger failed to attach: no filename supplied." );
return false;
}
// Check to see if this is initialized before using it
if( !smInitialized )
{
if( !init() )
{
Con::errorf( "ConsoleLogger failed to initalize." );
return false;
}
}
if( mLogging )
return false;
// Open the filestream
mStream.open( mFilename, ( mAppend ? FileStream::WriteAppend : FileStream::Write ) );
// Add this to list of active loggers
mActiveLoggers.push_back( this );
mLogging = true;
return true;
}
//-----------------------------------------------------------------------------
bool ConsoleLogger::detach()
{
// Make sure this is valid before messing with it
if( !smInitialized )
{
if( !init() )
{
return false;
}
}
if( !mLogging )
return false;
// Close filestream
mStream.close();
// Remove this object from the list of active loggers
for( int i = 0; i < mActiveLoggers.size(); i++ )
{
if( mActiveLoggers[i] == this )
{
mActiveLoggers.erase( i );
mLogging = false;
return true;
}
}
return false; // If this happens, it's bad...
}
//-----------------------------------------------------------------------------
void ConsoleLogger::logCallback( ConsoleLogEntry::Level level, const char *consoleLine )
{
ConsoleLogger *curr;
// Loop through active consumers and send them the message
for( int i = 0; i < mActiveLoggers.size(); i++ )
{
curr = mActiveLoggers[i];
// If the log level is within the log threshhold, log it
if( curr->mLevel <= level )
curr->log( consoleLine );
}
}
//-----------------------------------------------------------------------------
void ConsoleLogger::log( const char *consoleLine )
{
// Check to see if this is intalized before using it
if( !smInitialized )
{
if( !init() )
{
Con::errorf( "I don't know how this happened, but log called on this without it being initialized" );
return;
}
}
mStream.writeLine( (U8 *)consoleLine );
}
//-----------------------------------------------------------------------------
ConsoleMethod( ConsoleLogger, attach, bool, 2, 2, "() Attaches this object to the console and begins logging" )
{
ConsoleLogger *logger = static_cast<ConsoleLogger *>( object );
return logger->attach();
}
//-----------------------------------------------------------------------------
ConsoleMethod( ConsoleLogger, detach, bool, 2, 2, "() Detaches this object from the console and stops logging" )
{
ConsoleLogger *logger = static_cast<ConsoleLogger *>( object );
return logger->detach();
}

93
engine/console/consoleLogger.h Executable file
View File

@ -0,0 +1,93 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/simBase.h"
#include "console/console.h"
#include "core/fileStream.h"
#ifndef _CONSOLE_LOGGER_H_
#define _CONSOLE_LOGGER_H_
/// A class designed to be used as a console consumer and log
/// the data it receives to a file.
class ConsoleLogger : public SimObject
{
typedef SimObject Parent;
private:
bool mLogging; ///< True if it is currently consuming and logging
FileStream mStream; ///< File stream this object writes to
static bool smInitialized; ///< This is for use with the default constructor
bool mAppend; ///< If false, it will clear the file before logging to it.
StringTableEntry mFilename; ///< The file name to log to.
/// List of active ConsoleLoggers to send log messages to
static Vector<ConsoleLogger *> mActiveLoggers;
/// The log function called by the consumer callback
/// @param consoleLine Line of text to log
void log( const char *consoleLine );
/// Utility function, sets up the object (for script interface) returns true if successful
bool init();
public:
// @name Public console variables
/// @{
ConsoleLogEntry::Level mLevel; ///< The level of log messages to log
/// @}
DECLARE_CONOBJECT( ConsoleLogger );
static void initPersistFields();
/// Console constructor
///
/// @code
/// // Example script constructor usage.
/// %obj = new ConsoleLogger( objName, logFileName, [append = false] );
/// @endcode
bool processArguments( S32 argc, const char **argv );
/// Default constructor, make sure to initalize
ConsoleLogger();
/// Constructor
/// @param fileName File name to log to
/// @param append If false, it will clear the file, then start logging, else it will append
ConsoleLogger( const char *fileName, bool append = false );
/// Destructor
~ConsoleLogger();
/// Attach to the console and begin logging
///
/// Returns true if the action is successful
bool attach();
/// Detach from the console and stop logging
///
/// Returns true if the action is successful
bool detach();
/// Sets the level of console messages to log.
///
/// @param level Log level. Only items of the specified level or
/// lower are logged.
/// @see ConsoleLogEntry::Level
void setLogLevel( ConsoleLogEntry::Level level );
/// Returns the level of console messages to log
ConsoleLogEntry::Level getLogLevel() const;
/// The callback for the console consumer
///
/// @note This is a global callback, not executed per-instance.
/// @see Con::addConsumer
static void logCallback( ConsoleLogEntry::Level level, const char *consoleLine );
};
#endif

408
engine/console/consoleObject.cc Executable file
View File

@ -0,0 +1,408 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "console/consoleObject.h"
#include "core/stringTable.h"
#include "core/crc.h"
#include "console/console.h"
#include "console/consoleInternal.h"
#include "console/typeValidators.h"
AbstractClassRep * AbstractClassRep::classLinkList = NULL;
static AbstractClassRep::FieldList sg_tempFieldList;
U32 AbstractClassRep::NetClassCount [NetClassGroupsCount][NetClassTypesCount] = {{0, },};
U32 AbstractClassRep::NetClassBitSize[NetClassGroupsCount][NetClassTypesCount] = {{0, },};
AbstractClassRep ** AbstractClassRep::classTable[NetClassGroupsCount][NetClassTypesCount];
U32 AbstractClassRep::classCRC[NetClassGroupsCount] = {INITIAL_CRC_VALUE, };
bool AbstractClassRep::initialized = false;
//--------------------------------------
const AbstractClassRep::Field *AbstractClassRep::findField(StringTableEntry name) const
{
for(U32 i = 0; i < mFieldList.size(); i++)
if(mFieldList[i].pFieldname == name)
return &mFieldList[i];
return NULL;
}
//--------------------------------------
void AbstractClassRep::registerClassRep(AbstractClassRep* in_pRep)
{
AssertFatal(in_pRep != NULL, "AbstractClassRep::registerClassRep was passed a NULL pointer!");
#ifdef TORQUE_DEBUG // assert if this class is already registered.
for(AbstractClassRep *walk = classLinkList; walk; walk = walk->nextClass)
{
AssertFatal(dStrcmp(in_pRep->mClassName, walk->mClassName),
"Duplicate class name registered in AbstractClassRep::registerClassRep()");
}
#endif
in_pRep->nextClass = classLinkList;
classLinkList = in_pRep;
}
//--------------------------------------
ConsoleObject* AbstractClassRep::create(const char* in_pClassName)
{
AssertFatal(initialized,
"AbstractClassRep::create() - Tried to create an object before AbstractClassRep::initialize().");
for (AbstractClassRep *walk = classLinkList; walk; walk = walk->nextClass)
if (!dStrcmp(walk->getClassName(), in_pClassName))
return walk->create();
AssertWarn(0, avar("Couldn't find class rep for dynamic class: %s", in_pClassName));
return NULL;
}
//--------------------------------------
ConsoleObject* AbstractClassRep::create(const U32 groupId, const U32 typeId, const U32 in_classId)
{
AssertFatal(initialized,
"AbstractClassRep::create() - Tried to create an object before AbstractClassRep::initialize().");
AssertFatal(in_classId < NetClassCount[groupId][typeId],
"AbstractClassRep::create() - Class id out of range.");
AssertFatal(classTable[groupId][typeId][in_classId] != NULL,
"AbstractClassRep::create() - No class with requested ID type.");
// Look up the specified class and create it.
if(classTable[groupId][typeId][in_classId])
return classTable[groupId][typeId][in_classId]->create();
return NULL;
}
//--------------------------------------
static S32 QSORT_CALLBACK ACRCompare(const void *aptr, const void *bptr)
{
const AbstractClassRep *a = *((const AbstractClassRep **) aptr);
const AbstractClassRep *b = *((const AbstractClassRep **) bptr);
if(a->mClassType != b->mClassType)
return a->mClassType - b->mClassType;
return dStrcmp(a->getClassName(), b->getClassName());
}
void AbstractClassRep::initialize()
{
AssertFatal(!initialized, "Duplicate call to AbstractClassRep::initialize()!");
Vector<AbstractClassRep *> dynamicTable(__FILE__, __LINE__);
AbstractClassRep *walk;
// Initialize namespace references...
for (walk = classLinkList; walk; walk = walk->nextClass)
{
walk->mNamespace = Con::lookupNamespace(StringTable->insert(walk->getClassName()));
walk->mNamespace->mClassRep = walk;
}
// Initialize field lists... (and perform other console registration).
for (walk = classLinkList; walk; walk = walk->nextClass)
{
// sg_tempFieldList is used as a staging area for field lists
// (see addField, addGroup, etc.)
sg_tempFieldList.setSize(0);
walk->init();
// So if we have things in it, copy it over...
if (sg_tempFieldList.size() != 0)
walk->mFieldList = sg_tempFieldList;
// And of course delete it every round.
sg_tempFieldList.clear();
}
// Calculate counts and bit sizes for the various NetClasses.
for (U32 group = 0; group < NetClassGroupsCount; group++)
{
U32 groupMask = 1 << group;
// Specifically, for each NetClass of each NetGroup...
for(U32 type = 0; type < NetClassTypesCount; type++)
{
// Go through all the classes and find matches...
for (walk = classLinkList; walk; walk = walk->nextClass)
{
if(walk->mClassType == type && walk->mClassGroupMask & groupMask)
dynamicTable.push_back(walk);
}
// Set the count for this NetGroup and NetClass
NetClassCount[group][type] = dynamicTable.size();
if(!NetClassCount[group][type])
continue; // If no classes matched, skip to next.
// Sort by type and then by name.
dQsort((void *) &dynamicTable[0], dynamicTable.size(), sizeof(AbstractClassRep *), ACRCompare);
// Allocate storage in the classTable
classTable[group][type] = new AbstractClassRep*[NetClassCount[group][type]];
// Fill this in and assign class ids for this group.
for(U32 i = 0; i < NetClassCount[group][type];i++)
{
classTable[group][type][i] = dynamicTable[i];
dynamicTable[i]->mClassId[group] = i;
}
// And calculate the size of bitfields for this group and type.
NetClassBitSize[group][type] =
getBinLog2(getNextPow2(NetClassCount[group][type] + 1));
dynamicTable.clear();
}
}
// Ok, we're golden!
initialized = true;
}
//------------------------------------------------------------------------------
//-------------------------------------- ConsoleObject
char replacebuf[1024];
char* suppressSpaces(const char* in_pname)
{
U32 i = 0;
char chr;
do
{
chr = in_pname[i];
replacebuf[i++] = (chr != 32) ? chr : '_';
} while(chr);
return replacebuf;
}
void ConsoleObject::addGroup(const char* in_pGroupname, const char* in_pGroupDocs)
{
// Remove spaces.
char* pFieldNameBuf = suppressSpaces(in_pGroupname);
// Append group type to fieldname.
dStrcat(pFieldNameBuf, "_begingroup");
// Create Field.
AbstractClassRep::Field f;
f.pFieldname = StringTable->insert(pFieldNameBuf);
f.pGroupname = StringTable->insert(in_pGroupname);
if(in_pGroupDocs)
f.pFieldDocs = StringTable->insert(in_pGroupDocs);
else
f.pFieldDocs = NULL;
f.type = AbstractClassRep::StartGroupFieldType;
f.elementCount = 0;
f.groupExpand = false;
f.validator = NULL;
f.setDataFn = &defaultProtectedSetFn;
f.getDataFn = &defaultProtectedGetFn;
// Add to field list.
sg_tempFieldList.push_back(f);
}
void ConsoleObject::endGroup(const char* in_pGroupname)
{
// Remove spaces.
char* pFieldNameBuf = suppressSpaces(in_pGroupname);
// Append group type to fieldname.
dStrcat(pFieldNameBuf, "_endgroup");
// Create Field.
AbstractClassRep::Field f;
f.pFieldname = StringTable->insert(pFieldNameBuf);
f.pGroupname = StringTable->insert(in_pGroupname);
f.pFieldDocs = NULL;
f.type = AbstractClassRep::EndGroupFieldType;
f.groupExpand = false;
f.validator = NULL;
f.setDataFn = &defaultProtectedSetFn;
f.getDataFn = &defaultProtectedGetFn;
f.elementCount = 0;
// Add to field list.
sg_tempFieldList.push_back(f);
}
void ConsoleObject::addField(const char* in_pFieldname,
const U32 in_fieldType,
const dsize_t in_fieldOffset,
const char* in_pFieldDocs)
{
addField(
in_pFieldname,
in_fieldType,
in_fieldOffset,
1,
NULL,
in_pFieldDocs);
}
void ConsoleObject::addProtectedField(const char* in_pFieldname,
const U32 in_fieldType,
const dsize_t in_fieldOffset,
AbstractClassRep::SetDataNotify in_setDataFn,
AbstractClassRep::GetDataNotify in_getDataFn,
const char* in_pFieldDocs)
{
addProtectedField(
in_pFieldname,
in_fieldType,
in_fieldOffset,
in_setDataFn,
in_getDataFn,
1,
NULL,
in_pFieldDocs);
}
void ConsoleObject::addField(const char* in_pFieldname,
const U32 in_fieldType,
const dsize_t in_fieldOffset,
const U32 in_elementCount,
EnumTable *in_table,
const char* in_pFieldDocs)
{
AbstractClassRep::Field f;
f.pFieldname = StringTable->insert(in_pFieldname);
f.pGroupname = NULL;
if(in_pFieldDocs)
f.pFieldDocs = StringTable->insert(in_pFieldDocs);
else
f.pFieldDocs = NULL;
f.type = in_fieldType;
f.offset = in_fieldOffset;
f.elementCount = in_elementCount;
f.table = in_table;
f.validator = NULL;
f.setDataFn = &defaultProtectedSetFn;
f.getDataFn = &defaultProtectedGetFn;
sg_tempFieldList.push_back(f);
}
void ConsoleObject::addProtectedField(const char* in_pFieldname,
const U32 in_fieldType,
const dsize_t in_fieldOffset,
AbstractClassRep::SetDataNotify in_setDataFn,
AbstractClassRep::GetDataNotify in_getDataFn,
const U32 in_elementCount,
EnumTable *in_table,
const char* in_pFieldDocs)
{
AbstractClassRep::Field f;
f.pFieldname = StringTable->insert(in_pFieldname);
f.pGroupname = NULL;
if(in_pFieldDocs)
f.pFieldDocs = StringTable->insert(in_pFieldDocs);
else
f.pFieldDocs = NULL;
f.type = in_fieldType;
f.offset = in_fieldOffset;
f.elementCount = in_elementCount;
f.table = in_table;
f.validator = NULL;
f.setDataFn = in_setDataFn;
f.getDataFn = in_getDataFn;
sg_tempFieldList.push_back(f);
}
void ConsoleObject::addFieldV(const char* in_pFieldname,
const U32 in_fieldType,
const dsize_t in_fieldOffset,
TypeValidator *v,
const char* in_pFieldDocs)
{
AbstractClassRep::Field f;
f.pFieldname = StringTable->insert(in_pFieldname);
f.pGroupname = NULL;
if(in_pFieldDocs)
f.pFieldDocs = StringTable->insert(in_pFieldDocs);
else
f.pFieldDocs = NULL;
f.type = in_fieldType;
f.offset = in_fieldOffset;
f.elementCount = 1;
f.table = NULL;
f.setDataFn = &defaultProtectedSetFn;
f.getDataFn = &defaultProtectedGetFn;
f.validator = v;
v->fieldIndex = sg_tempFieldList.size();
sg_tempFieldList.push_back(f);
}
void ConsoleObject::addDepricatedField(const char *fieldName)
{
AbstractClassRep::Field f;
f.pFieldname = StringTable->insert(fieldName);
f.pGroupname = NULL;
f.pFieldDocs = NULL;
f.type = AbstractClassRep::DepricatedFieldType;
f.offset = 0;
f.elementCount = 0;
f.table = NULL;
f.validator = NULL;
f.setDataFn = &defaultProtectedSetFn;
f.getDataFn = &defaultProtectedGetFn;
sg_tempFieldList.push_back(f);
}
bool ConsoleObject::removeField(const char* in_pFieldname)
{
for (U32 i = 0; i < sg_tempFieldList.size(); i++) {
if (dStricmp(in_pFieldname, sg_tempFieldList[i].pFieldname) == 0) {
sg_tempFieldList.erase(i);
return true;
}
}
return false;
}
//--------------------------------------
void ConsoleObject::initPersistFields()
{
}
//--------------------------------------
void ConsoleObject::consoleInit()
{
}
ConsoleObject::~ConsoleObject()
{
}
//--------------------------------------
AbstractClassRep* ConsoleObject::getClassRep() const
{
return NULL;
}

744
engine/console/consoleObject.h Executable file
View File

@ -0,0 +1,744 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _CONSOLEOBJECT_H_
#define _CONSOLEOBJECT_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _STRINGTABLE_H_
#include "core/stringTable.h"
#endif
#ifndef _BITSET_H_
#include "core/bitSet.h"
#endif
#ifndef _CONSOLE_H_
#include "console/console.h"
#endif
class Namespace;
class ConsoleObject;
enum NetClassTypes {
NetClassTypeObject = 0,
NetClassTypeDataBlock,
NetClassTypeEvent,
NetClassTypesCount,
};
enum NetClassGroups {
NetClassGroupGame = 0,
NetClassGroupCommunity,
NetClassGroup3,
NetClassGroup4,
NetClassGroupsCount,
};
enum NetClassMasks {
NetClassGroupGameMask = BIT(NetClassGroupGame),
NetClassGroupCommunityMask = BIT(NetClassGroupCommunity),
};
enum NetDirection
{
NetEventDirAny,
NetEventDirServerToClient,
NetEventDirClientToServer,
};
class SimObject;
class TypeValidator;
/// Core functionality for class manipulation.
///
/// @section AbstractClassRep_intro Introduction (or, Why AbstractClassRep?)
///
/// Many of Torque's subsystems, especially network, console, and sim,
/// require the ability to programatically instantiate classes. For instance,
/// when objects are ghosted, the networking layer needs to be able to create
/// an instance of the object on the client. When the console scripting
/// language runtime encounters the "new" keyword, it has to be able to fill
/// that request.
///
/// Since standard C++ doesn't provide a function to create a new instance of
/// an arbitrary class at runtime, one must be created. This is what
/// AbstractClassRep and ConcreteClassRep are all about. They allow the registration
/// and instantiation of arbitrary classes at runtime.
///
/// In addition, ACR keeps track of the fields (registered via addField() and co.) of
/// a class, allowing programmatic access of class fields.
///
/// @see ConsoleObject
///
/// @note In general, you will only access the functionality implemented in this class via
/// ConsoleObject::create(). Most of the time, you will only ever need to use this part
/// part of the engine indirectly - ie, you will use the networking system or the console,
/// or ConsoleObject, and they will indirectly use this code. <b>The following discussion
/// is really only relevant for advanced engine users.</b>
///
/// @section AbstractClassRep_netstuff NetClasses and Class IDs
///
/// Torque supports a notion of group, type, and direction for objects passed over
/// the network. Class IDs are assigned sequentially per-group, per-type, so that, for instance,
/// the IDs assigned to Datablocks are seperate from the IDs assigned to NetObjects or NetEvents.
/// This can translate into significant bandwidth savings (especially since the size of the fields
/// for transmitting these bits are determined at run-time based on the number of IDs given out.
///
/// @section AbstractClassRep_details AbstractClassRep Internals
///
/// Much like ConsoleConstructor, ACR does some preparatory work at runtime before execution
/// is passed to main(). In actual fact, this preparatory work is done by the ConcreteClassRep
/// template. Let's examine this more closely.
///
/// If we examine ConsoleObject, we see that two macros must be used in the definition of a
/// properly integrated objects. From the ConsoleObject example:
///
/// @code
/// // This is from inside the class definition...
/// DECLARE_CONOBJECT(TorqueObject);
///
/// // And this is from outside the class definition...
/// IMPLEMENT_CONOBJECT(TorqueObject);
/// @endcode
///
/// What do these things actually do?
///
/// Not all that much, in fact. They expand to code something like this:
///
/// @code
/// // This is from inside the class definition...
/// static ConcreteClassRep<TorqueObject> dynClassRep;
/// static AbstractClassRep* getParentStaticClassRep();
/// static AbstractClassRep* getStaticClassRep();
/// virtual AbstractClassRep* getClassRep() const;
/// @endcode
///
/// @code
/// // And this is from outside the class definition...
/// AbstractClassRep* TorqueObject::getClassRep() const { return &TorqueObject::dynClassRep; }
/// AbstractClassRep* TorqueObject::getStaticClassRep() { return &dynClassRep; }
/// AbstractClassRep* TorqueObject::getParentStaticClassRep() { return Parent::getStaticClassRep(); }
/// ConcreteClassRep<TorqueObject> TorqueObject::dynClassRep("TorqueObject", 0, -1, 0);
/// @endcode
///
/// As you can see, getClassRep(), getStaticClassRep(), and getParentStaticClassRep() are just
/// accessors to allow access to various ConcreteClassRep instances. This is where the Parent
/// typedef comes into play as well - it lets getParentStaticClassRep() get the right
/// class rep.
///
/// In addition, dynClassRep is declared as a member of TorqueObject, and defined later
/// on. Much like ConsoleConstructor, ConcreteClassReps add themselves to a global linked
/// list in their constructor.
///
/// Then, when AbstractClassRep::initialize() is called, from Con::init(), we iterate through
/// the list and perform the following tasks:
/// - Sets up a Namespace for each class.
/// - Call the init() method on each ConcreteClassRep. This method:
/// - Links namespaces between parent and child classes, using Con::classLinkNamespaces.
/// - Calls initPersistFields() and consoleInit().
/// - As a result of calling initPersistFields, the field list for the class is populated.
/// - Assigns network IDs for classes based on their NetGroup membership. Determines
/// bit allocations for network ID fields.
///
/// @nosubgrouping
class AbstractClassRep
{
friend class ConsoleObject;
public:
/// @name 'Tructors
/// @{
AbstractClassRep()
{
VECTOR_SET_ASSOCIATION(mFieldList);
parentClass = NULL;
}
virtual ~AbstractClassRep() { }
/// @}
/// @name Representation Interface
/// @{
S32 mClassGroupMask; ///< Mask indicating in which NetGroups this object belongs.
S32 mClassType; ///< Stores the NetClass of this class.
S32 mNetEventDir; ///< Stores the NetDirection of this class.
S32 mClassId[NetClassGroupsCount]; ///< Stores the IDs assigned to this class for each group.
S32 getClassId (U32 netClassGroup) const;
static U32 getClassCRC (U32 netClassGroup);
const char* getClassName() const;
static AbstractClassRep* getClassList();
Namespace* getNameSpace();
AbstractClassRep* getNextClass();
AbstractClassRep* getParentClass();
/// Helper class to see if we are a given class, or a subclass thereof.
bool isClass(AbstractClassRep *acr)
{
AbstractClassRep *walk = this;
// Walk up parents, checking for equivalence.
while(walk)
{
if(walk == acr)
return true;
walk = walk->parentClass;
};
return false;
}
virtual ConsoleObject* create () const = 0;
protected:
virtual void init() const = 0;
const char * mClassName;
AbstractClassRep * nextClass;
AbstractClassRep * parentClass;
Namespace * mNamespace;
/// @}
/// @name Fields
/// @{
public:
/// This is a function pointer typedef to support get/set callbacks for fields
typedef bool (*SetDataNotify)( void *obj, const char *data );
typedef const char *(*GetDataNotify)( void *obj, const char *data );
enum ACRFieldTypes
{
StartGroupFieldType = 0xFFFFFFFD,
EndGroupFieldType = 0xFFFFFFFE,
DepricatedFieldType = 0xFFFFFFFF
};
struct Field {
const char* pFieldname; ///< Name of the field.
const char* pGroupname; ///< Optionally filled field containing the group name.
///
/// This is filled when type is StartField or EndField
const char* pFieldDocs; ///< Documentation about this field; see consoleDoc.cc.
bool groupExpand; ///< Flag to track expanded/not state of this group in the editor.
U32 type; ///< A type ID. @see ACRFieldTypes
U32 offset; ///< Memory offset from beginning of class for this field.
S32 elementCount; ///< Number of elements, if this is an array.
EnumTable * table; ///< If this is an enum, this points to the table defining it.
BitSet32 flag; ///< Stores various flags
TypeValidator *validator; ///< Validator, if any.
SetDataNotify setDataFn; ///< Set data notify Fn
GetDataNotify getDataFn; ///< Get data notify Fn
};
typedef Vector<Field> FieldList;
FieldList mFieldList;
bool mDynamicGroupExpand;
const Field *findField(StringTableEntry fieldName) const;
/// @}
/// @name Abstract Class Database
/// @{
protected:
static AbstractClassRep ** classTable[NetClassGroupsCount][NetClassTypesCount];
static AbstractClassRep * classLinkList;
static U32 classCRC[NetClassGroupsCount];
static bool initialized;
static ConsoleObject* create(const char* in_pClassName);
static ConsoleObject* create(const U32 groupId, const U32 typeId, const U32 in_classId);
public:
static U32 NetClassCount [NetClassGroupsCount][NetClassTypesCount];
static U32 NetClassBitSize[NetClassGroupsCount][NetClassTypesCount];
static void registerClassRep(AbstractClassRep*);
static void initialize(); // Called from Con::init once on startup
/// @}
};
inline AbstractClassRep *AbstractClassRep::getClassList()
{
return classLinkList;
}
inline U32 AbstractClassRep::getClassCRC(U32 group)
{
return classCRC[group];
}
inline AbstractClassRep *AbstractClassRep::getNextClass()
{
return nextClass;
}
inline AbstractClassRep *AbstractClassRep::getParentClass()
{
return parentClass;
}
inline S32 AbstractClassRep::getClassId(U32 group) const
{
return mClassId[group];
}
inline const char* AbstractClassRep::getClassName() const
{
return mClassName;
}
inline Namespace *AbstractClassRep::getNameSpace()
{
return mNamespace;
}
//------------------------------------------------------------------------------
//-------------------------------------- ConcreteClassRep
//
/// Helper class for AbstractClassRep.
///
/// @see AbtractClassRep
/// @see ConsoleObject
template <class T>
class ConcreteClassRep : public AbstractClassRep
{
public:
ConcreteClassRep(const char *name, S32 netClassGroupMask, S32 netClassType, S32 netEventDir, AbstractClassRep *parent)
{
// name is a static compiler string so no need to worry about copying or deleting
mClassName = name;
// Clean up mClassId
for(U32 i = 0; i < NetClassGroupsCount; i++)
mClassId[i] = -1;
// Set properties for this ACR
mClassType = netClassType;
mClassGroupMask = netClassGroupMask;
mNetEventDir = netEventDir;
parentClass = parent;
// Finally, register ourselves.
registerClassRep(this);
};
/// Perform class specific initialization tasks.
///
/// Link namespaces, call initPersistFields() and consoleInit().
void init() const
{
// Get handle to our parent class, if any, and ourselves (we are our parent's child).
AbstractClassRep *parent = T::getParentStaticClassRep();
AbstractClassRep *child = T::getStaticClassRep ();
// If we got reps, then link those namespaces! (To get proper inheritance.)
if(parent && child)
Con::classLinkNamespaces(parent->getNameSpace(), child->getNameSpace());
// Finally, do any class specific initialization...
T::initPersistFields();
T::consoleInit();
}
/// Wrap constructor.
ConsoleObject* create() const { return new T; }
};
//------------------------------------------------------------------------------
// Forward declaration of this function so it can be used in the class
const char *defaultProtectedGetFn( void *obj, const char *data );
/// Interface class to the console.
///
/// @section ConsoleObject_basics The Basics
///
/// Any object which you want to work with the console system should derive from this,
/// and access functionality through the static interface.
///
/// This class is always used with the DECLARE_CONOBJECT and IMPLEMENT_* macros.
///
/// @code
/// // A very basic example object. It will do nothing!
/// class TorqueObject : public ConsoleObject {
/// // Must provide a Parent typedef so the console system knows what we inherit from.
/// typedef ConsoleObject Parent;
///
/// // This does a lot of menial declaration for you.
/// DECLARE_CONOBJECT(TorqueObject);
///
/// // This is for us to register our fields in.
/// static void initPersistFields();
///
/// // A sample field.
/// S8 mSample;
/// }
/// @endcode
///
/// @code
/// // And the accordant implementation...
/// IMPLEMENT_CONOBJECT(TorqueObject);
///
/// void TorqueObject::initPersistFields()
/// {
/// // If you want to inherit any fields from the parent (you do), do this:
/// Parent::initPersistFields();
///
/// // Pass the field, the type, the offset, and a usage string.
/// addField("sample", TypeS8, Offset(mSample, TorqueObject), "A test field.");
/// }
/// @endcode
///
/// That's all you need to do to get a class registered with the console system. At this point,
/// you can instantiate it via script, tie methods to it using ConsoleMethod, register fields,
/// and so forth. You can also register any global variables related to the class by creating
/// a consoleInit() method.
///
/// You will need to use different IMPLEMENT_ macros in different cases; for instance, if you
/// are making a NetObject (for ghosting), a DataBlock, or a NetEvent.
///
/// @see AbstractClassRep for gory implementation details.
/// @nosubgrouping
class ConsoleObject
{
protected:
/// @deprecated This is disallowed.
ConsoleObject() { /* disallowed */ }
/// @deprecated This is disallowed.
ConsoleObject(const ConsoleObject&);
protected:
/// Get a reference to a field by name.
const AbstractClassRep::Field *findField(StringTableEntry fieldName) const;
public:
/// Gets the ClassRep.
virtual AbstractClassRep* getClassRep() const;
/// Set the value of a field.
bool setField(const char *fieldName, const char *value);
virtual ~ConsoleObject();
public:
/// @name Object Creation
/// @{
static ConsoleObject* create(const char* in_pClassName);
static ConsoleObject* create(const U32 groupId, const U32 typeId, const U32 in_classId);
/// @}
public:
/// Get the classname from a class tag.
static const char* lookupClassName(const U32 in_classTag);
protected:
/// @name Fields
/// @{
/// Mark the beginning of a group of fields.
///
/// This is used in the consoleDoc system.
/// @see console_autodoc
static void addGroup(const char* in_pGroupname, const char* in_pGroupDocs = NULL);
/// Mark the end of a group of fields.
///
/// This is used in the consoleDoc system.
/// @see console_autodoc
static void endGroup(const char* in_pGroupname);
/// Register a complex field.
///
/// @param in_pFieldname Name of the field.
/// @param in_fieldType Type of the field. @see ConsoleDynamicTypes
/// @param in_fieldOffset Offset to the field from the start of the class; calculated using the Offset() macro.
/// @param in_elementCount Number of elements in this field. Arrays of elements are assumed to be contiguous in memory.
/// @param in_table An EnumTable, if this is an enumerated field.
/// @param in_pFieldDocs Usage string for this field. @see console_autodoc
static void addField(const char* in_pFieldname,
const U32 in_fieldType,
const dsize_t in_fieldOffset,
const U32 in_elementCount = 1,
EnumTable * in_table = NULL,
const char* in_pFieldDocs = NULL);
/// Register a simple field.
///
/// @param in_pFieldname Name of the field.
/// @param in_fieldType Type of the field. @see ConsoleDynamicTypes
/// @param in_fieldOffset Offset to the field from the start of the class; calculated using the Offset() macro.
/// @param in_pFieldDocs Usage string for this field. @see console_autodoc
static void addField(const char* in_pFieldname,
const U32 in_fieldType,
const dsize_t in_fieldOffset,
const char* in_pFieldDocs);
/// Register a validated field.
///
/// A validated field is just like a normal field except that you can't
/// have it be an array, and that you give it a pointer to a TypeValidator
/// subclass, which is then used to validate any value placed in it. Invalid
/// values are ignored and an error is printed to the console.
///
/// @see addField
/// @see typeValidators.h
static void addFieldV(const char* in_pFieldname,
const U32 in_fieldType,
const dsize_t in_fieldOffset,
TypeValidator *v,
const char * in_pFieldDocs = NULL);
/// Register a complex protected field.
///
/// @param in_pFieldname Name of the field.
/// @param in_fieldType Type of the field. @see ConsoleDynamicTypes
/// @param in_fieldOffset Offset to the field from the start of the class; calculated using the Offset() macro.
/// @param in_setDataFn When this field gets set, it will call the callback provided. @see console_protected
/// @param in_getDataFn When this field is accessed for it's data, it will return the value of this function
/// @param in_elementCount Number of elements in this field. Arrays of elements are assumed to be contiguous in memory.
/// @param in_table An EnumTable, if this is an enumerated field.
/// @param in_pFieldDocs Usage string for this field. @see console_autodoc
static void addProtectedField(const char* in_pFieldname,
const U32 in_fieldType,
const dsize_t in_fieldOffset,
AbstractClassRep::SetDataNotify in_setDataFn,
AbstractClassRep::GetDataNotify in_getDataFn = &defaultProtectedGetFn,
const U32 in_elementCount = 1,
EnumTable * in_table = NULL,
const char* in_pFieldDocs = NULL);
/// Register a simple protected field.
///
/// @param in_pFieldname Name of the field.
/// @param in_fieldType Type of the field. @see ConsoleDynamicTypes
/// @param in_fieldOffset Offset to the field from the start of the class; calculated using the Offset() macro.
/// @param in_setDataFn When this field gets set, it will call the callback provided. @see console_protected
/// @param in_getDataFn When this field is accessed for it's data, it will return the value of this function
/// @param in_pFieldDocs Usage string for this field. @see console_autodoc
static void addProtectedField(const char* in_pFieldname,
const U32 in_fieldType,
const dsize_t in_fieldOffset,
AbstractClassRep::SetDataNotify in_setDataFn,
AbstractClassRep::GetDataNotify in_getDataFn = &defaultProtectedGetFn,
const char* in_pFieldDocs = NULL);
/// Add a deprecated field.
///
/// A deprecated field will always be undefined, even if you assign a value to it. This
/// is useful when you need to make sure that a field is not being used anymore.
static void addDepricatedField(const char *fieldName);
/// Remove a field.
///
/// Sometimes, you just have to remove a field!
/// @returns True on success.
static bool removeField(const char* in_pFieldname);
/// @}
public:
/// Register dynamic fields in a subclass of ConsoleObject.
///
/// @see addField(), addFieldV(), addDepricatedField(), addGroup(), endGroup()
static void initPersistFields();
/// Register global constant variables and do other one-time initialization tasks in
/// a subclass of ConsoleObject.
///
/// @deprecated You should use ConsoleMethod and ConsoleFunction, not this, to
/// register methods or commands.
/// @see console
static void consoleInit();
/// @name Field List
/// @{
/// Get a list of all the fields. This information cannot be modified.
const AbstractClassRep::FieldList& getFieldList() const;
/// Get a list of all the fields, set up so we can modify them.
///
/// @note This is a bad trick to pull if you aren't very careful,
/// since you can blast field data!
AbstractClassRep::FieldList& getModifiableFieldList();
/// Get a handle to a boolean telling us if we expanded the dynamic group.
///
/// @see GuiInspector::Inspect()
bool& getDynamicGroupExpand();
/// @}
/// @name ConsoleObject Implementation
///
/// These functions are implemented in every subclass of
/// ConsoleObject by an IMPLEMENT_CONOBJECT or IMPLEMENT_CO_* macro.
/// @{
/// Get the abstract class information for this class.
static AbstractClassRep *getStaticClassRep() { return NULL; }
/// Get the abstract class information for this class's superclass.
static AbstractClassRep *getParentStaticClassRep() { return NULL; }
/// Get our network-layer class id.
///
/// @param netClassGroup The net class for which we want our ID.
/// @see
S32 getClassId(U32 netClassGroup) const;
/// Get our compiler and platform independent class name.
///
/// @note This name can be used to instantiate another instance using create()
const char *getClassName() const;
/// @}
};
// Deprecated? -pw
// Nope, not in TGE - THB
#define addNamedField(fieldName,type,className) addField(#fieldName, type, Offset(fieldName,className))
#define addNamedFieldV(fieldName,type,className, validator) addFieldV(#fieldName, type, Offset(fieldName,className), validator)
//------------------------------------------------------------------------------
//-------------------------------------- Inlines
//
inline S32 ConsoleObject::getClassId(U32 netClassGroup) const
{
AssertFatal(getClassRep() != NULL,"Cannot get tag from non-declared dynamic class!");
return getClassRep()->getClassId(netClassGroup);
}
inline const char * ConsoleObject::getClassName() const
{
AssertFatal(getClassRep() != NULL,
"Cannot get tag from non-declared dynamic class");
return getClassRep()->getClassName();
}
inline const AbstractClassRep::Field * ConsoleObject::findField(StringTableEntry name) const
{
AssertFatal(getClassRep() != NULL,
avar("Cannot get field '%s' from non-declared dynamic class.", name));
return getClassRep()->findField(name);
}
inline bool ConsoleObject::setField(const char *fieldName, const char *value)
{
//sanity check
if ((! fieldName) || (! fieldName[0]) || (! value))
return false;
if (! getClassRep())
return false;
const AbstractClassRep::Field *myField = getClassRep()->findField(StringTable->insert(fieldName));
if (! myField)
return false;
Con::setData(
myField->type,
(void *) (((const char *)(this)) + myField->offset),
0,
1,
&value,
myField->table,
myField->flag);
return true;
}
inline ConsoleObject* ConsoleObject::create(const char* in_pClassName)
{
return AbstractClassRep::create(in_pClassName);
}
inline ConsoleObject* ConsoleObject::create(const U32 groupId, const U32 typeId, const U32 in_classId)
{
return AbstractClassRep::create(groupId, typeId, in_classId);
}
inline const AbstractClassRep::FieldList& ConsoleObject::getFieldList() const
{
return getClassRep()->mFieldList;
}
inline AbstractClassRep::FieldList& ConsoleObject::getModifiableFieldList()
{
return getClassRep()->mFieldList;
}
inline bool& ConsoleObject::getDynamicGroupExpand()
{
return getClassRep()->mDynamicGroupExpand;
}
/// @name ConsoleObject Macros
/// @{
#define DECLARE_CONOBJECT(className) \
static ConcreteClassRep<className> dynClassRep; \
static AbstractClassRep* getParentStaticClassRep(); \
static AbstractClassRep* getStaticClassRep(); \
virtual AbstractClassRep* getClassRep() const
#define IMPLEMENT_CONOBJECT(className) \
AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \
AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \
AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \
ConcreteClassRep<className> className::dynClassRep(#className, 0, -1, 0, className::getParentStaticClassRep())
#define IMPLEMENT_CO_NETOBJECT_V1(className) \
AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \
AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \
AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \
ConcreteClassRep<className> className::dynClassRep(#className, NetClassGroupGameMask, NetClassTypeObject, 0, className::getParentStaticClassRep())
#define IMPLEMENT_CO_DATABLOCK_V1(className) \
AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \
AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \
AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \
ConcreteClassRep<className> className::dynClassRep(#className, NetClassGroupGameMask, NetClassTypeDataBlock, 0, className::getParentStaticClassRep())
/// @}
//------------------------------------------------------------------------------
// Protected field default get/set functions
//
// The reason for these functions is that it will save one branch per console
// data request and script functions will still execute at the same speed as
// before the modifications to allow protected static fields. These will just
// inline and the code should be roughly the same size, and just as fast as
// before the modifications. -pw
inline bool defaultProtectedSetFn( void *obj, const char *data )
{
return true;
}
inline const char *defaultProtectedGetFn( void *obj, const char *data )
{
return data;
}
#endif //_CONSOLEOBJECT_H_

76
engine/console/consoleParser.cc Executable file
View File

@ -0,0 +1,76 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "console/console.h"
#include "console/consoleParser.h"
namespace Compiler
{
static ConsoleParser *gParserList = NULL;
static ConsoleParser *gDefaultParser = NULL;
void freeConsoleParserList(void)
{
ConsoleParser *pParser;
while(pParser = gParserList)
{
gParserList = pParser->next;
delete pParser;
}
gDefaultParser = NULL;
}
bool addConsoleParser(char *ext, fnGetCurrentFile gcf, fnGetCurrentLine gcl, fnParse p, fnRestart r, fnSetScanBuffer ssb, bool def /* = false */)
{
AssertFatal(ext && gcf && gcl && p && r, "AddConsoleParser called with one or more NULL arguments");
ConsoleParser *pParser;
if(pParser = new ConsoleParser)
{
pParser->ext = ext;
pParser->getCurrentFile = gcf;
pParser->getCurrentLine = gcl;
pParser->parse = p;
pParser->restart = r;
pParser->setScanBuffer = ssb;
if(def)
gDefaultParser = pParser;
pParser->next = gParserList;
gParserList = pParser;
return true;
}
return false;
}
ConsoleParser * getParserForFile(const char *filename)
{
char *ptr;
if(filename == NULL)
return gDefaultParser;
if(ptr = dStrrchr((char *)filename, '.'))
{
ptr++;
ConsoleParser *p;
for(p = gParserList;p;p = p->next)
{
if(dStricmp(ptr, p->ext) == 0)
return p;
}
}
return gDefaultParser;
}
} // end namespace Con

105
engine/console/consoleParser.h Executable file
View File

@ -0,0 +1,105 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//
// TorqueBASIC
// (c) Copyright 2004 Burnt Wasp
//-----------------------------------------------------------------------------
#ifndef _CONSOLE_PARSER_H_
#define _CONSOLE_PARSER_H_
#include <stdio.h>
namespace Compiler
{
//////////////////////////////////////////////////////////////////////////
/// \brief Function for GetCurrentFile from the lexer
//////////////////////////////////////////////////////////////////////////
typedef const char *(*fnGetCurrentFile)();
//////////////////////////////////////////////////////////////////////////
/// \brief Function for GetCurrentLine from the lexer
//////////////////////////////////////////////////////////////////////////
typedef S32 (*fnGetCurrentLine)();
//////////////////////////////////////////////////////////////////////////
/// \brief Function for Parse from the lexer
//////////////////////////////////////////////////////////////////////////
typedef S32 (*fnParse)();
//////////////////////////////////////////////////////////////////////////
/// \brief Function for Restart from the lexer
//////////////////////////////////////////////////////////////////////////
typedef void (*fnRestart)(FILE *input_file);
//////////////////////////////////////////////////////////////////////////
/// \brief Function for SetScanBuffer from the lexer
//////////////////////////////////////////////////////////////////////////
typedef void (*fnSetScanBuffer)(const char *sb, const char *fn);
//////////////////////////////////////////////////////////////////////////
/// \brief List of parsers for the compiler
//////////////////////////////////////////////////////////////////////////
struct ConsoleParser
{
struct ConsoleParser *next; //!< Next object in list or NULL
char *ext; //!< Filename extension handled by this parser
fnGetCurrentFile getCurrentFile; //!< GetCurrentFile lexer function
fnGetCurrentLine getCurrentLine; //!< GetCurrentLine lexer function
fnParse parse; //!< Parse lexer function
fnRestart restart; //!< Restart lexer function
fnSetScanBuffer setScanBuffer; //!< SetScanBuffer lexer function
};
// Macros
//////////////////////////////////////////////////////////////////////////
/// \brief Declare a parser's function prototypes
//////////////////////////////////////////////////////////////////////////
#define CON_DECLARE_PARSER(prefix) \
const char * prefix##GetCurrentFile(); \
S32 prefix##GetCurrentLine(); \
void prefix##SetScanBuffer(const char *sb, const char *fn); \
S32 prefix##parse(); \
void prefix##restart(FILE *input_file)
//////////////////////////////////////////////////////////////////////////
/// \brief Helper macro to add console parsers
//////////////////////////////////////////////////////////////////////////
#define CON_ADD_PARSER(prefix, ext, def) \
Compiler::addConsoleParser(ext, prefix##GetCurrentFile, prefix##GetCurrentLine, prefix##parse, \
prefix##restart, prefix##SetScanBuffer, def)
//////////////////////////////////////////////////////////////////////////
/// \brief Free the console parser list
///
/// \sa AddConsoleParser()
//////////////////////////////////////////////////////////////////////////
void freeConsoleParserList(void);
//////////////////////////////////////////////////////////////////////////
/// \brief Add a console parser to the list
///
/// \param ext Filename extension
/// \param gcf GetCurrentFile function
/// \param gcl GetCurrentLine function
/// \param p Parse function
/// \param r Restart function
/// \param ssb SetScanBuffer function
/// \param def true if this is the default parser (<b>Note:</b> set this only on the .cs parser!)
/// \return true for success, false for failure (out of memory)
/// \sa FreeConsoleParserList(), ConsoleParser
//////////////////////////////////////////////////////////////////////////
bool addConsoleParser(char *ext, fnGetCurrentFile gcf, fnGetCurrentLine gcl, fnParse p, fnRestart r, fnSetScanBuffer ssb, bool def = false);
//////////////////////////////////////////////////////////////////////////
/// \brief Get the parser for a particular file based on its extension
///
/// \param filename Filename of file to obtain parser for
/// \sa ConsoleParser
//////////////////////////////////////////////////////////////////////////
ConsoleParser * getParserForFile(const char *filename);
} // end namespace Con
#endif // _CONSOLE_PARSER_H_

527
engine/console/consoleTypes.cc Executable file
View File

@ -0,0 +1,527 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/consoleTypes.h"
#include "core/stringTable.h"
#include "core/color.h"
#include "console/simBase.h"
//////////////////////////////////////////////////////////////////////////
// TypeString
//////////////////////////////////////////////////////////////////////////
ConsoleType( string, TypeString, sizeof(const char*) )
ConsoleGetType( TypeString )
{
return *((const char **)(dptr));
}
ConsoleSetType( TypeString )
{
if(argc == 1)
*((const char **) dptr) = StringTable->insert(argv[0]);
else
Con::printf("(TypeString) Cannot set multiple args to a single string.");
}
//////////////////////////////////////////////////////////////////////////
// TypeCaseString
//////////////////////////////////////////////////////////////////////////
ConsoleType( caseString, TypeCaseString, sizeof(const char*) )
ConsoleSetType( TypeCaseString )
{
if(argc == 1)
*((const char **) dptr) = StringTable->insert(argv[0], true);
else
Con::printf("(TypeCaseString) Cannot set multiple args to a single string.");
}
ConsoleGetType( TypeCaseString )
{
return *((const char **)(dptr));
}
//////////////////////////////////////////////////////////////////////////
// TypeFileName
//////////////////////////////////////////////////////////////////////////
ConsolePrepType( filename, TypeFilename, sizeof( const char* ) )
ConsoleSetType( TypeFilename )
{
if(argc == 1)
{
char buffer[1024];
if (Con::expandScriptFilename(buffer, 1024, argv[0]))
*((const char **) dptr) = StringTable->insert(buffer);
else
Con::warnf("(TypeFilename) illegal filename detected: %s", argv[0]);
}
else
Con::printf("(TypeFilename) Cannot set multiple args to a single filename.");
}
ConsoleGetType( TypeFilename )
{
return *((const char **)(dptr));
}
ConsoleProcessData( TypeFilename )
{
if( Con::expandScriptFilename( buffer, bufferSz, data ) )
return buffer;
else
{
Con::warnf("(TypeFilename) illegal filename detected: %s", data);
return data;
}
}
//////////////////////////////////////////////////////////////////////////
// TypeS8
//////////////////////////////////////////////////////////////////////////
ConsoleType( char, TypeS8, sizeof(U8) )
ConsoleGetType( TypeS8 )
{
char* returnBuffer = Con::getReturnBuffer(256);
dSprintf(returnBuffer, 256, "%d", *((U8 *) dptr) );
return returnBuffer;
}
ConsoleSetType( TypeS8 )
{
if(argc == 1)
*((U8 *) dptr) = dAtoi(argv[0]);
else
Con::printf("(TypeU8) Cannot set multiple args to a single S32.");
}
//////////////////////////////////////////////////////////////////////////
// TypeS32
//////////////////////////////////////////////////////////////////////////
ConsoleType( int, TypeS32, sizeof(S32) )
ConsoleGetType( TypeS32 )
{
char* returnBuffer = Con::getReturnBuffer(256);
dSprintf(returnBuffer, 256, "%d", *((S32 *) dptr) );
return returnBuffer;
}
ConsoleSetType( TypeS32 )
{
if(argc == 1)
*((S32 *) dptr) = dAtoi(argv[0]);
else
Con::printf("(TypeS32) Cannot set multiple args to a single S32.");
}
//////////////////////////////////////////////////////////////////////////
// TypeS32Vector
//////////////////////////////////////////////////////////////////////////
ConsoleType( intList, TypeS32Vector, sizeof(Vector<S32>) )
ConsoleGetType( TypeS32Vector )
{
Vector<S32> *vec = (Vector<S32> *)dptr;
S32 buffSize = ( vec->size() * 15 ) + 16 ;
char* returnBuffer = Con::getReturnBuffer( buffSize );
S32 maxReturn = buffSize;
returnBuffer[0] = '\0';
S32 returnLeng = 0;
for (Vector<S32>::iterator itr = vec->begin(); itr != vec->end(); itr++)
{
// concatenate the next value onto the return string
dSprintf(returnBuffer + returnLeng, maxReturn - returnLeng, "%d ", *itr);
// update the length of the return string (so far)
returnLeng = dStrlen(returnBuffer);
}
// trim off that last extra space
if (returnLeng > 0 && returnBuffer[returnLeng - 1] == ' ')
returnBuffer[returnLeng - 1] = '\0';
return returnBuffer;
}
ConsoleSetType( TypeS32Vector )
{
Vector<S32> *vec = (Vector<S32> *)dptr;
// we assume the vector should be cleared first (not just appending)
vec->clear();
if(argc == 1)
{
const char *values = argv[0];
const char *endValues = values + dStrlen(values);
S32 value;
// advance through the string, pulling off S32's and advancing the pointer
while (values < endValues && dSscanf(values, "%d", &value) != 0)
{
vec->push_back(value);
const char *nextValues = dStrchr(values, ' ');
if (nextValues != 0 && nextValues < endValues)
values = nextValues + 1;
else
break;
}
}
else if (argc > 1)
{
for (S32 i = 0; i < argc; i++)
vec->push_back(dAtoi(argv[i]));
}
else
Con::printf("Vector<S32> must be set as { a, b, c, ... } or \"a b c ...\"");
}
//////////////////////////////////////////////////////////////////////////
// TypeF32
//////////////////////////////////////////////////////////////////////////
ConsoleType( float, TypeF32, sizeof(F32) )
ConsoleGetType( TypeF32 )
{
char* returnBuffer = Con::getReturnBuffer(256);
dSprintf(returnBuffer, 256, "%g", *((F32 *) dptr) );
return returnBuffer;
}
ConsoleSetType( TypeF32 )
{
if(argc == 1)
*((F32 *) dptr) = dAtof(argv[0]);
else
Con::printf("(TypeF32) Cannot set multiple args to a single F32.");
}
//////////////////////////////////////////////////////////////////////////
// TypeF32Vector
//////////////////////////////////////////////////////////////////////////
ConsoleType( floatList, TypeF32Vector, sizeof(Vector<F32>) )
ConsoleGetType( TypeF32Vector )
{
Vector<F32> *vec = (Vector<F32> *)dptr;
S32 buffSize = ( vec->size() * 15 ) + 16 ;
char* returnBuffer = Con::getReturnBuffer( buffSize );
S32 maxReturn = buffSize;
returnBuffer[0] = '\0';
S32 returnLeng = 0;
for (Vector<F32>::iterator itr = vec->begin(); itr != vec->end(); itr++)
{
// concatenate the next value onto the return string
dSprintf(returnBuffer + returnLeng, maxReturn - returnLeng, "%g ", *itr);
// update the length of the return string (so far)
returnLeng = dStrlen(returnBuffer);
}
// trim off that last extra space
if (returnLeng > 0 && returnBuffer[returnLeng - 1] == ' ')
returnBuffer[returnLeng - 1] = '\0';
return returnBuffer;
}
ConsoleSetType( TypeF32Vector )
{
Vector<F32> *vec = (Vector<F32> *)dptr;
// we assume the vector should be cleared first (not just appending)
vec->clear();
if(argc == 1)
{
const char *values = argv[0];
const char *endValues = values + dStrlen(values);
F32 value;
// advance through the string, pulling off F32's and advancing the pointer
while (values < endValues && dSscanf(values, "%g", &value) != 0)
{
vec->push_back(value);
const char *nextValues = dStrchr(values, ' ');
if (nextValues != 0 && nextValues < endValues)
values = nextValues + 1;
else
break;
}
}
else if (argc > 1)
{
for (S32 i = 0; i < argc; i++)
vec->push_back(dAtof(argv[i]));
}
else
Con::printf("Vector<F32> must be set as { a, b, c, ... } or \"a b c ...\"");
}
//////////////////////////////////////////////////////////////////////////
// TypeBool
//////////////////////////////////////////////////////////////////////////
ConsoleType( bool, TypeBool, sizeof(bool) )
ConsoleGetType( TypeBool )
{
return *((bool *) dptr) ? "1" : "0";
}
ConsoleSetType( TypeBool )
{
if(argc == 1)
*((bool *) dptr) = dAtob(argv[0]);
else
Con::printf("(TypeBool) Cannot set multiple args to a single bool.");
}
//////////////////////////////////////////////////////////////////////////
// TypeBoolVector
//////////////////////////////////////////////////////////////////////////
ConsoleType( boolList, TypeBoolVector, sizeof(Vector<bool>) )
ConsoleGetType( TypeBoolVector )
{
Vector<bool> *vec = (Vector<bool>*)dptr;
char* returnBuffer = Con::getReturnBuffer(1024);
S32 maxReturn = 1024;
returnBuffer[0] = '\0';
S32 returnLeng = 0;
for (Vector<bool>::iterator itr = vec->begin(); itr < vec->end(); itr++)
{
// concatenate the next value onto the return string
dSprintf(returnBuffer + returnLeng, maxReturn - returnLeng, "%d ", (*itr == true ? 1 : 0));
returnLeng = dStrlen(returnBuffer);
}
// trim off that last extra space
if (returnLeng > 0 && returnBuffer[returnLeng - 1] == ' ')
returnBuffer[returnLeng - 1] = '\0';
return(returnBuffer);
}
ConsoleSetType( TypeBoolVector )
{
Vector<bool> *vec = (Vector<bool>*)dptr;
// we assume the vector should be cleared first (not just appending)
vec->clear();
if (argc == 1)
{
const char *values = argv[0];
const char *endValues = values + dStrlen(values);
S32 value;
// advance through the string, pulling off bool's and advancing the pointer
while (values < endValues && dSscanf(values, "%d", &value) != 0)
{
vec->push_back(value == 0 ? false : true);
const char *nextValues = dStrchr(values, ' ');
if (nextValues != 0 && nextValues < endValues)
values = nextValues + 1;
else
break;
}
}
else if (argc > 1)
{
for (S32 i = 0; i < argc; i++)
vec->push_back(dAtob(argv[i]));
}
else
Con::printf("Vector<bool> must be set as { a, b, c, ... } or \"a b c ...\"");
}
//////////////////////////////////////////////////////////////////////////
// TypeEnum
//////////////////////////////////////////////////////////////////////////
ConsoleType( enumval, TypeEnum, sizeof(S32) )
ConsoleGetType( TypeEnum )
{
AssertFatal(tbl, "Null enum table passed to getDataTypeEnum()");
S32 dptrVal = *(S32*)dptr;
for (S32 i = 0; i < tbl->size; i++)
{
if (dptrVal == tbl->table[i].index)
{
return tbl->table[i].label;
}
}
//not found
return "";
}
ConsoleSetType( TypeEnum )
{
AssertFatal(tbl, "Null enum table passed to setDataTypeEnum()");
if (argc != 1) return;
S32 val = 0;
for (S32 i = 0; i < tbl->size; i++)
{
if (! dStricmp(argv[0], tbl->table[i].label))
{
val = tbl->table[i].index;
break;
}
}
*((S32 *) dptr) = val;
}
//////////////////////////////////////////////////////////////////////////
// TypeFlag
//////////////////////////////////////////////////////////////////////////
ConsoleType( flag, TypeFlag, sizeof(S32) )
ConsoleGetType( TypeFlag )
{
BitSet32 tempFlags = *(BitSet32 *)dptr;
if (tempFlags.test(flag)) return "true";
else return "false";
}
ConsoleSetType( TypeFlag )
{
bool value = true;
if (argc != 1)
{
Con::printf("flag must be true or false");
}
else
{
value = dAtob(argv[0]);
}
((BitSet32 *)dptr)->set(flag, value);
}
//////////////////////////////////////////////////////////////////////////
// TypeColorF
//////////////////////////////////////////////////////////////////////////
ConsoleType( ColorF, TypeColorF, sizeof(ColorF) )
ConsoleGetType( TypeColorF )
{
ColorF * color = (ColorF*)dptr;
char* returnBuffer = Con::getReturnBuffer(256);
dSprintf(returnBuffer, 256, "%g %g %g %g", color->red, color->green, color->blue, color->alpha);
return(returnBuffer);
}
ConsoleSetType( TypeColorF )
{
ColorF *tmpColor = (ColorF *) dptr;
if(argc == 1)
{
tmpColor->set(0, 0, 0, 1);
F32 r,g,b,a;
S32 args = dSscanf(argv[0], "%g %g %g %g", &r, &g, &b, &a);
tmpColor->red = r;
tmpColor->green = g;
tmpColor->blue = b;
if (args == 4)
tmpColor->alpha = a;
}
else if(argc == 3)
{
tmpColor->red = dAtof(argv[0]);
tmpColor->green = dAtof(argv[1]);
tmpColor->blue = dAtof(argv[2]);
tmpColor->alpha = 1.f;
}
else if(argc == 4)
{
tmpColor->red = dAtof(argv[0]);
tmpColor->green = dAtof(argv[1]);
tmpColor->blue = dAtof(argv[2]);
tmpColor->alpha = dAtof(argv[3]);
}
else
Con::printf("Color must be set as { r, g, b [,a] }");
}
//////////////////////////////////////////////////////////////////////////
// TypeColorI
//////////////////////////////////////////////////////////////////////////
ConsoleType( ColorI, TypeColorI, sizeof(ColorI) )
ConsoleGetType( TypeColorI )
{
ColorI *color = (ColorI *) dptr;
char* returnBuffer = Con::getReturnBuffer(256);
dSprintf(returnBuffer, 256, "%d %d %d %d", color->red, color->green, color->blue, color->alpha);
return returnBuffer;
}
ConsoleSetType( TypeColorI )
{
ColorI *tmpColor = (ColorI *) dptr;
if(argc == 1)
{
tmpColor->set(0, 0, 0, 255);
S32 r,g,b,a;
S32 args = dSscanf(argv[0], "%d %d %d %d", &r, &g, &b, &a);
tmpColor->red = r;
tmpColor->green = g;
tmpColor->blue = b;
if (args == 4)
tmpColor->alpha = a;
}
else if(argc == 3)
{
tmpColor->red = dAtoi(argv[0]);
tmpColor->green = dAtoi(argv[1]);
tmpColor->blue = dAtoi(argv[2]);
tmpColor->alpha = 255;
}
else if(argc == 4)
{
tmpColor->red = dAtoi(argv[0]);
tmpColor->green = dAtoi(argv[1]);
tmpColor->blue = dAtoi(argv[2]);
tmpColor->alpha = dAtoi(argv[3]);
}
else
Con::printf("Color must be set as { r, g, b [,a] }");
}
//////////////////////////////////////////////////////////////////////////
// TypeSimObjectPtr
//////////////////////////////////////////////////////////////////////////
ConsoleType( SimObjectPtr, TypeSimObjectPtr, sizeof(SimObject*) )
ConsoleSetType( TypeSimObjectPtr )
{
if(argc == 1)
{
SimObject **obj = (SimObject **)dptr;
*obj = Sim::findObject(argv[0]);
}
else
Con::printf("(TypeSimObjectPtr) Cannot set multiple args to a single S32.");
}
ConsoleGetType( TypeSimObjectPtr )
{
SimObject **obj = (SimObject**)dptr;
char* returnBuffer = Con::getReturnBuffer(256);
dSprintf(returnBuffer, 256, "%s", *obj ? (*obj)->getName() ? (*obj)->getName() : (*obj)->getIdString() : "");
return returnBuffer;
}
//////////////////////////////////////////////////////////////////////////
// TypeSimObjectName
//////////////////////////////////////////////////////////////////////////
ConsoleType( SimObjectPtr, TypeSimObjectName, sizeof(SimObject*) )
ConsoleSetType( TypeSimObjectName )
{
if(argc == 1)
{
SimObject **obj = (SimObject **)dptr;
*obj = Sim::findObject(argv[0]);
}
else
Con::printf("(TypeSimObjectName) Cannot set multiple args to a single S32.");
}
ConsoleGetType( TypeSimObjectName )
{
SimObject **obj = (SimObject**)dptr;
char* returnBuffer = Con::getReturnBuffer(128);
dSprintf(returnBuffer, 128, "%s", *obj && (*obj)->getName() ? (*obj)->getName() : "");
return returnBuffer;
}

39
engine/console/consoleTypes.h Executable file
View File

@ -0,0 +1,39 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _CONSOLETYPES_H_
#define _CONSOLETYPES_H_
#ifndef _DYNAMIC_CONSOLETYPES_H_
#include "console/dynamicTypes.h"
#endif
#ifndef Offset
#if defined(TORQUE_COMPILER_GCC) && (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))
#define Offset(m,T) ((int)(&((T *)1)->m) - 1)
#else
#define Offset(x, cls) ((dsize_t)((const char *)&(((cls *)0)->x)-(const char *)0))
#endif
#endif
// Define Core Console Types
DefineConsoleType( TypeF32 )
DefineConsoleType( TypeS8 )
DefineConsoleType( TypeS32 )
DefineConsoleType( TypeS32Vector )
DefineConsoleType( TypeBool )
DefineConsoleType( TypeBoolVector )
DefineConsoleType( TypeF32Vector )
DefineConsoleType( TypeString )
DefineConsoleType( TypeCaseString )
DefineConsoleType( TypeFilename )
DefineConsoleType( TypeEnum )
DefineConsoleType( TypeFlag )
DefineConsoleType( TypeColorI )
DefineConsoleType( TypeColorF )
DefineConsoleType( TypeSimObjectPtr )
DefineConsoleType( TypeSimObjectName )
#endif

72
engine/console/dynamicTypes.cc Executable file
View File

@ -0,0 +1,72 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/dynamicTypes.h"
// Init the globals.
ConsoleBaseType *ConsoleBaseType::smListHead = NULL;
S32 ConsoleBaseType::smConsoleTypeCount = 0;
// And, we also privately store the types lookup table.
VectorPtr<ConsoleBaseType*> gConsoleTypeTable;
ConsoleBaseType *ConsoleBaseType::getListHead()
{
return smListHead;
}
void ConsoleBaseType::initialize()
{
// Prep and empty the vector.
gConsoleTypeTable.setSize(smConsoleTypeCount+1);
dMemset(gConsoleTypeTable.address(), 0, sizeof(ConsoleBaseType*) * gConsoleTypeTable.size());
// Walk the list and register each one with the console system.
ConsoleBaseType *walk = getListHead();
while(walk)
{
// Store a pointer to the type in the appropriate slot.
const S32 id = walk->getTypeID();
AssertFatal(gConsoleTypeTable[id]==NULL, "ConsoleBaseType::initialize - encountered a table slot that contained something!");
gConsoleTypeTable[id] = walk;
// Advance down the list...
walk = walk->getListNext();
}
// Alright, we're all done here; we can now achieve fast lookups by ID.
}
ConsoleBaseType *ConsoleBaseType::getType(const S32 typeID)
{
return gConsoleTypeTable[typeID];
}
//-------------------------------------------------------------------------
ConsoleBaseType::ConsoleBaseType(const S32 size, S32 *idPtr, const char *aTypeName)
{
// General initialization.
mInspectorFieldType = NULL;
// Store general info.
mTypeSize = size;
mTypeName = aTypeName;
// Get our type ID and store it.
mTypeID = smConsoleTypeCount++;
*idPtr = mTypeID;
// Link ourselves into the list.
mListNext = smListHead;
smListHead = this;
// Alright, all done for now. Console initialization time code
// takes us from having a list of general info and classes to
// a fully initialized type table.
}
ConsoleBaseType::~ConsoleBaseType()
{
// Nothing to do for now; we could unlink ourselves from the list, but why?
}

123
engine/console/dynamicTypes.h Executable file
View File

@ -0,0 +1,123 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _DYNAMIC_CONSOLETYPES_H_
#define _DYNAMIC_CONSOLETYPES_H_
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
class ConsoleBaseType
{
protected:
/// This is used to generate unique IDs for each type.
static S32 smConsoleTypeCount;
/// We maintain a linked list of all console types; this is its head.
static ConsoleBaseType *smListHead;
/// Next item in the list of all console types.
ConsoleBaseType *mListNext;
/// Destructor is private to avoid people mucking up the list.
~ConsoleBaseType();
S32 mTypeID;
dsize_t mTypeSize;
const char *mTypeName;
const char *mInspectorFieldType;
public:
/// @name cbt_list List Interface
///
/// Interface for accessing/traversing the list of types.
/// Get the head of the list.
static ConsoleBaseType *getListHead();
/// Get the item that follows this item in the list.
ConsoleBaseType *getListNext() const
{
return mListNext;
}
/// Called once to initialize the console type system.
static void initialize();
/// Call me to get a pointer to a type's info.
static ConsoleBaseType *getType(const S32 typeID);
/// @}
/// The constructor is responsible for linking an element into the
/// master list, registering the type ID, etc.
ConsoleBaseType(const S32 size, S32 *idPtr, const char *aTypeName);
const S32 getTypeID() const { return mTypeID; }
const S32 getTypeSize() const { return mTypeSize; }
const char *getTypeName() const { return mTypeName; }
void setInspectorFieldType(const char *type) { mInspectorFieldType = type; }
const char *getInspectorFieldType() { return mInspectorFieldType; }
virtual void setData(void *dptr, S32 argc, const char **argv, EnumTable *tbl, BitSet32 flag)=0;
virtual const char *getData(void *dptr, EnumTable *tbl, BitSet32 flag )=0;
virtual const char *getTypeClassName()=0;
virtual const bool isDatablock() { return false; };
virtual const char *prepData(const char *data, char *buffer, U32 bufferLen) { return data; };
};
#define DefineConsoleType( type ) extern S32 type;
#define ConsoleType( typeName, type, size ) \
class ConsoleType##type : public ConsoleBaseType \
{ \
public: \
ConsoleType##type (const S32 aSize, S32 *idPtr, const char *aTypeName) : ConsoleBaseType(aSize, idPtr, aTypeName) { } \
virtual void setData(void *dptr, S32 argc, const char **argv, EnumTable *tbl, BitSet32 flag); \
virtual const char *getData(void *dptr, EnumTable *tbl, BitSet32 flag ); \
virtual const char *getTypeClassName() { return #typeName ; } \
}; \
S32 type = -1; \
ConsoleType##type gConsoleType##type##Instance(size,&type,#type); \
#define ConsolePrepType( typeName, type, size ) \
class ConsoleType##type : public ConsoleBaseType \
{ \
public: \
ConsoleType##type (const S32 aSize, S32 *idPtr, const char *aTypeName) : ConsoleBaseType(aSize, idPtr, aTypeName) { } \
virtual void setData(void *dptr, S32 argc, const char **argv, EnumTable *tbl, BitSet32 flag); \
virtual const char *getData(void *dptr, EnumTable *tbl, BitSet32 flag ); \
virtual const char *getTypeClassName() { return #typeName; }; \
virtual const char *prepData(const char *data, char *buffer, U32 bufferLen); \
}; \
S32 type = -1; \
ConsoleType##type gConsoleType##type##Instance(size,&type,#type); \
#define ConsoleSetType( type ) \
void ConsoleType##type::setData(void *dptr, S32 argc, const char **argv, EnumTable *tbl, BitSet32 flag)
#define ConsoleGetType( type ) \
const char *ConsoleType##type::getData(void *dptr, EnumTable *tbl, BitSet32 flag)
#define ConsoleProcessData( type ) \
const char *ConsoleType##type::prepData(const char *data, char *buffer, U32 bufferSz)
#define DatablockConsoleType( typeName, type, size, className ) \
class ConsoleType##type : public ConsoleBaseType \
{ \
public: \
ConsoleType##type (const S32 aSize, S32 *idPtr, const char *aTypeName) : ConsoleBaseType(aSize, idPtr, aTypeName) { } \
virtual void setData(void *dptr, S32 argc, const char **argv, EnumTable *tbl, BitSet32 flag); \
virtual const char *getData(void *dptr, EnumTable *tbl, BitSet32 flag ); \
virtual const char *getTypeClassName() { return #className; }; \
virtual const bool isDatablock() { return true; }; \
}; \
S32 type = -1; \
ConsoleType##type gConsoleType##type##Instance(size,&type,#type); \
#endif

299
engine/console/scriptObject.cc Executable file
View File

@ -0,0 +1,299 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "console/simBase.h"
#include "console/consoleTypes.h"
//-----------------------------------------------------------------------------
// Script object placeholder
//-----------------------------------------------------------------------------
class ScriptObject : public SimObject
{
typedef SimObject Parent;
StringTableEntry mClassName;
StringTableEntry mSuperClassName;
public:
ScriptObject();
bool onAdd();
void onRemove();
DECLARE_CONOBJECT(ScriptObject);
static void initPersistFields();
};
IMPLEMENT_CONOBJECT(ScriptObject);
void ScriptObject::initPersistFields()
{
addGroup("Classes", "Script objects have the ability to inherit and have class information.");
addField("class", TypeString, Offset(mClassName, ScriptObject), "Class of object.");
addField("superClass", TypeString, Offset(mSuperClassName, ScriptObject), "Superclass of object.");
endGroup("Classes");
}
ScriptObject::ScriptObject()
{
mClassName = "";
mSuperClassName = "";
}
bool ScriptObject::onAdd()
{
if (!Parent::onAdd())
return false;
// it's possible that all the namespace links can fail, if
// multiple objects are named the same thing with different script
// hierarchies.
// linkNamespaces will now return false and echo an error message
// rather than asserting.
// superClassName -> ScriptObject
StringTableEntry parent = StringTable->insert("ScriptObject");
if(mSuperClassName[0])
{
if(Con::linkNamespaces(parent, mSuperClassName))
parent = mSuperClassName;
}
// className -> superClassName
if (mClassName[0])
{
if(Con::linkNamespaces(parent, mClassName))
parent = mClassName;
}
// objectName -> className
StringTableEntry objectName = getName();
if (objectName && objectName[0])
{
if(Con::linkNamespaces(parent, objectName))
parent = objectName;
}
// Store our namespace
mNameSpace = Con::lookupNamespace(parent);
// Call onAdd in script!
Con::executef(this, 2, "onAdd", Con::getIntArg(getId()));
return true;
}
void ScriptObject::onRemove()
{
// We call this on this objects namespace so we unlink them after. - jdd
//
// Call onRemove in script!
Con::executef(this, 2, "onRemove", Con::getIntArg(getId()));
// Restore NameSpace's
StringTableEntry child = getName();
if( child && child[0] )
{
if(mClassName && mClassName[0])
{
if(Con::unlinkNamespaces(mClassName, child))
child = mClassName;
}
if(mSuperClassName && mSuperClassName[0])
{
if(Con::unlinkNamespaces(mSuperClassName, child))
child = mSuperClassName;
}
Con::unlinkNamespaces(getClassName(), child);
}
else
{
child = mClassName;
if(child && child[0])
{
if(mSuperClassName && mSuperClassName[0])
{
if(Con::unlinkNamespaces(mSuperClassName, child))
child = mSuperClassName;
}
Con::unlinkNamespaces(getClassName(), child);
}
else
{
if(mSuperClassName && mSuperClassName[0])
Con::unlinkNamespaces(getClassName(), mSuperClassName);
}
}
Parent::onRemove();
}
//-----------------------------------------------------------------------------
// Script group placeholder
//-----------------------------------------------------------------------------
class ScriptGroup : public SimGroup
{
typedef SimGroup Parent;
StringTableEntry mClassName;
StringTableEntry mSuperClassName;
public:
ScriptGroup();
bool onAdd();
void onRemove();
DECLARE_CONOBJECT(ScriptGroup);
static void initPersistFields();
};
IMPLEMENT_CONOBJECT(ScriptGroup);
void ScriptGroup::initPersistFields()
{
addGroup("Classes");
addField("class", TypeString, Offset(mClassName, ScriptGroup));
addField("superClass", TypeString, Offset(mSuperClassName, ScriptGroup));
endGroup("Classes");
}
ScriptGroup::ScriptGroup()
{
mClassName = "";
mSuperClassName = "";
}
bool ScriptGroup::onAdd()
{
if (!Parent::onAdd())
return false;
// superClassName -> ScriptGroup
StringTableEntry parent = StringTable->insert("ScriptGroup");
if(mSuperClassName[0])
{
if(Con::linkNamespaces(parent, mSuperClassName))
parent = mSuperClassName;
}
// className -> superClassName
if(mClassName[0])
{
if(Con::linkNamespaces(parent, mClassName))
parent = mClassName;
}
// objectName -> className
StringTableEntry objectName = getName();
if (objectName && objectName[0])
{
if(Con::linkNamespaces(parent, objectName))
parent = objectName;
}
// Store our namespace
mNameSpace = Con::lookupNamespace(parent);
// Call onAdd in script!
Con::executef(this, 2, "onAdd", Con::getIntArg(getId()));
return true;
}
void ScriptGroup::onRemove()
{
// Call onRemove in script!
Con::executef(this, 2, "onRemove", Con::getIntArg(getId()));
Parent::onRemove();
}
//-----------------------------------------------------------------------------
// Script Class placeholder
//-----------------------------------------------------------------------------
class ScriptClass : public SimObject
{
typedef SimObject Parent;
StringTableEntry mLibraryName;
StringTableEntry mClassName;
public:
ScriptClass();
DECLARE_CONOBJECT(ScriptClass);
bool onAdd();
static void initPersistFields();
};
IMPLEMENT_CONOBJECT(ScriptClass);
void ScriptClass::initPersistFields()
{
Parent::initPersistFields();
addGroup("ClassLibrary", "Script objects have the ability to inherit and have class information.");
addField("libraryName", TypeString, Offset(mLibraryName, ScriptClass), "Class Library This Belongs to.");
addField("className", TypeString, Offset(mClassName, ScriptClass), "Script Class Namespace this object defines.");
endGroup("ClassLibrary");
}
ScriptClass::ScriptClass()
{
mLibraryName = "";
mClassName = "";
}
bool ScriptClass::onAdd()
{
SimGroup *scriptClassGroup = Sim::getScriptClassGroup();
if( !scriptClassGroup )
{
Con::errorf("ScriptClass::onAdd - No ScriptClassGroup found!");
return false;
}
if (!Parent::onAdd())
return false;
// No library goes in root.
if( !mLibraryName || mLibraryName == "" )
{
scriptClassGroup->addObject( this );
return true;
}
SimGroup *libraryGroup = dynamic_cast<SimGroup*>( scriptClassGroup->findObjectByInternalName( mLibraryName ) );
if( libraryGroup != NULL )
{
libraryGroup->addObject( this );
return true;
}
libraryGroup = new SimGroup();
if ( libraryGroup == NULL )
{
Con::errorf("ScriptClass::onAdd - Unable to create non-existent Script ClassLibrary %s!", mLibraryName );
return false;
}
// Register the Script ClassLibrary SimGroup
libraryGroup->registerObject();
// Set Internal Name
libraryGroup->setInternalName( StringTable->insert( mLibraryName ) );
// Add to ScriptClassGroup SimGroup
scriptClassGroup->addObject( libraryGroup );
// Add ourselves to our new Script ClassLibrary
libraryGroup->addObject( this );
// Success!
return true;
}

2033
engine/console/simBase.cc Executable file

File diff suppressed because it is too large Load Diff

1406
engine/console/simBase.h Executable file

File diff suppressed because it is too large Load Diff

301
engine/console/simDictionary.cc Executable file
View File

@ -0,0 +1,301 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/simDictionary.h"
#include "console/simBase.h"
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
extern S32 HashPointer(StringTableEntry e);
SimNameDictionary::SimNameDictionary()
{
hashTable = NULL;
mutex = Mutex::createMutex();
}
SimNameDictionary::~SimNameDictionary()
{
delete[] hashTable;
Mutex::destroyMutex(mutex);
}
void SimNameDictionary::insert(SimObject* obj)
{
if(!obj->objectName)
return;
Mutex::lockMutex(mutex);
if(!hashTable)
{
hashTable = new SimObject *[DefaultTableSize];
hashTableSize = DefaultTableSize;
hashEntryCount = 0;
S32 i;
for(i = 0; i < hashTableSize; i++)
hashTable[i] = NULL;
}
S32 idx = HashPointer(obj->objectName) % hashTableSize;
obj->nextNameObject = hashTable[idx];
hashTable[idx] = obj;
hashEntryCount++;
if(hashEntryCount > hashTableSize)
{
// resize the hash table
S32 i;
SimObject *head = NULL, *walk, *temp;
for(i = 0; i < hashTableSize; i++) {
walk = hashTable[i];
while(walk)
{
temp = walk->nextNameObject;
walk->nextNameObject = head;
head = walk;
walk = temp;
}
}
delete[] hashTable;
hashTableSize = hashTableSize * 2 + 1;
hashTable = new SimObject *[hashTableSize];
for(i = 0; i < hashTableSize;i++)
hashTable[i] = NULL;
while(head)
{
temp = head->nextNameObject;
idx = HashPointer(head->objectName) % hashTableSize;
head->nextNameObject = hashTable[idx];
hashTable[idx] = head;
head = temp;
}
}
Mutex::unlockMutex(mutex);
}
SimObject* SimNameDictionary::find(StringTableEntry name)
{
// NULL is a valid lookup - it will always return NULL
if(!hashTable)
return NULL;
Mutex::lockMutex(mutex);
S32 idx = HashPointer(name) % hashTableSize;
SimObject *walk = hashTable[idx];
while(walk)
{
if(walk->objectName == name)
{
Mutex::unlockMutex(mutex);
return walk;
}
walk = walk->nextNameObject;
}
Mutex::unlockMutex(mutex);
return NULL;
}
void SimNameDictionary::remove(SimObject* obj)
{
if(!obj->objectName)
return;
Mutex::lockMutex(mutex);
SimObject **walk = &hashTable[HashPointer(obj->objectName) % hashTableSize];
while(*walk)
{
if(*walk == obj)
{
*walk = obj->nextNameObject;
obj->nextNameObject = (SimObject*)-1;
hashEntryCount--;
Mutex::unlockMutex(mutex);
return;
}
walk = &((*walk)->nextNameObject);
}
Mutex::unlockMutex(mutex);
}
//----------------------------------------------------------------------------
SimManagerNameDictionary::SimManagerNameDictionary()
{
hashTable = new SimObject *[DefaultTableSize];
hashTableSize = DefaultTableSize;
hashEntryCount = 0;
S32 i;
for(i = 0; i < hashTableSize; i++)
hashTable[i] = NULL;
mutex = Mutex::createMutex();
}
SimManagerNameDictionary::~SimManagerNameDictionary()
{
delete[] hashTable;
Mutex::destroyMutex(mutex);
}
void SimManagerNameDictionary::insert(SimObject* obj)
{
if(!obj->objectName)
return;
Mutex::lockMutex(mutex);
S32 idx = HashPointer(obj->objectName) % hashTableSize;
obj->nextManagerNameObject = hashTable[idx];
hashTable[idx] = obj;
hashEntryCount++;
if(hashEntryCount > hashTableSize)
{
// resize the hash table
S32 i;
SimObject *head = NULL, *walk, *temp;
for(i = 0; i < hashTableSize; i++) {
walk = hashTable[i];
while(walk)
{
temp = walk->nextManagerNameObject;
walk->nextManagerNameObject = head;
head = walk;
walk = temp;
}
}
delete[] hashTable;
hashTableSize = hashTableSize * 2 + 1;
hashTable = new SimObject *[hashTableSize];
for(i = 0; i < hashTableSize;i++)
hashTable[i] = NULL;
while(head)
{
temp = head->nextManagerNameObject;
idx = HashPointer(head->objectName) % hashTableSize;
head->nextManagerNameObject = hashTable[idx];
hashTable[idx] = head;
head = temp;
}
}
Mutex::unlockMutex(mutex);
}
SimObject* SimManagerNameDictionary::find(StringTableEntry name)
{
// NULL is a valid lookup - it will always return NULL
Mutex::lockMutex(mutex);
S32 idx = HashPointer(name) % hashTableSize;
SimObject *walk = hashTable[idx];
while(walk)
{
if(walk->objectName == name)
{
Mutex::unlockMutex(mutex);
return walk;
}
walk = walk->nextManagerNameObject;
}
Mutex::unlockMutex(mutex);
return NULL;
}
void SimManagerNameDictionary::remove(SimObject* obj)
{
if(!obj->objectName)
return;
Mutex::lockMutex(mutex);
SimObject **walk = &hashTable[HashPointer(obj->objectName) % hashTableSize];
while(*walk)
{
if(*walk == obj)
{
*walk = obj->nextManagerNameObject;
obj->nextManagerNameObject = (SimObject*)-1;
hashEntryCount--;
Mutex::unlockMutex(mutex);
return;
}
walk = &((*walk)->nextManagerNameObject);
}
Mutex::unlockMutex(mutex);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
SimIdDictionary::SimIdDictionary()
{
for(S32 i = 0; i < DefaultTableSize; i++)
table[i] = NULL;
mutex = Mutex::createMutex();
}
SimIdDictionary::~SimIdDictionary()
{
Mutex::destroyMutex(mutex);
}
void SimIdDictionary::insert(SimObject* obj)
{
Mutex::lockMutex(mutex);
S32 idx = obj->getId() & TableBitMask;
obj->nextIdObject = table[idx];
AssertFatal( obj->nextIdObject != obj, "SimIdDictionary::insert - Creating Infinite Loop linking to self!" );
table[idx] = obj;
Mutex::unlockMutex(mutex);
}
SimObject* SimIdDictionary::find(S32 id)
{
Mutex::lockMutex(mutex);
S32 idx = id & TableBitMask;
SimObject *walk = table[idx];
while(walk)
{
if(walk->getId() == U32(id))
{
Mutex::unlockMutex(mutex);
return walk;
}
walk = walk->nextIdObject;
}
Mutex::unlockMutex(mutex);
return NULL;
}
void SimIdDictionary::remove(SimObject* obj)
{
Mutex::lockMutex(mutex);
SimObject **walk = &table[obj->getId() & TableBitMask];
while(*walk && *walk != obj)
walk = &((*walk)->nextIdObject);
if(*walk)
*walk = obj->nextIdObject;
Mutex::unlockMutex(mutex);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

95
engine/console/simDictionary.h Executable file
View File

@ -0,0 +1,95 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _SIMDICTIONARY_H_
#define _SIMDICTIONARY_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _STRINGTABLE_H_
#include "core/stringTable.h"
#endif
#ifndef _PLATFORMMUTEX_H_
#include "platform/platformMutex.h"
#endif
class SimObject;
//----------------------------------------------------------------------------
/// Map of names to SimObjects
///
/// Provides fast lookup for name->object and
/// for fast removal of an object given object*
class SimNameDictionary
{
enum
{
DefaultTableSize = 29
};
SimObject **hashTable; // hash the pointers of the names...
S32 hashTableSize;
S32 hashEntryCount;
void *mutex;
public:
void insert(SimObject* obj);
void remove(SimObject* obj);
SimObject* find(StringTableEntry name);
SimNameDictionary();
~SimNameDictionary();
};
class SimManagerNameDictionary
{
enum
{
DefaultTableSize = 29
};
SimObject **hashTable; // hash the pointers of the names...
S32 hashTableSize;
S32 hashEntryCount;
void *mutex;
public:
void insert(SimObject* obj);
void remove(SimObject* obj);
SimObject* find(StringTableEntry name);
SimManagerNameDictionary();
~SimManagerNameDictionary();
};
//----------------------------------------------------------------------------
/// Map of ID's to SimObjects.
///
/// Provides fast lookup for ID->object and
/// for fast removal of an object given object*
class SimIdDictionary
{
enum
{
DefaultTableSize = 4096,
TableBitMask = 4095
};
SimObject *table[DefaultTableSize];
void *mutex;
public:
void insert(SimObject* obj);
void remove(SimObject* obj);
SimObject* find(S32 id);
SimIdDictionary();
~SimIdDictionary();
};
#endif //_SIMDICTIONARY_H_

537
engine/console/simManager.cc Executable file
View File

@ -0,0 +1,537 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/platformMutex.h"
#include "console/simBase.h"
#include "core/stringTable.h"
#include "console/console.h"
#include "core/fileStream.h"
#include "core/resManager.h"
#include "core/fileObject.h"
#include "console/consoleInternal.h"
#include "core/idGenerator.h"
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// We comment out the implementation of the Con namespace when doxygenizing because
// otherwise Doxygen decides to ignore our docs in console.h
#ifndef DOXYGENIZING
namespace Sim
{
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// event queue variables:
SimTime gCurrentTime;
SimTime gTargetTime;
void *gEventQueueMutex;
SimEvent *gEventQueue;
U32 gEventSequence;
//---------------------------------------------------------------------------
// event queue init/shutdown
static void initEventQueue()
{
gCurrentTime = 0;
gTargetTime = 0;
gEventSequence = 1;
gEventQueue = NULL;
gEventQueueMutex = Mutex::createMutex();
}
static void shutdownEventQueue()
{
// Delete all pending events
Mutex::lockMutex(gEventQueueMutex);
SimEvent *walk = gEventQueue;
while(walk)
{
SimEvent *temp = walk->nextEvent;
delete walk;
walk = temp;
}
Mutex::unlockMutex(gEventQueueMutex);
Mutex::destroyMutex(gEventQueueMutex);
}
//---------------------------------------------------------------------------
// event post
U32 postEvent(SimObject *destObject, SimEvent* event,U32 time)
{
AssertFatal(time >= getCurrentTime(),
"Sim::postEvent: Cannot go back in time. (flux capacitor unavailable -- BJG)");
AssertFatal(destObject, "Destination object for event doesn't exist.");
Mutex::lockMutex(gEventQueueMutex);
event->time = time;
event->destObject = destObject;
if(!destObject)
{
delete event;
Mutex::unlockMutex(gEventQueueMutex);
return InvalidEventId;
}
event->sequenceCount = gEventSequence++;
SimEvent **walk = &gEventQueue;
SimEvent *current;
while((current = *walk) != NULL && (current->time < event->time))
walk = &(current->nextEvent);
// [tom, 6/24/2005] This ensures that SimEvents are dispatched in the same order that they are posted.
// This is needed to ensure Con::threadSafeExecute() executes script code in the correct order.
while((current = *walk) != NULL && (current->time == event->time))
walk = &(current->nextEvent);
event->nextEvent = current;
*walk = event;
U32 seqCount = event->sequenceCount;
Mutex::unlockMutex(gEventQueueMutex);
return seqCount;
}
//---------------------------------------------------------------------------
// event cancellation
void cancelEvent(U32 eventSequence)
{
Mutex::lockMutex(gEventQueueMutex);
SimEvent **walk = &gEventQueue;
SimEvent *current;
while((current = *walk) != NULL)
{
if(current->sequenceCount == eventSequence)
{
*walk = current->nextEvent;
delete current;
Mutex::unlockMutex(gEventQueueMutex);
return;
}
else
walk = &(current->nextEvent);
}
Mutex::unlockMutex(gEventQueueMutex);
}
static void cancelPendingEvents(SimObject *obj)
{
Mutex::lockMutex(gEventQueueMutex);
SimEvent **walk = &gEventQueue;
SimEvent *current;
while((current = *walk) != NULL)
{
if(current->destObject == obj)
{
*walk = current->nextEvent;
delete current;
}
else
walk = &(current->nextEvent);
}
Mutex::unlockMutex(gEventQueueMutex);
}
//---------------------------------------------------------------------------
// event pending test
bool isEventPending(U32 eventSequence)
{
Mutex::lockMutex(gEventQueueMutex);
for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent)
if(walk->sequenceCount == eventSequence)
{
Mutex::unlockMutex(gEventQueueMutex);
return true;
}
Mutex::unlockMutex(gEventQueueMutex);
return false;
}
U32 getEventTimeLeft(U32 eventSequence)
{
Mutex::lockMutex(gEventQueueMutex);
for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent)
if(walk->sequenceCount == eventSequence)
{
SimTime t = walk->time - getCurrentTime();
Mutex::unlockMutex(gEventQueueMutex);
return t;
}
Mutex::unlockMutex(gEventQueueMutex);
return 0;
}
U32 getScheduleDuration(U32 eventSequence)
{
for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent)
if(walk->sequenceCount == eventSequence)
return (walk->time-walk->startTime);
return 0;
}
U32 getTimeSinceStart(U32 eventSequence)
{
for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent)
if(walk->sequenceCount == eventSequence)
return (getCurrentTime()-walk->startTime);
return 0;
}
//---------------------------------------------------------------------------
// event timing
void advanceToTime(SimTime targetTime)
{
AssertFatal(targetTime >= getCurrentTime(), "EventQueue::process: cannot advance to time in the past.");
Mutex::lockMutex(gEventQueueMutex);
gTargetTime = targetTime;
while(gEventQueue && gEventQueue->time <= targetTime)
{
SimEvent *event = gEventQueue;
gEventQueue = gEventQueue->nextEvent;
AssertFatal(event->time >= gCurrentTime,
"SimEventQueue::pop: Cannot go back in time (flux capacitor not installed - BJG).");
gCurrentTime = event->time;
SimObject *obj = event->destObject;
if(!obj->isDeleted())
event->process(obj);
delete event;
}
gCurrentTime = targetTime;
Mutex::unlockMutex(gEventQueueMutex);
}
void advanceTime(SimTime delta)
{
advanceToTime(getCurrentTime() + delta);
}
U32 getCurrentTime()
{
Mutex::lockMutex(gEventQueueMutex);
SimTime t = gCurrentTime;
Mutex::unlockMutex(gEventQueueMutex);
return t;
}
U32 getTargetTime()
{
return gTargetTime;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
SimGroup *gRootGroup = NULL;
SimManagerNameDictionary *gNameDictionary;
SimIdDictionary *gIdDictionary;
U32 gNextObjectId;
static void initRoot()
{
gIdDictionary = new SimIdDictionary;
gNameDictionary = new SimManagerNameDictionary;
gRootGroup = new SimGroup();
gRootGroup->setId(RootGroupId);
gRootGroup->assignName("RootGroup");
gRootGroup->registerObject();
gNextObjectId = DynamicObjectIdFirst;
}
static void shutdownRoot()
{
gRootGroup->deleteObject();
delete gNameDictionary;
delete gIdDictionary;
}
//---------------------------------------------------------------------------
SimObject* findObject(const char* name)
{
// Play nice with bad code - JDD
if( !name )
return NULL;
SimObject *obj;
char c = *name;
if(c == '/')
return gRootGroup->findObject(name + 1 );
if(c >= '0' && c <= '9')
{
// it's an id group
const char* temp = name + 1;
for(;;)
{
c = *temp++;
if(!c)
return findObject(dAtoi(name));
else if(c == '/')
{
obj = findObject(dAtoi(name));
if(!obj)
return NULL;
return obj->findObject(temp);
}
}
}
S32 len;
for(len = 0; name[len] != 0 && name[len] != '/'; len++)
;
StringTableEntry stName = StringTable->lookupn(name, len);
if(!stName)
return NULL;
obj = gNameDictionary->find(stName);
if(!name[len])
return obj;
if(!obj)
return NULL;
return obj->findObject(name + len + 1);
}
SimObject* findObject(SimObjectId id)
{
return gIdDictionary->find(id);
}
SimGroup *getRootGroup()
{
return gRootGroup;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#define InstantiateNamedSet(set) g##set = new SimSet; g##set->registerObject(#set); gRootGroup->addObject(g##set)
#define InstantiateNamedGroup(set) g##set = new SimGroup; g##set->registerObject(#set); gRootGroup->addObject(g##set)
SimDataBlockGroup *gDataBlockGroup;
SimDataBlockGroup *getDataBlockGroup()
{
return gDataBlockGroup;
}
void init()
{
initEventQueue();
initRoot();
InstantiateNamedSet(ActiveActionMapSet);
InstantiateNamedSet(GhostAlwaysSet);
InstantiateNamedSet(LightSet);
InstantiateNamedSet(WayPointSet);
InstantiateNamedSet(fxReplicatorSet);
InstantiateNamedSet(fxFoliageSet);
InstantiateNamedGroup(ActionMapGroup);
InstantiateNamedGroup(ClientGroup);
InstantiateNamedGroup(GuiGroup);
InstantiateNamedGroup(ScriptClassGroup);
InstantiateNamedGroup(GuiDataGroup);
InstantiateNamedGroup(TCPGroup);
InstantiateNamedGroup(ClientConnectionGroup);
InstantiateNamedGroup(ChunkFileGroup);
gDataBlockGroup = new SimDataBlockGroup();
gDataBlockGroup->registerObject("DataBlockGroup");
gRootGroup->addObject(gDataBlockGroup);
InstantiateNamedSet(sgMissionLightingFilterSet);
}
void shutdown()
{
shutdownRoot();
shutdownEventQueue();
}
}
#endif // DOXYGENIZING.
SimDataBlockGroup::SimDataBlockGroup()
{
mLastModifiedKey = 0;
}
S32 QSORT_CALLBACK SimDataBlockGroup::compareModifiedKey(const void* a,const void* b)
{
return (reinterpret_cast<const SimDataBlock* >(a))->getModifiedKey() -
(reinterpret_cast<const SimDataBlock*>(b))->getModifiedKey();
}
void SimDataBlockGroup::sort()
{
if(mLastModifiedKey != SimDataBlock::getNextModifiedKey())
{
mLastModifiedKey = SimDataBlock::getNextModifiedKey();
dQsort(objectList.address(),objectList.size(),sizeof(SimObject *),compareModifiedKey);
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
bool SimObject::registerObject()
{
AssertFatal( !mFlags.test( Added ), "reigsterObject - Object already registered!");
mFlags.clear(Deleted | Removed);
if(!mId)
mId = Sim::gNextObjectId++;
AssertFatal(Sim::gIdDictionary && Sim::gNameDictionary,
"SimObject::registerObject - tried to register an object before Sim::init()!");
Sim::gIdDictionary->insert(this);
Sim::gNameDictionary->insert(this);
// Notify object
bool ret = onAdd();
if(!ret)
unregisterObject();
AssertFatal(!ret || isProperlyAdded(), "Object did not call SimObject::onAdd()");
return ret;
}
//---------------------------------------------------------------------------
void SimObject::unregisterObject()
{
mFlags.set(Removed);
// Notify object first
onRemove();
// Clear out any pending notifications before
// we call our own, just in case they delete
// something that we have referenced.
clearAllNotifications();
// Notify all objects that are waiting for delete
// messages
if (getGroup())
getGroup()->removeObject(this);
processDeleteNotifies();
// Do removals from the Sim.
Sim::gNameDictionary->remove(this);
Sim::gIdDictionary->remove(this);
Sim::cancelPendingEvents(this);
}
//---------------------------------------------------------------------------
void SimObject::deleteObject()
{
AssertFatal(mFlags.test(Added),
"SimObject::deleteObject: Object not registered.");
AssertFatal(!isDeleted(),"SimManager::deleteObject: "
"Object has already been deleted");
AssertFatal(!isRemoved(),"SimManager::deleteObject: "
"Object in the process of being removed");
mFlags.set(Deleted);
unregisterObject();
delete this;
}
//---------------------------------------------------------------------------
void SimObject::setId(SimObjectId newId)
{
if(!mFlags.test(Added))
{
mId = newId;
return;
}
// get this object out of the id dictionary if it's in it
Sim::gIdDictionary->remove(this);
// Free current Id.
// Assign new one.
mId = newId ? newId : Sim::gNextObjectId++;
Sim::gIdDictionary->insert(this);
}
void SimObject::assignName(const char *name)
{
StringTableEntry newName = NULL;
if(name[0])
newName = StringTable->insert(name);
if(mGroup)
mGroup->nameDictionary.remove(this);
if(mFlags.test(Added))
Sim::gNameDictionary->remove(this);
objectName = newName;
if(mGroup)
mGroup->nameDictionary.insert(this);
if(mFlags.test(Added))
Sim::gNameDictionary->insert(this);
}
//---------------------------------------------------------------------------
bool SimObject::registerObject(U32 id)
{
setId(id);
return registerObject();
}
bool SimObject::registerObject(const char *name)
{
assignName(name);
return registerObject();
}
bool SimObject::registerObject(const char *name, U32 id)
{
setId(id);
assignName(name);
return registerObject();
}

25
engine/console/stringStack.cc Executable file
View File

@ -0,0 +1,25 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/stringStack.h"
void StringStack::getArgcArgv(StringTableEntry name, U32 *argc, const char ***in_argv)
{
U32 startStack = mFrameOffsets[--mNumFrames] + 1;
U32 argCount = getMin(mStartStackSize - startStack, (U32)MaxArgs);
*in_argv = mArgV;
mArgV[0] = name;
for(U32 i = 0; i < argCount; i++)
mArgV[i+1] = mBuffer + mStartOffsets[startStack + i];
argCount++;
mStartStackSize = startStack - 1;
*argc = argCount;
mStart = mStartOffsets[mStartStackSize];
mLen = 0;
}

247
engine/console/stringStack.h Executable file
View File

@ -0,0 +1,247 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _STRINGSTACK_H_
#define _STRINGSTACK_H_
#include "platform/platform.h"
#include "console/console.h"
#include "console/compiler.h"
#include "core/stringTable.h"
/// Core stack for interpreter operations.
///
/// This class provides some powerful semantics for working with strings, and is
/// used heavily by the console interpreter.
struct StringStack
{
enum {
MaxStackDepth = 1024,
MaxArgs = 20,
ReturnBufferSpace = 512
};
char *mBuffer;
U32 mBufferSize;
const char *mArgV[MaxArgs];
U32 mFrameOffsets[MaxStackDepth];
U32 mStartOffsets[MaxStackDepth];
U32 mNumFrames;
U32 mArgc;
U32 mStart;
U32 mLen;
U32 mStartStackSize;
U32 mFunctionOffset;
U32 mArgBufferSize;
char *mArgBuffer;
void validateBufferSize(U32 size)
{
if(size > mBufferSize)
{
mBufferSize = size + 2048;
mBuffer = (char *) dRealloc(mBuffer, mBufferSize);
}
}
void validateArgBufferSize(U32 size)
{
if(size > mArgBufferSize)
{
mArgBufferSize = size + 2048;
mArgBuffer = (char *) dRealloc(mArgBuffer, mArgBufferSize);
}
}
StringStack()
{
mBufferSize = 0;
mBuffer = NULL;
mNumFrames = 0;
mStart = 0;
mLen = 0;
mStartStackSize = 0;
mFunctionOffset = 0;
validateBufferSize(8192);
validateArgBufferSize(2048);
}
/// Set the top of the stack to be an integer value.
void setIntValue(U32 i)
{
validateBufferSize(mStart + 32);
dSprintf(mBuffer + mStart, 32, "%d", i);
mLen = dStrlen(mBuffer + mStart);
}
/// Set the top of the stack to be a float value.
void setFloatValue(F64 v)
{
validateBufferSize(mStart + 32);
dSprintf(mBuffer + mStart, 32, "%g", v);
mLen = dStrlen(mBuffer + mStart);
}
/// Return a temporary buffer we can use to return data.
///
/// @note This clobbers anything in our buffers!
char *getReturnBuffer(U32 size)
{
if(size > ReturnBufferSpace)
{
validateArgBufferSize(size);
return mArgBuffer;
}
else
{
validateBufferSize(mStart + size);
return mBuffer + mStart;
}
}
/// Return a buffer we can use for arguments.
///
/// This updates the function offset.
char *getArgBuffer(U32 size)
{
validateBufferSize(mStart + mFunctionOffset + size);
char *ret = mBuffer + mStart + mFunctionOffset;
mFunctionOffset += size;
return ret;
}
/// Clear the function offset.
void clearFunctionOffset()
{
mFunctionOffset = 0;
}
/// Set a string value on the top of the stack.
void setStringValue(const char *s)
{
if(!s)
{
mLen = 0;
mBuffer[mStart] = 0;
return;
}
mLen = dStrlen(s);
validateBufferSize(mStart + mLen + 2);
dStrcpy(mBuffer + mStart, s);
}
/// Get the top of the stack, as a StringTableEntry.
///
/// @note Don't free this memory!
inline StringTableEntry getSTValue()
{
return StringTable->insert(mBuffer + mStart);
}
/// Get an integer representation of the top of the stack.
inline U32 getIntValue()
{
return dAtoi(mBuffer + mStart);
}
/// Get a float representation of the top of the stack.
inline F64 getFloatValue()
{
return dAtof(mBuffer + mStart);
}
/// Get a string representation of the top of the stack.
///
/// @note This returns a pointer to the actual top of the stack, be careful!
inline const char *getStringValue()
{
return mBuffer + mStart;
}
/// Advance the start stack, placing a zero length string on the top.
///
/// @note You should use StringStack::push, not this, if you want to
/// properly push the stack.
void advance()
{
mStartOffsets[mStartStackSize++] = mStart;
mStart += mLen;
mLen = 0;
}
/// Advance the start stack, placing a single character, null-terminated strong
/// on the top.
///
/// @note You should use StringStack::push, not this, if you want to
/// properly push the stack.
void advanceChar(char c)
{
mStartOffsets[mStartStackSize++] = mStart;
mStart += mLen;
mBuffer[mStart] = c;
mBuffer[mStart+1] = 0;
mStart += 1;
mLen = 0;
}
/// Push the stack, placing a zero-length string on the top.
void push()
{
advanceChar(0);
}
inline void setLen(U32 newlen)
{
mLen = newlen;
}
/// Pop the start stack.
void rewind()
{
mStart = mStartOffsets[--mStartStackSize];
mLen = dStrlen(mBuffer + mStart);
}
// Terminate the current string, and pop the start stack.
void rewindTerminate()
{
mBuffer[mStart] = 0;
mStart = mStartOffsets[--mStartStackSize];
mLen = dStrlen(mBuffer + mStart);
}
/// Compare 1st and 2nd items on stack, consuming them in the process,
/// and returning true if they matched, false if they didn't.
U32 compare()
{
// Figure out the 1st and 2nd item offsets.
U32 oldStart = mStart;
mStart = mStartOffsets[--mStartStackSize];
// Compare current and previous strings.
U32 ret = !dStricmp(mBuffer + mStart, mBuffer + oldStart);
// Put an empty string on the top of the stack.
mLen = 0;
mBuffer[mStart] = 0;
return ret;
}
void pushFrame()
{
mFrameOffsets[mNumFrames++] = mStartStackSize;
mStartOffsets[mStartStackSize++] = mStart;
mStart += ReturnBufferSpace;
validateBufferSize(0);
}
/// Get the arguments for a function call from the stack.
void getArgcArgv(StringTableEntry name, U32 *argc, const char ***in_argv);
};
#endif

274
engine/console/telnetConsole.cc Executable file
View File

@ -0,0 +1,274 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/event.h"
#include "console/telnetConsole.h"
#include "platform/gameInterface.h"
TelnetConsole *TelConsole = NULL;
void TelnetConsole::create()
{
TelConsole = new TelnetConsole;
}
void TelnetConsole::destroy()
{
delete TelConsole;
TelConsole = NULL;
}
ConsoleFunction( telnetSetParameters, void, 4, 5, "(int port, string consolePass, string listenPass)"
"Initialize and open the telnet console.\n\n"
"@param port Port to listen on for console connections (0 will shut down listening).\n"
"@param consolePass Password for read/write access to console.\n"
"@param listenPass Password for read access to console."
"@param remoteEcho [optional] Enable echoing back to the client, off by default.")
{
if (TelConsole)
{
bool remoteEcho = false;
if( argc == 5 )
remoteEcho = dAtob( argv[4] );
TelConsole->setTelnetParameters(dAtoi(argv[1]), argv[2], argv[3], remoteEcho);
}
}
static void telnetCallback(ConsoleLogEntry::Level level, const char *consoleLine)
{
level;
if (TelConsole)
TelConsole->processConsoleLine(consoleLine);
}
TelnetConsole::TelnetConsole()
{
Con::addConsumer(telnetCallback);
mAcceptSocket = InvalidSocket;
mAcceptPort = -1;
mClientList = NULL;
mRemoteEchoEnabled = false;
}
TelnetConsole::~TelnetConsole()
{
Con::removeConsumer(telnetCallback);
if(mAcceptSocket != InvalidSocket)
Net::closeSocket(mAcceptSocket);
TelnetClient *walk = mClientList, *temp;
while(walk)
{
temp = walk->nextClient;
if(walk->socket != InvalidSocket)
Net::closeSocket(walk->socket);
delete walk;
walk = temp;
}
}
void TelnetConsole::setTelnetParameters(S32 port, const char *telnetPassword, const char *listenPassword, bool remoteEcho)
{
if(port == mAcceptPort)
return;
mRemoteEchoEnabled = remoteEcho;
if(mAcceptSocket != InvalidSocket)
{
Net::closeSocket(mAcceptSocket);
mAcceptSocket = InvalidSocket;
}
mAcceptPort = port;
if(mAcceptPort != -1 && mAcceptPort != 0)
{
mAcceptSocket = Net::openSocket();
Net::bind(mAcceptSocket, mAcceptPort);
Net::listen(mAcceptSocket, 4);
Net::setBlocking(mAcceptSocket, false);
}
dStrncpy(mTelnetPassword, telnetPassword, PasswordMaxLength);
dStrncpy(mListenPassword, listenPassword, PasswordMaxLength);
}
void TelnetConsole::processConsoleLine(const char *consoleLine)
{
if (mClientList==NULL) return; // just escape early. don't even do another step...
// ok, spew this line out to all our subscribers...
S32 len = dStrlen(consoleLine)+1;
for(TelnetClient *walk = mClientList; walk; walk = walk->nextClient)
{
if(walk->state == FullAccessConnected || walk->state == ReadOnlyConnected)
{
Net::send(walk->socket, (const unsigned char*)consoleLine, len);
Net::send(walk->socket, (const unsigned char*)"\r\n", 2);
}
}
}
void TelnetConsole::process()
{
NetAddress address;
if(mAcceptSocket != InvalidSocket)
{
// ok, see if we have any new connections:
NetSocket newConnection;
newConnection = Net::accept(mAcceptSocket, &address);
if(newConnection != InvalidSocket)
{
Con::printf ("Telnet connection from %i.%i.%i.%i",
address.netNum[0], address.netNum[1], address.netNum[2], address.netNum[3]);
TelnetClient *cl = new TelnetClient;
cl->socket = newConnection;
cl->curPos = 0;
cl->state = PasswordTryOne;
Net::setBlocking(newConnection, false);
char *connectMessage = "Torque Telnet Remote Console\r\n\r\nEnter Password:";
Net::send(cl->socket, (const unsigned char*)connectMessage, dStrlen(connectMessage)+1);
cl->nextClient = mClientList;
mClientList = cl;
}
}
char recvBuf[256];
char reply[1024];
// see if we have any input to process...
for(TelnetClient *client = mClientList; client; client = client->nextClient)
{
S32 numBytes;
Net::Error err = Net::recv(client->socket, (unsigned char*)recvBuf, sizeof(recvBuf), &numBytes);
if((err != Net::NoError && err != Net::WouldBlock) || numBytes == 0)
{
Net::closeSocket(client->socket);
client->socket = InvalidSocket;
continue;
}
S32 replyPos = 0;
for(S32 i = 0; i < numBytes;i++)
{
if(recvBuf[i] == '\r')
continue;
// execute the current command
if(recvBuf[i] == '\n')
{
reply[replyPos++] = '\r';
reply[replyPos++] = '\n';
client->curLine[client->curPos] = 0;
client->curPos = 0;
if(client->state == FullAccessConnected)
{
Net::send(client->socket, (const unsigned char*)reply, replyPos);
replyPos = 0;
dStrcpy(mPostEvent.data, client->curLine);
mPostEvent.size = ConsoleEventHeaderSize + dStrlen(client->curLine) + 1;
Game->postEvent(mPostEvent);
// note - send prompt next
const char *prompt = Con::getVariable("Con::Prompt");
Net::send(client->socket, (const unsigned char*)prompt, dStrlen(prompt));
}
else if(client->state == ReadOnlyConnected)
{
Net::send(client->socket, (const unsigned char*)reply, replyPos);
replyPos = 0;
}
else
{
client->state++;
if(!dStrncmp(client->curLine, mTelnetPassword, PasswordMaxLength))
{
Net::send(client->socket, (const unsigned char*)reply, replyPos);
replyPos = 0;
// send prompt
const char *prompt = Con::getVariable("Con::Prompt");
Net::send(client->socket, (const unsigned char*)prompt, dStrlen(prompt));
client->state = FullAccessConnected;
}
else if(!dStrncmp(client->curLine, mListenPassword, PasswordMaxLength))
{
Net::send(client->socket, (const unsigned char*)reply, replyPos);
replyPos = 0;
// send prompt
const char *listenConnected = "Connected.\r\n";
Net::send(client->socket, (const unsigned char*)listenConnected, dStrlen(listenConnected));
client->state = ReadOnlyConnected;
}
else
{
const char *sendStr;
if(client->state == DisconnectThisDude)
sendStr = "Too many tries... cya.";
else
sendStr = "Nope... try agian.\r\nEnter Password:";
Net::send(client->socket, (const unsigned char*)sendStr, dStrlen(sendStr));
if(client->state == DisconnectThisDude)
{
Net::closeSocket(client->socket);
client->socket = InvalidSocket;
}
}
}
}
else if(recvBuf[i] == '\b')
{
// pull the old backspace manuever...
if(client->curPos > 0)
{
client->curPos--;
if(client->state == FullAccessConnected)
{
reply[replyPos++] = '\b';
reply[replyPos++] = ' ';
reply[replyPos++] = '\b';
}
}
}
else if(client->curPos < Con::MaxLineLength-1)
{
client->curLine[client->curPos++] = recvBuf[i];
// don't echo password chars...
if(client->state == FullAccessConnected)
reply[replyPos++] = recvBuf[i];
}
}
// Echo the character back to the user, unless the remote echo
// is disabled (by default)
if(replyPos && mRemoteEchoEnabled)
Net::send(client->socket, (const unsigned char*)reply, replyPos);
}
TelnetClient ** walk = &mClientList;
TelnetClient *cl;
while((cl = *walk) != NULL)
{
if(cl->socket == InvalidSocket)
{
*walk = cl->nextClient;
delete cl;
}
else
walk = &cl->nextClient;
}
}

104
engine/console/telnetConsole.h Executable file
View File

@ -0,0 +1,104 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _TELNETCONSOLE_H_
#define _TELNETCONSOLE_H_
#ifndef _CONSOLE_H_
#include "console/console.h"
#endif
/// Telnet admin console.
///
/// Torque supports remote access to its console. This is most useful when
/// running a dedicated server, as you can remotely administer the game
/// (for instance, kicking people). In the context of a MMORPG, this sort of
/// functionality would be useful for managing a server.
///
/// There are a number of products for Tribes2 which allow remote administration
/// via a nice GUI.
///
/// @section telnetconsole_use Using the Telnet Console
///
/// The TelnetConsole is designed to be used globally, so you don't instantiate
/// it like a normal class. Instead, you allow it to manage itself:
///
/// @code
/// // How to initialize the TelnetConsole.
/// TelnetConsole::create();
///
/// // How to shut down the TelnetConsole.
/// TelnetConsole::destroy();
/// @endcode
///
///
class TelnetConsole
{
NetSocket mAcceptSocket;
S32 mAcceptPort;
enum {
PasswordMaxLength = 32 ///< Maximum length of the telnet and listen passwords.
};
bool mRemoteEchoEnabled;
char mTelnetPassword[PasswordMaxLength+1];
char mListenPassword[PasswordMaxLength+1];
ConsoleEvent mPostEvent;
/// State of a TelnetClient.
enum State
{
PasswordTryOne, ///< Allow three password attempts.
PasswordTryTwo,
PasswordTryThree,
DisconnectThisDude, ///< If they've failed all three, disconnect them.
FullAccessConnected, ///< They presented the telnetPassword, they get full access.
ReadOnlyConnected ///< They presented the listenPassword, they get read only access.
};
/// Represents a connection to the telnet console.
///
/// This is also a linked list.
struct TelnetClient
{
NetSocket socket;
char curLine[Con::MaxLineLength];
S32 curPos;
S32 state; ///< State of the client.
/// @see TelnetConsole::State
TelnetClient *nextClient;
};
TelnetClient *mClientList;
TelnetConsole();
~TelnetConsole();
public:
static void create(); ///< Initialize the telnet console.
static void destroy(); ///< Shut down the telnet console.
void process(); ///< Called by the main loop to let the console process commands
/// and connections.
/// Configure the parameter for the telnet console.
///
/// @param port Port on which to listen for connections.
/// @param telnetPassword Password for full access to the console.
/// @param listenPassword Password for read-only access to the console.
/// @param remoteEcho Enable/disable echoing input back to the client
void setTelnetParameters(S32 port, const char *telnetPassword, const char *listenPassword, bool remoteEcho = false);
/// Callback to handle a line from the console.
///
/// @note This is used internally by the class; you
/// shouldn't need to call it.
///
/// @see Con::addConsumer()
void processConsoleLine(const char *line);
};
extern TelnetConsole *TelConsole;
#endif

876
engine/console/telnetDebugger.cc Executable file
View File

@ -0,0 +1,876 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "console/console.h"
#include "console/telnetDebugger.h"
#include "platform/event.h"
#include "core/stringTable.h"
#include "console/consoleInternal.h"
#include "console/ast.h"
#include "console/compiler.h"
#include "platform/gameInterface.h"
//
// Enhanced TelnetDebugger for Torsion
// http://www.sickheadgames.com/torsion
//
//
// Debugger commands:
//
// CEVAL console line - evaluate the console line
// output: none
//
// BRKVARSET varName passct expr - NOT IMPLEMENTED!
// output: none
//
// BRKVARCLR varName - NOT IMPLEMENTED!
// output: none
//
// BRKSET file line clear passct expr - set a breakpoint on the file,line
// it must pass passct times for it to break and if clear is true, it
// clears when hit
// output:
//
// BRKNEXT - stop execution at the next breakable line.
// output: none
//
// BRKCLR file line - clear a breakpoint on the file,line
// output: none
//
// BRKCLRALL - clear all breakpoints
// output: none
//
// CONTINUE - continue execution
// output: RUNNING
//
// STEPIN - run until next statement
// output: RUNNING
//
// STEPOVER - run until next break <= current frame
// output: RUNNING
//
// STEPOUT - run until next break <= current frame - 1
// output: RUNNING
//
// EVAL tag frame expr - evaluate the expr in the console, on the frame'th stack frame
// output: EVALOUT tag exprResult
//
// FILELIST - list script files loaded
// output: FILELISTOUT file1 file2 file3 file4 ...
//
// BREAKLIST file - get a list of breakpoint-able lines in the file
// output: BREAKLISTOUT file skipBreakPairs skiplinecount breaklinecount skiplinecount breaklinecount ...
//
//
// Other output:
//
// BREAK file1 line1 function1 file2 line2 function2 ... - Sent when the debugger hits a
// breakpoint. It lists out one file/line/function triplet for each stack level.
// The first one is the top of the stack.
//
// COUT console-output - echo of console output from engine
//
// BRKMOV file line newline - sent when a breakpoint is moved to a breakable line.
//
// BRKCLR file line - sent when a breakpoint cannot be moved to a breakable line on the client.
//
ConsoleFunction( dbgSetParameters, void, 3, 4, "(int port, string password, bool waitForClient)"
"Open a debug server port on the specified port, requiring the specified password, "
"and optionally waiting for the debug client to connect.")
{
if (TelDebugger)
TelDebugger->setDebugParameters(dAtoi(argv[1]), argv[2], argc > 3 ? dAtob(argv[3]) : false );
}
ConsoleFunction( dbgIsConnected, bool, 1, 1, "()"
"Returns true if a script debugging client is connected else return false.")
{
return TelDebugger && TelDebugger->isConnected();
}
ConsoleFunction( dbgDisconnect, void, 1, 1, "()"
"Forcibly disconnects any attached script debugging client.")
{
if (TelDebugger)
TelDebugger->disconnect();
}
static void debuggerConsumer(ConsoleLogEntry::Level level, const char *line)
{
level;
if (TelDebugger)
TelDebugger->processConsoleLine(line);
}
TelnetDebugger::TelnetDebugger()
{
Con::addConsumer(debuggerConsumer);
mAcceptPort = -1;
mAcceptSocket = InvalidSocket;
mDebugSocket = InvalidSocket;
mState = NotConnected;
mCurPos = 0;
mBreakpoints = NULL;
mBreakOnNextStatement = false;
mStackPopBreakIndex = -1;
mProgramPaused = false;
mWaitForClient = false;
// Add the version number in a global so that
// scripts can detect the presence of the
// "enhanced" debugger features.
Con::evaluatef( "$dbgVersion = %d;", Version );
}
TelnetDebugger::Breakpoint **TelnetDebugger::findBreakpoint(StringTableEntry fileName, S32 lineNumber)
{
Breakpoint **walk = &mBreakpoints;
Breakpoint *cur;
while((cur = *walk) != NULL)
{
if(cur->fileName == fileName && cur->lineNumber == U32(lineNumber))
return walk;
walk = &cur->next;
}
return NULL;
}
TelnetDebugger::~TelnetDebugger()
{
Con::removeConsumer(debuggerConsumer);
if(mAcceptSocket != InvalidSocket)
Net::closeSocket(mAcceptSocket);
if(mDebugSocket != InvalidSocket)
Net::closeSocket(mDebugSocket);
}
TelnetDebugger *TelDebugger = NULL;
void TelnetDebugger::create()
{
TelDebugger = new TelnetDebugger;
}
void TelnetDebugger::destroy()
{
delete TelDebugger;
TelDebugger = NULL;
}
void TelnetDebugger::send(const char *str)
{
Net::send(mDebugSocket, (const unsigned char*)str, dStrlen(str));
}
void TelnetDebugger::disconnect()
{
if ( mDebugSocket != InvalidSocket )
{
Net::closeSocket(mDebugSocket);
mDebugSocket = InvalidSocket;
}
removeAllBreakpoints();
mState = NotConnected;
mProgramPaused = false;
}
void TelnetDebugger::setDebugParameters(S32 port, const char *password, bool waitForClient)
{
// Don't bail if same port... we might just be wanting to change
// the password.
// if(port == mAcceptPort)
// return;
if(mAcceptSocket != InvalidSocket)
{
Net::closeSocket(mAcceptSocket);
mAcceptSocket = InvalidSocket;
}
mAcceptPort = port;
if(mAcceptPort != -1 && mAcceptPort != 0)
{
mAcceptSocket = Net::openSocket();
Net::bind(mAcceptSocket, mAcceptPort);
Net::listen(mAcceptSocket, 4);
Net::setBlocking(mAcceptSocket, false);
}
dStrncpy(mDebuggerPassword, password, PasswordMaxLength);
mWaitForClient = waitForClient;
if ( !mWaitForClient )
return;
// Wait for the client to fully connect.
while ( mState != Connected )
{
Platform::sleep(10);
process();
}
}
void TelnetDebugger::processConsoleLine(const char *consoleLine)
{
if(mState != NotConnected)
{
send("COUT ");
send(consoleLine);
send("\r\n");
}
}
void TelnetDebugger::process()
{
NetAddress address;
if(mAcceptSocket != InvalidSocket)
{
// ok, see if we have any new connections:
NetSocket newConnection;
newConnection = Net::accept(mAcceptSocket, &address);
if(newConnection != InvalidSocket && mDebugSocket == InvalidSocket)
{
Con::printf ("Debugger connection from %i.%i.%i.%i",
address.netNum[0], address.netNum[1], address.netNum[2], address.netNum[3]);
mState = PasswordTry;
mDebugSocket = newConnection;
Net::setBlocking(newConnection, false);
}
else if(newConnection != InvalidSocket)
Net::closeSocket(newConnection);
}
// see if we have any input to process...
if(mDebugSocket == InvalidSocket)
return;
checkDebugRecv();
if(mDebugSocket == InvalidSocket)
removeAllBreakpoints();
}
void TelnetDebugger::checkDebugRecv()
{
for (;;)
{
// Process all the complete commands in the buffer.
while ( mCurPos > 0 )
{
// Remove leading whitespace.
while ( mCurPos > 0 && ( mLineBuffer[0] == 0 || mLineBuffer[0] == '\r' || mLineBuffer[0] == '\n' ) )
{
mCurPos--;
dMemmove(mLineBuffer, mLineBuffer + 1, mCurPos);
}
// Look for a complete command.
bool gotCmd = false;
for(S32 i = 0; i < mCurPos; i++)
{
if( mLineBuffer[i] == 0 )
mLineBuffer[i] = '_';
else if ( mLineBuffer[i] == '\r' || mLineBuffer[i] == '\n' )
{
// Send this command to be processed.
mLineBuffer[i] = '\n';
processLineBuffer(i+1);
// Remove the command from the buffer.
mCurPos -= i + 1;
dMemmove(mLineBuffer, mLineBuffer + i + 1, mCurPos);
gotCmd = true;
break;
}
}
// If we didn't find a command in this pass
// then we have an incomplete buffer.
if ( !gotCmd )
break;
}
// found no <CR> or <LF>
if(mCurPos == MaxCommandSize) // this shouldn't happen
{
disconnect();
return;
}
S32 numBytes;
Net::Error err = Net::recv(mDebugSocket, (unsigned char*)(mLineBuffer + mCurPos), MaxCommandSize - mCurPos, &numBytes);
if((err != Net::NoError && err != Net::WouldBlock) || numBytes == 0)
{
disconnect();
return;
}
if(err == Net::WouldBlock)
return;
mCurPos += numBytes;
}
}
void TelnetDebugger::executionStopped(CodeBlock *code, U32 lineNumber)
{
if(mProgramPaused)
return;
if(mBreakOnNextStatement)
{
setBreakOnNextStatement( false );
breakProcess();
return;
}
Breakpoint **bp = findBreakpoint(code->name, lineNumber);
if(!bp)
return;
Breakpoint *brk = *bp;
mProgramPaused = true;
Con::evaluatef("$Debug::result = %s;", brk->testExpression);
if(Con::getBoolVariable("$Debug::result"))
{
brk->curCount++;
if(brk->curCount >= brk->passCount)
{
brk->curCount = 0;
if(brk->clearOnHit)
removeBreakpoint(code->name, lineNumber);
breakProcess();
}
}
mProgramPaused = false;
}
void TelnetDebugger::pushStackFrame()
{
if(mState == NotConnected)
return;
if(mBreakOnNextStatement && mStackPopBreakIndex > -1 &&
gEvalState.stack.size() > mStackPopBreakIndex)
setBreakOnNextStatement( false );
}
void TelnetDebugger::popStackFrame()
{
if(mState == NotConnected)
return;
if(mStackPopBreakIndex > -1 && gEvalState.stack.size()-1 <= mStackPopBreakIndex)
setBreakOnNextStatement( true );
}
void TelnetDebugger::breakProcess()
{
// Send out a break with the full stack.
sendBreak();
mProgramPaused = true;
while(mProgramPaused)
{
Platform::sleep(10);
checkDebugRecv();
if(mDebugSocket == InvalidSocket)
{
mProgramPaused = false;
removeAllBreakpoints();
debugContinue();
return;
}
}
}
void TelnetDebugger::sendBreak()
{
// echo out the break
send("BREAK");
char buffer[MaxCommandSize];
char scope[MaxCommandSize];
S32 last = 0;
for(S32 i = (S32) gEvalState.stack.size() - 1; i >= last; i--)
{
CodeBlock *code = gEvalState.stack[i]->code;
const char *file = "<none>";
if (code && code->name && code->name[0])
file = code->name;
Namespace *ns = gEvalState.stack[i]->scopeNamespace;
scope[0] = 0;
if ( ns ) {
if ( ns->mParent && ns->mParent->mPackage && ns->mParent->mPackage[0] ) {
dStrcat( scope, ns->mParent->mPackage );
dStrcat( scope, "::" );
}
if ( ns->mName && ns->mName[0] ) {
dStrcat( scope, ns->mName );
dStrcat( scope, "::" );
}
}
const char *function = gEvalState.stack[i]->scopeName;
if ((!function) || (!function[0]))
function = "<none>";
dStrcat( scope, function );
U32 line=0, inst;
U32 ip = gEvalState.stack[i]->ip;
if (code)
code->findBreakLine(ip, line, inst);
dSprintf(buffer, MaxCommandSize, " %s %d %s", file, line, scope);
send(buffer);
}
send("\r\n");
}
void TelnetDebugger::processLineBuffer(S32 cmdLen)
{
if (mState == PasswordTry)
{
if(dStrncmp(mLineBuffer, mDebuggerPassword, cmdLen-1))
{
// failed password:
send("PASS WrongPassword.\r\n");
disconnect();
}
else
{
send("PASS Connected.\r\n");
mState = mWaitForClient ? Initialize : Connected;
}
return;
}
else
{
char evalBuffer[MaxCommandSize];
char varBuffer[MaxCommandSize];
char fileBuffer[MaxCommandSize];
char clear[MaxCommandSize];
S32 passCount, line, frame;
if(dSscanf(mLineBuffer, "CEVAL %[^\n]", evalBuffer) == 1)
{
ConsoleEvent postEvent;
dStrcpy(postEvent.data, evalBuffer);
postEvent.size = ConsoleEventHeaderSize + dStrlen(evalBuffer) + 1;
Game->postEvent(postEvent);
}
else if(dSscanf(mLineBuffer, "BRKVARSET %s %d %[^\n]", varBuffer, &passCount, evalBuffer) == 3)
addVariableBreakpoint(varBuffer, passCount, evalBuffer);
else if(dSscanf(mLineBuffer, "BRKVARCLR %s", varBuffer) == 1)
removeVariableBreakpoint(varBuffer);
else if(dSscanf(mLineBuffer, "BRKSET %s %d %s %d %[^\n]", fileBuffer,&line,&clear,&passCount,evalBuffer) == 5)
addBreakpoint(fileBuffer, line, dAtob(clear), passCount, evalBuffer);
else if(dSscanf(mLineBuffer, "BRKCLR %s %d", fileBuffer, &line) == 2)
removeBreakpoint(fileBuffer, line);
else if(!dStrncmp(mLineBuffer, "BRKCLRALL\n", cmdLen))
removeAllBreakpoints();
else if(!dStrncmp(mLineBuffer, "BRKNEXT\n", cmdLen))
debugBreakNext();
else if(!dStrncmp(mLineBuffer, "CONTINUE\n", cmdLen))
debugContinue();
else if(!dStrncmp(mLineBuffer, "STEPIN\n", cmdLen))
debugStepIn();
else if(!dStrncmp(mLineBuffer, "STEPOVER\n", cmdLen))
debugStepOver();
else if(!dStrncmp(mLineBuffer, "STEPOUT\n", cmdLen))
debugStepOut();
else if(dSscanf(mLineBuffer, "EVAL %s %d %[^\n]", varBuffer, &frame, evalBuffer) == 3)
evaluateExpression(varBuffer, frame, evalBuffer);
else if(!dStrncmp(mLineBuffer, "FILELIST\n", cmdLen))
dumpFileList();
else if(dSscanf(mLineBuffer, "BREAKLIST %s", fileBuffer) == 1)
dumpBreakableList(fileBuffer);
else
{
// invalid stuff.
send("DBGERR Invalid command!\r\n");
}
}
}
void TelnetDebugger::addVariableBreakpoint(const char*, S32, const char*)
{
send("addVariableBreakpoint\r\n");
}
void TelnetDebugger::removeVariableBreakpoint(const char*)
{
send("removeVariableBreakpoint\r\n");
}
void TelnetDebugger::addAllBreakpoints(CodeBlock *code)
{
if(mState == NotConnected)
return;
char buffer[MaxCommandSize];
dSprintf(buffer, MaxCommandSize, "LOAD %s\r\n", code->name);
send(buffer);
// Find the breakpoints for this code block and attach them.
Breakpoint **walk = &mBreakpoints;
Breakpoint *cur;
while((cur = *walk) != NULL)
{
if(cur->fileName == code->name)
{
cur->code = code;
// Find the fist breakline starting from and
// including the requested breakline.
S32 newLine = code->findFirstBreakLine(cur->lineNumber);
if (newLine <= 0)
{
walk = &cur->next;
dSprintf(buffer, MaxCommandSize, "BRKCLR %s %d\r\n", cur->fileName, cur->lineNumber);
send(buffer);
removeBreakpoint(cur->fileName, cur->lineNumber);
continue;
}
// If the requested breakline does not match
// the actual break line we need to inform
// the client.
if (newLine != cur->lineNumber)
{
// If we already have a line at this breapoint then
// tell the client to clear the breakpoint.
if ( findBreakpoint(cur->fileName, newLine) ) {
walk = &cur->next;
dSprintf(buffer, MaxCommandSize, "BRKCLR %s %d\r\n", cur->fileName, cur->lineNumber);
send(buffer);
removeBreakpoint(cur->fileName, cur->lineNumber);
continue;
}
// We're moving the breakpoint to new line... inform the
// client so it can update it's view.
dSprintf(buffer, MaxCommandSize, "BRKMOV %s %d %d\r\n", cur->fileName, cur->lineNumber, newLine);
send(buffer);
cur->lineNumber = newLine;
}
code->setBreakpoint(cur->lineNumber);
}
walk = &cur->next;
}
// Enable all breaks if a break next was set.
if (mBreakOnNextStatement)
code->setAllBreaks();
}
void TelnetDebugger::clearCodeBlockPointers(CodeBlock *code)
{
Breakpoint **walk = &mBreakpoints;
Breakpoint *cur;
while((cur = *walk) != NULL)
{
if(cur->code == code)
cur->code = NULL;
walk = &cur->next;
}
}
void TelnetDebugger::addBreakpoint(const char *fileName, S32 line, bool clear, S32 passCount, const char *evalString)
{
fileName = StringTable->insert(fileName);
Breakpoint **bp = findBreakpoint(fileName, line);
if(bp)
{
// trying to add the same breakpoint...
Breakpoint *brk = *bp;
dFree(brk->testExpression);
brk->testExpression = dStrdup(evalString);
brk->passCount = passCount;
brk->clearOnHit = clear;
brk->curCount = 0;
}
else
{
// Note that if the code block is not already
// loaded it is handled by addAllBreakpoints.
CodeBlock* code = CodeBlock::find(fileName);
if (code)
{
// Find the fist breakline starting from and
// including the requested breakline.
S32 newLine = code->findFirstBreakLine(line);
if (newLine <= 0)
{
char buffer[MaxCommandSize];
dSprintf(buffer, MaxCommandSize, "BRKCLR %s %d\r\n", fileName, line);
send(buffer);
return;
}
// If the requested breakline does not match
// the actual break line we need to inform
// the client.
if (newLine != line)
{
char buffer[MaxCommandSize];
// If we already have a line at this breapoint then
// tell the client to clear the breakpoint.
if ( findBreakpoint(fileName, newLine) ) {
dSprintf(buffer, MaxCommandSize, "BRKCLR %s %d\r\n", fileName, line);
send(buffer);
return;
}
// We're moving the breakpoint to new line... inform the client.
dSprintf(buffer, MaxCommandSize, "BRKMOV %s %d %d\r\n", fileName, line, newLine);
send(buffer);
line = newLine;
}
code->setBreakpoint(line);
}
Breakpoint *brk = new Breakpoint;
brk->code = code;
brk->fileName = fileName;
brk->lineNumber = line;
brk->passCount = passCount;
brk->clearOnHit = clear;
brk->curCount = 0;
brk->testExpression = dStrdup(evalString);
brk->next = mBreakpoints;
mBreakpoints = brk;
}
}
void TelnetDebugger::removeBreakpointsFromCode(CodeBlock *code)
{
Breakpoint **walk = &mBreakpoints;
Breakpoint *cur;
while((cur = *walk) != NULL)
{
if(cur->code == code)
{
dFree(cur->testExpression);
*walk = cur->next;
delete walk;
}
else
walk = &cur->next;
}
}
void TelnetDebugger::removeBreakpoint(const char *fileName, S32 line)
{
fileName = StringTable->insert(fileName);
Breakpoint **bp = findBreakpoint(fileName, line);
if(bp)
{
Breakpoint *brk = *bp;
*bp = brk->next;
if ( brk->code )
brk->code->clearBreakpoint(brk->lineNumber);
dFree(brk->testExpression);
delete brk;
}
}
void TelnetDebugger::removeAllBreakpoints()
{
Breakpoint *walk = mBreakpoints;
while(walk)
{
Breakpoint *temp = walk->next;
if ( walk->code )
walk->code->clearBreakpoint(walk->lineNumber);
dFree(walk->testExpression);
delete walk;
walk = temp;
}
mBreakpoints = NULL;
}
void TelnetDebugger::debugContinue()
{
if (mState == Initialize) {
mState = Connected;
return;
}
setBreakOnNextStatement( false );
mStackPopBreakIndex = -1;
mProgramPaused = false;
send("RUNNING\r\n");
}
void TelnetDebugger::setBreakOnNextStatement( bool enabled )
{
if ( enabled )
{
// Apply breaks on all the code blocks.
for(CodeBlock *walk = CodeBlock::getCodeBlockList(); walk; walk = walk->nextFile)
walk->setAllBreaks();
mBreakOnNextStatement = true;
}
else if ( !enabled )
{
// Clear all the breaks on the codeblocks
// then go reapply the breakpoints.
for(CodeBlock *walk = CodeBlock::getCodeBlockList(); walk; walk = walk->nextFile)
walk->clearAllBreaks();
for(Breakpoint *w = mBreakpoints; w; w = w->next)
{
if ( w->code )
w->code->setBreakpoint(w->lineNumber);
}
mBreakOnNextStatement = false;
}
}
void TelnetDebugger::debugBreakNext()
{
if (mState != Connected)
return;
if ( !mProgramPaused )
setBreakOnNextStatement( true );
}
void TelnetDebugger::debugStepIn()
{
// Note that step in is allowed during
// the initialize state, so that we can
// break on the first script line executed.
setBreakOnNextStatement( true );
mStackPopBreakIndex = -1;
mProgramPaused = false;
// Don't bother sending this to the client
// if it's in the initialize state. It will
// just be ignored as the client knows it
// is in a running state when it connects.
if (mState != Initialize)
send("RUNNING\r\n");
else
mState = Connected;
}
void TelnetDebugger::debugStepOver()
{
if (mState != Connected)
return;
setBreakOnNextStatement( true );
mStackPopBreakIndex = gEvalState.stack.size();
mProgramPaused = false;
send("RUNNING\r\n");
}
void TelnetDebugger::debugStepOut()
{
if (mState != Connected)
return;
setBreakOnNextStatement( false );
mStackPopBreakIndex = gEvalState.stack.size() - 1;
if ( mStackPopBreakIndex == 0 )
mStackPopBreakIndex = -1;
mProgramPaused = false;
send("RUNNING\r\n");
}
void TelnetDebugger::evaluateExpression(const char *tag, S32 frame, const char *evalBuffer)
{
// Make sure we're passing a valid frame to the eval.
if ( frame > gEvalState.stack.size() )
frame = gEvalState.stack.size() - 1;
if ( frame < 0 )
frame = 0;
// Build a buffer just big enough for this eval.
const char* format = "return %s;";
dsize_t len = dStrlen( format ) + dStrlen( evalBuffer );
char* buffer = new char[ len ];
dSprintf( buffer, len, format, evalBuffer );
// Execute the eval.
CodeBlock *newCodeBlock = new CodeBlock();
const char* result = newCodeBlock->compileExec( NULL, buffer, false, frame );
delete [] buffer;
// Create a new buffer that fits the result.
format = "EVALOUT %s %s\r\n";
len = dStrlen( format ) + dStrlen( tag ) + dStrlen( result );
buffer = new char[ len ];
dSprintf( buffer, len, format, tag, result[0] ? result : "\"\"" );
send( buffer );
delete [] buffer;
}
void TelnetDebugger::dumpFileList()
{
send("FILELISTOUT ");
for(CodeBlock *walk = CodeBlock::getCodeBlockList(); walk; walk = walk->nextFile)
{
send(walk->name);
if(walk->nextFile)
send(" ");
}
send("\r\n");
}
void TelnetDebugger::dumpBreakableList(const char *fileName)
{
fileName = StringTable->insert(fileName);
CodeBlock *file = CodeBlock::find(fileName);
char buffer[MaxCommandSize];
if(file)
{
dSprintf(buffer, MaxCommandSize, "BREAKLISTOUT %s %d", fileName, file->breakListSize >> 1);
send(buffer);
for(U32 i = 0; i < file->breakListSize; i += 2)
{
dSprintf(buffer, MaxCommandSize, " %d %d", file->breakList[i], file->breakList[i+1]);
send(buffer);
}
send("\r\n");
}
else
send("DBGERR No such file!\r\n");
}

116
engine/console/telnetDebugger.h Executable file
View File

@ -0,0 +1,116 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _TELNETDEBUGGER_H_
#define _TELNETDEBUGGER_H_
class CodeBlock;
/// Telnet debug service implementation.
///
/// This is the C++ side of the built-in Torque debugger.
///
/// To use the debugger, use dbgSetParameters(port, password); in the console
/// of the server to enable debugger connections. Then on some other system,
/// start up the app (you don't have to start a game or connect to the
/// server) and exec("common/debugger/debugger.cs"); in the console. Then use
/// the debugger GUI to connect to the server with the right port and password.
///
/// @see http://www.planettribes.com/tribes2/editing.shtml for more thorough discussion.
class TelnetDebugger
{
S32 mAcceptPort;
NetSocket mAcceptSocket;
NetSocket mDebugSocket;
enum {
// We should only change this if we truely
// break the protocol in a future version.
Version = 2,
PasswordMaxLength = 32,
MaxCommandSize = 2048
};
char mDebuggerPassword[PasswordMaxLength+1];
enum State
{
NotConnected,
PasswordTry,
Initialize,
Connected
};
S32 mState;
char mLineBuffer[MaxCommandSize];
S32 mCurPos;
bool mWaitForClient;
TelnetDebugger();
~TelnetDebugger();
struct Breakpoint
{
StringTableEntry fileName;
CodeBlock *code;
U32 lineNumber;
S32 passCount;
S32 curCount;
char *testExpression;
bool clearOnHit;
Breakpoint *next;
};
Breakpoint *mBreakpoints;
Breakpoint **findBreakpoint(StringTableEntry fileName, S32 lineNumber);
bool mProgramPaused;
bool mBreakOnNextStatement;
S32 mStackPopBreakIndex;
void addVariableBreakpoint(const char *varName, S32 passCount, const char *evalString);
void removeVariableBreakpoint(const char *varName);
void addBreakpoint(const char *fileName, S32 line, bool clear, S32 passCount, const char *evalString);
void removeBreakpoint(const char *fileName, S32 line);
void removeAllBreakpoints();
void debugBreakNext();
void debugContinue();
void debugStepIn();
void debugStepOver();
void debugStepOut();
void evaluateExpression(const char *tag, S32 frame, const char *evalBuffer);
void dumpFileList();
void dumpBreakableList(const char *fileName);
void removeBreakpointsFromCode(CodeBlock *code);
void sendBreak();
void checkDebugRecv();
void processLineBuffer(S32);
void breakProcess();
void setBreakOnNextStatement( bool enabled );
public:
static void create();
static void destroy();
void disconnect();
bool isConnected() const { return mState == Connected; }
void process();
void popStackFrame();
void pushStackFrame();
void addAllBreakpoints(CodeBlock *code);
void clearCodeBlockPointers(CodeBlock *code);
virtual void executionStopped(CodeBlock *code, U32 lineNumber);
void send(const char *s);
void setDebugParameters(S32 port, const char *password, bool waitForClient);
void processConsoleLine(const char *consoleLine);
};
extern TelnetDebugger *TelDebugger;
#endif

View File

@ -0,0 +1,70 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "console/console.h"
#include "console/consoleObject.h"
#include "console/typeValidators.h"
#include "console/simBase.h"
#include <stdarg.h>
void TypeValidator::consoleError(SimObject *object, const char *format, ...)
{
char buffer[1024];
va_list argptr;
va_start(argptr, format);
dVsprintf(buffer, sizeof(buffer), format, argptr);
va_end(argptr);
AbstractClassRep *rep = object->getClassRep();
AbstractClassRep::Field &fld = rep->mFieldList[fieldIndex];
const char *objectName = object->getName();
if(!objectName)
objectName = "unnamed";
Con::warnf("%s - %s(%d) - invalid value for %s: %s",
rep->getClassName(), objectName, object->getId(), fld.pFieldname, buffer);
}
void FRangeValidator::validateType(SimObject *object, void *typePtr)
{
F32 *v = (F32 *) typePtr;
if(*v < minV || *v > maxV)
{
consoleError(object, "Must be between %g and %g", minV, maxV);
if(*v < minV)
*v = minV;
else if(*v > maxV)
*v = maxV;
}
}
void IRangeValidator::validateType(SimObject *object, void *typePtr)
{
S32 *v = (S32 *) typePtr;
if(*v < minV || *v > maxV)
{
consoleError(object, "Must be between %d and %d", minV, maxV);
if(*v < minV)
*v = minV;
else if(*v > maxV)
*v = maxV;
}
}
void IRangeValidatorScaled::validateType(SimObject *object, void *typePtr)
{
S32 *v = (S32 *) typePtr;
*v /= factor;
if(*v < minV || *v > maxV)
{
consoleError(object, "Scaled value must be between %d and %d", minV, maxV);
if(*v < minV)
*v = minV;
else if(*v > maxV)
*v = maxV;
}
}

72
engine/console/typeValidators.h Executable file
View File

@ -0,0 +1,72 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _TYPEVALIDATORS_H_
#define _TYPEVALIDATORS_H_
class TypeValidator
{
public:
S32 fieldIndex;
/// Prints a console error message for the validator.
///
/// The message is prefaced with with:
/// @code
/// className objectName (objectId) - invalid value for fieldName: msg
/// @endcode
void consoleError(SimObject *object, const char *format, ...);
/// validateType is called for each assigned value on the field this
/// validator is attached to.
virtual void validateType(SimObject *object, void *typePtr) = 0;
};
/// Floating point min/max range validator
class FRangeValidator : public TypeValidator
{
F32 minV, maxV;
public:
FRangeValidator(F32 minValue, F32 maxValue)
{
minV = minValue;
maxV = maxValue;
}
void validateType(SimObject *object, void *typePtr);
};
/// Signed integer min/max range validator
class IRangeValidator : public TypeValidator
{
S32 minV, maxV;
public:
IRangeValidator(S32 minValue, S32 maxValue)
{
minV = minValue;
maxV = maxValue;
}
void validateType(SimObject *object, void *typePtr);
};
/// Scaled integer field validator
///
/// @note This should NOT be used on a field that gets exported -
/// the field is only validated once on initial assignment
class IRangeValidatorScaled : public TypeValidator
{
S32 minV, maxV;
S32 factor;
public:
IRangeValidatorScaled(S32 scaleFactor, S32 minValueScaled, S32 maxValueScaled)
{
minV = minValueScaled;
maxV = maxValueScaled;
factor = scaleFactor;
}
void validateType(SimObject *object, void *typePtr);
};
#endif