Skip to content

Instantly share code, notes, and snippets.

@Jokcy
Last active November 29, 2018 07:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jokcy/81e726b0b2d281ff7e3e0c6a9929d538 to your computer and use it in GitHub Desktop.
Save Jokcy/81e726b0b2d281ff7e3e0c6a9929d538 to your computer and use it in GitHub Desktop.
graphql compiler
const input1 = `User {
id,
name,
age
}`
const input2 = `User( age:21 ){
id,
name,
assigned Task( content: 'run' ): {
id,
content
}
}`
let WHITESPACE = /\s/
let UPPER_CASE = /[A-Z]/
let LOWER_CASE = /[a-z]/
let LETTER = /[A-Za-z]/
let NUMBER = /[0-9]/
function tokenizer(input) {
let current = 0
let tokens = []
while (current < input.length) {
let char = input[current]
// 匹配小括号
if (char === '(' || char === ')') {
tokens.push({
type: 'paren',
value: char,
})
current++
continue
}
// 匹配大括号
if (char === '{' || char === '}') {
tokens.push({
type: 'brace',
value: char,
})
current++
continue
}
// 匹配逗号
if (char === ',') {
tokens.push({
type: 'comma',
value: char,
})
current++
continue
}
// 匹配分号
if (char === ':') {
tokens.push({
type: 'colon',
value: char,
})
current++
continue
}
if (WHITESPACE.test(char)) {
current++
continue
}
// 找到number
if (NUMBER.test(char)) {
let value = ''
console.log('start number')
while (NUMBER.test(char)) {
value += char
char = input[++current]
}
tokens.push({ type: 'number', value })
continue
}
// 字符串变量
if (char === "'") {
let value = ''
char = input[++current]
console.log('start string')
while (char !== "'") {
value += char
char = input[++current]
}
char = input[++current]
tokens.push({ type: 'string', value })
continue
}
// 先测试大写开始
// 因为大写开始也符合字母开始
if (UPPER_CASE.test(char)) {
let value = ''
console.log('start type')
while (LETTER.test(char)) {
value += char
char = input[++current]
}
tokens.push({ type: 'type', value })
continue
}
// 匹配普通字符串
if (LETTER.test(char)) {
let value = ''
console.log('start field')
while (LETTER.test(char)) {
value += char
char = input[++current]
}
tokens.push({ type: 'field', value })
continue
}
throw new TypeError('I dont know what this character is: ' + char)
}
return tokens
}
function parser(tokens) {
let current = 0
let ast = []
function walk() {
let { type, value } = tokens[current]
if (type === 'type') {
current += 1
return { type: 'type', value }
}
if (value === ':') {
current += 1
return
}
if (type === 'paren' && value === '(') {
// let token = tokens[++current]
let attrs = {}
let name, value
let token = tokens[++current]
while (
token.type !== 'paren' ||
(token.type === 'paren' && token.value !== ')')
) {
name = tokens[current].value
current += 2
value = tokens[current].value
current += 1
attrs[name] = value
// 是否通过逗号分隔变量?
if (tokens[current].type === 'comma') {
current += 1
}
token = tokens[current]
}
current += 1
return { type: 'attrs', value: attrs }
}
if (type === 'brace' && value === '{') {
let fields = []
let relations = ast.relations || {}
let token = tokens[++current]
let field
console.log('start fields')
while (
token.type !== 'brace' ||
(token.type === 'brace' && token.value !== '}')
) {
field = token.value
token = tokens[++current]
console.log(field, token)
if (token.type === 'brace' && token.value === '}') {
console.log('last field')
fields.push(field)
continue
}
if (token.type === 'comma') {
fields.push(field)
current += 1
token = tokens[current]
continue
}
if (token.type === 'type') {
let relation = (relations[`${field} ${token.value}`] = {
name: field,
target: [],
})
console.log('start relation', field, tokens[current].type)
while (
token.type !== 'brace' ||
(token.type === 'brace' && token.value !== '}')
) {
let result = walk()
if (result) {
result = Array.isArray(result) ? result : [result]
relation.target.push(...result)
}
token = tokens[current]
console.log('relation loop', token)
}
console.log('relation end', ast)
// current += 1
token = tokens[current]
}
}
current += 1
// console.log(tokens[current])
return [
{ type: 'fields', value: fields },
{ type: 'relations', value: relations },
]
}
}
while (current < tokens.length) {
// ast.body.push(walk())
let result = walk()
if (result) {
result = Array.isArray(result) ? result : [result]
ast.push(...result)
}
}
console.log('ast:', ast)
return ast
}
function turnInToObject(maps) {
// if (maps.length > 4) {
// // 语法是不是不对?
// return
// }
return maps.reduce((result, item) => {
if (item.type == 'relations') {
let relationsObj = {}
Object.keys(item.value).forEach(relation => {
relationsObj[relation] = Object.assign({}, item.value[relation], {
target: turnInToObject(item.value[relation].target),
})
})
result['relations'] = relationsObj
} else {
result[item.type] = item.value
}
return result
}, {})
}
function parse(input) {
const tokens = tokenizer(input)
console.log(tokens)
const results = parser(tokens)
// return results
return turnInToObject(results)
}
// console.log(parse(input2)[3].value['assigned Task'])
console.log(parse(input1))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment