예제 #1
0
        // Skips the rest of a block comment.
        static void SkipBlockComment(Parser parser)
        {
            NextChar(parser); // The opening "*".

            int nesting = 1;
            while (nesting > 0)
            {
                char c = PeekChar(parser);
                if (c == '\0')
                {
                    LexError(parser, "Unterminated block comment.");
                    return;
                }

                if (c == '/' && PeekNextChar(parser) == '*')
                {
                    NextChar(parser);
                    NextChar(parser);
                    nesting++;
                    continue;
                }
                if (c == '*' && PeekNextChar(parser) == '/')
                {
                    NextChar(parser);
                    NextChar(parser);
                    nesting--;
                    continue;
                }

                // Regular comment character.
                NextChar(parser);
            }
        }
예제 #2
0
 static string GetTokenString(Parser parser)
 {
     return parser.Source.Substring(parser.TokenStart, parser.CurrentChar - parser.TokenStart);
 }
예제 #3
0
        // If the current character is [c], then consumes it and makes a token of type
        // [two]. Otherwise makes a token of type [one].
        static void TwoCharToken(Parser parser, char c, TokenType two, TokenType one)
        {
            if (PeekChar(parser) == c)
            {
                NextChar(parser);
                MakeToken(parser, two);
                return;
            }

            MakeToken(parser, one);
        }
예제 #4
0
 // Skips the rest of the current line.
 static void SkipLineComment(Parser parser)
 {
     while (PeekChar(parser) != '\n' && PeekChar(parser) != '\0')
     {
         NextChar(parser);
     }
 }
예제 #5
0
        // Finishes lexing an identifier. Handles reserved words.
        static void ReadName(Parser parser, TokenType type)
        {
            while (IsName(PeekChar(parser)) || IsDigit(PeekChar(parser)))
            {
                NextChar(parser);
            }

            string tokenName = GetTokenString(parser);

            switch (tokenName)
            {
                case "break":
                    type = TokenType.Break;
                    break;
                case "class":
                    type = TokenType.Class;
                    break;
                case "construct":
                    type = TokenType.Construct;
                    break;
                case "else":
                    type = TokenType.Else;
                    break;
                case "false":
                    type = TokenType.False;
                    break;
                case "for":
                    type = TokenType.For;
                    break;
                case "foreign":
                    type = TokenType.Foreign;
                    break;
                case "if":
                    type = TokenType.If;
                    break;
                case "import":
                    type = TokenType.Import;
                    break;
                case "in":
                    type = TokenType.In;
                    break;
                case "is":
                    type = TokenType.Is;
                    break;
                case "null":
                    type = TokenType.Null;
                    break;
                case "return":
                    type = TokenType.Return;
                    break;
                case "static":
                    type = TokenType.Static;
                    break;
                case "super":
                    type = TokenType.Super;
                    break;
                case "this":
                    type = TokenType.This;
                    break;
                case "true":
                    type = TokenType.True;
                    break;
                case "var":
                    type = TokenType.Var;
                    break;
                case "while":
                    type = TokenType.While;
                    break;
            }

            MakeToken(parser, type);
        }
예제 #6
0
 // Parses the numeric value of the current token.
 static void MakeNumber(Parser parser, bool isHex)
 {
     string s = GetTokenString(parser);
     try
     {
         parser.number = isHex ? Convert.ToInt32(s, 16) : Convert.ToDouble(s, CultureInfo.InvariantCulture);
     }
     catch (OverflowException)
     {
         LexError(parser, "Number too big");
     }
     MakeToken(parser, TokenType.TOKEN_NUMBER);
 }
예제 #7
0
 // Returns the current character the parser is sitting on.
 static char PeekChar(Parser parser)
 {
     return parser.CurrentChar < parser.Source.Length ? parser.Source[parser.CurrentChar] : '\0';
 }
예제 #8
0
        // Finishes lexing a hexadecimal number literal.
        static void ReadHexNumber(Parser parser)
        {
            // Skip past the `x` used to denote a hexadecimal literal.
            NextChar(parser);

            // Iterate over all the valid hexadecimal digits found.
            while (ReadHexDigit(parser) != -1)
            {
            }

            MakeNumber(parser, true);
        }
