| 15 | } |
| 16 | |
| 17 | export default function lexer(lexicon: Lexicon, query: string): ILexerResult { |
| 18 | const result: ILexemeResult[] = []; |
| 19 | |
| 20 | while (query.length) { |
| 21 | query = query.replace(/^\s+/, ''); |
| 22 | |
| 23 | const previous = result.slice(-1)[0]; |
| 24 | const previousLexeme = previous ? previous.lexeme : null; |
| 25 | |
| 26 | const lexemes: ILexeme[] = lexicon.filter( |
| 27 | lexeme => |
| 28 | lexeme.if && |
| 29 | (!Array.isArray(lexeme.if) |
| 30 | ? lexeme.if(result, previous) |
| 31 | : previousLexeme |
| 32 | ? lexeme.if && lexeme.if.indexOf(previousLexeme.type) !== -1 |
| 33 | : lexeme.if && lexeme.if.indexOf(undefined) !== -1) |
| 34 | ); |
| 35 | |
| 36 | const next = R.find(lexeme => lexeme.regexp.test(query), lexemes); |
| 37 | if (!next) { |
| 38 | return {lexemes: result, valid: false, error: query}; |
| 39 | } |
| 40 | |
| 41 | const match = query.match(next.regexp) ?? []; |
| 42 | const value = match[next.regexpMatch || 0]; |
| 43 | const flags = match[next.regexpFlags || -1]; |
| 44 | result.push({lexeme: next, flags, value}); |
| 45 | |
| 46 | query = query.substring(value.length); |
| 47 | } |
| 48 | |
| 49 | const [terminalPrevious, last] = [undefined, undefined, ...result].slice( |
| 50 | -2 |
| 51 | ); |
| 52 | |
| 53 | const terminal: boolean = |
| 54 | !last || |
| 55 | (typeof last.lexeme.terminal === 'function' |
| 56 | ? last.lexeme.terminal(result, terminalPrevious) |
| 57 | : last.lexeme.terminal); |
| 58 | |
| 59 | return { |
| 60 | lexemes: result, |
| 61 | valid: terminal |
| 62 | }; |
| 63 | } |