public bool AddVariable(HotloaderVariable variable) { lock (p_Mutex) { int hash = variable.GetHashCode(); //the name cannot contains "." if (variable.Name.Contains(".")) { return(false); } //does the name already exist? if (NameExists(variable.Name)) { return(false); } //remove from variables parent if (variable.Parent != null) { variable.Parent.RemoveVariable(variable); } //add variable.changeParent(this); p_ChildVariables.Add(variable); return(true); } }
public HotloaderExpression(Hotloader hotloader, HotloaderVariable variable) { p_Hotloader = hotloader; p_Globals = hotloader.Globals; p_Variable = variable; p_Operators = new HotloaderValueOperator[0]; p_Operands = new HotloaderValueOperand[0]; }
public object this[string fullName] { get { HotloaderVariable var = p_GlobalClass.ResolveVariable(fullName); if (var == null) { return(null); } return(var.Value.Evaluate()); } }
public void BindMethod(MethodInfo method, HotloaderVariable var) { //convert the method to a callback //and add it with a variable so it //will be called every time it is //accessed. HotloaderEvaluationCallback callback = (HotloaderEvaluationCallback) Delegate.CreateDelegate( typeof(HotloaderEvaluationCallback), method); var.Value.SetEvaluationCallback(callback); }
public HotloaderValueOperand(HotloaderFile file, HotloaderClass globals, int line, int column, HotloaderVariable variable, object raw, HotloaderValueType type) { p_Raw = raw; p_Type = type; p_Globals = globals; p_Line = line; p_Column = column; p_Variable = variable; p_File = file; }
public object GetValue(out HotloaderValueType type) { type = p_Type; //evaluation? HotloaderExpression eval = p_Raw as HotloaderExpression; if (eval != null) { return(eval.Evaluate(out type)); } /*variable?*/ if (p_Type == HotloaderValueType.VARIABLE) { //resolve the variable HotloaderVariable variable = p_Variable.Parent.ResolveVariable((string)p_Raw); if (variable == null) { variable = p_Globals.ResolveVariable((string)p_Raw); } if (variable == null) { throw new HotloaderParserException( p_File, p_Line, p_Column, String.Format( "Variable \"{0}\" does not exist", p_Raw)); } //verify no cross-referencing if (variable == p_Variable) { throw new HotloaderParserException( p_File, p_Line, p_Column, "Cycle-dependancy found! Cannot self-reference"); } //evaluate object value = variable.Value.Evaluate(out type); return(value); } //this should never happen. return(p_Raw); }
public void BindField(FieldInfo field, HotloaderClass cls) { string name = field.Name; HotloaderVariable v = cls.GetVariable(name); if (v == null) { v = new HotloaderVariable(name, this); if (!cls.AddVariable(v)) { throw new Exception( String.Format( "Cannot bind field. Name \"{0}\" already in use.", name)); } } BindField(field, v); }
public void BindField(FieldInfo field, HotloaderVariable var) { //const? bool isConst = field.IsLiteral && !field.IsInitOnly; //bind HotloaderExpression expression = var.Value; if (!isConst) { expression.SetAssignmentCallback(delegate(object value) { field.SetValue(null, value); }); } else { expression.SetValue(field.GetValue(null)); } //set accessors var.Accessors = (isConst ? HotloaderAccessor.CONST | HotloaderAccessor.STATIC : HotloaderAccessor.NONE); }
private void handleBlock( List <HotloaderVariable> variables, HotloaderFile file, byte *blockPtr, byte *blockEnd, int currentLine, int currentColumn, ref bool negativeFlag, ref parserMode mode, ref HotloaderAccessor currentAccessor, ref HotloaderVariable currentVariable, ref HotloaderExpression currentExpression, ref HotloaderClass currentClass) { //read the block as a string string block = readString(blockPtr, blockEnd); //hash the string so we can do quick compares with keywords int hash = block.GetHashCode(); #region include if (hash == STRING_INCLUDE_HASH) { //valid? if (mode != parserMode.NONE) { throw new HotloaderParserException( file, currentLine, currentColumn, "Unexpected include"); } mode = parserMode.INCLUDE; return; } #endregion #region reserve word check /*since we detect errors if some keywords * are used anyway, some are discarded here.*/ if (mode != parserMode.ASSIGNMENT) { if (hash == STRING_TRUE_HASH || hash == STRING_FALSE_HASH) { throw new HotloaderParserException( file, currentLine, currentColumn, String.Format( "Unexpected use of symbol \"{0}\"", block)); } } #endregion #region accessors bool isConst = hash == STRING_CONST_HASH; bool isStatic = hash == STRING_STATIC_HASH; //valid mode? if (mode != parserMode.NONE) { if (isConst || isStatic) { throw new HotloaderParserException( file, currentLine, currentColumn, "Unexpected accessor"); } } if (isConst) { currentAccessor |= HotloaderAccessor.CONST; } if (isStatic) { currentAccessor |= HotloaderAccessor.STATIC; } if (isConst || isStatic) { return; } #endregion #region end of a class? if (hash == STRING_END_HASH) { //verify that we can end a class if (currentClass.Parent == null || mode != parserMode.NONE) { throw new HotloaderParserException( file, currentLine, currentColumn, "Unexpected end of class token."); } //end the class by just setting //the current class to the parent currentClass = currentClass.Parent; return; } #endregion #region class declaration //are we expecting a class name? if (mode == parserMode.CLASS) { //a class cannot have accessors! if (currentAccessor != HotloaderAccessor.NONE) { throw new HotloaderParserException( file, currentLine, currentColumn, "Classes cannot have accessors"); } //get the class HotloaderClass cls = currentClass.GetClass(block); if (cls == null) { cls = new HotloaderClass(block, this); //only reason this will return false is //if it already exists! if (!currentClass.AddClass(cls)) { throw new HotloaderParserException( file, currentLine, currentColumn, String.Format( "Cannot declare class \"{0}\". Name already used elsewhere.", block)); } } //set the current class to the newly created one //so every variable/class added will be added //to this one. currentClass = cls; //we are no longer reading a class mode = parserMode.NONE; return; } #endregion #region variable declaration //we must be in assignment mode by now.. if (mode == parserMode.VARIABLE) { throw new HotloaderParserException( file, currentLine, currentColumn, "Unexpected variable declaration"); } //we assume this is an alpha-numerical string if (mode == parserMode.NONE) { //does this variable already exist? //if not, add it. currentVariable = currentClass.GetVariable(block); if (currentVariable == null) { //if the add function returns false, then //the variable name is already taken! currentVariable = new HotloaderVariable(block, this); if (!currentClass.AddVariable(currentVariable)) { throw new HotloaderParserException( file, currentLine, currentColumn, String.Format( "Cannot declare variable \"{0}\". Name already used elsewhere.", block)); } } //add to the list of added variables from the file variables.Add(currentVariable); //if we can't change this variable (it's marked as static //just set the current variable to a dummy one. if ((currentVariable.Accessors & HotloaderAccessor.STATIC) == HotloaderAccessor.STATIC) { currentVariable = new HotloaderVariable("dummy", this); currentVariable.changeParent(currentClass); } //set expression currentExpression = currentVariable.Value; currentExpression.Clear(); //set accessors currentVariable.Accessors = currentAccessor; currentAccessor = HotloaderAccessor.NONE; //wait for assignment etc.. mode = parserMode.VARIABLE; } #endregion #region variable assignment if (mode == parserMode.ASSIGNMENT) { #region boolean string blockLower = block.ToLower(); if (blockLower == "true" || blockLower == "false") { currentExpression.AddOperand( (block == "true"), HotloaderValueType.BOOLEAN, currentLine, currentColumn, file); return; } #endregion #region numerical? double decimalValue; bool isDecimal = Double.TryParse(block, out decimalValue); if (isDecimal) { //is it an actual decimal or integer? bool explicitDecimal = block.Contains("."); //negate? if (negativeFlag) { decimalValue = -decimalValue; negativeFlag = false; } //deturmine value type HotloaderValueType type = explicitDecimal ? HotloaderValueType.DECIMAL : HotloaderValueType.NUMERICAL; //deturmine raw object for the string object raw = decimalValue; if (!explicitDecimal) { raw = (long)decimalValue; } currentExpression.AddOperand( raw, type, currentLine, currentColumn, file); return; } #endregion #region base conversion if (block.Length > 2 && *blockPtr == '0') { //detect base conversion int sourceBase = -1; byte sourceBaseByte = *(blockPtr + 1); if (sourceBaseByte == 'x') { sourceBase = 16; } else if (sourceBaseByte == 'b') { sourceBase = 2; } //is it actually a conversion? if (sourceBase != -1) { string convert = block.Substring(2); //attempt to convert try { long converted = Convert.ToInt64(convert, sourceBase); //add the operand currentExpression.AddOperand( converted, HotloaderValueType.NUMERICAL, currentLine, currentColumn, file); } catch { throw new HotloaderParserException( file, currentLine, currentColumn, String.Format( "Unable to convert {0} from base {1}", convert, sourceBase)); } return; } } #endregion #region variable? //verify the name while (blockPtr != blockEnd) { byte current = *(blockPtr++); if (!isNameCharacter(current)) { throw new HotloaderParserException( file, currentLine, currentColumn, String.Format( "Invalid variable name \"{0}\"", block)); } } //default to a variable reference currentExpression.AddOperand( block, HotloaderValueType.VARIABLE, currentLine, currentColumn, file); #endregion } #endregion }
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); }
public bool RemoveVariable(HotloaderVariable variable) { lock (p_Mutex) { return(p_ChildVariables.Remove(variable)); } }