예제 #9
0
        public static ObjFn Compile(WrenVM vm, ObjModule module, string sourcePath, string source, bool printErrors)
        {
            Parser parser = new Parser
            {
                vm = vm,
                Module = module,
                SourcePath = sourcePath,
                Source = source,
                TokenStart = 0,
                CurrentChar = 0,
                CurrentLine = 1,
                Current = { Type = TokenType.Error, Start = 0, Length = 0, Line = 0 },
                SkipNewlines = true,
                PrintErrors = printErrors,
                HasError = false,
                Raw = ""
            };

            Compiler compiler = new Compiler(parser, null, true);

            // Read the first token.
            compiler.NextToken();

            compiler.IgnoreNewlines();

            while (!compiler.Match(TokenType.Eof))
            {
                compiler.Definition();

                // If there is no newline, it must be the end of the block on the same line.
                if (!compiler.MatchLine())
                {
                    compiler.Consume(TokenType.Eof, "Expect end of file.");
                    break;
                }
            }

            compiler.Emit(Instruction.NULL);
            compiler.Emit(Instruction.RETURN);

            // See if there are any implicitly declared module-level variables that never
            // got an explicit definition.
            // TODO: It would be nice if the error was on the line where it was used.
            for (int i = 0; i < parser.Module.Variables.Count; i++)
            {
                ModuleVariable t = parser.Module.Variables[i];
                if (t.Container == Obj.Undefined)
                {
                    compiler.Error(string.Format("Variable '{0}' is used but not defined.", t.Name));
                }
            }

            return compiler.EndCompiler("(script)");
        }
예제 #10
0
        // Initializes [compiler].
        public Compiler(Parser parser, Compiler parent, bool isFunction)
        {
            _parser = parser;
            _parent = parent;

            // Initialize this to null before allocating in case a GC gets triggered in
            // the middle of initializing the compiler.
            _constants = new List<Obj>();

            _numUpValues = 0;
            _numParams = 0;
            _loop = null;
            _enclosingClass = null;

            parser.vm.Compiler = this;

            if (parent == null)
            {
                _numLocals = 0;

                // Compiling top-level code, so the initial scope is module-level.
                _scopeDepth = -1;
            }
            else
            {
                // Declare a fake local variable for the receiver so that it's slot in the
                // stack is taken. For methods, we call this "this", so that we can resolve
                // references to that like a normal variable. For functions, they have no
                // explicit "this". So we pick a bogus name. That way references to "this"
                // inside a function will try to walk up the parent chain to find a method
                // enclosing the function whose "this" we can close over.
                _numLocals = 1;
                if (isFunction)
                {
                    _locals[0].Name = null;
                    _locals[0].Length = 0;
                }
                else
                {
                    _locals[0].Name = "this";
                    _locals[0].Length = 4;
                }
                _locals[0].Depth = -1;
                _locals[0].IsUpvalue = false;

                // The initial scope for function or method is a local scope.
                _scopeDepth = 0;
            }

            _bytecode = new List<byte>();
            _debugSourceLines = new int[16000];
        }
예제 #11
0
        private static void LexError(Parser parser, string format)
        {
            parser.HasError = true;
            if (!parser.PrintErrors) return;

            Console.Error.Write("[{0} line {1}] Error: ", parser.SourcePath, parser.CurrentLine);

            Console.Error.WriteLine(format);
        }
예제 #12
0
        // Finishes lexing an identifier. Handles reserved words.
        static void ReadName(Parser parser, TokenType type)
        {
            while (IsName(PeekChar(parser)) || IsDigit(PeekChar(parser)))
            {
                NextChar(parser);
            }

            string tokenName = GetTokenString(parser);

            switch (tokenName)
            {
                case "break":
                    type = TokenType.TOKEN_BREAK;
                    break;
                case "class":
                    type = TokenType.TOKEN_CLASS;
                    break;
                case "construct":
                    type = TokenType.TOKEN_CONSTRUCT;
                    break;
                case "else":
                    type = TokenType.TOKEN_ELSE;
                    break;
                case "false":
                    type = TokenType.TOKEN_FALSE;
                    break;
                case "for":
                    type = TokenType.TOKEN_FOR;
                    break;
                case "foreign":
                    type = TokenType.TOKEN_FOREIGN;
                    break;
                case "if":
                    type = TokenType.TOKEN_IF;
                    break;
                case "import":
                    type = TokenType.TOKEN_IMPORT;
                    break;
                case "in":
                    type = TokenType.TOKEN_IN;
                    break;
                case "is":
                    type = TokenType.TOKEN_IS;
                    break;
                case "null":
                    type = TokenType.TOKEN_NULL;
                    break;
                case "return":
                    type = TokenType.TOKEN_RETURN;
                    break;
                case "static":
                    type = TokenType.TOKEN_STATIC;
                    break;
                case "super":
                    type = TokenType.TOKEN_SUPER;
                    break;
                case "this":
                    type = TokenType.TOKEN_THIS;
                    break;
                case "true":
                    type = TokenType.TOKEN_TRUE;
                    break;
                case "var":
                    type = TokenType.TOKEN_VAR;
                    break;
                case "while":
                    type = TokenType.TOKEN_WHILE;
                    break;
            }

            MakeToken(parser, type);
        }
