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 bool RemoveClass(HotloaderClass cls) { bool result = p_ChildClasses.Remove(cls); if (result) { cls.p_Parent = null; } return(result); }
public void AttachManagedClass(Type type) { HotloaderClass cls = new HotloaderClass(type.Name, this); if (!Globals.AddClass(cls)) { cls = Globals.ResolveClass(type.Name); } AttachManagedClass(type, cls); }
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 HotloaderVariable ResolveVariable(string fullName) { string[] split = fullName.Split('.'); //get the class HotloaderClass cls = resolveClass(split, 0, split.Length - 1); if (cls == null) { return(null); } //get the variable return(cls.GetVariable(split[split.Length - 1])); }
public void AttachManagedClass(Type type, HotloaderClass cls) { //attach fields FieldInfo[] fields = type.GetFields(); int fieldCount = fields.Length; for (int c = 0; c < fieldCount; c++) { FieldInfo field = fields[c]; //must be public if (!field.IsPublic) { continue; } BindField(field, cls); } //attach methods MethodInfo[] methods = type.GetMethods(); int methodCount = methods.Length; for (int c = 0; c < methodCount; c++) { MethodInfo method = methods[c]; string name = method.Name; //reserved name? if ( name == "GetHashCode" || name == "ToString" || name == "Equals" || name == "GetType") { continue; } //bind BindMethod(method, cls); } }
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 bool AddClass(HotloaderClass cls) { lock (p_Mutex) { //does the name already exist? if (NameExists(cls.p_Name)) { return(false); } //remove from existing parent if (cls.p_Parent != null) { cls.p_Parent.RemoveClass(cls); } cls.p_Parent = this; p_ChildClasses.Add(cls); return(true); } }
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 Hotloader(bool addDefaultClasses) { p_GlobalClass = new HotloaderClass("GLOBALS", this); /**/ p_Files = new List <HotloaderFile>(); p_Heartbeat = new Heartbeat("hotloader"); p_Heartbeat.Speed(10); p_Heartbeat.Start(this, tick); if (addDefaultClasses) { initDefaults(); } try { AddFile("test.txt"); } catch (HotloaderParserException ex) { ex.Print(Console.Out); } } public HotloaderFile AddFile(string filename) { filename = new FileInfo(filename).FullName; lock (p_Mutex) { //does the file already exist? HotloaderFile buffer = GetFile(filename); if (buffer != null) { return(buffer); } buffer = new HotloaderFile(filename); p_Files.Add(buffer); //force update p_ForceUpdate = true; //clean up return(buffer); } } public bool RemoveFile(string filename) { //get the instance to remove HotloaderFile file = GetFile(filename); if (file == null) { return(false); } return(RemoveFile(file)); } public bool RemoveFile(HotloaderFile file) { lock (p_Mutex) { //remove all variables we loaded from //this file. List <HotloaderVariable> vars = file.Variables; foreach (HotloaderVariable v in vars) { v.Remove(); } //remove all included files List <HotloaderFile> includes = file.Includes; foreach (HotloaderFile i in includes) { RemoveFile(i); } //remove bool result = p_Files.Remove(file); if (!result) { return(false); } //force update on all files since //we might of removed variables //that exist elsewhere. p_ForceUpdate = true; return(true); } } public bool HasFile(string filename) { return(GetFile(filename) != null); } public HotloaderFile GetFile(string filename) { lock (p_Mutex) { filename = new FileInfo(filename).FullName; filename = filename.ToLower(); foreach (HotloaderFile f in p_Files) { if (f.Filename.ToLower() == filename) { return(f); } } return(null); } } public object EvaluateExpression(string expression) { //escape control characters expression = expression.Replace(";", "\";\""); //create a variable name that will never occur in normal code. string variableName = generateString(new Random(), 20); expression = variableName + "=" + expression + ";"; //convert the expression to just a variable being assigned and grab //that from globals. byte[] data = Encoding.ASCII.GetBytes(expression); fixed(byte *dataPtr = data) { byte *ptr = dataPtr; //load the expression in load(null, ptr, data.Length); } //get the variable HotloaderVariable variable = p_GlobalClass.GetVariable(variableName); p_GlobalClass.RemoveVariable(variable); //evaluate return(variable.Value.Evaluate()); } public HotloaderClass Globals { get { return(p_GlobalClass); } } public void ForceDispose() { p_Heartbeat.ForceStop(); } public void Dispose() { p_Heartbeat.Stop(); } private void tick(object state) { Hotloader hot = (Hotloader)state; /*do we check the hash? we do this every second * instead of every tick.*/ long hashTime = hot.p_LastHashCheck + TimeSpan.TicksPerSecond; long now = DateTime.Now.Ticks; bool checkHash = now >= hashTime; if (checkHash) { hot.p_LastHashCheck = now; } lock (p_Mutex) { List <HotloaderFile> files = hot.p_Files; int fileLength = files.Count; //keep iterating until the files were not modified! bool modified; do { modified = false; for (int c = 0; c < fileLength; c++) { HotloaderFile file = files[c]; //does the file exist? if (!File.Exists(file.Filename)) { //remove it RemoveFile(file); modified = true; fileLength = files.Count; break; } //has the file changed? FileStream fileStream; bool changed = file.HasChanged(out fileStream); //file modified/force update? if (p_ForceUpdate || changed) { try { hot.fileChanged(file, fileStream); } catch (HotloaderParserException ex) { ex.Print(Console.Out); } //a file been removed/added? if (files.Count != fileLength) { modified = true; fileLength = files.Count; fileStream.Close(); break; } } //clean up file.Close(); } }while(modified); p_ForceUpdate = false; } }
internal void changeParent(HotloaderClass newParent) { p_Parent = newParent; }