Module Excmd.Expression

type t

An alias to AST.expression, abstracted that mutation may be controlled.

type flag_payload =
| Empty
| Payload of string AST.or_subexpr

Basic getters

val count : t -> int
val command : t -> string AST.or_subexpr
val mem : string -> t -> bool

mem fl expr returns true if expr contains flag fl, false otherwise.

Notably, this does not resolve any unresolved words from the parsed expression.

val is_resolved : string -> t -> bool

is_resolved fl expr returns true if expr contains a flag fl and the last such flag fl is already resolved; and false otherwise.

val has_payload : string -> t -> bool

has_payload fl expr returns true if expr contains a flag fl, the last such flag fl is already resolved, and that last flag fl resolved to a string payload instead of a bool. Returns false otherwise.

val flags : t -> string list

flags expr returns a list of the flags used in expr, including only the names of flags - not the payloads.

Resolvers (mutative getters)

val positionals : t -> string AST.or_subexpr list

positionals expr returns a list of positional (non-flag) arguments in expr.

This fully resolves expr — any ambiguous words will be consumed as positional arguments, becoming unavailable as flag-payloads.

val iter : (string -> flag_payload -> unit) -> t -> unit

iter f expr applies f to all flags in expression expr. f receives the flag as its first argument, and the associated, fully-resolved value as the second argument.

This fully resolves expr — any ambiguous words will be consumed as the values to their associated flags, becoming unavailable as positional arguments.

val iteri : (int -> string -> flag_payload -> unit) -> t -> unit

iteri f expr, as with iter, applies f to each flag in expr. However, f will also receive the index of each flag as an argument.

val rev_iteri : (int -> string -> flag_payload -> unit) -> t -> unit

rev_iteri f expr, as with iteri, applies f to each flag in expr; except that the flags are observed in reverse order — from the end of the expression, to the start. (This is marginally more efficient than either iter or iteri.)

val flag : string -> t -> flag_payload option

flag fl expr finds a flag by the name of fl, resolves it if necessary, and produces the payload there of, if any.

This can yield ...

  • None, indicating flag fl was not present at all.
  • Some Empty, indicating a flag fl was present, but the last such resolved to having no payload.
  • Some (Payload str), indicating a flag fl was present and the last such became resolved to the payload str. This can involve resolution of the word immediately following said fl.

Other helpers

val pp : t -> unit

Pretty-print a expression. Implementation varies between platforms.

val hydrate : t -> AST.expression

Type-converter between abstract Expression.t and concrete AST.expression.

Careful; the operations in this module are intended to maintain safe invariants, consuming components of a expression in reasonable ways. You must not devolve one into a raw AST node, modify it unsafely, and then expect to continue to use the functions in this module.

val from_script : AST.t -> t array

Note: Resolution of ambiguous words

It's important to note that a expression is a mutable structure, and that accesses intentionally mutate that structure — in particular, a given word in the original parsed string can only be either a positional argument or the argument to a preceding flag.

Any function that accesses either the value of a flag, or accesses the positionals at all, is going to “resolve” that word in the original source. If the word was ambiguously positioned, that access will result in the datastructure changing — to prevent the word later becoming resolved in an incompatible way.

For example: given the following input command as a Expression.t,

hello --where world 

... there's two possible ways to interpret the 'world', chosen between by the order in which you invoke either positionals, or flag-value-reading functions (like iter or find/flag):

(* Yields zero positionals, and 'world' as the value associated with the flag '--where'. *)
let expr1 = Parser.expression_of_string "hello --where world" in
let where = Expression.flag "where" expr1 (* Some (Payload 'world') *) in
let xs = Expression.positionals expr1 (* [] *)

(* Yields one positional, 'world', and no value associated with the flag '--where'. *)
let expr2 = Parser.expression_of_string "hello --where world" in
let xs = Expression.positionals expr2 (* ['world'] *) in
let where = Expression.flag "where" expr2 (* Some (Empty) *)

Once any ambiguous words have been so resolved (or when a function is called that inherently resolves all ambiguous words, such as positionals or iter), a Expression.t is considered “fully resolved.” No functions in this module will further mutate such a expression.