예제 #13
0
 private static char peekchar(Parser parser)
 {
     throw new NotImplementedException();
 }
예제 #14
0
        static int ReadHexDigit(Parser parser)
        {
            char c = NextChar(parser);
            if (c >= '0' && c <= '9') return c - '0';
            if (c >= 'a' && c <= 'f') return c - 'a' + 10;
            if (c >= 'A' && c <= 'F') return c - 'A' + 10;

            // Don't consume it if it isn't expected. Keeps us from reading past the end
            // of an unterminated string.
            parser.CurrentChar--;
            return -1;
        }
예제 #15
0
 // Returns the character after the current character.
 static char PeekNextChar(Parser parser)
 {
     // If we're at the end of the source, don't read past it.
     return parser.CurrentChar >= parser.Source.Length - 1 ? '\0' : parser.Source[parser.CurrentChar + 1];
 }
예제 #16
0
 // Parses the numeric value of the current token.
 static void MakeNumber(Parser parser, bool isHex)
 {
     string s = GetTokenString(parser);
     try
     {
         parser.Number = isHex ? Convert.ToInt32(s, 16) : Convert.ToDouble(s, CultureInfo.InvariantCulture);
     }
     catch (OverflowException)
     {
         LexError(parser, "Number too big");
     }
     catch (FormatException)
     {
         LexError(parser, "Number was not in correct format");
     }
     MakeToken(parser, TokenType.Number);
 }
예제 #17
0
 // Advances the parser forward one character.
 static char NextChar(Parser parser)
 {
     char c = PeekChar(parser);
     parser.CurrentChar++;
     if (c == '\n') parser.CurrentLine++;
     return c;
 }
예제 #18
0
        // Finishes lexing a number literal.
        static void ReadNumber(Parser parser)
        {
            while (IsDigit(PeekChar(parser))) NextChar(parser);

            // See if it has a floating point. Make sure there is a digit after the "."
            // so we don't get confused by method calls on number literals.
            if (PeekChar(parser) == '.' && IsDigit(PeekNextChar(parser)))
            {
                NextChar(parser);
                while (IsDigit(PeekChar(parser))) NextChar(parser);
            }

            // See if the number is in scientific notation
            if (PeekChar(parser) == 'e' || PeekChar(parser) == 'E')
            {
                NextChar(parser);

                // if the exponant is negative
                if (PeekChar(parser) == '-') NextChar(parser);

                if (!IsDigit(PeekChar(parser)))
                {
                    LexError(parser, "Unterminated scientific notation.");
                }

                while (IsDigit(PeekChar(parser))) NextChar(parser);
            }

            MakeNumber(parser, false);
        }
예제 #19
0
        // Sets the parser's current token to the given [type] and current character
        // range.
        static void MakeToken(Parser parser, TokenType type)
        {
            parser.Current.Type = type;
            parser.Current.Start = parser.TokenStart;
            parser.Current.Length = parser.CurrentChar - parser.TokenStart;
            parser.Current.Line = parser.CurrentLine;

            // Make line tokens appear on the line containing the "\n".
            if (type == TokenType.Line) parser.Current.Line--;
        }
예제 #20
0
 // Adds [c] to the current string literal being tokenized.
 static void AddStringChar(Parser parser, char c)
 {
     parser.Raw += c;
 }
예제 #21
0
        // Sets the parser's current token to the given [type] and current character
        // range.
        static void MakeToken(Parser parser, TokenType type)
        {
            parser.current.type = type;
            parser.current.start = parser.tokenStart;
            parser.current.length = parser.currentChar - parser.tokenStart;
            parser.current.line = parser.currentLine;

            // Make line tokens appear on the line containing the "\n".
            if (type == TokenType.TOKEN_LINE) parser.current.line--;
        }