public void TestParseClass() { var source = "class Test Deprecated Transient; \n" + "var private deprecated int X; \n" + "struct transient testStruct\n" + "{ var float a, b, c; };\n" + "private simulated function float MyFunc( out testStruct one, coerce optional float two ) \n" + "{\n" + " return one.b + funcB(one, two);\n" + "}\n" + "auto state MyState\n" + "{\n" + "ignores MyFunc;\n" + "function StateFunc()\n" + "{\n" + "}\n" + "Begin:\n" + " moredragons\n" + "}\n" + "final static operator(254) int >>>( coerce float left, coerce float right )\n" + "{\n" + " all the dragons\n" + "}\n" + "\n"; var parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); var decl = parser.TryParseClass(); Assert.IsNotNull(decl); Assert.AreEqual(decl.Name.ToLower(), "test"); Assert.AreEqual(decl.Specifiers[1].Value.ToLower(), "transient"); Assert.AreEqual(decl.VariableDeclarations[0].Variables[0].Name.ToLower(), "x"); Assert.AreEqual(decl.TypeDeclarations[0].Name.ToLower(), "teststruct"); Assert.AreEqual(decl.Functions[0].Name.ToLower(), "myfunc"); Assert.AreEqual(decl.States[0].Name.ToLower(), "mystate"); Assert.AreEqual(decl.Operators[0].OperatorKeyword.ToLower(), ">>>"); Assert.AreEqual(log.Messages[log.Messages.Count - 1].Message.Substring(0, 10), "No parent "); source = "test ;\n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseClass()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected class declaration!"); source = "class ;\n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseClass()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected class name!"); source = "class Test deprecated\n var int local;"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseClass()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected semi-colon!"); source = "class Test deprecated; \n var int ;"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseClass()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Malformed instance variable!"); source = "class Test deprecated; \n struct fail;"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseClass()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Malformed type declaration!"); source = "class Test deprecated; \n struct fail { var int one; } struct next;"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseClass()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected semi-colon!"); source = "class Test deprecated; \n struct fail { var int one; }; function myfunc() {} var int fail;"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseClass()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected function/state/operator declaration!"); return; }
public static bool ResolveAllClassesInPackage(IMEPackage pcc, ref SymbolTable symbols) { string fileName = Path.GetFileNameWithoutExtension(pcc.FilePath); #if DEBUGSCRIPT string dumpFolderPath = Path.Combine(ME3Directory.gamePath, "ScriptDump", fileName); Directory.CreateDirectory(dumpFolderPath); #endif var log = new MessageLog(); Debug.WriteLine($"{fileName}: Beginning Parse."); var classes = new List <(Class ast, string scriptText)>(); foreach (ExportEntry export in pcc.Exports.Where(exp => exp.IsClass)) { Class cls = ME3ObjectToASTConverter.ConvertClass(export.GetBinaryData <UClass>(), false); string scriptText = ""; try { #if DEBUGSCRIPT var codeBuilder = new CodeBuilderVisitor(); cls.AcceptVisitor(codeBuilder); scriptText = codeBuilder.GetCodeString(); File.WriteAllText(Path.Combine(dumpFolderPath, $"{cls.Name}.uc"), scriptText); var parser = new ClassOutlineParser(new TokenStream <string>(new StringLexer(scriptText, log)), log); cls = parser.TryParseClass(); if (cls == null || log.Content.Any()) { DisplayError(scriptText, log.ToString()); return(false); } #endif if (export.ObjectName == "Object") { symbols = SymbolTable.CreateIntrinsicTable(cls); } else { symbols.AddType(cls); } classes.Add(cls, scriptText); } catch (Exception e) when(!App.IsDebug) { DisplayError(scriptText, log.ToString()); return(false); } } Debug.WriteLine($"{fileName}: Finished parse."); foreach (var validationPass in Enums.GetValues <ValidationPass>()) { foreach ((Class ast, string scriptText) in classes) { try { var validator = new ClassValidationVisitor(log, symbols, validationPass); ast.AcceptVisitor(validator); if (log.Content.Any()) { DisplayError(scriptText, log.ToString()); return(false); } } catch (Exception e) when(!App.IsDebug) { DisplayError(scriptText, log.ToString()); return(false); } } Debug.WriteLine($"{fileName}: Finished validation pass {validationPass}."); } switch (fileName) { case "Core": symbols.InitializeOperators(); break; case "Engine": symbols.ValidateIntrinsics(); break; } #if DEBUGSCRIPT //parse function bodies for testing purposes foreach ((Class ast, string scriptText) in classes) { symbols.RevertToObjectStack(); if (!ast.Name.CaseInsensitiveEquals("Object")) { symbols.GoDirectlyToStack(((Class)ast.Parent).GetInheritanceString()); symbols.PushScope(ast.Name); } foreach (Function function in ast.Functions.Where(func => !func.IsNative && func.IsDefined)) { CodeBodyParser.ParseFunction(function, scriptText, symbols, log); if (log.Content.Any()) { DisplayError(scriptText, log.ToString()); } } } #endif symbols.RevertToObjectStack(); return(true); }