337p人体粉嫩胞高清图片,97人妻精品一区二区三区在线 ,日本少妇自慰免费完整版,99精品国产福久久久久久,久久精品国产亚洲av热一区,国产aaaaaa一级毛片,国产99久久九九精品无码,久久精品国产亚洲AV成人公司
網易首頁 > 網易號 > 正文 申請入駐

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

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.

相關推薦
熱點推薦
中美會談結束,沉默11天,特朗普宣布,中國將再買2000萬噸大豆

中美會談結束,沉默11天,特朗普宣布,中國將再買2000萬噸大豆

影孖看世界
2026-03-29 14:12:17
昨日送別張雪峰!濟南、濱州等地也有紀念活動

昨日送別張雪峰!濟南、濱州等地也有紀念活動

山東教育
2026-03-29 18:35:28
李昌鈺離世讓人破防:真正的傳奇,是他活了106歲的博士母親

李昌鈺離世讓人破防:真正的傳奇,是他活了106歲的博士母親

閱微札記
2026-03-29 10:14:27
中國石油,最新業績公布!受原油價格拖累,凈利潤5年來首現負增長,仍豪擲458億元分紅

中國石油,最新業績公布!受原油價格拖累,凈利潤5年來首現負增長,仍豪擲458億元分紅

每日經濟新聞
2026-03-29 19:45:04
特斯拉車主:Model 3 + HW4.0 買完 FSD 秒推送!

特斯拉車主:Model 3 + HW4.0 買完 FSD 秒推送!

新浪財經
2026-03-29 13:56:50
末代港督彭定康夫婦,帶3個漂亮女兒回英國,29年過去今過得咋樣

末代港督彭定康夫婦,帶3個漂亮女兒回英國,29年過去今過得咋樣

攬星河的筆記
2026-03-26 00:26:09
澳門乒乓世界杯賽程:3月30日國乒對陣表,CCTV5直播

澳門乒乓世界杯賽程:3月30日國乒對陣表,CCTV5直播

小犙拍客在北漂
2026-03-29 17:51:00
寧波“老人派發傳單,女孩接手后快速暈倒”視頻瘋傳,警方辟謠:傳單檢測無安眠藥、無常規毒品

寧波“老人派發傳單,女孩接手后快速暈倒”視頻瘋傳,警方辟謠:傳單檢測無安眠藥、無常規毒品

環球網資訊
2026-03-29 14:42:28
抱緊美日大腿,停飛中國航班、拒絕中國游客的小國,如今怎樣了?

抱緊美日大腿,停飛中國航班、拒絕中國游客的小國,如今怎樣了?

興史興談
2026-03-28 12:37:56
突發!李榮浩怒斥單依純:從力捧到決裂,單依純到底做錯了什么?

突發!李榮浩怒斥單依純:從力捧到決裂,單依純到底做錯了什么?

影像溫度
2026-03-29 15:42:52
昨天,上海樓市最狂熱最詭異的一天!!

昨天,上海樓市最狂熱最詭異的一天!!

新浪財經
2026-03-29 13:28:22
中國電磁炮專家,竟是美國間諜,出賣大量情報,讓國家損失慘重

中國電磁炮專家,竟是美國間諜,出賣大量情報,讓國家損失慘重

青煙小先生
2026-03-02 17:17:14
拿600萬美元還不閉嘴?卡戴珊與前男友錄像帶恩怨再爆猛料

拿600萬美元還不閉嘴?卡戴珊與前男友錄像帶恩怨再爆猛料

仰臥撐FTUer
2026-03-29 20:18:06
朝鮮導游對中國游客說,中國有幾個方面不如朝鮮,他們說的對嗎?

朝鮮導游對中國游客說,中國有幾個方面不如朝鮮,他們說的對嗎?

番外行
2026-03-29 00:15:03
1986年韓先楚拒絕葬在八寶山,他對陳云說:那里有我不愿見到的人

1986年韓先楚拒絕葬在八寶山,他對陳云說:那里有我不愿見到的人

百年歷史老號
2026-03-25 18:27:41
比賴清德更狂的人出現了,如果她當臺灣地區領導人,我軍該怎么辦

比賴清德更狂的人出現了,如果她當臺灣地區領導人,我軍該怎么辦

聽風喃
2026-03-27 17:40:05
2026,中美最終國運之戰已經開始!我們每個人都已參與其中!

2026,中美最終國運之戰已經開始!我們每個人都已參與其中!

愛吃醋的貓咪
2026-03-27 22:02:25
1976年播報毛主席訃告,播音員念完三遍后突然冒出一句話,全國都慌了

1976年播報毛主席訃告,播音員念完三遍后突然冒出一句話,全國都慌了

文史明鑒
2026-03-25 19:14:13
下一周(3.30)持有這些個股的要小心了!(附個股)

下一周(3.30)持有這些個股的要小心了!(附個股)

股市皆大事
2026-03-29 13:10:53
中國腦梗人數全球第一:肉吃得越少,血管就越通,真的嗎?

中國腦梗人數全球第一:肉吃得越少,血管就越通,真的嗎?

蜉蝣說
2026-03-24 10:00:57
2026-03-29 21:04:49
碼農翻身 incentive-icons
碼農翻身
有趣且硬核的技術文章
242文章數 639關注度
往期回顧 全部

科技要聞

馬斯克承認xAI"建錯了",11位創始人均離職

頭條要聞

中國警告美國:勿將"沖突戰亂"引入亞太地區

頭條要聞

中國警告美國:勿將"沖突戰亂"引入亞太地區

體育要聞

絕殺衛冕冠軍后,他單手指天把勝利獻給父親

娛樂要聞

張凌赫事件持續升級!官方點名怒批

財經要聞

Kimi、Minimax 們的算力荒

汽車要聞

嵐圖泰山X8配置曝光 四激光雷達/華為新一代座艙

態度原創

親子
時尚
教育
本地
軍事航空

親子要聞

寶藍和爸爸叔叔挑戰盲選三種顏色做彩泥,看看誰做的彩泥更漂亮!

伊姐周日熱推:電視劇《冬去春來》;電視劇《你是遲來的歡喜》......

教育要聞

1分鐘學會不規則圖形的面積計算方法!

本地新聞

在濰坊待了三天,沒遇到一個“濰坊人”

軍事要聞

美兩棲攻擊艦載3500名增援到達

無障礙瀏覽 進入關懷版