A Files clause introduces names corresponding to files or directories in the Vesta repository. Generally, these files or directories are named by relative paths, which are interpreted relative to the location of the model containing the Files clause. Absolute paths are permitted, though they are expected to be rarely used.
<
Files ::= FileClause* FileClause ::= files FileItem*; FileItem ::= FileSpec | FileBinding FileSpec ::= [ Arc = ] DelimPath FileBinding ::= Arc = `[' FileSpec*, `]' DelimPath ::= [ Delim ] Path [ Delim ] Path ::= Arc { Delim Arc }* Arc ::= Id | Integer | Text
Each FileItem in a Files clause takes one of two forms: a FileSpec or a FileBinding. Each form introduces (binds) exactly one name. In the FileSpec case, the name corresponds to the contents of a single file or directory; in the FileBinding case, the name corresponds to a binding consisting of perhaps many files or directories. In both cases, the identifier introduced into the Vesta naming context or the identifiers introduced into the binding can be specified explicitly or derived from an Arc in the Path.
For example, consider the following files clause:
files scripts = bin; c_files = [ utils.c, main.c ];
Suppose the directory containing this model also contains a directory named bin and files named utils.c and main.c. Then this files clause introduces the two names scripts and c_files into the context. The former is bound to a binding whose structure corresponds to the bin directory. The latter is bound to a binding that maps the names utils.c and main.c to the contents of those files, respectively. The file contents are values of type t_text.
<
When multiple FileItem's are given in a FileClause, the files keyword simply distributes over each of the FileItem's. That is:
files FileItem_1; ...; FileItem_n;
desugars to:
files FileItem_1; ...; files FileItem_n;
When the initial Arc is omitted from a FileSpec, it is inferred from the path. In particular:
files [ Delim ] { Arc Delim }* Arc [ Delim ];
desugars to:
files Arc = [ Delim ] { Arc Delim }* Arc [ Delim ];
<
Multiple FileClause's are evaluated independently:
Eval( FileClause_0 FileClause_1 ... FileClause_n , C) = { val C2 = Eval( FileClause_1 ... FileClause_n , C); return _append(Eval( FileClause_0 , C), C2); }
That leaves only two cases to consider: FileSpec (in which the initial Arc is specified) and FileBinding.
// FileSpec Eval( files Arc = DelimPath , C) = _bind1(id, v)
where:
// FileBinding Eval( files Arc = [ FileSpec_1, ..., FileSpec_n ] , C) = _bind1(id, Eval( files FileSpec_1; ...; FileSpec_n , C))
Again, id is the t_text representation of Arc.
The FileBinding form of the Files clause provides a convenient way to create a binding containing multiple FileSpecs. Without this construct, it would be necessary to name each file twice, once in the FileSpec and once in a subsequent binding constructor. Making a binding with FileBinding is semantically similar to constructing a file system directory, with the additional property that there is an enumeration order for the component files.
Notice that the grammar and evaluation rules given above for FileSpec and FileBinding allow a general Arc on the left-hand side of each equal sign, not just an Id. This was done to simplify the definitions and desugaring rules. However, it would be useless to write constructs like the following, which introduce names that cannot be referenced in the body of the model:
files 33; 34 = 34; "hash-table.c"; "foo bar" = [ foo, bar ];
Therefore, we introduce an additional restriction: the context created by a Files clause must bind only names that are legal identifiers; that is, names that match the syntax of the Id token.
If you need to use files whose names are not legal identifiers, you should either assign them legal names with the equal sign syntax or embed them in a binding. Some possibilities:
// Choose a legal name files f33 = 33; f34 = 34; hash_table.c = "hash-table.c"; foo_bar = [ foo, bar ];
// Embed in a binding files f = [ 33, 34 ]; src = [ "hash-table.c" ];