首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

使用 ANTLR 實現命令列參數解析器 (詞法/語法/AST/Walker 完整流程)

     編輯:關於.NET

命令列參數解析無統一標準,Unix 風格 (`-v`, `--verbose`) 與 Windows 風格 (`/v`, `-v`) 並存。.NET `Main(string[] args)` 僅按空格分割,無法處理「選項帶參數、參數含特殊字符、短選項合併」等複雜場景。本文展示使用 **ANTLR 3** 從詞法/語法定義到 AST 構建、Tree Walker 遍歷、實體類映射的完整工程化流程。

自定義參數規範

  1. 空格分割參數 (兼容 .NET 默認)。
  2. 選項/開關前綴:`/`、`\`、`-`。
  3. 選項參數用 `:` 分隔,多值用 `,` 或 `;` 分隔。
  4. 選項後為命令行位置參數。
  5. 選項名以字母開頭。
  6. 參數值支援字母數字下劃線,含雙位元組/特殊字符需雙引號包裹。

ANTLR 開發步驟

1. 詞法/語法定義 (CmdPara.g)

grammar CmdPara;
options { language=CSharp2; output=AST; ASTLabelType=CommonTree; }
tokens { Option; }

cmdLine : String option* para* EOF!;
option  : swt ((':' para)((','|';')para)*)? -> ^(Option swt para*);
swt     : ('/'|'-'|'\\') ID -> ID;
para    : String | INT | ID;
ID      : ('a'..'z'|'A'..'Z')(('a'..'z')|('A'..'Z')|'0'..'9'|'&'|'/'|'\\'|'.'|'_')*; // 不支援中文
String  : ('"' .+ '"') | ('\'' .+ '\'');
INT     : ('1'..'9')('0'..'9')*;
WS      : (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN;};
  • `-> ^(Option swt para*)`:AST 重寫規則,構建 `Option` 為根、選項名與參數為子節點的樹。
  • `WS` 隱藏通道:空白字符不進入 Token 流,僅作分隔。

2. 實體類定義 (Rz.CmdLine)

public class CmdLine {
    public string ExeFile { get; set; }
    public List<Switch> CmdSwitches { get; } = new List<Switch>();
    public List<string> Parameters { get; } = new List<string>();
}
public class Switch {
    public string SwitchName { get; set; }
    public List<string> Parameters { get; } = new List<string>();
}

3. Tree Grammar Walker (CmdWalker.g)

tree grammar CmdWalker;
options { language=CSharp2; tokenVocab=CmdPara; ASTLabelType=CommonTree; }
@header { using Rz.CmdLine; }

cmdLine returns [CmdLine r = new CmdLine()]
: String { r.ExeFile = $String.Text; }
  (option { r.CmdSwitches.Add($option.r); })*
  (para { r.Parameters.Add($para.r); })*
;

option returns [Switch r = new Switch()]
: ^(Option swt { r.SwitchName = $swt.r; } (para { r.Parameters.Add($para.r); })*)
;

swt returns [string r] : ID { r = $ID.Text; };
para returns [string r] : String { r = $String.Text; } | INT { r = $INT.Text; } | ID { r = $ID.Text; };
  • `returns [Type r = new Type()]`:規則返回值自動實例化,屬性賦值直接構建物件圖。
  • `$rule.r`:引用子規則返回值。

4. 解析引擎封裝 (CmdEngine)

public class CmdEngine {
    public static CmdLine Parse(string cmdLine) {
        var input = new ANTLRStringStream(cmdLine);
        var lexer = new CmdParaLexer(input);
        var tokens = new CommonTokenStream(lexer);
        var parser = new CmdParaParser(tokens);
        var tree = new CommonTreeNodeStream(parser.cmdLine().Tree);
        var walker = new CmdWalker(tree);
        return walker.cmdLine();
    }
}

5. 測試驗證

static void Main(string[] args) {
    Console.WriteLine(string.Join(" ", args)); // .NET 默認分割
    var cmd = CmdEngine.Parse(Environment.CommandLine); // 完整命令行字符串
    Console.WriteLine(cmd.ExeFile);
    foreach (var s in cmd.CmdSwitches)
        Console.WriteLine($"Switch /{s.SwitchName}: {string.Join(",", s.Parameters)}");
    foreach (var p in cmd.Parameters)
        Console.WriteLine($"Command Parameter [{cmd.Parameters.IndexOf(p)}]: {p}");
    Console.ReadLine();
}

運行結果示例

解析結果截圖

輸入:`MyApp.exe /option1:value1,value2 /option2 "param with space" positional1 positional2`
正確解析出:ExeFile、兩個 Switch (含多參數)、兩個位置參數 (含空格參數)。

對比現有開源庫

庫風格特點
CodePlex 參數解析Unix/GNU短選項合併 (`-xyzw`)、長選項 (`--long=val`)、支援 `-sHello` / `-s Hello`
Mono.GetOptionsUnix`getopts` 風格,參考
本文 ANTLR 方案自定義語法完全可控、AST 結構化、擴展性極強、適合已用 ANTLR 專案

適用性評估

  • ✅ **已使用 ANTLR 做 DSL/語言處理**的專案:零學習成本,複用現有工具鏈。
  • ✅ **極其複雜/非標準參數規範** (如嵌套選項、條件參數)。
  • ❌ **一般專案**:引入 ANTLR 運行時 (數百 KB) + 生成代碼過重,推薦 CommandLineParser、CliFx、System.CommandLine 等輕量庫。

ANTLR 賦予「語法即配置」的極致靈活性 — 當參數規範超越常規庫能力邊界時,它是終極武器。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved