public void TestPythonString() { SetTerminal(TerminalFactory.CreatePythonString("String")); //1. Single quotes TryMatch(@"'00\a\b\t\n\v\f\r\'\\00' "); Assert.IsTrue((string)_token.Value == "00\a\b\t\n\v\f\r\'\\00", "Failed to process escaped characters."); TryMatch("'abcd\nefg' "); Assert.IsTrue(_token.IsError(), "Failed to detect erroneous multi-line string."); TryMatch("'''abcd\nefg''' "); Assert.IsTrue((string)_token.Value == "abcd\nefg", "Failed to process line break in triple-quote string."); TryMatch(@"'''abcd\" + "\n" + "efg''' "); Assert.IsTrue((string)_token.Value == "abcd\nefg", "Failed to process escaped line-break char."); TryMatch(@"r'00\a\b\t\n\v\f\r00' "); Assert.IsTrue((string)_token.Value == @"00\a\b\t\n\v\f\r00", "Failed to process string with disabled escapes."); //2. Double quotes - we use TryMatchDoubles which replaces single quotes with doubles and then calls TryMatch TryMatchDoubles(@"'00\a\b\t\n\v\f\r\'\\00' "); Assert.IsTrue((string)_token.Value == "00\a\b\t\n\v\f\r\"\\00", "Failed to process escaped characters."); TryMatchDoubles("'abcd\nefg' "); Assert.IsTrue(_token.IsError(), "Failed to detect erroneous multi-line string. (Double quotes)"); TryMatchDoubles("'''abcd\nefg''' "); Assert.IsTrue((string)_token.Value == "abcd\nefg", "Failed to process line break in triple-quote string. (Double quotes)"); TryMatchDoubles(@"'''abcd\" + "\n" + "efg''' "); Assert.IsTrue((string)_token.Value == "abcd\nefg", "Failed to process escaped line-break char. (Double quotes)"); TryMatchDoubles(@"r'00\a\b\t\n\v\f\r00' "); Assert.IsTrue((string)_token.Value == @"00\a\b\t\n\v\f\r00", "Failed to process string with disabled escapes. (Double quotes)"); }//method
public void TestString_Python() { Parser parser; Token token; parser = TestHelper.CreateParser(TerminalFactory.CreatePythonString("String")); //1. Single quotes token = parser.ParseInput(@"'00\a\b\t\n\v\f\r\'\\00' "); Assert.True((string)token.Value == "00\a\b\t\n\v\f\r\'\\00", "Failed to process escaped characters."); token = parser.ParseInput("'abcd\nefg' "); Assert.True(token.IsError(), "Failed to detect erroneous multi-line string."); token = parser.ParseInput("'''abcd\nefg''' "); Assert.True((string)token.Value == "abcd\nefg", "Failed to process line break in triple-quote string."); token = parser.ParseInput(@"'''abcd\" + "\n" + "efg''' "); Assert.True((string)token.Value == "abcd\nefg", "Failed to process escaped line-break char."); token = parser.ParseInput(@"r'00\a\b\t\n\v\f\r00' "); Assert.True((string)token.Value == @"00\a\b\t\n\v\f\r00", "Failed to process string with disabled escapes."); //2. Double quotes - we use TryMatchDoubles which replaces single quotes with doubles and then calls TryMatch token = parser.ParseInput(ReplaceQuotes(@"'00\a\b\t\n\v\f\r\'\\00' ")); Assert.True((string)token.Value == "00\a\b\t\n\v\f\r\"\\00", "Failed to process escaped characters."); token = parser.ParseInput(ReplaceQuotes("'abcd\nefg' ")); Assert.True(token.IsError(), "Failed to detect erroneous multi-line string. (Double quotes)"); token = parser.ParseInput(ReplaceQuotes("'''abcd\nefg''' ")); Assert.True((string)token.Value == "abcd\nefg", "Failed to process line break in triple-quote string. (Double quotes)"); token = parser.ParseInput(ReplaceQuotes(@"'''abcd\" + "\n" + "efg''' ")); Assert.True((string)token.Value == "abcd\nefg", "Failed to process escaped line-break char. (Double quotes)"); token = parser.ParseInput(ReplaceQuotes(@"r'00\a\b\t\n\v\f\r00' ")); Assert.True((string)token.Value == @"00\a\b\t\n\v\f\r00", "Failed to process string with disabled escapes. (Double quotes)"); }//method
// ROADMAP: // 1. Basic object-tree shit (atoms) // 2. Proc support // 3. Preprocessor public DreamGrammar() : base(caseSensitive: true) { // Thankfully, we get built-in pythonic numbers, which is exactly what we need. var NUMBER = TerminalFactory.CreatePythonNumber("number"); var IDENTIFIER = TerminalFactory.CreatePythonIdentifier("identifier"); var STRING = TerminalFactory.CreatePythonString("string"); // TODO: May need to be custom. // Constant terminals var PROC = ToTerm("proc"); var VAR = ToTerm("var"); var RETURN = ToTerm("return"); var COMMA = ToTerm(","); var SLASH = ToTerm("/"); var PIPE = ToTerm("|"); #region Comment stuff // Dream uses both line comments and block comments. var lineComment = new CommentTerminal("lineComment", "//", "\n", "\r"); // May or may not work, since BYOND permits nesting and most other languages don't. var blockComment = new CommentTerminal("blockComment", "/*", "*/"); // From MiniPython example: // //comment must to be added to NonGrammarTerminals list; it is not used directly in grammar rules, // // so we add it to this list to let Scanner know that it is also a valid terminal. // However, with Dream, we may need to make them grammar terminals so we can handle nesting properly. - N3X base.NonGrammarTerminals.Add(lineComment); base.NonGrammarTerminals.Add(blockComment); // Needed for EOL escaping. base.NonGrammarTerminals.Add(ToTerm(@"\")); #endregion #region Non-Terminals // Blocks //////////////////////////// // procs var procblock = new NonTerminal("procblock", "proc block"); var procslash = new NonTerminal("procslash"); var procdef = new NonTerminal("procdef", "proc definition"); var procdefs = new NonTerminal("procdefs"); var procdef_no_path = new NonTerminal("procdef_no_path", "proc definition (sans path)"); var procdecl = new NonTerminal("procdecl"); var procchildren = new NonTerminal("procchildren", "proc children"); // Atoms var atomdef = new NonTerminal("atomdef"); var atomblock = new NonTerminal("atomblock", "atom declaration"); var atomchildren = new NonTerminal("atomchildren", "atom declaration"); var atomchild = new NonTerminal("atomchild", "atom child"); // Path stuff var path = new NonTerminal("path"); var abspath = new NonTerminal("abspath", "absolute path"); var relpath = new NonTerminal("relpath", "relative path"); var pathslash = new NonTerminal("pathslash"); // Statements ///////////////////////////// // Variable declaration var vardefs = new NonTerminal("vardefs"); var vardef = new NonTerminal("vardef"); var inline_vardef_no_default = new NonTerminal("inline_vardef_no_default"); var inline_vardef = new NonTerminal("inline_vardef"); var varblock = new NonTerminal("varblock"); // Parameters var param = new NonTerminal("param", "parameter" /*, typeof(OpenBYOND.Dream.AST.ParameterNode)*/); var parameters = new NonTerminal("parameters"); var paramlist = new NonTerminal("paramlist", "parameter list", typeof(ParamListNode)); // Primitives (Identifiers won't work because of the "anything in <list>" rule.) var primitive = new NonTerminal("primitive"); var primitivelist = new NonTerminal("primitivelist", "primitive list"); // Expressions var const_expression = new NonTerminal("const_expression"); var expression = new NonTerminal("expression"); var expressions = new NonTerminal("expressions"); var assignable_expression = new NonTerminal("assignable_expression"); var assignment = new NonTerminal("assignment"); var operations = new NonTerminal("operations"); // Statements var return_stmt = new NonTerminal("return_stmt"); var script = new NonTerminal("script", "script root", typeof(StatementListNode)); var declblocks = new NonTerminal("declblocks", "declarative blocks"); // Easier on the eyes. var INDENT = Indent; var DEDENT = Dedent; #endregion #region BNF Rules #region Type Paths // <path> ::= <abspath> | <relpath> path.Rule = abspath | relpath; // <relpath> ::= <identifier> // | <relpath> '/' <identifier> relpath.Rule = IDENTIFIER | relpath + SLASH + IDENTIFIER; // <abspath> ::= '/' <relpath> // | <abspath> '/' <identifier> abspath.Rule = SLASH + relpath | abspath + SLASH + IDENTIFIER; // <pathslash> ::= <path> '/' pathslash.Rule = path + SLASH; #endregion #region Blocks (atoms, procs, if, etc) // <atomblock> ::= <path> INDENT <atomchildren> DEDENT atomblock.Rule = path + INDENT + atomchildren + DEDENT; // <atomchildren> ::= <atomchild>* atomchildren.Rule = MakeStarRule(atomchildren, atomchild); // <atomchild> ::= <vardecl> // | <atomblock> // | <procblock> // | <varblock> atomchild.Rule = vardef | atomblock | procblock | varblock ; // <procblock> ::= PROC INDENT <procdefs> DEDENT procblock.Rule = PROC + INDENT + procdefs + DEDENT; // <procdefs> ::= <procdef_no_path>+ procdefs.Rule = MakePlusRule(procdefs, procdef_no_path); // <declblocks> ::= <procblock> // | <atomblock> declblocks.Rule = atomblock | procblock | procdecl | procdef | varblock ; #endregion #region Variable stuff // <vardef> ::= <path> <varblock> // | <varblock> // | <inline_vardef> vardef.Rule = path + varblock | varblock | inline_vardef ; // <vardefs> ::= <vardef>+ vardefs.Rule = MakePlusRule(vardefs, vardef); // var/honk is basically // VAR path(/) identifier(honk) // <inline_vardef_no_default> ::= VAR <abspath> '/' IDENTIFIER // | VAR '/' IDENTIFIER inline_vardef_no_default.Rule = VAR + abspath ; // <inline_vardef> ::= inline_vardef_no_default // | inline_vardef_no_default '=' inline_vardef.Rule = inline_vardef_no_default | inline_vardef_no_default + "=" + const_expression ; // <varblock> ::= VAR INDENT <vardefs> DEDENT varblock.Rule = VAR + INDENT + vardefs + DEDENT; #endregion #region Proc stuff // <parameters> ::= '(' <paramlist> ')' parameters.Rule = ToTerm("(") + paramlist + ")"; // <paramlist> ::= <param>* paramlist.Rule = MakeStarRule(paramlist, COMMA, param); // This is probably one of the worst parts about Dream. This shit is an absolute mess. - N3X // <param> ::= <inline_vardef> // var/type/blah [=blah] // | <identifier> // blah // | <inline_vardef_no_default> 'as' <primitivelist> // var/blah as obj|mob // | <identifier> 'as' <primitivelist> // blah as obj|mob param.Rule = inline_vardef | IDENTIFIER | inline_vardef_no_default + "as" + primitivelist | IDENTIFIER + "as" + primitivelist ; // <procdef> ::= <path> <parameters> INDENT <expressions> DEDENT procdef.Rule = path + parameters + INDENT + expressions + DEDENT; // <procdef_no_path> ::= IDENTIFIER <parameters> INDENT <expressions> DEDENT procdef_no_path.Rule = IDENTIFIER + parameters + INDENT + expressions + DEDENT; // <procslash> ::= PROC SLASH procslash.Rule = PROC + SLASH; // <procdecl> ::= <pathslash> <procslash> <procdef_no_path> // | <pathslash> <procblock> // | <procblock> procdecl.Rule = pathslash + procslash + procdef_no_path | pathslash + procblock | procblock ; #endregion #region Expressions // Note: this gets particularly hairy. // <const_expression> ::= NUMBER // | STRING // | '(' <const_expression> ')' // | <operations> const_expression.Rule = NUMBER | STRING | "(" + const_expression + ")" | operations ; // <operations> ::= <const_expression> "*" <const_expression> // | <const_expression> "/" <const_expression> // | <const_expression> "%" <const_expression> // | <const_expression> "+" <const_expression> // | <const_expression> "-" <const_expression> operations.Rule = const_expression + "*" + const_expression | const_expression + "/" + const_expression | const_expression + "%" + const_expression | const_expression + "+" + const_expression | const_expression + "-" + const_expression ; // <expression> ::= <assignment> // | <inline_vardef> // | <return_stmt> expression.Rule = assignment | inline_vardef | return_stmt ; // <expressions> ::= <expression>* expressions.Rule = MakeStarRule(expressions, expression); // <assignable_expression> ::= <const_expression> // | IDENTIFIER // | STRING assignable_expression.Rule = const_expression | IDENTIFIER | STRING ; // <assignment> ::= IDENTIFIER '=' <assignable_expression> assignment.Rule = IDENTIFIER + "=" + assignable_expression; // <return_stmt> ::= RETURN const_expression return_stmt.Rule = RETURN + const_expression; #endregion // Okay, this is apparently the right way to do it. - N3X primitive.Rule = ToTerm("obj") | "mob" | "turf"/*|"anything" + "in" + list*/ ; // <primitivelist> ::= <primitive>+ (| seperator) primitivelist.Rule = MakePlusRule(primitivelist, PIPE, primitive); // <paramlist> ::= <parameter>* paramlist.Rule = MakeStarRule(paramlist, COMMA, param); // <script> ::= <declblocks>* script.Rule = declblocks; this.Root = script; #endregion AddToNoReportGroup(Eos); this.MarkReservedWords("break", "continue", "else", "for", "if", "return", "while", "proc"); this.LanguageFlags = LanguageFlags.NewLineBeforeEOF | LanguageFlags.CreateAst | LanguageFlags.SupportsBigInt; }