<cite id="ffb66"></cite><cite id="ffb66"><track id="ffb66"></track></cite>
      <legend id="ffb66"><li id="ffb66"></li></legend>
      色婷婷久,激情色播,久久久无码专区,亚洲中文字幕av,国产成人A片,av无码免费,精品久久国产,99视频精品3
      網易首頁 > 網易號 > 正文 申請入駐

      三天時間,我用這門國產編程語言寫了個編譯器,同事們看后大為震驚!

      0
      分享至

      張大胖很幸運,剛畢業就進入了一家芯片設計公司。

      不過,工作了一個月以后,他就覺得不對勁了。

      原因很簡單,對于公司開發的最新芯片,主流的編程語言還不支持呢!

      無論是驗證特性,還是編寫測試,都不得不用最最古老的形式:匯編!

      張大胖決心改變這種狀況,他找到了梁經理,說出了自己的想法。

      確實是這樣,操作系統、圖形學,編譯器號稱程序員的三大浪漫,但每一項真做起來都讓人頭大。

      張大胖當年的畢業設計,一個極其簡單的編譯器,就讓他做了8個月!

      不過,梁經理似乎發現了一個“秘密武器”。

      MoonBit是一門國產開源編程語言,由 Rescript 作者張宏波(現居深圳)及其團隊打造,它的一些優秀特性,非常適合用來開發編譯器和新編程語言。

      張大胖非常高興,決定利用之前畢業設計的經驗,用MoonBit做個實驗,設計并實現一個新的編程語言!

      為了記錄自己的工作,他用日記的形式記錄下了每天的進展:


      ———日記開始———

      第一天:語言設計與詞法分析

      晚上下班,照常來說,我會打開原神欣賞一下提瓦特的風景。不過今天要用MoonBit實現新編程語言了,游戲就先放到一邊吧。

      1、語言設計

      我打算設計的這門語言叫 TinyMoonBit。它得有常見的數值類型、分支循環、函數、內存管理和 CFFI。語法上,我決定讓它看起來像 MoonBit,但內核更像 C。

      同時,為了體現“現代”語言的優越性,我給它設計了更嚴格的類型系統,比如 1 + 1.0 這種在 C 語言里司空見慣的隱式類型轉換,在 TinyMoonBit 里是不允許的。

      我隨手寫下了一個 TinyMoonBit 的示例程序,用來計算斐波那契數列:

      extern fn print_int(x : Int) -> Unit;
      // 遞歸實現斐波那契數列
      fn fib(n : Int) -> Int {
        if n <= 1 { return n; }
        return fib(n - 1) + fib(n - 2);
      }
      fn main() -> Unit {
        print_int(fib(10));
      }

      2、詞法分析

      第一步是詞法分析。通俗地說,就是把一長串代碼文本,切分成一個個有意義的“單詞”,我們稱之為 Token。比如 let x = 5;,處理后應該變成一個 Token 序列:Keyword("let") -> Identifier("x") -> Operator("=") -> IntLiteral(5) -> Symbol(";")。

      在 MoonBit 里,這件事出奇的簡單。首先,用一個代數數據類型(ADT)來定義所有可能的 Token:

      pub enum Token {
        Bool(Bool)       // 布爾值:true, false
        Int(Int)         // 整數
        Keyword(String)  // 關鍵字:let, if, fn
        Upper(String)    // 類型標識符:Int, Bool
        Lower(String)    // 變量標識符:x, n
        Symbol(String)   // 運算符:+, -, ->
        Bracket(Char)    // 括號:(, ), {, }
        EOF              // 文件結束標記
      } derive(Show, Eq)

      然后,借助 MoonBit 的一大殺器——字符串模式匹配,我們可以像寫詩一樣實現詞法分析器:

      pub fn lex(code: String) -> Array[Token] {
        let tokens = Array::new()
        loop code[:] {
          // 跳過空白字符
          [' ' | '\n' | '\r' | '\t', ..rest] => 
            continue rest
          // 處理單行注釋
          [.."http://", ..rest] =>
            continue loop rest {
              ['\n', ..rest] => break rest
              [_, ..rest] => continue rest
              [] => break ""
            }
          
          // 識別多字符運算符 (順序很重要!)
          [.."->", ..rest] => { tokens.push(Symbol("->")); continue rest }
          [.."<=", ..rest] => { tokens.push(Symbol("<=")); continue rest }
          
          // 識別單字符運算符
          [':' |1 ';' | '+' | '-' | '*' | '/' | '<' | '=' as c, ..rest] => {
            tokens.push(Symbol("\{c}")); continue rest
          }
          
          // 識別標識符和關鍵字
          ['a'..='z', ..] as code => {
            let (tok, rest) = lex_ident(code) // lex_ident 會區分關鍵字和普通變量
            tokens.push(tok)
            continue rest
          }
          // ... 其他匹配,如數字、大寫字母開頭的類型等 ...
          
          [] => { tokens.push(EOF); break tokens }
        }
      }

      MoonBit 特性解讀

      函數式循環 (loop):它不是簡單的重復,而是一個不斷用新狀態(rest)迭代自身的遞歸過程,非常適合處理“吃掉”一部分字符串,然后處理剩余部分的場景。

      強大的模式匹配:[.."->", ..rest] 這樣的語法,可以直觀地匹配字符串前綴,比晦澀的正則表達式清晰百倍。

      只花了大半個小時,詞法分析器就寫完并測試通過了。我暗自竊喜,這要是用別的語言,光是處理各種邊界情況就得焦頭爛額。MoonBit 的 ADT 和模式匹配,寫起來真是既優雅又高效。

      第二天:語法分析與類型檢查

      有了昨晚的順利開局,我信心更足了。我趁著午休和晚上的時間,開始攻克編譯器的第二個難關。

      1、語法分析:從扁平到立體

      詞法分析產生的是一維的 Token 序列,而程序本身是有層次結構的。語法分析的任務,就是將這個扁平的序列,組織成一棵能夠反映代碼結構的抽象語法樹(AST)。

      首先,我定義了 AST 的節點。這就像為程序搭建骨架,每一塊骨骼都代表一種語法結構:

      // 表達式的定義
      pub enum Expr {
        AtomExpr(AtomExpr, mut ty~ : Type?)          // 原子表達式 (變量、字面量等)
        Unary(String, Expr, mut ty~ : Type?)         // 一元運算:-, !
        Binary(String, Expr, Expr, mut ty~ : Type?)  // 二元運算:+, -, *, /
      } derive(Show, Eq, ToJson)
      // 語句的定義
      pub enum Stmt {
        Let(String, Type, Expr)      // 變量聲明:let x : Int = 5;
        If(Expr, Array[Stmt], Array[Stmt])           // 條件分支
        While(Expr, Array[Stmt])     // 循環
        Return(Expr?)                // 返回
        // ... 其他語句 ...
      } derive(Show, Eq, ToJson)
      // 函數和程序的頂層結構
      pub struct Function {
        name : String
        params : Array[(String, Type)]
        ret_ty : Type
        body : Array[Stmt]
      } derive(Show, Eq, ToJson)
      pub type Program Map[String, Function]

      設計巧思:注意到每個表達式節點都有一個 mut ty~ : Type? 字段嗎?這是一個可變的可選類型字段。這樣,我就可以在后續的類型檢查階段,直接把推斷出的類型信息“填”進去,而無需重新構建一棵新樹,非常巧妙。

      有了骨架,接下來就是用 遞歸下降 的方法來填充它。簡單來說,就是為每一種語法結構(如函數、語句、表達式)編寫一個解析函數。在 MoonBit 中,這又成了模式匹配的絕佳舞臺:

      pub fn parse_function(tokens : ArrayView[Token]) -> (Function, ArrayView[Token]) raise {
        // Function一定由fn關鍵字,Lower,左括號開頭
        guard tokens is [Keyword("fn"), Lower(fname), Bracket('('), .. rest_tokens]
        let params : Array[(String, Type)] = Array::new()
        let (tokens, ret_ty) = loop rest_tokens {
          // 參數格式:param_name : Type
          [Lower(param_name), Symbol(":"), Upper(type_name), .. rest] => {
            params.push((param_name, parse_type(type_name)))
            continue rest
          }
          [Symbol(","), .. rest] => continue rest
          [Bracket(')'), Symbol("->"), Upper(ret_ty), .. rest] => 
            break (rest, parse_type(ret_ty))
        }
        // ... 解析函數體
      }

      整個過程就像剝洋蔥,parse_program 調用 parse_function,parse_function 調用 parse_stmt,parse_stmt 調用 parse_expr,層層遞進,直到把所有 Token 都消耗完畢。

      MoonBit 高級特性應用

      derive(Show, Eq, ToJson):這個小小的注解威力巨大。Show 讓我能輕松打印 AST 用于調試,Eq 用于測試,而 ToJson 能一鍵將 AST 序列化為 JSON,方便檢查其結構。

      raise 錯誤處理:通過在函數簽名中標記 raise,我可以優雅地向上拋出解析錯誤,而不用到處傳遞錯誤碼。

      2、類型檢查:確保語義正確

      在代碼生成之前,通常需要實現一個類型檢查階段。一些語句雖然符合語法,但可能不符合語義,例如我有一個foo函數,然后又有了1+foo這樣的代碼,但這是語義不正確的,因為一個整數無法與一個函數相加。

      我設計了一個環境鏈來處理作用域:

      pub struct TypeEnv[K, V] {
        parent : TypeEnv[K, V]?
        data : Map[K, V]
      }
      pub fn TypeEnv::get[K : Eq + Hash, V](self : Self[K, V], key : K) -> V? {
        match self.data.get(key) {
          Some(value) => Some(value)
          None => match self.parent {
            Some(parent_env) => parent_env.get(key)
            None => None
          }
        }
      }

      類型檢查器會自頂向下掃描每個表達式,填充類型信息并驗證類型一致性:

      pub fn Expr::check_type(self : Self, env : TypeEnv[String, Type]) -> Type raise {
        match self {
          Binary("+", lhs, rhs, ..) as node => {
            let lhs_type = lhs.check_type(env)
            let rhs_type = rhs.check_type(env)
            guard lhs_type == rhs_type else {
              raise TypeCheckError("類型不匹配")
            }
            node.ty = Some(lhs_type)
            lhs_type
          }
          // ... 其他表達式類型
        }
      }

      語法分析消耗的時間比預想的多一些,尤其是在處理運算符優先級時頗費腦筋。但在 MoonBit 強大的模式匹配和 AI 助手的幫助下,我還是在深夜前完成了這項工作。萬里長征,只剩下最后一步——代碼生成了。

      第三天:代碼生成,最后的難關

      MoonBit本身語言特性適合編譯器實現之外,也有一個超級好用的LLVM綁定,叫做llvm.mbt。

      1、LLVM:現代編譯器的基石

      LLVM作為現代編譯器基礎設施的集大成者,為我們提供了一個強大而靈活的解決方案。通過將程序轉換為LLVM中間表示(IR),我們可以利用LLVM成熟的工具鏈將代碼編譯到多種目標架構。

      我們先來試用LLVM的經典起手三段式:

      pub fn initialize_llvm() -> (Context, Module, Builder) {
        let ctx = @IR.Context::new()          // LLVM上下文
        let mod = ctx.addModule("demo")       // 模塊容器
        let builder = ctx.createBuilder()     // IR構建器
        (context, module, builder)
      }

      有了這三樣法寶,我們就能像搭積木一樣,一條條地構建出程序的 LLVM IR。比如生成一個計算 a*b+c 的函數:

      pub fn generate_muladd_function() -> String {
        let (ctx, mod, builder) = initialize_llvm()
        // 定義函數簽名:i32 muladd(i32, i32, i32)
        let i32_ty = ctx.getInt32Ty()
        let func_type = ctx.getFunctionType(i32_ty, [i32_ty, i32_ty, i32_ty])
        let func_value = mod.addFunction(func_type, "muladd")
        // 創建函數入口基本塊
        let entry_block = func_value.addBasicBlock(name="entry")
        builder.setInsertPoint(entry_block)
        // 獲取函數參數并生成計算指令
        let arg_a = func_value.getArg(0).unwrap()
        let arg_b = func_value.getArg(1).unwrap()
        let arg_c = func_value.getArg(2).unwrap()
        let mul_result = builder.createMul(arg_a, arg_b)
        let add_result = builder.createAdd(mul_result, arg_c)
        let _ = builder.createRet(add_result)
        mod.dump()
      }

      這會生成非常清晰的 LLVM IR:

      define i32 @muladd(i32 %a, i32 %b, i32 %c) {
      entry:
        %mul_res = mul i32 %a, %b
        %add_res = add i32 %mul_res, %c
        ret i32 %add_res
      }

      看起來很簡單,對吧?但真正的挑戰在于,如何將我們前一天生成的、復雜的 AST,系統性地翻譯成這一條條的 LLVM 指令。

      2、類型系統的映射

      在LLVM中,類型系統相當復雜。llvm.mbt使用Trait Object的概念,&Type可以表示任意LLVM類型。我需要建立TinyMoonBit類型與LLVM類型的映射:

      pub fn CodeGen::convert_type(self : Self, parser_type : Type) -> &@llvm.Type {
        match parser_type {
          Type::Unit => self.ctx.getVoidTy() as &@llvm.Type
          Type::Bool => self.ctx.getInt1Ty()
          Type::Int => self.ctx.getInt32Ty()  
          Type::Double => self.ctx.getDoubleTy()
        }
      }

      然后就是真正的代碼生成。我需要為 AST 中的每一種節點編寫一個 emit 方法。其中最有趣也最關鍵的,是如何處理變量和分支:

      3、變量處理:SSA與可變性的橋梁

      TinyMoonBit支持變量的重新賦值,但LLVM IR采用SSA(Static Single Assignment)形式,每個變量只能賦值一次。我需要采用alloca + load/store模式來處理可變變量:

      // 變量聲明:let x : Int = 5;
      Let(var_name, var_type, init_expr) => {
        let llvm_type = self.convert_type(var_type)
        let alloca = self.builder.createAlloca(llvm_type, name=var_name)
        env.symbols.set(var_name, alloca)
        let init_value = init_expr.emit(env)
        let _ = self.builder.createStore(alloca, init_value)
      }
      // 變量賦值:x = 10;
      Assign(var_name, rhs_expr) => {
        let var_ptr = env.get_symbol(var_name).unwrap()
        let rhs_value = rhs_expr.emit(env)
        let _ = self.builder.createStore(var_ptr, rhs_value)
      }

      4、控制流:基本塊的藝術

      控制流是程序邏輯的骨架。在LLVM IR中,控制流通過基本塊和分支指令來實現。每個基本塊代表一個沒有內部跳轉的指令序列:

      // if-else語句的實現
      If(cond, then_stmts, else_stmts) => {
        let cond_val = cond.emit(env)
        // 創建三個基本塊
        let then_block = func.addBasicBlock(name="if.then")
        let else_block = func.addBasicBlock(name="if.else") 
        let merge_block = func.addBasicBlock(name="if.end")
        // 條件分支
        let _ = builder.createCondBr(cond_val, then_block, else_block)
        // 生成then分支
        builder.setInsertPoint(then_block)
        then_stmts.each(s => s.emit(env))
        let _ = builder.createBr(merge_block)
        // 生成else分支  
        builder.setInsertPoint(else_block)
        else_stmts.each(s => s.emit(env))
        let _ = builder.createBr(merge_block)
        // 繼續在merge塊生成后續代碼
        builder.setInsertPoint(merge_block)
      }

      5、完整的代碼生成

      經過詞法分析、語法分析、類型檢查和代碼生成四個階段,我們的編譯器已經能夠將TinyMoonBit源代碼轉換為完整的LLVM IR。

      對于我們的斐波那契例子:

      fn fib(n : Int) -> Int {
        if n <= 1 { return n; }
        return fib(n - 1) + fib(n - 2);
      }

      最終生成的LLVM IR:

      define i32 @fib(i32 %0) {
      entry:
        %1 = alloca i32, align 4
        store i32 %0, ptr %1, align 4
        %2 = load i32, ptr %1, align 4
        %3 = icmp sle i32 %2, 1
        br i1 %3, label %4, label %6
      4:                                                
        %5 = load i32, ptr %1, align 4
        ret i32 %5
      6:                                                
        %7 = load i32, ptr %1, align 4
        %8 = sub i32 %7, 1
        %9 = call i32 @fib(i32 %8)
        %10 = load i32, ptr %1, align 4
        %11 = sub i32 %10, 2
        %12 = call i32 @fib(i32 %11)
        %13 = add i32 %9, %12
        ret i32 %13
      }

      使用LLC工具鏈,我們可以進一步將LLVM IR編譯成RISC-V匯編代碼,完成整個編譯過程。

      攻克了這些核心難點后,剩下的工作就是水到渠成了。周三深夜,當我看到 fib(10) 的代碼成功生成了復雜的 LLVM IR ,并順利通過llc鏈接編譯成可執行程序并且運行通過時,我知道,我成功了!


      ———日記結束———

      總結

      周四的午休時刻,張大胖向同事們展示了自己三天時間編寫的TinyMoonBit編譯器。

      確實,MoonBit的模式匹配讓詞法分析變得異常簡單,遞歸下降語法分析也很直觀。

      最關鍵的是llvm.mbt這個綁定庫,讓代碼生成變得容易很多。

      有了MoonBit以后,開發新語言就不那么難了,也許你也可以再把編譯原理撿起來,用MoonBit完成自己的年輕時的夢想:實現一個自己的編程語言!

      資源推薦

      對這個項目感興趣的讀者,可以從以下鏈接獲取更多信息:

      TinyMoonBit 完整項目 :


      https://github.com/Kaida-Amethyst/TinyMoonbitLLVM

      MoonBit 官方文檔 :


      https://www.moonbitlang.com/docs/

      llvm.mbt 文檔 :


      https://mooncakes.io/docs/Kaida-Amethyst/llvm

      LLVM 官方教程 :


      https://www.llvm.org/docs/tutorial/

      特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。

      Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

      相關推薦
      熱點推薦
      大冷門!3號種子王欣瑜一輪游:連丟2局不敵世界第120 馬年第一敗

      大冷門!3號種子王欣瑜一輪游:連丟2局不敵世界第120 馬年第一敗

      風過鄉
      2026-02-24 12:52:12
      朱珠去北京干休所給100歲奶奶拜年,重孫女和太奶同框很溫馨

      朱珠去北京干休所給100歲奶奶拜年,重孫女和太奶同框很溫馨

      何偵愛體育
      2026-02-24 08:15:13
      中國男籃決戰日本隊,最強首發五人揭曉,要打爆小日子20分

      中國男籃決戰日本隊,最強首發五人揭曉,要打爆小日子20分

      宗介說體育
      2026-02-24 09:59:41
      全球頭號毒販被斃,墨西哥的天正在晴,也基本順特朗普了

      全球頭號毒販被斃,墨西哥的天正在晴,也基本順特朗普了

      邵旭峰域
      2026-02-24 10:49:17
      吳石夫人王碧奎晚年自述,寧在臺流浪不返大陸,居美國訴心底真意

      吳石夫人王碧奎晚年自述,寧在臺流浪不返大陸,居美國訴心底真意

      嘮叨說歷史
      2026-02-02 18:45:08
      江蘇一家人均1萬6去貝加爾湖看藍冰,為省200塊全家遇難

      江蘇一家人均1萬6去貝加爾湖看藍冰,為省200塊全家遇難

      王曉愛體彩
      2026-02-24 06:13:59
      214億就封王!明天,廣州或將誕生新地王!

      214億就封王!明天,廣州或將誕生新地王!

      房二娃
      2026-02-24 19:08:09
      冬奧會各國獎勵:中國運動員獲贈一臺車 美國寒酸 3國一分錢不給

      冬奧會各國獎勵:中國運動員獲贈一臺車 美國寒酸 3國一分錢不給

      侃球熊弟
      2026-02-24 02:15:03
      一覺醒來,伊朗有了大動作

      一覺醒來,伊朗有了大動作

      虛聲
      2026-02-23 20:01:14
      楊揚:一兩塊金牌,成了中國短道速滑深層矛盾的“遮羞布”

      楊揚:一兩塊金牌,成了中國短道速滑深層矛盾的“遮羞布”

      上觀新聞
      2026-02-24 19:39:05
      江蘇一家去貝加爾湖旅游:一萬六都花了,卻為省200全家遇難

      江蘇一家去貝加爾湖旅游:一萬六都花了,卻為省200全家遇難

      觀察鑒娛
      2026-02-24 09:48:21
      不服就干!荷蘭打響反華第一槍,通告全球,斷的就是中方退路

      不服就干!荷蘭打響反華第一槍,通告全球,斷的就是中方退路

      流史歲月
      2026-02-24 11:21:40
      繼張本智和反華拜鬼,被官媒點名后,石川佳純也走上了他的老路

      繼張本智和反華拜鬼,被官媒點名后,石川佳純也走上了他的老路

      姩姩有娛
      2026-02-23 18:09:28
      中國中免A股今日跌停,港股三日累計下跌23%,市場在擔憂什么?

      中國中免A股今日跌停,港股三日累計下跌23%,市場在擔憂什么?

      讀創
      2026-02-24 15:35:06
      韓國要求俄使館撤下宣傳條幅,俄方:此為全體俄羅斯人所熟知,無意冒犯任何人

      韓國要求俄使館撤下宣傳條幅,俄方:此為全體俄羅斯人所熟知,無意冒犯任何人

      文匯報
      2026-02-24 04:20:05
      突發,杜蘭特加盟,再次聯手庫里!

      突發,杜蘭特加盟,再次聯手庫里!

      體育新角度
      2026-02-24 09:40:21
      你干過哪些陰暗齷齪的事?網友:最后一個真的好炸裂好真實

      你干過哪些陰暗齷齪的事?網友:最后一個真的好炸裂好真實

      帶你感受人間冷暖
      2026-02-17 01:00:24
      四年戰爭透支百年國運:俄烏戰爭四周年祭

      四年戰爭透支百年國運:俄烏戰爭四周年祭

      望岳
      2026-02-24 19:41:38
      終結隊史最長16連??!國王拒灰熊四殺 威少25+7引7人上雙

      終結隊史最長16連??!國王拒灰熊四殺 威少25+7引7人上雙

      醉臥浮生
      2026-02-24 11:24:35
      攤手也能傳染?NBA名嘴:東契奇的雕像應該是他抱怨裁判的樣子

      攤手也能傳染?NBA名嘴:東契奇的雕像應該是他抱怨裁判的樣子

      愛體育
      2026-02-25 00:01:58
      2026-02-25 00:24:49
      碼農翻身 incentive-icons
      碼農翻身
      有趣且硬核的技術文章
      228文章數 627關注度
      往期回顧 全部

      科技要聞

      宇樹科技發布四足機器人Unitree As2

      頭條要聞

      男子摟住繼女強吻動作親密 當地婦聯介入

      頭條要聞

      男子摟住繼女強吻動作親密 當地婦聯介入

      體育要聞

      蘇翊鳴總結米蘭征程:我仍是那個熱愛單板滑雪的少年

      娛樂要聞

      汪小菲官宣三胎出生:承諾會照顧好3個孩子

      財經要聞

      縣城消費「限時繁榮」了十天

      汽車要聞

      入門即滿配 威蘭達AIR版上市 13.78萬元起

      態度原創

      藝術
      旅游
      健康
      公開課
      軍事航空

      藝術要聞

      2025年第八屆全國青年美展 | 油畫作品選刊

      旅游要聞

      西藏山南庫拉崗日雪山,女生徒步遇大雪:躲進牛棚用衛生巾取暖

      轉頭就暈的耳石癥,能開車上班嗎?

      公開課

      李玫瑾:為什么性格比能力更重要?

      軍事要聞

      美軍參聯會主席警告:對伊朗動武可能帶來重大風險

      無障礙瀏覽 進入關懷版