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;
        }