Jurassic.Compiler.Lexer.ReadStringLiteral C# (CSharp) Method

ReadStringLiteral() public method

Reads a string literal.
public ReadStringLiteral ( int firstChar ) : Jurassic.Compiler.Token
firstChar int The first character of the string literal.
return Jurassic.Compiler.Token
        public Token ReadStringLiteral(int firstChar)
        {
            System.Diagnostics.Debug.Assert(firstChar == '\'' || firstChar == '"' || firstChar == '`');
            var contents = new StringBuilder();
            int lineTerminatorCount = 0;
            int escapeSequenceCount = 0;

            // In order to support tagged template literals properly, we need to capture the raw
            // text, including escape sequences.
            StringBuilder previousInputCapture = null;
            if (firstChar == '`')
            {
                previousInputCapture = this.InputCaptureStringBuilder;
                this.InputCaptureStringBuilder = new StringBuilder();
            }

            int c;
            while (true)
            {
                c = ReadNextChar();
                if (c == firstChar)
                    break;
                if (c == -1)
                    throw new JavaScriptException(this.engine, ErrorType.SyntaxError, "Unexpected end of input in string literal.", this.lineNumber, this.Source.Path);
                if (IsLineTerminator(c))
                {
                    // Line terminators are only allowed in template literals.
                    if (firstChar != '`')
                        throw new JavaScriptException(this.engine, ErrorType.SyntaxError, "Unexpected line terminator in string literal.", this.lineNumber, this.Source.Path);
                    ReadLineTerminator(c);
                    contents.Append('\n');
                }
                else if (c == '\\')
                {
                    // Escape sequence or line continuation.
                    c = ReadNextChar();
                    if (IsLineTerminator(c))
                    {
                        // Line continuation.
                        ReadLineTerminator(c);

                        // Keep track of the number of line terminators so the parser can compute
                        // line numbers correctly.
                        lineTerminatorCount++;

                        // Increment the internal line number so errors can be tracked properly.
                        this.lineNumber++;
                        this.columnNumber = 1;
                    }
                    else
                    {
                        // Escape sequence.
                        switch (c)
                        {
                            case 'b':
                                // Backspace.
                                contents.Append((char)0x08);
                                break;
                            case 'f':
                                // Form feed.
                                contents.Append((char)0x0C);
                                break;
                            case 'n':
                                // Line feed.
                                contents.Append((char)0x0A);
                                break;
                            case 'r':
                                // Carriage return.
                                contents.Append((char)0x0D);
                                break;
                            case 't':
                                // Horizontal tab.
                                contents.Append((char)0x09);
                                break;
                            case 'v':
                                // Vertical tab.
                                contents.Append((char)0x0B);
                                break;
                            case 'x':
                                // ASCII escape.
                                contents.Append(ReadHexEscapeSequence(2));
                                break;
                            case 'u':
                                // Unicode escape.
                                c = this.reader.Peek();
                                if (c == '{')
                                    // ECMAScript 6 hex escape sequence.
                                    contents.Append(ReadExtendedUnicodeSequence());
                                else
                                    contents.Append(ReadHexEscapeSequence(4));
                                break;
                            case '0':
                                // Null character or octal escape sequence.
                                c = this.reader.Peek();
                                if (c >= '0' && c <= '9')
                                    contents.Append(ReadOctalEscapeSequence(firstChar, 0));
                                else
                                    contents.Append((char)0);
                                break;
                            case '1':
                            case '2':
                            case '3':
                            case '4':
                            case '5':
                            case '6':
                            case '7':
                                // Octal escape sequence.
                                contents.Append(ReadOctalEscapeSequence(firstChar, c - '0'));
                                break;
                            case '8':
                            case '9':
                                throw new JavaScriptException(this.engine, ErrorType.SyntaxError, "Invalid octal escape sequence.", this.lineNumber, this.Source.Path);
                            default:
                                contents.Append((char)c);
                                break;
                        }
                        escapeSequenceCount ++;
                    }
                }
                else if (c == '$' && firstChar == '`')
                {
                    // This is a template literal substitution if the next character is '{'
                    if (this.reader.Peek() == '{')
                    {
                        // Yes, this is a substitution!
                        ReadNextChar();
                        break;
                    }
                    else
                    {
                        // Not a substitution.
                        contents.Append((char)c);
                    }
                }
                else
                {
                    contents.Append((char)c);
                }
            }

            // Template literals return a different type of token.
            if (firstChar == '`')
            {
                var rawText = this.InputCaptureStringBuilder.ToString(0, this.InputCaptureStringBuilder.Length - (c == '$' ? 2 : 1));
                this.InputCaptureStringBuilder = previousInputCapture;
                if (this.InputCaptureStringBuilder != null)
                    this.InputCaptureStringBuilder.Append(previousInputCapture.ToString());
                return new TemplateLiteralToken(contents.ToString(), rawText, substitutionFollows: c == '$');
            }

            // Return a regular string literal token.
            return new StringLiteralToken(contents.ToString(), escapeSequenceCount, lineTerminatorCount);
        }