func parseObjectField(parser *Parser, isConst bool, fieldNames map[string]bool) (*ast.ObjectField, string, error) { start := parser.Token.Start name, err := parseName(parser) if err != nil { return nil, "", err } fieldName := name.Value if _, ok := fieldNames[fieldName]; ok { descp := fmt.Sprintf("Duplicate input object field %v.", fieldName) return nil, "", gqlerrors.NewSyntaxError(parser.Source, start, descp) } _, err = expect(parser, lexer.TokenKind[lexer.COLON]) if err != nil { return nil, "", err } value, err := parseValueLiteral(parser, isConst) if err != nil { return nil, "", err } return ast.NewObjectField(&ast.ObjectField{ Name: name, Value: value, Loc: loc(parser, start), }), fieldName, nil }
// If the next token is a keyword with the given value, return that token after // advancing the parser. Otherwise, do not change the parser state and return false. func expectKeyWord(parser *Parser, value string) (lexer.Token, error) { token := parser.Token if token.Kind == lexer.TokenKind[lexer.NAME] && token.Value == value { err := advance(parser) return token, err } descp := fmt.Sprintf("Expected \"%s\", found %s", value, lexer.GetTokenDesc(token)) return token, gqlerrors.NewSyntaxError(parser.Source, token.Start, descp) }
// If the next token is of the given kind, return that token after advancing // the parser. Otherwise, do not change the parser state and return false. func expect(parser *Parser, kind int) (lexer.Token, error) { token := parser.Token if token.Kind == kind { err := advance(parser) return token, err } descp := fmt.Sprintf("Expected %s, found %s", lexer.GetTokenKindDesc(kind), lexer.GetTokenDesc(token)) return token, gqlerrors.NewSyntaxError(parser.Source, token.Start, descp) }
// 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]+)? func readNumber(s *source.Source, start int, firstCode rune, codeLength int) (Token, error) { code := firstCode body := s.Body position := start isFloat := false if code == '-' { // - position += codeLength code, codeLength = runeAt(body, position) } if code == '0' { // 0 position += codeLength code, codeLength = runeAt(body, position) if code >= '0' && code <= '9' { description := fmt.Sprintf("Invalid number, unexpected digit after 0: %v.", printCharCode(code)) return Token{}, gqlerrors.NewSyntaxError(s, position, description) } } else { p, err := readDigits(s, position, code, codeLength) if err != nil { return Token{}, err } position = p code, codeLength = runeAt(body, position) } if code == '.' { // . isFloat = true position += codeLength code, codeLength = runeAt(body, position) p, err := readDigits(s, position, code, codeLength) if err != nil { return Token{}, err } position = p code, codeLength = runeAt(body, position) } if code == 'E' || code == 'e' { // E e isFloat = true position += codeLength code, codeLength = runeAt(body, position) if code == '+' || code == '-' { // + - position += codeLength code, codeLength = runeAt(body, position) } p, err := readDigits(s, position, code, codeLength) if err != nil { return Token{}, err } position = p } kind := TokenKind[INT] if isFloat { kind = TokenKind[FLOAT] } return makeToken(kind, start, position, string(body[start:position])), nil }
// Helper function for creating an error when an unexpected lexed token // is encountered. func unexpected(parser *Parser, atToken lexer.Token) error { var token lexer.Token if (atToken == lexer.Token{}) { token = parser.Token } else { token = parser.Token } description := fmt.Sprintf("Unexpected %v", lexer.GetTokenDesc(token)) return gqlerrors.NewSyntaxError(parser.Source, token.Start, description) }
// 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]+)? func readNumber(s *source.Source, start int, firstCode rune) (Token, error) { code := firstCode body := s.Body position := start isFloat := false if code == 45 { // - position += 1 code = charCodeAt(body, position) } if code == 48 { // 0 position += 1 code = charCodeAt(body, position) if code >= 48 && code <= 57 { description := fmt.Sprintf("Invalid number, unexpected digit after 0: \"%c\".", code) return Token{}, gqlerrors.NewSyntaxError(s, position, description) } } else { p, err := readDigits(s, position, code) if err != nil { return Token{}, err } position = p code = charCodeAt(body, position) } if code == 46 { // . isFloat = true position += 1 code = charCodeAt(body, position) p, err := readDigits(s, position, code) if err != nil { return Token{}, err } position = p code = charCodeAt(body, position) } if code == 69 || code == 101 { // E e isFloat = true position += 1 code = charCodeAt(body, position) if code == 43 || code == 45 { // + - position += 1 code = charCodeAt(body, position) } p, err := readDigits(s, position, code) if err != nil { return Token{}, err } position = p } kind := TokenKind[INT] if isFloat { kind = TokenKind[FLOAT] } return makeToken(kind, start, position, body[start:position]), nil }
// Returns the new position in the source after reading digits. func readDigits(s *source.Source, start int, firstCode rune, codeLength int) (int, error) { body := s.Body position := start code := firstCode if code >= '0' && code <= '9' { // 0 - 9 for { if code >= '0' && code <= '9' { // 0 - 9 position += codeLength code, codeLength = runeAt(body, position) continue } else { break } } return position, nil } var description string description = fmt.Sprintf("Invalid number, expected digit but got: %v.", printCharCode(code)) return position, gqlerrors.NewSyntaxError(s, position, description) }
// Returns the new position in the source after reading digits. func readDigits(s *source.Source, start int, firstCode rune) (int, error) { body := s.Body position := start code := firstCode if code >= 48 && code <= 57 { // 0 - 9 for { if code >= 48 && code <= 57 { // 0 - 9 position += 1 code = charCodeAt(body, position) continue } else { break } } return position, nil } var description string if code != 0 { description = fmt.Sprintf("Invalid number, expected digit but got: \"%c\".", code) } else { description = fmt.Sprintf("Invalid number, expected digit but got: EOF.") } return position, gqlerrors.NewSyntaxError(s, position, description) }
func readToken(s *source.Source, fromPosition int) (Token, error) { body := s.Body bodyLength := len(body) position, runePosition := positionAfterWhitespace(body, fromPosition) if position >= bodyLength { return makeToken(TokenKind[EOF], position, position, ""), nil } code, codeLength := runeAt(body, position) // SourceCharacter if code < 0x0020 && code != 0x0009 && code != 0x000A && code != 0x000D { return Token{}, gqlerrors.NewSyntaxError(s, runePosition, fmt.Sprintf(`Invalid character %v`, printCharCode(code))) } switch code { // ! case '!': return makeToken(TokenKind[BANG], position, position+1, ""), nil // $ case '$': return makeToken(TokenKind[DOLLAR], position, position+1, ""), nil // ( case '(': return makeToken(TokenKind[PAREN_L], position, position+1, ""), nil // ) case ')': return makeToken(TokenKind[PAREN_R], position, position+1, ""), nil // . case '.': next1, _ := runeAt(body, position+1) next2, _ := runeAt(body, position+2) if next1 == '.' && next2 == '.' { return makeToken(TokenKind[SPREAD], position, position+3, ""), nil } break // : case ':': return makeToken(TokenKind[COLON], position, position+1, ""), nil // = case '=': return makeToken(TokenKind[EQUALS], position, position+1, ""), nil // @ case '@': return makeToken(TokenKind[AT], position, position+1, ""), nil // [ case '[': return makeToken(TokenKind[BRACKET_L], position, position+1, ""), nil // ] case ']': return makeToken(TokenKind[BRACKET_R], position, position+1, ""), nil // { case '{': return makeToken(TokenKind[BRACE_L], position, position+1, ""), nil // | case '|': return makeToken(TokenKind[PIPE], position, position+1, ""), nil // } case '}': return makeToken(TokenKind[BRACE_R], position, position+1, ""), nil // A-Z case 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z': return readName(s, position, runePosition), nil // _ // a-z case '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z': return readName(s, position, runePosition), nil // - // 0-9 case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': token, err := readNumber(s, position, code, codeLength) if err != nil { return token, err } return token, nil // " case '"': token, err := readString(s, position) if err != nil { return token, err } return token, nil } description := fmt.Sprintf("Unexpected character %v.", printCharCode(code)) return Token{}, gqlerrors.NewSyntaxError(s, runePosition, description) }
func readString(s *source.Source, start int) (Token, error) { body := s.Body position := start + 1 runePosition := start + 1 chunkStart := position var code rune var n int var valueBuffer bytes.Buffer for { code, n = runeAt(body, position) if position < len(body) && // not LineTerminator code != 0x000A && code != 0x000D && // not Quote (") code != '"' { // SourceCharacter if code < 0x0020 && code != 0x0009 { return Token{}, gqlerrors.NewSyntaxError(s, runePosition, fmt.Sprintf(`Invalid character within String: %v.`, printCharCode(code))) } position += n runePosition++ if code == '\\' { // \ valueBuffer.Write(body[chunkStart : position-1]) code, n = runeAt(body, position) switch code { case '"': valueBuffer.WriteRune('"') break case '/': valueBuffer.WriteRune('/') break case '\\': valueBuffer.WriteRune('\\') break case 'b': valueBuffer.WriteRune('\b') break case 'f': valueBuffer.WriteRune('\f') break case 'n': valueBuffer.WriteRune('\n') break case 'r': valueBuffer.WriteRune('\r') break case 't': valueBuffer.WriteRune('\t') break case 'u': // Check if there are at least 4 bytes available if len(body) <= position+4 { return Token{}, gqlerrors.NewSyntaxError(s, runePosition, fmt.Sprintf("Invalid character escape sequence: "+ "\\u%v", string(body[position+1:]))) } charCode := uniCharCode( rune(body[position+1]), rune(body[position+2]), rune(body[position+3]), rune(body[position+4]), ) if charCode < 0 { return Token{}, gqlerrors.NewSyntaxError(s, runePosition, fmt.Sprintf("Invalid character escape sequence: "+ "\\u%v", string(body[position+1:position+5]))) } valueBuffer.WriteRune(charCode) position += 4 runePosition += 4 break default: return Token{}, gqlerrors.NewSyntaxError(s, runePosition, fmt.Sprintf(`Invalid character escape sequence: \\%c.`, code)) } position += n runePosition++ chunkStart = position } continue } else { break } } if code != '"' { // quote (") return Token{}, gqlerrors.NewSyntaxError(s, runePosition, "Unterminated string.") } stringContent := body[chunkStart:position] valueBuffer.Write(stringContent) value := valueBuffer.String() return makeToken(TokenKind[STRING], start, position+1, value), nil }
func readToken(s *source.Source, fromPosition int) (Token, error) { body := s.Body bodyLength := len(body) position := positionAfterWhitespace(body, fromPosition) code := charCodeAt(body, position) if position >= bodyLength { return makeToken(TokenKind[EOF], position, position, ""), nil } switch code { // ! case 33: return makeToken(TokenKind[BANG], position, position+1, ""), nil // $ case 36: return makeToken(TokenKind[DOLLAR], position, position+1, ""), nil // ( case 40: return makeToken(TokenKind[PAREN_L], position, position+1, ""), nil // ) case 41: return makeToken(TokenKind[PAREN_R], position, position+1, ""), nil // . case 46: if charCodeAt(body, position+1) == 46 && charCodeAt(body, position+2) == 46 { return makeToken(TokenKind[SPREAD], position, position+3, ""), nil } break // : case 58: return makeToken(TokenKind[COLON], position, position+1, ""), nil // = case 61: return makeToken(TokenKind[EQUALS], position, position+1, ""), nil // @ case 64: return makeToken(TokenKind[AT], position, position+1, ""), nil // [ case 91: return makeToken(TokenKind[BRACKET_L], position, position+1, ""), nil // ] case 93: return makeToken(TokenKind[BRACKET_R], position, position+1, ""), nil // { case 123: return makeToken(TokenKind[BRACE_L], position, position+1, ""), nil // | case 124: return makeToken(TokenKind[PIPE], position, position+1, ""), nil // } case 125: return makeToken(TokenKind[BRACE_R], position, position+1, ""), nil // A-Z case 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90: return readName(s, position), nil // _ // a-z case 95, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122: return readName(s, position), nil // - // 0-9 case 45, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57: token, err := readNumber(s, position, code) if err != nil { return token, err } else { return token, nil } // " case 34: token, err := readString(s, position) if err != nil { return token, err } return token, nil } description := fmt.Sprintf("Unexpected character \"%c\".", code) return Token{}, gqlerrors.NewSyntaxError(s, position, description) }
func readString(s *source.Source, start int) (Token, error) { body := s.Body position := start + 1 chunkStart := position var code rune var value string for { code = charCodeAt(body, position) if position < len(body) && code != 34 && code != 10 && code != 13 && code != 0x2028 && code != 0x2029 { position += 1 if code == 92 { // \ value += body[chunkStart : position-1] code = charCodeAt(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: charCode := uniCharCode( charCodeAt(body, position+1), charCodeAt(body, position+2), charCodeAt(body, position+3), charCodeAt(body, position+4), ) if charCode < 0 { return Token{}, gqlerrors.NewSyntaxError(s, position, "Bad character escape sequence.") } value += fmt.Sprintf("%c", charCode) position += 4 break default: return Token{}, gqlerrors.NewSyntaxError(s, position, "Bad character escape sequence.") } position += 1 chunkStart = position } continue } else { break } } if code != 34 { return Token{}, gqlerrors.NewSyntaxError(s, position, "Unterminated string.") } value += body[chunkStart:position] return makeToken(TokenKind[STRING], start, position+1, value), nil }