一個極簡的詞法分析器實現

文章目錄

  • 推薦:Tiny Lexer - 一個極簡的C語言詞法分析器
    • 特點
    • 核心代碼實現
    • 學習價值
    • 擴展建議
  • 用Java實現一個簡單的詞法分析器
    • 完整實現代碼
    • 代碼解析
    • 示例輸出
    • 擴展建議
  • 用Go實現極簡詞法分析器
    • 完整實現代碼
    • 代碼解析
    • 示例輸出
    • 擴展建議

最近兩天搞一個DSL,不得不重溫了一把antlr4和編譯原理之詞法分析。

推薦:Tiny Lexer - 一個極簡的C語言詞法分析器

推薦一個非常小巧但完整的C語言詞法分析器實現 - Tiny Lexer。它具有以下優點:

特點

  • 代碼量極小(約100行核心代碼)
  • 純C實現,無外部依賴
  • 易于理解和學習
  • 包含完整的功能:標識符、數字、運算符識別等

核心代碼實現

#include <stdio.h>
#include <ctype.h>
#include <string.h>typedef enum {TOKEN_EOF,TOKEN_NUMBER,TOKEN_IDENTIFIER,TOKEN_OPERATOR,TOKEN_UNKNOWN
} TokenType;typedef struct {TokenType type;char value[32];
} Token;Token get_next_token(const char** input) {Token token = {TOKEN_UNKNOWN, {0}};// 跳過空白字符while (isspace(**input)) {(*input)++;}// 檢查文件結束if (**input == '\0') {token.type = TOKEN_EOF;return token;}// 處理數字if (isdigit(**input)) {token.type = TOKEN_NUMBER;int i = 0;while (isdigit(**input) && i < sizeof(token.value)-1) {token.value[i++] = *(*input)++;}token.value[i] = '\0';return token;}// 處理標識符(字母開頭)if (isalpha(**input)) {token.type = TOKEN_IDENTIFIER;int i = 0;while ((isalnum(**input) || **input == '_') && i < sizeof(token.value)-1) {token.value[i++] = *(*input)++;}token.value[i] = '\0';return token;}// 處理運算符if (strchr("+-*/=(){};", **input)) {token.type = TOKEN_OPERATOR;token.value[0] = *(*input)++;token.value[1] = '\0';return token;}// 未知字符token.value[0] = *(*input)++;return token;
}int main() {const char* input = "int x = 42 + y;";const char* p = input;while (1) {Token token = get_next_token(&p);if (token.type == TOKEN_EOF) break;const char* type_str;switch (token.type) {case TOKEN_NUMBER: type_str = "NUMBER"; break;case TOKEN_IDENTIFIER: type_str = "IDENTIFIER"; break;case TOKEN_OPERATOR: type_str = "OPERATOR"; break;default: type_str = "UNKNOWN"; break;}printf("Token: %-12s Value: %s\n", type_str, token.value);}return 0;
}

學習價值

  1. 詞法分析基本原理:展示了如何將輸入流分解為token
  2. 狀態機概念:通過條件判斷實現了簡單的狀態轉移
  3. 可擴展性:可以輕松添加更多token類型和規則
  4. 實用性:雖然簡單,但包含了詞法分析的核心功能

擴展建議

學習這個基本實現后,你可以嘗試:

  1. 添加更多運算符和關鍵字識別
  2. 實現更復雜的數字格式(如浮點數)
  3. 添加錯誤處理機制
  4. 將其擴展為遞歸下降語法分析器

這個實現去除了所有不必要的復雜性,是學習編譯原理前端技術的理想起點。

用Java實現一個簡單的詞法分析器

