using System; using System.Collections.Generic; using System.Linq; namespace NTERA.Interpreter.Compiler { public static class Preprocessor { public static IDictionary PreprocessFile(string contents, string filename) { Dictionary procs = new Dictionary(); Lexer lexer = new Lexer(contents); Marker startMarker = lexer.TokenMarker; string currentDefinitionName = null; List currentDefinitionParameters = new List(); List currentDefinitionVariables = new List(); bool isReturnFunction = false; void Commit() { if (currentDefinitionName != null) { string procBody = contents.Substring(startMarker.Pointer, lexer.TokenMarker.Pointer - startMarker.Pointer); var definition = new FunctionDefinition(currentDefinitionName, currentDefinitionParameters.ToArray(), currentDefinitionVariables.ToArray(), isReturnFunction, filename, startMarker); procs.Add(definition, procBody); isReturnFunction = false; currentDefinitionName = null; currentDefinitionParameters.Clear(); } } using (var enumerator = lexer.GetEnumerator()) { do { if (lexer.TokenMarker.Column != 1) continue; if (enumerator.Current == Token.AtSymbol) { Commit(); startMarker = lexer.TokenMarker; enumerator.MoveNext(); if (enumerator.Current != Token.Identifer) throw new ParserException("Invalid function declaration - Expected an identifier", lexer.TokenMarker); currentDefinitionName = lexer.Identifer; enumerator.MoveNext(); if (enumerator.Current == Token.NewLine || enumerator.Current == Token.EOF) continue; if (enumerator.Current != Token.LParen && enumerator.Current != Token.Comma) throw new ParserException("Invalid function declaration", lexer.TokenMarker); enumerator.MoveNext(); if (enumerator.Current != Token.Identifer && enumerator.Current != Token.RParen) throw new ParserException("Invalid function declaration", lexer.TokenMarker); while (enumerator.Current == Token.Identifer) { string parameterName = lexer.Identifer; List indices = new List(); Value? defaultValue = null; enumerator.MoveNext(); while (enumerator.Current == Token.Colon && enumerator.MoveNext()) { if (enumerator.Current == Token.Value) { indices.Add(lexer.Value.Type == ValueType.Real ? ((int)lexer.Value).ToString() : lexer.Value.String); } else if (enumerator.Current == Token.Identifer) { indices.Add(lexer.Identifer); } enumerator.MoveNext(); } if (enumerator.Current == Token.Equal) { enumerator.MoveNext(); bool hasUnaryMinus = false; if (enumerator.Current == Token.Minus) { hasUnaryMinus = true; enumerator.MoveNext(); } if (enumerator.Current == Token.Identifer) { defaultValue = lexer.Identifer; } else if (enumerator.Current == Token.Value) { defaultValue = lexer.Value; if (hasUnaryMinus) { if (defaultValue.Value.Type == ValueType.Real) defaultValue = defaultValue * -1; else defaultValue = "-" + defaultValue; } } else throw new ParserException("Invalid function declaration", lexer.TokenMarker); enumerator.MoveNext(); } if (enumerator.Current == Token.Comma || enumerator.Current == Token.RParen) { enumerator.MoveNext(); } else if (enumerator.Current != Token.NewLine && enumerator.Current != Token.EOF) throw new ParserException("Invalid function declaration", lexer.TokenMarker); currentDefinitionParameters.Add(new FunctionParameter(parameterName, indices.ToArray(), defaultValue)); } if (enumerator.Current == Token.RParen) enumerator.MoveNext(); if (enumerator.Current != Token.NewLine && enumerator.Current != Token.EOF) throw new ParserException("Invalid function declaration", lexer.TokenMarker); } else if (enumerator.Current == Token.Sharp) { enumerator.MoveNext(); switch (enumerator.Current) { case Token.Dims: case Token.Dim: { bool isString = enumerator.Current != Token.Dim; enumerator.MoveNext(); VariableType variableType = VariableType.None; while (enumerator.Current == Token.Const || enumerator.Current == Token.Ref || enumerator.Current == Token.Dynamic) { if (enumerator.Current == Token.Const) variableType |= VariableType.Constant; else if (enumerator.Current == Token.Ref) variableType |= VariableType.Reference; else if (enumerator.Current == Token.Dynamic) variableType |= VariableType.Dynamic; enumerator.MoveNext(); } string variable = lexer.Identifer; enumerator.MoveNext(); Value? defaultValue = null; if (enumerator.Current == Token.Comma) { while (enumerator.MoveNext() && enumerator.Current != Token.Equal && enumerator.Current != Token.NewLine && enumerator.Current != Token.EOF) { } //arraySize = (int)lexer.Expression().Real; } if (enumerator.Current == Token.Equal) { enumerator.MoveNext(); if (isString) lexer.Type = LexerType.String; defaultValue = lexer.Expression(); lexer.Type = LexerType.Both; } else if (enumerator.Current != Token.NewLine && enumerator.Current != Token.EOF) { throw new ParserException("Invalid function declaration", lexer.TokenMarker); } currentDefinitionVariables.Add(new FunctionVariable(variable, isString ? ValueType.String : ValueType.Real, variableType, defaultValue)); break; } case Token.ReturnFunction: { isReturnFunction = true; break; } } } else { //resynchronize to next line while (enumerator.Current != Token.NewLine && enumerator.Current != Token.EOF && enumerator.MoveNext()) { } } } while (enumerator.MoveNext()); } Commit(); return procs; } private static IList> SplitCSV(IEnumerable lines) { List> csv = new List>(); foreach (var line in lines) { if (string.IsNullOrWhiteSpace(line) || line[0] == ';') continue; string newLine = line; int commentIndex = line.IndexOf(';'); if (commentIndex >= 0) newLine = line.Substring(0, commentIndex); string[] split = newLine.Split(','); csv.Add(split.ToList()); } return csv; } private static Dictionary NameIndexDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["ITEM"] = new[] { "ITEM", "ITEMSALES", "ITEMPRICE" }, ["BASE"] = new[] { "BASE", "LOSEBASE", "MAXBASE", "DOWNBASE" }, ["ABL"] = new[] { "ABL" }, ["TALENT"] = new[] { "TALENT" }, ["EXP"] = new[] { "EXP" }, ["MARK"] = new[] { "MARK" }, ["PALAM"] = new[] { "PALAM", "UP", "DOWN", "JUEL", "GOTJUEL", "CUP", "CDOWN" }, ["STAIN"] = new[] { "STAIN" }, ["SOURCE"] = new[] { "SOURCE" }, ["EX"] = new[] { "EX", "NOWEX" }, ["TEQUIP"] = new[] { "TEQUIP" }, ["EQUIP"] = new[] { "EQUIP" }, ["FLAG"] = new[] { "FLAG" }, ["TFLAG"] = new[] { "TFLAG" }, ["CFLAG"] = new[] { "CFLAG" }, ["STRNAME"] = new[] { "STR" }, ["SAVESTR"] = new[] { "SAVESTR" }, ["TCVAR"] = new[] { "TCVAR" }, ["TSTR"] = new[] { "TSTR" }, ["CSTR"] = new[] { "CSTR" }, ["CDFLAG1"] = new[] { "CDFLAG" }, ["CDFLAG2"] = new[] { "CDFLAG" }, ["GLOBAL"] = new[] { "GLOBAL" }, ["GLOBALS"] = new[] { "GLOBALS" }, }; public static void ProcessCSV(CSVDefinition targetDefinition, string filename, IEnumerable lines) { if (filename.EndsWith("_TR", StringComparison.OrdinalIgnoreCase)) return; if (filename.Equals("VariableSize", StringComparison.OrdinalIgnoreCase)) return; if (filename.Equals("_Replace", StringComparison.OrdinalIgnoreCase)) return; var csv = SplitCSV(lines); void AddVariableIndices(string variableName) { Dictionary varIndices = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var line in csv) if (!string.IsNullOrWhiteSpace(line[1])) varIndices[line[1]] = int.Parse(line[0]); targetDefinition.VariableIndexDictionary[variableName] = varIndices; } if (filename.Equals("GameBase", StringComparison.OrdinalIgnoreCase)) { foreach (var line in csv) targetDefinition.GameBaseInfo.Add(line[0], line[1]); return; } if (NameIndexDictionary.TryGetValue(filename, out var variables)) { foreach (var variable in variables) AddVariableIndices(variable); return; } if (filename.Equals("STR", StringComparison.OrdinalIgnoreCase)) { Dictionary strDefaultValues = new Dictionary(); foreach (var line in csv) strDefaultValues.Add(int.Parse(line[0]), line[1]); targetDefinition.VariableDefaultValueDictionary["STR"] = strDefaultValues; return; } if (filename.StartsWith("CHARA", StringComparison.OrdinalIgnoreCase)) { //Dictionary strDefaultValues = new Dictionary(); //foreach (var line in csv) // strDefaultValues.Add(int.Parse(line[0]), line[1]); //targetDefinition.VariableDefaultValueDictionary["STR"] = strDefaultValues; return; } //AddVariableIndices(Path.GetFileNameWithoutExtension(filename)); } } }