命令列參數解析無統一標準,Unix 風格 (`-v`, `--verbose`) 與 Windows 風格 (`/v`, `-v`) 並存。.NET `Main(string[] args)` 僅按空格分割,無法處理「選項帶參數、參數含特殊字符、短選項合併」等複雜場景。本文展示使用 **ANTLR 3** 從詞法/語法定義到 AST 構建、Tree Walker 遍歷、實體類映射的完整工程化流程。
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;};
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>();
}
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; };
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();
}
}
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.GetOptions | Unix | `getopts` 風格,參考 |
| 本文 ANTLR 方案 | 自定義 | 語法完全可控、AST 結構化、擴展性極強、適合已用 ANTLR 專案 |
CommandLineParser、CliFx、System.CommandLine 等輕量庫。ANTLR 賦予「語法即配置」的極致靈活性 — 當參數規範超越常規庫能力邊界時,它是終極武器。