UNPKG

graphql

Version:

A Query Language and Runtime which can target any service.

417 lines (381 loc) 12 kB
/* @flow / /** * Copyright (c) 2015, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ 'use strict'; /** * A representation of a lexed Token. Value is optional, is it is * not needed for punctuators like BANG or PAREN_L. */ var _Object$defineProperty = require('babel-runtime/core-js/object/define-property')['default']; _Object$defineProperty(exports, '__esModule', { value: true }); exports.lex = lex; exports.getTokenDesc = getTokenDesc; exports.getTokenKindDesc = getTokenKindDesc; var _error = require('./error'); /** * 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 omitted 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. */ function lex(source) { var prevPosition = 0; return function nextToken(resetPosition) { var token = readToken(source, resetPosition === undefined ? prevPosition : resetPosition); prevPosition = token.end; return token; }; } /** * An enum describing the different kinds of tokens that the lexer omits. */ var TokenKind = { EOF: 1, BANG: 2, DOLLAR: 3, PAREN_L: 4, PAREN_R: 5, SPREAD: 6, COLON: 7, EQUALS: 8, AT: 9, BRACKET_L: 10, BRACKET_R: 11, BRACE_L: 12, PIPE: 13, BRACE_R: 14, NAME: 15, VARIABLE: 16, INT: 17, FLOAT: 18, STRING: 19 }; exports.TokenKind = TokenKind; /** * A helper function to describe a token as a string for debugging */ function getTokenDesc(token) { return token.value ? '' + getTokenKindDesc(token.kind) + ' "' + token.value + '"' : getTokenKindDesc(token.kind); } /** * A helper function to describe a token kind as a string for debugging */ function getTokenKindDesc(kind) { return tokenDescription[kind]; } var tokenDescription = {}; tokenDescription[TokenKind.EOF] = 'EOF'; tokenDescription[TokenKind.BANG] = '!'; tokenDescription[TokenKind.DOLLAR] = '$'; tokenDescription[TokenKind.PAREN_L] = '('; tokenDescription[TokenKind.PAREN_R] = ')'; tokenDescription[TokenKind.SPREAD] = '...'; tokenDescription[TokenKind.COLON] = ':'; tokenDescription[TokenKind.EQUALS] = '='; tokenDescription[TokenKind.AT] = '@'; tokenDescription[TokenKind.BRACKET_L] = '['; tokenDescription[TokenKind.BRACKET_R] = ']'; tokenDescription[TokenKind.BRACE_L] = '{'; tokenDescription[TokenKind.PIPE] = '|'; tokenDescription[TokenKind.BRACE_R] = '}'; tokenDescription[TokenKind.NAME] = 'Name'; tokenDescription[TokenKind.VARIABLE] = 'Variable'; tokenDescription[TokenKind.INT] = 'Int'; tokenDescription[TokenKind.FLOAT] = 'Float'; tokenDescription[TokenKind.STRING] = 'String'; var charCodeAt = String.prototype.charCodeAt; var fromCharCode = String.fromCharCode; var slice = String.prototype.slice; /** * Helper function for constructing the Token object. */ function makeToken(kind, start, end, value) { return { kind: kind, start: start, end: end, value: value }; } /** * 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 * fucntion for more complicated tokens. */ function readToken(source, fromPosition) { var body = source.body; var bodyLength = body.length; var position = positionAfterWhitespace(body, fromPosition); var code = charCodeAt.call(body, position); if (position >= bodyLength) { return makeToken(TokenKind.EOF, position, position); } 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 (charCodeAt.call(body, position + 1) === 46 && charCodeAt.call(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 (0, _error.error)(source, position, 'Unexpected character "' + fromCharCode(code) + '"'); } /** * Reads from body starting at startPosition until it finds a non-whitespace * or commented character, then returns the position of that character for * lexing. */ function positionAfterWhitespace(body, startPosition) { var bodyLength = body.length; var position = startPosition; while (position < bodyLength) { var code = charCodeAt.call(body, position); // Skip whitespace if (code === 32 || // space code === 44 || // comma code === 160 || // '\xa0' code === 8232 || // line separator code === 8233 || // paragraph separator code > 8 && code < 14 // whitespace ) { ++position; // Skip comments } else if (code === 35) { // # ++position; while (position < bodyLength && (code = charCodeAt.call(body, position)) && code !== 10 && code !== 13 && code !== 8232 && code !== 8233) { ++position; } } else { break; } } return position; } /** * 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-?[0-9]+)? */ function readNumber(source, start, firstCode) { var code = firstCode; var body = source.body; var position = start; var isFloat = false; if (code === 45) { // - code = charCodeAt.call(body, ++position); } if (code === 48) { // 0 code = charCodeAt.call(body, ++position); } else if (code >= 49 && code <= 57) { // 1 - 9 do { code = charCodeAt.call(body, ++position); } while (code >= 48 && code <= 57); // 0 - 9 } else { throw (0, _error.error)(source, position, 'Invalid number'); } if (code === 46) { // . isFloat = true; code = charCodeAt.call(body, ++position); if (code >= 48 && code <= 57) { // 0 - 9 do { code = charCodeAt.call(body, ++position); } while (code >= 48 && code <= 57); // 0 - 9 } else { throw (0, _error.error)(source, position, 'Invalid number'); } if (code === 101) { // e code = charCodeAt.call(body, ++position); if (code === 45) { // - code = charCodeAt.call(body, ++position); } if (code >= 48 && code <= 57) { // 0 - 9 do { code = charCodeAt.call(body, ++position); } while (code >= 48 && code <= 57); // 0 - 9 } else { throw (0, _error.error)(source, position, 'Invalid number'); } } } return makeToken(isFloat ? TokenKind.FLOAT : TokenKind.INT, start, position, slice.call(body, start, position)); } /** * Reads a string token from the source file. * * "([^"\\\u000A\u000D\u2028\u2029]|(\\(u[0-9a-fA-F]{4}|["\\/bfnrt])))*" */ function readString(source, start) { var body = source.body; var position = start + 1; var chunkStart = position; var code; var value = ''; while (position < body.length && (code = charCodeAt.call(body, position)) && code !== 34 && code !== 10 && code !== 13 && code !== 8232 && code !== 8233) { ++position; if (code === 92) { // \ value += slice.call(body, chunkStart, position - 1); code = charCodeAt.call(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: var charCode = uniCharCode(charCodeAt.call(body, position + 1), charCodeAt.call(body, position + 2), charCodeAt.call(body, position + 3), charCodeAt.call(body, position + 4)); if (charCode < 0) { throw (0, _error.error)(source, position, 'Bad character escape sequence'); } value += fromCharCode(charCode); position += 4; break; default: throw (0, _error.error)(source, position, 'Bad character escape sequence'); } ++position; chunkStart = position; } } if (code !== 34) { throw (0, _error.error)(source, position, 'Unterminated string'); } value += slice.call(body, chunkStart, position); return makeToken(TokenKind.STRING, start, position + 1, value); } /** * Converts four hexidecimal chars to the integer that the * string represents. For example, uniCharCode('0','0','0','f') * will return 15, and uniCharCode('0','0','f','f') returns 255. * * Returns a negative number on error, if a char was invalid. * * This is implemented by noting that char2hex() returns -1 on error, * which means the result of ORing the char2hex() will also be negative. */ function uniCharCode(a, b, c, d) { return char2hex(a) << 12 | char2hex(b) << 8 | char2hex(c) << 4 | char2hex(d); } /** * Converts a hex character to its integer value. * '0' becomes 0, '9' becomes 9 * 'A' becomes 10, 'F' becomes 15 * 'a' becomes 10, 'f' becomes 15 * * Returns -1 on error. */ function char2hex(a) { return a >= 48 && a <= 57 ? a - 48 : // 0-9 a >= 65 && a <= 70 ? a - 55 : // A-F a >= 97 && a <= 102 ? a - 87 : // a-f -1; } /** * Reads an alphanumeric + underscore name from the source. * * [_A-Za-z][_0-9A-Za-z]* */ function readName(source, position) { var body = source.body; var bodyLength = body.length; var end = position + 1; var code; while (end !== bodyLength && (code = charCodeAt.call(body, end)) && (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, slice.call(body, position, end)); }