const tokenTypes = { OPAREN: '(', CPAREN: ')', KEYWORD: 'keyword', QUOTE: '"', LITERAL: 'literal', ATTRIBUTE: 'attribute', EOF: 'eof', } const scan = source => { pos = 0 line = 1 tokens = [] while (pos < source.length) { if (source[pos].match(/\(/)) { tokens.push({ type: tokenTypes.OPAREN, line: line, }) pos++ } else if (source[pos].match(/\)/)) { tokens.push({ type: tokenTypes.CPAREN, line: line, }) pos++ } else if (source[pos].match(/['"]/)) { tokens.push({ type: tokenTypes.QUOTE, line: line, }) pos++ } else if (source[pos].match(/:/)) { let value = /:([^()'"\s]+)/.exec(source)[1].trim() tokens.push({ type: tokenTypes.ATTRIBUTE, line: line, value: value, }) pos += value.length + 1 // the +1 is to account for the colon } else if (source[pos].match(/\n/)) { line++ pos++ } else if (source[pos].match(/\s/)) { pos++ } else { let value = /[^()"':]+/.exec(source.slice(pos))[0] tokens.push({ type: tokenTypes.LITERAL, line: line, value: value.trim(), }) pos += value.length } } tokens.push({ type: tokenTypes.EOF, line: line, }) return tokens } module.exports = { scan: scan, tokenTypes: tokenTypes, }