Next Previous Contents

6. AMC specific CGL built-in's

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.

6.1 Informational

6.2 Output

6.3 State file

6.4 Utility

6.5 Errors

6.6 Interface files

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.

6.7 Defining New Syntax

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_module

This parses keywords at module scope. These follow the module header in a source module with no special grouping.

parse_block

Parse 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_present

This 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"))

The 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.

6.8 Processing tokens directly

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:


Next Previous Contents