Last active
November 29, 2018 07:54
-
-
Save Jokcy/81e726b0b2d281ff7e3e0c6a9929d538 to your computer and use it in GitHub Desktop.
graphql compiler
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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