<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次建國,每次沒超79年,而2026年恰好是以色列建國78年

      猶太人曾3次建國,每次沒超79年,而2026年恰好是以色列建國78年

      百年歷史老號
      2026-03-20 08:17:22
      衛冕冠軍被準絕殺!托馬斯反絕殺未出手跪地捂臉 NCAA又奇跡一戰

      衛冕冠軍被準絕殺!托馬斯反絕殺未出手跪地捂臉 NCAA又奇跡一戰

      顏小白的籃球夢
      2026-03-23 10:10:13
      澳門這個酒窖,不知價值多少

      澳門這個酒窖,不知價值多少

      地主陸
      2026-03-22 14:54:11
      女子被觸摸隱私部位后續!衣服全被脫光,商家只開除,拒絕賠償

      女子被觸摸隱私部位后續!衣服全被脫光,商家只開除,拒絕賠償

      潮鹿逐夢
      2026-03-18 10:26:41
      孩子都是奔著媽媽來的!網友:我媳婦夢到的是大白蛇鉆到她肚子里

      孩子都是奔著媽媽來的!網友:我媳婦夢到的是大白蛇鉆到她肚子里

      帶你感受人間冷暖
      2026-03-14 00:10:08
      江蘇省醫療資源最好的五個城市,是否和您想的一樣?

      江蘇省醫療資源最好的五個城市,是否和您想的一樣?

      健身狂人
      2026-03-23 09:44:59
      迪麗熱巴經紀人換血后首秀,機場造型美到封神,新團隊審美在線!

      迪麗熱巴經紀人換血后首秀,機場造型美到封神,新團隊審美在線!

      喜歡歷史的阿繁
      2026-03-21 17:08:15
      日本汽車為什么一敗涂地!日本專家悲憤:我們已經輸了

      日本汽車為什么一敗涂地!日本專家悲憤:我們已經輸了

      沙雕小琳琳
      2026-03-22 13:48:44
      國際金價盤中跌破每盎司4200美元

      國際金價盤中跌破每盎司4200美元

      國際在線
      2026-03-23 16:05:10
      周杰倫一手好牌打得稀爛,《太陽之子》被宣發拖成啞火局

      周杰倫一手好牌打得稀爛,《太陽之子》被宣發拖成啞火局

      光影新天地
      2026-03-22 19:14:42
      聯賽杯曼城2比0阿森納奪冠,4分鐘擊潰榜首隊,主帥博弈瓜帥完勝

      聯賽杯曼城2比0阿森納奪冠,4分鐘擊潰榜首隊,主帥博弈瓜帥完勝

      云兒評球
      2026-03-23 16:21:05
      全國人大代表建議: 公務員退休年齡延長至70歲

      全國人大代表建議: 公務員退休年齡延長至70歲

      互聯網大觀
      2026-03-19 18:51:34
      外交部:如果中東戰事持續擴大,整個地區將陷入不可收拾的局面

      外交部:如果中東戰事持續擴大,整個地區將陷入不可收拾的局面

      新京報
      2026-03-23 15:48:12
      炸穿阿瓦士!美以聯手端掉伊朗革命衛隊總部,現場夷成白地

      炸穿阿瓦士!美以聯手端掉伊朗革命衛隊總部,現場夷成白地

      老馬拉車莫少裝
      2026-03-23 13:00:44
      騰訊提醒:國行Switch即將停止運營 別忘了領4款游戲

      騰訊提醒:國行Switch即將停止運營 別忘了領4款游戲

      快科技
      2026-03-23 16:26:39
      快扔掉!戴一天,輻射量相當于拍117次胸片

      快扔掉!戴一天,輻射量相當于拍117次胸片

      FM93浙江交通之聲
      2025-10-28 00:01:43
      香菇再次被關注!醫生發現:癌癥患者吃香菇,不用多久或有5改善

      香菇再次被關注!醫生發現:癌癥患者吃香菇,不用多久或有5改善

      讀懂世界歷史
      2026-02-12 21:48:53
      土耳其伊斯坦布爾發生爆炸致建筑坍塌,有人員失聯

      土耳其伊斯坦布爾發生爆炸致建筑坍塌,有人員失聯

      界面新聞
      2026-03-22 19:23:00
      徹底繞開光刻機!中國6G突然官宣重大突破,西方封鎖徹底失效

      徹底繞開光刻機!中國6G突然官宣重大突破,西方封鎖徹底失效

      混沌錄
      2026-03-23 16:08:15
      吳清緊急召集五大巨頭座談,“十五五”資本市場的底牌正在揭開

      吳清緊急召集五大巨頭座談,“十五五”資本市場的底牌正在揭開

      圓維度
      2026-03-23 14:17:11
      2026-03-23 17:04:49
      碼農翻身 incentive-icons
      碼農翻身
      有趣且硬核的技術文章
      239文章數 635關注度
      往期回顧 全部

      科技要聞

      裁掉2萬多名員工后,扎克伯格對自己下手了

      頭條要聞

      特朗普向伊朗發出48小時"最后通牒" 中方表態

      頭條要聞

      特朗普向伊朗發出48小時"最后通牒" 中方表態

      體育要聞

      不敢放手一搏,你拿什么去爭冠?

      娛樂要聞

      劉燁47歲生日,安娜曬全家福為其慶生

      財經要聞

      滬指險守3800點 全市場超百股跌停

      汽車要聞

      "拒絕"豪車稅 新款Panamera盡享版99.8萬元起精準入局

      態度原創

      親子
      本地
      教育
      數碼
      公開課

      親子要聞

      有娃之后才開竅,分享一波寶媽的“生活智慧”,省時又省力

      本地新聞

      這里是寶雞 嫽滴很!

      教育要聞

      《暑期實習怎么找?3個方法提高成功率》

      數碼要聞

      哈趣Ace1耳夾式耳機:百元預算撬動全能體驗

      公開課

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

      無障礙瀏覽 進入關懷版