public void TestParseVarDecl() { var source = "var private struct transient twoStruct extends testStruct\n" + "{\n" + " var etestnumeration num;\n" + "} structA, structB;"; var parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); var decl = parser.TryParseVarDecl(); Assert.IsNotNull(decl); Assert.AreEqual(decl.Specifiers[0].Value.ToLower(), "private"); Assert.AreEqual(decl.VarType.Name.ToLower(), "twostruct"); Assert.AreEqual(decl.Variables[1].Name.ToLower(), "structb"); source = "var ;\n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseVarDecl()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected variable type or struct/enum type declaration!"); source = "var int ;\n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseVarDecl()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Malformed variable names!"); source = "var int test\n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseVarDecl()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected semi-colon!"); return; }
public void TestParseStruct() { var source = "struct transient testStruct\n" + "{ var float a, b, c; };\n"; var parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); var s = parser.TryParseStruct(); Assert.IsNotNull(s); Assert.AreEqual(s.Name.ToLower(), "teststruct"); Assert.AreEqual(s.Members[0].Variables[1].Name.ToLower(), "b"); source = "struct transient {\n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseStruct()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected struct name!"); source = "struct transient testStruct \n var float a; }"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseStruct()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected '{'!"); source = "struct transient testStruct { \n var a; }"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseStruct()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Malformed struct content!"); return; }
public void TestParseSpecifier() { var source = "coerce native"; var parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.AreEqual(parser.TryParseSpecifier(GlobalLists.ParameterSpecifiers).Value.ToLower(), "coerce"); Assert.IsNull(parser.TryParseSpecifier(GlobalLists.ParameterSpecifiers)); return; }
public void TestParseState() { var source = "auto state MyState\n" + "{\n" + "ignores MyFunc;\n" + "function StateFunc()\n" + "{\n" + "}\n" + "\n" + "Begin:\n" + " moredragons\n" + "}\n"; var parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); var state = parser.TryParseState(); Assert.IsNotNull(state); Assert.AreEqual(state.Name.ToLower(), "mystate"); Assert.AreEqual(state.Specifiers[0].Value.ToLower(), "auto"); Assert.AreEqual(state.Ignores[0].Name.ToLower(), "myfunc"); source = "auto state { \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseState()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected state name!"); source = "auto state MyState ignores \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseState()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected '{'!"); source = "auto state MyState { ignores ; \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseState()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Malformed ignore statement!"); source = "auto state MyState { ignores one, ; \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseState()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Malformed ignore statement!"); source = "auto state MyState { ignores one, two } \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseState()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected semi-colon!"); source = "auto state MyState { \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseState()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected '}'!"); return; }
public void TestParseParameter() { var source = "coerce out int one float two[5] optional )"; var parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.AreEqual(parser.TryParseParameter().Name.ToLower(), "one"); Assert.AreEqual(parser.TryParseParameter().IsStaticArray, true); Assert.IsNull(parser.TryParseParameter()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected parameter type!"); return; }
public static (ASTNode ast, MessageLog log) CompileAST(string script, string type) { var log = new MessageLog(); var parser = new ClassOutlineParser(new TokenStream <string>(new StringLexer(script, log)), log); try { ASTNode ast = parser.ParseDocument(type); log.LogMessage($"Parse{(ast is null ? " failed!" : "d!")}"); return(ast, log); } catch (Exception e) { log.LogMessage($"Parse failed! Exception: {e}"); return(null, log); } }
public void TestParseFunction() { var source = "private simulated function float MyFunc( out testStruct one, coerce optional float two ) \n" + "{\n" + " return one.b + funcB(one, two);\n" + "}\n"; var parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); var func = parser.TryParseFunction(); Assert.IsNotNull(func); Assert.AreEqual(func.Name.ToLower(), "myfunc"); Assert.AreEqual(func.Specifiers[0].Value.ToLower(), "private"); Assert.AreEqual(func.Parameters[1].Name.ToLower(), "two"); Assert.AreEqual(func.ReturnType.Name.ToLower(), "float"); source = "private simulated function ( \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseFunction()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected function name or return type!"); source = "function int funcA ) \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseFunction()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected '('!"); source = "function int funcA ( out int, ) \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseFunction()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Malformed parameter!"); source = "function int funcA ( out int one two ) \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseFunction()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Unexpected parameter content!"); source = "function int funcA ( out int one, int two) \n {"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseFunction()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Malformed function body!"); return; }
public void TestParseEnum() { var source = "enum ETestnumeration {\n" + " TEST_value1,\n" + " TEST_value2,\n" + " TEST_value3,\n" + "};\n"; var parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); var enumeration = parser.TryParseEnum(); Assert.IsNotNull(enumeration); Assert.AreEqual(enumeration.Name.ToLower(), "etestnumeration"); Assert.AreEqual(enumeration.Values[1].Name.ToLower(), "test_value2"); source = "enum {\n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseEnum()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected enumeration name!"); source = "enum test \n test_value1, \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseEnum()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected '{'!"); source = "enum test {\n } \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseEnum()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected non-empty enumeration!"); source = "enum test {\n int value } \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseEnum()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Malformed enumeration content!"); return; }
public void TestParseOpDecl() { var source = "final static operator(254) int >>>( coerce float left, coerce float right )\n" + "{\n" + " all the dragons\n" + "}\n"; var parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); var decl = parser.TryParseOperatorDecl(); Assert.IsNotNull(decl); Assert.AreEqual(decl.OperatorKeyword.ToLower(), ">>>"); Assert.AreEqual((decl as InOpDeclaration).Precedence, 254); Assert.AreEqual(decl.ReturnType.Name.ToLower(), "int"); Assert.AreEqual((decl as InOpDeclaration).RightOperand.Name.ToLower(), "right"); source = "final static operator int \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseOperatorDecl()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected '('! (Did you forget to specify operator precedence?)"); source = "final static operator(asd) int \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseOperatorDecl()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected an integer number!"); source = "final static operator(54 int \n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseOperatorDecl()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected ')'!"); source = "final static operator(54) (\n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseOperatorDecl()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected operator name or return type!"); source = "final static operator(254) int >>>\n {"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseOperatorDecl()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Expected '('!"); source = "final static operator(254) int >>>( coerce left, coerce float right )\n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseOperatorDecl()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Malformed operand!"); source = "final static operator(254) int >>>( coerce float left coerce float right )\n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseOperatorDecl()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Unexpected operand content!"); source = "final static operator(254) int >>>( coerce float left )\n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseOperatorDecl()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "In-fix operators requires exactly 2 parameters!"); source = "final static preoperator int >>>( coerce float left, coerce float right )\n"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseOperatorDecl()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Post/Pre-fix operators requires exactly 1 parameter!"); source = "final static operator(254) int >>>( coerce float left, coerce float right )\n { asdasd"; parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); Assert.IsNull(parser.TryParseOperatorDecl()); Assert.AreEqual(log.AllErrors[log.AllErrors.Count - 1].Message, "Malformed operator body!"); return; }
public void BasicClassTest() { var source = "class Test Deprecated Transient; \n" + "var enum ETestnumeration {\n" + " TEST_value1,\n" + " TEST_value2,\n" + " TEST_value3,\n" + "} inlineNumeration, testnum2;\n" + "var private deprecated int X; \n" + "VAR INT Y, Z; \n" + "var ETestnumeration testnum;\n" + "struct transient testStruct\n" + "{ var float a, b, c; };\n" + "var private struct transient twoStruct extends testStruct\n" + "{\n" + " var etestnumeration num;\n" + "} structA, structB;\n" + "function float funcB( testStruct one, float two ) \n" + "{\n" + " local float c;" + " one.a = 1.3 * c;" + " while (true)" + " {" + " c = c - c - c;" + " }" + " if (false) {" + " switch(one.a) {" + " case one.a:" + " c = one.a;" + " break;" + " case 1.2:" + " case 1.3:" + " c = c + 0.5;" + " break;" + " default:" + " c = 6.6;" + " }" + " }" + " return one.a + 0.33 * (0.66 + 0.1) * 1.5;\n" + "}\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" + "\n" + "Begin:\n" + " moredragons\n" + "}\n" + "\n" + "final static operator(254) int >>>( coerce float left, coerce float right )\n" + "{\n" + " all the dragons\n" + "}\n" + "\n" + "\n" + "\n"; var parser = new ClassOutlineParser(new TokenStream <String>(new StringLexer(source)), log); var symbols = new SymbolTable(); Class obj = new Class("Object", null, null, null, null, null, null, null, null, null, null); obj.OuterClass = obj; symbols.PushScope(obj.Name); symbols.AddSymbol(obj.Name, obj); VariableType integer = new VariableType("int", null, null); symbols.AddSymbol(integer.Name, integer); VariableType floatingpoint = new VariableType("float", null, null); symbols.AddSymbol(floatingpoint.Name, floatingpoint); InOpDeclaration plus_float = new InOpDeclaration("+", 20, false, null, floatingpoint, new FunctionParameter(floatingpoint, null, null, null, null), new FunctionParameter(floatingpoint, null, null, null, null), null, null, null); symbols.AddOperator(plus_float); InOpDeclaration sub_float = new InOpDeclaration("-", 20, false, null, floatingpoint, new FunctionParameter(floatingpoint, null, null, null, null), new FunctionParameter(floatingpoint, null, null, null, null), null, null, null); symbols.AddOperator(sub_float); InOpDeclaration mult_float = new InOpDeclaration("*", 16, false, null, floatingpoint, new FunctionParameter(floatingpoint, null, null, null, null), new FunctionParameter(floatingpoint, null, null, null, null), null, null, null); symbols.AddOperator(mult_float); Class node = (Class)parser.ParseDocument(); var ClassValidator = new ClassValidationVisitor(log, symbols); node.AcceptVisitor(ClassValidator); symbols.GoDirectlyToStack(node.GetInheritanceString()); foreach (Function f in node.Functions) { symbols.PushScope(f.Name); var p = new CodeBodyParser(new TokenStream <String>(new StringLexer(source)), f.Body, symbols, f, log); var b = p.ParseBody(); symbols.PopScope(); } var CodeBuilder = new CodeBuilderVisitor(); node.AcceptVisitor(CodeBuilder); Console.Write(CodeBuilder.GetCodeString()); Assert.IsTrue(log.AllErrors.Count == 0); return; }
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); }