public void AddOperator(HotloaderValueOperator op) { //do not allow if constant if ((p_Variable.Accessors & HotloaderAccessor.CONST) == HotloaderAccessor.CONST) { return; } lock (p_Mutex) { Array.Resize(ref p_Operators, p_Operators.Length + 1); p_Operators[p_Operators.Length - 1] = op; } }
private object safeEvaluate(out HotloaderValueType type) { type = HotloaderValueType.NONE; //let the evaluation callback deal with it? if (p_EvaluationCallback != null) { object ret = p_EvaluationCallback(); type = getType(ret); return(ret); } //check for valid evaluation if (!Valid) { throw new Exception("Invalid evaluation"); } #region evaluate each operand int operandLength = p_Operands.Length; object[] operands = new object[operandLength]; HotloaderValueType[] types = new HotloaderValueType[operandLength]; for (int c = 0; c < operandLength; c++) { HotloaderValueOperand opand = p_Operands[c]; HotloaderValueType t; operands[c] = opand.GetValue(out t); types[c] = t; //should this type be the return type? if ((int)t > (int)type) { type = t; } //boolean? if so, we do not //allow operators on bools if (t == HotloaderValueType.BOOLEAN && operandLength != 1) { throw new HotloaderParserException( opand.File, opand.Line, opand.Column, "Booleans cannot be operands"); } } #endregion //empty? if (operandLength == 0) { return(null); } //boolean? if (type == HotloaderValueType.BOOLEAN) { //not operator? bool not = false; if (p_Operators.Length != 0) { if (p_Operators[0] != HotloaderValueOperator.NOT) { throw new HotloaderParserException( p_Operands[0].File, p_Operands[0].Line, p_Operands[0].Column, "Invalid boolean expression"); } not = true; } //return the boolean bool value = (bool)operands[0]; return (not ? !value : value); } //just 1 operand? if (operandLength == 1) { type = types[0]; return(operands[0]); } object buffer = toType(operands[0], types[0], type); //perform each operator int opLength = p_Operators.Length; for (int c = 0; c < opLength; c++) { HotloaderValueOperator op = p_Operators[c]; object operand = operands[c + 1]; HotloaderValueType operandType = types[c + 1]; operand = toType( operand, operandType, type); #region just a string concat? if (type == HotloaderValueType.STRING) { //must be add if (op != HotloaderValueOperator.ADD) { throw new HotloaderParserException( p_Operands[c + 1].File, p_Operands[c + 1].Line, p_Operands[c + 1].Column, "Cannot perform operation on a string"); } //add buffer = (string)buffer + (string)operand; continue; } #endregion #region numeric operand #region bitwise //bitwise? bool isBitwise = op == HotloaderValueOperator.AND || op == HotloaderValueOperator.OR || op == HotloaderValueOperator.XOR || op == HotloaderValueOperator.SHIFTL || op == HotloaderValueOperator.SHIFTR; //if it's bitwise, convert both sides to an int then convert back once //the bitwise has been done if (isBitwise) { long a = (long)toType(buffer, type, HotloaderValueType.NUMERICAL); long b = (long)toType(operand, type, HotloaderValueType.NUMERICAL); long result = 0; switch (op) { case HotloaderValueOperator.AND: result = a & b; break; case HotloaderValueOperator.OR: result = a | b; break; case HotloaderValueOperator.XOR: result = a ^ b; break; case HotloaderValueOperator.SHIFTL: result = a << (sbyte)b; break; case HotloaderValueOperator.SHIFTR: result = a >> (sbyte)b; break; } buffer = toType(result, HotloaderValueType.NUMERICAL, type); continue; } #endregion //just convert the operands to a double then do the maths on that. double d1 = (double)toType(buffer, type, HotloaderValueType.DECIMAL); double d2 = (double)toType(operand, type, HotloaderValueType.DECIMAL); double res = 0; switch (op) { case HotloaderValueOperator.ADD: res = d1 + d2; break; case HotloaderValueOperator.SUBTRACT: res = d1 - d2; break; case HotloaderValueOperator.MULTIPLY: res = d1 * d2; break; case HotloaderValueOperator.DIVIDE: res = d1 / d2; break; case HotloaderValueOperator.MODULUS: res = d1 % d2; break; case HotloaderValueOperator.POWER: res = Math.Pow(d1, d2); break; } buffer = toType(res, HotloaderValueType.DECIMAL, type); #endregion } return(buffer); }
private void load(HotloaderFile file, byte *data, int length) { byte *ptr = data; byte *ptrEnd = data + length; byte *blockStart = ptr; byte *blockEnd = ptr; bool negativeFlag = false; //keep track of what classes/variables are being added. List <HotloaderVariable> fileVariables = new List <HotloaderVariable>(); List <HotloaderFile> fileIncludes = new List <HotloaderFile>(); // HotloaderClass currentClass = p_GlobalClass; HotloaderVariable currentVariable = null; HotloaderExpression currentExpression = null; //define what mode we are in (what type of block //we are reading). parserMode mode = parserMode.NONE; // HotloaderAccessor currentAccessor = HotloaderAccessor.NONE; //where we are in a more usable way. int currentLine = 1; int currentColumn = 0; while (ptr != ptrEnd) { byte current = *(ptr++); currentColumn++; //are we at the end of the file? bool atEnd = ptr == ptrEnd; #region control characters //newline? bool newLine = current == '\n' || current == '\r'; if (newLine) { if (current == '\n') { currentColumn = 0; currentLine++; } } //whitespace bool whitespace = newLine || current == ' ' || current == '\t'; //alpha numeric? //(only make the call if //we know the character is not a whitespace. bool nameChar = !whitespace && isNameCharacter(current); #endregion #region byte block evaluation bool evaluateBlock = atEnd || whitespace || !nameChar; if (evaluateBlock) { //if we are at the end, make sure //we include the last character //in the block. if (atEnd && !whitespace && nameChar) { blockEnd++; } //is the block blank? bool isBlank = blockStart == blockEnd; int blockLength = (int)(blockEnd - blockStart); //read the block as a string if (!isBlank) { handleBlock( fileVariables, file, blockStart, blockEnd, currentLine, currentColumn - blockLength + 1, ref negativeFlag, ref mode, ref currentAccessor, ref currentVariable, ref currentExpression, ref currentClass); //block been changed outside pointer? if (blockStart > ptr) { ptr = blockStart; } } //reset blockStart = blockEnd = ptr; //do not process anything else if it is a whitespace if (whitespace) { continue; } } #endregion #region comment if (current == '#') { //skip over line while (ptr != ptrEnd && *(ptr++) != '\n') { } //we hit newline? if (*(ptr - 1) == '\n') { ptr--; } blockStart = blockEnd = ptr; continue; } #endregion #region string literal if (current == '"') { //valid? if (mode != parserMode.ASSIGNMENT && mode != parserMode.INCLUDE) { throw new HotloaderParserException( file, currentLine, currentColumn, "Unexpected string literal"); } byte *literalStart = ptr; if (!readStringLiteral(ref ptr, ptrEnd)) { throw new HotloaderParserException( file, currentLine, currentColumn, "String literal did not terminate"); } //deturmine where the string ends byte *literalEnd = ptr - 1; //read the string and remove //string terminating characters string read = readString(literalStart, literalEnd); read = read.Replace("\\", ""); //include? if (mode == parserMode.INCLUDE) { mode = parserMode.NONE; if (!File.Exists(read)) { throw new HotloaderParserException( file, currentLine, currentColumn, String.Format( "Cannot include file \"{0}\". Does not exist.", read)); } //does the file exist? HotloaderFile include = GetFile(read); if (include == null) { include = AddFile(read); fileIncludes.Add(include); } else if (file.Includes.Contains(include)) { fileIncludes.Add(include); } } else { //add operand currentExpression.AddOperand( read, HotloaderValueType.STRING, currentLine, currentColumn, file); } //update line position currentColumn += (int)(literalEnd - literalStart) + 1; blockStart = blockEnd = ptr; continue; } #endregion #region expression scopes if (current == '(' || current == ')') { //valid? if (mode != parserMode.ASSIGNMENT) { throw new HotloaderParserException( file, currentLine, currentColumn, "Unexpected expression character"); } //close? if (current == ')') { //can we close? if (currentExpression.Parent == null) { throw new HotloaderParserException( file, currentLine, currentColumn, "Unexpected end of expression scope"); } //add the expression as an operand HotloaderExpression parent = currentExpression.Parent; HotloaderExpression expression = currentExpression; HotloaderValueOperand op = parent.AddOperand( expression, HotloaderValueType.EVALUATION, currentLine, currentColumn, file); //close currentExpression = currentExpression.Parent; } //open? if (current == '(') { HotloaderExpression newExpression = new HotloaderExpression(this, currentVariable); newExpression.Parent = currentExpression; currentExpression = newExpression; } // blockStart = blockEnd = ptr; continue; } #endregion #region operators #region class if (current == ':') { //valid? if (mode != parserMode.NONE) { throw new HotloaderParserException( file, currentLine, currentColumn, "Unexpected class symbol"); } mode = parserMode.CLASS; blockStart = blockEnd = ptr; continue; } #endregion #region assignment if (current == '=') { //valid? if (mode != parserMode.VARIABLE) { throw new HotloaderParserException( file, currentLine, currentColumn, "Unexpected assignment operator"); } mode = parserMode.ASSIGNMENT; blockStart = blockEnd = ptr; continue; } #endregion #region end assignment if (current == ';') { //valid? if (mode != parserMode.ASSIGNMENT || !currentExpression.Valid || currentExpression.Parent != null || currentExpression.Empty) { throw new HotloaderParserException( file, currentLine, currentColumn, "Unexpected end-of-expression character"); } mode = parserMode.NONE; blockStart = blockEnd = ptr; //poll the expression to mark the end of //an assignment so it can do necassary //functions (e.g invoke assignment callback) currentExpression.Poll(); currentExpression = null; currentVariable = null; continue; } #endregion #region value operators HotloaderValueOperator valueOp = HotloaderValueOperator.NONE; switch ((char)current) { case '+': valueOp = HotloaderValueOperator.ADD; break; case '-': valueOp = HotloaderValueOperator.SUBTRACT; break; case '*': valueOp = HotloaderValueOperator.MULTIPLY; break; case '/': valueOp = HotloaderValueOperator.DIVIDE; break; case '^': valueOp = HotloaderValueOperator.POWER; break; case '%': valueOp = HotloaderValueOperator.MODULUS; break; case '&': valueOp = HotloaderValueOperator.AND; break; case '|': valueOp = HotloaderValueOperator.OR; break; case '?': valueOp = HotloaderValueOperator.XOR; break; case '<': valueOp = HotloaderValueOperator.SHIFTL; break; case '>': valueOp = HotloaderValueOperator.SHIFTR; break; case '!': valueOp = HotloaderValueOperator.NOT; break; } //are we expecting a math operation? if (valueOp != HotloaderValueOperator.NONE && mode != parserMode.ASSIGNMENT) { throw new HotloaderParserException( file, currentLine, currentColumn, String.Format( "Unexpected operator {0}", (char)current)); } //wait, was this negative? bool addOp = true; if (valueOp == HotloaderValueOperator.SUBTRACT) { //make sure there would be an operand before //the subtract. Otherwise we assume it's a //negative integer/decimal. negativeFlag = currentExpression.Operands == currentExpression.Operators; //addOp = false; } if (valueOp != HotloaderValueOperator.NONE) { //if we have discovered a negate operator //do not add this as a maths operator! if (addOp) { currentExpression.AddOperator(valueOp); } blockStart = blockEnd = ptr; continue; } #endregion #endregion //invalid character? if (!nameChar) { throw new HotloaderParserException( file, currentLine, currentColumn, String.Format( "Invalid character {0}", (char)current)); } //incriment block end to include this //byte so later we can evaluate blocks //of the file. blockEnd++; } //not ended correctly? if (currentClass.Parent != null) { throw new HotloaderParserException( file, -1, -1, "Class not terminated"); } //find all includes that have been removed from the file and remove them if (file == null) { return; } List <HotloaderFile> oldIncludes = file.Includes; foreach (HotloaderFile f in oldIncludes) { if (!fileIncludes.Contains(f)) { RemoveFile(f); } } //find all variables that have been removed from the file and remove them List <HotloaderVariable> oldFiles = file.Variables; foreach (HotloaderVariable v in oldFiles) { if (!fileVariables.Contains(v)) { v.Remove(); } } file.setVariables(fileVariables); file.setIncludes(fileIncludes); }