參考 SICP,強大語言需三機制:
原子——最基本實體;
組合手段——將原子組合成複雜實體;
抽象手段——命名複雜實體,使其如原子般可再組合。
通用語言(Java)機制過於通用,構建領域專用語言(DSL)可消除語義鴻溝,讓代碼「看起來就是為了解決該問題而存在」。
class Result {
private String recognized, remaining;
private boolean succeeded;
// 構造、getter、靜態工廠 succeed/fail/concat
}
interface Parser { Result parse(String target); }
Zero:永遠成功,識別空串,返回目標原樣。Item:目標非空時消費首字符成功,否則失敗。| 組合子 | 功能 | 關鍵代碼片段 |
|---|---|---|
| SAT (Predicate, Parser) | 條件過濾:原解析器成功且滿足謂詞 | if(r.succeeded && pre.satisfy(r.recognized)) return r; |
| OR (Parser, Parser) | 優先嘗試第一個,失敗則試第二個 | return r.succeeded ? r : p2.parse(target); |
| SEQ (Parser, Parser) | 順序組合:首個成功後,以剩餘輸入跑第二個,識別結果拼接 | Result.concat(r1, r2) |
| OneOrMany (int max, Parser) | 重複 1~max 次,首次必須成功 | 遞歸 parse2(pre, count) 累積結果 |
digit = SAT(new IsDigit(), new Item())alpha = SAT(new IsAlpha(), new Item())digit_or_alpha = OR(digit, alpha)alpha_before_digit = SEQ(alpha, digit)one_to_ten_alpha = new OneOrMany(10, alpha)zero_or_many(max, p) = OR(new OneOrMany(max, p), new Zero())Java 介面機制天然提供抽象手段:以變量/方法命名組合結果,直接參與更高層組合。
ABNF 規則:NAME = ALPHA *63(ALPHA / DIGIT / "_")
public static Parser name() {
return new SEQ(alpha(),
zero_or_many(64, digit_or_alpha_or_underline()));
}
代碼與 ABNF 幾乎一一對應,驗證 DSL 表達力。
| 輸入 | 成功 | 識別結果 | 剩餘 |
|---|---|---|---|
| "" | false | "" | "" |
| "_U" | false | "" | "" |
| "U" | true | "U" | "" |
| "U {" | true | "U" | "{" |
| "USER001{" | true | "USER001" | "{" |
| 長合法串(64字符) | true | 前64字符 | 剩餘 |
無可變狀態、組合子即高階函數、領域概念直接映射代碼結構。擴展新組合子(如 ZERO_OR_MORE、SEP_BY)只需遵循同樣模式,極具擴展性。