AMC registers a bunch of special functions with the CGL engine. These functions interface with the machinery in AMC for parsing modules and generating output.
mod_name
This is the name of the module that AMC is currently compiling.
src_fname
This is the name of the source file containing the module that AMC is currently compiling.
src_lineno
This evaluates to the current line number of the input file. This is useful for languages (like C) that support a mechanism for generating correct error messages from preprocessed input.
src_column
This evaluates to the column number of the input file. It is useful for generating error messages within CGL code to the user.
out_fname
This is the name of the output file for the module that AMC is currently compiling.
int_fname
This is the name of the interface file for the module that AMC is currently compiling.
mod_type
Evaluates to the name of the type of the module that AMC is currently compiling.
loc_name
Evaluates to the location of the module that AMC is currently compiling.
dependencies(expr)
For each module the current module is dependant on expr
is evaluated. During evaluation, the variable
cur_modname is set to the module name of the
dependant module and the variable cur_modloc is
set to the location name of the dependant module. The
variable cur_modpublic is set to a true value if
the dependant module was included with the public;
it is false otherwise.
enum_roots(expr)
For each root defined in the project file expr is evaluated with
the following variables bound:
root_nameThis is the module name of the current root.
root_locThis is the name of the location where the module named
root_name can be found.
root_finalThis variable is only bound if the root has a command ``final'' statement associated with it. If bound, it contains the command that is associated with the ``final'' statement.
The is_defined built-in can be used to determine if
root_final is bound.
output_code(string)
This sends the string parameter to the output file for the
current module under compilation.
output_def(string)
This function sends the string parameter to the current
record of the interface file.
output_both(string)
This function sends the string parameter to the
current output file and to the current record of the interface file.
output_rec(key, value)
This function causes a custom record to be emitted to the interface file.
Modules that use interface files with custom record must provide
the handler argument to the copy_intfc built-in.
Unlike project state files, key does not have to be unqiue.
db_set(name, val)
This stores val in the persistant store for this project
with a key of name.
db_get(name, [result])
This obtains the value stored in the persistant store for the
current project under name with the db_set
function. If no record is present, nothing is returned.
Because it is possible to have empty records in the database the optional second argument is the name of a variable that will be set to true if the record is found and false if not. This way it is possible to tell if the get operation failed or just returned an empty string.
db_is_fresh
This variable is automatically define to be either true if
the project state file was created during this startup of AMC or
false if the state file contains information from a previous
run.
It can be used in conjunction with the clean_output
function so that when the state file must be re-created the entire
system must be re-compiled. This allows records in the interface
files to reference records in the state file without getting out of
sync.
db_walk(type, code)
This built-in iterates the persistent store. It pulls records
out of a certain type and executes code for each
variable in the persistent store. type can be one
of the following:
~var - Variables set with db_set and
db_get.~seq - Sequence generators (created with
init_seq_gen). db_var_name is bound to the
string name of the object currently being iterated over. In
addition, if type is ~var, then the string
value of the database variable is stored in db_var_value.
submit_os_cmd(string)
This causes AMC to submit string to the operating system for it
to execute.
clean_output
This destroys any intermediate build files so that all targets will be considered to be unmade for the next build cycle.
compile_module(spec)
Compile the module named spec. If spec includes
a : then the first string is taken to be the name of a location
defined in the project file and the second string is taken to be the
name of a module in that location. If the : is not found, the
default location is assumed instead.
make
This ensures that all root modules defined in the project file and their dependants are up to date.
check_kwargcnt
This built-in takes from one to three parameters. It is useful as the
first thing in handlers in the parse_block and
parse_body constructions. What it does is examine the
number of arguments provided to the current function (or the current
keyword in the cases of parse_body and parse_block) and
see if they fall within certain criterion. If they do not, an appropriate
error message is issued.
check_kwargcnt(1)
This form checks to make sure one and only one arguments have been provided. If not, a generic error message is issued.
check_kwargcnt(2,5)
This form checks to make sure that between two and five arguments have been provided. If not, a generic error message is issued.
check_kwargcnt(2,5,"state")
This form checks to make sure that between two and five arguments have been provided. If not, an error message indicating a problem with the ``state'' keyword is issued.
raise_error(error_msg)
In the event that a CGL program detects an error, it can halt
compilation of the current module by issuing an error. The
error_msg parameter is issued along with an appropriate
error message to the operator.
issue_warning(warn_msg)
Like the raise_error function, issue_warning
issues a warning message (warn_msg) to the operator.
Unlike raise_error, compilation for this module is not
aborted.
Interface files are created as a result of AMC compiling a module. They are often referred to as ``object modules'' in some situations. Each interface file is a collection of typed records.
copy_intfc(code)
For each dependant module, copy its textual interface records from their interface files into either the current interface file (for public modules) or into the current output file (for private modules).
For each record that is not an interface record, code is
evaluated with rec_name bound to the name of the record
and rec_value with the value of the record (see
output_rec).
Because each record originates from a specific dependency in the
uses clause, the variables cur_modname,
cur_modloc, and cur_modpublic are bound in the same way
as the dependencies built-in.
The code argument may be omitted. In which case the default
action when a custom record is encoutnered is to issue a warning, ignore
the record and continue processing.
read_intfc(code)
This is similar to the copy_intfc routine except that it
evaluates to the interface code instead of inserting it into the current
module.
AMC is really a high-level compiler development kit. The idea is that new syntax structures can be defined easily. There are several CGL procedures that make this not only possible, but very easy.
All constructs parsed with AMC follow a similar convention. For example, given the following input:
state_machine(foo)
{
state(1)
{
goto(2) { EVENT_1, EVENT_2 }
goto(4) { EVENT_3 }
/* If we don't match, stay in state 1. */
otherwise(1)
}
state(2)
{
goto(1) { EVENT_2 }
}
}
The basic structure is a keyword followed by some number of (optional) parameters and then an optional collection of other calls to keywords and so on.
Of course, the keywords are completely programmable. There are three important CGL functions that parse constructs like the example above:
parse_moduleThis parses keywords at module scope. These follow the module header in a source module with no special grouping.
parse_blockParse a nested block. These blocks can either be nested within
a parse_module or another parse_block construct.
They are delimited with curly braces ({ and }).
block_presentThis determines if there is a block delimited by curly braces
({ and }) to be parsed. For some syntax entire
blocks may be omitted. This allows the CGL program to know if the
block is there to be parsed or not. It returns true if
the { is the next token in the input stream. It returns
false if not.
As an example, let us define the code to parse everything under the
state_machine construct.
parse_block(
"state",
output_code("case "
arg(1)
": switch (cur_input) { \n")
parse_block(
"goto",
read_id_list(
output_code("case "
read_identifier
":\n"))
output_code("cur_state = "
arg(1)
";\nbreak;\n"),
"otherwise",
output_code("default: cur_state = "
arg(1)
";\nbreak;\n"))
output_code("\n}\n"))
parse_block (and parse_module) routines
take pairs of keywords and the associated code to perform
when that keyword is found. If an even number of arguments
is provided then any keyword not in the list is deemed an
error and a suitable error message is submitted and compilation
is aborted.
If the number of arguments is odd, then the last argument is
assumed to be a piece of code that is executed in the event that
no other keywords matched. In the event the default handler is
called, the variable unbound_keyword is set to the
keyword that was issued.
Of course, not every syntactic structure can be easily (with lots
of sugar) be represented using these mechanisms. Other parsing
routines also exist. In the example above, read_id_list
evaluates its argument for each identifier in a list and
assigns the variable read_identifier the value of the
identifier. Identifier lists can either contain one identifier
(in which case, it must be terminated by a semicolon (;)
or it can be enclosed in curly braces with commas for
separators.
Sometimes the block structure defined by AMC is too limited. For those occasions it is possible to process the token stream directly (with some added difficulty). The built-in functions that can do this follow a similar scheme to those for native languages:
walk_tokens(expr, err)
This processes all subsequent tokens by evaluating
expr for each token input.
During evaluation of expr, two variables are bound to hold
information about the current token.
The variable cur_token is bound to one of the following to
identify the class of token read:
IDENT - IdentifierSTRING - String literalICON - Integer constantOCON - Octal constantXCON - Hexadecimal constantCOMMA - A ,LP - A (RP - A )LC - A {RC - A }LB - A [RB - A ]COLON - A :SEMI - A ;EQUALS - A =MINUS - A -PLUS - A +STAR - A *QUESTION - A ?AT - A @TILDE - A ~ARROW - A ->DOT - A .cur_lexeme is the actual set of characters
that make up the token.
If there is an error during evaluation or lexical analysis the
err parameter is evaluated. If it evaluates to true,
processing of the token stream continues even in the face of errors.
If it evaluates to false, the error is propagated up and handled
appropriatly. If the parameter is omitted, the default is to propagate
the error.
pushback_token(token, lexeme)
This function forces the next iteration of the next
walk_tokens block to get a
token of type token with a lexeme of lexeme.
Unlike some of the other functions in this group, this one can
be called without being in an enclosing walk_tokens
and it will still take effect on the next walk_tokens
call.
clear_pushback
This removes any pushed-back (with pushback_token) tokens
and reads the next token from the input stream.
stop_walking
Calling this function within the expression of a walk_tokens
will abort any further walking of the
input file and continue processing using the previous mechanism.
This is useful if an "end delimiter" is detected and there is more code for AMC in the current module.
This call only effects the inner-most enclosing walk_tokens
call. If there are no enclosing calls,
this function has no effect.
keep_token
Calling this within an enlosing walk_tokens
function will result in the current token being re-evaluated.
This call only effects the inner-most enclosing walk_tokens
call. If there are no enclosing calls,
this function has no effect.
get_token(token_var_name, lexeme_var_name)
This function can be used to obtain the next token.
The two arguments are the names of two variables to bind with the next
token and lexeme. The return value is true if the next token
was obtained or false for end of file.
expecting_err(token, fatal)
This built-in generates an appropriate error message when expecting
a token identified as token and it was not found. If
fatal is true, compilation stops.
unexpected_err(token, fatal)
This built-in generates an appropriate error message when a token
that was not expected, identified as token, was found. If
fatal is true, compilation stops.