如果时间不是问题,理解常常来自“自己动手”的方法。在这里,如果问题是“预处理器在做什么?”,为什么不写一个简化的玩具预处理器并研究结果。我忘了名字,但有一个“法律”,说:“你只有真正明白,如果你能实现它”。
当然,编写一个完整的C/C++预处理器并不是很有吸引力。因此,下面的代码试图获得一个足够大,但极简主义的简化模型实现,它只是能够展示基本上正在发生的事情。一种紧凑的语言已被选中,以保持行数低。如果您使用抽象语法树级别而不是使用语法,则不需要解析器。
type MacroExpr =
| MacroDef of string * string list * MacroExpr
| MacroCall of string * (string * MacroExpr) list
| String of string
| Stringize of MacroExpr
| ArgRef of string
let rec run (macros : MacroExpr list) (call : MacroExpr) : MacroExpr =
let value_of arg_name =
match call with
| MacroCall(n,args) -> snd (List.find (fun a -> fst a = arg_name) args)
| _ -> failwith "bla bla"
match call with
| MacroCall(n,al) ->
let m = macros |> List.find (fun m -> match m with | MacroDef(n1,_,_) -> n1 = n | _ -> false)
let mname,margs,mbody =
match m with
| MacroDef(a,b,c) -> a,b,c
| _ -> failwithf "macros argument contains something else: %A" m
let rec x_to_string = function
| String s -> s
| Stringize x -> x_to_string x
| ArgRef(an) -> x_to_string (eval (snd(al |> List.find (fun (n,_) -> n = an))))
| MacroDef(_,_,_) -> failwith "No nested macros supported."
| MacroCall(n,_) -> n
and eval = function
| String s -> String s
| Stringize x -> String (x_to_string x)
| ArgRef(an) -> String (x_to_string (snd(al |> List.find (fun (n,_) -> n = an))))
| MacroDef(_,_,_) -> failwith "No nested macros supported."
| MacroCall(n,al1) ->
run macros (MacroCall(n, al1 |> List.map (fun (an,ax) -> an,eval ax)))
match mbody with
| ArgRef(an) -> snd(al |> List.find (fun (n,_) -> n = an))
| Stringize(x) -> String(x_to_string x)
| String s -> String s
| MacroCall(mn,al1) ->
run macros (MacroCall(mn, al1 |> List.map (fun (an,ax) -> an,eval ax)))
| MacroDef(_,_,_) -> failwithf "No nested macros supported. Found one in %s: %A" n mbody
| _ -> failwithf "'call' argument shall be a MacroCall expression but is a %A" call
let macros =
[
MacroDef ("W", [], String "124")
MacroDef ("str1", ["a"], Stringize (ArgRef "a"))
MacroDef ("str2", ["a"], MacroCall("str1", ["a",ArgRef "a"]))
]
let calls =
[
MacroCall("W",[])
MacroCall("str1",["a",MacroCall("W",[])])
MacroCall("str2",["a",MacroCall("W",[])])
]
calls
|> List.map (fun c -> run macros c)
macros
变量包含我们想要探索的预定义宏的列表。它们被定义为(最小,低科技)类型。
calls
变量包含一个宏调用列表,使用相同的技术。
即使我们忽略了功能run
的目前的执行情况,我们可以看到一些有趣的事情:
为“str2的”宏定义转发其参数“a”到宏调用STR1。 ArgRef - 对“str2”参数列表的引用。遵循急切评估原则的语言不会将该ArgRef传递给下一个宏评估,而是尝试首先解决它。毕竟,对“str1”的评估必须知道调用者“str2”的arglist。
因此,从str2调用str1的参数是["a",MacroCall("W",[])]
,而首先通过评估宏“W”到String "124"
来直接调用宏str1和["a",MacroCall("W",[])]
。
的最后两行中运行的所有呼叫的代码和对结果产生如预期不:
VAL它:MacroExpr列表= [字符串“124”;字符串“124”;字符串“W”]
现在,作为一个练习,您可以尝试更改运行函数,使其像c预处理器那样工作。提示:子功能x_to_string
和eval
是罪犯)
的那些2子功能固定的版本是:
let rec x_to_string = function
| String s -> s
| Stringize x -> x_to_string x
| ArgRef(an) -> x_to_string ((*eval*) (snd(al |> List.find (fun (n,_) -> n = an)))) // disabled eval
| MacroDef(_,_,_) -> failwith "No nested macros supported."
| MacroCall(n,_) -> n
and eval = function
| String s -> String s
| Stringize x -> String (x_to_string x)
| ArgRef(an) -> String (x_to_string (eval (snd(al |> List.find (fun (n,_) -> n = an))))) // added eval
| MacroDef(_,_,_) -> failwith "No nested macros supported."
| MacroCall(n,al1) ->
run macros (MacroCall(n, al1 |> List.map (fun (an,ax) -> an,eval ax)))
...现在结果如预期:
确定它:宏EXPR list = [String“124”;字符串“W”;字符串“124”]
想想这个问题,如果宏观膨胀是渴望或懒惰。为了帮助您,请阅读关于haskell的懒惰论证评估与几乎任何其他语言的渴望论证评估。 – BitTickler