示例#1
0
        /// <summary>
        /// Takes a Source and a UTF-8 character offset, and returns the corresponding
        /// line and column as a SourceLocation.
        /// </summary>
        public static SourceLocation GetLocation(Source source, int position)
        {
            var line = 1;
            var column = position + 1;

            var matches = _lineRegexp.Matches(source.Body);
            for (int i = 0; i < matches.Count && matches[i].Index < position; i++)
            {
                line += 1;
                column = position + 1 - (matches[i].Index + matches[i].Length);
            }

            return new SourceLocation { Line = line, Column = column };
        }
示例#2
0
 public Lexer(Source source)
 {
     _source = source;
 }
示例#3
0
 /// <summary>
 /// Given a Source object, this returns a Lexer for that source.
 /// A Lexer is a function that acts like a generator in that every time
 /// it is called, it returns the next token in the Source. Assuming the
 /// source lexes, the final Token emitted by the lexer will be of kind
 /// EOF, after which the lexer will repeatedly return EOF tokens whenever
 /// called.
 ///
 /// The argument to the lexer function is optional, and can be used to
 /// rewind or fast forward the lexer to a new position in the source.
 /// </summary>
 /// <param name="source">The source.</param>
 /// <returns></returns>
 public Lexer Create(Source source)
 {
     return new Lexer(source);
 }
示例#4
0
        /**
         * Gets the next token from the source starting at the given position.
         *
         * This skips over whitespace and comments until it finds the next lexable
         * token, then lexes punctuators immediately or calls the appropriate helper
         * function for more complicated tokens.
         */
        private Token readToken(Source source, int fromPosition)
        {
            var body = source.Body;
            var bodyLength = body.Length;

            var position = positionAfterWhitespace(body, fromPosition);

            if (position >= bodyLength)
            {
                return makeToken(TokenKind.EOF, position, position);
            }

            int code = body[position];

            // SourceCharacter
            if (code < 0x0020 && code != 0x0009 && code != 0x000A && code != 0x000D)
            {
                throw Errors.GraphQLError.SyntaxError(
                  source,
                  position,
                  String.Format("Invalid character {0}.", printCharCode(code))
                );
            }

            switch (code)
            {
                // !
                case 33: return makeToken(TokenKind.BANG, position, position + 1);
                // $
                case 36: return makeToken(TokenKind.DOLLAR, position, position + 1);
                // (
                case 40: return makeToken(TokenKind.PAREN_L, position, position + 1);
                // )
                case 41: return makeToken(TokenKind.PAREN_R, position, position + 1);
                // .
                case 46:
                    if (body[position + 1] == 46 &&
                        body[position + 2] == 46)
                    {
                        return makeToken(TokenKind.SPREAD, position, position + 3);
                    }
                    break;
                // :
                case 58: return makeToken(TokenKind.COLON, position, position + 1);
                // =
                case 61: return makeToken(TokenKind.EQUALS, position, position + 1);
                // @
                case 64: return makeToken(TokenKind.AT, position, position + 1);
                // [
                case 91: return makeToken(TokenKind.BRACKET_L, position, position + 1);
                // ]
                case 93: return makeToken(TokenKind.BRACKET_R, position, position + 1);
                // {
                case 123: return makeToken(TokenKind.BRACE_L, position, position + 1);
                // |
                case 124: return makeToken(TokenKind.PIPE, position, position + 1);
                // }
                case 125: return makeToken(TokenKind.BRACE_R, position, position + 1);
                // A-Z
                case 65:
                case 66:
                case 67:
                case 68:
                case 69:
                case 70:
                case 71:
                case 72:
                case 73:
                case 74:
                case 75:
                case 76:
                case 77:
                case 78:
                case 79:
                case 80:
                case 81:
                case 82:
                case 83:
                case 84:
                case 85:
                case 86:
                case 87:
                case 88:
                case 89:
                case 90:
                // _
                case 95:
                // a-z
                case 97:
                case 98:
                case 99:
                case 100:
                case 101:
                case 102:
                case 103:
                case 104:
                case 105:
                case 106:
                case 107:
                case 108:
                case 109:
                case 110:
                case 111:
                case 112:
                case 113:
                case 114:
                case 115:
                case 116:
                case 117:
                case 118:
                case 119:
                case 120:
                case 121:
                case 122:
                    return readName(source, position);
                // -
                case 45:
                // 0-9
                case 48:
                case 49:
                case 50:
                case 51:
                case 52:
                case 53:
                case 54:
                case 55:
                case 56:
                case 57:
                    return readNumber(source, position, code);
                // "
                case 34: return readString(source, position);
            }

            throw Errors.GraphQLError.SyntaxError(
              source,
              position,
              String.Format("Unexpected character \"{0}\".", printCharCode(code))
            );
        }
