(** Utility functions used by multiple phases. *) open Types (** {2 Generating variables} *) (** Generate a new identifier from a given base, e.g. ["foo"] becomes ["_foo_1"]. Uses an [int] reference defined in the implementation file as a counter to assert uniqueness of the generated variable. *) val fresh_id : string -> string (** Generate a new constant (a variable that is known to be assigned only once in total) from a given id, e.g. ["foo"] becomes ["_foo_1_"]. Uses the same counter as {!fresh_id}. *) val fresh_const : string -> string (** Check if an identifier is generated by the compiler. *) val is_generated_id : string -> bool (** Check if an identifier is a constant generated by the compiler. *) val is_const_id : string -> bool (** Generate an identifier from a base and a number. E.g., [generate_id "foo" 1] returns ["_foo_1"]*) val generate_id : string -> int -> string (** Generate a constant identifier from a base and a number. E.g., [generate_id "foo" 1] returns ["_foo_1_"]*) val generate_const : string -> int -> string (** Array dimensions are a special case sine they are suffixed by a non-fresh number (the dimension index), therefore an additional underscore is prefixed to assert uniqueness *) val generate_array_dim : string -> int -> string (** {2 AST traversal} *) (** Default transformation traversal for AST nodes of arbitrary constructor: [traverse unit_type fold_results visit_node node]. For each constructor that has one or more children of type {!Types.node}, the children are transformed with the given transformation function. A [(node, result)] tuple is returned, where the secondary result has the type of the given unit type. [visit_node] must also return such a tuple. [fold_results] is used to reduce results of multiple children to a single result. For [Program] nodes, {!flatten_blocks} is called on the resulting declaration list. This allows the transformation function to return multiple nodes as a replacement, in the form of a [Block] node with multiple children. *) val traverse : 'a -> ('a -> 'a -> 'a) -> (node -> (node * 'a)) -> node -> (node * 'a) (** Wrapper for {!traverse} that ignores the secondary traversal result. Returns {!Types.node} instead of a tuple. *) val traverse_unit : (node -> node) -> node -> node (** Wrapper for {!traverse} for list results. Defined as: [let traverse_list visit = traverse [] (@) visit] *) val traverse_list : (node -> (node * 'a list)) -> node -> (node * 'a list) (** Flatten [Block] nodes into containing node lists. E.g., [[A; Block [B; Block [C; D]]; E] -> [A; B; C; D; E]]. Traverses into elements of the list recursively first, so this is essentially a traversal, as can be seen in the example. Therefore the best practice is to call this function once on the children of a [Program] node after any traversal that may generate [Block] nodes that need to be flattened. *) val flatten_blocks : node list -> node list (** Extract the list of child nodes from a [Block] node. *) val block_body : node -> node list (** {2 AST node annotations} *) (** Add a single annotation to a node. *) val annotate : annotation -> node -> node (** Extract annotations from a node of arbitrary constructor. *) val annof : node -> annotation list (** Get the value of the [Loc] annotation of a node, or {!noloc} if no location can be found. *) val locof : node -> location (** Empty node location: [("", 0, 0, 0, 0)]. Used then location of a node is unknown (if the annotation was removed at some point) or non-existant/irrelevant (for generated nodes on which no errors will occur -- hopefully...). *) val noloc : location (** Get the value of the [Depth] annotation of a node. Raises {!Types.InvalidNode} if the annotation can not be found. *) val depthof : node -> int (** Get the value of the [Index] annotation of a node. Raises {!Types.InvalidNode} if the annotation can not be found. *) val indexof : node -> int (** Get the value of the [Type] annotation of a node. Some node types do not need to be annotated since they have inherent types. For example, a [VarDec] node has a type attribute, and a [Dim] node is always an [Int] (because array dimensions are integers). All nodes which have inherent types are [VarDec], [Param], [FunDec], [FunDef], [GlobalDec], [GlobalDef], [TypeCast], and [Dim]. Raises a {!Types.InvalidNode} if the annotation can not be found, and the type has no inherent type. *) val typeof : node -> ctype (** Get the value of the [LabelName] annotation of a node. Raises {!Types.InvalidNode} if the annotation can not be found. *) val labelof : node -> string (** Get the basic type of a declaration, removing array dimensions *) val basetypeof : node -> ctype (** Get the value of the name attribute from a variable or function declaration (similar to {!typeof} for nodes that have types attributes). Raises {!Types.InvalidNode} if the node is not one of [GlobalDec], [GlobalDef], [FunDec], [FunDef], [VarDec], [Param], or [Dim]. *) val nameof : node -> string (** Get the CiviC data type of a constant value. *) val const_type : const -> ctype (** Check if a constant value is eligible for creating optimized assembly instructions. E.g. [Intval (-1)] is eligible because the instruction [iloadc_m1] exists. Used to decide on which {!Types.instr} constructor to use during the assembly phases. This function always returns [false] when optimizations are disabled (when {!Globals.args}[.optimize = false]). *) val is_immediate_const : const -> bool (** Check if a node has an array type. I.e., [Array] or [ArrayDims]. *) val is_array : node -> bool (** {2 Logging} *) (** Horizontal line of ['-'] characters, used to separate output sections. *) val hline : string (** Print the stringification of a node to [stderr] (uses {!Stringify.node2str}). *) val prt_node : node -> unit (** Output a line to stderr if the verbosity level in {!Globals.args} is at least as high as the specified verbosity level. The line is indented with a number of spaces to match the longest phase identifier (so that logged lines align with identifiers logged by {!Main}). A newline is added automatically. *) val log_line : int -> string -> unit (** Print a line to [stderr] without indent (but do add a newline). *) val log_plain_line : int -> string -> unit (** Same as {!log_line}, but prints a node stringification instead of a literal string. *) val log_node : int -> node -> unit (** Generate an location tuple from Lexing data structures *) val loc_from_lexpos : Lexing.position -> Lexing.position -> location (** Print location tuple to stderr. Produces something like the following: {v int foo; ^^^ v} The location tuple is likely to originate from a node annotation, extracted using {!locof}.*) val prerr_loc : location -> unit (** Print location tuple to stderr, along with an error message. Produces something like the following: {v File "foo.cvc", line 10, characters 8-11: int foo; ^^^ v} *) val prerr_loc_msg : location -> string -> unit (** Print an error message for a node. Calls {!prerr_loc_msg}. *) val node_error : node -> string -> unit (** Print a warning message for a node. Calls {!prerr_loc_msg}. *) val node_warning : node -> string -> unit (** Print an error message of type {!Types.error_msg}. *) val print_error : error_msg -> unit (** Raise a {!Types.FatalError} if the given error list is not empty, and print the errors before quitting. *) val quit_on_error : error_msg list -> node -> node (** {2 String utilities} *) (** [repeat s n] returns a new string of [n] times [s]. *) val repeat : string -> int -> string (** [expand n s] adds spaces to [s] until the resulting string is at least [n] characters long. *) val expand : int -> string -> string (** {2 List utilities} *) (** [optmap f opt] maps [f] to the list value of [opt] if [opt] exists, and [None] otherwise. *) val optmap : ('a -> 'b) -> 'a list option -> 'b list option (** Same as {!optmap}, but returns the list value instead, or an empty list if [opt] is [None]. *) val optmapl : ('a -> 'b) -> 'a list option -> 'b list (** [List.mapi] clone (only available in OCaml version >= 4.00. Maps a function to a list like [List.map] does, but the iterator function is called with the element's index as an additional argument. *) val mapi : (int -> 'a -> 'b) -> 'a list -> 'b list (** {2 List utilities} *) (** [optdo f opt] applies [f] to the value of [opt] if [opt] exists, and returns [None] otherwise. *) val optdo : ('a -> 'b) -> 'a option -> 'b option