下面我將實現一個最簡的詞法分析器(Lexer),它可以識別以下類型的詞法單元(Token):

  • 整數(如 123
  • 浮點數(如 3.14
  • 標識符(如 variable
  • 運算符(如 +, -, *, /
  • 分隔符(如 (, ), ;

完整實現代碼

import java.util.ArrayList;
import java.util.List;public class SimpleLexer {// Token類型枚舉public enum TokenType {INTEGER,    // 整數FLOAT,      // 浮點數IDENTIFIER, // 標識符OPERATOR,   // 運算符DELIMITER,  // 分隔符EOF         // 文件結束}// Token類public static class Token {public final TokenType type;public final String value;public Token(TokenType type, String value) {this.type = type;this.value = value;}@Overridepublic String toString() {return String.format("Token(%s, '%s')", type, value);}}private final String input;private int position = 0;public SimpleLexer(String input) {this.input = input;}// 詞法分析主方法public List<Token> tokenize() {List<Token> tokens = new ArrayList<>();while (position < input.length()) {char current = peek();// 跳過空白字符if (Character.isWhitespace(current)) {consume();continue;}// 處理數字if (Character.isDigit(current)) {tokens.add(readNumber());continue;}// 處理標識符(以字母或下劃線開頭)if (Character.isLetter(current) || current == '_') {tokens.add(readIdentifier());continue;}// 處理運算符if (isOperator(current)) {tokens.add(new Token(TokenType.OPERATOR, Character.toString(consume())));continue;}// 處理分隔符if (isDelimiter(current)) {tokens.add(new Token(TokenType.DELIMITER, Character.toString(consume())));continue;}throw new RuntimeException("Unexpected character: " + current);}tokens.add(new Token(TokenType.EOF, ""));return tokens;}// 讀取數字(整數或浮點數)private Token readNumber() {StringBuilder sb = new StringBuilder();boolean hasDecimal = false;while (position < input.length() && (Character.isDigit(peek()) || (!hasDecimal && peek() == '.'))) {if (peek() == '.') {hasDecimal = true;}sb.append(consume());}return new Token(hasDecimal ? TokenType.FLOAT : TokenType.INTEGER, sb.toString());}// 讀取標識符private Token readIdentifier() {StringBuilder sb = new StringBuilder();while (position < input.length() && (Character.isLetterOrDigit(peek()) || peek() == '_')) {sb.append(consume());}return new Token(TokenType.IDENTIFIER, sb.toString());}// 輔助方法:查看當前字符但不消耗private char peek() {return input.charAt(position);}// 輔助方法:消耗當前字符并返回private char consume() {return input.charAt(position++);}// 判斷是否是運算符private boolean isOperator(char c) {return c == '+' || c == '-' || c == '*' || c == '/';}// 判斷是否是分隔符private boolean isDelimiter(char c) {return c == '(' || c == ')' || c == ';';}// 測試public static void main(String[] args) {String input = "x = 123 + (y * 3.14);";SimpleLexer lexer = new SimpleLexer(input);List<Token> tokens = lexer.tokenize();for (Token token : tokens) {System.out.println(token);}}
}

代碼解析

  1. Token類型定義

    • 使用枚舉TokenType定義了各種詞法單元類型
    • Token類封裝了類型和實際值
  2. 核心方法

    • tokenize():主方法,逐個字符分析輸入字符串
    • readNumber():讀取整數或浮點數
    • readIdentifier():讀取標識符
  3. 輔助方法

    • peek():查看當前字符但不移動指針
    • consume():讀取當前字符并移動指針
    • isOperator()isDelimiter():判斷字符類型
  4. 處理流程

    • 跳過空白字符
    • 按優先級識別數字、標識符、運算符和分隔符
    • 遇到無法識別的字符拋出異常

示例輸出

對于輸入"x = 123 + (y * 3.14);",輸出結果為:

Token(IDENTIFIER, 'x')
Token(OPERATOR, '=')
Token(INTEGER, '123')
Token(OPERATOR, '+')
Token(DELIMITER, '(')
Token(IDENTIFIER, 'y')
Token(OPERATOR, '*')
Token(FLOAT, '3.14')
Token(DELIMITER, ')')
Token(DELIMITER, ';')
Token(EOF, '')

擴展建議

這個簡單詞法分析器可以進一步擴展:

  1. 支持更多運算符(如==, !=等)
  2. 添加關鍵字識別(如if, while等)
  3. 支持字符串字面量
  4. 添加注釋處理
  5. 改進錯誤處理機制

這個實現是編譯器前端的基礎組件,可以作為更復雜詞法分析器的起點。

用Go實現極簡詞法分析器

下面是一個用Go語言實現的極簡詞法分析器,它可以識別以下詞法單元:

  • 整數(如 123
  • 標識符(如 variable
  • 運算符(如 +, -, *, /
  • 分隔符(如 (, ), ;

完整實現代碼

package mainimport ("fmt""unicode"
)// TokenType 表示詞法單元類型
type TokenType intconst (EOF TokenType = iota // 文件結束INTEGER             // 整數IDENTIFIER          // 標識符OPERATOR            // 運算符DELIMITER           // 分隔符
)// Token 表示一個詞法單元
type Token struct {Type  TokenTypeValue string
}// String 實現Stringer接口
func (t Token) String() string {return fmt.Sprintf("Token(%v, '%s')", t.Type, t.Value)
}// Lexer 詞法分析器結構體
type Lexer struct {input    stringposition int
}// NewLexer 創建新的詞法分析器
func NewLexer(input string) *Lexer {return &Lexer{input: input}
}// NextToken 獲取下一個詞法單元
func (l *Lexer) NextToken() Token {// 跳過空白字符l.skipWhitespace()// 檢查是否到達輸入末尾if l.position >= len(l.input) {return Token{Type: EOF, Value: ""}}current := l.peek()// 處理數字if unicode.IsDigit(current) {return l.readNumber()}// 處理標識符if unicode.IsLetter(current) || current == '_' {return l.readIdentifier()}// 處理運算符if isOperator(current) {token := Token{Type: OPERATOR, Value: string(l.consume())}return token}// 處理分隔符if isDelimiter(current) {token := Token{Type: DELIMITER, Value: string(l.consume())}return token}panic(fmt.Sprintf("未知字符: %c", current))
}// 讀取數字
func (l *Lexer) readNumber() Token {start := l.positionfor l.position < len(l.input) && unicode.IsDigit(l.peek()) {l.consume()}return Token{Type: INTEGER, Value: l.input[start:l.position]}
}// 讀取標識符
func (l *Lexer) readIdentifier() Token {start := l.positionfor l.position < len(l.input) && (unicode.IsLetter(l.peek()) || unicode.IsDigit(l.peek()) || l.peek() == '_') {l.consume()}return Token{Type: IDENTIFIER, Value: l.input[start:l.position]}
}// 跳過空白字符
func (l *Lexer) skipWhitespace() {for l.position < len(l.input) && unicode.IsSpace(l.peek()) {l.consume()}
}// 查看當前字符但不消耗
func (l *Lexer) peek() rune {if l.position >= len(l.input) {return 0}return rune(l.input[l.position])
}// 消耗當前字符并返回
func (l *Lexer) consume() rune {if l.position >= len(l.input) {return 0}char := rune(l.input[l.position])l.position++return char
}// 判斷是否是運算符
func isOperator(r rune) bool {return r == '+' || r == '-' || r == '*' || r == '/'
}// 判斷是否是分隔符
func isDelimiter(r rune) bool {return r == '(' || r == ')' || r == ';'
}func main() {input := "x = 123 + (y * 456);"lexer := NewLexer(input)for {token := lexer.NextToken()fmt.Println(token)if token.Type == EOF {break}}
}

代碼解析

  1. Token類型定義

    • 使用TokenType枚舉定義詞法單元類型
    • Token結構體包含類型和實際值
  2. Lexer結構體

    • 保存輸入字符串和當前位置
    • 提供NextToken()方法逐個生成詞法單元
  3. 核心方法

    • readNumber():讀取整數
    • readIdentifier():讀取標識符
    • skipWhitespace():跳過空白字符
  4. 輔助方法

    • peek():查看當前字符但不移動指針
    • consume():讀取當前字符并移動指針
    • isOperator()isDelimiter():判斷字符類型

示例輸出

對于輸入"x = 123 + (y * 456);",輸出結果為:

Token(IDENTIFIER, 'x')
Token(OPERATOR, '=')
Token(INTEGER, '123')
Token(OPERATOR, '+')
Token(DELIMITER, '(')
Token(IDENTIFIER, 'y')
Token(OPERATOR, '*')
Token(INTEGER, '456')
Token(DELIMITER, ')')
Token(DELIMITER, ';')
Token(EOF, '')

擴展建議

這個基礎詞法分析器可以進一步擴展:

  1. 添加浮點數支持
  2. 支持更多運算符(如==, !=等)
  3. 添加關鍵字識別
  4. 支持字符串字面量
  5. 添加注釋處理
  6. 改進錯誤處理機制

這個實現展示了Go語言簡潔高效的特點,適合作為更復雜詞法分析器的基礎。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/899692.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/899692.shtml
英文地址,請注明出處:http://en.pswp.cn/news/899692.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

強制用戶裸奔,微軟封鎖唯一后門操作

周末剛結束&#xff0c;那個常年將「用戶為中心」掛嘴邊的微軟又雙叒叕開始作妖&#xff01; 不錯&#xff0c;大伙兒今后可能再沒法通過「OOBE\BYPASSNRO」命令繞過微軟強制聯網要求了。 熟悉 Windows 11 操作系統的都知道&#xff0c;除硬件上諸多限制外&#xff1b; 軟件層…

大模型備案:攔截關鍵詞列表與敏感詞庫深度解析

隨著《生成式人工智能服務管理暫行辦法》正式實施&#xff0c;大模型上線備案成為企業合規運營的核心環節。其中&#xff0c;敏感詞庫建設與攔截關鍵詞列表管理直接關系內容安全紅線&#xff0c;今天我們就來詳細解析一下大模型備案的這一部分&#xff0c;希望對想要做備案的朋…

快速上手Linux系統輸入輸出

一、管理系統中的輸入輸出 1.什么是重定向&#xff1f; 將原本要輸出到屏幕上的內容&#xff0c;重新輸入到其他設備中或文件中 重定向類型包括 輸入重定向輸出重定向 2.輸入重定向 指定設備&#xff08;通常是文件或命令的執行結果&#xff09;來代替鍵盤作為新的輸入設…

文小言全新升級!多模型協作與智能語音功能帶來更流暢的AI體驗

文小言全新升級&#xff01;多模型協作與智能語音功能帶來更流暢的AI體驗 在3月31日的百度AI DAY上&#xff0c;文小言正式宣布了一系列令人興奮的品牌煥新與功能升級。此次更新不僅帶來了全新的品牌視覺形象&#xff0c;更讓文小言在智能助手的技術和用戶體驗方面邁上了一個新…

C++基礎算法(插入排序)

1.插入排序 插入排序&#xff08;Insertion Sort&#xff09;介紹&#xff1a; 插入排序是一種簡單直觀的排序算法&#xff0c;它的工作原理類似于我們整理撲克牌的方式。 1.基本思想 插入排序的基本思想是&#xff1a; 1.將數組分為已排序和未排序兩部分 2.每次從未排序部分…

k近鄰算法K-Nearest Neighbors(KNN)

算法核心 KNN算法的核心思想是“近朱者赤&#xff0c;近墨者黑”。對于一個待分類或預測的樣本點&#xff0c;它會查找訓練集中與其距離最近的K個樣本點&#xff08;即“最近鄰”&#xff09;。然后根據這K個最近鄰的標簽信息來對當前樣本進行分類或回歸。 在分類任務中&#…

【Feign】??使用 openFeign 時傳遞 MultipartFile 類型的參數參考

&#x1f4a5;&#x1f4a5;????歡迎閱讀本文章????&#x1f4a5;&#x1f4a5; &#x1f3c6;本篇文章閱讀大約耗時三分鐘。 ??motto&#xff1a;不積跬步、無以千里 &#x1f4cb;&#x1f4cb;&#x1f4cb;本文目錄如下&#xff1a;&#x1f381;&#x1f381;&a…

zk基礎—1.一致性原理和算法二

大綱 1.分布式系統特點 2.分布式系統的理論 3.兩階段提交Two-Phase Commit(2PC) 4.三階段提交Three-Phase Commit(3PC) 5.Paxos島的故事來對應ZooKeeper 6.Paxos算法推導過程 7.Paxos協議的核心思想 8.ZAB算法簡述 6.Paxos算法推導過程 (1)Paxos的概念 (2)問題描述 …

216. 組合總和 III 回溯

目錄 問題描述 解決思路 關鍵點 代碼實現 代碼解析 1. 初始化結果和路徑 2. 深度優先搜索&#xff08;DFS&#xff09; 3. 遍歷候選數字 4. 遞歸與回溯 示例分析 復雜度與優化 回溯算法三部曲 1. 路徑選擇&#xff1a;記錄當前路徑 2. 遞歸探索&#xff1a;進入下…

從AI大模型到MCP中臺:構建下一代智能服務的核心架構

從AI大模型到MCP中臺&#xff1a;構建下一代智能服務的核心架構 引言&#xff1a;AI大模型帶來的服務重構革命 在ChatGPT掀起全球AI熱潮的今天&#xff0c;大模型展現出的驚人能力正在重塑整個軟件服務架構。但鮮為人知的是&#xff0c;真正決定AI服務成敗的不僅是模型本身&a…

美團小程序 mtgsig1.2 拼好飯案例 分析 mtgsig

聲明 本文章中所有內容僅供學習交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包內容、敏感網址、數據接口等均已做脫敏處理&#xff0c;嚴禁用于商業用途和非法用途&#xff0c;否則由此產生的一切后果均與作者無關&#xff01; 逆向分析 美團網頁、小程序、app全是指…

【大模型基礎_毛玉仁】5.5 模型編輯應用

目錄 5.5 模型編輯應用5.5.1 精準模型更新5.5.2 保護被遺忘權5.5.3 提升模型安全 5.5 模型編輯應用 大語言模型面臨更新成本高、隱私保護難、安全風險大等問題。模型編輯技術&#xff1a; 通過細粒度修改預訓練模型&#xff0c;避免從頭訓練&#xff0c;降低更新成本&#xff…

揭秘:父子組件之間的傳遞

基礎知識 組件與組件之間有三大方面的知識點&#xff1a; 子組件通過props defineProps&#xff08;{}&#xff09;接收父組件傳遞到參數和方法&#xff1b;子組件可以通過定義 emit 事件&#xff0c;向父組件發送事件&#xff1b;父組件調用子組件通過defineExpose 導出的方法…

微前端實現方案對比Qiankun VS npm組件

架構層面&#xff1a; 1、Qiankun是典型的微前端架構&#xff0c;側重構建多個獨立前端應用協同工作的架構&#xff0c;主應用負責自用用的加載、卸載和通信&#xff1b;子應用不限制&#xff0c;可以是VUE、React等&#xff1b; 2、Qiankun松耦合&#xff0c;各個自應用獨立…

可編輯160頁PPT | 營銷流程和管理數字化轉型規劃

薦言分享&#xff1a;隨著技術的發展和消費者行為的變化&#xff0c;傳統營銷方式已難以滿足現代企業的需求。企業需要借助數字化手段&#xff0c;對營銷流程進行全面梳理和優化&#xff0c;提升營銷活動的精準度和效率。同時&#xff0c;通過數字化營銷管理&#xff0c;企業可…

Ecovadis認證需要準備哪些材料?

Ecovadis認證&#xff0c;作為全球領先的企業社會責任&#xff08;CSR&#xff09;評估平臺&#xff0c;其準備材料的過程不僅需要詳盡無遺&#xff0c;更要體現出企業在環境、社會、勞工和倫理四大方面的卓越實踐與持續改進的決心。 首先&#xff0c;環境管理方面&#xff0c…

程序化廣告行業(45/89):RTB競價后續流程、結算規則及相關要點解讀

程序化廣告行業&#xff08;45/89&#xff09;&#xff1a;RTB競價后續流程、結算規則及相關要點解讀 大家好&#xff01;一直以來&#xff0c;我都希望能和大家一起在程序化廣告這個領域不斷探索、共同成長&#xff0c;這也是我寫這系列博客的初衷。之前我們了解了程序化廣告…

權重參數矩陣

目錄 1. 權重參數矩陣的定義與作用 2. 權重矩陣的初始化與訓練 3. 權重矩陣的解讀與分析 (1) 可視化權重分布 (2) 統計指標分析 4. 權重矩陣的常見問題與優化 (1) 過擬合與欠擬合 (2) 梯度問題 (3) 權重對稱性問題 5. 實際應用示例 案例1&#xff1a;全連接網絡中的…

文法 2025/3/3

文法的定義 一個文法G是一個四元組&#xff1a;G(,,S,P) &#xff1a;一個非空有限的終極符號集合。它的每個元素稱為終極符號或終極符&#xff0c;一般用小寫字母表示。終極符號是一個語言不可再分的基本符號。 &#xff1a;一個非空有限的非終極符號集合。它的每個元素稱為…

字符串復習

344:反轉字符串 編寫一個函數&#xff0c;其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 s 的形式給出。 不要給另外的數組分配額外的空間&#xff0c;你必須原地修改輸入數組、使用 O(1) 的額外空間解決這一問題。 示例 1&#xff1a; 輸入&#xff1a;s ["…