词法分析的核心任务是将无结构的原始字符串输入,转化为一个结构化的、线性的词元(Token)序列。每个词法单元都代表了公式中的一个最小语法单元,例如数字、操作符、括号或变量名。在此过程中,所有与语法结构无关的字符,如空格、换行符等,都会被识别并丢弃。
一个典型的词法分析器(一般取名 Lexer
或者 Tokenizer
)。它会维护一个指向输入字符串的指针,并从当前位置开始,按顺序根据正则表达式进行匹配。一旦找到匹配项,它就会生成一个对应的词元对象,并将指针向前移动匹配的长度,然后开始下一轮匹配。
每个词元对象不仅包含其类型(type) 和值 (value),还必须包含精确的位置信息,如行号(line)和列号(column)。
export class Lexer {
analyse(source: Iterator<string>) {
const tokens: Token[] = []
const it = new PeekIterator(source, '\0')
while (it.hasNext()) {
const c = it.next()
if (c === '\0') {
break
}
const lookahead = it.peek()
if (c === ' ' || c === '\n' || c === '\r') {
continue
}
if (c === '(' || c === ')') {
tokens.push(new Token(TokenType.BRACKET, c))
continue
}
if (c === '"' || c === "'") {
it.putBack()
tokens.push(Token.makeString(it))
continue
}
if (AlphabetHelper.isLetter(c)) {
it.putBack()
const lastToken = tokens[tokens.length - 1] || null
const newToken = Token.makeVarOrFunction(it)
if(newToken.getType() !== TokenType.FUNCTION) {
tokens.push(newToken)
continue
} else if(lastToken && lastToken.getType() !== TokenType.OPERATOR && lastToken.getValue()!=='('){
throw new FormulaError({'errCode':ERRORCode.UNEXPECTED_FUNCTION_NAME})
} else {
tokens.push(newToken)
continue
}
}
if (AlphabetHelper.isNumber(c)) {
it.putBack()
tokens.push(Token.makeNumber(it))
continue
}
throw LexicalException.fromChar(c)
}
return tokens
}
}
export class AlphabetHelper {
static ptnLetter = /^[a-zA-Z]$/
static ptnNumber = /^[0-9]$/
static ptnLiteral = /^[_a-zA-Z0-9]$/
static ptnOperator = /^[+\-*/><=!&|^%,]$/
static isLetter(c: string): boolean {
return AlphabetHelper.ptnLetter.test(c)
}
static isNumber(c: string): boolean {
return AlphabetHelper.ptnNumber.test(c)
}
static isLiteral(c: string): boolean {
return AlphabetHelper.ptnLiteral.test(c)
}
static isOperator(c: string): boolean {
return AlphabetHelper.ptnOperator.test(c)
}
}