Prelude.MacroSimple macro expansion in strings.
A macro is a pattern in a string that is mapped to a named OCaml function that takes a variable number of parameters and returns some value as its expansion.
A macro call in a string looks like (in the default syntax) "{name}"; this example is nullary (i.e. it takes no parameters). Parameters are separated from the name, and from each other by '|'; here is a macro being called with three parameters: "{name|p1|p2|p3}".
Macro calls nest, so a parameter can be formed from the expansion of some other macro: "A nested call: {foo|My parameter is {bar}.}"
Typcally, macros return string values which simply replace their calls in the string, e.g.:
"I'm not {upcase|yelling}!" might become:
"I'm not YELLING!" if the implementation of upcase does the obvious thing.
That being said, macros can actually expand to any kind of value, so instead of the simple:
"text {em|contained} in a paragraph" expanding to the string:
"text <em>contained</em> in a paragraph" the em macro might return a tree structure representing the HTML, as for Caml on the Web:
[`Data "text"; `El ((("", "em"), []), [`Data "contained"]); `Data "in a paragraph"]A macro also takes an arbitrary data parameter that can be () in the simplest case, or something like an opened database handle or some complex data structure; this value is threaded though all the macro calls and so acts like the accumulator of a fold.
A trivial example of a macro is this one that simply acts as a shorthand for a longer string:
Macro.(define "inria" (fun d _ _ -> d, "Institut national de recherche en informatique et en automatique"))Note how the macro needs to return the data parameter d.
This can written more compactly, via the Macro.k combinator, as:
Macro.(define "inria" (k "Institut national de recherche en
informatique et en automatique")) Here is a macro that does significant work -- file inclusion:
(fun d _ -> function [] -> d,"" | fn::_ -> d,readfile fn) Quoting is done with macros that expand to metacharacters; see Macro.left, Macro.sep, and Macro.right for quoters that support the default syntax..
See the macrotutorial.
Syntax error: unterminated macro call, i.e. missing right.
Bad macro name error: currently, a macro name can only be a literal string.
I.e., computed macro names are not (currently) allowed.
type ('a, 'b) macro = 'a -> string -> 'b list -> ('a, 'b) resultThe type of macro results, a pair of:
and ('a, 'b) env = (string * ('a, 'b) macro) listThe type of parsed macro expression nodes (as returned by parse).
val empty : ('a, 'b) envempty is the empty environment.
(define name macro env) binds name to macro in environment env and returns the new environment.
N.B. the new binding goes at the front of the old environment and hence overrides any older macro of the same name.
Example: if env is the environment:
Macro.(define "a" (k "1") empty |> define "b" (k "2") |> define "a" (k "3"))then:
(Stream.of_string "{a}" |> Macro.to_string env ()) = "3"(eval ?default env data name parms) calls the macro bound to name in the environment env with parameters parms, exactly as it would be called if found in the string passed to to_string.
If name is not found in env, default is called (with name as its name). If default is not provided, Undefined is raised.
Example:
(eval (define "a" (k "1") empty) () "a" []) = ((), "1")Within a string, a macro call is a parenthesized expresssion (that is, macro calls nest); the opening and closing "parentheses" can be any pair of characters; the defaults are '{' and '}'.
The string of characters beginning after the left paren is the name of the macro; the name is terminated either by the matching right paren, or else by the first parameter separator character, the default being '|'.
Parameters are separated from the name and from each other by this separator.
Here are some calls:
"{}" -- calls the macro whose name is the empty string with no parameters"{foo}" -- calls the macro named "foo" with no parameters"{foo|bar}" -- calls the macro named "foo" with the parameter list ["bar"]"{foo|bar|zap}" -- calls the macro named "foo" with the parameter list ["bar";"zap"]"{foo||zap}" -- calls the macro named "foo" with the parameter list ["";"zap"]"{foo|{bar}}" -- calls the macro named "foo" with the parameter list whose List.hd is the value of the macro call "{bar}"val parse :
?left:char ->
?sep:char ->
?right:char ->
char Stream.t ->
node list(parse ?left ?sep ?right stream) parses the string on stream and returns a list of node's.
Example:
(Stream.of_string "Foo {bar|1|2}" |> parse) = [S "Foo "; M ("bar", [[S "1"]; [S "2"]])] val defines :
?preserve:bool ->
string ->
('a, string) env ->
node list ->
('a, string) env * node list(defines ?preserve definer env nodes) returns the node list nodes after adding in-line macro definitions in nodes to env. This allows the string that's being expanded to define its own macro definitions.
definer is the name of the inline macro-defining macro.
The inline definitions do not have to precede their uses.
Any M-nodes representing calls to definer are removed from the node list, unless (preserve = true).
Example: given the input string:
let str = "Hey, {name}! Hel{define|name|Buddy}lo." we would have:
(Stream.of_string str
|> parse
|> defines "define" []
|> fun (env,ns) -> string_of_nodes env () ns)
= "Hey, Buddy! Hello." val fold :
?default:('a, 'b) macro ->
('b list -> string -> 'b list) ->
('b list -> ('a, 'b) result -> 'a * 'b list) ->
('a, 'b) env ->
('a * 'b list) ->
node list ->
'a * 'b list(fold ?default s m env (data,z) nodes) folds s and m over a list of node's, with data as initial macro data parameter and z as the initial accumulator for the fold.
s is applied to the fold accumulator and the string value of each S-node.
m is applied to the fold accumulator and the result of the evaluation of each M-node; the result of the evaluation includes the possibly updated data parameter.
The macro definitions are in the environment env.
data is passed on to each macro call, along with the name the macro was called with, and its list of parameters.
If provided, ~default is a default macro that is called whenever a macro call with an unknown name is encountered. Otherwise, in such a case, Undefined is raised with the name of the offending call.
(string_of_nodes ?default env data nodes) expands the macro calls in nodes and returns macro data and a string representation of the expansion.
It's equivalent to:
fold snoc (fun a (d,s) -> d, s::a) env (data, []) ns |> fun (d,l) -> d, rev l |> String.concat "" but more efficient.
val to_string :
?define:'a ->
?left:char ->
?sep:char ->
?right:char ->
?default:('b, string) macro ->
('b, string) env ->
'b ->
char Stream.t ->
string(to_string ?define ?left ?sep ?right ?default env data stream) is (parse ~left ~sep ~right stream |> string_of_nodes ?default env data)
These may make it easier to write macros.
val k : 'b -> ('a, 'b) macro(k x) is the constant macro that always expands to x.
val skip1 : (string -> 'b list -> 'b) -> ('a, 'b) macro(skip1 f) converts function f into a macro by not passing on the data parameter.
f skips the 1st parameter of the function.
(skip2 f) converts function f into a macro by not passing on the name parameter.
f skips the 2nd parameter of the macro.
val skip12 : ('b list -> 'b) -> ('a, 'b) macro(skip12 f) converts function f into a macro by passing on neither the data nor name.
f skips the 1st and 2nd parameters of the function.
(syntax ?def n f) converts function f into a macro that raises an error if it is not called with exactly n parameters, unless ~def is given, in which case def is returned as the value.
Note that the function f receives its parameters as an array of guaranteed length n, for easier access.
Coming soon.