Skip to content

Instantly share code, notes, and snippets.

@assyrianic
Last active July 29, 2023 18:50
Show Gist options
  • Save assyrianic/7032a8b003a1b617cde9c8ffef8974db to your computer and use it in GitHub Desktop.
Save assyrianic/7032a8b003a1b617cde9c8ffef8974db to your computer and use it in GitHub Desktop.
a VScript-like scripting language exclusively made for SourcePawn
/**
* cfgscript.inc
*
* Copyright [2023] Assyrianic aka Nergal
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"),
* to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE ANDNONINFRINGEMENT.
*
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#if defined _cfgscript_included
#endinput
#endif
#define _cfgscript_included
#include <adt>
#include <plugin_utils>
enum {
OBJ_NIL,
OBJ_INT,
OBJ_FLT,
OBJ_STR,
OBJ_TAB,
OBJ_FNC,
//OBJ_TUP, /// tuple type.
OBJ_VEC,
};
typeset SPFFIFunc {
/// for general functions:
/// math.sin(x)
/// mod.fn(a,b,c,d)
function void(ArrayList csl_obj_args, CSLObj retval);
/// for binary operator style functions:
/// a do_thing b
function void(const CSLObj a, const CSLObj b, CSLObj retval);
};
enum struct CSLObj {
float vec[3];
any val;
SPFFIFunc f;
int size;
int tag;
void Init(int tag) {
if( tag==OBJ_STR || tag==OBJ_TAB ) {
StringMap map = new StringMap();
this.val = map;
}
this.tag = tag;
}
void Destroy(bool deep=false) {
if( this.tag==OBJ_STR || this.tag==OBJ_TAB ) {
if( deep && OBJ_TAB ) {
StringMap h = this.val;
StringMapSnapshot snap = h.Snapshot();
if( snap != null ) {
int len = snap.Length;
for( int i; i < len; i++ ) {
int keysize = snap.KeyBufferSize(i) + 1;
char[] key = new char[keysize];
snap.GetKey(i, key, keysize);
CSLObj a; h.GetArray(key, a, sizeof(a));
a.Destroy(true);
}
delete snap;
}
}
Handle h = this.val;
delete h;
}
this.val = 0;
this.size = 0;
this.tag = 0;
this.f = INVALID_FUNCTION;
for( int i; i < 3; i++ ) {
this.vec[i] = 0.0;
}
}
bool IsNil() {
return this.tag==OBJ_NIL;
}
bool GetInt(int& i) {
if( this.tag != OBJ_INT ) {
return false;
}
i = this.val;
return true;
}
bool GetFloat(float& f) {
if( this.tag != OBJ_FLT ) {
return false;
}
f = this.val;
return true;
}
int GetStrLen() {
if( this.tag != OBJ_STR ) {
return 0;
}
return this.size;
}
bool GetStr(char[] buf, int& len=0) {
if( this.tag != OBJ_STR ) {
return false;
}
StringMap map = this.val;
if( map==null ) {
return false;
}
if( len==0 ) {
len = this.size - 1;
}
map.GetString("str", buf, len);
return true;
}
StringMap GetMap() {
if( this.tag != OBJ_TAB ) {
return null;
}
StringMap map = this.val;
return( map==null )? null : map;
}
SPFFIFunc GetFunc() {
if( this.tag != OBJ_FNC ) {
return INVALID_FUNCTION;
}
return this.f;
}
bool GetVec(float buf[3]) {
if( this.tag != OBJ_VEC ) {
return false;
}
buf = this.vec;
return true;
}
bool SetInt(int i) {
if( this.tag != OBJ_INT ) {
return false;
}
this.val = i;
return true;
}
bool SetFloat(float f) {
if( this.tag != OBJ_FLT ) {
return false;
}
this.val = f;
return true;
}
bool SetStr(const char[] str, int len=0) {
if( this.tag != OBJ_STR ) {
return false;
}
StringMap map = this.val;
map.SetString("str", str);
if( len <= 0 ) {
len = strlen(str);
}
this.size = len + 1;
return true;
}
bool SetMap(StringMap map) {
if( this.tag != OBJ_TAB ) {
return false;
}
this.val = map;
return true;
}
bool SetFunc(SPFFIFunc f=INVALID_FUNCTION, Handle pl_of_f=null) {
if( this.tag != OBJ_FNC ) {
return false;
}
this.f = f;
this.val = pl_of_f;
return true;
}
bool SetVec(const float v[3]) {
if( this.tag != OBJ_VEC ) {
return false;
}
this.vec = v;
return true;
}
bool IsArithmeticType() {
return( this.tag==OBJ_INT || this.tag==OBJ_FLT );
}
bool AreSameType(CSLObj b) {
return( this.tag==b.tag );
}
bool AreIntType(CSLObj b) {
return( this.tag==OBJ_INT && b.tag==OBJ_INT );
}
bool AreFloatType(CSLObj b) {
return( this.tag==OBJ_FLT && b.tag==OBJ_FLT );
}
bool AreStrType(CSLObj b) {
return( this.tag==OBJ_STR && b.tag==OBJ_STR );
}
bool AreMapType(CSLObj b) {
return( this.tag==OBJ_TAB && b.tag==OBJ_TAB );
}
bool AreFuncType(CSLObj b) {
return( this.tag==OBJ_FNC && b.tag==OBJ_FNC );
}
bool AreVecType(CSLObj b) {
return( this.tag==OBJ_VEC && b.tag==OBJ_VEC );
}
bool AreArithmetible(CSLObj b) {
return( this.IsArithmeticType() && b.IsArithmeticType() );
}
bool ConvertToFloat() {
if( this.tag==OBJ_FLT ) {
return true;
}
switch( this.tag ) {
case OBJ_NIL: {
this.tag = OBJ_FLT;
float f = 0.0;
this.val = f;
}
case OBJ_INT: {
this.tag = OBJ_FLT;
int i = this.val;
float f = float(i);
this.val = f;
}
case OBJ_STR: {
char[] str = new char[this.size + 1];
StringMap str_data = this.val;
str_data.GetString("str", str, this.size);
float f = StringToFloat(str);
delete str_data;
this.val = f;
this.size = 0;
this.tag = OBJ_FLT;
}
case OBJ_TAB, OBJ_FNC: {
return false;
}
}
return true;
}
bool ConvertToInt() {
if( this.tag==OBJ_INT ) {
return true;
}
switch( this.tag ) {
case OBJ_NIL: {
this.tag = OBJ_INT;
this.val = 0;
}
case OBJ_FLT: {
this.tag = OBJ_INT;
float f = this.val;
this.val = RoundFloat(f);
}
case OBJ_STR: {
char[] str = new char[this.size + 1];
StringMap str_data = this.val;
str_data.GetString("str", str, this.size);
int i = StringToInt(str, 0);
delete str_data;
this.val = i;
this.size = 0;
this.tag = OBJ_INT;
}
case OBJ_TAB, OBJ_FNC: {
return false;
}
}
return true;
}
bool ConvertToStr() {
if( this.tag==OBJ_STR ) {
return true;
}
switch( this.tag ) {
case OBJ_NIL: {
this.Init(OBJ_STR);
this.SetStr("");
}
case OBJ_FLT: {
float f = this.val;
char flt_str[30];
int len = FloatToString(f, flt_str, sizeof(flt_str)) + 1;
this.Init(OBJ_STR);
this.SetStr(flt_str, len);
}
case OBJ_INT: {
char int_str[30];
int len = IntToString(this.val, int_str, sizeof(int_str)) + 1;
this.Init(OBJ_STR);
this.SetStr(int_str, len);
}
case OBJ_TAB, OBJ_FNC: {
return false;
}
}
return true;
}
bool ConcatStr(CSLObj b) {
if( this.tag != OBJ_STR || b.tag != OBJ_STR ) {
return false;
}
char[] first_str = new char[this.size + 1];
char[] second_str = new char[b.size + 1];
StringMap first_str_data = this.val;
StringMap second_str_data = b.val;
first_str_data.GetString("str", first_str, this.size);
second_str_data.GetString("str", second_str, b.size);
char[] merged_str = new char[this.size + b.size + 1];
int len = Format(merged_str, this.size + b.size, "%s%s", first_str, second_str);
this.SetStr(merged_str, this.size + b.size);
return len > 0;
}
int StrCmp(CSLObj b) {
if( this.tag != OBJ_STR || b.tag != OBJ_STR ) {
return false;
}
char[] first_str = new char[this.size + 1];
char[] second_str = new char[b.size + 1];
StringMap first_str_data = this.val;
StringMap second_str_data = b.val;
first_str_data.GetString("str", first_str, this.size);
second_str_data.GetString("str", second_str, b.size);
return strcmp(first_str, second_str);
}
bool MakeInt(int i) {
this.Init(OBJ_INT);
return this.SetInt(i);
}
bool MakeFloat(float f) {
this.Init(OBJ_FLT);
return this.SetFloat(f);
}
bool MakeStr(const char[] s, int len=0) {
this.Init(OBJ_STR);
return this.SetStr(s, len);
}
bool MakeMap(StringMap m) {
this.Init(OBJ_TAB);
return this.SetMap(m);
}
bool MakeFunc(SPFFIFunc f, Handle plugin=null) {
this.Init(OBJ_FNC);
return this.SetFunc(f, plugin);
}
bool MakeVec(const float v[3]) {
this.Init(OBJ_VEC);
return this.SetVec(v);
}
void PrintType() {
switch( this.tag ) {
case OBJ_NIL: PrintToServer("objtype :: nil");
case OBJ_INT: PrintToServer("objtype :: int");
case OBJ_FLT: PrintToServer("objtype :: float");
case OBJ_STR: PrintToServer("objtype :: string");
case OBJ_TAB: PrintToServer("objtype :: table");
case OBJ_FNC: PrintToServer("objtype :: func");
case OBJ_VEC: PrintToServer("objtype :: vec");
}
}
}
bool ConcatStrs(CSLObj a, CSLObj b, CSLObj res) {
if( a.tag != OBJ_STR || b.tag != OBJ_STR ) {
return false;
}
char[] first_str = new char[a.size + 1];
char[] second_str = new char[b.size + 1];
StringMap first_str_data = a.val;
StringMap second_str_data = b.val;
first_str_data.GetString("str", first_str, a.size);
second_str_data.GetString("str", second_str, b.size);
char[] merged_str = new char[a.size + b.size + 1];
int len = Format(merged_str, a.size + b.size, "%s%s", first_str, second_str);
res.Init(OBJ_STR);
res.SetStr(merged_str, a.size + b.size);
return len > 0;
}
/*
enum struct CSLFunc {
StringMap vars; /// map[string]CSLObj
StringMap params; /// map[string]CSLObj
ArrayList instrs;
void Init() {
}
/// map[VarName]Value
bool Invoke(StringMap params, CSLObj ret) {
this.params = params;
return true;
}
}
enum struct CSLScript {
StringMap funcs;
ArrayList strs; /// []string, to refer to strings by index.
void Init() {
this.funcs = new StringMap();
}
bool GetFunc(const char[] name, CSLFunc buffer) {
return this.funcs.GetArray(name, buffer, sizeof(buffer));
}
bool Invoke(const char[] name, const CSLObj[] args, int len_args, CSLObj ret) {
return false;
}
}
enum struct CSLSys {
StringMap scripts;
StringMap builtins; /// map[string]FuncObj
void Init() {
this.scripts = new StringMap();
this.builtins = new StringMap();
}
}
*/
/// ---------- LEXER ----------
/*
stock bool _IsCharNumeric(int c) {
return( c >= '0' && c <= '9' );
}
stock bool _IsCharAlpha(int c) {
return( c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' );
}
stock bool _StrEqual(const char[] a, const char[] b) {
int i, j;
while( a[i] != 0 && b[j] != 0 && a[i]==b[j] ) {
i++; j++;
}
return i - j;
}
*/
enum {
TokenInvalid,
/// literals
TokenInt, /// 1, 3, 0x2f
TokenFloat, /// 0.4
TokenString, /// "abc", 'abc'
TokenIdent, /// a, b, i, j
/// keywords
TokenBreak, TokenContinue, TokenIf, TokenElse, TokenDefined,
TokenFor, TokenIn, TokenNil, TokenVar,
/// delimiters
TokenLParen, TokenRParen, /// ()
TokenLBrace, TokenRBrace, /// {}
TokenLSq, TokenRSq, /// []
TokenComma, TokenColon, /// , :
/// operators
TokenAdd, /// +
TokenSub, /// -
TokenMul, /// *
TokenDiv, /// /
TokenMod, /// %
TokenPow, /// **
TokenDot, /// .
TokenNot, /// !
TokenLSH, /// <<
TokenRSHA, /// >>
TokenRSHL, /// >>>
TokenXor, /// ^
TokenAnd, /// &
TokenAndXor, /// &^
TokenOr, /// |
TokenCmp, /// ==
TokenNotCmp, /// !=
TokenLT, /// <
TokenLE, /// <=
TokenGT, /// >
TokenGE, /// >=
TokenLogicAnd, /// &&
TokenLogicOr, /// ||
TokenAssign, /// =
TokenAddAssign, /// +=
TokenSubAssign, /// -=
TokenMulAssign, /// *=
TokenDivAssign, /// /=
TokenModAssign, /// %=
TokenPowAssign, /// **=
TokenLSHAssign, /// <<=
TokenRSHAAssign, /// >>=
TokenRSHLAssign, /// >>>=
TokenXorAssign, /// ^=
TokenAndAssign, /// &=
TokenAndXorAssign, /// &^=
TokenOrAssign, /// |=
};
enum {
LEXEME_SIZE = 64,
FLAG_DOT = 1,
};
enum struct CSLToken {
int size;
int tag;
any val;
char lexeme[LEXEME_SIZE];
}
enum struct CSLLexState {
CSLToken curr_tok;
int src_idx;
int line;
void Init() {
this.line = 1;
}
bool LexBinary(const char[] src) {
while( src[this.src_idx] != 0 && (IsCharNumeric(src[this.src_idx])) ) {
switch( src[this.src_idx] ) {
case '0', '1': {
this.curr_tok.lexeme[this.curr_tok.size++] = src[this.src_idx++];
}
default: {
this.curr_tok.lexeme[this.curr_tok.size++] = src[this.src_idx++];
LogError("ConfigScript :: invalid binary literal: '%s'", this.curr_tok.lexeme);
return false;
}
}
}
return true;
}
bool LexHex(const char[] src) {
while( src[this.src_idx] != 0 && (IsCharNumeric(src[this.src_idx]) || IsCharAlpha(src[this.src_idx])) ) {
switch( src[this.src_idx] ) {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f',
'A', 'B', 'C', 'D', 'E', 'F': {
this.curr_tok.lexeme[this.curr_tok.size++] = src[this.src_idx++];
}
default: {
this.curr_tok.lexeme[this.curr_tok.size++] = src[this.src_idx++];
LogError("ConfigScript :: invalid hex literal: '%s'", this.curr_tok.lexeme);
return false;
}
}
}
return true;
}
bool LexDec(const char[] src, bool& is_flt) {
int lit_flags = 0;
while( src[this.src_idx] != 0 && (IsCharNumeric(src[this.src_idx]) || src[this.src_idx]=='.') ) {
switch( src[this.src_idx] ) {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': {
this.curr_tok.lexeme[this.curr_tok.size++] = src[this.src_idx++];
}
case '.': {
if( lit_flags & FLAG_DOT ) {
LogError("ConfigScript :: extra dot in decimal literal");
return false;
}
this.curr_tok.lexeme[this.curr_tok.size++] = src[this.src_idx++];
lit_flags |= FLAG_DOT;
is_flt = true;
}
default: {
this.curr_tok.lexeme[this.curr_tok.size++] = src[this.src_idx++];
LogError("ConfigScript :: invalid decimal literal: '%s'", this.curr_tok.lexeme);
return false;
}
}
}
return true;
}
void GetToken(const char[] src, int src_len) {
CSLToken empty;
this.curr_tok = empty;
while( this.src_idx < src_len ) {
switch( src[this.src_idx] ) {
case ' ', '\t', '\n': {
if( src[this.src_idx]=='\n' ) {
this.line++;
}
this.src_idx++;
continue;
}
case '#': {
/// single-line comment
while( this.src_idx < src_len && src[this.src_idx] != '\n' ) {
this.src_idx++;
}
this.src_idx++;
continue;
}
case '0': { /// possible hex, octal, binary, or float.
this.curr_tok.lexeme[this.curr_tok.size++] = src[this.src_idx++];
switch( src[this.src_idx] ) {
case 'b', 'B': {
/// Binary.
this.curr_tok.lexeme[this.curr_tok.size++] = src[this.src_idx++];
if( this.LexBinary(src) ) {
this.curr_tok.val = StringToInt(this.curr_tok.lexeme[2], 2);
}
this.curr_tok.tag = TokenInt;
return;
}
case 'x', 'X': {
/// Hex.
this.curr_tok.lexeme[this.curr_tok.size++] = src[this.src_idx++];
if( this.LexHex(src) ) {
this.curr_tok.val = StringToInt(this.curr_tok.lexeme, 16);
}
this.curr_tok.tag = TokenInt;
return;
}
case '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': {
/// Decimal/Float.
bool is_float = false;
if( this.LexDec(src, is_float) ) {
this.curr_tok.val = is_float? StringToFloat(this.curr_tok.lexeme) : StringToInt(this.curr_tok.lexeme);
this.curr_tok.tag = is_float? TokenFloat : TokenInt;
}
return;
}
default: {
this.curr_tok.tag = TokenInt;
return;
}
}
}
case '.': {
if( this.src_idx+1 < src_len && IsCharNumeric(src[this.src_idx+1]) ) {
/// Decimal/Float.
bool is_float = false;
if( this.LexDec(src, is_float) ) {
this.curr_tok.val = is_float? StringToFloat(this.curr_tok.lexeme) : StringToInt(this.curr_tok.lexeme);
this.curr_tok.tag = is_float? TokenFloat : TokenInt;
}
} else {
this.curr_tok.tag = TokenDot;
this.src_idx++;
}
return;
}
case '1', '2', '3', '4', '5', '6', '7', '8', '9': {
bool is_float = false;
if( this.LexDec(src, is_float) ) {
this.curr_tok.val = is_float? StringToFloat(this.curr_tok.lexeme) : StringToInt(this.curr_tok.lexeme);
this.curr_tok.tag = is_float? TokenFloat : TokenInt;
}
return;
}
case '\'', '"': {
char quote = src[this.src_idx];
this.src_idx++;
while( src[this.src_idx] != 0 && src[this.src_idx] != quote ) {
this.curr_tok.lexeme[this.curr_tok.size++] = src[this.src_idx++];
}
this.src_idx++;
this.curr_tok.tag = TokenString;
return;
}
case 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', '_', 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z': {
while( this.src_idx < src_len && (IsCharAlpha(src[this.src_idx]) || src[this.src_idx]=='_') ) {
this.curr_tok.lexeme[this.curr_tok.size++] = src[this.src_idx++];
}
if( StrEqual(this.curr_tok.lexeme, "if") ) {
this.curr_tok.tag = TokenIf;
} else if( StrEqual(this.curr_tok.lexeme, "else") ) {
this.curr_tok.tag = TokenElse;
} else if( StrEqual(this.curr_tok.lexeme, "in") ) {
this.curr_tok.tag = TokenIn;
} else if( StrEqual(this.curr_tok.lexeme, "for") ) {
this.curr_tok.tag = TokenFor;
} else if( StrEqual(this.curr_tok.lexeme, "nil") ) {
this.curr_tok.tag = TokenNil;
} else if( StrEqual(this.curr_tok.lexeme, "defined") ) {
this.curr_tok.tag = TokenDefined;
} else if( StrEqual(this.curr_tok.lexeme, "break") ) {
this.curr_tok.tag = TokenBreak;
} else if( StrEqual(this.curr_tok.lexeme, "continue") ) {
this.curr_tok.tag = TokenContinue;
} else if( StrEqual(this.curr_tok.lexeme, "var") ) {
this.curr_tok.tag = TokenVar;
} else {
this.curr_tok.tag = TokenIdent;
}
return;
}
case '(': {
this.curr_tok.tag = TokenLParen;
this.src_idx++;
return;
}
case ')': {
this.curr_tok.tag = TokenRParen;
this.src_idx++;
return;
}
case ',': {
this.curr_tok.tag = TokenComma;
this.src_idx++;
return;
}
case '{': {
this.curr_tok.tag = TokenLBrace;
this.src_idx++;
return;
}
case '}': {
this.curr_tok.tag = TokenRBrace;
this.src_idx++;
return;
}
case '[': {
this.curr_tok.tag = TokenLSq;
this.src_idx++;
return;
}
case ']': {
this.curr_tok.tag = TokenRSq;
this.src_idx++;
return;
}
case '+': {
if( src[this.src_idx+1]=='=' ) {
this.curr_tok.tag = TokenAddAssign;
this.src_idx += 2;
return;
}
this.src_idx++;
this.curr_tok.tag = TokenAdd;
return;
}
case '-': {
if( src[this.src_idx+1]=='=' ) {
this.curr_tok.tag = TokenSubAssign;
this.src_idx += 2;
return;
}
this.src_idx++;
this.curr_tok.tag = TokenSub;
return;
}
case '*': {
if( src[this.src_idx+1]=='*' ) {
if( src[this.src_idx+2]=='=' ) {
this.curr_tok.tag = TokenPowAssign;
this.src_idx += 3;
return;
}
this.curr_tok.tag = TokenPow;
this.src_idx += 2;
return;
} else if( src[this.src_idx+1]=='=' ) {
this.curr_tok.tag = TokenMulAssign;
this.src_idx += 2;
return;
}
this.src_idx++;
this.curr_tok.tag = TokenMul;
return;
}
case '/': {
if( src[this.src_idx+1]=='=' ) {
this.curr_tok.tag = TokenDivAssign;
this.src_idx += 2;
return;
}
this.src_idx++;
this.curr_tok.tag = TokenDiv;
return;
}
case '!': {
if( src[this.src_idx+1]=='=' ) {
this.curr_tok.tag = TokenNotCmp;
this.src_idx += 2;
return;
}
this.src_idx++;
this.curr_tok.tag = TokenNot;
return;
}
case '<': {
if( src[this.src_idx+1]=='<' ) {
if( src[this.src_idx+2]=='=' ) {
this.curr_tok.tag = TokenLSHAssign;
this.src_idx += 3;
return;
}
this.curr_tok.tag = TokenLSH;
this.src_idx += 2;
return;
} else if( src[this.src_idx+1]=='=' ) {
this.curr_tok.tag = TokenLE;
this.src_idx += 2;
return;
}
this.src_idx++;
this.curr_tok.tag = TokenLT;
return;
}
case '>': {
if( src[this.src_idx+1]=='>' ) {
if( src[this.src_idx+2]=='=' ) {
this.curr_tok.tag = TokenRSHAAssign;
this.src_idx += 3;
return;
} else if( src[this.src_idx+2]=='>' ) {
if( src[this.src_idx+3]=='=' ) {
this.curr_tok.tag = TokenRSHLAssign;
this.src_idx += 4;
return;
}
this.curr_tok.tag = TokenRSHL;
this.src_idx += 3;
return;
}
this.curr_tok.tag = TokenRSHA;
this.src_idx += 2;
return;
} else if( src[this.src_idx+1]=='=' ) {
this.curr_tok.tag = TokenGE;
this.src_idx += 2;
return;
}
this.src_idx++;
this.curr_tok.tag = TokenGT;
return;
}
case '|': {
if( src[this.src_idx+1]=='|' ) {
this.curr_tok.tag = TokenLogicOr;
this.src_idx += 2;
return;
} else if( src[this.src_idx+1]=='=' ) {
this.curr_tok.tag = TokenOrAssign;
this.src_idx += 2;
return;
}
this.src_idx++;
this.curr_tok.tag = TokenOr;
return;
}
case '&': {
if( src[this.src_idx+1]=='&' ) {
this.curr_tok.tag = TokenLogicAnd;
this.src_idx += 2;
return;
} else if( src[this.src_idx+1]=='=' ) {
this.curr_tok.tag = TokenAndAssign;
this.src_idx += 2;
return;
} else if( src[this.src_idx+1]=='^' ) {
if( src[this.src_idx+2]=='=' ) {
this.curr_tok.tag = TokenAndXorAssign;
this.src_idx += 3;
return;
}
this.curr_tok.tag = TokenAndXor;
this.src_idx += 2;
return;
}
this.src_idx++;
this.curr_tok.tag = TokenAnd;
return;
}
case '^': {
if( src[this.src_idx+1]=='=' ) {
this.curr_tok.tag = TokenXorAssign;
this.src_idx += 2;
return;
}
this.src_idx++;
this.curr_tok.tag = TokenXor;
return;
}
case ':': {
this.src_idx++;
this.curr_tok.tag = TokenColon;
return;
}
case '=': {
if( src[this.src_idx+1]=='=' ) {
this.curr_tok.tag = TokenCmp;
this.src_idx += 2;
return;
}
this.src_idx++;
this.curr_tok.tag = TokenAssign;
return;
}
default: {
this.src_idx++;
}
}
}
this.curr_tok.tag = TokenInvalid;
}
}
/// -------------- PARSER --------------
/**
* CSL "Config Scripting Language" grammar:
*
* script = stmts EOF .
*
* === Statements ===
* stmt = if | while | foreach | decl | assign | expr_stmt | flow .
* stmts = +stmt .
* block = '{' *stmt '}' .
* if = 'if' expr block [ 'else' ( if | block ) ] .
* while = 'for' [ expr ] block .
* decl = 'var' ident '=' expr .
* assign = ident ( [op +] '=' ) expr .
* foreach = 'for' expr block . // requires 'in' expression for this one.
* flow = 'break' | 'continue' .
* expr_stmt = expr .
*
* === Expressions ===
* exprs = expr *( ',' expr ) .
* expr = in_expr .
*
* in_expr = or_expr *( 'in' or_expr ) .
* or_expr = and_expr *( '||' and_expr ) .
* and_expr = cmp_expr *( '&&' cmp_expr ) .
* cmp_expr = rel_expr *( ('==' | '!=') rel_expr ) .
* rel_expr = add_expr *( ('<' | '<=' | '>' | '>=') add_expr ) .
* add_expr = mul_expr *( ('+' | '-' | '|' | '^') mul_expr ) .
* mul_expr = pow_expr *( ('*' | '/' | '%' | '<<' | '>>' | '>>>' | '&' | '&^') pow_expr ) .
* pow_expr = pre_expr *( '**' pre_expr ) .
* fn_expr = pre_expr *( ident pre_expr ) . /// removed for interferring with decl syntax. Need statement terminators.
* pre_expr = ( "+" | "-" | "!" | "^" | "defined" ) post_expr .
* post_expr = factor *( '[' exprs ']' | '(' exprs ')' | '.' ident ) .
*
* key_expr = ident ':' expr .
* key_exprs = key_expr *( ',' key_expr ) .
* factor = number | ident | string | 'nil' | '(' expr ')' | '{' key_exprs '}' .
* comment = '#' ... .
*
* symbols that are all upper-case are immutable.
```py
# comment.
var player = get_uid(event.userid)
player.dmg_counter += event.damageamount
if a {}
if a {} else {}
if a {} else if b {} else {}
for i < a.CONSTANT {}
for v in m {}
a += b
```
*/
enum {
NODE_INVALID,
NODE_INT_LIT,
NODE_FLOAT_LIT,
NODE_STR_LIT,
NODE_IDENT,
NODE_NIL,
NODE_BIN_OP, /// .a is left expr node, .b is right expr node.
NODE_UNARY_OP, /// .a is the expr we're doing the op on.
NODE_FUNC_CALL, /// .a is the caller, .b is the start of the arg list, .c is number of args.
NODE_ARRAY_IDX, /// .a is indexer, .b is expr list of exprs.
NODE_EXPR_LIST, /// .a is expr node, .b is next node in expr list.
NODE_PROP_ACCESS, /// .a is the map, .b is the property.
NODE_KEY_VAL, /// .a is the prop, .b is the value.
NODE_KEYVAL_EXPR, /// .a is the expr list of keyvals, .b is the number of keyvals.
NODE_VEC_EXPR, /// .a is 1st index expression, .b is second, .c is third.
NODE_END_NODE_EXPRS,
NODE_START_NODE_STMTS,
NODE_IF_STMT, /// .a is expr, .b is block stmt, .c is else or -1 if there was no else block.
NODE_BLOCK_STMT, /// .a is stmts list, .b is the number of stmts.
NODE_FLOW_STMT, /// .op is used for this one, either a break or continue.
NODE_SIMPLE_FOR, ///
NODE_FOR_EACH, ///
NODE_DECL, /// .a is identifier, .b is initializer expr.
NODE_ASSIGN, /// .a is the lhs expr, .b is the rhs expr, .op is operation.
NODE_EXPR_STMT, /// .a is the expr node.
NODE_STMT_LIST, /// .a is stmt node, .b is next node in stmt list.
NODE_END_NODE_STMTS,
NODE_SCRIPT, /// .a is stmts list, .b is the number of stmts.
};
enum struct CSLNode {
/// value of identifier/string.
char s[LEXEME_SIZE];
/// value of a literal.
any val;
/// used for binary ops. use `a` for unary ops.
/// a, b, & c are used as indexes to other AST data.
int a;
int b;
int c;
/// this uses Token enum or other values.
int op;
/// denotes what kind of node we're dealing with.
int node_type;
void MakeIntLit(int value) {
this.node_type = NODE_INT_LIT;
this.val = value;
}
void MakeFloatLit(float value) {
this.node_type = NODE_FLOAT_LIT;
this.val = value;
}
void MakeStrLit(const char strval[LEXEME_SIZE]) {
this.node_type = NODE_STR_LIT;
this.s = strval;
}
void MakeIdent(const char name[LEXEME_SIZE]) {
this.node_type = NODE_IDENT;
this.s = name;
}
void MakeNil() {
this.node_type = NODE_NIL;
}
void MakeBinaryNode(int node_type, int a_node, int b_node, int op) {
this.node_type = node_type;
this.a = a_node;
this.b = b_node;
this.op = op;
}
void MakeUnaryNode(int node_type, int a_node, int op) {
this.node_type = node_type;
this.a = a_node;
this.op = op;
}
}
enum struct CSL_AST {
ArrayList ast; /// []CSLNode
/// this is for keeping track of the strings & maps
/// so we do not leak handles.
ArrayList tracked_maps;
ArrayList tracked_strs;
StringMap vars;
void Init() {
this.ast = new ArrayList(sizeof(CSLNode));
this.tracked_maps = new ArrayList();
this.tracked_strs = new ArrayList();
this.vars = new StringMap();
}
void Destroy() {
delete this.ast;
int len = this.tracked_maps.Length;
for( int i; i < len; i++ ) {
CSLObj data;
this.tracked_maps.GetArray(i, data, sizeof(data));
data.Destroy(true);
}
delete this.tracked_maps;
len = this.tracked_strs.Length;
for( int i; i < len; i++ ) {
CSLObj data;
this.tracked_strs.GetArray(i, data, sizeof(data));
data.Destroy(true);
}
delete this.tracked_strs;
}
int InsertNode(CSLNode node) {
return this.ast.PushArray(node, sizeof(node));
}
int GetNodeType(int n) {
if( n >= this.ast.Length ) {
return NODE_INVALID;
}
CSLNode node;
this.ast.GetArray(n, node, sizeof(node));
return node.node_type;
}
bool GetNode(int n, CSLNode buffer) {
if( n >= this.ast.Length ) {
return false;
}
return this.ast.GetArray(n, buffer, sizeof(buffer))==sizeof(CSLNode);
}
bool RegisterObj(const char[] name, CSLObj o) {
return this.vars.SetArray(name, o, sizeof(o));
}
bool RegisterFunc(const char[] name, SPFFIFunc fn, Handle plugin=null) {
CSLObj fnobj; fnobj.MakeFunc(fn, plugin);
return this.vars.SetArray(name, fnobj, sizeof(fnobj));
}
bool RegisterFloat(const char[] name, float f) {
CSLObj o; o.MakeFloat(f);
return this.vars.SetArray(name, o, sizeof(o));
}
bool RegisterInt(const char[] name, int i) {
CSLObj o; o.MakeInt(i);
return this.vars.SetArray(name, o, sizeof(o));
}
bool RegisterStr(const char[] name, const char[] str) {
CSLObj o; o.MakeStr(str);
return this.vars.SetArray(name, o, sizeof(o));
}
bool RegisterTable(const char[] name, StringMap map) {
CSLObj o; o.MakeMap(map);
return this.vars.SetArray(name, o, sizeof(o));
}
bool RegisterVec(const char[] name, float vec[3]) {
CSLObj o; o.MakeVec(vec);
return this.vars.SetArray(name, o, sizeof(o));
}
bool CreateNameSpace(const char[] name) {
StringMap names = new StringMap();
CSLObj namespc; namespc.MakeMap(names);
return this.RegisterObj(name, namespc);
}
bool AddToNameSpace(const char[] name, const char[] v, CSLObj o) {
CSLObj namespc;
if( !this.vars.GetArray(name, namespc, sizeof(namespc)) ) {
return false;
}
StringMap m = namespc.GetMap();
return( m==null )? false : m.SetArray(v, o, sizeof(o));
}
bool AddFuncToNameSpace(const char[] name, const char[] v, SPFFIFunc fn, Handle plugin=null) {
CSLObj namespc;
if( !this.vars.GetArray(name, namespc, sizeof(namespc)) ) {
return false;
}
StringMap m = namespc.GetMap();
CSLObj fnobj; fnobj.MakeFunc(fn, plugin);
return( m==null )? false : m.SetArray(v, fnobj, sizeof(fnobj));
}
bool AddFloatToNameSpace(const char[] name, const char[] v, float f) {
CSLObj namespc;
if( !this.vars.GetArray(name, namespc, sizeof(namespc)) ) {
return false;
}
StringMap m = namespc.GetMap();
CSLObj o; o.MakeFloat(f);
return( m==null )? false : m.SetArray(v, o, sizeof(o));
}
bool AddIntToNameSpace(const char[] name, const char[] v, int i) {
CSLObj namespc;
if( !this.vars.GetArray(name, namespc, sizeof(namespc)) ) {
return false;
}
StringMap m = namespc.GetMap();
CSLObj o; o.MakeInt(i);
return( m==null )? false : m.SetArray(v, o, sizeof(o));
}
bool AddStrToNameSpace(const char[] name, const char[] v, const char[] str) {
CSLObj namespc;
if( !this.vars.GetArray(name, namespc, sizeof(namespc)) ) {
return false;
}
StringMap m = namespc.GetMap();
CSLObj o; o.MakeStr(str);
return( m==null )? false : m.SetArray(v, o, sizeof(o));
}
bool AddTableToNameSpace(const char[] name, const char[] v, StringMap map) {
CSLObj namespc;
if( !this.vars.GetArray(name, namespc, sizeof(namespc)) ) {
return false;
}
StringMap m = namespc.GetMap();
CSLObj o; o.MakeMap(map);
return( m==null )? false : m.SetArray(v, o, sizeof(o));
}
bool AddVecToNameSpace(const char[] name, const char[] v, float vec[3]) {
CSLObj namespc;
if( !this.vars.GetArray(name, namespc, sizeof(namespc)) ) {
return false;
}
StringMap m = namespc.GetMap();
CSLObj o; o.MakeVec(vec);
return( m==null )? false : m.SetArray(v, o, sizeof(o));
}
bool DestroyNameSpace(const char[] name) {
CSLObj namespc;
if( !this.vars.GetArray(name, namespc, sizeof(namespc)) ) {
return false;
}
namespc.Destroy(true);
return this.vars.Remove(name);
}
void PrintAllVars() {
StringMapSnapshot snap = this.vars.Snapshot();
if( snap==null ) {
return;
}
int entries = snap.Length;
for( int i; i < entries; i++ ) {
int strsize = snap.KeyBufferSize(i) + 1;
char[] key_buffer = new char[strsize];
snap.GetKey(i, key_buffer, strsize);
CSLObj val; this.vars.GetArray(key_buffer, val, sizeof(val));
char type_tag[64];
switch( val.tag ) {
case OBJ_INT: type_tag = "int";
case OBJ_FLT: type_tag = "float";
case OBJ_TAB: type_tag = "table";
case OBJ_STR: type_tag = "string";
case OBJ_FNC: type_tag = "func";
case OBJ_VEC: type_tag = "vec";
case OBJ_NIL: type_tag = "nil";
}
PrintToServer("'%s' :: '%s'", key_buffer, type_tag);
}
delete snap;
}
}
void PrintAST(CSL_AST ast, int n) {
if( n < 0 ) {
PrintToServer("PrintAST :: 'n' is negative.");
return;
}
CSLNode node;
ast.ast.GetArray(n, node, sizeof(node));
switch( node.node_type ) {
case NODE_INVALID: {
PrintToServer("Invalid Node");
}
case NODE_INT_LIT: {
PrintToServer("Int Literal: '%i'", node.val);
}
case NODE_FLOAT_LIT: {
float f = node.val;
PrintToServer("Float Literal: '%f'", f);
}
case NODE_STR_LIT: {
PrintToServer("String Literal: '%s'", node.s);
}
case NODE_IDENT: {
PrintToServer("Ident Expr: '%s'", node.s);
}
case NODE_NIL: {
PrintToServer("Nil Expr");
}
case NODE_BIN_OP: {
char op_str[LEXEME_SIZE];
switch( node.op ) {
case TokenAdd: op_str = "+";
case TokenSub: op_str = "-";
case TokenMul: op_str = "*";
case TokenDiv: op_str = "/";
case TokenMod: op_str = "%";
case TokenPow: op_str = "**";
case TokenLSH: op_str = "<<";
case TokenRSHA: op_str = ">>";
case TokenRSHL: op_str = ">>>";
case TokenXor: op_str = "^";
case TokenAnd: op_str = "&";
case TokenAndXor: op_str = "&^";
case TokenOr: op_str = "|";
case TokenCmp: op_str = "==";
case TokenNotCmp: op_str = "!=";
case TokenLT: op_str = "<";
case TokenLE: op_str = "<=";
case TokenGT: op_str = ">";
case TokenGE: op_str = ">=";
case TokenLogicAnd: op_str = "&&";
case TokenLogicOr: op_str = "||";
case TokenIn: op_str = "in";
default: op_str = node.s;
}
PrintToServer("Binary Expr: '%s', printing left", op_str);
PrintAST(ast, node.a);
PrintToServer("Binary Expr: printing right");
PrintAST(ast, node.b);
if( node.c > 0 ) {
PrintToServer("Binary Expr: printing extra");
PrintAST(ast, node.c);
}
}
case NODE_UNARY_OP: {
char op_str[10];
switch( node.op ) {
case TokenAdd: op_str = "+";
case TokenSub: op_str = "-";
case TokenXor: op_str = "^";
case TokenNot: op_str = "!";
case TokenDefined: op_str = "defined";
}
PrintToServer("PrintAST :: Unary Expr: '%s'", op_str);
PrintAST(ast, node.a);
}
case NODE_FUNC_CALL: {
PrintToServer("Function Call :: Func, number of args: '%i'", node.c);
PrintAST(ast, node.a);
PrintToServer("Has Args? '%s'", node.b != -1? "yes" : "no");
if( node.b != -1 ) {
PrintToServer("Function Call :: Arg :: ");
PrintAST(ast, node.b);
}
}
case NODE_EXPR_LIST: {
PrintToServer("Expr List :: ");
PrintAST(ast, node.a);
if( node.b != -1 ) {
PrintToServer("Expr List :: going to next.");
PrintAST(ast, node.b);
}
}
case NODE_PROP_ACCESS: {
PrintToServer("Map Access :: Accessor");
PrintAST(ast, node.a);
PrintToServer("Map Access :: Prop");
PrintAST(ast, node.b);
}
case NODE_KEY_VAL: {
PrintToServer("Key Val :: Prop");
PrintAST(ast, node.a);
PrintToServer("Key Val :: Value");
PrintAST(ast, node.b);
}
case NODE_KEYVAL_EXPR: {
PrintToServer("%sKey Val List :: number of keyvals '%s'", node.a == -1? "Empty " : "", node.b);
if( node.a != -1 ) {
PrintAST(ast, node.a);
}
}
case NODE_VEC_EXPR: {
PrintToServer("Vector Expression :: [");
if( node.a != -1 ) {
PrintAST(ast, node.a);
}
if( node.b != -1 ) {
PrintAST(ast, node.b);
}
if( node.c != -1 ) {
PrintAST(ast, node.c);
}
PrintToServer("]");
}
case NODE_IF_STMT: {
PrintToServer("If Statement :: Printing Condition Expression");
PrintAST(ast, node.a);
PrintToServer("If Statement :: Printing Block");
PrintAST(ast, node.b);
if( node.c != -1 ) {
PrintToServer("If Statement :: Printing Else");
PrintAST(ast, node.c);
}
}
case NODE_STMT_LIST: {
PrintToServer("Statement List :: ");
PrintAST(ast, node.a);
if( node.b != -1 ) {
PrintToServer("Statement List :: going to next.");
PrintAST(ast, node.b);
}
}
case NODE_BLOCK_STMT: {
PrintToServer("Block Statement :: number of statements '%i'", node.b);
if( node.a != -1 ) {
PrintAST(ast, node.a);
}
}
case NODE_EXPR_STMT: {
PrintToServer("Expr Statement :: ");
PrintAST(ast, node.a);
}
case NODE_ASSIGN: {
char op_str[10];
switch( node.op ) {
case TokenAdd: op_str = "+=";
case TokenSub: op_str = "-=";
case TokenMul: op_str = "*=";
case TokenDiv: op_str = "/=";
case TokenMod: op_str = "%=";
case TokenPow: op_str = "**=";
case TokenLSH: op_str = "<<=";
case TokenRSHA: op_str = ">>=";
case TokenRSHL: op_str = ">>>=";
case TokenXor: op_str = "^=";
case TokenAnd: op_str = "&=";
case TokenAndXor: op_str = "&^=";
case TokenOr: op_str = "|=";
case TokenAssign: op_str = "=";
}
PrintToServer("Assignment Statement :: Op: %s", op_str);
PrintToServer("Assignment Statement :: Printing LHS");
PrintAST(ast, node.a);
PrintToServer("Assignment Statement :: Printing RHS");
PrintAST(ast, node.b);
}
case NODE_DECL: {
PrintToServer("Decl Statement :: Var Expr");
PrintAST(ast, node.a);
PrintToServer("Decl Statement :: Initializer Expr");
PrintAST(ast, node.b);
}
case NODE_SCRIPT: {
PrintToServer("Script :: '%i' statements", node.b);
PrintAST(ast, node.a);
}
}
}
stock int GetListOfNodes(CSL_AST ast, int stmt_list_idx, int[] stmt_list) {
int num_stmts;
int j = stmt_list_idx;
while( j != -1 ) {
CSLNode iter; ast.GetNode(j, iter);
/// iter.a holds the node to the key value expression.
stmt_list[num_stmts] = iter.a;
j = iter.b;
num_stmts++;
}
return num_stmts;
}
bool ExecScript(CSL_AST ast, int n) {
if( n < 0 ) {
LogError("ConfigScript :: ExecScript :: Runtime Error :: bad script node (%i)", n);
return false;
}
CSLNode node; ast.ast.GetArray(n, node, sizeof(node));
if( node.node_type != NODE_SCRIPT ) {
LogError("ConfigScript :: ExecScript :: Runtime Error :: node is not script node (%i)", node.node_type);
return false;
}
int[] stmt_list = new int[node.b];
int num_stmts = GetListOfNodes(ast, node.a, stmt_list);
return ExecStmts(ast, stmt_list, num_stmts);
}
bool ExecStmts(CSL_AST ast, int[] stmt_list, int num_stmts) {
for( int curr_stmt; curr_stmt < num_stmts; curr_stmt++ ) {
CSLNode stmt_node; ast.GetNode(stmt_list[curr_stmt], stmt_node);
if( stmt_node.node_type <= NODE_START_NODE_STMTS ) {
LogError("ConfigScript :: ExecStmts :: Runtime Error :: node is not statement node (%i)", stmt_node.node_type);
return false;
}
switch( stmt_node.node_type ) {
case NODE_IF_STMT: {
CSLObj cond;
if( !EvaluateExprs(ast, stmt_node.a, cond) ) {
LogError("ConfigScript :: ExecStmts :: Runtime Error :: failed to evaluate condition for if-statement");
return false;
}
if( cond.val != 0 ) {
if( !ExecStmt(ast, stmt_node.b) ) {
LogError("ConfigScript :: ExecStmts :: Runtime Error :: failed to evaluate statements for if-statement");
return false;
}
} else if( stmt_node.c != -1 ) {
if( !ExecStmt(ast, stmt_node.c) ) {
LogError("ConfigScript :: ExecStmts :: Runtime Error :: failed to evaluate condition for if-statement else block");
return false;
}
}
}
case NODE_BLOCK_STMT: {
int[] nested_stmt_list = new int[stmt_node.b];
int num_nested_stmts = GetListOfNodes(ast, stmt_node.a, nested_stmt_list);
if( !ExecStmts(ast, nested_stmt_list, num_nested_stmts) ) {
LogError("ConfigScript :: ExecStmts :: Runtime Error :: block statement failed!");
return false;
}
}
case NODE_EXPR_STMT: {
CSLObj throwaway;
EvaluateExprs(ast, stmt_node.a, throwaway);
}
case NODE_DECL: {
CSLNode ident; ast.GetNode(stmt_node.a, ident);
if( ident.node_type != NODE_IDENT ) {
LogError("ConfigScript :: ExecStmts :: Runtime Error :: cannot declare without an identifier.");
return false;
}
CSLObj init_expr;
if( !EvaluateExprs(ast, stmt_node.b, init_expr) ) {
LogError("ConfigScript :: ExecStmts :: Runtime Error :: failed to evaluate expression for declaration.");
return false;
}
return ast.vars.SetArray(ident.s, init_expr, sizeof(init_expr));
}
case NODE_ASSIGN: {
/// lvalues.
/// a = b
/// a.b = c
/// a[b] = c
/// a.b[c] = d
/// a.b.c = d
CSLNode lvalue_node; ast.GetNode(stmt_node.a, lvalue_node);
switch( lvalue_node.node_type ) {
case NODE_IDENT: {
CSLObj rvalue_obj; EvaluateExprs(ast, stmt_node.b, rvalue_obj);
ast.vars.SetArray(lvalue_node.s, rvalue_obj, sizeof(rvalue_obj));
}
case NODE_PROP_ACCESS: {
CSLObj map;
CSLNode accessor; ast.GetNode(stmt_node.a, accessor);
CSLNode accessee; ast.GetNode(stmt_node.b, accessee);
while( accessee.node_type==NODE_PROP_ACCESS ) {
CSLNode prop_node; ast.GetNode(accessor.b, prop_node);
ast.GetNode(prop_node.b, accessee);
accessor = accessee;
}
if( accessee.node_type==NODE_ARRAY_IDX ) {
} else if( accessee.node_type==NODE_IDENT ) {
PrintToServer("accessor.node_type == %s | accessee.node_type==NODE_IDENT", accessor.s);
} else {
/// error, not an lvalue.
return false;
}
}
case NODE_ARRAY_IDX: {
}
default: {
/// error, not an lvalue.
return false;
}
}
}
}
}
return true;
}
bool ExecStmt(CSL_AST ast, int n) {
if( n < 0 ) {
LogError("ConfigScript :: ExecStmt :: Runtime Error :: node index is bad (%i)", n);
return false;
}
CSLNode node; ast.ast.GetArray(n, node, sizeof(node));
if( node.node_type <= NODE_START_NODE_STMTS ) {
LogError("ConfigScript :: ExecStmt :: Runtime Error :: node is not statement node (%i)", node.node_type);
return false;
}
switch( node.node_type ) {
case NODE_IF_STMT: {
CSLObj cond;
if( !EvaluateExprs(ast, node.a, cond) ) {
LogError("ConfigScript :: ExecStmt :: Runtime Error :: failed to evaluate condition for if-statement");
return false;
}
if( cond.val != 0 ) {
if( !ExecStmt(ast, node.b) ) {
LogError("ConfigScript :: ExecStmt :: Runtime Error :: failed to evaluate statements for if-statement");
return false;
}
} else if( node.c != -1 ) {
if( !ExecStmt(ast, node.c) ) {
LogError("ConfigScript :: ExecStmt :: Runtime Error :: failed to evaluate condition for if-statement else block");
return false;
}
}
return true;
}
case NODE_BLOCK_STMT: {
int[] nested_stmt_list = new int[node.b];
int num_nested_stmts = GetListOfNodes(ast, node.a, nested_stmt_list);
return ExecStmts(ast, nested_stmt_list, num_nested_stmts);
}
case NODE_EXPR_STMT: {
CSLObj throwaway;
return EvaluateExprs(ast, node.a, throwaway);
}
case NODE_DECL: {
CSLNode ident; ast.GetNode(node.a, ident);
if( ident.node_type != NODE_IDENT ) {
LogError("ConfigScript :: ExecStmts :: Runtime Error :: cannot declare without an identifier.");
return false;
}
CSLObj init_expr;
if( !EvaluateExprs(ast, node.b, init_expr) ) {
LogError("ConfigScript :: ExecStmts :: Runtime Error :: failed to evaluate expression for declaration.");
return false;
}
return ast.vars.SetArray(ident.s, init_expr, sizeof(init_expr));
}
}
return false;
}
/// 'vars' is map[string]CSLObj
/// TODO: make this iterative using a stack?
/// This would require a stack for multiple things though...
/**
func DFSIterative(root Node) {
stack := new(NodeStack)
stack.Push(root)
for !stack.IsEmpty() {
curr := stack.Pop()
// do thing with `curr`.
if curr.Left != nil {
stack.push(curr.Left)
}
if curr.Right != nil {
stack.push(curr.Right)
}
}
}
*/
bool EvaluateExprs(CSL_AST ast, int n, CSLObj result) {
if( n < 0 ) {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: node index is bad (%i).", n);
return false;
}
CSLNode node; ast.ast.GetArray(n, node, sizeof(node));
if( node.node_type >= NODE_END_NODE_EXPRS ) {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: node is not an expression node (%i).", node.node_type);
return false;
}
switch( node.node_type ) {
case NODE_INVALID: {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid node.");
return false;
}
case NODE_INT_LIT: {
CSLObj int_lit;
int_lit.Init(OBJ_INT);
int_lit.SetInt(node.val);
result = int_lit;
return true;
}
case NODE_FLOAT_LIT: {
CSLObj flt_lit;
flt_lit.Init(OBJ_FLT);
float f = node.val;
flt_lit.SetFloat(f);
result = flt_lit;
return true;
}
case NODE_IDENT: {
bool res = ast.vars.GetArray(node.s, result, sizeof(result));
if( !res ) {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: no var '%s' exists or failed to retrieve var.", node.s);
/// error here, doesn't exist or couldn't grab.
}
return res;
}
case NODE_STR_LIT: {
result.Init(OBJ_STR);
result.SetStr(node.s, LEXEME_SIZE);
return true;
}
case NODE_NIL: {
result.Destroy();
return true;
}
case NODE_BIN_OP: {
CSLObj resA, resB;
switch( node.op ) {
case TokenAdd: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val + resB.val;
} else if( resA.AreFloatType(resB) ) {
result.Init(OBJ_FLT);
float a = resA.val;
float b = resB.val;
float c = a + b;
result.val = c;
} else if( resA.AreArithmetible(resB) ) {
resA.ConvertToFloat();
resB.ConvertToFloat();
result.Init(OBJ_FLT);
float a = resA.val;
float b = resB.val;
float c = a + b;
result.val = c;
} else if( resA.AreStrType(resB) ) {
ConcatStrs(resA, resB, result);
} else if( (resA.tag==OBJ_FLT || resA.tag==OBJ_INT) && resB.tag==OBJ_STR ) {
resA.ConvertToStr();
ConcatStrs(resA, resB, result);
} else if( (resB.tag==OBJ_FLT || resB.tag==OBJ_INT) && resA.tag==OBJ_STR ) {
resB.ConvertToStr();
ConcatStrs(resA, resB, result);
} else if( resA.AreVecType(resB) ) {
result.Init(OBJ_VEC);
for( int i; i < 3; i++ ) {
result.vec[i] = resA.vec[i] + resB.vec[i];
}
} else if( (resA.tag==OBJ_VEC && resB.IsArithmeticType()) || (resB.tag==OBJ_VEC && resA.IsArithmeticType()) ) {
result.Init(OBJ_VEC);
if( resA.tag==OBJ_VEC ) {
resB.ConvertToFloat();
float f = resB.val;
for( int i; i < 3; i++ ) {
result.vec[i] = resA.vec[i] + f;
}
} else {
resA.ConvertToFloat();
float f = resA.val;
for( int i; i < 3; i++ ) {
result.vec[i] = resB.vec[i] + f;
}
}
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '+'.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '+' operation failed.");
return false;
}
case TokenSub: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val - resB.val;
} else if( resA.AreFloatType(resB) ) {
result.Init(OBJ_FLT);
float a = resA.val;
float b = resB.val;
float c = a - b;
result.val = c;
} else if( resA.AreArithmetible(resB) ) {
resA.ConvertToFloat();
resB.ConvertToFloat();
result.Init(OBJ_FLT);
float a = resA.val;
float b = resB.val;
float c = a - b;
result.val = c;
} else if( resA.AreVecType(resB) ) {
result.Init(OBJ_VEC);
for( int i; i < 3; i++ ) {
result.vec[i] = resA.vec[i] - resB.vec[i];
}
} else if( (resA.tag==OBJ_VEC && resB.IsArithmeticType()) || (resB.tag==OBJ_VEC && resA.IsArithmeticType()) ) {
result.Init(OBJ_VEC);
if( resA.tag==OBJ_VEC ) {
resB.ConvertToFloat();
float f = resB.val;
for( int i; i < 3; i++ ) {
result.vec[i] = resA.vec[i] - f;
}
} else {
resA.ConvertToFloat();
float f = resA.val;
for( int i; i < 3; i++ ) {
result.vec[i] = resB.vec[i] - f;
}
}
} else {
/// error here because maps aren't arithmetible.
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '-'.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '-' operation failed.");
return false;
}
case TokenMul: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val * resB.val;
} else if( resA.AreFloatType(resB) ) {
result.Init(OBJ_FLT);
float a = resA.val;
float b = resB.val;
float c = a * b;
result.val = c;
} else if( resA.AreArithmetible(resB) ) {
resA.ConvertToFloat();
resB.ConvertToFloat();
result.Init(OBJ_FLT);
float a = resA.val;
float b = resB.val;
float c = a * b;
result.val = c;
} else if( resA.AreVecType(resB) ) {
float f = GetVectorDotProduct(resA.vec, resB.vec);
result.MakeFloat(f);
} else if( (resA.tag==OBJ_VEC && resB.IsArithmeticType()) || (resB.tag==OBJ_VEC && resA.IsArithmeticType()) ) {
result.Init(OBJ_VEC);
float v[3], mult;
if( resA.tag==OBJ_VEC ) {
v = resA.vec;
mult = resB.val;
} else {
v = resB.vec;
mult = resA.val;
}
ScaleVector(v, mult);
result.vec = v;
} else {
/// error here because maps aren't arithmetible.
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '*'.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '*' operation failed.");
return false;
}
case TokenDiv: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
if( resB.val==0 ) {
/// div by 0 error.
return false;
}
result.val = resA.val / resB.val;
} else if( resA.AreFloatType(resB) ) {
result.Init(OBJ_FLT);
float a = resA.val;
float b = resB.val;
if( b==0.0 ) {
/// div by 0 error.
return false;
}
float c = a / b;
result.val = c;
} else if( resA.AreArithmetible(resB) ) {
resA.ConvertToFloat();
resB.ConvertToFloat();
float a = resA.val;
float b = resB.val;
if( b==0.0 ) {
/// div by 0 error.
return false;
}
result.MakeFloat(a / b);
} else {
/// error here because maps aren't arithmetible.
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '/'.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '/' operation failed.");
return false;
}
case TokenMod: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
if( resB.val==0 ) {
/// div by 0 error.
return false;
}
result.val = resA.val % resB.val;
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '%'.");
/// Error here because modulo is an int only operation.
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '%' operation failed.");
return false;
}
case TokenPow: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
int a = resA.val;
int b = resB.val;
float f = Pow(a+0.0, b+0.0);
int i = RoundFloat(f);
result.val = i;
} else if( resA.AreFloatType(resB) ) {
result.Init(OBJ_FLT);
float a = resA.val;
float b = resB.val;
float c = Pow(a, b);
result.val = c;
} else if( resA.AreArithmetible(resB) ) {
resA.ConvertToFloat();
resB.ConvertToFloat();
result.Init(OBJ_FLT);
float a = resA.val;
float b = resB.val;
float c = Pow(a, b);
result.val = c;
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '**' aka power exponent.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '**' operation failed.");
return false;
}
case TokenLSH: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val << resB.val;
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '<<' aka Left Bit Shift.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '<<' operation failed.");
return false;
}
case TokenRSHA: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val >> resB.val;
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '>>' aka Right Bit Shift (Arithmetic).");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '>>' operation failed.");
return false;
}
case TokenRSHL: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val >>> resB.val;
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '>>>' aka Right Bit Shift (Logical).");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '>>>' operation failed.");
return false;
}
case TokenXor: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val ^ resB.val;
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '^' aka bitwise XOR.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '^' operation failed.");
return false;
}
case TokenAnd: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val & resB.val;
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '&' aka bitwise AND.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '&' operation failed.");
return false;
}
case TokenAndXor: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val & ~resB.val;
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '&^' aka bitwise AND XOR.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '&^' operation failed.");
return false;
}
case TokenOr: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val | resB.val;
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '|' aka bitwise OR.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '|' operation failed.");
return false;
}
case TokenCmp: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val == resB.val;
} else if( resA.AreFloatType(resB) ) {
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a == b;
} else if( resA.AreArithmetible(resB) ) {
resA.ConvertToFloat();
resB.ConvertToFloat();
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a == b;
} else if( resA.AreStrType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.StrCmp(resB)==0;
} else if( resA.AreVecType(resB) ) {
result.Init(OBJ_INT);
result.val = 1;
for( int i; i < 3; i++ ) {
result.val = resA.vec[i]==resB.vec[i] & 1;
}
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '=='.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '==' operation failed.");
return false;
}
case TokenNotCmp: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val != resB.val;
} else if( resA.AreFloatType(resB) ) {
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a != b;
} else if( resA.AreArithmetible(resB) ) {
resA.ConvertToFloat();
resB.ConvertToFloat();
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a != b;
} else if( resA.AreStrType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.StrCmp(resB) != 0;
} else if( resA.AreVecType(resB) ) {
result.Init(OBJ_INT);
result.val = 1;
for( int i; i < 3; i++ ) {
result.val = resA.vec[i] != resB.vec[i] & 1;
}
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '!='.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '!=' operation failed.");
return false;
}
case TokenLT: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val < resB.val;
} else if( resA.AreFloatType(resB) ) {
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a < b;
} else if( resA.AreArithmetible(resB) ) {
resA.ConvertToFloat();
resB.ConvertToFloat();
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a < b;
} else if( resA.AreStrType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.StrCmp(resB) < 0;
} else if( resA.AreVecType(resB) ) {
result.Init(OBJ_INT);
result.val = 1;
for( int i; i < 3; i++ ) {
result.val = (resA.vec[i] < resB.vec[i]) & 1;
}
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '<'.");
return false;
}
if( node.c != -1 ) {
CSLObj resC;
EvaluateExprs(ast, node.c, resC);
result.val = result.val && resC.val;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '<' operation failed.");
return false;
}
case TokenLE: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val <= resB.val;
} else if( resA.AreFloatType(resB) ) {
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a <= b;
} else if( resA.AreArithmetible(resB) ) {
resA.ConvertToFloat();
resB.ConvertToFloat();
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a <= b;
} else if( resA.AreStrType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.StrCmp(resB) <= 0;
} else if( resA.AreVecType(resB) ) {
result.Init(OBJ_INT);
result.val = 1;
for( int i; i < 3; i++ ) {
result.val = (resA.vec[i] <= resB.vec[i]) & 1;
}
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '<='.");
return false;
}
if( node.c != -1 ) {
CSLObj resC;
EvaluateExprs(ast, node.c, resC);
result.val = result.val && resC.val;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '<=' operation failed.");
return false;
}
case TokenGT: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val > resB.val;
} else if( resA.AreFloatType(resB) ) {
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a > b;
} else if( resA.AreArithmetible(resB) ) {
resA.ConvertToFloat();
resB.ConvertToFloat();
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a > b;
} else if( resA.AreStrType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.StrCmp(resB) > 0;
} else if( resA.AreVecType(resB) ) {
result.Init(OBJ_INT);
result.val = 1;
for( int i; i < 3; i++ ) {
result.val = (resA.vec[i] > resB.vec[i]) & 1;
}
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '>'.");
return false;
}
if( node.c != -1 ) {
CSLObj resC;
EvaluateExprs(ast, node.c, resC);
result.val = result.val && resC.val;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '>' operation failed.");
return false;
}
case TokenGE: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val >= resB.val;
} else if( resA.AreFloatType(resB) ) {
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a >= b;
} else if( resA.AreArithmetible(resB) ) {
resA.ConvertToFloat();
resB.ConvertToFloat();
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a >= b;
} else if( resA.AreStrType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.StrCmp(resB) >= 0;
} else if( resA.AreVecType(resB) ) {
result.Init(OBJ_INT);
result.val = 1;
for( int i; i < 3; i++ ) {
result.val = (resA.vec[i] >= resB.vec[i]) & 1;
}
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '>='.");
return false;
}
if( node.c != -1 ) {
CSLObj resC;
EvaluateExprs(ast, node.c, resC);
result.val = result.val && resC.val;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '>=' operation failed.");
return false;
}
case TokenLogicAnd: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val && resB.val;
} else if( resA.AreFloatType(resB) ) {
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a && b;
} else if( resA.AreArithmetible(resB) ) {
resA.ConvertToFloat();
resB.ConvertToFloat();
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a && b;
} else if( resA.AreStrType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.size && resB.size;
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '&&'.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '&&' operation failed.");
return false;
}
case TokenLogicOr: {
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreIntType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.val || resB.val;
} else if( resA.AreFloatType(resB) ) {
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a || b;
} else if( resA.AreArithmetible(resB) ) {
resA.ConvertToFloat();
resB.ConvertToFloat();
float a = resA.val;
float b = resB.val;
result.Init(OBJ_INT);
result.val = a || b;
} else if( resA.AreStrType(resB) ) {
result.Init(OBJ_INT);
result.val = resA.size || resB.size;
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for binary '||'.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary '||' operation failed.");
return false;
}
case TokenIn: {
/// a in b
if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
if( resA.AreStrType(resB) ) {
/// find substring in str
/// TODO: implement...
result.Init(OBJ_INT);
result.val = 1;
} else if( resB.tag==OBJ_TAB ) {
StringMap map = resB.GetMap();
if( map==null ) {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: bad StringMap for 'in' operator.");
return false;
}
StringMapSnapshot snap = map.Snapshot();
if( snap==null ) {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: failed to create Snapshot for 'in' operator.");
return false;
}
bool is_in_map = false;
int entries = snap.Length;
for( int i; i < entries; i++ ) {
int strsize = snap.KeyBufferSize(i) + 1;
char[] key_buffer = new char[strsize];
snap.GetKey(i, key_buffer, strsize);
CSLObj val;
map.GetArray(key_buffer, val, sizeof(val));
if( resA.AreSameType(val) ) {
switch( resA.tag ) {
case OBJ_INT, OBJ_FLT, OBJ_TAB: {
is_in_map = resA.val==val.val;
}
case OBJ_STR: {
is_in_map = resA.val==val.val && resA.size==val.size;
}
case OBJ_FNC: {
is_in_map = resA.f==val.f;
}
}
}
if( is_in_map ) {
break;
}
}
delete snap;
result.Init(OBJ_INT);
result.val = view_as< int >(is_in_map);
} else if( resA.tag==OBJ_FLT && resB.tag==OBJ_VEC ) {
result.Init(OBJ_INT);
float find = resA.val;
for( int i; i < 3; i++ ) {
if( find==resB.vec[i] ) {
result.val = 1;
break;
}
}
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operands for 'in' binary operator.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: binary 'in' operator failed.");
return false;
}
default: {
/*
if( node.s[0] != 0 ) {
CSLObj binary_func;
if( !ast.vars.GetArray(node.s, binary_func, sizeof(binary_func)) || binary_func.tag != OBJ_FNC ) {
/// error, missing func or something idk.
return false;
} else if( EvaluateExprs(ast, node.a, resA) && EvaluateExprs(ast, node.b, resB) ) {
SPFFIFunc f = binary_func.GetFunc();
Handle fnc_pl = binary_func.val;
if( f==INVALID_FUNCTION ) {
/// error bad function.
return false;
}
Call_StartFunction(fnc_pl, f);
Call_PushArrayEx(resA, sizeof(resA), SM_PARAM_COPYBACK);
Call_PushArrayEx(resB, sizeof(resB), SM_PARAM_COPYBACK);
Call_PushArrayEx(result, sizeof(result), SM_PARAM_COPYBACK);
bool call_result = Call_Finish()==SP_ERROR_NONE;
return call_result;
}
}
*/
return false;
}
}
}
case NODE_UNARY_OP: {
CSLObj resA;
switch( node.op ) {
case TokenAdd: {
/// abs Value.
if( EvaluateExprs(ast, node.a, resA) ) {
if( resA.tag==OBJ_INT ) {
result.Init(OBJ_INT);
result.val = resA.val < 0? -resA.val : resA.val;
} else if( resA.tag==OBJ_FLT ) {
result.Init(OBJ_FLT);
float f = result.val;
f = FloatAbs(f);
result.val = f;
} else if( resA.tag==OBJ_VEC ) {
result.Init(OBJ_VEC);
for( int i; i < 3; i++ ) {
result.vec[i] = FloatAbs(resA.vec[i]);
}
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operand for unary '+' aka absolute value.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: unary '+' operation failed.");
return false;
}
case TokenSub: {
/// Negate
if( EvaluateExprs(ast, node.a, resA) ) {
if( resA.tag==OBJ_INT ) {
result.Init(OBJ_INT);
result.val = -resA.val;
} else if( resA.tag==OBJ_FLT ) {
result.Init(OBJ_FLT);
result.val = resA.val | 0x80000000;
} else if( resA.tag==OBJ_VEC ) {
result.Init(OBJ_VEC);
for( int i; i < 3; i++ ) {
result.vec[i] = -resA.vec[i];
}
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operand for unary '-' aka negate.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: unary '-' operation failed.");
return false;
}
case TokenXor: {
/// Bitwise Complement/NOT.
if( EvaluateExprs(ast, node.a, resA) ) {
if( resA.tag==OBJ_INT ) {
result.Init(OBJ_INT);
result.val = ~resA.val;
} else if( resA.tag==OBJ_VEC ) {
result.Init(OBJ_VEC);
NormalizeVector(resA.vec, result.vec);
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operand for unary '^' aka integer complement/vector normalize.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: unary '^' operation failed.");
return false;
}
case TokenNot: {
/// Bitwise Complement/NOT.
if( EvaluateExprs(ast, node.a, resA) ) {
if( resA.tag==OBJ_INT ) {
result.Init(OBJ_INT);
result.val = !resA.val;
} else if( resA.tag==OBJ_FLT ) {
result.Init(OBJ_FLT);
float f = resA.val;
f = view_as< int >(!f) + 0.0;
result.val = f;
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid operand for unary '!'.");
return false;
}
return true;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: unary '!' operation failed.");
return false;
}
case TokenDefined: {
if( ast.GetNodeType(node.a)==NODE_PROP_ACCESS ) {
CSLNode accessor; ast.GetNode(node.a, accessor);
int accessor_node = accessor.a;
int prop_node = accessor.b;
/// handle nested accesses...
while( ast.GetNodeType(prop_node)==NODE_PROP_ACCESS ) {
CSLNode nested_accessor;
ast.GetNode(prop_node, nested_accessor);
accessor_node = nested_accessor.a;
prop_node = nested_accessor.b;
}
if( !EvaluateExprs(ast, accessor_node, resA) ) {
/// error here for some reason.
return false;
}
CSLNode prop; ast.GetNode(prop_node, prop);
StringMap map = resA.GetMap();
if( map==null ) {
/// error bad map here.
return false;
}
bool res = map.ContainsKey(prop.s);
result.Init(OBJ_INT);
result.val = view_as< int >(res==true);
return res;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: 'defined' operator failed.");
return false;
}
}
}
case NODE_FUNC_CALL: {
CSLObj resF;
if( EvaluateExprs(ast, node.a, resF) && resF.tag==OBJ_FNC ) {
SPFFIFunc f = resF.GetFunc();
Handle fnc_pl = resF.val;
if( f==INVALID_FUNCTION ) {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: invalid function in function call!");
return false;
}
int p = node.b;
int c = 0;
Call_StartFunction(fnc_pl, f);
ArrayList args = new ArrayList(sizeof(CSLObj));
while( p != -1 ) {
CSLNode expr_list_node; ast.GetNode(p, expr_list_node);
CSLObj arg; EvaluateExprs(ast, expr_list_node.a, arg);
args.PushArray(arg, sizeof(arg));
c++;
p = expr_list_node.b;
}
Call_PushCell(args);
Call_PushArrayEx(result, sizeof(result), SM_PARAM_COPYBACK);
bool call_result = Call_Finish()==SP_ERROR_NONE;
int len = args.Length;
for( int i; i < len; i++ ) {
CSLObj arg; args.GetArray(i, arg, sizeof(arg));
arg.Destroy();
}
delete args;
return call_result;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: function call operation failed!");
return false;
}
case NODE_PROP_ACCESS: {
CSLObj resA;
if( EvaluateExprs(ast, node.a, resA) && resA.tag==OBJ_TAB && ast.GetNodeType(node.b)==NODE_IDENT ) {
StringMap map = resA.GetMap();
if( map==null ) {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: bad map in map access operation.");
return false;
}
CSLNode prop_name; ast.GetNode(node.b, prop_name);
return map.GetArray(prop_name.s, result, sizeof(result));
} else {
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: map accessor is not a map.");
return false;
}
LogError("ConfigScript :: EvaluateExprs :: Runtime Error :: map access operation failed.");
return false;
}
case NODE_KEYVAL_EXPR: {
/// node.b holds how many key values were parsed.
int[] kv_nodes = new int[node.b];
int kv_counter = 0;
int j = node.a;
/// first get all the kv node indexes.
while( j != -1 ) {
CSLNode iter; ast.GetNode(j, iter);
/// iter.a holds the node to the key value expression.
kv_nodes[kv_counter] = iter.a;
j = iter.b;
kv_counter++;
}
/// next get kv nodes themselves.
CSLNode[] kvs = new CSLNode[node.b];
bool retrieval = true;
for( int i; i < node.b; i++ ) {
retrieval = ast.GetNode(kv_nodes[i], kvs[i]) && retrieval;
}
if( !retrieval ) {
/// error here.
return false;
}
/// next verify the key parts are identifiers.
retrieval = true;
for( int i; i < node.b; i++ ) {
retrieval = ast.GetNodeType(kvs[i].a)==NODE_IDENT && retrieval;
}
if( !retrieval ) {
/// error, non-ident as key.
return false;
}
/// now we eval the value part.
CSLObj[] vals = new CSLObj[node.b];
retrieval = true;
for( int i; i < node.b; i++ ) {
retrieval = EvaluateExprs(ast, kvs[i].b, vals[i]) && retrieval;
}
if( !retrieval ) {
/// error, expr failed somewhere.
return false;
}
/// setup the map.
CSLObj new_map;
new_map.Init(OBJ_TAB);
StringMap m = new_map.GetMap();
if( m==null ) {
/// out of memory?
new_map.Destroy();
return false;
}
retrieval = true;
for( int i; i < node.b; i++ ) {
CSLNode key_ident; ast.GetNode(kvs[i].a, key_ident);
retrieval = m.SetArray(key_ident.s, vals[i], sizeof(vals[])) && retrieval;
}
if( !retrieval ) {
/// possibly duplicate key?
new_map.Destroy();
return false;
}
result = new_map;
return true;
}
case NODE_VEC_EXPR: {
float v[3];
int nodes[3];
nodes[0] = node.a;
nodes[1] = node.b;
nodes[2] = node.c;
for( int i; i < 3; i++ ) {
CSLObj res;
if( nodes[i] != -1 && EvaluateExprs(ast, nodes[i], res) ) {
res.ConvertToFloat();
v[i] = res.val;
}
}
result.MakeVec(v);
return true;
}
case NODE_ARRAY_IDX: {
CSLObj resA;
if( !EvaluateExprs(ast, node.a, resA) ) {
return false;
} else if( resA.tag != OBJ_STR && resA.tag != OBJ_TAB && resA.tag != OBJ_VEC ) {
/// error, can't 'index' a non-tabular type.
return false;
}
if( node.c==1 ) {
CSLObj resB;
CSLNode expr_of_idx;
ast.GetNode(node.b, expr_of_idx);
EvaluateExprs(ast, expr_of_idx.a, resB);
switch( resA.tag ) {
case OBJ_STR: {
if( resB.tag==OBJ_INT ) {
int len = resA.GetStrLen()
if( resB.val > len ) {
return false;
}
char[] s = new char[len];
if( !resA.GetStr(s, len) ) {
return false;
}
result.MakeInt(s[resB.val]);
return true;
}
return false;
}
case OBJ_VEC: {
if( resB.tag==OBJ_INT ) {
if( resB.val >= 3 ) {
return false;
}
result.MakeFloat(resA.vec[resB.val]);
return true;
}
return false;
}
case OBJ_TAB: {
if( resB.IsArithmeticType() ) {
char buffer[CELL_KEY_SIZE];
PackCellToStr(resB.val, buffer);
StringMap map = resA.GetMap();
if( map==null ) {
/// error here.
return false;
}
return map.GetArray(buffer, result, sizeof(result));
}
return false;
}
}
return false;
}
/// multi-expression indexing.
/// saving for stuff like slicing but keeping
/// commented for now to keep cfgscript small.
/*
int[] expr_nodes = new int[node.c];
int expr_counter = 0;
int j = node.b;
/// first get all the indexing node indexes.
while( j != -1 ) {
CSLNode iter; ast.GetNode(j, iter);
/// iter.a holds the node to the expression.
expr_nodes[expr_counter] = iter.a;
j = iter.b;
expr_counter++;
}
*/
return false;
}
}
return false;
}
enum struct CSLParser {
CSLLexState lexer;
CSL_AST nodes;
bool errored;
void Init(const char[] src, int src_len, const char[] filename) {
this.nodes.Init();
this.lexer.Init();
this.lexer.GetToken(src, src_len);
this.nodes.vars.SetString("filename", filename);
this.nodes.vars.SetValue("filename_size", strlen(filename) + 1);
}
void Destroy() {
this.nodes.Destroy();
}
bool CreateNameSpace(const char[] name) {
return this.nodes.CreateNameSpace(name);
}
bool AddToNameSpace(const char[] name, const char[] v, CSLObj o) {
return this.nodes.AddToNameSpace(name, v, o);
}
bool DestroyNameSpace(const char[] name) {
return this.nodes.DestroyNameSpace(name);
}
bool RegisterObj(const char[] name, CSLObj o) {
return this.nodes.RegisterObj(name, o);
}
bool RegisterFunc(const char[] name, SPFFIFunc fn, Handle plugin=null) {
return this.nodes.RegisterFunc(name, fn, plugin);
}
bool RegisterFloat(const char[] name, float f) {
return this.nodes.RegisterFloat(name, f);
}
bool RegisterInt(const char[] name, int i) {
return this.nodes.RegisterInt(name, i);
}
bool RegisterStr(const char[] name, const char[] str) {
return this.nodes.RegisterStr(name, str);
}
bool RegisterTable(const char[] name, StringMap map) {
return this.nodes.RegisterTable(name, map);
}
bool RegisterVec(const char[] name, float vec[3]) {
return this.nodes.RegisterVec(name, vec);
}
bool AddFuncToNameSpace(const char[] name, const char[] v, SPFFIFunc fn, Handle plugin=null) {
return this.nodes.AddFuncToNameSpace(name, v, fn, plugin);
}
bool AddFloatToNameSpace(const char[] name, const char[] v, float f) {
return this.nodes.AddFloatToNameSpace(name, v, f);
}
bool AddIntToNameSpace(const char[] name, const char[] v, int i) {
return this.nodes.AddIntToNameSpace(name, v, i);
}
bool AddStrToNameSpace(const char[] name, const char[] v, const char[] str) {
return this.nodes.AddStrToNameSpace(name, v, str);
}
bool AddTableToNameSpace(const char[] name, const char[] v, StringMap map) {
return this.nodes.AddTableToNameSpace(name, v, map);
}
bool AddVecToNameSpace(const char[] name, const char[] v, float vec[3]) {
return this.nodes.AddVecToNameSpace(name, v, vec);
}
}
/// script = +stmt EOF .
int ParseScript(CSLParser parser, const char[] src, int src_len) {
CSLNode script; script.node_type = NODE_SCRIPT;
int first_stmt = -1;
int prev_stmt_node = -1;
int num_stmts = 0;
while( parser.lexer.curr_tok.tag != TokenInvalid ) {
int stmt_node = ParseStmt(parser, src, src_len);
if( stmt_node == -1 ) {
int filename_len;
parser.nodes.vars.GetValue("filename_size", filename_len);
char[] filename_str = new char[filename_len + 1];
parser.nodes.vars.GetString("filename", filename_str, filename_len);
LogError("ConfigScript :: %s -- Parse Error :: Statement errored. (L:%i)", filename_str, parser.lexer.line);
break;
}
CSLNode parsed_stmt;
parsed_stmt.node_type = NODE_STMT_LIST;
parsed_stmt.a = stmt_node;
parsed_stmt.b = -1;
int stmt_node_idx = parser.nodes.InsertNode(parsed_stmt);
if( first_stmt == -1 ) {
first_stmt = stmt_node_idx;
}
if( prev_stmt_node != -1 ) {
CSLNode prev_parsed_stmt;
parser.nodes.ast.GetArray(prev_stmt_node, prev_parsed_stmt, sizeof(prev_parsed_stmt));
prev_parsed_stmt.b = stmt_node_idx;
parser.nodes.ast.SetArray(prev_stmt_node, prev_parsed_stmt, sizeof(prev_parsed_stmt));
}
prev_stmt_node = stmt_node_idx;
num_stmts++;
}
if( prev_stmt_node != -1 ) {
CSLNode prev_parsed_stmt;
parser.nodes.ast.GetArray(prev_stmt_node, prev_parsed_stmt, sizeof(prev_parsed_stmt));
prev_parsed_stmt.b = -1;
parser.nodes.ast.SetArray(prev_stmt_node, prev_parsed_stmt, sizeof(prev_parsed_stmt));
}
script.a = first_stmt;
script.b = num_stmts;
return parser.nodes.InsertNode(script);
}
/// Statement Rule Productions.
/// block = '{' +stmt '}' .
int ParseBlock(CSLParser parser, const char[] src, int src_len) {
parser.lexer.GetToken(src, src_len); /// advance past '{'.
CSLNode block_stmt;
block_stmt.node_type = NODE_BLOCK_STMT;
int first_stmt = -1;
int prev_stmt_node = -1;
int num_stmts = 0;
if( parser.lexer.curr_tok.tag != TokenRBrace ) {
while( parser.lexer.curr_tok.tag > TokenInvalid && parser.lexer.curr_tok.tag != TokenRBrace ) {
int stmt_node = ParseStmt(parser, src, src_len);
if( stmt_node == -1 ) {
/// error, stmt messed up.
break;
}
CSLNode parsed_stmt;
parsed_stmt.node_type = NODE_STMT_LIST;
parsed_stmt.a = stmt_node;
parsed_stmt.b = -1;
int stmt_node_idx = parser.nodes.InsertNode(parsed_stmt);
if( first_stmt == -1 ) {
first_stmt = stmt_node_idx;
}
if( prev_stmt_node != -1 ) {
CSLNode prev_parsed_stmt;
parser.nodes.ast.GetArray(prev_stmt_node, prev_parsed_stmt, sizeof(prev_parsed_stmt));
prev_parsed_stmt.b = stmt_node_idx;
parser.nodes.ast.SetArray(prev_stmt_node, prev_parsed_stmt, sizeof(prev_parsed_stmt));
}
prev_stmt_node = stmt_node_idx;
num_stmts++;
}
if( parser.lexer.curr_tok.tag != TokenRBrace ) {
/// error here.
return -1;
}
if( prev_stmt_node != -1 ) {
CSLNode prev_parsed_stmt;
parser.nodes.ast.GetArray(prev_stmt_node, prev_parsed_stmt, sizeof(prev_parsed_stmt));
prev_parsed_stmt.b = -1;
parser.nodes.ast.SetArray(prev_stmt_node, prev_parsed_stmt, sizeof(prev_parsed_stmt));
}
}
if( parser.lexer.curr_tok.tag==TokenRBrace ) {
parser.lexer.GetToken(src, src_len); /// advance past '}'.
}
block_stmt.a = first_stmt;
block_stmt.b = num_stmts;
return parser.nodes.InsertNode(block_stmt);
}
/// stmt = if | while | foreach | decl | assign | expr_stmt | flow .
int ParseStmt(CSLParser parser, const char[] src, int src_len) {
switch( parser.lexer.curr_tok.tag ) {
case TokenIf: return ParseIfStmt(parser, src, src_len);
case TokenVar: return ParseDeclStmt(parser, src, src_len);
case TokenFor: return ParseForStmt(parser, src, src_len);
case TokenBreak, TokenContinue: { /// flow = 'break' | 'continue' .
CSLNode flow;
flow.node_type = NODE_FLOW_STMT;
flow.op = parser.lexer.curr_tok.tag;
parser.lexer.GetToken(src, src_len);
return parser.nodes.InsertNode(flow);
}
case TokenIdent: return ParseAssignOrExprStmt(parser, src, src_len);
default: {
int filename_len;
parser.nodes.vars.GetValue("filename_size", filename_len);
char[] filename_str = new char[filename_len + 1];
parser.nodes.vars.GetString("filename", filename_str, filename_len);
LogError("ConfigScript :: %s -- Parse Error :: unknown token. (L:%i)", filename_str, parser.lexer.line);
/// error, bad statement starter.
}
}
return -1;
}
/// if = 'if' expr block [ 'else' ( if | block ) ] .
int ParseIfStmt(CSLParser parser, const char[] src, int src_len) {
parser.lexer.GetToken(src, src_len); /// advance past 'if'.
if( parser.lexer.curr_tok.tag==TokenLBrace ) {
int filename_len;
parser.nodes.vars.GetValue("filename_size", filename_len);
char[] filename_str = new char[filename_len + 1];
parser.nodes.vars.GetString("filename", filename_str, filename_len);
LogError("ConfigScript :: %s -- Parse Error :: if-statement missing condition expression. (L:%i)", filename_str, parser.lexer.line);
/// error, missing condition for if-stmt.
return -1;
}
CSLNode if_stmt;
if_stmt.node_type = NODE_IF_STMT;
if_stmt.a = ParseMainExpr(parser, src, src_len);
if_stmt.b = ParseBlock(parser, src, src_len);
if_stmt.c = -1;
if( parser.lexer.curr_tok.tag==TokenElse ) {
parser.lexer.GetToken(src, src_len); /// advance past 'else'.
if( parser.lexer.curr_tok.tag==TokenIf ) {
if_stmt.c = ParseIfStmt(parser, src, src_len);
} else if( parser.lexer.curr_tok.tag==TokenLBrace ) {
if_stmt.c = ParseBlock(parser, src, src_len);
} else {
/// error, bad else portion.
int filename_len;
parser.nodes.vars.GetValue("filename_size", filename_len);
char[] filename_str = new char[filename_len + 1];
parser.nodes.vars.GetString("filename", filename_str, filename_len);
LogError("ConfigScript :: %s -- Parse Error :: bad else block. (L:%i)", filename_str, parser.lexer.line);
return -1;
}
}
return parser.nodes.InsertNode(if_stmt);
}
/// while = 'for' expr block .
/// foreach = 'for' expr block . // requires 'in' expression for this one.
int ParseForStmt(CSLParser parser, const char[] src, int src_len) {
parser.lexer.GetToken(src, src_len); /// advance past 'for'.
/// TODO:
return -1;
}
/// decl = 'var' ident '=' expr .
int ParseDeclStmt(CSLParser parser, const char[] src, int src_len) {
parser.lexer.GetToken(src, src_len); /// advance past 'var'.
CSLNode decl_stmt;
decl_stmt.node_type = NODE_DECL;
decl_stmt.a = ParseMainExpr(parser, src, src_len);
if( parser.lexer.curr_tok.tag != TokenAssign ) {
int filename_len;
parser.nodes.vars.GetValue("filename_size", filename_len);
char[] filename_str = new char[filename_len + 1];
parser.nodes.vars.GetString("filename", filename_str, filename_len);
LogError("ConfigScript :: %s -- Parse Error :: missing '=' for variable declaration. (L:%i)", filename_str, parser.lexer.line);
return -1;
}
parser.lexer.GetToken(src, src_len); /// advance past '='.
decl_stmt.b = ParseMainExpr(parser, src, src_len);
return parser.nodes.InsertNode(decl_stmt);
}
/// assign = ident ( [op +] '=' ) expr .
/// expr_stmt = expr .
/**
* expression stmts and assigns always start with an identifier.
* because of that, we can merge the two production rules into one function.
*/
int ParseAssignOrExprStmt(CSLParser parser, const char[] src, int src_len) {
CSLNode stmt_with_ident;
int possible_ident = ParseMainExpr(parser, src, src_len);
if( parser.lexer.curr_tok.tag >= TokenAssign && parser.lexer.curr_tok.tag <= TokenOrAssign ) {
/// assign statement.
stmt_with_ident.node_type = NODE_ASSIGN;
int op = parser.lexer.curr_tok.tag;
parser.lexer.GetToken(src, src_len);
stmt_with_ident.a = possible_ident;
int b = ParseMainExpr(parser, src, src_len);
if( parser.lexer.curr_tok.tag != TokenAssign ) {
CSLNode bin_op;
switch( op ) {
case TokenAddAssign: {
bin_op.MakeBinaryNode(NODE_BIN_OP, possible_ident, b, TokenAdd);
}
case TokenSubAssign: {
bin_op.MakeBinaryNode(NODE_BIN_OP, possible_ident, b, TokenSub);
}
case TokenMulAssign: {
bin_op.MakeBinaryNode(NODE_BIN_OP, possible_ident, b, TokenMul);
}
case TokenDivAssign: {
bin_op.MakeBinaryNode(NODE_BIN_OP, possible_ident, b, TokenDiv);
}
case TokenModAssign: {
bin_op.MakeBinaryNode(NODE_BIN_OP, possible_ident, b, TokenMod);
}
case TokenPowAssign: {
bin_op.MakeBinaryNode(NODE_BIN_OP, possible_ident, b, TokenPow);
}
case TokenLSHAssign: {
bin_op.MakeBinaryNode(NODE_BIN_OP, possible_ident, b, TokenLSH);
}
case TokenRSHAAssign: {
bin_op.MakeBinaryNode(NODE_BIN_OP, possible_ident, b, TokenRSHA);
}
case TokenRSHLAssign: {
bin_op.MakeBinaryNode(NODE_BIN_OP, possible_ident, b, TokenRSHL);
}
case TokenXorAssign: {
bin_op.MakeBinaryNode(NODE_BIN_OP, possible_ident, b, TokenXor);
}
case TokenAndAssign: {
bin_op.MakeBinaryNode(NODE_BIN_OP, possible_ident, b, TokenAnd);
}
case TokenAndXorAssign: {
bin_op.MakeBinaryNode(NODE_BIN_OP, possible_ident, b, TokenAndXor);
}
case TokenOrAssign: {
bin_op.MakeBinaryNode(NODE_BIN_OP, possible_ident, b, TokenOr);
}
}
b = parser.nodes.InsertNode(bin_op);
}
stmt_with_ident.b = b;
return parser.nodes.InsertNode(stmt_with_ident);
}
/// otherwise consider it an expression statement.
stmt_with_ident.node_type = NODE_EXPR_STMT;
stmt_with_ident.a = possible_ident;
return parser.nodes.InsertNode(stmt_with_ident);
}
/// Expression Rule Productions.
/// expr = in_expr .
int ParseMainExpr(CSLParser parser, const char[] src, int src_len) {
return ParseInExpr(parser, src, src_len);
}
/// in_expr = or_expr *( 'in' or_expr ) .
int ParseInExpr(CSLParser parser, const char[] src, int src_len) {
int n = ParseOrExpr(parser, src, src_len);
while( parser.lexer.curr_tok.tag==TokenIn ) {
int op = parser.lexer.curr_tok.tag;
parser.lexer.GetToken(src, src_len);
int b_node = ParseOrExpr(parser, src, src_len);
CSLNode node;
node.MakeBinaryNode(NODE_BIN_OP, n, b_node, op);
n = parser.nodes.InsertNode(node);
}
return n;
}
/// or_expr = and_expr *( '||' and_expr ) .
int ParseOrExpr(CSLParser parser, const char[] src, int src_len) {
int n = ParseAndExpr(parser, src, src_len);
while( parser.lexer.curr_tok.tag==TokenLogicOr ) {
int op = parser.lexer.curr_tok.tag;
parser.lexer.GetToken(src, src_len);
int b_node = ParseAndExpr(parser, src, src_len);
CSLNode node;
node.MakeBinaryNode(NODE_BIN_OP, n, b_node, op);
n = parser.nodes.InsertNode(node);
}
return n;
}
/// and_expr = cmp_expr *( '&&' cmp_expr ) .
int ParseAndExpr(CSLParser parser, const char[] src, int src_len) {
int n = ParseCmpExpr(parser, src, src_len);
while( parser.lexer.curr_tok.tag==TokenLogicAnd ) {
int op = parser.lexer.curr_tok.tag;
parser.lexer.GetToken(src, src_len);
int b_node = ParseCmpExpr(parser, src, src_len);
CSLNode node;
node.MakeBinaryNode(NODE_BIN_OP, n, b_node, op);
n = parser.nodes.InsertNode(node);
}
return n;
}
/// cmp_expr = rel_expr *( ('==' | '!=') rel_expr ) .
int ParseCmpExpr(CSLParser parser, const char[] src, int src_len) {
int n = ParseRelExpr(parser, src, src_len);
while( parser.lexer.curr_tok.tag==TokenCmp || parser.lexer.curr_tok.tag==TokenNotCmp ) {
int op = parser.lexer.curr_tok.tag;
parser.lexer.GetToken(src, src_len);
int b_node = ParseRelExpr(parser, src, src_len);
CSLNode node;
node.MakeBinaryNode(NODE_BIN_OP, n, b_node, op);
n = parser.nodes.InsertNode(node);
}
return n;
}
/// rel_expr = add_expr *( ('<' | '<=' | '>' | '>=') add_expr ) .
int ParseRelExpr(CSLParser parser, const char[] src, int src_len) {
int n = ParseAddExpr(parser, src, src_len);
/**
* a <= b <= c
* [ .a = A, .b = B, .op = OP, .c = [ .a = B, .b = C, .op = OP, .c = -1 ] ]
*
* a <= b <= c <= d
* [ .a = A, .b = B, .op = OP, .c = [ .a = B, .b = C, .op = OP, .c = [ .a = C, .b = D, .op = OP, .c = -1 ] ] ]
*
* parse expr like normal. Check if another op is there, if so, rerun to .c
*/
CSLNode chain_node;
chain_node.node_type = NODE_BIN_OP;
chain_node.a = n;
if( parser.lexer.curr_tok.tag==TokenLT
|| parser.lexer.curr_tok.tag==TokenLE
|| parser.lexer.curr_tok.tag==TokenGT
|| parser.lexer.curr_tok.tag==TokenGE
) {
int op = parser.lexer.curr_tok.tag;
parser.lexer.GetToken(src, src_len);
CSLLexState saved_lex; saved_lex = parser.lexer;
chain_node.b = ParseAddExpr(parser, src, src_len);
chain_node.op = op;
if( parser.lexer.curr_tok.tag==TokenLT
|| parser.lexer.curr_tok.tag==TokenLE
|| parser.lexer.curr_tok.tag==TokenGT
|| parser.lexer.curr_tok.tag==TokenGE
) {
parser.lexer = saved_lex;
chain_node.c = ParseRelExpr(parser, src, src_len);
}
n = parser.nodes.InsertNode(chain_node);
}
return n;
}
/// add_expr = mul_expr *( ('+' | '-' | '|' | '^') mul_expr ) .
int ParseAddExpr(CSLParser parser, const char[] src, int src_len) {
int n = ParseMulExpr(parser, src, src_len);
while( parser.lexer.curr_tok.tag==TokenAdd
|| parser.lexer.curr_tok.tag==TokenSub
|| parser.lexer.curr_tok.tag==TokenOr
|| parser.lexer.curr_tok.tag==TokenXor
) {
int op = parser.lexer.curr_tok.tag;
parser.lexer.GetToken(src, src_len);
int b_node = ParseMulExpr(parser, src, src_len);
CSLNode node;
node.MakeBinaryNode(NODE_BIN_OP, n, b_node, op);
n = parser.nodes.InsertNode(node);
}
return n;
}
/// mul_expr = pow_expr *( ('*' | '/' | '%' | '<<' | '>>' | '>>>' | '&' | '&^') pow_expr ) .
int ParseMulExpr(CSLParser parser, const char[] src, int src_len) {
int n = ParsePowExpr(parser, src, src_len);
while( parser.lexer.curr_tok.tag==TokenMul
|| parser.lexer.curr_tok.tag==TokenDiv
|| parser.lexer.curr_tok.tag==TokenMod
|| parser.lexer.curr_tok.tag==TokenLSH
|| parser.lexer.curr_tok.tag==TokenRSHA
|| parser.lexer.curr_tok.tag==TokenRSHL
|| parser.lexer.curr_tok.tag==TokenAnd
|| parser.lexer.curr_tok.tag==TokenAndXor
) {
int op = parser.lexer.curr_tok.tag;
parser.lexer.GetToken(src, src_len);
int b_node = ParsePowExpr(parser, src, src_len);
CSLNode node;
node.MakeBinaryNode(NODE_BIN_OP, n, b_node, op);
n = parser.nodes.InsertNode(node);
}
return n;
}
/// pow_expr = pre_expr *( '**' pre_expr ) .
int ParsePowExpr(CSLParser parser, const char[] src, int src_len) {
int n = ParsePreExpr(parser, src, src_len);
while( parser.lexer.curr_tok.tag==TokenPow ) {
int op = parser.lexer.curr_tok.tag;
parser.lexer.GetToken(src, src_len);
int b_node = ParsePreExpr(parser, src, src_len);
CSLNode node;
node.MakeBinaryNode(NODE_BIN_OP, n, b_node, op);
n = parser.nodes.InsertNode(node);
}
return n;
}
/// pre_expr = *( "+" | "-" | "!" | "^" | 'defined' ) post_expr .
int ParsePreExpr(CSLParser parser, const char[] src, int src_len) {
switch( parser.lexer.curr_tok.tag ) {
case TokenAdd, TokenSub, TokenNot, TokenXor, TokenDefined: {
/// abs value, negate, logical NOT, bool NOT/complement.
CSLNode unary;
int op = parser.lexer.curr_tok.tag;
parser.lexer.GetToken(src, src_len);
int n = ParsePreExpr(parser, src, src_len);
unary.MakeUnaryNode(NODE_UNARY_OP, n, op);
return parser.nodes.InsertNode(unary);
}
}
return ParsePostExpr(parser, src, src_len);
}
/// post_expr = factor *( '[' exprs ']' | '(' exprs ')' | '.' ident ) .
int ParsePostExpr(CSLParser parser, const char[] src, int src_len) {
int n = ParseFactor(parser, src, src_len);
while( parser.lexer.curr_tok.tag==TokenLParen || parser.lexer.curr_tok.tag==TokenDot || parser.lexer.curr_tok.tag==TokenLSq ) {
switch( parser.lexer.curr_tok.tag ) {
case TokenLSq: {
int end_token = TokenRSq;
CSLNode array_idx;
array_idx.node_type = NODE_ARRAY_IDX;
array_idx.a = n;
array_idx.b = -1;
parser.lexer.GetToken(src, src_len);
/// gotta make an expr "list" here.
/// [ .a = expr1 | .b=arg2 ] -> [ .a = expr2 | .b = arg3 ] -> [ .a = expr3 | .b = -1 ]
int first_expr = -1;
int prev_expr_node = -1;
int num_of_exprs = 0;
if( parser.lexer.curr_tok.tag != end_token ) {
while( parser.lexer.curr_tok.tag != end_token ) {
int expr_node = ParseMainExpr(parser, src, src_len);
CSLNode parsed_expr;
parsed_expr.node_type = NODE_EXPR_LIST;
parsed_expr.a = expr_node;
parsed_expr.b = -1;
int expr_node_idx = parser.nodes.InsertNode(parsed_expr);
if( first_expr == -1 ) {
first_expr = expr_node_idx;
}
if( prev_expr_node != -1 ) {
CSLNode prev_parsed_expr;
parser.nodes.ast.GetArray(prev_expr_node, prev_parsed_expr, sizeof(prev_parsed_expr));
prev_parsed_expr.b = expr_node_idx;
parser.nodes.ast.SetArray(prev_expr_node, prev_parsed_expr, sizeof(prev_parsed_expr));
}
if( parser.lexer.curr_tok.tag==TokenComma ) {
parser.lexer.GetToken(src, src_len);
}
prev_expr_node = expr_node_idx;
num_of_exprs++;
}
if( parser.lexer.curr_tok.tag==end_token ) {
parser.lexer.GetToken(src, src_len);
} else {
int filename_len;
parser.nodes.vars.GetValue("filename_size", filename_len);
char[] filename_str = new char[filename_len + 1];
parser.nodes.vars.GetString("filename", filename_str, filename_len);
LogError("ConfigScript :: %s -- Parse Error :: missing ending ']' in array access. (L:%i)", filename_str, parser.lexer.line);
break;
}
if( prev_expr_node != -1 ) {
CSLNode prev_parsed_expr;
parser.nodes.ast.GetArray(prev_expr_node, prev_parsed_expr, sizeof(prev_parsed_expr));
prev_parsed_expr.b = -1;
parser.nodes.ast.SetArray(prev_expr_node, prev_parsed_expr, sizeof(prev_parsed_expr));
}
}
array_idx.b = first_expr;
array_idx.c = num_of_exprs;
n = parser.nodes.InsertNode(array_idx);
}
case TokenLParen: {
int end_token = TokenRParen;
CSLNode func_call;
func_call.node_type = NODE_FUNC_CALL;
func_call.a = n;
func_call.b = -1;
parser.lexer.GetToken(src, src_len);
/// gotta make an expr "list" here.
/// [ .a = expr1 | .b=arg2 ] -> [ .a = expr2 | .b = arg3 ] -> [ .a = expr3 | .b = -1 ]
int first_arg = -1;
int prev_arg_node = -1;
int num_of_args = 0;
if( parser.lexer.curr_tok.tag != end_token ) {
while( parser.lexer.curr_tok.tag != end_token ) {
int expr_node = ParseMainExpr(parser, src, src_len);
CSLNode parsed_arg;
parsed_arg.node_type = NODE_EXPR_LIST;
parsed_arg.a = expr_node;
parsed_arg.b = -1;
int arg_node = parser.nodes.InsertNode(parsed_arg);
if( first_arg == -1 ) {
first_arg = arg_node;
}
if( prev_arg_node != -1 ) {
CSLNode prev_parsed_arg;
parser.nodes.ast.GetArray(prev_arg_node, prev_parsed_arg, sizeof(prev_parsed_arg));
prev_parsed_arg.b = arg_node;
parser.nodes.ast.SetArray(prev_arg_node, prev_parsed_arg, sizeof(prev_parsed_arg));
}
if( parser.lexer.curr_tok.tag==TokenComma ) {
parser.lexer.GetToken(src, src_len);
}
prev_arg_node = arg_node;
num_of_args++;
}
if( parser.lexer.curr_tok.tag==end_token ) {
parser.lexer.GetToken(src, src_len);
} else {
int filename_len;
parser.nodes.vars.GetValue("filename_size", filename_len);
char[] filename_str = new char[filename_len + 1];
parser.nodes.vars.GetString("filename", filename_str, filename_len);
LogError("ConfigScript :: %s -- Parse Error :: missing ending ')' in function call. (L:%i)", filename_str, parser.lexer.line);
break;
}
if( prev_arg_node != -1 ) {
CSLNode prev_parsed_arg;
parser.nodes.ast.GetArray(prev_arg_node, prev_parsed_arg, sizeof(prev_parsed_arg));
prev_parsed_arg.b = -1;
parser.nodes.ast.SetArray(prev_arg_node, prev_parsed_arg, sizeof(prev_parsed_arg));
}
}
func_call.b = first_arg;
func_call.c = num_of_args;
n = parser.nodes.InsertNode(func_call);
}
case TokenDot: {
CSLNode node;
node.node_type = NODE_PROP_ACCESS;
node.a = n;
parser.lexer.GetToken(src, src_len);
node.b = ParseFactor(parser, src, src_len);
n = parser.nodes.InsertNode(node);
}
}
}
return n;
}
/// key_expr = ident ':' expr .
int ParseKeyExpr(CSLParser parser, const char[] src, int src_len) {
CSLNode kv;
kv.node_type = NODE_KEY_VAL;
kv.a = ParseFactor(parser, src, src_len);
if( parser.lexer.curr_tok.tag==TokenColon ) {
parser.lexer.GetToken(src, src_len);
} else {
int filename_len;
parser.nodes.vars.GetValue("filename_size", filename_len);
char[] filename_str = new char[filename_len + 1];
parser.nodes.vars.GetString("filename", filename_str, filename_len);
LogError("ConfigScript :: %s -- Parse Error :: missing ':' in key expression. (L:%i)", filename_str, parser.lexer.line);
return -1;
}
kv.b = ParseMainExpr(parser, src, src_len);
return parser.nodes.InsertNode(kv);
}
/// key_exprs = key_expr *( ',' key_expr ) .
/// vec_expr = '[' [number [ ',' number [ ',' number ] ] ']' .
/// factor = number | ident | string | 'nil' | '(' expr ')' | '{' key_exprs '}' | vec_expr .
/// number = float_lit | int_lit .
int ParseFactor(CSLParser parser, const char[] src, int src_len) {
int n = -1;
CSLNode node;
switch( parser.lexer.curr_tok.tag ) {
case TokenInt: {
node.MakeIntLit(parser.lexer.curr_tok.val);
n = parser.nodes.InsertNode(node);
}
case TokenFloat: {
float f = parser.lexer.curr_tok.val;
node.MakeFloatLit(f);
n = parser.nodes.InsertNode(node);
}
case TokenString: {
node.MakeStrLit(parser.lexer.curr_tok.lexeme);
n = parser.nodes.InsertNode(node);
}
case TokenIdent: {
node.MakeIdent(parser.lexer.curr_tok.lexeme);
n = parser.nodes.InsertNode(node);
}
case TokenNil: {
node.MakeNil();
n = parser.nodes.InsertNode(node);
}
case TokenLParen: {
/// inner expr.
parser.lexer.GetToken(src, src_len);
n = ParseMainExpr(parser, src, src_len);
if( parser.lexer.curr_tok.tag != TokenRParen ) {
int filename_len;
parser.nodes.vars.GetValue("filename_size", filename_len);
char[] filename_str = new char[filename_len + 1];
parser.nodes.vars.GetString("filename", filename_str, filename_len);
LogError("ConfigScript :: %s -- Parse Error :: missing ending ) (L:%i)", filename_str, parser.lexer.line);
}
}
case TokenLBrace: {
parser.lexer.GetToken(src, src_len);
CSLNode kv_expr;
kv_expr.node_type = NODE_KEYVAL_EXPR;
int first_kv = -1;
int prev_kv_node = -1;
int num_keyvals = 0;
if( parser.lexer.curr_tok.tag != TokenRBrace ) {
while( parser.lexer.curr_tok.tag != TokenRBrace ) {
int kv_node = ParseKeyExpr(parser, src, src_len);
CSLNode parsed_kv;
parsed_kv.node_type = NODE_EXPR_LIST;
parsed_kv.a = kv_node;
parsed_kv.b = -1;
int kvexpr_node = parser.nodes.InsertNode(parsed_kv);
if( first_kv == -1 ) {
first_kv = kvexpr_node;
}
if( prev_kv_node != -1 ) {
CSLNode prev_parsed_kv;
parser.nodes.ast.GetArray(prev_kv_node, prev_parsed_kv, sizeof(prev_parsed_kv));
prev_parsed_kv.b = kvexpr_node;
parser.nodes.ast.SetArray(prev_kv_node, prev_parsed_kv, sizeof(prev_parsed_kv));
}
if( parser.lexer.curr_tok.tag==TokenComma ) {
parser.lexer.GetToken(src, src_len);
}
prev_kv_node = kvexpr_node;
num_keyvals++;
}
if( parser.lexer.curr_tok.tag != TokenRBrace ) {
/// error here.
}
if( prev_kv_node != -1 ) {
CSLNode prev_parsed_kv;
parser.nodes.ast.GetArray(prev_kv_node, prev_parsed_kv, sizeof(prev_parsed_kv));
prev_parsed_kv.b = -1;
parser.nodes.ast.SetArray(prev_kv_node, prev_parsed_kv, sizeof(prev_parsed_kv));
}
}
kv_expr.a = first_kv;
kv_expr.b = num_keyvals;
n = parser.nodes.InsertNode(kv_expr);
}
case TokenLSq: {
parser.lexer.GetToken(src, src_len);
CSLNode vec_expr;
vec_expr.node_type = NODE_VEC_EXPR;
vec_expr.a = vec_expr.b = vec_expr.c = -1;
if( parser.lexer.curr_tok.tag != TokenRSq ) {
vec_expr.a = ParseMainExpr(parser, src, src_len);
if( parser.lexer.curr_tok.tag==TokenComma ) {
parser.lexer.GetToken(src, src_len);
}
}
if( parser.lexer.curr_tok.tag != TokenRSq ) {
vec_expr.b = ParseMainExpr(parser, src, src_len);
if( parser.lexer.curr_tok.tag==TokenComma ) {
parser.lexer.GetToken(src, src_len);
}
}
if( parser.lexer.curr_tok.tag != TokenRSq ) {
vec_expr.c = ParseMainExpr(parser, src, src_len);
}
if( parser.lexer.curr_tok.tag != TokenRSq ) {
int filename_len;
parser.nodes.vars.GetValue("filename_size", filename_len);
char[] filename_str = new char[filename_len + 1];
parser.nodes.vars.GetString("filename", filename_str, filename_len);
LogError("ConfigScript :: %s -- Parse Error :: missing ending ] (L:%i)", filename_str, parser.lexer.line);
return -1;
}
n = parser.nodes.InsertNode(vec_expr);
}
}
parser.lexer.GetToken(src, src_len); /// advance lexer.
return n;
}
#include <sdktools>
#include <cfgscript>
#pragma semicolon 1
#pragma newdecls required
#define PLUGIN_VERSION "1.0.0"
public Plugin myinfo = {
name = "Cfg Script Tester",
author = "Nergal",
description = "Plugin that tests CfgScript.",
version = PLUGIN_VERSION,
url = "zzzzzzzzzzzzz"
};
/// math.sin(float_int_or_vec_value) float|vec
public void exported_sin(ArrayList csl_obj_args, CSLObj retval) {
if( csl_obj_args.Length < 1 ) {
return;
}
CSLObj arg1; csl_obj_args.GetArray(0, arg1, sizeof(arg1));
if( arg1.IsArithmeticType() ) {
arg1.ConvertToFloat();
float f = arg1.val;
retval.MakeFloat(Sine(f));
} else if( arg1.tag==OBJ_VEC ) {
float f[3];
for( int i; i < 3; i++ ) {
f[i] = Sine(arg1.vec[i]);
}
retval.MakeVec(f);
}
}
/// math.cos(float_int_or_vec_value) float|vec
public void exported_cos(ArrayList csl_obj_args, CSLObj retval) {
if( csl_obj_args.Length < 1 ) {
return;
}
CSLObj arg1; csl_obj_args.GetArray(0, arg1, sizeof(arg1));
if( arg1.IsArithmeticType() ) {
arg1.ConvertToFloat();
float f = arg1.val;
retval.MakeFloat(Cosine(f));
} else if( arg1.tag==OBJ_VEC ) {
float f[3];
for( int i; i < 3; i++ ) {
f[i] = Cosine(arg1.vec[i]);
}
retval.MakeVec(f);
}
}
/// math.tan(float_int_or_vec_value) float|vec
public void exported_tan(ArrayList csl_obj_args, CSLObj retval) {
if( csl_obj_args.Length < 1 ) {
return;
}
CSLObj arg1; csl_obj_args.GetArray(0, arg1, sizeof(arg1));
if( arg1.IsArithmeticType() ) {
arg1.ConvertToFloat();
float f = arg1.val;
retval.MakeFloat(Tangent(f));
} else if( arg1.tag==OBJ_VEC ) {
float f[3];
for( int i; i < 3; i++ ) {
f[i] = Tangent(arg1.vec[i]);
}
retval.MakeVec(f);
}
}
/// vec.dist(vA, vB) float
public void exported_vec_dist(ArrayList csl_obj_args, CSLObj retval) {
if( csl_obj_args.Length < 2 ) {
return;
}
CSLObj arg1; csl_obj_args.GetArray(0, arg1, sizeof(arg1));
CSLObj arg2; csl_obj_args.GetArray(1, arg2, sizeof(arg2));
if( !arg1.AreVecType(arg2) ) {
return;
}
float f = GetVectorDistance(arg1.vec, arg2.vec);
retval.MakeFloat(f);
}
/// vec.cross(vA, vB) vec
public void exported_vec_cross(ArrayList csl_obj_args, CSLObj retval) {
if( csl_obj_args.Length < 2 ) {
return;
}
CSLObj a; csl_obj_args.GetArray(0, a, sizeof(a));
CSLObj b; csl_obj_args.GetArray(1, b, sizeof(b));
if( !a.AreVecType(b) ) {
return;
}
float res[3]; GetVectorCrossProduct(a.vec, b.vec, res);
retval.MakeVec(res);
}
/// io.server_println(str)
public void exported_server_println(ArrayList csl_obj_args, CSLObj retval) {
if( csl_obj_args.Length < 1 ) {
LogError("io.server_println :: not enough args.");
return;
}
CSLObj arg1; csl_obj_args.GetArray(0, arg1, sizeof(arg1));
if( arg1.tag != OBJ_STR && !arg1.ConvertToStr() ) {
LogError("io.server_println :: failed to convert arg to string.");
return;
}
int len = arg1.GetStrLen();
char[] str = new char[len];
arg1.GetStr(str, len);
PrintToServer("%s", str);
}
public void OnPluginStart() {
CSLParser parser;
char sauce1[] = "( (((760.8+(players-1)) * ((players-1)-1.0))**1.0341) + 2046.0 ) / boss_count";
char sauce2[] = "math.sin(math.PI / 4)**2 + math.cos(math.PI / 4)**2";
char sauce3[] = "vec.cross([5, 5, 3.5], ^[3, 2, 1])";
char sauce4[] = "if 1 > 3 > 2 { io.server_println('1 > 3 > 2') } else if 1 < 3 > 2 { io.server_println('1 < 3 > 2') } else { io.server_println('else') }";
char sauce[] = "var a = 1 io.server_println(a)"
//"a += 2\n"
;
parser.Init(sauce, sizeof(sauce), "tester");
Handle plugin = GetMyHandle();
/// make math lib.
parser.CreateNameSpace("math");
parser.AddFuncToNameSpace("math", "sin", exported_sin, plugin);
parser.AddFuncToNameSpace("math", "cos", exported_cos, plugin);
parser.AddFuncToNameSpace("math", "tan", exported_tan, plugin);
//parser.AddFuncToNameSpace("math", "log", exported_log, plugin);
parser.AddFloatToNameSpace("math", "PI", FLOAT_PI);
/// make vec lib.
parser.CreateNameSpace("vec");
parser.AddFuncToNameSpace("vec", "cross", exported_vec_cross, plugin);
parser.AddFuncToNameSpace("vec", "dist", exported_vec_dist, plugin);
/// make io lib.
parser.CreateNameSpace("io");
parser.AddFuncToNameSpace("io", "server_println", exported_server_println, plugin);
int parsed_node = ParseScript(parser, sauce, sizeof(sauce));
PrintAST(parser.nodes, parsed_node);
///*
PrintToServer(ExecScript(parser.nodes, parsed_node)? "'%s' :: exec succeeded!" : "'%s' :: failed exec!", sauce);
//*/
parser.nodes.PrintAllVars();
/*
CSLObj res;
if( EvaluateExprs(parser.nodes, parsed_node, res) ) {
switch( res.tag ) {
case OBJ_STR: {
char[] str = new char[res.size];
res.GetStr(str);
PrintToServer("result of '%s' :: %s", sauce, str);
res.Destroy();
}
case OBJ_INT: {
PrintToServer("result of '%s' :: %i", sauce, res.val);
}
case OBJ_FLT: {
float f = res.val;
PrintToServer("result of '%s' :: %f", sauce, f);
}
case OBJ_VEC: {
PrintToServer("result of '%s' :: [%f, %f, %f]", sauce, res.vec[0], res.vec[1], res.vec[2]);
}
case OBJ_NIL: {
PrintToServer("result of '%s' :: nil", sauce);
}
case OBJ_TAB: {
PrintToServer("result of '%s' :: <table>", sauce);
}
case OBJ_FNC: {
PrintToServer("result of '%s' :: <func>", sauce);
}
}
} else {
PrintToServer("'%s' :: failed to evaluate!", sauce);
}
//*/
parser.Destroy();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment