onlineParser.js.flow 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. // @flow strict
  2. import { Lexer } from '../lexer';
  3. import { Source } from '../source';
  4. import GraphQLGrammar from './grammar';
  5. import type {
  6. GraphQLGrammarRule,
  7. GraphQLGrammarRuleName,
  8. GraphQLGrammarRuleConstraint,
  9. GraphQLGrammarTokenConstraint,
  10. GraphQLGrammarOfTypeConstraint,
  11. GraphQLGrammarListOfTypeConstraint,
  12. GraphQLGrammarPeekConstraint,
  13. GraphQLGrammarConstraintsSet,
  14. } from './grammar';
  15. export const TokenKind = {
  16. NAME: 'Name',
  17. INT: 'Int',
  18. FLOAT: 'Float',
  19. STRING: 'String',
  20. BLOCK_STRING: 'BlockString',
  21. COMMENT: 'Comment',
  22. PUNCTUATION: 'Punctuation',
  23. EOF: '<EOF>',
  24. INVALID: 'Invalid',
  25. };
  26. export const RuleKind = {
  27. TOKEN_CONSTRAINT: 'TokenConstraint',
  28. OF_TYPE_CONSTRAINT: 'OfTypeConstraint',
  29. LIST_OF_TYPE_CONSTRAINT: 'ListOfTypeConstraint',
  30. PEEK_CONSTRAINT: 'PeekConstraint',
  31. CONSTRAINTS_SET: 'ConstraintsSet',
  32. CONSTRAINTS_SET_ROOT: 'ConstraintsSetRoot',
  33. RULE_NAME: 'RuleName',
  34. INVALID: 'Invalid',
  35. };
  36. interface BaseOnlineParserRule {
  37. kind: string;
  38. name?: string;
  39. depth: number;
  40. step: number;
  41. expanded: boolean;
  42. state: string;
  43. optional?: boolean;
  44. eatNextOnFail?: boolean;
  45. }
  46. interface TokenOnlineParserRule
  47. extends BaseOnlineParserRule,
  48. GraphQLGrammarTokenConstraint {}
  49. interface OfTypeOnlineParserRule
  50. extends BaseOnlineParserRule,
  51. GraphQLGrammarOfTypeConstraint {}
  52. interface ListOfTypeOnlineParserRule
  53. extends BaseOnlineParserRule,
  54. GraphQLGrammarListOfTypeConstraint {}
  55. interface PeekOnlineParserRule
  56. extends BaseOnlineParserRule,
  57. GraphQLGrammarPeekConstraint {
  58. index: number;
  59. matched: boolean;
  60. }
  61. interface ConstraintsSetOnlineParserRule extends BaseOnlineParserRule {
  62. constraintsSet: boolean;
  63. constraints: GraphQLGrammarConstraintsSet;
  64. }
  65. type OnlineParserRule =
  66. | TokenOnlineParserRule
  67. | OfTypeOnlineParserRule
  68. | ListOfTypeOnlineParserRule
  69. | PeekOnlineParserRule
  70. | ConstraintsSetOnlineParserRule;
  71. export type OnlineParserState = {|
  72. rules: Array<OnlineParserRule>,
  73. kind: () => string,
  74. step: () => number,
  75. levels: Array<number>,
  76. indentLevel: number,
  77. name: string | null,
  78. type: string | null,
  79. |};
  80. type Token = {|
  81. kind: string,
  82. value: string,
  83. tokenName?: ?string,
  84. ruleName?: ?string,
  85. |};
  86. type LexerToken = {|
  87. kind: string,
  88. value: ?string,
  89. |};
  90. type OnlineParserConfig = {|
  91. tabSize: number,
  92. |};
  93. type OnlineParserConfigOption = {|
  94. tabSize: ?number,
  95. |};
  96. export class OnlineParser {
  97. state: OnlineParserState;
  98. _lexer: Lexer;
  99. _config: OnlineParserConfig;
  100. constructor(
  101. source: string,
  102. state?: OnlineParserState,
  103. config?: OnlineParserConfigOption,
  104. ) {
  105. this.state = state || OnlineParser.startState();
  106. this._config = {
  107. tabSize: config?.tabSize ?? 2,
  108. };
  109. this._lexer = new Lexer(new Source(source));
  110. }
  111. static startState(): OnlineParserState {
  112. return {
  113. rules: [
  114. // $FlowFixMe[cannot-spread-interface]
  115. {
  116. name: 'Document',
  117. state: 'Document',
  118. kind: 'ListOfTypeConstraint',
  119. ...GraphQLGrammar.Document,
  120. expanded: false,
  121. depth: 1,
  122. step: 1,
  123. },
  124. ],
  125. name: null,
  126. type: null,
  127. levels: [],
  128. indentLevel: 0,
  129. kind(): string {
  130. return this.rules[this.rules.length - 1]?.state || '';
  131. },
  132. step(): number {
  133. return this.rules[this.rules.length - 1]?.step || 0;
  134. },
  135. };
  136. }
  137. static copyState(state: OnlineParserState): OnlineParserState {
  138. return {
  139. name: state.name,
  140. type: state.type,
  141. rules: JSON.parse(JSON.stringify(state.rules)),
  142. levels: [...state.levels],
  143. indentLevel: state.indentLevel,
  144. kind(): string {
  145. return this.rules[this.rules.length - 1]?.state || '';
  146. },
  147. step(): number {
  148. return this.rules[this.rules.length - 1]?.step || 0;
  149. },
  150. };
  151. }
  152. sol(): boolean {
  153. return (
  154. this._lexer.source.locationOffset.line === 1 &&
  155. this._lexer.source.locationOffset.column === 1
  156. );
  157. }
  158. parseToken(): Token {
  159. const rule = (this._getNextRule(): any);
  160. if (this.sol()) {
  161. this.state.indentLevel = Math.floor(
  162. this.indentation() / this._config.tabSize,
  163. );
  164. }
  165. if (!rule) {
  166. return {
  167. kind: TokenKind.INVALID,
  168. value: '',
  169. };
  170. }
  171. let token;
  172. if (this._lookAhead().kind === '<EOF>') {
  173. return {
  174. kind: TokenKind.EOF,
  175. value: '',
  176. ruleName: rule.name,
  177. };
  178. }
  179. switch (rule.kind) {
  180. case RuleKind.TOKEN_CONSTRAINT:
  181. token = this._parseTokenConstraint(rule);
  182. break;
  183. case RuleKind.LIST_OF_TYPE_CONSTRAINT:
  184. token = this._parseListOfTypeConstraint(rule);
  185. break;
  186. case RuleKind.OF_TYPE_CONSTRAINT:
  187. token = this._parseOfTypeConstraint(rule);
  188. break;
  189. case RuleKind.PEEK_CONSTRAINT:
  190. token = this._parsePeekConstraint(rule);
  191. break;
  192. case RuleKind.CONSTRAINTS_SET_ROOT:
  193. token = this._parseConstraintsSetRule(rule);
  194. break;
  195. default:
  196. return {
  197. kind: TokenKind.INVALID,
  198. value: '',
  199. ruleName: rule.name,
  200. };
  201. }
  202. if (token && token.kind === TokenKind.INVALID) {
  203. if (rule.optional === true) {
  204. this.state.rules.pop();
  205. } else {
  206. this._rollbackRule();
  207. }
  208. return this.parseToken() || token;
  209. }
  210. return token;
  211. }
  212. indentation(): number {
  213. const match = this._lexer.source.body.match(/\s*/);
  214. let indent = 0;
  215. if (match && match.length === 0) {
  216. const whiteSpaces = match[0];
  217. let pos = 0;
  218. while (whiteSpaces.length > pos) {
  219. if (whiteSpaces.charCodeAt(pos) === 9) {
  220. indent += 2;
  221. } else {
  222. indent++;
  223. }
  224. pos++;
  225. }
  226. }
  227. return indent;
  228. }
  229. _parseTokenConstraint(rule: TokenOnlineParserRule): Token {
  230. rule.expanded = true;
  231. const token = this._lookAhead();
  232. if (!this._matchToken(token, rule)) {
  233. return {
  234. kind: TokenKind.INVALID,
  235. value: '',
  236. tokenName: rule.tokenName,
  237. ruleName: rule.name,
  238. };
  239. }
  240. this._advanceToken();
  241. const parserToken = this._transformLexerToken(token, rule);
  242. this._popMatchedRule(parserToken);
  243. return parserToken;
  244. }
  245. _parseListOfTypeConstraint(rule: ListOfTypeOnlineParserRule): Token {
  246. this._pushRule(
  247. GraphQLGrammar[rule.listOfType],
  248. rule.depth + 1,
  249. rule.listOfType,
  250. 1,
  251. rule.state,
  252. );
  253. rule.expanded = true;
  254. const token = this.parseToken();
  255. return token;
  256. }
  257. _parseOfTypeConstraint(rule: OfTypeOnlineParserRule): Token {
  258. if (rule.expanded) {
  259. this._popMatchedRule();
  260. return this.parseToken();
  261. }
  262. this._pushRule(rule.ofType, rule.depth + 1, rule.tokenName, 1, rule.state);
  263. rule.expanded = true;
  264. const token = this.parseToken();
  265. return token;
  266. }
  267. _parsePeekConstraint(rule: PeekOnlineParserRule): Token {
  268. if (rule.expanded) {
  269. this._popMatchedRule();
  270. return this.parseToken();
  271. }
  272. while (!rule.matched && rule.index < rule.peek.length - 1) {
  273. rule.index++;
  274. const constraint = rule.peek[rule.index];
  275. let { ifCondition } = constraint;
  276. if (typeof ifCondition === 'string') {
  277. ifCondition = GraphQLGrammar[ifCondition];
  278. }
  279. let token = this._lookAhead();
  280. if (ifCondition && this._matchToken(token, ifCondition)) {
  281. rule.matched = true;
  282. rule.expanded = true;
  283. this._pushRule(constraint.expect, rule.depth + 1, '', 1, rule.state);
  284. token = this.parseToken();
  285. return token;
  286. }
  287. }
  288. return {
  289. kind: TokenKind.INVALID,
  290. value: '',
  291. ruleName: rule.name,
  292. };
  293. }
  294. _parseConstraintsSetRule(rule: ConstraintsSetOnlineParserRule): Token {
  295. if (rule.expanded) {
  296. this._popMatchedRule();
  297. return this.parseToken();
  298. }
  299. for (let index = rule.constraints.length - 1; index >= 0; index--) {
  300. this._pushRule(
  301. rule.constraints[index],
  302. rule.depth + 1,
  303. '',
  304. index,
  305. rule.state,
  306. );
  307. }
  308. rule.expanded = true;
  309. return this.parseToken();
  310. }
  311. _matchToken(
  312. token: Token | LexerToken,
  313. rule: GraphQLGrammarTokenConstraint,
  314. ): boolean {
  315. if (typeof token.value === 'string') {
  316. if (
  317. (typeof rule.ofValue === 'string' && token.value !== rule.ofValue) ||
  318. (Array.isArray(rule.oneOf) && !rule.oneOf.includes(token.value)) ||
  319. (typeof rule.ofValue !== 'string' &&
  320. !Array.isArray(rule.oneOf) &&
  321. token.kind !== rule.token)
  322. ) {
  323. return false;
  324. }
  325. return this._butNot(token, rule);
  326. }
  327. if (token.kind !== rule.token) {
  328. return false;
  329. }
  330. return this._butNot(token, rule);
  331. }
  332. _butNot(
  333. token: Token | LexerToken,
  334. rule: GraphQLGrammarRuleConstraint,
  335. ): boolean {
  336. if (rule.butNot) {
  337. if (Array.isArray(rule.butNot)) {
  338. if (
  339. rule.butNot.reduce(
  340. (matched, constraint) =>
  341. matched || this._matchToken(token, constraint),
  342. false,
  343. )
  344. ) {
  345. return false;
  346. }
  347. return true;
  348. }
  349. return !this._matchToken(token, rule.butNot);
  350. }
  351. return true;
  352. }
  353. _transformLexerToken(lexerToken: LexerToken, rule: any): Token {
  354. let token;
  355. const ruleName = rule.name || '';
  356. const tokenName = rule.tokenName || '';
  357. if (lexerToken.kind === '<EOF>' || lexerToken.value !== undefined) {
  358. token = {
  359. kind: lexerToken.kind,
  360. value: lexerToken.value || '',
  361. tokenName,
  362. ruleName,
  363. };
  364. if (token.kind === TokenKind.STRING) {
  365. token.value = `"${token.value}"`;
  366. } else if (token.kind === TokenKind.BLOCK_STRING) {
  367. token.value = `"""${token.value}"""`;
  368. }
  369. } else {
  370. token = {
  371. kind: TokenKind.PUNCTUATION,
  372. value: lexerToken.kind,
  373. tokenName,
  374. ruleName,
  375. };
  376. if (/^[{([]/.test(token.value)) {
  377. if (this.state.indentLevel !== undefined) {
  378. this.state.levels = this.state.levels.concat(
  379. this.state.indentLevel + 1,
  380. );
  381. }
  382. } else if (/^[})\]]/.test(token.value)) {
  383. this.state.levels.pop();
  384. }
  385. }
  386. return token;
  387. }
  388. _getNextRule(): OnlineParserRule | null {
  389. return this.state.rules[this.state.rules.length - 1] || null;
  390. }
  391. _popMatchedRule(token: ?Token) {
  392. const rule = this.state.rules.pop();
  393. if (!rule) {
  394. return;
  395. }
  396. if (token && rule.kind === RuleKind.TOKEN_CONSTRAINT) {
  397. const constraint = rule;
  398. if (typeof constraint.definitionName === 'string') {
  399. this.state.name = token.value || null;
  400. } else if (typeof constraint.typeName === 'string') {
  401. this.state.type = token.value || null;
  402. }
  403. }
  404. const nextRule = this._getNextRule();
  405. if (!nextRule) {
  406. return;
  407. }
  408. if (
  409. nextRule.depth === rule.depth - 1 &&
  410. nextRule.expanded &&
  411. nextRule.kind === RuleKind.CONSTRAINTS_SET_ROOT
  412. ) {
  413. this.state.rules.pop();
  414. }
  415. if (
  416. nextRule.depth === rule.depth - 1 &&
  417. nextRule.expanded &&
  418. nextRule.kind === RuleKind.LIST_OF_TYPE_CONSTRAINT
  419. ) {
  420. nextRule.expanded = false;
  421. nextRule.optional = true;
  422. }
  423. }
  424. _rollbackRule() {
  425. if (!this.state.rules.length) {
  426. return;
  427. }
  428. const popRule = () => {
  429. const lastPoppedRule = this.state.rules.pop();
  430. if (lastPoppedRule.eatNextOnFail === true) {
  431. this.state.rules.pop();
  432. }
  433. };
  434. const poppedRule = this.state.rules.pop();
  435. if (!poppedRule) {
  436. return;
  437. }
  438. let popped = 0;
  439. let nextRule = this._getNextRule();
  440. while (
  441. nextRule &&
  442. (poppedRule.kind !== RuleKind.LIST_OF_TYPE_CONSTRAINT ||
  443. nextRule.expanded) &&
  444. nextRule.depth > poppedRule.depth - 1
  445. ) {
  446. this.state.rules.pop();
  447. popped++;
  448. nextRule = this._getNextRule();
  449. }
  450. if (nextRule && nextRule.expanded) {
  451. if (nextRule.optional === true) {
  452. popRule();
  453. } else {
  454. if (
  455. nextRule.kind === RuleKind.LIST_OF_TYPE_CONSTRAINT &&
  456. popped === 1
  457. ) {
  458. this.state.rules.pop();
  459. return;
  460. }
  461. this._rollbackRule();
  462. }
  463. }
  464. }
  465. _pushRule(
  466. baseRule: any,
  467. depth: number,
  468. name?: string,
  469. step?: number,
  470. state?: string,
  471. ) {
  472. this.state.name = null;
  473. this.state.type = null;
  474. let rule = baseRule;
  475. switch (this._getRuleKind(rule)) {
  476. case RuleKind.RULE_NAME:
  477. rule = (rule: GraphQLGrammarRuleName);
  478. this._pushRule(
  479. GraphQLGrammar[rule],
  480. depth,
  481. (typeof name === 'string' ? name : undefined) || rule,
  482. step,
  483. state,
  484. );
  485. break;
  486. case RuleKind.CONSTRAINTS_SET:
  487. rule = (rule: GraphQLGrammarConstraintsSet);
  488. this.state.rules.push({
  489. name: name || '',
  490. depth,
  491. expanded: false,
  492. constraints: rule,
  493. constraintsSet: true,
  494. kind: RuleKind.CONSTRAINTS_SET_ROOT,
  495. state:
  496. (typeof name === 'string' ? name : undefined) ||
  497. (typeof state === 'string' ? state : undefined) ||
  498. this._getNextRule()?.state ||
  499. '',
  500. step:
  501. typeof step === 'number'
  502. ? step
  503. : (this._getNextRule()?.step || 0) + 1,
  504. });
  505. break;
  506. case RuleKind.OF_TYPE_CONSTRAINT:
  507. rule = (rule: GraphQLGrammarOfTypeConstraint);
  508. this.state.rules.push({
  509. name: name || '',
  510. ofType: rule.ofType,
  511. optional: Boolean(rule.optional),
  512. butNot: rule.butNot,
  513. eatNextOnFail: Boolean(rule.eatNextOnFail),
  514. depth,
  515. expanded: false,
  516. kind: RuleKind.OF_TYPE_CONSTRAINT,
  517. state:
  518. (typeof rule.tokenName === 'string' ? rule.tokenName : undefined) ||
  519. (typeof name === 'string' ? name : undefined) ||
  520. (typeof state === 'string' ? state : undefined) ||
  521. this._getNextRule()?.state ||
  522. '',
  523. step:
  524. typeof step === 'number'
  525. ? step
  526. : (this._getNextRule()?.step || 0) + 1,
  527. });
  528. break;
  529. case RuleKind.LIST_OF_TYPE_CONSTRAINT:
  530. rule = (rule: GraphQLGrammarListOfTypeConstraint);
  531. this.state.rules.push({
  532. listOfType: rule.listOfType,
  533. optional: Boolean(rule.optional),
  534. butNot: rule.butNot,
  535. eatNextOnFail: Boolean(rule.eatNextOnFail),
  536. name: name || '',
  537. depth,
  538. expanded: false,
  539. kind: RuleKind.LIST_OF_TYPE_CONSTRAINT,
  540. state:
  541. (typeof name === 'string' ? name : undefined) ||
  542. (typeof state === 'string' ? state : undefined) ||
  543. this._getNextRule()?.state ||
  544. '',
  545. step:
  546. typeof step === 'number'
  547. ? step
  548. : (this._getNextRule()?.step || 0) + 1,
  549. });
  550. break;
  551. case RuleKind.TOKEN_CONSTRAINT:
  552. rule = (rule: GraphQLGrammarTokenConstraint);
  553. this.state.rules.push({
  554. token: rule.token,
  555. ofValue: rule.ofValue,
  556. oneOf: rule.oneOf,
  557. definitionName: Boolean(rule.definitionName),
  558. typeName: Boolean(rule.typeName),
  559. optional: Boolean(rule.optional),
  560. butNot: rule.butNot,
  561. eatNextOnFail: Boolean(rule.eatNextOnFail),
  562. name: name || '',
  563. depth,
  564. expanded: false,
  565. kind: RuleKind.TOKEN_CONSTRAINT,
  566. state:
  567. (typeof rule.tokenName === 'string' ? rule.tokenName : undefined) ||
  568. (typeof state === 'string' ? state : undefined) ||
  569. this._getNextRule()?.state ||
  570. '',
  571. step:
  572. typeof step === 'number'
  573. ? step
  574. : (this._getNextRule()?.step || 0) + 1,
  575. });
  576. break;
  577. case RuleKind.PEEK_CONSTRAINT:
  578. rule = (rule: GraphQLGrammarPeekConstraint);
  579. this.state.rules.push({
  580. peek: rule.peek,
  581. optional: Boolean(rule.optional),
  582. butNot: rule.butNot,
  583. eatNextOnFail: Boolean(rule.eatNextOnFail),
  584. name: name || '',
  585. depth,
  586. index: -1,
  587. matched: false,
  588. expanded: false,
  589. kind: RuleKind.PEEK_CONSTRAINT,
  590. state:
  591. (typeof state === 'string' ? state : undefined) ||
  592. this._getNextRule()?.state ||
  593. '',
  594. step:
  595. typeof step === 'number'
  596. ? step
  597. : (this._getNextRule()?.step || 0) + 1,
  598. });
  599. break;
  600. }
  601. }
  602. _getRuleKind(rule: GraphQLGrammarRule | OnlineParserRule): string {
  603. if (Array.isArray(rule)) {
  604. return RuleKind.CONSTRAINTS_SET;
  605. }
  606. if (rule.constraintsSet === true) {
  607. return RuleKind.CONSTRAINTS_SET_ROOT;
  608. }
  609. if (typeof rule === 'string') {
  610. return RuleKind.RULE_NAME;
  611. }
  612. if (Object.prototype.hasOwnProperty.call(rule, 'ofType')) {
  613. return RuleKind.OF_TYPE_CONSTRAINT;
  614. }
  615. if (Object.prototype.hasOwnProperty.call(rule, 'listOfType')) {
  616. return RuleKind.LIST_OF_TYPE_CONSTRAINT;
  617. }
  618. if (Object.prototype.hasOwnProperty.call(rule, 'peek')) {
  619. return RuleKind.PEEK_CONSTRAINT;
  620. }
  621. if (Object.prototype.hasOwnProperty.call(rule, 'token')) {
  622. return RuleKind.TOKEN_CONSTRAINT;
  623. }
  624. return RuleKind.INVALID;
  625. }
  626. _advanceToken(): LexerToken {
  627. return (this._lexer.advance(): any);
  628. }
  629. _lookAhead(): LexerToken {
  630. try {
  631. return (this._lexer.lookahead(): any);
  632. } catch (err) {
  633. return { kind: TokenKind.INVALID, value: '' };
  634. }
  635. }
  636. }