示例#5
0
        /// <summary>
        /// Reads a string token from the source file.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="start">The start.</param>
        /// <remarks>
        /// "([^"\\\u000A\u000D\u2028\u2029]|(\\(u[0-9a-fA-F]{4}|["\\/bfnrt])))*"
        /// </remarks>
        Token readString(Source source, int start)
        {
            var body = source.Body;
            var position = start + 1;
            var chunkStart = position;
            var code = 0;
            var value = String.Empty;

            while (
              position < body.Length &&
              (code = body.CharCodeAt(position)) != -1 &&
              // not LineTerminator
              code != 0x000A && code != 0x000D &&
              // not Quote (")
              code != 34
            )
            {
                // SourceCharacter
                if (code < 0x0020 && code != 0x0009)
                {
                    throw Errors.GraphQLError.SyntaxError(
                      source,
                      position,
                      String.Format("Invalid character within String: {0}.", printCharCode(code))
              );
                }

                ++position;
                if (code == 92)
                { // \
                    value += body.Slice(chunkStart, position - 1);
                    code = body[position];
                    switch (code)
                    {
                        case 34: value += '"'; break;
                        case 47: value += '/'; break;
                        case 92: value += '\\'; break;
                        case 98: value += '\b'; break;
                        case 102: value += '\f'; break;
                        case 110: value += '\n'; break;
                        case 114: value += '\r'; break;
                        case 116: value += '\t'; break;
                        case 117: // u
                            var charCode = uniCharCode(
                              body[position + 1],
                              body[position + 2],
                              body[position + 3],
                              body[position + 4]
                            );
                            if (charCode < 0)
                            {
                                throw Errors.GraphQLError.SyntaxError(
                                  source,
                                  position,
                                  "Invalid character escape sequence: " +
                                  "\\u" + body.Slice(position + 1, position + 5) + "."
                    );
                            }
                            value += (char)charCode;
                            position += 4;
                            break;
                        default:
                            throw Errors.GraphQLError.SyntaxError(
                              source,
                              position,
                              String.Format("Invalid character escape sequence: \\{0}.", code)
                  );
                    }
                    ++position;
                    chunkStart = position;
                }
            }

            if (code != 34)
            { // quote (")
                throw Errors.GraphQLError.SyntaxError(source, position, "Unterminated string.");
            }

            value += body.Slice(chunkStart, position);
            return makeToken(TokenKind.STRING, start, position + 1, value);
        }
示例#6
0
        /**
         * Reads a number token from the source file, either a float
         * or an int depending on whether a decimal point appears.
         *
         * Int:   -?(0|[1-9][0-9]*)
         * Float: -?(0|[1-9][0-9]*)(\.[0-9]+)?((E|e)(+|-)?[0-9]+)?
         */
        private Token readNumber(Source source, int start, int firstCode)
        {
            var code = firstCode;
            var body = source.Body;
            var position = start;
            var isFloat = false;

            if (code == 45)
            { // -
                code = body[++position];
            }

            if (code == 48)
            { // 0
                code = body[++position];
                if (code >= 48 && code <= 57)
                {
                    throw Errors.GraphQLError.SyntaxError(
                      source,
                      position,
                      String.Format("Invalid number, unexpected digit after 0: {0}", printCharCode(code))
                    );
                }
            }
            else
            {
                position = readDigits(source, position, code);
                code = body[position];
            }

            if (code == 46)
            { // .
                isFloat = true;

                code = body[++position];
                position = readDigits(source, position, code);
                code = body[position];
            }

            if (code == 69 || code == 101)
            { // E e
                isFloat = true;

                code = body[++position];
                if (code == 43 || code == 45)
                { // + -
                    code = body[++position];
                }
                position = readDigits(source, position, code);
            }

            return makeToken(
              isFloat ? TokenKind.FLOAT : TokenKind.INT,
              start,
              position,
              body.Slice(start, position)
            );
        }
示例#7
0
 /**
  * Reads an alphanumeric + underscore name from the source.
  *
  * [_A-Za-z][_0-9A-Za-z]*
  */
 Token readName(Source source, int position)
 {
     var body = source.Body;
     var bodyLength = body.Length;
     var end = position + 1;
     var code = 0;
     while (
       end != bodyLength &&
       (code = body.CharCodeAt(end)) != -1 &&
       (
         code == 95 || // _
         code >= 48 && code <= 57 || // 0-9
         code >= 65 && code <= 90 || // A-Z
         code >= 97 && code <= 122 // a-z
       )
     )
     {
         ++end;
     }
     return makeToken(
       TokenKind.NAME,
       position,
       end,
       body.Slice(position, end)
     );
 }
示例#8
0
 /**
  * Returns the new position in the source after reading digits.
  */
 int readDigits(Source source, int start, int firstCode)
 {
     var body = source.Body;
     var position = start;
     var code = firstCode;
     if (code >= 48 && code <= 57)
     { // 0 - 9
         do
         {
             code = body[++position];
         } while (code >= 48 && code <= 57); // 0 - 9
         return position;
     }
     throw Errors.GraphQLError.SyntaxError(
       source,
       position,
       String.Format("Invalid number, expected digit but got: ${0}.", printCharCode(code))
       );
 }