protected override void ExtractInfo() { // Extract keyword Keyword = Element.Name.LocalName; // Check syntax IEnumerable <XNode> textNodes = Element.Nodes() .Where(n => n.NodeType == XmlNodeType.Text); if (textNodes.Any()) { string text = Element.Value.Trim().Ellipses(25); string fmt = Resources.SyntaxExceptionXmlUnexpectedText; XNode textNode = textNodes.ElementAt(0); ISourceEntity src = new XmlSourceEntity(textNode); throw LayoutScriptException.Create <SyntaxException>(null, src, fmt, text, Keyword); } // Extract parameters foreach (XAttribute attr in Element.Attributes()) { SetParameter(attr.Name.LocalName, attr.Value); } // Parse nested statements foreach (XElement elem in Element.Elements()) { AddNestedStatement(XmlStatement.Parse(elem)); } }
private void InterpretStatement(Statement stmt) { StatementType type = stmt.StatementType; InterpretAction interpret; string msg; switch (type) { case StatementType.Directive: // Get action based on directive name. if (!directiveActionMap.TryGetValue(stmt.Keyword, out interpret)) { msg = "Unknown directive '{0}'."; throw LayoutScriptException.Create <LayoutScriptException>(layout, stmt, msg, stmt.Keyword); } break; case StatementType.None: // Invalid state. Should never happen if the statement was parsed correctly... msg = "Invalid statement."; throw LayoutScriptException.Create <LayoutScriptException>(layout, stmt, msg); default: // Get action based on statement type if (!statementTypeActionMap.TryGetValue(type, out interpret)) { // Should never happen... msg = string.Format("Bug! No action exists for statement type '{0}'!", type); throw new InvalidOperationException(msg); } break; } interpret(stmt); }
private bool GetParameter(Statement stmt, string paramName, bool isRequired, out string value) { bool hasParam = stmt.Parameters.TryGetValue(paramName, out value); if (!hasParam && isRequired) { string msg = "Missing required parameter '{0}'"; throw LayoutScriptException.Create <SyntaxException>(layout, stmt, msg, paramName); } return(hasParam); }
private void EnsureParameters(Statement stmt, params string[] paramNames) { List <string> unknownParams = stmt.Parameters .Select(x => x.Key) .Where(x => !paramNames.Contains(x)) .ToList(); if (unknownParams.Any()) { string msg = "Unknown parameter '{0}'."; throw LayoutScriptException.Create <SyntaxException>(layout, stmt, msg, unknownParams[0]); } }
private void InterpretRootStatement(Statement stmt) { // Validate root element name if (stmt.Keyword != Keywords.XmlDocumentRoot) { string msg = Resources.SyntaxExceptionXmlInvalidRootElement; throw LayoutScriptException.Create <SyntaxException>(layout, stmt, msg, Keywords.XmlDocumentRoot); } // Interpret nested statements foreach (Statement childStmt in stmt.NestedStatements) { InterpretStatement(childStmt); } }
private void InterpretTypedef(Statement stmt) { EnsureParameters(stmt, Parameters.Comment, Parameters.Kind, Parameters.Name); GetRequiredParameter(stmt, Parameters.Kind, out string baseTypeName); GetRequiredParameter(stmt, Parameters.Name, out string newTypeName); if (!SymbolTable.IsIdentifierValid(newTypeName)) { string msg = "Invalid identifier '{0}'. " + "Identifiers must consist of only letters, numbers, and underscores. " + "Identifiers cannot begin with a digit nor can they be identical to a reserved word."; throw LayoutScriptException.Create <LayoutScriptException>(layout, stmt, msg, newTypeName); } if (TryLookupType(newTypeName, out TypeInfo dummyType)) { string msg = "Type '{0}' already exists."; throw LayoutScriptException.Create <LayoutScriptException>(layout, stmt, msg, newTypeName); } bool isStruct = baseTypeName == Keywords.DataTypes.Struct; bool isUnion = baseTypeName == Keywords.DataTypes.Union; TypeInfo baseType; if (isStruct || isUnion) { baseType = CreateStructureType(stmt, isUnion); } else { bool exists = TryLookupType(baseTypeName, out baseType); if (!exists) { string msg = "Unknown type '{0}'."; throw LayoutScriptException.Create <LayoutScriptException>(layout, stmt, msg, baseTypeName); } } userDefinedTypes[newTypeName] = baseType; }
private void InterpretLocal(Statement stmt) { EnsureParameters(stmt, Parameters.Comment, Parameters.Name, Parameters.Value); GetRequiredParameter(stmt, Parameters.Name, out string varName); GetRequiredParameter(stmt, Parameters.Value, out string valueStr); if (CurrentCodeBlock.Symbol.Lookup(varName) != null) { string msg = "A variable with identifier '{0}' already exists in the current scope."; throw LayoutScriptException.Create <LayoutScriptException>(layout, stmt, msg, varName); } try { double value = EvaluateExpression(valueStr); CurrentCodeBlock.Locals[varName] = value; } catch (SyntaxErrorException e) { string msg = "'{0}' is not a valid expression."; throw LayoutScriptException.Create <LayoutScriptException>(layout, e, stmt, msg, valueStr); } }
private void InterpretInclude(Statement stmt) { EnsureParameters(stmt, Parameters.Path); GetRequiredParameter(stmt, Parameters.Path, out string path); // Path is relative to the current layout script's path. // If the current script was not loaded from a file, the path is // relative to the current program's directory. string fullPath; if (layout.SourcePath == null) { fullPath = Directory.GetCurrentDirectory() + "/" + path; } else { fullPath = Path.GetDirectoryName(layout.SourcePath) + "/" + path; } fullPath = Path.GetFullPath(fullPath); if (!File.Exists(fullPath)) { string fmt = "File not found - {0}"; throw LayoutScriptException.Create <LayoutScriptException>(layout, stmt, fmt, path); } LayoutScript incl = LayoutScript.Load(fullPath); if (includedLayouts.Contains(incl)) { string msg = "Include cycle detected!"; throw LayoutScriptException.Create <LayoutScriptException>(layout, stmt, msg); } includedLayouts.Add(incl); InterpretRootStatement(incl.RootStatement); }
private string EvaluateOperator(Operator op, string varName) { bool isSpecialVar = SpecialVariables.AllSpecialVariables.Contains(varName); bool isLocalVar = TryLookupLocal(varName, out double localVarVal); // Handle special variables if (isSpecialVar) { if (op != Operator.ValueOf) { Log(LogLevel.Warning, "Operator {0} cannot be used on special variable '{1}'.", op, varName); return(""); } switch (varName) { case SpecialVariables.Filesize: return(file.Length + ""); case SpecialVariables.GlobalOffset: return(GlobalOffset + ""); case SpecialVariables.Offset: return(CurrentCodeBlock.Offset + ""); } } // Handle local variables if (isLocalVar) { if (op != Operator.ValueOf) { Log(LogLevel.Warning, "Operator {0} cannot be used on local variable '{1}'.", op, varName); return(""); } return(localVarVal + ""); } bool isVar = CurrentCodeBlock.Symbol.TryLookup(varName, out SymbolTable sym); bool isUserDefinedType = userDefinedTypes.TryGetValue(varName, out TypeInfo usrType); bool isBuiltinType = BuiltInPrimitives.TryGetValue(varName, out TypeInfo builtinType); if (!(isVar || isUserDefinedType || isBuiltinType)) { // TODO: get statement. Perhaps throw a different exception and have Caller of ResolveVariables catch and handle it string msg = "Unknown variable or typename '{0}'"; throw LayoutScriptException.Create <LayoutScriptException>(layout, null, msg, varName); } string val = ""; switch (op) { case Operator.GlobalOffsetOf: if (CurrentCodeBlock.IsProcessingTypedef) { string msg = "GlobalOffsetOf operator not valid within type definitions."; throw LayoutScriptException.Create <LayoutScriptException>(layout, null, msg, varName); } if (isVar) { val = sym.GlobalDataAddress.ToString(); } else { string msg = "GlobalOffsetOf operator not valid for types."; throw LayoutScriptException.Create <LayoutScriptException>(layout, null, msg, varName); } break; case Operator.OffsetOf: if (CurrentCodeBlock.IsProcessingTypedef) { string msg = "OffsetOf operator not valid within type definitions."; throw LayoutScriptException.Create <LayoutScriptException>(layout, null, msg, varName); } if (isVar) { val = sym.LocalDataAddress.ToString(); } else { string msg = "OffsetOf operator not valid for types."; throw LayoutScriptException.Create <LayoutScriptException>(layout, null, msg, varName); } break; case Operator.SizeOf: if (isVar) { val = sym.DataLength.ToString(); } else if (isBuiltinType) { val = builtinType.Size.ToString(); } else if (isUserDefinedType) { val = usrType.Size.ToString(); } break; case Operator.ValueOf: if (CurrentCodeBlock.IsProcessingTypedef) { string msg = "ValueOf operator not valid within type definitions."; throw LayoutScriptException.Create <LayoutScriptException>(layout, null, msg, varName); } if (isVar) { val = StringValueOf(sym); } else { string msg = "ValueOf operator not valid for types."; throw LayoutScriptException.Create <LayoutScriptException>(layout, null, msg, varName); } break; } return(val); }
private void InterpretFileObjectDefinition(Statement stmt) { EnsureParameters(stmt, Parameters.Comment, Parameters.Count, Parameters.Name); bool hasCount = GetParameter(stmt, Parameters.Count, out string countStr); bool hasName = GetParameter(stmt, Parameters.Name, out string objName); string typeName = stmt.Keyword; bool isAnonymousStructure = (typeName == Keywords.DataTypes.Struct || typeName == Keywords.DataTypes.Union); TypeInfo type = default(TypeInfo); if (!isAnonymousStructure && !TryLookupType(typeName, out type)) { string msg = "Unknown type '{0}'."; throw LayoutScriptException.Create <LayoutScriptException>(layout, stmt, msg, typeName); } // Evaluate 'count' expression int count = 1; if (hasCount) { try { count = (int)EvaluateExpression(countStr); } catch (SyntaxErrorException e) { string msg = "'{0}' is not a valid expression."; throw LayoutScriptException.Create <LayoutScriptException>(layout, e, stmt, msg, countStr); } if (count <= 0) { string msg = "Parameter '{0}' must be a positive integer."; throw LayoutScriptException.Create <LayoutScriptException>(layout, stmt, msg, Parameters.Count); } } // Validate name and create object symbol SymbolTable sym = SymbolTable.CreateRootSymbolTable(); if (hasName) { if (!SymbolTable.IsIdentifierValid(objName)) { string msg = "Invalid identifier '{0}'. " + "Identifiers must consist of only letters, numbers, and underscores. " + "Identifiers cannot begin with a digit nor can they be identical to a reserved word."; throw LayoutScriptException.Create <LayoutScriptException>(layout, stmt, msg, objName); } if (CurrentCodeBlock.Symbol.Lookup(objName) != null || TryLookupLocal(objName, out double dummyLocalValue)) { string msg = "A variable with identifier '{0}' already exists in the current scope."; throw LayoutScriptException.Create <LayoutScriptException>(layout, stmt, msg, objName); } if (hasCount) { sym = CurrentCodeBlock.Symbol.Insert(objName, count); } else { sym = CurrentCodeBlock.Symbol.Insert(objName); } } // Set symbol properties sym.GlobalDataAddress = GlobalOffset; sym.LocalDataAddress = CurrentCodeBlock.Offset; if (isAnonymousStructure || type.IsStruct || type.IsUnion) { sym.DataType = typeof(Structure); } else { sym.DataType = type.NativeType; } int totalDataLength = 0; SymbolTable elemSym = sym; for (int i = 0; i < count; i++) { if (hasCount && hasName) { elemSym = sym[i]; } elemSym.GlobalDataAddress = GlobalOffset; if (isAnonymousStructure || type.IsStruct || type.IsUnion) { if (hasName) { scopeStack.Push(new CodeBlock(elemSym)); } else { scopeStack.Push(new CodeBlock(SymbolTable.CreateNamelessSymbolTable(CurrentCodeBlock.Symbol))); } CurrentCodeBlock.IsUnion = (stmt.Keyword == Keywords.DataTypes.Union || (type != null && type.IsUnion)); IEnumerable <Statement> members = (isAnonymousStructure) ? stmt.NestedStatements : type.Members; if (!members.Any()) { string msg = "Empty structures are not allowed."; throw LayoutScriptException.Create <SyntaxException>(layout, stmt, msg); } foreach (Statement member in members) { InterpretStatement(member); } CodeBlock oldScope = scopeStack.Pop(); int numBytes = oldScope.MaxOffset; if (!CurrentCodeBlock.IsUnion) { CurrentCodeBlock.Offset += numBytes; } else { CurrentCodeBlock.MaxOffset = numBytes; } elemSym.DataType = sym.DataType; elemSym.DataLength = numBytes; totalDataLength += numBytes; } else { int numBytes = type.Size; int newOffset = CurrentCodeBlock.Offset + numBytes; if (newOffset > file.Length) { string msg = "Object definition runs past the end of the file."; throw LayoutScriptException.Create <LayoutScriptException>(layout, stmt, msg); } if (!CurrentCodeBlock.IsUnion) { CurrentCodeBlock.Offset = newOffset; } else { CurrentCodeBlock.MaxOffset = numBytes; } elemSym.DataType = type.NativeType; elemSym.DataLength = numBytes; totalDataLength += numBytes; } } sym.DataLength = totalDataLength; }