Go语言词法分析器执行异常:else分支提前触发问题排查
词法分析器异常问题排查
问题描述
我用Go语言实现了一个词法分析器,输入内容为+- */。预期程序会先识别+、-、*、/等合法Token,再处理未知Token,但实际执行时,else分支的未知Token提示被优先输出,且代码中println(token.text, token.kind, "token 119")语句未按预期打印有效内容。
实现代码
// Invalid token found, print error message and exit. func abort(message string) { panic(message) } // Skip whitespace except newlines, which we will use to indicate the end of a statement. func (s *Source) skipWhitespace() { for s.curChar == " " || s.curChar == "\t" || s.curChar == "\r" { s.nextChar() } } // Skip comments in the code. func skipComment() {} // Return the next token. func (s *Source) getToken() Token { var token Token // Check the first character of this token to see if we can decide what it is. // If it is a multiple character operator (e.g., !=), number, identifier, or keyword then we will process the rest. // s.skipWhitespace() println("curChar: " + s.curChar) s.skipWhitespace() println("curChar: after 84 " + s.curChar) if s.curChar == "+" { token = Token{s.curChar, PLUS} } else if s.curChar == "-" { token = Token{s.curChar, MINUS} } else if s.curChar == "*" { token = Token{s.curChar, ASTERISK} } else if s.curChar == "/" { token = Token{s.curChar, SLASH} } else if s.curChar == "\n" { token = Token{s.curChar, NEWLINE} } else if s.curChar == "\000" { token = Token{s.curChar, EOF} } else { println("Unknown token: " + s.curChar) } s.nextChar() return token } // Process the next character. func (s *Source) nextChar() { s.curPos++ if s.curPos >= len(s.source) { s.curChar = "\000" // EOF } else { s.curChar = string(s.source[s.curPos]) } } func do_lexing(codeString string) { // code_string = `+- */` // Initialize the source. source := Source{source: codeString + "\n", curChar: "", curPos: -1} // Loop through and print all tokens. token := source.getToken() println(token.text, token.kind, "token 119") for token.kind != EOF { println(token.text, token.kind) token = source.getToken() } }
实际输出结果
curChar: curChar: after 84 Unknown token: 0 token 199 0 curChar: + curChar: after 84 + + 202 curChar: - curChar: after 84 - - 203 curChar: curChar: after 84 * * 204 curChar: / curChar: after 84 / / 205 curChar: curChar: after 84 0 curChar: curChar: after 84
问题原因与修复方案
核心问题
初始化未加载第一个有效字符
在do_lexing函数中,Source初始化时curPos设为-1,curChar为空字符串。第一次调用getToken时,直接处理空字符:- 空字符不在
skipWhitespace的跳过列表中,跳过逻辑不执行 - 进入else分支打印未知Token提示
- 之后调用
nextChar()才将curChar设置为输入的第一个字符+
这导致第一次getToken返回零值Token,出现开头的无效输出。
- 空字符不在
打印顺序符合代码执行流程
代码中先调用getToken()(内部包含println),再打印token 119,所以getToken里的打印会先出现,这是正常执行顺序,但因为第一次getToken拿到无效Token,导致token 119打印内容异常。
修复方案
初始化时加载第一个字符
在do_lexing中初始化Source后,先调用一次nextChar(),确保curChar指向输入的第一个有效字符:func do_lexing(codeString string) { source := Source{source: codeString + "\n", curChar: "", curPos: -1} source.nextChar() // 初始化第一个有效字符 token := source.getToken() println(token.text, token.kind, "token 119") // 剩余循环逻辑保持不变 }调整
getToken的处理顺序
将skipWhitespace()移到打印当前字符之前,确保先跳过空格再处理有效字符:func (s *Source) getToken() Token { var token Token s.skipWhitespace() // 先跳过空格 println("curChar: " + s.curChar) println("curChar: after skip " + s.curChar) // 后续Token判断逻辑保持不变 }完善else分支的Token赋值
在else分支中给token设置明确的未知类型,避免返回零值:} else { println("Unknown token: " + s.curChar) token = Token{s.curChar, UNKNOWN} // 需提前定义UNKNOWN常量 }
内容的提问来源于stack exchange,提问作者Farhan Ali Raza
相关产品推荐
相关产品推荐

