Module Excmd.AST
Describing structures
Some of our data-structures provide conversion functions to produce alternative formats of themselves. These are automatically generated at compile-time by PPX preprocessors in OCaml.
Unfortunately, we use different data-structures per compile-target: in native OCaml, we use {{:https://github.com/ocaml-ppx/ppx_deriving_yojson} ppx_deriving_yojson} to produce a value of type Yojson.Basic.json; but when being compiled to JavaScript, we use BuckleScript's jsConverter tooling to generate the built-in Js.t type. These generators also produce functions of different names: to_yojson is available on the native side, and tToJs on the BuckleScript side.
Due to the fact that the relevant types differ between platforms, fully generic code involving alternative-format representations like the above isn't clean and easy. Both of the above flavours of conversion-function will raise a runtime exception if called on a platform that doesn't support them; if you need to, you can catch said exception and swap implementations based on that.
val tOfJs : 'a -> 'bval tToJs : 'a -> 'bval expressionOfJs : 'a -> 'bval expressionToJs : 'a -> 'bval to_yojson : 'a -> 'bval of_yojson : 'a -> 'bval expression_to_yojson : 'a -> 'bval expression_of_yojson : 'a -> 'b
type 'a unresolved=|Unresolved|Resolved of 'a|AbsentThis triple extends the usual
optional type with an intermediateUnresolvedstate. It's used inflags to indicate the presence of a subsequentPositionalargument that may-or-may-not be thepayloadof saidflag.
val unresolved_to_yojson : a. ('a -> Yojson.Safe.json) -> 'a unresolved -> Yojson.Safe.json
type 'a or_subexpr=|Sub of expression|Literal of 'aWraps any value that may be replaced in the AST by a subexpression; i.e., in the expression
echo hello worldthe literalhellowill be wrapped inLiteral. This is necessary because it could just as easily beecho (echo hello) world.
type word= string or_subexpr listThe most granular element of an excmd, like a shell command, is the "word." These are a series of parameters to a command, separated by whitespace:
echo each of these is a separate "word"
Words can also be produced by subexpressions,
echo each of these is a (echo separate) "word"
... or created via quotation:
echo "this is only a single word"
A single shell "word" can actually be composed of multiple quotations or subexpressions, as long as they are not separated by whitespace:
echo "this""is""only""a""single"(echo word)(echo also)
Such a composition is represented in this type, by a series of
or_subexprs in alist.
type flag={name : string;mutable payload : word unresolved;}A single
--flag, possible with-a=payload. The flag's name is always literal; but the payload may be:cmd --fl="a quotation,"
cmd --fl=(echo a subexpression)
cmd --fl=or"a"(echo smooshing)"thereof"
(Although this type is technically mutable, it's probably a better idea to leave the mutation up to the
Expression-interface.)
type arg=|Positional of word|Flag of flagtype expression={count : int;cmd : word;mutable rev_args : arg list;}
val or_subexpr_to_yojson : a. ('a -> Yojson.Safe.json) -> 'a or_subexpr -> Yojson.Safe.jsonval word_to_yojson : word -> Yojson.Safe.jsonval flag_to_yojson : flag -> Yojson.Safe.jsonval arg_to_yojson : arg -> Yojson.Safe.jsonval expression_to_yojson : expression -> Yojson.Safe.json
type t={expressions : expression array;}
val to_yojson : t -> Yojson.Safe.jsonval make_expression : ?count:string -> cmd:word -> rev_args:arg list -> expressionval pipe_last : from:expression -> into:expression -> expressionval copy_word : word -> wordval copy_flag : flag -> flagval copy_arg : arg -> argval copy_expression : expression -> expressionval copy : t -> tval pp_bs : 'a -> 'bval pp_native : t -> unitval pp : t -> unitval pp_expression_bs : 'a -> 'bval pp_expression_native : expression -> unitval pp_expression : expression -> unit
val is_literal : 'a or_subexpr -> boolval get_literal_exn : 'a or_subexpr -> 'aval get_sub_exn : 'a or_subexpr -> expression