├── v.mod ├── fixtures ├── variable.graphql ├── pokeapi.graphql └── simple.graphql ├── .editorconfig ├── .gitattributes ├── .gitignore ├── src ├── main.example ├── location.v ├── source.v ├── parser_test.v ├── token_kind.v ├── directive_location.v ├── character_classes.v ├── document_keys.v ├── ast.v ├── kinds.v ├── block_string.v ├── graphql.md ├── node.v ├── lexer.v ├── src.graphql.md └── parser.v ├── .github └── workflows │ └── ci.yml ├── LICENSE └── README.md /v.mod: -------------------------------------------------------------------------------- 1 | Module{ 2 | name: 'graphql' 3 | description: 'GrahQL parser for V' 4 | version: '0.1.0' 5 | license: 'MIT' 6 | dependencies: [] 7 | } 8 | -------------------------------------------------------------------------------- /fixtures/variable.graphql: -------------------------------------------------------------------------------- 1 | query searchPokemon($name: String) { 2 | pokemon(name: $name) { 3 | number 4 | name 5 | image 6 | } 7 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = lf 4 | insert_final_newline = true 5 | trim_trailing_whitespace = true 6 | 7 | [*.v] 8 | indent_style = tab 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | *.bat eol=crlf 3 | 4 | **/*.v linguist-language=V 5 | **/*.vv linguist-language=V 6 | **/*.vsh linguist-language=V 7 | **/v.mod linguist-language=V 8 | -------------------------------------------------------------------------------- /fixtures/pokeapi.graphql: -------------------------------------------------------------------------------- 1 | query Pokemons { 2 | pokemons { 3 | id 4 | name 5 | } 6 | } 7 | 8 | query PokemonList { 9 | pokemons(first: 151) { 10 | name 11 | classification 12 | id 13 | number 14 | image 15 | types 16 | } 17 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | main 3 | something 4 | *.exe 5 | *.exe~ 6 | *.so 7 | *.dylib 8 | *.dll 9 | 10 | # Ignore binary output folders 11 | bin/ 12 | 13 | # Ignore common editor/system specific metadata 14 | .DS_Store 15 | .idea/ 16 | .vscode/ 17 | *.iml 18 | 19 | # ENV 20 | .env 21 | 22 | # vweb and database 23 | *.db 24 | *.js 25 | 26 | log.txt 27 | graphql-v -------------------------------------------------------------------------------- /src/main.example: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | import graphql 4 | import os 5 | import time 6 | 7 | fn main() { 8 | watch := time.new_stopwatch() 9 | println('Running Github GQL fixture') 10 | path := os.real_path('./fixtures/github.graphql') 11 | 12 | file_content := os.read_file(path) or { panic(err) } 13 | println('Reading file with length ${file_content.len}') 14 | 15 | doc := graphql.parse(file_content, none) or { panic('Error: ${err}') } 16 | println('DocumentNode token count: ${doc.token_count}') 17 | 18 | end := watch.elapsed() 19 | println('Time elapsed: ${end}') 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | build: 9 | strategy: 10 | matrix: 11 | os: ["ubuntu-latest", "macos-latest"] 12 | runs-on: ${{ matrix.os }} 13 | steps: 14 | - name: Install V 15 | uses: vlang/setup-v@v1.4 16 | with: 17 | check-latest: true 18 | 19 | - name: Checkout ${{ github.event.repository.name }} 20 | uses: actions/checkout@v2 21 | 22 | - name: Check if code is formatted 23 | run: | 24 | v fmt -diff . 25 | v fmt -verify . 26 | 27 | - name: Build ${{ github.event.repository.name }} 28 | run: v . -shared 29 | 30 | - name: Run Tests 31 | run: v test . -------------------------------------------------------------------------------- /src/location.v: -------------------------------------------------------------------------------- 1 | module graphql 2 | 3 | import regex 4 | 5 | // LineRegExp regex pattern to match line breaks 6 | const line_reg_exp = r'/\r\n|[\n\r]/g' 7 | 8 | struct SourceLocation { 9 | pub: 10 | line i64 = 1 11 | column i64 = 1 12 | } 13 | 14 | fn compile_and_get_rex(source string) ![]string { 15 | mut re := regex.regex_opt(graphql.line_reg_exp)! 16 | 17 | return re.find_all_str(source) 18 | } 19 | 20 | // getLocation takes a Source and a UTF-8 character offset, 21 | // and returns the corresponding line and column as a SourceLocation. 22 | pub fn get_location(source Source, position int) !SourceLocation { 23 | mut last_line_start := 0 24 | mut line := 1 25 | 26 | for i, matched in compile_and_get_rex(source.body)! { 27 | // // invariant @match.index is int 28 | // println('>>>>>>> MATCHED LENGTH ${matched.len} (${matched})') 29 | 30 | if i >= position { 31 | break 32 | } 33 | 34 | last_line_start = i + matched.len 35 | line += 1 36 | } 37 | 38 | return SourceLocation{ 39 | line: line 40 | column: position + 1 - last_line_start 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/source.v: -------------------------------------------------------------------------------- 1 | module graphql 2 | 3 | struct LocationOffset { 4 | pub: 5 | line int 6 | column int 7 | } 8 | 9 | // A representation of source input to GraphQL. The `name` and `locationOffset` parameters are 10 | // optional, but they are useful for clients who store GraphQL documents in source files. 11 | // For example, if the GraphQL input starts at line 40 in a file named `Foo.graphql`, it might 12 | // be useful for `name` to be `"Foo.graphql"` and location to be `{ line: 40, column: 1 }`. 13 | // The `line` and `column` properties in `locationOffset` are 1-indexed. 14 | pub struct Source { 15 | pub: 16 | body string 17 | name string 18 | location_offset LocationOffset 19 | } 20 | 21 | fn Source.new(body string, name ?string, location_offset ?LocationOffset) Source { 22 | return Source{ 23 | body: &body 24 | name: name or { 'GraphQL request' } 25 | location_offset: location_offset or { 26 | LocationOffset{ 27 | line: 1 28 | column: 1 29 | } 30 | } 31 | } 32 | } 33 | 34 | fn (s Source) get() string { 35 | return 'Source' 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Luiz Fonseca 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /fixtures/simple.graphql: -------------------------------------------------------------------------------- 1 | type Tweet { 2 | id: ID! 3 | # The tweet text. No more than 140 characters! 4 | body: String 5 | # When the tweet was published 6 | date: Date 7 | # Who published the tweet 8 | Author: User 9 | # Views, retweets, likes, etc 10 | Stats: Stat 11 | } 12 | 13 | type User { 14 | id: ID! 15 | username: String 16 | first_name: String 17 | last_name: String 18 | full_name: String 19 | name: String @deprecated 20 | avatar_url: Url 21 | } 22 | 23 | type Stat { 24 | views: Int 25 | likes: Int 26 | retweets: Int 27 | responses: Int 28 | } 29 | 30 | type Notification { 31 | id: ID 32 | date: Date 33 | type: String 34 | } 35 | 36 | type Meta { 37 | count: Int 38 | } 39 | 40 | scalar Url 41 | scalar Date 42 | 43 | type Query { 44 | Tweet(id: ID!): Tweet 45 | Tweets(limit: Int, skip: Int, sort_field: String, sort_order: String): [Tweet] 46 | TweetsMeta: Meta 47 | User(id: ID!): User 48 | Notifications(limit: Int): [Notification] 49 | NotificationsMeta: Meta 50 | } 51 | 52 | type Mutation { 53 | createTweet(body: String): Tweet 54 | deleteTweet(id: ID!): Tweet 55 | markTweetRead(id: ID!): Boolean 56 | } 57 | -------------------------------------------------------------------------------- /src/parser_test.v: -------------------------------------------------------------------------------- 1 | module graphql 2 | 3 | import os 4 | 5 | fn test_query_parse() { 6 | body := '{ 7 | node(id: 4) { 8 | id, 9 | name 10 | } 11 | }' 12 | 13 | result := parse(body, none) or { panic(err) } 14 | 15 | assert result.kind == Kind.document 16 | assert result.loc?.source.body == body 17 | } 18 | 19 | fn test_github_parse() ! { 20 | path := os.real_path('./fixtures/github.graphql') 21 | 22 | body := os.read_file(path)! 23 | 24 | result := parse(body, none) or { panic(err) } 25 | 26 | assert result.kind == Kind.document 27 | assert result.token_count == 19_882 28 | } 29 | 30 | fn test_simple_parse() ! { 31 | path := os.real_path('./fixtures/simple.graphql') 32 | 33 | body := os.read_file(path)! 34 | 35 | result := parse(body, none) or { panic(err) } 36 | 37 | assert result.kind == Kind.document 38 | assert result.token_count == 175 39 | } 40 | 41 | fn test_pokeapi_parse() ! { 42 | path := os.real_path('./fixtures/pokeapi.graphql') 43 | 44 | body := os.read_file(path)! 45 | 46 | result := parse(body, none) or { panic(err) } 47 | 48 | assert result.kind == Kind.document 49 | assert result.token_count == 27 50 | } 51 | 52 | fn test_variable_definition_parse() ! { 53 | body := 'query getUser(\$id: ID! = "default") { 54 | user(id: \$id) { 55 | name 56 | } 57 | }' 58 | 59 | result := parse(body, none) or { panic(err) } 60 | 61 | assert result.kind == Kind.document 62 | assert result.token_count == 21 63 | } -------------------------------------------------------------------------------- /src/token_kind.v: -------------------------------------------------------------------------------- 1 | module graphql 2 | 3 | const _token_kind_map = { 4 | TokenKind.sof: '' 5 | TokenKind.eof: '' 6 | TokenKind.bang: '!' 7 | TokenKind.question_mark: '?' 8 | TokenKind.dollar: '$' 9 | TokenKind.amp: '&' 10 | TokenKind.paren_l: '(' 11 | TokenKind.paren_r: ')' 12 | TokenKind.spread: '...' 13 | TokenKind.colon: ':' 14 | TokenKind.equals: '=' 15 | TokenKind.at: '@' 16 | TokenKind.bracket_l: '[' 17 | TokenKind.bracket_r: ']' 18 | TokenKind.brace_l: '{' 19 | TokenKind.pipe: '|' 20 | TokenKind.brace_r: '}' 21 | TokenKind.name: 'Name' 22 | TokenKind.integer: 'Int' 23 | TokenKind.float: 'Float' 24 | TokenKind.string_value: 'String' 25 | TokenKind.block_string: 'BlockString' 26 | TokenKind.comment: 'Comment' 27 | } 28 | 29 | // An exported enum describing the different kinds of tokens that the 30 | // lexer emits. 31 | pub enum TokenKind { 32 | sof 33 | eof 34 | bang 35 | question_mark 36 | dollar 37 | amp 38 | paren_l 39 | paren_r 40 | spread 41 | colon 42 | equals 43 | at 44 | bracket_l 45 | bracket_r 46 | brace_l 47 | pipe 48 | brace_r 49 | name 50 | integer 51 | float 52 | string_value 53 | block_string 54 | comment 55 | } 56 | 57 | // Returns the character value of the TokenKind enum 58 | pub fn (t TokenKind) gql_str() string { 59 | return graphql._token_kind_map[t] 60 | } 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL for the V language 2 | 3 | ## Current state 4 | 5 | Work in progress. 6 | 7 | - [x] Language Parser (lexer, parser implementation) 8 | - [x] Fixtures are covered 100% (GH, Simple) 9 | - [ ] Integration tests 10 | - [ ] Schema execute 11 | - [ ] Publish to VPM 12 | - [ ] Documentation 13 | 14 | ## Usage 15 | 16 | ### Parsing a .graphql definition file 17 | 18 | You can parse any string/file using the `.parse` function. 19 | It returns a [DocumentNode](./src/graphql/src.graphql.md#documentnode) with al the definitions that were parsed, in a tree structure. 20 | 21 | ```v 22 | import luizfonseca.graphql 23 | import os 24 | 25 | fn main() { 26 | // For files 27 | file := os.read_file('path-to-file.graphql') 28 | 29 | // For string inputs 30 | // input := 'query { 31 | // myQuery(id: 1) { 32 | // name 33 | // age 34 | // } 35 | //}' 36 | 37 | // max_tokens: number of tokens to parse. 38 | // Increasing this means supporting longer queries/schema definitions 39 | doc := graphql.parse(file, graphql.ParserOptions{ max_tokens: 25_000 }) 40 | 41 | dump(doc.token_count) 42 | dump(doc.definitions) 43 | } 44 | ``` 45 | 46 | ## Links 47 | 48 | - [The V programming language](https://vlang.io/) 49 | - [GraphQL](https://graphql.org/) 50 | 51 | ## Notes 52 | 53 | - This package is inspired by the work of the GraphQL.js package and as such is not yet perfected for the V lang. 54 | 55 | ## Contributing 56 | 57 | - Feedbacks/PRs are welcome, but keep in mind this is still a **work in progress**. 58 | 59 | ## Credits 60 | 61 | - [GraphQL](https://graphql.org/) 62 | - [GraphQL.js](https://graphql.org/graphql-js/) 63 | - “GraphQL” is a trademark managed by the [GraphQL Foundation](https://graphql.org/foundation/). 64 | -------------------------------------------------------------------------------- /src/directive_location.v: -------------------------------------------------------------------------------- 1 | module graphql 2 | 3 | const _directive_location_map = { 4 | DirectiveLocation.query: 'QUERY' 5 | DirectiveLocation.mutation: 'MUTATION' 6 | DirectiveLocation.subscription: 'SUBSCRIPTION' 7 | DirectiveLocation.field: 'FIELD' 8 | DirectiveLocation.fragment_definition: 'FRAGMENT_DEFINITION' 9 | DirectiveLocation.fragment_spread: 'FRAGMENT_SPREAD' 10 | DirectiveLocation.inline_fragment: 'INLINE_FRAGMENT' 11 | DirectiveLocation.variable_definition: 'VARIABLE_DEFINITION' 12 | DirectiveLocation.schema: 'SCHEMA' 13 | DirectiveLocation.scalar: 'SCALAR' 14 | DirectiveLocation.object: 'OBJECT' 15 | DirectiveLocation.field_definition: 'FIELD_DEFINITION' 16 | DirectiveLocation.argument_definition: 'ARGUMENT_DEFINITION' 17 | DirectiveLocation.@interface: 'INTERFACE' 18 | DirectiveLocation.@union: 'UNION' 19 | DirectiveLocation.@enum: 'ENUM' 20 | DirectiveLocation.enum_value: 'ENUM_VALUE' 21 | DirectiveLocation.input_object: 'INPUT_OBJECT' 22 | DirectiveLocation.input_field_definition: 'INPUT_FIELD_DEFINITION' 23 | } 24 | 25 | pub enum DirectiveLocation { 26 | // Request Definitions 27 | query 28 | mutation 29 | subscription 30 | field 31 | fragment_definition 32 | fragment_spread 33 | inline_fragment 34 | variable_definition 35 | // Type System Definitions 36 | schema 37 | scalar 38 | object 39 | field_definition 40 | argument_definition 41 | @interface 42 | @union 43 | @enum 44 | enum_value 45 | input_object 46 | input_field_definition 47 | } 48 | 49 | fn (d DirectiveLocation) gql_str() string { 50 | return graphql._directive_location_map[d] 51 | } 52 | -------------------------------------------------------------------------------- /src/character_classes.v: -------------------------------------------------------------------------------- 1 | module graphql 2 | 3 | /** 4 | * ``` 5 | * WhiteSpace :: 6 | * - "Horizontal Tab (U+0009)" 7 | * - "Space (U+0020)" 8 | * ``` 9 | * @internal 10 | */ 11 | fn is_white_space(code int) bool { 12 | return code == 0x0009 || code == 0x0020 13 | } 14 | 15 | /** 16 | * ``` 17 | * Digit :: one of 18 | * - `0` `1` `2` `3` `4` `5` `6` `7` `8` `9` 19 | * ``` 20 | * @internal 21 | */ 22 | fn is_digit(code int) bool { 23 | return code >= 0x0030 && code <= 0x0039 24 | } 25 | 26 | /** 27 | * ``` 28 | * Letter :: one of 29 | * - `A` `B` `C` `D` `E` `F` `G` `H` `I` `J` `K` `L` `M` 30 | * - `N` `O` `P` `Q` `R` `S` `T` `U` `V` `W` `X` `Y` `Z` 31 | * - `a` `b` `c` `d` `e` `f` `g` `h` `i` `j` `k` `l` `m` 32 | * - `n` `o` `p` `q` `r` `s` `t` `u` `v` `w` `x` `y` `z` 33 | * ``` 34 | * @internal 35 | */ 36 | fn is_letter(code int) bool { 37 | return (code >= 0x0061 && code <= 0x007a) || (code >= 0x0041 && code <= 0x005a) 38 | } 39 | 40 | /** 41 | * ``` 42 | * NameStart :: 43 | * - Letter 44 | * - `_` 45 | * ``` 46 | * @internal 47 | */ 48 | fn is_name_start(code u8) bool { 49 | return is_letter(code) || code == 0x005f 50 | } 51 | 52 | /** 53 | * ``` 54 | * NameContinue :: 55 | * - Letter 56 | * - Digit 57 | * - `_` 58 | * ``` 59 | * @internal 60 | */ 61 | fn is_name_continue(code u8) bool { 62 | return is_letter(code) || is_digit(code) || code == 0x005f 63 | } 64 | 65 | fn is_unicode_scalar_value(code int) bool { 66 | return (code >= 0x0000 && code <= 0xd7ff) || (code >= 0xe000 && code <= 0x10ffff) 67 | } 68 | 69 | /** 70 | * The GraphQL specification defines source text as a sequence of unicode scalar 71 | * values (which Unicode defines to exclude surrogate code points). However 72 | * JavaScript defines strings as a sequence of UTF-16 code units which may 73 | * include surrogates. A surrogate pair is a valid source character as it 74 | * encodes a supplementary code point (above U+FFFF), but unpaired surrogate 75 | * code points are not valid source characters. 76 | */ 77 | fn is_supplementary_code_point(body string, location int) bool { 78 | return is_leading_surrogate(body[location]) && is_trailing_surrogate(body[location + 1]) 79 | } 80 | 81 | fn is_leading_surrogate(code int) bool { 82 | return code >= 0xd800 && code <= 0xdbff 83 | } 84 | 85 | fn is_trailing_surrogate(code int) bool { 86 | return code >= 0xdc00 && code <= 0xdfff 87 | } 88 | -------------------------------------------------------------------------------- /src/document_keys.v: -------------------------------------------------------------------------------- 1 | module graphql 2 | 3 | // The set of allowed kind values for AST nodes. 4 | const _query_document_keys = { 5 | Kind.name: []string{} 6 | Kind.document: ['definitions'] 7 | Kind.operation_definition: ['name', 'variableDefinitions', 'directives', 'selectionSet'] 8 | Kind.variable: ['name'] 9 | Kind.variable_definition: ['variable', 'type', 'defaultValue', 'directives'] 10 | Kind.selection_set: ['selections'] 11 | Kind.field: ['alias', 'name', 'arguments', 'directives', 'selectionSet', 12 | 'nullabilityAssertion'] 13 | Kind.argument: ['name', 'value'] 14 | Kind.list_nullability_operator: ['nullabilityAssertion'] 15 | Kind.non_null_assertion: ['nullabilityAssertion'] 16 | Kind.error_boundary: ['nullabilityAssertion'] 17 | Kind.fragment_spread: ['name', 'directives'] 18 | Kind.inline_fragment: ['typeCondition', 'directives', 'selectionSet'] 19 | Kind.fragment_definition: ['name', 'variableDefinitions', 'typeCondition', 'directives', 20 | 'selectionSet'] 21 | Kind.int_value: [] 22 | Kind.float_value: [] 23 | Kind.string_value: [] 24 | Kind.boolean: [] 25 | Kind.null: [] 26 | Kind.enum_value: [] 27 | Kind.list: ['values'] 28 | Kind.object: ['fields'] 29 | Kind.object_field: ['name', 'value'] 30 | Kind.directive: ['name', 'arguments'] 31 | Kind.named_type: ['name'] 32 | // 33 | Kind.list_type: ['type'] 34 | Kind.non_null_type: ['type'] 35 | // 36 | Kind.schema_definition: ['description', 'directives', 'operationTypes'] 37 | Kind.operation_type_definition: ['type'] 38 | // 39 | Kind.scalar_type_definition: ['description', 'name', 'directives'] 40 | Kind.object_type_definition: ['description', 'name', 'interfaces', 'directives', 'fields'] 41 | Kind.field_definition: ['description', 'name', 'arguments', 'type', 'directives'] 42 | Kind.input_value_definition: ['description', 'name', 'type', 'defaultValue', 'directives'] 43 | Kind.interface_type_definition: ['description', 'name', 'interfaces', 'directives', 'fields'] 44 | Kind.union_type_definition: ['description', 'name', 'directives', 'types'] 45 | Kind.enum_type_definition: ['description', 'name', 'directives', 'values'] 46 | Kind.enum_value_definition: ['description', 'name', 'directives'] 47 | Kind.input_object_type_definition: ['description', 'name', 'directives', 'fields'] 48 | Kind.directive_definition: ['description', 'name', 'arguments', 'locations'] 49 | // 50 | Kind.schema_extension: ['directives', 'operationTypes'] 51 | Kind.scalar_type_extension: ['name', 'directives'] 52 | Kind.object_type_extension: ['name', 'interfaces', 'directives', 'fields'] 53 | Kind.interface_type_extension: ['name', 'interfaces', 'directives', 'fields'] 54 | Kind.union_type_extension: ['name', 'directives', 'types'] 55 | Kind.enum_type_extension: ['name', 'directives', 'values'] 56 | Kind.input_object_type_extension: ['name', 'directives', 'fields'] 57 | } 58 | -------------------------------------------------------------------------------- /src/ast.v: -------------------------------------------------------------------------------- 1 | module graphql 2 | 3 | pub struct Location { 4 | pub: 5 | // The character offset at which this Node begins. 6 | start int 7 | // The character offset at which this Node ends. 8 | end int 9 | // The Token at which this Node begins. 10 | start_token ?&Token 11 | // The Token at which this Node ends. 12 | end_token ?&Token 13 | // The Source document the AST represents. 14 | source Source 15 | } 16 | 17 | fn Location.new(start_token Token, end_token Token, source Source) Location { 18 | return Location{ 19 | start: start_token.start 20 | end: end_token.end 21 | start_token: &start_token 22 | end_token: &end_token 23 | source: source 24 | } 25 | } 26 | 27 | fn (l Location) get_symbol_tag() string { 28 | return 'Location' 29 | } 30 | 31 | fn (l Location) to_json() { 32 | // @todo 33 | } 34 | 35 | pub struct Token { 36 | pub: 37 | kind TokenKind 38 | // The character offset at which this Node begins. 39 | start int 40 | // The character offset at which this Node ends. 41 | end int 42 | // The 1-indexed line number on which this Token appears. 43 | line int 44 | // The 1-indexed column number at which this Token begins. 45 | column int 46 | // For non-punctuation tokens, represents the interpreted value of the token. 47 | //
48 | // **Note**: is `none` for punctuation tokens, but typed as string for 49 | // convenience in the parser. 50 | value ?string 51 | mut: 52 | // Tokens exist as nodes in a double-linked-list amongst all tokens 53 | // including ignored tokens. is always the first node and 54 | // the last. 55 | prev ?&Token 56 | next ?&Token 57 | } 58 | 59 | fn Token.new(kind TokenKind, start int, end int, line int, column int) Token { 60 | return Token{ 61 | kind: kind 62 | start: start 63 | end: end 64 | line: line 65 | column: column 66 | } 67 | } 68 | 69 | fn (t Token) get_symbol_tag() string { 70 | return 'Token' 71 | } 72 | 73 | fn (t Token) to_json() map[string]string { 74 | return { 75 | 'kind': t.kind.str() 76 | 'value': t.value or { '' } 77 | 'line': t.line.str() 78 | 'column': t.column.str() 79 | } 80 | } 81 | 82 | // All AST Nodes should implement a MUTable common loc property 83 | pub interface ASTNodeInterface { 84 | mut: 85 | loc ?Location 86 | } 87 | 88 | // The list of all possible AST node types. 89 | pub type ASTNode = ArgumentNode 90 | | BooleanValueNode 91 | | DirectiveDefinitionNode 92 | | DirectiveNode 93 | | DocumentNode 94 | | EnumTypeDefinitionNode 95 | | EnumTypeExtensionNode 96 | | EnumValueDefinitionNode 97 | | EnumValueNode 98 | | ErrorBoundaryNode 99 | | FieldDefinitionNode 100 | | FieldNode 101 | | FloatValueNode 102 | | FragmentDefinitionNode 103 | | FragmentSpreadNode 104 | | InlineFragmentNode 105 | | InputObjectTypeDefinitionNode 106 | | InputObjectTypeExtensionNode 107 | | InputValueDefinitionNode 108 | | IntValueNode 109 | | InterfaceTypeDefinitionNode 110 | | InterfaceTypeExtensionNode 111 | | ListNullabilityOperatorNode 112 | | ListTypeNode 113 | | ListValueNode 114 | | NameNode 115 | | NamedTypeNode 116 | | NonNullAssertionNode 117 | | NonNullTypeNode 118 | | NullValueNode 119 | | ObjectFieldNode 120 | | ObjectTypeDefinitionNode 121 | | ObjectTypeExtensionNode 122 | | ObjectValueNode 123 | | OperationDefinitionNode 124 | | OperationTypeDefinitionNode 125 | | ScalarTypeDefinitionNode 126 | | ScalarTypeExtensionNode 127 | | SchemaDefinitionNode 128 | | SchemaExtensionNode 129 | | SelectionSetNode 130 | | StringValueNode 131 | | UnionTypeDefinitionNode 132 | | UnionTypeExtensionNode 133 | | VariableDefinitionNode 134 | | VariableNode 135 | -------------------------------------------------------------------------------- /src/kinds.v: -------------------------------------------------------------------------------- 1 | module graphql 2 | 3 | // The set of allowed kind values for AST nodes. 4 | const _kind_map = { 5 | Kind.name: 'Name' 6 | Kind.document: 'Document' 7 | Kind.operation_definition: 'OperationDefinition' 8 | Kind.variable_definition: 'VariableDefinition' 9 | Kind.selection_set: 'SelectionSet' 10 | Kind.field: 'Field' 11 | Kind.argument: 'Argument' 12 | Kind.list_nullability_operator: 'ListNullabilityOperator' 13 | Kind.non_null_assertion: 'NonNullAssertion' 14 | Kind.error_boundary: 'ErrorBoundary' 15 | Kind.fragment_spread: 'FragmentSpread' 16 | Kind.inline_fragment: 'InlineFragment' 17 | Kind.fragment_definition: 'FragmentDefinition' 18 | Kind.variable: 'Variable' 19 | Kind.int_value: 'IntValue' 20 | Kind.float_value: 'FloatValue' 21 | Kind.string_value: 'StringValue' 22 | Kind.boolean: 'BooleanValue' 23 | Kind.null: 'NullValue' 24 | Kind.enum_value: 'EnumValue' 25 | Kind.list: 'ListValue' 26 | Kind.object: 'ObjectValue' 27 | Kind.object_field: 'ObjectField' 28 | Kind.directive: 'Directive' 29 | Kind.named_type: 'NamedType' 30 | Kind.list_type: 'ListType' 31 | Kind.non_null_type: 'NonNullType' 32 | Kind.schema_definition: 'SchemaDefinition' 33 | Kind.operation_type_definition: 'OperationTypeDefinition' 34 | Kind.scalar_type_definition: 'ScalarTypeDefinition' 35 | Kind.object_type_definition: 'ObjectTypeDefinition' 36 | Kind.field_definition: 'FieldDefinition' 37 | Kind.input_value_definition: 'InputValueDefinition' 38 | Kind.interface_type_definition: 'InterfaceTypeDefinition' 39 | Kind.union_type_definition: 'UnionTypeDefinition' 40 | Kind.enum_type_definition: 'EnumTypeDefinition' 41 | Kind.enum_value_definition: 'EnumValueDefinition' 42 | Kind.input_object_type_definition: 'InputObjectTypeDefinition' 43 | Kind.directive_definition: 'DirectiveDefinition' 44 | Kind.schema_extension: 'SchemaExtension' 45 | Kind.scalar_type_extension: 'ScalarTypeExtension' 46 | Kind.object_type_extension: 'ObjectTypeExtension' 47 | Kind.interface_type_extension: 'InterfaceTypeExtension' 48 | Kind.union_type_extension: 'UnionTypeExtension' 49 | Kind.enum_type_extension: 'EnumTypeExtension' 50 | Kind.input_object_type_extension: 'InputObjectTypeExtension' 51 | } 52 | 53 | pub enum Kind { 54 | name 55 | document 56 | // Documen 57 | operation_definition 58 | variable_definition 59 | selection_set 60 | field 61 | argument 62 | // Nullability Modifier 63 | list_nullability_operator 64 | non_null_assertion 65 | error_boundary 66 | // Fragments * 67 | fragment_spread 68 | inline_fragment 69 | fragment_definition 70 | // Value 71 | variable 72 | int_value 73 | float_value 74 | string_value 75 | boolean 76 | null 77 | enum_value 78 | list 79 | object 80 | object_field 81 | // Directive 82 | directive 83 | // Type 84 | named_type 85 | list_type 86 | non_null_type 87 | // Type System Definition 88 | schema_definition 89 | operation_type_definition 90 | // Type Definition 91 | scalar_type_definition 92 | object_type_definition 93 | field_definition 94 | input_value_definition 95 | interface_type_definition 96 | union_type_definition 97 | enum_type_definition 98 | enum_value_definition 99 | input_object_type_definition 100 | // Directive Definition 101 | directive_definition 102 | // Type System Extension 103 | schema_extension 104 | // Type Extension 105 | scalar_type_extension 106 | object_type_extension 107 | interface_type_extension 108 | union_type_extension 109 | enum_type_extension 110 | input_object_type_extension 111 | } 112 | 113 | // Returns the string value of the Kind enum 114 | pub fn (k Kind) gql_str() string { 115 | return graphql._kind_map[k] 116 | } 117 | -------------------------------------------------------------------------------- /src/block_string.v: -------------------------------------------------------------------------------- 1 | module graphql 2 | 3 | // @internal 4 | // Produces the value of a block string from its parsed raw value. 5 | // This implements the GraphQL spec's BlockStringValue() static algorithm. 6 | fn dedent_block_string_lines(lines []string) []string { 7 | mut common_indent := max_i64 8 | mut first_non_empty_line := 0 9 | mut last_non_empty_line := -1 10 | 11 | for i := 0; i < lines.len; i++ { 12 | line := lines[i] 13 | indent := leading_whitespace(line) 14 | 15 | if indent == line.len { 16 | continue // skip empty lines 17 | } 18 | 19 | if first_non_empty_line == 0 { 20 | first_non_empty_line = i 21 | } 22 | 23 | last_non_empty_line = i 24 | 25 | if i != 0 && indent < common_indent { 26 | common_indent = indent 27 | } 28 | } 29 | 30 | new_lines := lines.map(fn [lines, common_indent] (line string) string { 31 | if lines.index(line) == 0 { 32 | return line 33 | } else { 34 | // EDGE case: sometimes commonIndent can be greater than the bounds here. IN JS if you 35 | // call string.slice(number_greater_than_list_len) it will return an empty string but 36 | // not in Vlang (index out of bounds) 37 | if common_indent >= line.len { 38 | return '' 39 | } 40 | return line[common_indent..] 41 | } 42 | }) 43 | 44 | // println(new_lines) 45 | 46 | return new_lines[first_non_empty_line..last_non_empty_line + 1] 47 | } 48 | 49 | // Helper function to find leading whitespace in a string. 50 | fn leading_whitespace(str string) int { 51 | mut counter := 0 52 | 53 | for i := 0; counter < str.len && is_white_space(str[counter]); i++ { 54 | counter += i 55 | } 56 | return counter 57 | } 58 | 59 | // Check if a string is printable as a block string. 60 | pub fn is_printable_as_block_string(value string) bool { 61 | if value == '' { 62 | return true // empty string is printable 63 | } 64 | 65 | mut is_empty_line := true 66 | mut has_indent := false 67 | mut has_common_indent := true 68 | mut seen_non_empty_line := false 69 | 70 | for c in value.runes() { 71 | match c { 72 | 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x000b, 0x000c, 73 | 0x000e, 0x000f { 74 | return false // Has non-printable characters 75 | } 76 | 0x000d { 77 | return false // Has \r or \r\n which will be replaced as \n 78 | } 79 | 10 { 80 | if is_empty_line && !seen_non_empty_line { 81 | return false // Has leading new line 82 | } 83 | seen_non_empty_line = true 84 | is_empty_line = true 85 | has_indent = false 86 | } 87 | 9, 32 { 88 | has_indent = is_empty_line 89 | } 90 | else { 91 | has_common_indent = has_indent 92 | is_empty_line = false 93 | } 94 | } 95 | } 96 | 97 | if is_empty_line { 98 | return false // Has trailing empty lines 99 | } 100 | 101 | if has_common_indent && seen_non_empty_line { 102 | return false // Has internal indent 103 | } 104 | 105 | return true 106 | } 107 | 108 | pub struct BlockStringOptions { 109 | pub: 110 | minimize bool 111 | } 112 | 113 | // Print a block string in the indented block form by adding a leading and trailing blank line. 114 | // However, if a block string starts with whitespace and is a single-line, adding a leading blank line would strip that whitespace. 115 | pub fn print_block_string(value string, options ?BlockStringOptions) ?string { 116 | escaped_value := value.replace('"""', '\\"""') 117 | lines := escaped_value.split('\r\n|\n|\r') 118 | 119 | // Expand a block string's raw value into independent lines. 120 | is_single_line := lines.len == 1 121 | 122 | // If common indentation is found we can fix some of those cases by adding leading new line 123 | force_leading_new_line := lines.len > 1 && lines[0..].all(it.len == 0 || is_white_space(it[0])) 124 | // lines[1..] |line| { 125 | // len(line) == 0 or is_white_space(line[0] as int) 126 | // } 127 | 128 | // // Trailing triple quotes just look confusing but doesn't force trailing new line 129 | has_trailing_triple_quotes := escaped_value.ends_with('\\"""') 130 | 131 | // // Trailing quote (single or double) or slash forces trailing new line 132 | has_trailing_quote := value.ends_with('"') && !has_trailing_triple_quotes 133 | has_trailing_slash := value.ends_with('\\') 134 | force_trailing_newline := has_trailing_quote || has_trailing_slash 135 | 136 | // add leading and trailing new lines only if it improves readability 137 | print_as_multiple_lines := !options?.minimize && (!is_single_line 138 | || value.len > 70 || force_trailing_newline || force_leading_new_line 139 | || has_trailing_triple_quotes) 140 | 141 | mut result := '' 142 | 143 | // Format a multi-line block quote to account for leading space. 144 | skip_leading_new_line := is_single_line && is_white_space(value[0]) 145 | if (print_as_multiple_lines && !skip_leading_new_line) || force_leading_new_line { 146 | result += '\n' 147 | } 148 | 149 | result += escaped_value 150 | if print_as_multiple_lines || force_trailing_newline { 151 | result += '\n' 152 | } 153 | 154 | return '"""' + result + '"""' 155 | } 156 | -------------------------------------------------------------------------------- /src/graphql.md: -------------------------------------------------------------------------------- 1 | # module graphql 2 | 3 | 4 | ## Contents 5 | - [get_location](#get_location) 6 | - [is_printable_as_block_string](#is_printable_as_block_string) 7 | - [parse](#parse) 8 | - [print_block_string](#print_block_string) 9 | - [ASTNodeInterface](#ASTNodeInterface) 10 | - [IDirectiveNode](#IDirectiveNode) 11 | - [ASTNode](#ASTNode) 12 | - [DefinitionNode](#DefinitionNode) 13 | - [ExecutableDefinitionNode](#ExecutableDefinitionNode) 14 | - [NonNullType](#NonNullType) 15 | - [NullabilityAssertionNode](#NullabilityAssertionNode) 16 | - [SelectionNode](#SelectionNode) 17 | - [TypeDefinitionNode](#TypeDefinitionNode) 18 | - [TypeDirectives](#TypeDirectives) 19 | - [TypeExtensionNode](#TypeExtensionNode) 20 | - [TypeNode](#TypeNode) 21 | - [TypeSystemDefinitionNode](#TypeSystemDefinitionNode) 22 | - [TypeSystemExtensionNode](#TypeSystemExtensionNode) 23 | - [ValueNode](#ValueNode) 24 | - [DirectiveLocation](#DirectiveLocation) 25 | - [Kind](#Kind) 26 | - [gql_str](#gql_str) 27 | - [OperationTypeNode](#OperationTypeNode) 28 | - [gql_str](#gql_str) 29 | - [TokenKind](#TokenKind) 30 | - [gql_str](#gql_str) 31 | - [BlockStringOptions](#BlockStringOptions) 32 | - [BooleanValueNode](#BooleanValueNode) 33 | - [ConstArgumentNode](#ConstArgumentNode) 34 | - [ConstObjectFieldNode](#ConstObjectFieldNode) 35 | - [ConstObjectValueNode](#ConstObjectValueNode) 36 | - [DirectiveDefinitionNode](#DirectiveDefinitionNode) 37 | - [DirectiveNode](#DirectiveNode) 38 | - [DocumentNode](#DocumentNode) 39 | - [EnumTypeDefinitionNode](#EnumTypeDefinitionNode) 40 | - [EnumTypeExtensionNode](#EnumTypeExtensionNode) 41 | - [EnumValueDefinitionNode](#EnumValueDefinitionNode) 42 | - [EnumValueNode](#EnumValueNode) 43 | - [ErrorBoundaryNode](#ErrorBoundaryNode) 44 | - [FieldDefinitionNode](#FieldDefinitionNode) 45 | - [FieldNode](#FieldNode) 46 | - [FloatValueNode](#FloatValueNode) 47 | - [FragmentDefinitionNode](#FragmentDefinitionNode) 48 | - [FragmentSpreadNode](#FragmentSpreadNode) 49 | - [InlineFragmentNode](#InlineFragmentNode) 50 | - [InputObjectTypeDefinitionNode](#InputObjectTypeDefinitionNode) 51 | - [InputObjectTypeExtensionNode](#InputObjectTypeExtensionNode) 52 | - [InputValueDefinitionNode](#InputValueDefinitionNode) 53 | - [IntValueNode](#IntValueNode) 54 | - [InterfaceTypeDefinitionNode](#InterfaceTypeDefinitionNode) 55 | - [InterfaceTypeExtensionNode](#InterfaceTypeExtensionNode) 56 | - [Lexer](#Lexer) 57 | - [ListNullabilityOperatorNode](#ListNullabilityOperatorNode) 58 | - [ListTypeNode](#ListTypeNode) 59 | - [ListValueNode](#ListValueNode) 60 | - [Location](#Location) 61 | - [NameNode](#NameNode) 62 | - [NamedTypeNode](#NamedTypeNode) 63 | - [NonNullAssertionNode](#NonNullAssertionNode) 64 | - [NonNullTypeNode](#NonNullTypeNode) 65 | - [NullValueNode](#NullValueNode) 66 | - [ObjectFieldNode](#ObjectFieldNode) 67 | - [ObjectTypeDefinitionNode](#ObjectTypeDefinitionNode) 68 | - [ObjectTypeExtensionNode](#ObjectTypeExtensionNode) 69 | - [ObjectValueNode](#ObjectValueNode) 70 | - [OperationDefinitionNode](#OperationDefinitionNode) 71 | - [OperationTypeDefinitionNode](#OperationTypeDefinitionNode) 72 | - [Parser](#Parser) 73 | - [ParserOptions](#ParserOptions) 74 | - [ScalarTypeDefinitionNode](#ScalarTypeDefinitionNode) 75 | - [ScalarTypeExtensionNode](#ScalarTypeExtensionNode) 76 | - [SchemaDefinitionNode](#SchemaDefinitionNode) 77 | - [SchemaExtensionNode](#SchemaExtensionNode) 78 | - [SelectionSetNode](#SelectionSetNode) 79 | - [Source](#Source) 80 | - [StringValueNode](#StringValueNode) 81 | - [Token](#Token) 82 | - [UnionTypeDefinitionNode](#UnionTypeDefinitionNode) 83 | - [UnionTypeExtensionNode](#UnionTypeExtensionNode) 84 | - [VariableDefinitionNode](#VariableDefinitionNode) 85 | - [VariableNode](#VariableNode) 86 | 87 | ## get_location 88 | [[Return to contents]](#Contents) 89 | 90 | ## is_printable_as_block_string 91 | [[Return to contents]](#Contents) 92 | 93 | ## parse 94 | [[Return to contents]](#Contents) 95 | 96 | ## print_block_string 97 | [[Return to contents]](#Contents) 98 | 99 | ## ASTNodeInterface 100 | [[Return to contents]](#Contents) 101 | 102 | ## IDirectiveNode 103 | [[Return to contents]](#Contents) 104 | 105 | ## ASTNode 106 | [[Return to contents]](#Contents) 107 | 108 | ## DefinitionNode 109 | [[Return to contents]](#Contents) 110 | 111 | ## ExecutableDefinitionNode 112 | [[Return to contents]](#Contents) 113 | 114 | ## NonNullType 115 | [[Return to contents]](#Contents) 116 | 117 | ## NullabilityAssertionNode 118 | [[Return to contents]](#Contents) 119 | 120 | ## SelectionNode 121 | [[Return to contents]](#Contents) 122 | 123 | ## TypeDefinitionNode 124 | [[Return to contents]](#Contents) 125 | 126 | ## TypeDirectives 127 | [[Return to contents]](#Contents) 128 | 129 | ## TypeExtensionNode 130 | [[Return to contents]](#Contents) 131 | 132 | ## TypeNode 133 | [[Return to contents]](#Contents) 134 | 135 | ## TypeSystemDefinitionNode 136 | [[Return to contents]](#Contents) 137 | 138 | ## TypeSystemExtensionNode 139 | [[Return to contents]](#Contents) 140 | 141 | ## ValueNode 142 | [[Return to contents]](#Contents) 143 | 144 | ## DirectiveLocation 145 | [[Return to contents]](#Contents) 146 | 147 | ## Kind 148 | [[Return to contents]](#Contents) 149 | 150 | ## gql_str 151 | [[Return to contents]](#Contents) 152 | 153 | ## OperationTypeNode 154 | [[Return to contents]](#Contents) 155 | 156 | ## gql_str 157 | [[Return to contents]](#Contents) 158 | 159 | ## TokenKind 160 | [[Return to contents]](#Contents) 161 | 162 | ## gql_str 163 | [[Return to contents]](#Contents) 164 | 165 | ## BlockStringOptions 166 | [[Return to contents]](#Contents) 167 | 168 | ## BooleanValueNode 169 | [[Return to contents]](#Contents) 170 | 171 | ## ConstArgumentNode 172 | [[Return to contents]](#Contents) 173 | 174 | ## ConstObjectFieldNode 175 | [[Return to contents]](#Contents) 176 | 177 | ## ConstObjectValueNode 178 | [[Return to contents]](#Contents) 179 | 180 | ## DirectiveDefinitionNode 181 | [[Return to contents]](#Contents) 182 | 183 | ## DirectiveNode 184 | [[Return to contents]](#Contents) 185 | 186 | ## DocumentNode 187 | [[Return to contents]](#Contents) 188 | 189 | ## EnumTypeDefinitionNode 190 | [[Return to contents]](#Contents) 191 | 192 | ## EnumTypeExtensionNode 193 | [[Return to contents]](#Contents) 194 | 195 | ## EnumValueDefinitionNode 196 | [[Return to contents]](#Contents) 197 | 198 | ## EnumValueNode 199 | [[Return to contents]](#Contents) 200 | 201 | ## ErrorBoundaryNode 202 | [[Return to contents]](#Contents) 203 | 204 | ## FieldDefinitionNode 205 | [[Return to contents]](#Contents) 206 | 207 | ## FieldNode 208 | [[Return to contents]](#Contents) 209 | 210 | ## FloatValueNode 211 | [[Return to contents]](#Contents) 212 | 213 | ## FragmentDefinitionNode 214 | [[Return to contents]](#Contents) 215 | 216 | ## FragmentSpreadNode 217 | [[Return to contents]](#Contents) 218 | 219 | ## InlineFragmentNode 220 | [[Return to contents]](#Contents) 221 | 222 | ## InputObjectTypeDefinitionNode 223 | [[Return to contents]](#Contents) 224 | 225 | ## InputObjectTypeExtensionNode 226 | [[Return to contents]](#Contents) 227 | 228 | ## InputValueDefinitionNode 229 | [[Return to contents]](#Contents) 230 | 231 | ## IntValueNode 232 | [[Return to contents]](#Contents) 233 | 234 | ## InterfaceTypeDefinitionNode 235 | [[Return to contents]](#Contents) 236 | 237 | ## InterfaceTypeExtensionNode 238 | [[Return to contents]](#Contents) 239 | 240 | ## Lexer 241 | [[Return to contents]](#Contents) 242 | 243 | ## ListNullabilityOperatorNode 244 | [[Return to contents]](#Contents) 245 | 246 | ## ListTypeNode 247 | [[Return to contents]](#Contents) 248 | 249 | ## ListValueNode 250 | [[Return to contents]](#Contents) 251 | 252 | ## Location 253 | [[Return to contents]](#Contents) 254 | 255 | ## NameNode 256 | [[Return to contents]](#Contents) 257 | 258 | ## NamedTypeNode 259 | [[Return to contents]](#Contents) 260 | 261 | ## NonNullAssertionNode 262 | [[Return to contents]](#Contents) 263 | 264 | ## NonNullTypeNode 265 | [[Return to contents]](#Contents) 266 | 267 | ## NullValueNode 268 | [[Return to contents]](#Contents) 269 | 270 | ## ObjectFieldNode 271 | [[Return to contents]](#Contents) 272 | 273 | ## ObjectTypeDefinitionNode 274 | [[Return to contents]](#Contents) 275 | 276 | ## ObjectTypeExtensionNode 277 | [[Return to contents]](#Contents) 278 | 279 | ## ObjectValueNode 280 | [[Return to contents]](#Contents) 281 | 282 | ## OperationDefinitionNode 283 | [[Return to contents]](#Contents) 284 | 285 | ## OperationTypeDefinitionNode 286 | [[Return to contents]](#Contents) 287 | 288 | ## Parser 289 | [[Return to contents]](#Contents) 290 | 291 | ## ParserOptions 292 | [[Return to contents]](#Contents) 293 | 294 | ## ScalarTypeDefinitionNode 295 | [[Return to contents]](#Contents) 296 | 297 | ## ScalarTypeExtensionNode 298 | [[Return to contents]](#Contents) 299 | 300 | ## SchemaDefinitionNode 301 | [[Return to contents]](#Contents) 302 | 303 | ## SchemaExtensionNode 304 | [[Return to contents]](#Contents) 305 | 306 | ## SelectionSetNode 307 | [[Return to contents]](#Contents) 308 | 309 | ## Source 310 | [[Return to contents]](#Contents) 311 | 312 | ## StringValueNode 313 | [[Return to contents]](#Contents) 314 | 315 | ## Token 316 | [[Return to contents]](#Contents) 317 | 318 | ## UnionTypeDefinitionNode 319 | [[Return to contents]](#Contents) 320 | 321 | ## UnionTypeExtensionNode 322 | [[Return to contents]](#Contents) 323 | 324 | ## VariableDefinitionNode 325 | [[Return to contents]](#Contents) 326 | 327 | ## VariableNode 328 | [[Return to contents]](#Contents) 329 | 330 | #### Powered by vdoc. Generated on: 4 May 2024 02:50:47 331 | -------------------------------------------------------------------------------- /src/node.v: -------------------------------------------------------------------------------- 1 | module graphql 2 | 3 | pub struct NameNode { 4 | pub: 5 | kind Kind = Kind.name 6 | value string 7 | pub mut: 8 | loc ?Location 9 | } 10 | 11 | pub struct DocumentNode { 12 | pub: 13 | kind Kind = Kind.document 14 | definitions []DefinitionNode 15 | token_count int 16 | pub mut: 17 | loc ?Location 18 | } 19 | 20 | pub type TypeSystemExtensionNode = SchemaExtensionNode | TypeExtensionNode 21 | 22 | pub type DefinitionNode = DirectiveDefinitionNode 23 | | EnumTypeDefinitionNode 24 | | FragmentDefinitionNode 25 | | InputObjectTypeDefinitionNode 26 | | InterfaceTypeDefinitionNode 27 | | ObjectTypeDefinitionNode 28 | | OperationDefinitionNode 29 | | ScalarTypeDefinitionNode 30 | | SchemaDefinitionNode 31 | | SchemaExtensionNode 32 | | TypeExtensionNode 33 | | UnionTypeDefinitionNode 34 | pub type ExecutableDefinitionNode = FragmentDefinitionNode | OperationDefinitionNode 35 | 36 | pub struct OperationDefinitionNode { 37 | pub: 38 | kind Kind = Kind.operation_definition 39 | operation OperationTypeNode 40 | name ?NameNode 41 | variable_definitions ?[]VariableDefinitionNode 42 | directives ?[]DirectiveNode 43 | selection_set SelectionSetNode 44 | pub mut: 45 | loc ?Location 46 | } 47 | 48 | pub enum OperationTypeNode { 49 | query 50 | mutation 51 | subscription 52 | } 53 | 54 | pub fn (o OperationTypeNode) gql_str() string { 55 | return o.str() 56 | } 57 | 58 | pub struct VariableDefinitionNode { 59 | pub: 60 | kind Kind = Kind.variable_definition 61 | variable VariableNode 62 | @type TypeNode 63 | default_value ?ValueNode 64 | directives ?[]DirectiveNode 65 | pub mut: 66 | loc ?Location 67 | } 68 | 69 | pub struct VariableNode { 70 | pub: 71 | kind Kind = Kind.variable 72 | name NameNode 73 | pub mut: 74 | loc ?Location 75 | } 76 | 77 | pub struct SelectionSetNode { 78 | pub: 79 | kind Kind = Kind.selection_set 80 | selections []SelectionNode 81 | pub mut: 82 | loc ?Location 83 | } 84 | 85 | pub type SelectionNode = FieldNode | FragmentSpreadNode | InlineFragmentNode 86 | 87 | pub struct FieldNode { 88 | pub: 89 | kind Kind = Kind.field 90 | alias ?NameNode 91 | name NameNode 92 | arguments ?[]ArgumentNode 93 | nullability_assertion ?NullabilityAssertionNode 94 | directives []DirectiveNode 95 | selection_set ?SelectionSetNode 96 | pub mut: 97 | loc ?Location 98 | } 99 | 100 | pub type NullabilityAssertionNode = ErrorBoundaryNode 101 | | ListNullabilityOperatorNode 102 | | NonNullAssertionNode 103 | 104 | pub struct ListNullabilityOperatorNode { 105 | pub: 106 | kind Kind = Kind.list_nullability_operator 107 | nullability_assertion ?NullabilityAssertionNode 108 | pub mut: 109 | loc ?Location 110 | } 111 | 112 | pub struct NonNullAssertionNode { 113 | pub: 114 | kind Kind = Kind.non_null_assertion 115 | nullability_assertion ?ListNullabilityOperatorNode 116 | pub mut: 117 | loc ?Location 118 | } 119 | 120 | pub struct ErrorBoundaryNode { 121 | pub: 122 | kind Kind = Kind.error_boundary 123 | nullability_assertion ?ListNullabilityOperatorNode 124 | pub mut: 125 | loc ?Location 126 | } 127 | 128 | // Values 129 | 130 | // -- 131 | pub type ValueNode = BooleanValueNode 132 | | ConstObjectValueNode 133 | | EnumValueNode 134 | | FloatValueNode 135 | | IntValueNode 136 | | ListValueNode 137 | | NullValueNode 138 | | ObjectValueNode 139 | | StringValueNode 140 | | VariableNode 141 | 142 | struct ArgumentNode { 143 | pub: 144 | kind Kind = Kind.argument 145 | name NameNode 146 | value ValueNode 147 | pub mut: 148 | loc ?Location 149 | } 150 | 151 | interface IArgumentNode { 152 | kind Kind 153 | name NameNode 154 | value ValueNode 155 | mut: 156 | loc ?Location 157 | } 158 | 159 | pub struct ConstArgumentNode { 160 | pub: 161 | kind Kind = Kind.argument 162 | name NameNode 163 | value ValueNode 164 | pub mut: 165 | loc ?Location 166 | } 167 | 168 | // Fragments 169 | pub struct FragmentSpreadNode { 170 | pub: 171 | kind Kind = Kind.fragment_spread 172 | name NameNode 173 | directives ?[]DirectiveNode 174 | pub mut: 175 | loc ?Location 176 | } 177 | 178 | pub struct InlineFragmentNode { 179 | pub: 180 | kind Kind = Kind.inline_fragment 181 | type_condition ?NamedTypeNode 182 | directives ?[]DirectiveNode 183 | selection_set SelectionSetNode 184 | pub mut: 185 | loc ?Location 186 | } 187 | 188 | pub struct FragmentDefinitionNode { 189 | pub: 190 | kind Kind = Kind.fragment_definition 191 | name NameNode 192 | type_condition NamedTypeNode 193 | directives []DirectiveNode 194 | selection_set SelectionSetNode 195 | pub mut: 196 | loc ?Location 197 | } 198 | 199 | pub struct IntValueNode { 200 | pub: 201 | kind Kind = Kind.int_value 202 | value ?string 203 | is_const bool 204 | pub mut: 205 | loc ?Location 206 | } 207 | 208 | pub struct FloatValueNode { 209 | pub: 210 | kind Kind = Kind.float_value 211 | value ?string 212 | is_const bool 213 | pub mut: 214 | loc ?Location 215 | } 216 | 217 | pub struct StringValueNode { 218 | pub: 219 | kind Kind = Kind.string_value 220 | value ?string 221 | block ?bool 222 | is_const bool 223 | pub mut: 224 | loc ?Location 225 | } 226 | 227 | pub struct BooleanValueNode { 228 | pub: 229 | kind Kind = Kind.boolean 230 | value bool 231 | is_const bool 232 | pub mut: 233 | loc ?Location 234 | } 235 | 236 | pub struct NullValueNode { 237 | pub: 238 | kind Kind = Kind.null 239 | is_const bool 240 | pub mut: 241 | loc ?Location 242 | } 243 | 244 | pub struct EnumValueNode { 245 | pub: 246 | kind Kind = Kind.enum_value 247 | value string 248 | is_const bool 249 | pub mut: 250 | loc ?Location 251 | } 252 | 253 | pub struct ListValueNode { 254 | pub: 255 | kind Kind = Kind.list 256 | values []ValueNode 257 | is_const bool 258 | pub mut: 259 | loc ?Location 260 | } 261 | 262 | pub struct ObjectValueNode { 263 | pub: 264 | kind Kind = Kind.object 265 | fields []ObjectFieldNode 266 | is_const bool 267 | pub mut: 268 | loc ?Location 269 | } 270 | 271 | pub struct ConstObjectValueNode { 272 | pub: 273 | kind Kind = Kind.object 274 | fields []ValueNode 275 | is_const bool = true 276 | pub mut: 277 | loc ?Location 278 | } 279 | 280 | pub struct ObjectFieldNode { 281 | pub: 282 | kind Kind = Kind.object_field 283 | name NameNode 284 | value ValueNode 285 | is_const bool 286 | pub mut: 287 | loc ?Location 288 | } 289 | 290 | pub struct ConstObjectFieldNode { 291 | pub: 292 | kind Kind = Kind.object_field 293 | name NameNode 294 | value ValueNode 295 | is_const bool = true 296 | pub mut: 297 | loc ?Location 298 | } 299 | 300 | // Directives 301 | 302 | pub type TypeDirectives = DirectiveNode 303 | 304 | pub interface IDirectiveNode { 305 | kind Kind 306 | name NameNode 307 | arguments ?[]ArgumentNode 308 | mut: 309 | loc ?Location 310 | } 311 | 312 | pub struct DirectiveNode { 313 | pub: 314 | kind Kind = Kind.directive 315 | name NameNode 316 | arguments ?[]ArgumentNode 317 | pub mut: 318 | loc ?Location 319 | is_const bool 320 | } 321 | 322 | // Type reference 323 | 324 | pub type TypeNode = ListTypeNode | NamedTypeNode | NonNullTypeNode 325 | pub type NonNullType = ListTypeNode | NamedTypeNode 326 | 327 | pub struct NamedTypeNode { 328 | pub: 329 | kind Kind = Kind.named_type 330 | node_type NameNode 331 | pub mut: 332 | loc ?Location 333 | } 334 | 335 | pub struct ListTypeNode { 336 | pub: 337 | kind Kind = Kind.list_type 338 | node_type TypeNode 339 | pub mut: 340 | loc ?Location 341 | } 342 | 343 | pub struct NonNullTypeNode { 344 | pub: 345 | kind Kind = Kind.non_null_type 346 | node_type NonNullType 347 | pub mut: 348 | loc ?Location 349 | } 350 | 351 | // Type system definition 352 | 353 | pub type TypeSystemDefinitionNode = DirectiveDefinitionNode 354 | | SchemaDefinitionNode 355 | | TypeDefinitionNode 356 | 357 | pub struct SchemaDefinitionNode { 358 | pub: 359 | kind Kind = Kind.schema_definition 360 | description ?StringValueNode 361 | directives ?[]DirectiveNode 362 | operation_types []OperationTypeDefinitionNode 363 | pub mut: 364 | loc ?Location 365 | } 366 | 367 | pub struct OperationTypeDefinitionNode { 368 | pub: 369 | kind Kind = Kind.operation_type_definition 370 | operation OperationTypeNode 371 | @type NamedTypeNode 372 | pub mut: 373 | loc ?Location 374 | } 375 | 376 | // Type definition 377 | 378 | pub type TypeDefinitionNode = EnumTypeDefinitionNode 379 | | InputObjectTypeDefinitionNode 380 | | InterfaceTypeDefinitionNode 381 | | ObjectTypeDefinitionNode 382 | | ScalarTypeDefinitionNode 383 | | UnionTypeDefinitionNode 384 | 385 | pub struct ScalarTypeDefinitionNode { 386 | kind Kind 387 | description ?StringValueNode 388 | name NameNode 389 | directives ?[]DirectiveNode 390 | pub mut: 391 | loc ?Location 392 | } 393 | 394 | pub struct ObjectTypeDefinitionNode { 395 | kind Kind 396 | description ?StringValueNode 397 | name NameNode 398 | interfaces ?[]NamedTypeNode 399 | directives ?[]DirectiveNode 400 | fields ?[]FieldDefinitionNode 401 | pub mut: 402 | loc ?Location 403 | } 404 | 405 | pub struct FieldDefinitionNode { 406 | kind Kind = Kind.field_definition 407 | description ?StringValueNode 408 | name NameNode 409 | arguments ?[]InputValueDefinitionNode 410 | @type TypeNode 411 | directives ?[]DirectiveNode 412 | pub mut: 413 | loc ?Location 414 | } 415 | 416 | pub struct InputValueDefinitionNode { 417 | kind Kind 418 | description ?StringValueNode 419 | name NameNode 420 | @type TypeNode 421 | default_value ?ValueNode 422 | directives ?[]DirectiveNode 423 | pub mut: 424 | loc ?Location 425 | } 426 | 427 | pub struct InterfaceTypeDefinitionNode { 428 | kind Kind = Kind.interface_type_definition 429 | description ?StringValueNode 430 | name NameNode 431 | interfaces ?[]NamedTypeNode 432 | directives ?[]DirectiveNode 433 | fields ?[]FieldDefinitionNode 434 | pub mut: 435 | loc ?Location 436 | } 437 | 438 | pub struct UnionTypeDefinitionNode { 439 | kind Kind = Kind.union_type_definition 440 | description ?StringValueNode 441 | name NameNode 442 | directives ?[]DirectiveNode 443 | types ?[]NamedTypeNode 444 | pub mut: 445 | loc ?Location 446 | } 447 | 448 | pub struct EnumTypeDefinitionNode { 449 | kind Kind = Kind.enum_type_definition 450 | description ?StringValueNode 451 | name NameNode 452 | directives ?[]DirectiveNode 453 | values ?[]EnumValueDefinitionNode 454 | pub mut: 455 | loc ?Location 456 | } 457 | 458 | pub struct EnumValueDefinitionNode { 459 | kind Kind = Kind.enum_value_definition 460 | description ?StringValueNode 461 | name NameNode 462 | directives ?[]DirectiveNode 463 | pub mut: 464 | loc ?Location 465 | } 466 | 467 | pub struct InputObjectTypeDefinitionNode { 468 | kind Kind = Kind.input_object_type_definition 469 | description ?StringValueNode 470 | name NameNode 471 | directives ?[]DirectiveNode 472 | fields ?[]InputValueDefinitionNode 473 | pub mut: 474 | loc ?Location 475 | } 476 | 477 | pub struct DirectiveDefinitionNode { 478 | kind Kind = Kind.directive_definition 479 | description ?StringValueNode 480 | name NameNode 481 | arguments ?[]InputValueDefinitionNode 482 | repeatable bool 483 | locations []NameNode 484 | pub mut: 485 | loc ?Location 486 | } 487 | 488 | pub struct SchemaExtensionNode { 489 | kind Kind = Kind.schema_extension 490 | directives ?[]DirectiveNode 491 | operation_types ?[]OperationTypeDefinitionNode 492 | pub mut: 493 | loc ?Location 494 | } 495 | 496 | // Type Extensions 497 | 498 | pub type TypeExtensionNode = EnumTypeExtensionNode 499 | | InputObjectTypeExtensionNode 500 | | InterfaceTypeExtensionNode 501 | | ObjectTypeExtensionNode 502 | | ScalarTypeExtensionNode 503 | | UnionTypeExtensionNode 504 | 505 | pub struct ScalarTypeExtensionNode { 506 | kind Kind = Kind.scalar_type_extension 507 | name NameNode 508 | directives ?[]DirectiveNode 509 | pub mut: 510 | loc ?Location 511 | } 512 | 513 | pub struct ObjectTypeExtensionNode { 514 | kind Kind = Kind.object_type_extension 515 | name NameNode 516 | interfaces ?[]NamedTypeNode 517 | directives ?[]DirectiveNode 518 | fields ?[]FieldDefinitionNode 519 | pub mut: 520 | loc ?Location 521 | } 522 | 523 | pub struct InterfaceTypeExtensionNode { 524 | kind Kind = Kind.interface_type_extension 525 | name NameNode 526 | interfaces ?[]NamedTypeNode 527 | directives ?[]DirectiveNode 528 | fields ?[]FieldDefinitionNode 529 | pub mut: 530 | loc ?Location 531 | } 532 | 533 | pub struct UnionTypeExtensionNode { 534 | kind Kind = Kind.union_type_extension 535 | name NameNode 536 | directives ?[]DirectiveNode 537 | types ?[]NamedTypeNode 538 | pub mut: 539 | loc ?Location 540 | } 541 | 542 | pub struct EnumTypeExtensionNode { 543 | kind Kind = Kind.enum_type_extension 544 | name NameNode 545 | directives ?[]DirectiveNode 546 | values ?[]EnumValueDefinitionNode 547 | pub mut: 548 | loc ?Location 549 | } 550 | 551 | pub struct InputObjectTypeExtensionNode { 552 | kind Kind = Kind.object_type_extension 553 | name NameNode 554 | directives ?[]DirectiveNode 555 | fields ?[]InputValueDefinitionNode 556 | pub mut: 557 | loc ?Location 558 | } 559 | -------------------------------------------------------------------------------- /src/lexer.v: -------------------------------------------------------------------------------- 1 | module graphql 2 | 3 | pub struct Lexer { 4 | pub: 5 | source Source 6 | mut: 7 | line_start int 8 | line int 9 | last_token &Token 10 | token &Token 11 | } 12 | 13 | fn Lexer.new(source Source) Lexer { 14 | start_of_file_token := Token.new(TokenKind.sof, 0, 0, 0, 0) 15 | 16 | return Lexer{ 17 | source: source 18 | last_token: &start_of_file_token 19 | token: &start_of_file_token 20 | line: 1 21 | line_start: 0 22 | } 23 | } 24 | 25 | // Advances the token stream to the next non-ignored token. 26 | fn (mut lexer Lexer) advance() &Token { 27 | lexer.last_token = lexer.token 28 | token := lexer.lookahead() 29 | // println('..advancing from ${lexer.token.kind} to ${token.kind}') 30 | 31 | lexer.token = token 32 | 33 | return token 34 | } 35 | 36 | // Looks ahead and returns the next non-ignored token, 37 | // but does not change the state of Lexer. 38 | fn (mut lexer Lexer) lookahead() &Token { 39 | mut token := lexer.token 40 | 41 | if token.kind != TokenKind.eof { 42 | for { 43 | if next := token.next { 44 | token = next 45 | } else { 46 | // println('char ends at ${token.end}') 47 | mut next_token := read_next_token(mut lexer, token.end) or { panic(err) } 48 | 49 | // println('...<< prev token ${token.kind}') 50 | // println('...>> next token ${next_token.kind}') 51 | // println('...>> next token VALUE ${next_token.value}') 52 | token.next = &next_token 53 | next_token.prev = token 54 | token = &next_token 55 | } 56 | 57 | // Exits loop 58 | if token.kind != TokenKind.comment { 59 | break 60 | } 61 | } 62 | } 63 | 64 | return token 65 | } 66 | 67 | fn read_next_token(mut lexer Lexer, start int) !Token { 68 | body := lexer.source.body 69 | 70 | mut body_length := body.len 71 | mut position := start 72 | 73 | // println('==== BODY_LENGTH ${body_length}') 74 | 75 | for { 76 | // println('==== POSITION ${position}') 77 | // println('==== LINE_START ${lexer.line_start}, COLUMN ${lexer.token.column}') 78 | if position >= body_length { 79 | // println('EOF reached') 80 | break 81 | } 82 | 83 | code := body[position] 84 | 85 | // println('code : "${code.ascii_str()}"') 86 | 87 | match code { 88 | // Ignored :: 89 | // - UnicodeBOM 90 | // - WhiteSpace 91 | // - LineTerminator 92 | // - Comment 93 | // - Comma 94 | // 95 | // UnicodeBOM :: "Byte Order Mark (U+FEFF)" 96 | // 97 | // WhiteSpace :: 98 | // - "Horizontal Tab (U+0009)" 99 | // - "Space (U+0020)" 100 | // 101 | // Comma :: , 102 | 0xfeff, 0x0009, 0x0020, 0x002c { 103 | position += 1 104 | continue 105 | } 106 | // LineTerminator :: 107 | // - "New Line (U+000A)" 108 | // - "Carriage Return (U+000D)" [lookahead != "New Line (U+000A)"] 109 | // - "Carriage Return (U+000D)" "New Line (U+000A)" 110 | // \n 111 | 0x000a { 112 | position += 1 113 | lexer.line += 1 114 | lexer.line_start = position 115 | continue 116 | } 117 | // \r 118 | 0x000d { 119 | if body[position + 1] == 0x000a { 120 | position += 2 121 | } else { 122 | position += 1 123 | } 124 | 125 | lexer.line += 1 126 | lexer.line_start = position 127 | continue 128 | } 129 | // # (comment) 130 | 0x0023 { 131 | return lexer.read_comment(position) 132 | } 133 | // Token :: 134 | // - Punctuator 135 | // - Name 136 | // - IntValue 137 | // - FloatValue 138 | // - StringValue 139 | // 140 | // Punctuator :: one of ! $ & ( ) ... : = @ [ ] { | } 141 | 0x0021 { // ! 142 | return create_token(lexer, TokenKind.bang, position, position + 1, none) 143 | } 144 | 0x0024 { // $ 145 | return create_token(lexer, TokenKind.dollar, position, position + 1, none) 146 | } 147 | 0x0026 { // & 148 | return create_token(lexer, TokenKind.amp, position, position + 1, none) 149 | } 150 | 0x0028 { // ( 151 | return create_token(lexer, TokenKind.paren_l, position, position + 1, 152 | none) 153 | } 154 | 0x0029 { // ) 155 | return create_token(lexer, TokenKind.paren_r, position, position + 1, 156 | none) 157 | } 158 | 0x002e { // . 159 | if body[position + 1] == 0x002e && body[position + 2] == 0x002e { 160 | return create_token(lexer, TokenKind.spread, position, position + 3, 161 | none) 162 | } 163 | break 164 | } 165 | 0x003a { // : 166 | return create_token(lexer, TokenKind.colon, position, position + 1, none) 167 | } 168 | 0x003d { // = 169 | return create_token(lexer, TokenKind.equals, position, position + 1, none) 170 | } 171 | 0x0040 { // @ 172 | return create_token(lexer, TokenKind.at, position, position + 1, none) 173 | } 174 | 0x005b { // [ 175 | return create_token(lexer, TokenKind.bracket_l, position, position + 1, 176 | none) 177 | } 178 | 0x005d { // ] 179 | return create_token(lexer, TokenKind.bracket_r, position, position + 1, 180 | none) 181 | } 182 | 0x007b { // { 183 | return create_token(lexer, TokenKind.brace_l, position, position + 1, 184 | none) 185 | } 186 | 0x007c { // | 187 | return create_token(lexer, TokenKind.pipe, position, position + 1, none) 188 | } 189 | 0x007d { // } 190 | return create_token(lexer, TokenKind.brace_r, position, position + 1, 191 | none) 192 | } 193 | 0x003f { // ? 194 | return create_token(lexer, TokenKind.question_mark, position, position + 1, 195 | none) 196 | } 197 | 0x0022 { // " 198 | 199 | if body[position + 1] == 0x0022 && body[position + 2] == 0x0022 { 200 | // println('matched comment') 201 | 202 | return lexer.read_block_string(position) 203 | } 204 | return lexer.read_string(position) 205 | } 206 | else { 207 | // Do nothing 208 | } 209 | } 210 | 211 | // IntValue | FloatValue (Digit | -) 212 | if is_digit(code) || code == 0x002d { 213 | return lexer.read_number(position, code) or { panic(err) } 214 | } 215 | 216 | // Name 217 | if is_name_start(code) { 218 | name_token := lexer.read_name(position) 219 | // println('code (NAME) : ${name_token.value}') 220 | return name_token 221 | } 222 | 223 | return error('unexpected character') 224 | } 225 | 226 | return create_token(lexer, TokenKind.eof, body_length, body_length, none) 227 | } 228 | 229 | fn (lexer Lexer) read_name(start int) Token { 230 | body := lexer.source.body 231 | body_length := body.len 232 | 233 | mut position := start + 1 234 | 235 | for { 236 | if position >= body_length { 237 | break 238 | } 239 | 240 | code := body[position] 241 | if is_name_continue(code) { 242 | // println('name continuing ${body[start..position]}') 243 | position += 1 244 | } else { 245 | break 246 | } 247 | } 248 | 249 | return create_token(lexer, TokenKind.name, start, position, body[start..position]) 250 | } 251 | 252 | fn (lexer Lexer) read_number(start int, first_code u8) !Token { 253 | body := lexer.source.body 254 | 255 | mut code := first_code 256 | mut position := start 257 | mut is_float := false 258 | 259 | // NegativeSign (-) 260 | if code == 0x002d { 261 | position += 1 262 | code = body[position] 263 | } 264 | 265 | // Zero(0) 266 | if code == 0x0030 { 267 | position += 1 268 | 269 | code = body[position] 270 | 271 | if is_digit(code) { 272 | return error('Invalid number, unexpected digit after 0') 273 | } 274 | } else { 275 | position = lexer.read_digits(position, code) 276 | code = body[position] 277 | } 278 | 279 | // Full stop (.) 280 | if code == 0x002e { 281 | is_float = true 282 | 283 | position += 1 284 | code = body[position] 285 | position = lexer.read_digits(position, code) 286 | code = body[position] 287 | } 288 | 289 | if code == 0x0045 || code == 0x0065 { 290 | is_float = true 291 | position += 1 292 | code = body[position] 293 | 294 | // + - 295 | if code == 0x002b || code == 0x002d { 296 | position += 1 297 | code = body[position] 298 | } 299 | 300 | position = lexer.read_digits(position, code) 301 | code = body[position] 302 | } 303 | 304 | // Numbers cannot be followed by . or NameStart 305 | if code == 0x002e || is_name_start(code) { 306 | return error('INvalid number') 307 | } 308 | 309 | kind := if is_float { 310 | TokenKind.float 311 | } else { 312 | TokenKind.integer 313 | } 314 | return create_token(lexer, kind, start, position, body[start..position]) 315 | } 316 | 317 | fn (lexer Lexer) read_digits(start int, first_code u8) int { 318 | if !is_digit(first_code) { 319 | error('Invalid number') 320 | } 321 | 322 | body := lexer.source.body 323 | mut position := start + 1 // +1 to skip first first_code 324 | 325 | for { 326 | if !is_digit(body[position]) { 327 | break 328 | } 329 | position += 1 330 | } 331 | 332 | return position 333 | } 334 | 335 | fn (lexer Lexer) read_string(start int) Token { 336 | body := lexer.source.body 337 | body_length := body.len 338 | 339 | mut position := start + 1 340 | mut chunk_start := position 341 | 342 | mut value := '' 343 | 344 | for { 345 | if position >= body_length { 346 | break 347 | } 348 | 349 | code := body[position] 350 | 351 | // Closing Quote (") 352 | if code == 0x0022 { 353 | value += body[chunk_start..position] 354 | return create_token(lexer, TokenKind.string_value, start, position + 1, value) 355 | } 356 | 357 | // Escape Sequence (\) 358 | if code == 0x005c { 359 | value += body[chunk_start..position] 360 | 361 | escape := if body[position + 1] == 0x0075 { 362 | if body[position + 2] == 0x007b { 363 | read_escaped_unicode_variable_width(lexer, position) 364 | } else { 365 | read_escaped_unicode_fixed_width(lexer, position) 366 | } 367 | } else { 368 | read_escaped_character(lexer, position) 369 | } 370 | value += escape.value 371 | position += escape.size 372 | chunk_start += position 373 | continue 374 | } 375 | 376 | // LineTerminator (\n | \r) 377 | if code == 0x000a || code == 0x000d { 378 | break 379 | } 380 | 381 | // SourceCharacter 382 | if is_unicode_scalar_value(code) { 383 | position++ 384 | } else if is_supplementary_code_point(body, position) { 385 | position += 2 386 | } else { 387 | panic('invalid char') 388 | } 389 | } 390 | 391 | panic('unterminated string') 392 | } 393 | 394 | fn (mut lexer Lexer) read_comment(start int) Token { 395 | body := lexer.source.body 396 | body_length := body.len 397 | 398 | mut position := start + 1 399 | 400 | for { 401 | if position >= body_length { 402 | break 403 | } 404 | 405 | code := body[position] 406 | 407 | // LineTerminator (\n | \r) 408 | if code == 0x000a || code == 0x000d { 409 | break 410 | } 411 | 412 | // SourceCharacter 413 | if is_unicode_scalar_value(code) { 414 | position++ 415 | } else if is_supplementary_code_point(body, position) { 416 | position += 2 417 | } else { 418 | break 419 | } 420 | } 421 | 422 | return create_token(lexer, TokenKind.comment, start, position, body[start + 1..position]) 423 | } 424 | 425 | fn (mut lexer Lexer) read_block_string(start int) Token { 426 | body := lexer.source.body 427 | body_length := body.len 428 | 429 | mut line_start := lexer.line_start 430 | 431 | mut position := start + 3 432 | mut chunk_start := position 433 | mut current_line := '' 434 | 435 | mut block_lines := []string{} 436 | 437 | for { 438 | if position >= body_length { 439 | break 440 | } 441 | 442 | code := body[position] 443 | 444 | // Closing Triple-Quote (""") 445 | if code == 0x0022 && body[position + 1] == 0x0022 && body[position + 2] == 0x0022 { 446 | current_line += body[chunk_start..position] 447 | block_lines << current_line 448 | 449 | // println('dedented') 450 | // println(dedent_block_string_lines(block_lines).join('\n')) 451 | token := create_token(lexer, TokenKind.block_string, start, position + 3, 452 | dedent_block_string_lines(block_lines).join('\n')) 453 | 454 | // println('LINE ${block_lines.len - 1}') 455 | lexer.line += block_lines.len - 1 456 | lexer.line_start = line_start 457 | 458 | return token 459 | } 460 | 461 | // Escaped Triple-Quote (\""") 462 | if code == 0x005c && body[position + 1] == 0x0022 && body[position + 2] == 0x0022 463 | && body[position + 3] == 0x0022 { 464 | current_line += body[chunk_start..position] 465 | // skip only slash 466 | chunk_start = position + 1 467 | 468 | position += 4 469 | continue 470 | } 471 | 472 | // LineTerminator 473 | if code == 0x000a || code == 0x000d { 474 | current_line += body[chunk_start..position] 475 | block_lines << current_line 476 | 477 | if code == 0x000d && body[position + 1] == 0x000a { 478 | position += 2 479 | } else { 480 | position++ 481 | } 482 | 483 | current_line = '' 484 | chunk_start = position 485 | line_start = position 486 | 487 | continue 488 | } 489 | 490 | if is_unicode_scalar_value(code) { 491 | position += 1 492 | } else if is_supplementary_code_point(body, position) { 493 | position += 2 494 | } else { 495 | error('Invalid character within string') 496 | } 497 | } 498 | 499 | panic('unterminated string') 500 | } 501 | 502 | fn create_token(lexer Lexer, kind TokenKind, start int, end int, value ?string) Token { 503 | line := lexer.line 504 | col := 1 + start - lexer.line_start 505 | 506 | return Token{ 507 | kind: kind 508 | start: start 509 | end: end 510 | line: line 511 | column: col 512 | value: value 513 | } 514 | } 515 | 516 | fn is_punctuator_token_kind(kind TokenKind) bool { 517 | return match kind { 518 | .bang, .question_mark, .dollar, .amp, .paren_l, .paren_r, .spread, .colon, .equals, .at, 519 | .bracket_l, .bracket_r, .brace_l, .pipe, .brace_r { 520 | true 521 | } 522 | else { 523 | false 524 | } 525 | } 526 | } 527 | 528 | struct EscapeSequence { 529 | value string 530 | size int 531 | } 532 | 533 | fn read_escaped_unicode_variable_width(lexer Lexer, position int) EscapeSequence { 534 | body := lexer.source.body 535 | mut point := 0 536 | mut size := 3 537 | 538 | for { 539 | // Cannot be larger than 12 chars (\u{00000000}). 540 | if size > 12 { 541 | break 542 | } 543 | 544 | size++ 545 | code := body[position + size] 546 | 547 | if code == 0x007d { 548 | if size < 5 || !is_unicode_scalar_value(point) { 549 | break 550 | } 551 | 552 | return EscapeSequence{ 553 | value: u8(point).ascii_str() 554 | size: size 555 | } 556 | } 557 | 558 | point = (point << 4) | read_hex_digit(code) 559 | if point < 0 { 560 | break 561 | } 562 | } 563 | 564 | panic('Invalid unicode escape sequence') 565 | } 566 | 567 | fn read_escaped_unicode_fixed_width(lexer Lexer, position int) EscapeSequence { 568 | body := lexer.source.body 569 | code := read16_bit_hex_code(body, position + 2) 570 | 571 | if is_unicode_scalar_value(code) { 572 | return EscapeSequence{ 573 | value: u8(code).ascii_str() 574 | size: 6 575 | } 576 | } 577 | 578 | // GraphQL allows JSON-style surrogate pair escape sequences, but only when 579 | // a valid pair is formed. 580 | if is_leading_surrogate(code) { 581 | // \u 582 | if body[position + 6] == 0x005c && body[position + 7] == 0x0075 { 583 | trailing_code := read16_bit_hex_code(body, position + 8) 584 | 585 | if is_trailing_surrogate(trailing_code) { 586 | return EscapeSequence{ 587 | value: [u8(code), u8(trailing_code)].bytestr() 588 | size: 12 589 | } 590 | } 591 | } 592 | } 593 | 594 | panic('Invalid Unicode escape sequence:') 595 | } 596 | 597 | // | Escaped Character | Code Point | Character Name | 598 | // | ----------------- | ---------- | ---------------------------- | 599 | // | `"` | U+0022 | double quote | 600 | // | `\` | U+005C | reverse solidus (back slash) | 601 | // | `/` | U+002F | solidus (forward slash) | 602 | // | `b` | U+0008 | backspace | 603 | // | `f` | U+000C | form feed | 604 | // | `n` | U+000A | line feed (new line) | 605 | // | `r` | U+000D | carriage return | 606 | // | `t` | U+0009 | horizontal tab | 607 | fn read_escaped_character(lexer Lexer, position int) EscapeSequence { 608 | body := lexer.source.body 609 | code := body[position + 1] 610 | 611 | match code { 612 | 0x0022 { // "" 613 | return EscapeSequence{ 614 | value: `"`.str() 615 | size: 2 616 | } 617 | } 618 | 0x005c { // \ 619 | return EscapeSequence{ 620 | value: `\\`.str() 621 | size: 2 622 | } 623 | } 624 | 0x002f { // / 625 | return EscapeSequence{ 626 | value: `/`.str() 627 | size: 2 628 | } 629 | } 630 | 0x0062 { // b 631 | return EscapeSequence{ 632 | value: `b`.str() 633 | size: 2 634 | } 635 | } 636 | 0x0066 { // f 637 | return EscapeSequence{ 638 | value: `f`.str() 639 | size: 2 640 | } 641 | } 642 | 0x006e { // n 643 | return EscapeSequence{ 644 | value: `n`.str() 645 | size: 2 646 | } 647 | } 648 | 0x0072 { // r 649 | return EscapeSequence{ 650 | value: `r`.str() 651 | size: 2 652 | } 653 | } 654 | 0x0074 { // t 655 | return EscapeSequence{ 656 | value: `t`.str() 657 | size: 2 658 | } 659 | } 660 | else { 661 | panic('Invalid character escape sequence') 662 | } 663 | } 664 | } 665 | 666 | // Reads four hexadecimal characters and returns the positive integer that 16bit 667 | // hexadecimal string represents. For example, "000f" will return 15, and "dead" 668 | // will return 57005. 669 | // 670 | // Returns a negative number if any char was not a valid hexadecimal digit. 671 | fn read16_bit_hex_code(body string, position int) int { 672 | return (read_hex_digit(body[position]) << 12) | (read_hex_digit(body[position + 1]) << 8) | (read_hex_digit(body[ 673 | position + 2]) << 4) | read_hex_digit(body[position + 3]) 674 | } 675 | 676 | fn read_hex_digit(code u8) int { 677 | // 0-9 678 | if code >= 0x0030 && code <= 0x0039 { 679 | return code - 0x0030 680 | } 681 | 682 | // A-F 683 | if code >= 0x0041 && code <= 0x0046 { 684 | return code - 0x0037 685 | } 686 | 687 | // a-f 688 | if code >= 0x0061 && code <= 0x0066 { 689 | return code - 0x0057 690 | } 691 | 692 | return -1 693 | } 694 | -------------------------------------------------------------------------------- /src/src.graphql.md: -------------------------------------------------------------------------------- 1 | # module src.graphql 2 | 3 | ## Contents 4 | 5 | - [print_block_string](#print_block_string) 6 | - [parse](#parse) 7 | - [is_printable_as_block_string](#is_printable_as_block_string) 8 | - [get_location](#get_location) 9 | - [IDirectiveNode](#IDirectiveNode) 10 | - [ASTNodeInterface](#ASTNodeInterface) 11 | - [DefinitionNode](#DefinitionNode) 12 | - [NullabilityAssertionNode](#NullabilityAssertionNode) 13 | - [ExecutableDefinitionNode](#ExecutableDefinitionNode) 14 | - [SelectionNode](#SelectionNode) 15 | - [TypeSystemExtensionNode](#TypeSystemExtensionNode) 16 | - [ValueNode](#ValueNode) 17 | - [NonNullType](#NonNullType) 18 | - [ASTNode](#ASTNode) 19 | - [TypeDefinitionNode](#TypeDefinitionNode) 20 | - [TypeDirectives](#TypeDirectives) 21 | - [TypeExtensionNode](#TypeExtensionNode) 22 | - [TypeNode](#TypeNode) 23 | - [TypeSystemDefinitionNode](#TypeSystemDefinitionNode) 24 | - [Kind](#Kind) 25 | - [gql_str](#gql_str) 26 | - [TokenKind](#TokenKind) 27 | - [gql_str](#gql_str) 28 | - [OperationTypeNode](#OperationTypeNode) 29 | - [gql_str](#gql_str) 30 | - [DirectiveLocation](#DirectiveLocation) 31 | - [InterfaceTypeDefinitionNode](#InterfaceTypeDefinitionNode) 32 | - [InterfaceTypeExtensionNode](#InterfaceTypeExtensionNode) 33 | - [IntValueNode](#IntValueNode) 34 | - [Lexer](#Lexer) 35 | - [ListNullabilityOperatorNode](#ListNullabilityOperatorNode) 36 | - [ListTypeNode](#ListTypeNode) 37 | - [ListValueNode](#ListValueNode) 38 | - [Location](#Location) 39 | - [NamedTypeNode](#NamedTypeNode) 40 | - [NameNode](#NameNode) 41 | - [NonNullAssertionNode](#NonNullAssertionNode) 42 | - [NonNullTypeNode](#NonNullTypeNode) 43 | - [NullValueNode](#NullValueNode) 44 | - [ObjectFieldNode](#ObjectFieldNode) 45 | - [ObjectTypeDefinitionNode](#ObjectTypeDefinitionNode) 46 | - [ObjectTypeExtensionNode](#ObjectTypeExtensionNode) 47 | - [ObjectValueNode](#ObjectValueNode) 48 | - [OperationDefinitionNode](#OperationDefinitionNode) 49 | - [OperationTypeDefinitionNode](#OperationTypeDefinitionNode) 50 | - [Parser](#Parser) 51 | - [ParserOptions](#ParserOptions) 52 | - [ScalarTypeDefinitionNode](#ScalarTypeDefinitionNode) 53 | - [ScalarTypeExtensionNode](#ScalarTypeExtensionNode) 54 | - [SchemaDefinitionNode](#SchemaDefinitionNode) 55 | - [SchemaExtensionNode](#SchemaExtensionNode) 56 | - [SelectionSetNode](#SelectionSetNode) 57 | - [Source](#Source) 58 | - [StringValueNode](#StringValueNode) 59 | - [Token](#Token) 60 | - [UnionTypeDefinitionNode](#UnionTypeDefinitionNode) 61 | - [UnionTypeExtensionNode](#UnionTypeExtensionNode) 62 | - [VariableDefinitionNode](#VariableDefinitionNode) 63 | - [VariableNode](#VariableNode) 64 | - [BlockStringOptions](#BlockStringOptions) 65 | - [BooleanValueNode](#BooleanValueNode) 66 | - [ConstArgumentNode](#ConstArgumentNode) 67 | - [ConstObjectFieldNode](#ConstObjectFieldNode) 68 | - [ConstObjectValueNode](#ConstObjectValueNode) 69 | - [DirectiveDefinitionNode](#DirectiveDefinitionNode) 70 | - [DirectiveNode](#DirectiveNode) 71 | - [DocumentNode](#DocumentNode) 72 | - [EnumTypeDefinitionNode](#EnumTypeDefinitionNode) 73 | - [EnumTypeExtensionNode](#EnumTypeExtensionNode) 74 | - [EnumValueDefinitionNode](#EnumValueDefinitionNode) 75 | - [EnumValueNode](#EnumValueNode) 76 | - [ErrorBoundaryNode](#ErrorBoundaryNode) 77 | - [FieldDefinitionNode](#FieldDefinitionNode) 78 | - [FieldNode](#FieldNode) 79 | - [FloatValueNode](#FloatValueNode) 80 | - [FragmentDefinitionNode](#FragmentDefinitionNode) 81 | - [FragmentSpreadNode](#FragmentSpreadNode) 82 | - [InlineFragmentNode](#InlineFragmentNode) 83 | - [InputObjectTypeDefinitionNode](#InputObjectTypeDefinitionNode) 84 | - [InputObjectTypeExtensionNode](#InputObjectTypeExtensionNode) 85 | - [InputValueDefinitionNode](#InputValueDefinitionNode) 86 | 87 | ## print_block_string 88 | 89 | ```v 90 | fn print_block_string(value string, options ?BlockStringOptions) ?string 91 | ``` 92 | 93 | Print a block string in the indented block form by adding a leading and trailing blank line. 94 | However, if a block string starts with whitespace and is a single-line, adding a leading blank line would strip that whitespace. 95 | 96 | [[Return to contents]](#Contents) 97 | 98 | ## parse 99 | 100 | ```v 101 | fn parse(source SourceOrString, options ?ParserOptions) !DocumentNode 102 | ``` 103 | 104 | [[Return to contents]](#Contents) 105 | 106 | ## is_printable_as_block_string 107 | 108 | ```v 109 | fn is_printable_as_block_string(value string) bool 110 | ``` 111 | 112 | Check if a string is printable as a block string. 113 | 114 | [[Return to contents]](#Contents) 115 | 116 | ## get_location 117 | 118 | ```v 119 | fn get_location(source Source, position int) !SourceLocation 120 | ``` 121 | 122 | getLocation takes a Source and a UTF-8 character offset, 123 | and returns the corresponding line and column as a SourceLocation. 124 | 125 | [[Return to contents]](#Contents) 126 | 127 | ## IDirectiveNode 128 | 129 | ```v 130 | interface IDirectiveNode { 131 | kind Kind 132 | name NameNode 133 | arguments ?[]ArgumentNode 134 | mut: 135 | loc ?Location 136 | } 137 | ``` 138 | 139 | [[Return to contents]](#Contents) 140 | 141 | ## ASTNodeInterface 142 | 143 | ```v 144 | interface ASTNodeInterface { 145 | mut: 146 | loc ?Location 147 | } 148 | ``` 149 | 150 | All AST Nodes should implement a MUTable common loc property 151 | 152 | [[Return to contents]](#Contents) 153 | 154 | ## DefinitionNode 155 | 156 | ```v 157 | type DefinitionNode = DirectiveDefinitionNode 158 | | EnumTypeDefinitionNode 159 | | FragmentDefinitionNode 160 | | InputObjectTypeDefinitionNode 161 | | InterfaceTypeDefinitionNode 162 | | ObjectTypeDefinitionNode 163 | | OperationDefinitionNode 164 | | ScalarTypeDefinitionNode 165 | | SchemaDefinitionNode 166 | | SchemaExtensionNode 167 | | TypeExtensionNode 168 | | UnionTypeDefinitionNode 169 | ``` 170 | 171 | [[Return to contents]](#Contents) 172 | 173 | ## NullabilityAssertionNode 174 | 175 | ```v 176 | type NullabilityAssertionNode = ErrorBoundaryNode 177 | | ListNullabilityOperatorNode 178 | | NonNullAssertionNode 179 | ``` 180 | 181 | [[Return to contents]](#Contents) 182 | 183 | ## ExecutableDefinitionNode 184 | 185 | ```v 186 | type ExecutableDefinitionNode = FragmentDefinitionNode | OperationDefinitionNode 187 | ``` 188 | 189 | [[Return to contents]](#Contents) 190 | 191 | ## SelectionNode 192 | 193 | ```v 194 | type SelectionNode = FieldNode | FragmentSpreadNode | InlineFragmentNode 195 | ``` 196 | 197 | [[Return to contents]](#Contents) 198 | 199 | ## TypeSystemExtensionNode 200 | 201 | ```v 202 | type TypeSystemExtensionNode = SchemaExtensionNode | TypeExtensionNode 203 | ``` 204 | 205 | [[Return to contents]](#Contents) 206 | 207 | ## ValueNode 208 | 209 | ```v 210 | type ValueNode = BooleanValueNode 211 | | ConstObjectValueNode 212 | | EnumValueNode 213 | | FloatValueNode 214 | | IntValueNode 215 | | ListValueNode 216 | | NullValueNode 217 | | ObjectValueNode 218 | | StringValueNode 219 | | VariableNode 220 | ``` 221 | 222 | -- 223 | 224 | [[Return to contents]](#Contents) 225 | 226 | ## NonNullType 227 | 228 | ```v 229 | type NonNullType = ListTypeNode | NamedTypeNode 230 | ``` 231 | 232 | [[Return to contents]](#Contents) 233 | 234 | ## ASTNode 235 | 236 | ```v 237 | type ASTNode = ArgumentNode 238 | | BooleanValueNode 239 | | DirectiveDefinitionNode 240 | | DirectiveNode 241 | | DocumentNode 242 | | EnumTypeDefinitionNode 243 | | EnumTypeExtensionNode 244 | | EnumValueDefinitionNode 245 | | EnumValueNode 246 | | ErrorBoundaryNode 247 | | FieldDefinitionNode 248 | | FieldNode 249 | | FloatValueNode 250 | | FragmentDefinitionNode 251 | | FragmentSpreadNode 252 | | InlineFragmentNode 253 | | InputObjectTypeDefinitionNode 254 | | InputObjectTypeExtensionNode 255 | | InputValueDefinitionNode 256 | | IntValueNode 257 | | InterfaceTypeDefinitionNode 258 | | InterfaceTypeExtensionNode 259 | | ListNullabilityOperatorNode 260 | | ListTypeNode 261 | | ListValueNode 262 | | NameNode 263 | | NamedTypeNode 264 | | NonNullAssertionNode 265 | | NonNullTypeNode 266 | | NullValueNode 267 | | ObjectFieldNode 268 | | ObjectTypeDefinitionNode 269 | | ObjectTypeExtensionNode 270 | | ObjectValueNode 271 | | OperationDefinitionNode 272 | | OperationTypeDefinitionNode 273 | | ScalarTypeDefinitionNode 274 | | ScalarTypeExtensionNode 275 | | SchemaDefinitionNode 276 | | SchemaExtensionNode 277 | | SelectionSetNode 278 | | StringValueNode 279 | | UnionTypeDefinitionNode 280 | | UnionTypeExtensionNode 281 | | VariableDefinitionNode 282 | | VariableNode 283 | ``` 284 | 285 | The list of all possible AST node types. 286 | 287 | [[Return to contents]](#Contents) 288 | 289 | ## TypeDefinitionNode 290 | 291 | ```v 292 | type TypeDefinitionNode = EnumTypeDefinitionNode 293 | | InputObjectTypeDefinitionNode 294 | | InterfaceTypeDefinitionNode 295 | | ObjectTypeDefinitionNode 296 | | ScalarTypeDefinitionNode 297 | | UnionTypeDefinitionNode 298 | ``` 299 | 300 | Type definition 301 | 302 | [[Return to contents]](#Contents) 303 | 304 | ## TypeDirectives 305 | 306 | ```v 307 | type TypeDirectives = DirectiveNode 308 | ``` 309 | 310 | Directives 311 | 312 | [[Return to contents]](#Contents) 313 | 314 | ## TypeExtensionNode 315 | 316 | ```v 317 | type TypeExtensionNode = EnumTypeExtensionNode 318 | | InputObjectTypeExtensionNode 319 | | InterfaceTypeExtensionNode 320 | | ObjectTypeExtensionNode 321 | | ScalarTypeExtensionNode 322 | | UnionTypeExtensionNode 323 | ``` 324 | 325 | Type Extensions 326 | 327 | [[Return to contents]](#Contents) 328 | 329 | ## TypeNode 330 | 331 | ```v 332 | type TypeNode = ListTypeNode | NamedTypeNode | NonNullTypeNode 333 | ``` 334 | 335 | Type reference 336 | 337 | [[Return to contents]](#Contents) 338 | 339 | ## TypeSystemDefinitionNode 340 | 341 | ```v 342 | type TypeSystemDefinitionNode = DirectiveDefinitionNode 343 | | SchemaDefinitionNode 344 | | TypeDefinitionNode 345 | ``` 346 | 347 | Type system definition 348 | 349 | [[Return to contents]](#Contents) 350 | 351 | ## Kind 352 | 353 | ```v 354 | enum Kind { 355 | name 356 | document 357 | // Documen 358 | operation_definition 359 | variable_definition 360 | selection_set 361 | field 362 | argument 363 | // Nullability Modifier 364 | list_nullability_operator 365 | non_null_assertion 366 | error_boundary 367 | // Fragments * 368 | fragment_spread 369 | inline_fragment 370 | fragment_definition 371 | // Value 372 | variable 373 | int_value 374 | float_value 375 | string_value 376 | boolean 377 | null 378 | enum_value 379 | list 380 | object 381 | object_field 382 | // Directive 383 | directive 384 | // Type 385 | named_type 386 | list_type 387 | non_null_type 388 | // Type System Definition 389 | schema_definition 390 | operation_type_definition 391 | // Type Definition 392 | scalar_type_definition 393 | object_type_definition 394 | field_definition 395 | input_value_definition 396 | interface_type_definition 397 | union_type_definition 398 | enum_type_definition 399 | enum_value_definition 400 | input_object_type_definition 401 | // Directive Definition 402 | directive_definition 403 | // Type System Extension 404 | schema_extension 405 | // Type Extension 406 | scalar_type_extension 407 | object_type_extension 408 | interface_type_extension 409 | union_type_extension 410 | enum_type_extension 411 | input_object_type_extension 412 | } 413 | ``` 414 | 415 | [[Return to contents]](#Contents) 416 | 417 | ## gql_str 418 | 419 | ```v 420 | fn (k Kind) gql_str() string 421 | ``` 422 | 423 | Returns the string value of the Kind enum 424 | 425 | [[Return to contents]](#Contents) 426 | 427 | ## TokenKind 428 | 429 | ```v 430 | enum TokenKind { 431 | sof 432 | eof 433 | bang 434 | question_mark 435 | dollar 436 | amp 437 | paren_l 438 | paren_r 439 | spread 440 | colon 441 | equals 442 | at 443 | bracket_l 444 | bracket_r 445 | brace_l 446 | pipe 447 | brace_r 448 | name 449 | integer 450 | float 451 | string_value 452 | block_string 453 | comment 454 | } 455 | ``` 456 | 457 | An exported enum describing the different kinds of tokens that the 458 | lexer emits. 459 | 460 | [[Return to contents]](#Contents) 461 | 462 | ## gql_str 463 | 464 | ```v 465 | fn (t TokenKind) gql_str() string 466 | ``` 467 | 468 | Returns the character value of the TokenKind enum 469 | 470 | [[Return to contents]](#Contents) 471 | 472 | ## OperationTypeNode 473 | 474 | ```v 475 | enum OperationTypeNode { 476 | query 477 | mutation 478 | subscription 479 | } 480 | ``` 481 | 482 | [[Return to contents]](#Contents) 483 | 484 | ## gql_str 485 | 486 | ```v 487 | fn (o OperationTypeNode) gql_str() string 488 | ``` 489 | 490 | [[Return to contents]](#Contents) 491 | 492 | ## DirectiveLocation 493 | 494 | ```v 495 | enum DirectiveLocation { 496 | // Request Definitions 497 | query 498 | mutation 499 | subscription 500 | field 501 | fragment_definition 502 | fragment_spread 503 | inline_fragment 504 | variable_definition 505 | // Type System Definitions 506 | schema 507 | scalar 508 | object 509 | field_definition 510 | argument_definition 511 | @interface 512 | @union 513 | @enum 514 | enum_value 515 | input_object 516 | input_field_definition 517 | } 518 | ``` 519 | 520 | [[Return to contents]](#Contents) 521 | 522 | ## InterfaceTypeDefinitionNode 523 | 524 | ```v 525 | struct InterfaceTypeDefinitionNode { 526 | kind Kind = Kind.interface_type_definition 527 | description ?StringValueNode 528 | name NameNode 529 | interfaces ?[]NamedTypeNode 530 | directives ?[]DirectiveNode 531 | fields ?[]FieldDefinitionNode 532 | pub mut: 533 | loc ?Location 534 | } 535 | ``` 536 | 537 | [[Return to contents]](#Contents) 538 | 539 | ## InterfaceTypeExtensionNode 540 | 541 | ```v 542 | struct InterfaceTypeExtensionNode { 543 | kind Kind = Kind.interface_type_extension 544 | name NameNode 545 | interfaces ?[]NamedTypeNode 546 | directives ?[]DirectiveNode 547 | fields ?[]FieldDefinitionNode 548 | pub mut: 549 | loc ?Location 550 | } 551 | ``` 552 | 553 | [[Return to contents]](#Contents) 554 | 555 | ## IntValueNode 556 | 557 | ```v 558 | struct IntValueNode { 559 | pub: 560 | kind Kind = Kind.int_value 561 | value ?string 562 | is_const bool 563 | pub mut: 564 | loc ?Location 565 | } 566 | ``` 567 | 568 | [[Return to contents]](#Contents) 569 | 570 | ## Lexer 571 | 572 | ```v 573 | struct Lexer { 574 | pub: 575 | source Source 576 | mut: 577 | line_start int 578 | line int 579 | last_token &Token 580 | token &Token 581 | } 582 | ``` 583 | 584 | [[Return to contents]](#Contents) 585 | 586 | ## ListNullabilityOperatorNode 587 | 588 | ```v 589 | struct ListNullabilityOperatorNode { 590 | pub: 591 | kind Kind = Kind.list_nullability_operator 592 | nullability_assertion ?NullabilityAssertionNode 593 | pub mut: 594 | loc ?Location 595 | } 596 | ``` 597 | 598 | [[Return to contents]](#Contents) 599 | 600 | ## ListTypeNode 601 | 602 | ```v 603 | struct ListTypeNode { 604 | pub: 605 | kind Kind = Kind.list_type 606 | node_type TypeNode 607 | pub mut: 608 | loc ?Location 609 | } 610 | ``` 611 | 612 | [[Return to contents]](#Contents) 613 | 614 | ## ListValueNode 615 | 616 | ```v 617 | struct ListValueNode { 618 | pub: 619 | kind Kind = Kind.list 620 | values []ValueNode 621 | is_const bool 622 | pub mut: 623 | loc ?Location 624 | } 625 | ``` 626 | 627 | [[Return to contents]](#Contents) 628 | 629 | ## Location 630 | 631 | ```v 632 | struct Location { 633 | pub: 634 | // The character offset at which this Node begins. 635 | start int 636 | // The character offset at which this Node ends. 637 | end int 638 | // The Token at which this Node begins. 639 | start_token ?&Token 640 | // The Token at which this Node ends. 641 | end_token ?&Token 642 | // The Source document the AST represents. 643 | source Source 644 | } 645 | ``` 646 | 647 | [[Return to contents]](#Contents) 648 | 649 | ## NamedTypeNode 650 | 651 | ```v 652 | struct NamedTypeNode { 653 | pub: 654 | kind Kind = Kind.named_type 655 | node_type NameNode 656 | pub mut: 657 | loc ?Location 658 | } 659 | ``` 660 | 661 | [[Return to contents]](#Contents) 662 | 663 | ## NameNode 664 | 665 | ```v 666 | struct NameNode { 667 | pub: 668 | kind Kind = Kind.name 669 | value string 670 | pub mut: 671 | loc ?Location 672 | } 673 | ``` 674 | 675 | [[Return to contents]](#Contents) 676 | 677 | ## NonNullAssertionNode 678 | 679 | ```v 680 | struct NonNullAssertionNode { 681 | pub: 682 | kind Kind = Kind.non_null_assertion 683 | nullability_assertion ?ListNullabilityOperatorNode 684 | pub mut: 685 | loc ?Location 686 | } 687 | ``` 688 | 689 | [[Return to contents]](#Contents) 690 | 691 | ## NonNullTypeNode 692 | 693 | ```v 694 | struct NonNullTypeNode { 695 | pub: 696 | kind Kind = Kind.non_null_type 697 | node_type NonNullType 698 | pub mut: 699 | loc ?Location 700 | } 701 | ``` 702 | 703 | [[Return to contents]](#Contents) 704 | 705 | ## NullValueNode 706 | 707 | ```v 708 | struct NullValueNode { 709 | pub: 710 | kind Kind = Kind.null 711 | is_const bool 712 | pub mut: 713 | loc ?Location 714 | } 715 | ``` 716 | 717 | [[Return to contents]](#Contents) 718 | 719 | ## ObjectFieldNode 720 | 721 | ```v 722 | struct ObjectFieldNode { 723 | pub: 724 | kind Kind = Kind.object_field 725 | name NameNode 726 | value ValueNode 727 | is_const bool 728 | pub mut: 729 | loc ?Location 730 | } 731 | ``` 732 | 733 | [[Return to contents]](#Contents) 734 | 735 | ## ObjectTypeDefinitionNode 736 | 737 | ```v 738 | struct ObjectTypeDefinitionNode { 739 | kind Kind 740 | description ?StringValueNode 741 | name NameNode 742 | interfaces ?[]NamedTypeNode 743 | directives ?[]DirectiveNode 744 | fields ?[]FieldDefinitionNode 745 | pub mut: 746 | loc ?Location 747 | } 748 | ``` 749 | 750 | [[Return to contents]](#Contents) 751 | 752 | ## ObjectTypeExtensionNode 753 | 754 | ```v 755 | struct ObjectTypeExtensionNode { 756 | kind Kind = Kind.object_type_extension 757 | name NameNode 758 | interfaces ?[]NamedTypeNode 759 | directives ?[]DirectiveNode 760 | fields ?[]FieldDefinitionNode 761 | pub mut: 762 | loc ?Location 763 | } 764 | ``` 765 | 766 | [[Return to contents]](#Contents) 767 | 768 | ## ObjectValueNode 769 | 770 | ```v 771 | struct ObjectValueNode { 772 | pub: 773 | kind Kind = Kind.object 774 | fields []ObjectFieldNode 775 | is_const bool 776 | pub mut: 777 | loc ?Location 778 | } 779 | ``` 780 | 781 | [[Return to contents]](#Contents) 782 | 783 | ## OperationDefinitionNode 784 | 785 | ```v 786 | struct OperationDefinitionNode { 787 | pub: 788 | kind Kind = Kind.operation_definition 789 | operation OperationTypeNode 790 | name ?NameNode 791 | variable_definitions ?[]VariableDefinitionNode 792 | directives ?[]DirectiveNode 793 | selection_set SelectionSetNode 794 | pub mut: 795 | loc ?Location 796 | } 797 | ``` 798 | 799 | [[Return to contents]](#Contents) 800 | 801 | ## OperationTypeDefinitionNode 802 | 803 | ```v 804 | struct OperationTypeDefinitionNode { 805 | pub: 806 | kind Kind = Kind.operation_type_definition 807 | operation OperationTypeNode 808 | @type NamedTypeNode 809 | pub mut: 810 | loc ?Location 811 | } 812 | ``` 813 | 814 | [[Return to contents]](#Contents) 815 | 816 | ## Parser 817 | 818 | ```v 819 | struct Parser { 820 | options ParserOptions 821 | mut: 822 | lexer Lexer 823 | token_counter int 824 | } 825 | ``` 826 | 827 | [[Return to contents]](#Contents) 828 | 829 | ## ParserOptions 830 | 831 | ```v 832 | struct ParserOptions { 833 | pub: 834 | // By default, the parser creates AST nodes that know the location 835 | // in the source that they correspond to. This configuration flag 836 | // disables that behavior for performance or testing. 837 | no_location bool 838 | // Parser CPU and memory usage is linear to the number of tokens in a document 839 | // however in extreme cases it becomes quadratic due to memory exhaustion. 840 | // Parsing happens before validation so even invalid queries can burn lots of 841 | // CPU time and memory. 842 | // To prevent this you can set a maximum number of tokens allowed within a document. 843 | // @default 50K tokens 844 | max_tokens int = 50_000 845 | // @deprecated 846 | allow_legacy_fragment_variables ?bool 847 | experimental_client_controlled_nullability ?bool 848 | } 849 | ``` 850 | 851 | [[Return to contents]](#Contents) 852 | 853 | ## ScalarTypeDefinitionNode 854 | 855 | ```v 856 | struct ScalarTypeDefinitionNode { 857 | kind Kind 858 | description ?StringValueNode 859 | name NameNode 860 | directives ?[]DirectiveNode 861 | pub mut: 862 | loc ?Location 863 | } 864 | ``` 865 | 866 | [[Return to contents]](#Contents) 867 | 868 | ## ScalarTypeExtensionNode 869 | 870 | ```v 871 | struct ScalarTypeExtensionNode { 872 | kind Kind = Kind.scalar_type_extension 873 | name NameNode 874 | directives ?[]DirectiveNode 875 | pub mut: 876 | loc ?Location 877 | } 878 | ``` 879 | 880 | [[Return to contents]](#Contents) 881 | 882 | ## SchemaDefinitionNode 883 | 884 | ```v 885 | struct SchemaDefinitionNode { 886 | pub: 887 | kind Kind = Kind.schema_definition 888 | description ?StringValueNode 889 | directives ?[]DirectiveNode 890 | operation_types []OperationTypeDefinitionNode 891 | pub mut: 892 | loc ?Location 893 | } 894 | ``` 895 | 896 | [[Return to contents]](#Contents) 897 | 898 | ## SchemaExtensionNode 899 | 900 | ```v 901 | struct SchemaExtensionNode { 902 | kind Kind = Kind.schema_extension 903 | directives ?[]DirectiveNode 904 | operation_types ?[]OperationTypeDefinitionNode 905 | pub mut: 906 | loc ?Location 907 | } 908 | ``` 909 | 910 | [[Return to contents]](#Contents) 911 | 912 | ## SelectionSetNode 913 | 914 | ```v 915 | struct SelectionSetNode { 916 | pub: 917 | kind Kind = Kind.selection_set 918 | selections []SelectionNode 919 | pub mut: 920 | loc ?Location 921 | } 922 | ``` 923 | 924 | [[Return to contents]](#Contents) 925 | 926 | ## Source 927 | 928 | ```v 929 | struct Source { 930 | pub: 931 | body string 932 | name string 933 | location_offset LocationOffset 934 | } 935 | ``` 936 | 937 | A representation of source input to GraphQL. The `name` and `locationOffset` parameters are 938 | optional, but they are useful for clients who store GraphQL documents in source files. 939 | For example, if the GraphQL input starts at line 40 in a file named `Foo.graphql`, it might 940 | be useful for `name` to be `"Foo.graphql"` and location to be `{ line: 40, column: 1 }`. 941 | The `line` and `column` properties in `locationOffset` are 1-indexed. 942 | 943 | [[Return to contents]](#Contents) 944 | 945 | ## StringValueNode 946 | 947 | ```v 948 | struct StringValueNode { 949 | pub: 950 | kind Kind = Kind.string_value 951 | value ?string 952 | block ?bool 953 | is_const bool 954 | pub mut: 955 | loc ?Location 956 | } 957 | ``` 958 | 959 | [[Return to contents]](#Contents) 960 | 961 | ## Token 962 | 963 | ```v 964 | struct Token { 965 | pub: 966 | kind TokenKind 967 | // The character offset at which this Node begins. 968 | start int 969 | // The character offset at which this Node ends. 970 | end int 971 | // The 1-indexed line number on which this Token appears. 972 | line int 973 | // The 1-indexed column number at which this Token begins. 974 | column int 975 | // For non-punctuation tokens, represents the interpreted value of the token. 976 | //
977 | // **Note**: is `none` for punctuation tokens, but typed as string for 978 | // convenience in the parser. 979 | value ?string 980 | mut: 981 | // Tokens exist as nodes in a double-linked-list amongst all tokens 982 | // including ignored tokens. is always the first node and 983 | // the last. 984 | prev ?&Token 985 | next ?&Token 986 | } 987 | ``` 988 | 989 | [[Return to contents]](#Contents) 990 | 991 | ## UnionTypeDefinitionNode 992 | 993 | ```v 994 | struct UnionTypeDefinitionNode { 995 | kind Kind = Kind.union_type_definition 996 | description ?StringValueNode 997 | name NameNode 998 | directives ?[]DirectiveNode 999 | types ?[]NamedTypeNode 1000 | pub mut: 1001 | loc ?Location 1002 | } 1003 | ``` 1004 | 1005 | [[Return to contents]](#Contents) 1006 | 1007 | ## UnionTypeExtensionNode 1008 | 1009 | ```v 1010 | struct UnionTypeExtensionNode { 1011 | kind Kind = Kind.union_type_extension 1012 | name NameNode 1013 | directives ?[]DirectiveNode 1014 | types ?[]NamedTypeNode 1015 | pub mut: 1016 | loc ?Location 1017 | } 1018 | ``` 1019 | 1020 | [[Return to contents]](#Contents) 1021 | 1022 | ## VariableDefinitionNode 1023 | 1024 | ```v 1025 | struct VariableDefinitionNode { 1026 | pub: 1027 | kind Kind = Kind.variable_definition 1028 | variable VariableNode 1029 | @type TypeNode 1030 | default_value ?ValueNode 1031 | directives ?[]DirectiveNode 1032 | pub mut: 1033 | loc ?Location 1034 | } 1035 | ``` 1036 | 1037 | [[Return to contents]](#Contents) 1038 | 1039 | ## VariableNode 1040 | 1041 | ```v 1042 | struct VariableNode { 1043 | pub: 1044 | kind Kind = Kind.variable 1045 | name NameNode 1046 | pub mut: 1047 | loc ?Location 1048 | } 1049 | ``` 1050 | 1051 | [[Return to contents]](#Contents) 1052 | 1053 | ## BlockStringOptions 1054 | 1055 | ```v 1056 | struct BlockStringOptions { 1057 | pub: 1058 | minimize bool 1059 | } 1060 | ``` 1061 | 1062 | [[Return to contents]](#Contents) 1063 | 1064 | ## BooleanValueNode 1065 | 1066 | ```v 1067 | struct BooleanValueNode { 1068 | pub: 1069 | kind Kind = Kind.boolean 1070 | value bool 1071 | is_const bool 1072 | pub mut: 1073 | loc ?Location 1074 | } 1075 | ``` 1076 | 1077 | [[Return to contents]](#Contents) 1078 | 1079 | ## ConstArgumentNode 1080 | 1081 | ```v 1082 | struct ConstArgumentNode { 1083 | pub: 1084 | kind Kind = Kind.argument 1085 | name NameNode 1086 | value ValueNode 1087 | pub mut: 1088 | loc ?Location 1089 | } 1090 | ``` 1091 | 1092 | [[Return to contents]](#Contents) 1093 | 1094 | ## ConstObjectFieldNode 1095 | 1096 | ```v 1097 | struct ConstObjectFieldNode { 1098 | pub: 1099 | kind Kind = Kind.object_field 1100 | name NameNode 1101 | value ValueNode 1102 | is_const bool = true 1103 | pub mut: 1104 | loc ?Location 1105 | } 1106 | ``` 1107 | 1108 | [[Return to contents]](#Contents) 1109 | 1110 | ## ConstObjectValueNode 1111 | 1112 | ```v 1113 | struct ConstObjectValueNode { 1114 | pub: 1115 | kind Kind = Kind.object 1116 | fields []ValueNode 1117 | is_const bool = true 1118 | pub mut: 1119 | loc ?Location 1120 | } 1121 | ``` 1122 | 1123 | [[Return to contents]](#Contents) 1124 | 1125 | ## DirectiveDefinitionNode 1126 | 1127 | ```v 1128 | struct DirectiveDefinitionNode { 1129 | kind Kind = Kind.directive_definition 1130 | description ?StringValueNode 1131 | name NameNode 1132 | arguments ?[]InputValueDefinitionNode 1133 | repeatable bool 1134 | locations []NameNode 1135 | pub mut: 1136 | loc ?Location 1137 | } 1138 | ``` 1139 | 1140 | [[Return to contents]](#Contents) 1141 | 1142 | ## DirectiveNode 1143 | 1144 | ```v 1145 | struct DirectiveNode { 1146 | pub: 1147 | kind Kind = Kind.directive 1148 | name NameNode 1149 | arguments ?[]ArgumentNode 1150 | pub mut: 1151 | loc ?Location 1152 | is_const bool 1153 | } 1154 | ``` 1155 | 1156 | [[Return to contents]](#Contents) 1157 | 1158 | ## DocumentNode 1159 | 1160 | ```v 1161 | struct DocumentNode { 1162 | pub: 1163 | kind Kind = Kind.document 1164 | definitions []DefinitionNode 1165 | token_count int 1166 | pub mut: 1167 | loc ?Location 1168 | } 1169 | ``` 1170 | 1171 | [[Return to contents]](#Contents) 1172 | 1173 | ## EnumTypeDefinitionNode 1174 | 1175 | ```v 1176 | struct EnumTypeDefinitionNode { 1177 | kind Kind = Kind.enum_type_definition 1178 | description ?StringValueNode 1179 | name NameNode 1180 | directives ?[]DirectiveNode 1181 | values ?[]EnumValueDefinitionNode 1182 | pub mut: 1183 | loc ?Location 1184 | } 1185 | ``` 1186 | 1187 | [[Return to contents]](#Contents) 1188 | 1189 | ## EnumTypeExtensionNode 1190 | 1191 | ```v 1192 | struct EnumTypeExtensionNode { 1193 | kind Kind = Kind.enum_type_extension 1194 | name NameNode 1195 | directives ?[]DirectiveNode 1196 | values ?[]EnumValueDefinitionNode 1197 | pub mut: 1198 | loc ?Location 1199 | } 1200 | ``` 1201 | 1202 | [[Return to contents]](#Contents) 1203 | 1204 | ## EnumValueDefinitionNode 1205 | 1206 | ```v 1207 | struct EnumValueDefinitionNode { 1208 | kind Kind = Kind.enum_value_definition 1209 | description ?StringValueNode 1210 | name NameNode 1211 | directives ?[]DirectiveNode 1212 | pub mut: 1213 | loc ?Location 1214 | } 1215 | ``` 1216 | 1217 | [[Return to contents]](#Contents) 1218 | 1219 | ## EnumValueNode 1220 | 1221 | ```v 1222 | struct EnumValueNode { 1223 | pub: 1224 | kind Kind = Kind.enum_value 1225 | value string 1226 | is_const bool 1227 | pub mut: 1228 | loc ?Location 1229 | } 1230 | ``` 1231 | 1232 | [[Return to contents]](#Contents) 1233 | 1234 | ## ErrorBoundaryNode 1235 | 1236 | ```v 1237 | struct ErrorBoundaryNode { 1238 | pub: 1239 | kind Kind = Kind.error_boundary 1240 | nullability_assertion ?ListNullabilityOperatorNode 1241 | pub mut: 1242 | loc ?Location 1243 | } 1244 | ``` 1245 | 1246 | [[Return to contents]](#Contents) 1247 | 1248 | ## FieldDefinitionNode 1249 | 1250 | ```v 1251 | struct FieldDefinitionNode { 1252 | kind Kind = Kind.field_definition 1253 | description ?StringValueNode 1254 | name NameNode 1255 | arguments ?[]InputValueDefinitionNode 1256 | @type TypeNode 1257 | directives ?[]DirectiveNode 1258 | pub mut: 1259 | loc ?Location 1260 | } 1261 | ``` 1262 | 1263 | [[Return to contents]](#Contents) 1264 | 1265 | ## FieldNode 1266 | 1267 | ```v 1268 | struct FieldNode { 1269 | pub: 1270 | kind Kind = Kind.field 1271 | alias ?NameNode 1272 | name NameNode 1273 | arguments ?[]ArgumentNode 1274 | nullability_assertion ?NullabilityAssertionNode 1275 | directives []DirectiveNode 1276 | selection_set ?SelectionSetNode 1277 | pub mut: 1278 | loc ?Location 1279 | } 1280 | ``` 1281 | 1282 | [[Return to contents]](#Contents) 1283 | 1284 | ## FloatValueNode 1285 | 1286 | ```v 1287 | struct FloatValueNode { 1288 | pub: 1289 | kind Kind = Kind.float_value 1290 | value ?string 1291 | is_const bool 1292 | pub mut: 1293 | loc ?Location 1294 | } 1295 | ``` 1296 | 1297 | [[Return to contents]](#Contents) 1298 | 1299 | ## FragmentDefinitionNode 1300 | 1301 | ```v 1302 | struct FragmentDefinitionNode { 1303 | pub: 1304 | kind Kind = Kind.fragment_definition 1305 | name NameNode 1306 | type_condition NamedTypeNode 1307 | directives []DirectiveNode 1308 | selection_set SelectionSetNode 1309 | pub mut: 1310 | loc ?Location 1311 | } 1312 | ``` 1313 | 1314 | [[Return to contents]](#Contents) 1315 | 1316 | ## FragmentSpreadNode 1317 | 1318 | ```v 1319 | struct FragmentSpreadNode { 1320 | pub: 1321 | kind Kind = Kind.fragment_spread 1322 | name NameNode 1323 | directives ?[]DirectiveNode 1324 | pub mut: 1325 | loc ?Location 1326 | } 1327 | ``` 1328 | 1329 | Fragments 1330 | 1331 | [[Return to contents]](#Contents) 1332 | 1333 | ## InlineFragmentNode 1334 | 1335 | ```v 1336 | struct InlineFragmentNode { 1337 | pub: 1338 | kind Kind = Kind.inline_fragment 1339 | type_condition ?NamedTypeNode 1340 | directives ?[]DirectiveNode 1341 | selection_set SelectionSetNode 1342 | pub mut: 1343 | loc ?Location 1344 | } 1345 | ``` 1346 | 1347 | [[Return to contents]](#Contents) 1348 | 1349 | ## InputObjectTypeDefinitionNode 1350 | 1351 | ```v 1352 | struct InputObjectTypeDefinitionNode { 1353 | kind Kind = Kind.input_object_type_definition 1354 | description ?StringValueNode 1355 | name NameNode 1356 | directives ?[]DirectiveNode 1357 | fields ?[]InputValueDefinitionNode 1358 | pub mut: 1359 | loc ?Location 1360 | } 1361 | ``` 1362 | 1363 | [[Return to contents]](#Contents) 1364 | 1365 | ## InputObjectTypeExtensionNode 1366 | 1367 | ```v 1368 | struct InputObjectTypeExtensionNode { 1369 | kind Kind = Kind.object_type_extension 1370 | name NameNode 1371 | directives ?[]DirectiveNode 1372 | fields ?[]InputValueDefinitionNode 1373 | pub mut: 1374 | loc ?Location 1375 | } 1376 | ``` 1377 | 1378 | [[Return to contents]](#Contents) 1379 | 1380 | ## InputValueDefinitionNode 1381 | 1382 | ```v 1383 | struct InputValueDefinitionNode { 1384 | kind Kind 1385 | description ?StringValueNode 1386 | name NameNode 1387 | @type TypeNode 1388 | default_value ?ValueNode 1389 | directives ?[]DirectiveNode 1390 | pub mut: 1391 | loc ?Location 1392 | } 1393 | ``` 1394 | 1395 | [[Return to contents]](#Contents) 1396 | 1397 | #### Powered by vdoc. Generated on: 17 Sep 2023 23:47:23 1398 | -------------------------------------------------------------------------------- /src/parser.v: -------------------------------------------------------------------------------- 1 | module graphql 2 | 3 | pub struct ParserOptions { 4 | pub: 5 | // By default, the parser creates AST nodes that know the location 6 | // in the source that they correspond to. This configuration flag 7 | // disables that behavior for performance or testing. 8 | no_location bool 9 | // Parser CPU and memory usage is linear to the number of tokens in a document 10 | // however in extreme cases it becomes quadratic due to memory exhaustion. 11 | // Parsing happens before validation so even invalid queries can burn lots of 12 | // CPU time and memory. 13 | // To prevent this you can set a maximum number of tokens allowed within a document. 14 | // @default 50K tokens 15 | max_tokens int = 50_000 16 | // @deprecated 17 | allow_legacy_fragment_variables ?bool 18 | experimental_client_controlled_nullability ?bool 19 | } 20 | 21 | pub struct Parser { 22 | options ParserOptions 23 | mut: 24 | lexer Lexer 25 | token_counter int 26 | } 27 | 28 | type SourceOrString = Source | string 29 | 30 | fn Parser.new(source SourceOrString, options ?ParserOptions) !&Parser { 31 | // initial empty source 32 | mut source_object := Source.new('', none, none) 33 | 34 | if source is string { 35 | source_object = Source.new(source, none, none) 36 | } else { 37 | source_object = source as Source 38 | } 39 | 40 | return &Parser{ 41 | lexer: Lexer.new(source_object) 42 | options: options or { ParserOptions{} } 43 | token_counter: 0 44 | } 45 | } 46 | 47 | pub fn parse(source SourceOrString, options ?ParserOptions) !DocumentNode { 48 | mut parser := Parser.new(source, options)! 49 | return parser.parse_document()! 50 | } 51 | 52 | // pub fn parse_value(source SourceOrString, options ?ParserOptions) !ValueNode { 53 | // mut parser := Parser.new(source, options)! 54 | 55 | // parser.expect_token(TokenKind.sof)! 56 | 57 | // value := parser 58 | // } 59 | 60 | fn (mut p Parser) parse_value_literal(is_const bool) !ValueNode { 61 | token := p.lexer.token 62 | 63 | match token.kind { 64 | .bracket_l { 65 | // println('== PARSE VALUE LITERAL (bracket_l)') 66 | return p.parse_list(is_const)! 67 | } 68 | .brace_l { 69 | // println('== PARSE VALUE LITERAL (brace_l)') 70 | return p.parse_object(is_const)! 71 | } 72 | .integer { 73 | // println('== PARSE VALUE LITERAL (integer)') 74 | p.advance_lexer()! 75 | 76 | return p.node[IntValueNode](token, mut IntValueNode{ 77 | kind: Kind.int_value 78 | value: token.value 79 | })! 80 | } 81 | .float { 82 | // println('== PARSE VALUE LITERAL (float)') 83 | 84 | p.advance_lexer()! 85 | 86 | return p.node[FloatValueNode](token, mut FloatValueNode{ 87 | kind: Kind.float_value 88 | value: token.value 89 | })! 90 | } 91 | .string_value, .block_string { 92 | // println('----- PARSING STRING LITERAL') 93 | return p.parse_string_literal()! 94 | } 95 | .dollar { 96 | // println('== PARSE VALUE LITERAL (dollar)') 97 | 98 | if is_const { 99 | p.expect_token(TokenKind.dollar)! 100 | 101 | if p.lexer.token.kind == TokenKind.name { 102 | var_name := p.lexer.token.value 103 | error('Unexpected variable ${var_name}') 104 | } else { 105 | // println('UNEXPECTED: parse_value_literal (dollar)') 106 | 107 | p.unexpected(token)! 108 | } 109 | } 110 | 111 | return p.parse_variable()! 112 | } 113 | .name { 114 | // println('== PARSE VALUE LITERAL (name)') 115 | 116 | p.advance_lexer()! 117 | 118 | match token.value or { '' } { 119 | 'true' { 120 | return p.node[BooleanValueNode](token, mut BooleanValueNode{ 121 | kind: Kind.boolean 122 | value: true 123 | })! 124 | } 125 | 'false' { 126 | return p.node[BooleanValueNode](token, mut BooleanValueNode{ 127 | kind: Kind.boolean 128 | value: false 129 | })! 130 | } 131 | 'null' { 132 | return p.node[NullValueNode](token, mut NullValueNode{ 133 | kind: Kind.null 134 | })! 135 | } 136 | else { 137 | return p.node[EnumValueNode](token, mut EnumValueNode{ 138 | kind: Kind.enum_value 139 | value: token.value or { '' } 140 | })! 141 | } 142 | } 143 | } 144 | else { 145 | // println('UNEXPECTED: parse_value_literal') 146 | p.unexpected(token)! 147 | return error('') 148 | } 149 | } 150 | } 151 | 152 | fn (mut p Parser) parse_variable() !VariableNode { 153 | start := p.lexer.token 154 | 155 | p.expect_token(TokenKind.dollar)! 156 | 157 | return p.node[VariableNode](start, mut VariableNode{ 158 | kind: Kind.variable 159 | name: p.parse_name()! 160 | }) 161 | } 162 | 163 | fn (mut p Parser) parse_list(is_const bool) !ListValueNode { 164 | item := fn [mut p, is_const] () !ValueNode { 165 | return p.parse_value_literal(is_const)! 166 | } 167 | 168 | p.expect_token(TokenKind.dollar)! 169 | 170 | return p.node[ListValueNode](p.lexer.token, mut ListValueNode{ 171 | kind: Kind.list 172 | values: p.any(TokenKind.bracket_l, item, TokenKind.bracket_r) 173 | }) 174 | } 175 | 176 | fn (mut p Parser) parse_object(is_const bool) !ObjectValueNode { 177 | item := fn [mut p, is_const] () !ObjectFieldNode { 178 | return p.parse_object_field(is_const) 179 | } 180 | 181 | return p.node[ObjectValueNode](p.lexer.token, mut ObjectValueNode{ 182 | kind: Kind.object 183 | fields: p.any(TokenKind.brace_l, item, TokenKind.brace_r) 184 | }) 185 | } 186 | 187 | // ObjectField[Const] : Name : Value[?Const] 188 | fn (mut p Parser) parse_object_field(is_const bool) !ObjectFieldNode { 189 | start := p.lexer.token 190 | name := p.parse_name()! 191 | 192 | p.expect_token(TokenKind.colon)! 193 | 194 | return p.node[ObjectFieldNode](start, mut ObjectFieldNode{ 195 | kind: Kind.object_field 196 | name: name 197 | value: p.parse_value_literal(is_const)! 198 | }) 199 | } 200 | 201 | fn (mut p Parser) parse_name() !NameNode { 202 | token := p.expect_token(TokenKind.name)! 203 | node := p.node[NameNode](token, mut NameNode{ 204 | kind: Kind.name 205 | value: token.value or { panic('Missing name node ${err}') } 206 | })! 207 | 208 | return node 209 | } 210 | 211 | fn (mut p Parser) parse_document() !DocumentNode { 212 | p.expect_token(TokenKind.sof)! 213 | mut definitions := []DefinitionNode{} 214 | 215 | for { 216 | definitions << p.parse_definition()! 217 | 218 | if p.expect_optional_token(TokenKind.eof) { 219 | break 220 | } 221 | } 222 | 223 | // println('FINISHED doc ${definitions.len}') 224 | 225 | return p.node[DocumentNode](p.lexer.token, mut DocumentNode{ 226 | kind: Kind.document 227 | definitions: definitions 228 | token_count: p.token_counter 229 | })! 230 | } 231 | 232 | // Definition : 233 | // - ExecutableDefinition 234 | // - TypeSystemDefinition 235 | // - TypeSystemExtension 236 | // 237 | // ExecutableDefinition : 238 | // - OperationDefinition 239 | // - FragmentDefinition 240 | // 241 | // TypeSystemDefinition : 242 | // - SchemaDefinition 243 | // - TypeDefinition 244 | // - DirectiveDefinition 245 | // 246 | // TypeDefinition : 247 | // - ScalarTypeDefinition 248 | // - ObjectTypeDefinition 249 | // - InterfaceTypeDefinition 250 | // - UnionTypeDefinition 251 | // - EnumTypeDefinition 252 | // - InputObjectTypeDefinition 253 | fn (mut parser Parser) parse_definition() !DefinitionNode { 254 | // println('!!parse_definition') 255 | 256 | if parser.peek(TokenKind.brace_l) { 257 | return parser.parse_operation_definition()! 258 | } 259 | 260 | // Many definitions begin with a description and require a lookahead. 261 | has_description := parser.peek_description() 262 | 263 | keyword_token := if has_description { 264 | parser.lexer.lookahead() 265 | } else { 266 | parser.lexer.token 267 | } 268 | 269 | // println('+++ NAME ${keyword_token.kind.gql_str()} VALUE: ${keyword_token.value}') 270 | if keyword_token.kind == TokenKind.name { 271 | // println('KEYWORD ${keyword_token.value}') 272 | match keyword_token.value or { panic(err) } { 273 | 'schema' { 274 | return parser.parse_schema_definition()! 275 | } 276 | 'scalar' { 277 | return parser.parse_scalar_type_definition()! 278 | } 279 | 'type' { 280 | return parser.parse_object_type_definition()! 281 | } 282 | 'interface' { 283 | return parser.parse_interface_type_definition()! 284 | } 285 | 'union' { 286 | return parser.parse_union_type_definition()! 287 | } 288 | 'enum' { 289 | return parser.parse_enum_type_definition()! 290 | } 291 | 'input' { 292 | return parser.parse_input_object_type_definition()! 293 | } 294 | 'directive' { 295 | // println('=== DIRECTIVE') 296 | // return parser.parsedire 297 | } 298 | else { 299 | // Unsupported 300 | } 301 | } 302 | 303 | if has_description { 304 | return error('Unexpected description for "${keyword_token.to_json()}", descriptions only in type definitions') 305 | } 306 | 307 | match keyword_token.value or { '' } { 308 | 'query', 'mutation', 'subscription' { 309 | return parser.parse_operation_definition()! 310 | } 311 | 'fragment' { 312 | return parser.parse_fragment_definition() 313 | } 314 | else { 315 | return error('not supported') 316 | } 317 | } 318 | } 319 | 320 | return error('Unexpected ${keyword_token.kind}. Previous: (${parser.lexer.last_token.kind}, ${parser.lexer.last_token.value})') 321 | } 322 | 323 | fn (mut parser Parser) parse_enum_type_definition() !EnumTypeDefinitionNode { 324 | start := parser.lexer.token 325 | description := parser.parse_description() 326 | 327 | parser.expect_keyword('enum')! 328 | 329 | name := parser.parse_enum_value_name()! 330 | directives := parser.parse_const_directives()! 331 | values := parser.parse_enum_values_definition()! 332 | 333 | return parser.node[EnumTypeDefinitionNode](start, mut EnumTypeDefinitionNode{ 334 | kind: Kind.enum_type_definition 335 | name: name 336 | directives: directives 337 | values: values 338 | description: description 339 | }) 340 | } 341 | 342 | fn (mut parser Parser) parse_enum_values_definition() ![]EnumValueDefinitionNode { 343 | mut nodes := []EnumValueDefinitionNode{} 344 | 345 | // optional_many 346 | if parser.expect_optional_token(TokenKind.brace_l) { 347 | for { 348 | nodes << parser.parse_enum_value_definition()! 349 | 350 | if parser.expect_optional_token(TokenKind.brace_r) { 351 | break 352 | } 353 | } 354 | } 355 | 356 | return nodes 357 | } 358 | 359 | fn (mut parser Parser) parse_enum_value_definition() !EnumValueDefinitionNode { 360 | start := parser.lexer.token 361 | description := parser.parse_description() 362 | name := parser.parse_name()! 363 | directives := parser.parse_const_directives()! 364 | 365 | return parser.node[EnumValueDefinitionNode](start, mut EnumValueDefinitionNode{ 366 | kind: Kind.enum_value_definition 367 | name: name 368 | description: description 369 | directives: directives 370 | }) 371 | } 372 | 373 | fn (mut parser Parser) parse_enum_value_name() !NameNode { 374 | match parser.lexer.token.value or { return error('No name node for ENUM') } { 375 | 'true', 'false', 'null' { 376 | return error('${parser.lexer.token.value} is reserved and cannot be used for an enum value') 377 | } 378 | else { 379 | return parser.parse_name()! 380 | } 381 | } 382 | } 383 | 384 | fn (mut parser Parser) parse_union_type_definition() !UnionTypeDefinitionNode { 385 | start := parser.lexer.token 386 | description := parser.parse_description() 387 | 388 | parser.expect_keyword('union')! 389 | 390 | name := parser.parse_name()! 391 | directives := parser.parse_const_directives()! 392 | types := parser.parse_union_member_types()! 393 | 394 | return parser.node[UnionTypeDefinitionNode](start, mut UnionTypeDefinitionNode{ 395 | kind: Kind.union_type_definition 396 | name: name 397 | directives: directives 398 | types: types 399 | description: description 400 | }) 401 | } 402 | 403 | fn (mut parser Parser) parse_union_member_types() ![]NamedTypeNode { 404 | mut nodes := []NamedTypeNode{} 405 | 406 | // delimited_many 407 | if parser.expect_optional_token(TokenKind.equals) { 408 | parser.expect_optional_token(TokenKind.pipe) 409 | 410 | for { 411 | nodes << parser.parse_named_type()! 412 | 413 | if !parser.expect_optional_token(TokenKind.pipe) { 414 | break 415 | } 416 | } 417 | } 418 | 419 | return nodes 420 | } 421 | 422 | fn (mut parser Parser) parse_input_object_type_definition() !InputObjectTypeDefinitionNode { 423 | start := parser.lexer.token 424 | description := parser.parse_description() 425 | 426 | parser.expect_keyword('input')! 427 | 428 | name := parser.parse_name()! 429 | directives := parser.parse_directives(true)! 430 | fields := parser.parse_input_fields_definition()! 431 | 432 | return parser.node[InputObjectTypeDefinitionNode](start, mut InputObjectTypeDefinitionNode{ 433 | kind: Kind.input_object_type_definition 434 | name: name 435 | directives: directives 436 | fields: fields 437 | description: description 438 | }) 439 | } 440 | 441 | fn (mut parser Parser) parse_interface_type_definition() !InterfaceTypeDefinitionNode { 442 | start := parser.lexer.token 443 | description := parser.parse_description() 444 | 445 | parser.expect_keyword('interface')! 446 | 447 | name := parser.parse_name()! 448 | interfaces := parser.parse_implements_interfaces()! 449 | directives := parser.parse_directives(true)! 450 | fields := parser.parse_fields_definition()! 451 | 452 | return parser.node[InterfaceTypeDefinitionNode](start, mut InterfaceTypeDefinitionNode{ 453 | kind: Kind.scalar_type_definition 454 | name: name 455 | fields: fields 456 | interfaces: interfaces 457 | directives: directives 458 | description: description 459 | }) 460 | } 461 | 462 | fn (mut parser Parser) parse_input_object_type_extension() !InputObjectTypeExtensionNode { 463 | start := parser.lexer.token 464 | 465 | parser.expect_keyword('extend')! 466 | parser.expect_keyword('input')! 467 | 468 | name := parser.parse_name()! 469 | 470 | directives := parser.parse_directives(true)! 471 | fields := parser.parse_input_fields_definition()! 472 | 473 | if directives.len == 0 && fields.len == 0 { 474 | parser.unexpected(parser.lexer.token)! 475 | } 476 | 477 | return parser.node[InputObjectTypeExtensionNode](start, mut InputObjectTypeExtensionNode{ 478 | kind: Kind.input_object_type_extension 479 | name: name 480 | directives: directives 481 | fields: fields 482 | }) 483 | } 484 | 485 | fn (mut parser Parser) parse_input_value_def() !InputValueDefinitionNode { 486 | start := parser.lexer.token 487 | description := parser.parse_description() 488 | name := parser.parse_name()! 489 | 490 | parser.expect_token(TokenKind.colon)! 491 | 492 | type_node := parser.parse_type_reference()! 493 | 494 | if parser.expect_optional_token(TokenKind.equals) { 495 | default_value := parser.parse_const_value_literal() 496 | directives := parser.parse_directives(true)! 497 | return parser.node[InputValueDefinitionNode](start, mut InputValueDefinitionNode{ 498 | kind: Kind.input_value_definition 499 | description: description 500 | name: name 501 | @type: type_node 502 | default_value: default_value 503 | directives: directives 504 | }) 505 | } 506 | 507 | directives := parser.parse_directives(true)! 508 | return parser.node[InputValueDefinitionNode](start, mut InputValueDefinitionNode{ 509 | kind: Kind.input_value_definition 510 | description: description 511 | name: name 512 | @type: type_node 513 | default_value: none 514 | directives: directives 515 | })! 516 | } 517 | 518 | fn (mut parser Parser) parse_input_fields_definition() ![]InputValueDefinitionNode { 519 | mut nodes := []InputValueDefinitionNode{} 520 | 521 | // optional_many 522 | if parser.expect_optional_token(TokenKind.brace_l) { 523 | for { 524 | nodes << parser.parse_input_value_def()! 525 | 526 | if parser.expect_optional_token(TokenKind.brace_r) { 527 | break 528 | } 529 | } 530 | } 531 | 532 | return nodes 533 | } 534 | 535 | fn (mut parser Parser) parse_object_type_definition() !ObjectTypeDefinitionNode { 536 | start := parser.lexer.token 537 | description := parser.parse_description() 538 | 539 | parser.expect_keyword('type')! 540 | 541 | name := parser.parse_name()! 542 | interfaces := parser.parse_implements_interfaces()! 543 | directives := parser.parse_directives(true)! 544 | fields := parser.parse_fields_definition()! 545 | 546 | return parser.node[ObjectTypeDefinitionNode](start, mut ObjectTypeDefinitionNode{ 547 | kind: Kind.object_type_definition 548 | description: description 549 | name: name 550 | interfaces: interfaces 551 | directives: directives 552 | fields: fields 553 | }) 554 | } 555 | 556 | fn (mut parser Parser) parse_implements_interfaces() ![]NamedTypeNode { 557 | mut nodes := []NamedTypeNode{} 558 | if parser.expect_optional_keyword('implements')! { 559 | parser.expect_optional_token(TokenKind.amp) 560 | 561 | for { 562 | nodes << parser.parse_named_type()! 563 | 564 | if !parser.expect_optional_token(TokenKind.amp) { 565 | break 566 | } 567 | } 568 | } 569 | 570 | return nodes 571 | } 572 | 573 | fn (mut parser Parser) parse_fields_definition() ![]FieldDefinitionNode { 574 | mut nodes := []FieldDefinitionNode{} 575 | 576 | // optional_many 577 | if parser.expect_optional_token(TokenKind.brace_l) { 578 | for { 579 | nodes << parser.parse_field_definition()! 580 | 581 | if parser.expect_optional_token(TokenKind.brace_r) { 582 | break 583 | } 584 | } 585 | } 586 | 587 | return nodes 588 | } 589 | 590 | fn (mut parser Parser) parse_field_definition() !FieldDefinitionNode { 591 | start := parser.lexer.token 592 | description := parser.parse_description() 593 | name := parser.parse_name()! 594 | args := parser.parse_argument_defs()! 595 | 596 | parser.expect_token(TokenKind.colon)! 597 | 598 | @type := parser.parse_type_reference()! 599 | directives := parser.parse_directives(true)! 600 | 601 | return parser.node[FieldDefinitionNode](start, mut FieldDefinitionNode{ 602 | kind: Kind.field_definition 603 | description: description 604 | name: name 605 | arguments: args 606 | directives: directives 607 | @type: @type 608 | }) 609 | } 610 | 611 | fn (mut parser Parser) parse_argument_defs() ![]InputValueDefinitionNode { 612 | mut nodes := []InputValueDefinitionNode{} 613 | 614 | // println('FN: parse_argument_defs') 615 | if parser.expect_optional_token(TokenKind.paren_l) { 616 | for { 617 | nodes << parser.parse_input_value_def()! 618 | 619 | if parser.expect_optional_token(TokenKind.paren_r) { 620 | break 621 | } 622 | } 623 | } 624 | 625 | return nodes 626 | } 627 | 628 | fn (mut parser Parser) parse_type_reference() !TypeNode { 629 | start := parser.lexer.token 630 | 631 | mut named_type_node := ?NamedTypeNode{} 632 | mut type_node := ListTypeNode{} 633 | 634 | if parser.expect_optional_token(TokenKind.bracket_l) { 635 | inner_type := parser.parse_type_reference()! 636 | 637 | parser.expect_token(TokenKind.bracket_r)! 638 | 639 | type_node = parser.node[ListTypeNode](start, mut ListTypeNode{ 640 | kind: Kind.list_type 641 | node_type: inner_type 642 | })! 643 | } else { 644 | named_type_node = parser.parse_named_type()! 645 | } 646 | 647 | if final_type := named_type_node { 648 | if parser.expect_optional_token(TokenKind.bang) { 649 | return TypeNode(parser.node(start, mut NonNullTypeNode{ 650 | kind: Kind.non_null_type 651 | node_type: final_type 652 | })) 653 | } 654 | 655 | return final_type 656 | } else { 657 | if parser.expect_optional_token(TokenKind.bang) { 658 | return TypeNode(parser.node(start, mut NonNullTypeNode{ 659 | kind: Kind.non_null_type 660 | node_type: type_node 661 | })) 662 | } 663 | 664 | return type_node 665 | } 666 | 667 | return error('could not return type reference') 668 | } 669 | 670 | fn (mut parser Parser) parse_scalar_type_definition() !ScalarTypeDefinitionNode { 671 | start := parser.lexer.token 672 | description := parser.parse_description() 673 | 674 | parser.expect_keyword('scalar')! 675 | 676 | name := parser.parse_name()! 677 | directives := parser.parse_directives(true)! 678 | 679 | return parser.node[ScalarTypeDefinitionNode](start, mut ScalarTypeDefinitionNode{ 680 | kind: Kind.scalar_type_extension 681 | description: description 682 | name: name 683 | directives: directives 684 | }) 685 | } 686 | 687 | fn (mut p Parser) parse_description() ?StringValueNode { 688 | if p.peek_description() { 689 | return p.parse_string_literal() or { 690 | panic(err) 691 | return none 692 | } 693 | } 694 | 695 | return none 696 | } 697 | 698 | fn (mut p Parser) parse_string_literal() !StringValueNode { 699 | token := p.lexer.token 700 | p.advance_lexer()! 701 | 702 | node := p.node[StringValueNode](token, mut StringValueNode{ 703 | kind: Kind.string_value 704 | value: token.value 705 | block: token.kind == TokenKind.block_string 706 | })! 707 | 708 | return node 709 | } 710 | 711 | fn (mut parser Parser) parse_const_value_literal() ?ValueNode { 712 | return parser.parse_value_literal(true) or { 713 | panic(err) 714 | return none 715 | } 716 | } 717 | 718 | fn (mut p Parser) parse_schema_definition() !SchemaDefinitionNode { 719 | start := p.lexer.token 720 | description := p.parse_description() 721 | 722 | p.expect_keyword('schema')! 723 | p.expect_token(TokenKind.brace_l)! 724 | 725 | mut operation_types := []OperationTypeDefinitionNode{} 726 | 727 | for { 728 | operation_types << p.parse_operation_type_definition()! 729 | 730 | if p.expect_optional_token(TokenKind.brace_r) { 731 | break 732 | } 733 | } 734 | 735 | node := p.node[SchemaDefinitionNode](start, mut SchemaDefinitionNode{ 736 | kind: Kind.schema_definition 737 | description: description 738 | directives: [] 739 | operation_types: operation_types 740 | })! 741 | 742 | return node 743 | } 744 | 745 | fn (mut parser Parser) parse_operation_definition() !OperationDefinitionNode { 746 | // println('..parsing operation') 747 | start := parser.lexer.token 748 | 749 | if parser.peek(TokenKind.brace_l) { 750 | // println('...next node is brace_l') 751 | node := parser.node[OperationDefinitionNode](start, mut OperationDefinitionNode{ 752 | kind: Kind.operation_definition 753 | operation: OperationTypeNode.query 754 | name: none 755 | variable_definitions: [] 756 | directives: [] 757 | selection_set: parser.parse_selection_set() 758 | })! 759 | 760 | // println('...created operationdefinitionnode') 761 | return node 762 | } 763 | 764 | // println('...operation defintino') 765 | 766 | operation := parser.parse_operation_type()! 767 | mut name := ?NameNode{} 768 | 769 | if parser.peek(TokenKind.name) { 770 | name = parser.parse_name()! 771 | } 772 | 773 | return parser.node[OperationDefinitionNode](start, mut OperationDefinitionNode{ 774 | kind: Kind.operation_definition 775 | operation: operation 776 | name: name 777 | variable_definitions: parser.parse_variable_definitions() 778 | directives: parser.parse_directives(false) or { panic(err) } 779 | selection_set: parser.parse_selection_set() 780 | })! 781 | } 782 | 783 | fn (mut parser Parser) scoped_parse_section() SelectionNode { 784 | return parser.parse_selection() 785 | } 786 | 787 | fn (mut parser Parser) parse_selection_set() SelectionSetNode { 788 | // println('...parsing selectionsetnode') 789 | parser.expect_token(TokenKind.brace_l) or { panic(err) } 790 | 791 | mut selections := []SelectionNode{} 792 | 793 | for { 794 | selections << parser.parse_selection() 795 | 796 | if parser.expect_optional_token(TokenKind.brace_r) { 797 | break 798 | } 799 | } 800 | 801 | return parser.node[SelectionSetNode](parser.lexer.token, mut SelectionSetNode{ 802 | kind: Kind.selection_set 803 | selections: selections 804 | }) or { panic('could not create selection nodes') } 805 | } 806 | 807 | fn (mut parser Parser) parse_selection() SelectionNode { 808 | if parser.peek(TokenKind.spread) { 809 | return parser.parse_fragment() 810 | } else { 811 | return parser.parse_field() 812 | } 813 | } 814 | 815 | fn (mut parser Parser) parse_fragment() FragmentSpreadNode { 816 | start := parser.lexer.token 817 | parser.expect_token(TokenKind.spread) or { panic(err) } 818 | 819 | has_type_condition := parser.expect_optional_keyword('on') or { false } 820 | 821 | if !has_type_condition && parser.peek(TokenKind.name) { 822 | return parser.node[FragmentSpreadNode](start, mut FragmentSpreadNode{ 823 | kind: Kind.fragment_spread 824 | name: NameNode{} 825 | directives: parser.parse_directives(false) or { panic(err) } 826 | }) or { panic(err) } 827 | } 828 | 829 | return parser.node[FragmentSpreadNode](start, mut FragmentSpreadNode{ 830 | kind: Kind.fragment_spread 831 | name: NameNode{} 832 | directives: parser.parse_directives(false) or { panic(err) } 833 | }) or { panic(err) } 834 | } 835 | 836 | fn (mut parser Parser) parse_fragment_definition() FragmentDefinitionNode { 837 | start := parser.lexer.token 838 | parser.expect_keyword('fragment') or { panic(err) } 839 | 840 | return parser.node[FragmentDefinitionNode](start, mut FragmentDefinitionNode{ 841 | kind: Kind.fragment_definition 842 | directives: parser.parse_directives(false) or { panic(err) } 843 | selection_set: parser.parse_selection_set() 844 | }) or { panic(err) } 845 | } 846 | 847 | fn (mut parser Parser) parse_field() FieldNode { 848 | start := parser.lexer.token 849 | 850 | name_or_alias := parser.parse_name() or { panic('could not parse field name ${err}') } 851 | mut name := NameNode{} 852 | mut alias := NameNode{} 853 | 854 | if parser.expect_optional_token(TokenKind.colon) { 855 | alias = name_or_alias 856 | name = parser.parse_name() or { panic('cant parse alias name') } 857 | } else { 858 | name = name_or_alias 859 | } 860 | 861 | selection_set := fn [mut parser] () ?SelectionSetNode { 862 | if parser.peek(TokenKind.brace_l) { 863 | parser.parse_selection_set() 864 | } 865 | 866 | return none 867 | } 868 | 869 | return parser.node[FieldNode](start, mut FieldNode{ 870 | kind: Kind.field 871 | alias: alias 872 | name: name 873 | arguments: parser.parse_arguments(false) 874 | nullability_assertion: none 875 | directives: parser.parse_directives(false) or { panic(err) } 876 | selection_set: selection_set() 877 | }) or { panic(err) } 878 | } 879 | 880 | fn (mut parser Parser) parse_argument_not_const() ArgumentNode { 881 | return parser.parse_argument(false) 882 | } 883 | 884 | fn (mut parser Parser) parse_const_argument() ArgumentNode { 885 | return parser.parse_argument(true) 886 | } 887 | 888 | fn (mut parser Parser) parse_arguments(is_const bool) []ArgumentNode { 889 | mut nodes := []ArgumentNode{} 890 | 891 | // optional_many 892 | if parser.expect_optional_token(TokenKind.paren_l) { 893 | for { 894 | if is_const { 895 | nodes << parser.parse_const_argument() 896 | } else { 897 | nodes << parser.parse_argument_not_const() 898 | } 899 | 900 | if parser.expect_optional_token(TokenKind.paren_r) { 901 | break 902 | } 903 | } 904 | } 905 | 906 | return nodes 907 | } 908 | 909 | fn (mut parser Parser) parse_argument(is_const bool) ArgumentNode { 910 | start := parser.lexer.token 911 | name := parser.parse_name() or { panic(err) } 912 | 913 | parser.expect_token(TokenKind.colon) or { panic(err) } 914 | 915 | return parser.node[ArgumentNode](start, mut ArgumentNode{ 916 | kind: Kind.argument 917 | name: name 918 | value: parser.parse_value_literal(is_const) or { panic(err) } 919 | }) or { panic(err) } 920 | } 921 | 922 | fn (mut parser Parser) parse_variable_definitions() []VariableDefinitionNode { 923 | mut nodes := []VariableDefinitionNode{} 924 | 925 | if parser.expect_optional_token(TokenKind.paren_l) { 926 | for { 927 | nodes << parser.parse_variable_definition() or { panic(err) } 928 | 929 | if parser.expect_optional_token(TokenKind.paren_r) { 930 | break 931 | } 932 | } 933 | } 934 | 935 | return nodes 936 | } 937 | 938 | fn (mut parser Parser) parse_variable_definition() !VariableDefinitionNode { 939 | default_value := if parser.expect_optional_token(TokenKind.equals) { 940 | parser.parse_const_value_literal() 941 | } else { 942 | return error('no default value') 943 | } 944 | 945 | return parser.node[VariableDefinitionNode](parser.lexer.token, mut VariableDefinitionNode{ 946 | kind: Kind.variable_definition 947 | variable: parser.parse_variable()! 948 | default_value: default_value 949 | })! 950 | } 951 | 952 | fn (mut p Parser) parse_named_type() !NamedTypeNode { 953 | node := p.node[NamedTypeNode](p.lexer.token, mut NamedTypeNode{ 954 | kind: Kind.named_type 955 | node_type: p.parse_name()! 956 | })! 957 | 958 | return node 959 | } 960 | 961 | fn (mut p Parser) parse_operation_type() !OperationTypeNode { 962 | operation_token := p.expect_token(TokenKind.name)! 963 | 964 | return match operation_token.value or { '' } { 965 | 'query' { 966 | OperationTypeNode.query 967 | } 968 | 'mutation' { 969 | OperationTypeNode.mutation 970 | } 971 | 'subscription' { 972 | OperationTypeNode.subscription 973 | } 974 | else { 975 | error('Unexpected token') 976 | } 977 | } 978 | } 979 | 980 | fn (mut p Parser) parse_operation_type_definition() !OperationTypeDefinitionNode { 981 | start := p.lexer.token 982 | 983 | operation := p.parse_operation_type()! 984 | p.expect_token(TokenKind.colon)! 985 | 986 | @type := p.parse_named_type()! 987 | 988 | node := p.node[OperationTypeDefinitionNode](start, mut OperationTypeDefinitionNode{ 989 | kind: Kind.operation_type_definition 990 | operation: operation 991 | @type: @type 992 | })! 993 | 994 | return node 995 | } 996 | 997 | fn (p Parser) peek_description() bool { 998 | return p.peek(TokenKind.string_value) || p.peek(TokenKind.block_string) 999 | } 1000 | 1001 | // Returns a node that, if configured to do so, sets a "loc" field as a 1002 | // location object, used to identify the place in the source that created a 1003 | // given parsed object. 1004 | fn (parser Parser) node[T](start_token &Token, mut node T) !T { 1005 | if !parser.options.no_location { 1006 | node.loc = Location.new(start_token, parser.lexer.last_token, parser.lexer.source) 1007 | } 1008 | 1009 | return node 1010 | 1011 | // return error('failed to convert node') 1012 | } 1013 | 1014 | // Determines if the next token is of a given kind 1015 | fn (p Parser) peek(kind TokenKind) bool { 1016 | return p.lexer.token.kind == kind 1017 | } 1018 | 1019 | // If the next token is of the given kind, return that token after advancing the lexer. 1020 | // Otherwise, do not change the parser state and throw an error. 1021 | fn (mut p Parser) expect_token(kind TokenKind) !&Token { 1022 | token := p.lexer.token 1023 | 1024 | // println('prev token ${token.kind}') 1025 | if token.kind == kind { 1026 | p.advance_lexer()! 1027 | return token 1028 | } 1029 | 1030 | return error('Expected ${kind.gql_str()} got ${token.kind.gql_str()}') 1031 | } 1032 | 1033 | // If the next token is of the given kind, return "true" after advancing the lexer. 1034 | // Otherwise, do not change the parser state and return "false". 1035 | fn (mut p Parser) expect_optional_token(kind TokenKind) bool { 1036 | token := p.lexer.token 1037 | 1038 | // println('current lexer token: ${token.kind}, expected: ${kind}') 1039 | 1040 | if token.kind == kind { 1041 | p.advance_lexer() or { return false } 1042 | return true 1043 | } 1044 | 1045 | return false 1046 | } 1047 | 1048 | // If the next token is a given keyword, advance the lexer. 1049 | // Otherwise, do not change the parser state and throw an error. 1050 | fn (mut p Parser) expect_keyword(value string) ! { 1051 | token := p.lexer.token 1052 | val := token.value or { '' } 1053 | 1054 | // println('>>>>> TOKEN (${token.kind}) value: "${val}" compared to expected "${value}"') 1055 | 1056 | if token.kind == TokenKind.name && val == value { 1057 | p.advance_lexer()! 1058 | return 1059 | } 1060 | 1061 | return error('Expected ${value}, got ${token.value}') 1062 | } 1063 | 1064 | // If the next token is a given keyword, return "true" after advancing the lexer. 1065 | // Otherwise, do not change the parser state and return "false". 1066 | fn (mut p Parser) expect_optional_keyword(value string) !bool { 1067 | token := p.lexer.token 1068 | val := token.value or { '' } 1069 | 1070 | if token.kind == TokenKind.name && val == value { 1071 | p.advance_lexer()! 1072 | return true 1073 | } 1074 | 1075 | return false 1076 | } 1077 | 1078 | // Helper function for creating an error when an unexpected lexed token is encountered. 1079 | fn (parser Parser) unexpected(at_token ?&Token) ! { 1080 | token := match at_token { 1081 | none { 1082 | parser.lexer.token 1083 | } 1084 | else { 1085 | at_token 1086 | } 1087 | } 1088 | 1089 | return error('Unexpected token ${token.to_json()}') 1090 | } 1091 | 1092 | // Returns a possibly empty list of parse nodes, determined by the parseFn. 1093 | // This list begins with a lex token of openKind and ends with a lex token of closeKind. 1094 | // Advances the parser to the next lex token after the closing token. 1095 | fn (mut p Parser) any[T](open_kind TokenKind, parse_fn fn () T, close_kind TokenKind) []T { 1096 | p.expect_token(open_kind) or { panic('err') } 1097 | mut nodes := []T{} 1098 | 1099 | for { 1100 | if p.expect_optional_token(close_kind) { 1101 | break 1102 | } 1103 | 1104 | nodes << parse_fn() 1105 | } 1106 | 1107 | return nodes 1108 | } 1109 | 1110 | fn (mut parser Parser) advance_lexer() ! { 1111 | // println('...advancing lexer') 1112 | token := parser.lexer.advance() 1113 | 1114 | // println('...advanced to ${token.kind} from previous') 1115 | 1116 | max_tokens := parser.options.max_tokens 1117 | 1118 | if token.kind != TokenKind.eof { 1119 | parser.token_counter += 1 1120 | 1121 | if parser.token_counter > max_tokens { 1122 | panic('Document currently contains (${parser.token_counter}) more than the defined max of ${max_tokens} tokens. Aborting') 1123 | } 1124 | } 1125 | 1126 | // println('...finished advancing') 1127 | } 1128 | 1129 | fn (mut parser Parser) parse_directive(is_const bool) !DirectiveNode { 1130 | start := parser.lexer.token 1131 | 1132 | parser.expect_token(TokenKind.at)! 1133 | 1134 | return parser.node[DirectiveNode](start, mut DirectiveNode{ 1135 | kind: Kind.directive 1136 | name: parser.parse_name()! 1137 | arguments: parser.parse_arguments(is_const) 1138 | is_const: is_const 1139 | }) 1140 | } 1141 | 1142 | fn (mut parser Parser) parse_directives(is_const bool) ![]DirectiveNode { 1143 | mut directives := []DirectiveNode{} 1144 | 1145 | for { 1146 | if !parser.peek(TokenKind.at) { 1147 | break 1148 | } 1149 | 1150 | directives << parser.parse_directive(is_const)! 1151 | } 1152 | 1153 | return directives 1154 | } 1155 | 1156 | fn (mut parser Parser) parse_const_directives() ![]DirectiveNode { 1157 | return parser.parse_directives(true) 1158 | } 1159 | 1160 | fn get_token_kind_desc(kind TokenKind) string { 1161 | if is_punctuator_token_kind(kind) { 1162 | return '"${kind.gql_str()}"' 1163 | } 1164 | 1165 | return kind.gql_str() 1166 | } 1167 | --------------------------------------------------------------------------------