8 Source Code Markup Language (SCML)
The Source Code Markup Language (scml) is a formatting language that can be used to create templates for source code that
needs to be output by Flick. For example, the corba C++ mapping requires a number of C++ classes and functions to be
generated alongside the actual defined types. These extra classes and functions are only there for the sake of presentation and
do not need any special analysis or optimization so generating them is usually just a matter of plugging the right strings into a
template. This phase of code generation can be done with a series of printfs, however, a more data oriented and
externalized approach is more flexible and possibly easier to maintain. Therefore, scml was created so that the user
could write the implementations of these functions as a template and data created by Flick would fill in the
holes.
The use of scml code in Flick is currently limited to the functions and methods for the corba C++ classes created for the
TAO implementation, although it would be nice to begin using it for other code which is currently done with printfs. This
code is read in and executed at start up so that it can define a number of new commands that are used to generate the
appropriate output. As a Flick back end then processes the pres_c and defers to the scml code whenever it encounters a
presentation function declaration.
8.1 The Language
8.1.1 Introduction
The syntax of the language was based on SGML in an attempt to make it more accessible to people in general since many are
already familiar with its most popular descendent, HTML. An example code section might be the best way to
start.
The following scml code is taken from runtime/headers/flick/pres/std_defines.scml:
<ifndef name="STD_DEFINES_SCML">
<define name="STD_DEFINES_SCML" kind="static-variable" value=true>
<!-- These are the standard set of defines used in SCML -->
<define name="scope" kind="bracket-tag" value="c-scope-handler"
rparams={name:string}>
<define name="macro" kind="bracket-tag" value="c-macro-handler"
rparams={name:string} oparams={close:bool=false
rparams:tag_list={}
oparams:tag_list={}}>
...
</ifndef>
|
Anything within angle brackets is a command name followed by the arguments for the command. So the first command is
ifndef, which is basically the same thing as a C preprocessor's `#ifndef', it checks to see if name has been declared or not and
executes the block if it is not defined. The second command, define, creates a static variable in the current scope with the
given name and value. From there we have a comment and then create some more commands for the system using define and
then we terminate the ifndef block with </ifndef>. So this basically corresponds to the organization of a header file in
C.
8.1.2 Types
Now that we know basically what a command looks like lets look a little deeper at the rest of the command. When executing a
command there are a series of assignments after it that set the values of some variables. These assignments are setting the
values of the parameters for the command. Each parameter has a name, which is the name used in the assignment, a type, which
is specified in the definition, and finally a default value. A name can be any string, but only C style identifiers can be used
regularly, otherwise one must surround the name with single quotes ('). The type of a parameter is currently limited
to:
- any any supported type
- arrays simple array types
- bool a boolean value (e.g., "true" or "false")
- int a signed integer
- float a single precision floating point number
- string a string
- tag_list a tag_list
- cmd a scoped reference to a command name
- scml scml code
- stream An I/O stream, this is a bit of a hack at the moment, try and avoid using it
8.1.3 Values
The format for declaring the tag that represents this parameter is the tag name, a colon, and an equal sign followed by a literal
value to set the default. For example, num:int=5 makes an integer tag named `num' and sets its value to 5, of course
if this tag is not added to a list, it cannot be accessed and is lost, which is why all parameter declarations are
tag_lists.
The set of literal values for these types are:
- bool true, false
- int regular and hex, uh, do not try to negate anything. . .
- float as one would expect, again do not negate
- strings "put some text here", it also supports C style escapes, except for octal values. (Note: one can also build
strings with cast stuff from Flick-generated tags, but do not try to use it for tag names or anything other than
printing. It will barf.)
- tag_list { fname:string="John" lname:string="Doe" }
- cmd struct::var::'T operator=(T )'
- scml Nothing other than the builtin contents tag which is created for block commands.
scml also supports simple expressions for most of its types:
- arrays [] (indexing, as in a[5])
- bool , ||, ==, !=, !
- int/float +, -, /, *, %, (), !, ==, !=, <, <=, >, >=, <<, >>
- strings + (concat)
- tag_list . (dereference), | (append)
- cmd none
- scml none
8.1.4 Commands
In order to allow this language to be flexible enough to handle any type of presentation it has only a minor set of builtin
commands from which all others are created. They are define, undef, rename, ifdef, ifndef, and include, although
define is really the only one that is needed, the others are just there to be nice. Define is basically the hook that is used to add
other objects into the system. It lets one create local variables, static variables, commands, and escape sequence for regular text.
The parameters of the define command are:
- name:string the name of the object
- kind:string the type of the object, needs to be one of:
- "variable" a local variable
- "static-variable" a static variable
- "tag" a command (e.g., <define name....)
- "bracket-tag" a block command (e.g., <ifndef name="..."></ifndef>)
- "escape" an escape sequence
- value:any The value for this object. If it is a variable then it is the type and value of the variable; if it is a command
than it is a name corresponding to the internal C handler code; and if it is an escape it is the string that the escape resolves
too.
- rparams:tag_list The list of required parameters for this command
- oparams:tag_list The list of optional parameters for this command
The name, kind, and value parameters are required in order for define to work properly. From define there comes a standard
set of commands that are useful, and cannot really be used to implement each other since they modify some scml execution
state, they are:
- scope name:string Creates a new lexical scope with given the given name
- macro name:string close:bool=false rparams:tag_list={} oparams:tag_list={} macro is
able to create commands from scml code to allow for code reuse, recursion, and other nice things. Close
specifies whether or not this is a block command and the rparams and oparams parameters are similar to define's.
- if 'test expression' This is a block command that will partition its contents around an else command if
there is one, and then execute the one block or the other based on the expr. (Note: this is sort of a special case.
One does not specify a parameter name because it is silly and annoying, so the first thing after the if needs to
be some valid boolean/integer expression.)
- for iter:string each:string length:int
For will iterate over an array, specified with the each parameter as the tag name, or will loop `length' times. The
`iter' parameter is the name of the tag you want created to hold the iterator number.
- ignore 'expressions' This will just evaluate the expressions inside the command and not print anything
out.
- pre indented:bool=true This tells the execution environment to go into preformatting mode. The
placement of this tag is important, if it is indented than any of its contents will be shifted to the left by that much.
This is to allow users to give structure to their scml code without affecting the output. The indented parameter
is used to indicate whether any macro calls or scml code should be indented relative to the indentation of where
they are executed. For example, if indented is set to true than any output from a macro call will be shifted to the
right so that all lines will line up with the beginning of the macro call. If it was set to false then any lines would
be indented based only on the indentation inside of the macro.
- defvar 'tag defs' This will go through the list of tags following the defvar id and declare those tags in the
local scope
- aliascmd name:string handler:cmd This is used to make a command in a scope without having to
actually define it, rather the command is simply a reference to the one specified by handler.
- create_stream name:string path:string This is used to create an output stream so that one can send
scml output to a different file. The `name' argument is used as the name of the static variable to be created. The
`path' argument should be a valid path for the file or empty for standard out.
- retarget output:stream This is used to redirect any scml output to the stream referred to by `output',
which was created by create_stream.
In addition to regular style commands with names, there are a number of "special" commands which are denoted by some
symbol. They are:
- <!-- --> This is a comment
- <|Some text|> This is a verbatim, it prints whatever is between the vertical bars without doing any processing
- <(some_expr)> This will print out the value of the expression inside the parentheses
- &escape_name; This is an escape sequence, it can be used in regular text to print some special character.
There is also a special tag which gets added to every block command. It is called `contents' and contains the scml code that
is between its start and terminating commands.
8.1.5 Larger Example
Now lets look at some useful code:
<macro name="idl" close=true rparams={type:string}>
<scope name=type>
<macro name="pres" close=true rparams={type:string}>
<scope name=type>
<macro name="func" close=true rparams={kind:string}>
<macro name=kind oparams={sub_contents:scml=contents}>
<pre><(sub_contents)></pre>
</macro>
</macro>
<(contents)> <!-- put the contents of idl here so it can see 'func'-->
</scope>
</macro>
</macro>
|
This code is used to setup some infrastructure that is used to actually define function bodies. The `idl' macro specifies an
idl type (e.g., struct, union, etc.) and adds a new scope with that as the name. Inside of this scope it creates the `pres' macro
which corresponds to a presentation type (e.g., var, out, etc.). Again a new scope is created with this name and we
make the `func' macro which will be used to define a function body. Since just putting the contents of `func'
right inside of the macro would print them out when the file is first being evaluated we make a new macro and
pass our contents to it. This way when the Flick back end needs to output a function body it just looks up the
right scope, grabs the macro and then executes it, thus printing out the function body. Now let's see this being
used:
<idl type="struct">
<pres type="out">
<func kind="T_out(T_ptr &)">
: ptr_(<(parameter[0])>)
{
this->ptr_ = 0;
}
</func>
</idl>
|
This code makes a macro named `T_out(T_ptr )' inside of the struct::out scope. The presentation_impl inside of the back
end now writes out the function type which it found in the cast and then looks up and executes this macro, resulting in the
following output.
blah_out::blah_out(blah_ptr &p)
: ptr_(p)
{
this->ptr_ = 0;
}
|
Notice that everything is spaced just fine. That is because func created the macro with `sub_contents' surrounded by the
preformatting tag. Since preformatting causes indentation artifacts to be ignored they were ignored here, the important thing to
note is that the indentation is based on the indentation of the block command that created the contants, not the one that used
it.
8.2 The SCML interpreter
8.2.1 Introduction
The scml interpreter is contained in the C presentation back end library so all of the relevant sources are in
c/pbe/lib/scml_*.cc and the header file is mom/c/scml.hh. The interpreter is primarily written in C++ with classes for the
lexxer, parser, execution environment, and support for these. Using the interpreter is simply a matter of constructing lexxer,
parser, and EE objects with an input file or string and then setting them in motion.
8.2.2 scml_stream
This class is used to hold any information about IO streams used in the code. The primary use of the class is for holding the
input scml code in a string which is then used by the lexxer. However, these stream objects can also be used in scml code for
setting the output stream with the create_stream and retarget commands.
-
char *tag_ref()
-
Make a TAG_REF string that refers to this object
-
static struct scml_stream *ptr(char *ref)
-
Convert a TAG_REF string into a scml_stream pointer
-
void set_flags(unsigned int the_flags), unsigned int get_flags()
-
Set/get the flags for the stream. Currently, the only flags are SSF_INPUT and SSF_OUTPUT, which
correspond to whether the stream will be used for input or output.
-
void set_data(char *str), char *get_data()
-
Set/get a C string as the stream, this does not work for an output file.
-
void set_desc(char *desc), char *get_desc()
-
Set/get the description of the stream (e.g., file name)
-
void set_include_directory_list(char **dir_list), char **get_include_directory_list()
-
Set/get the list of directories in which to find any include files
-
int get_length()
-
Get the length of the stream
-
void set_file(FILE *file), FILE *get_file()
-
Set/get a file as the stream, if this is an input stream then it will read the whole file into memory for processing,
otherwise it is just stored for later reference.
8.2.3 scml_string
The scml_string class is a bit of a hack to get around problems with using the pres_c tags as the representation of variables.
The problem has to do with the tags being able to hold cast and scml not wanting to deal with anything language specific: it
is only interested in strings, numbers, and the like. So the solution is to treat cast as "computed strings" and just
place all strings inside of scml_strings so that we can still process regular strings and cast without too many
problems.
-
char *tag_ref(), static struct scml_string *ptr(char *ref)
-
Encode/decode this scml_string object as a TAG_REF string.
-
static char *tag_string(tag_item *ti)
-
Attempts to construct a C string from a tag, this is done by getting a string directly from a TAG_STRING or
doing a make_chars on an encoded scml_string object.
-
tag_data *add_component()
-
Adds another component to this strings. It is just like concatenating a string except it needs to be placed inside
of a tag_data
-
void concat(struct scml_string *ss)
-
Concatenate an entire scml_string onto another one
-
int cmp(struct scml_string *ss)
-
Just like strcmp except it compares scml_strings
-
char *make_chars()
-
Tries to make a C string from this scml_string, it will fail if any of the components are "computed strings"
(e.g., cast structures)
-
void print()
-
Print the string using wprintf
8.2.4 scml_token
The scml_token class is the primitive used to represent scml code and is used by almost every class that is a part of the
interpreter. A token can be one of many kinds which may or may not have a value associated with it. Any values are contained
within the value field of scml_token. The possible values it may hold are:
-
b
-
A boolean
-
i
-
An integer
-
f
-
A float
-
id
-
An identifier
-
str
-
An scml_string
-
text
-
Regular text
-
escape
-
The name of an escape sequence
-
ti
-
A tag_item
-
tl
-
A tag_list
-
children
-
The list of children for a non-terminal token
-
void strip()
-
If the token contains a tag this will try to make the token the same value as the tag. It can only fail if the tag
does not have a corresponding scml type. Any other type of demotions, like float to int, need to be done
explicitly by the user since information can be lost
-
void promote(int required_kind)
-
This will promote a literal value to a more complex scml type. For example, it can promote an integer to a
float to make it easier for later stages to process.
-
int is_expr()
-
This will return true if the token represents some expression. For example, if it were an addition, subtraction,
or just a plain value.
-
int is_value()
-
This will return true if the token contains a literal value
-
int is_operator()
-
This will return true if the token represents a mathematical operator
-
int is_container()
-
This will return true if the token represents some container, like a tag list, or an expression in parentheses.
-
int get_prec()
-
If the token represents an operator this will return its precedence
-
char *get_identifier()
-
If the token represents an identifier this will return a C string version. The possible type of identifiers include
regular variable name, escape sequence names, and scoped names.
-
void print()
-
Used for debugging, just prints out the token
Each token also has a kind member which indicates what the token represents:
-
SCML_NONE
-
A null token, often used to indicate the end of a list of tokens
-
SCML_IGNORE
-
A null token, perhaps it once contained something meaningful, but should be ignored now. Or it is used when
a token must be returned, but nothing meaningful was done.
-
SCML_ERROR
-
An error token, processing has resulted in an error
-
SCML_DONE
-
Indicates the end of the stream
-
SCML_COL_POS
-
The column position in the stream is in i so that it can be tracked and used in error reporting.
-
SCML_ROW_POS
-
The row position in the stream is in i so that it can be tracked and used in error reporting.
-
SCML_TERM_BOOL
-
A literal boolean value in b
-
SCML_TERM_INT
-
A literal integer value in i
-
SCML_TERM_FLOAT
-
A literal float value in f
-
SCML_TERM_STRING
-
A literal string value in str
-
SCML_TERM_TAG
-
A tag value in ti
-
SCML_TERM_TAG_LIST
-
A tag list in tl
-
SCML_TERM_TEXT
-
Any text from scml code that is not an escape sequence or a command is held in text
-
SCML_TERM_ESCAPE
-
The name of an escape sequence is in escape
-
SCML_TERM_ID
-
An identifier string is in id
-
SCML_TERM_VERBATIM
-
Any text within a verbatim command is in text
-
SCML_TERM_LPAREN
-
A left parenthesis
-
SCML_TERM_RPAREN
-
A right parenthesis
-
SCML_TERM_LBRACE
-
A left bracket `['
-
SCML_TERM_RBRACE
-
A right bracket `]'
-
SCML_TERM_LCURLY
-
A left curly brace
-
SCML_TERM_RCURLY
-
A right curly brace
-
SCML_TERM_SLASH
-
A forward slash, `/'. This token is only created for the forward slash immediately after an `<', otherwise an
SCML_NT_DIV is created.
-
SCML_TERM_LT
-
A single less than sign, `<'. This token is used to signify the opening of a command, it is not the same as a
less than inside an expression in the command.
-
SCML_TERM_GT
-
A single greater than sign, `>'. This token is used to signify the closing of a command, it is not the same as
a greater than inside an expression in the command.
-
SCML_NT_COMMAND
-
A command with children pointing to the contents of the command. The token is a call to a tag if the first
token is a SCML_NT_NAME followed by an SCML_NONE terminated array of expressions corresponding to the
arguments. If the first token is an SCML_TERM_SLASH followed by an SCML_NT_NAME then the command is a
terminator for the tag with that name. Otherwise the token can be just be an expression that will be printed
later.
-
SCML_NT_SET
-
A converted SCML_NT_ASSIGN. It is a binary expression that indicates that the value of children[1] should
be set in children[0]
-
SCML_NT_ASSIGN
-
An equal sign, `='. This token will be converted to an SCML_NT_SET for no real reason; it is legacy and the
set should just be killed in favor of this token.
-
SCML_NT_PLUS
-
A plus sign, `+'. It is a binary expression so children[0] is the left side and children[1] is the right.
-
SCML_NT_MINUS
-
A minus sign, `-'. Also a binary expression so it is structured like SCML_NT_PLUS
-
SCML_NT_DIV
-
A division sign, `/'. Also a binary expression so it is structured like SCML_NT_PLUS
-
SCML_NT_MOD
-
A modulus sign, `%'. Also a binary expression so it is structured like SCML_NT_PLUS
-
SCML_NT_MULT
-
A multiplication sign, `*'. Also a binary expression so it is structured like SCML_NT_PLUS
-
SCML_NT_COLON
-
A colon, `:'. This token will immediately be converted into an SCML_NT_TAG when parsed.
-
SCML_NT_EQUAL
-
A C style equal sign, `=='. Also a binary expression so it is structured like SCML_NT_PLUS
-
SCML_NT_NOT_EQUAL
-
A C style not equal sign, `!='. Also a binary expression so it is structured like SCML_NT_PLUS
-
SCML_NT_LT
-
A double less than sign, `<<', used in expressions since a single one is already used to demarcate a command.
-
SCML_NT_GT
-
A double greater than sign, `>>', used in expressions since a single one is already used to demarcate a
command.
-
SCML_NT_LE
-
A less than or equal sign, `<='. Also a binary expression so it is structured like SCML_NT_PLUS
-
SCML_NT_GE
-
A greater than or equal sign, `>='. Also a binary expression so it is structured like SCML_NT_PLUS
-
SCML_NT_LAND
-
A C style logical and sign, `'. Also a binary expression so it is structured like SCML_NT_PLUS
-
SCML_NT_OR
-
An or sign, `|', used for doing union operations on tag lists.
-
SCML_NT_LOR
-
A C style logical or sign, `-'. Also a binary expression so it is structured like SCML_NT_PLUS
-
SCML_NT_NOT
-
A exclamation mark, `!', that signifies a logical not on children[0]
-
SCML_NT_DOT
-
A dot, `.', used for referencing tags within a tag list.
-
SCML_NT_SCOPE_RES
-
A C style scope resolution operator, `::'. Also a binary expression so it is structured like SCML_NT_PLUS
-
SCML_NT_NAME
-
A null token that indicates the token in children[0] can be used as a name.
-
SCML_NT_EXPR
-
A null token that indicates the token in children[0] is some kind of an expression
-
SCML_NT_TAG
-
A tag declaration converted from an SCML_NT_COLON. The tag name is contained in children[0] and the
name of the type is in children[1]
-
SCML_NT_SEL
-
A slot selection on an array variable. This token is created from a pair of brackets with children[0]
expecting to resolve to a tag and children[1] evaluating to an integer.
-
SCML_NT_COMMA
-
A comma, `,', used when separating the elements of a tag list.
-
SCML_NT_INIT
-
An initialization contained in curly braces. This is a bit hacky since one can create an array initializer, or a
tag list, depending on what kind of data is in the initialization.
-
SCML_NT_COND
-
A conditional expression similar to the one in C, `<test expr> ? <expr> : <expr>', except it is not
implemented yet.
-
SCML_NT_AND
-
An ampersand, `', used for to do intersection on a tag list, if it was implemented.
-
SCML_NT_XOR
-
A carat, `', for doing exclusive ors, except it is not implemented.
8.2.5 scml_token_sequence
An scml_token_sequence is used to reference sections of scml code. The class tracks the position and the origin of the code
so it is possible to format the code correctly, and point out where any errors are in the source.
-
char *tag_ref(), static struct scml_token_sequence *ptr(char *ref)
-
These are used to encode and decode the token sequence as a TAG_REF style string. This allows the sequence
to be passed around and used in scml code
-
void print()
-
Used for debugging, just prints the contents of the sequence out
-
void set_value(struct scml_token *st), struct scml_token *get_value()
-
Set/get the array of the tokens that represent the value of this sequence
-
void set_length(int len), int get_length()
-
Set/get the length of the array of tokens in this sequence
-
void set_stream_pos(struct scml_stream_pos *ssp), struct scml_stream_pos
*get_stream_pos()
-
Set/get the stream position object used when parsing the tokens in the sequence. This is mainly here for error
reporting when the tokens get processed
-
void set_indent(int offset), int get_indent()
-
Set/get the column based indentation of the tokens. This allows users of scml to structure their code with
indents without having it affect the way it will eventually be printed, even when preformatted.
8.2.6 scml_token_stack
The scml_token_stack is a support class for the parser that just maintains a stack of tokens.
-
void push(struct scml_token &st), void pop()
-
Push/pop a token
-
struct scml_token *index(int offset)
-
Access the token at the given offset from the top of the stack
-
int count()
-
Returns the number of tokens on the stack
-
void print()
-
Used for debugging, just prints out the contents of the stack
8.2.7 scml_stream_pos
The scml_stream_pos is used to lex the scml source and for tracking the position during execution. The class is slightly
different from a regular lexxer since it needs to operate in two modes, one for the raw text, and one for the command tokens.
Additionally, the class also needs to return position tokens so that it is possible to accurately track position in the source during
execution. Otherwise, the operation of the class is like one would expect, it translates text into tokens for others components to
process.
-
void set_stream(struct scml_stream *ss), struct scml_stream *get_stream()
-
Set/get the data stream for lexxing
-
void set_flags(unsigned int the_flags), unsigned int get_flags()
-
Set/get the flags
-
void set_state(int state), int get_state()
-
Set/get the state
-
void set_cursor(char *cursor), char *get_cursor()
-
Set/get the current position in the scml_stream buffer
-
void set_row(int row), int get_row()
-
Set/get the current row in the stream
-
void set_column(int column), int get_column()
-
Set/get the current column in the stream
-
int get_last_row(), int get_last_column()
-
Get the last row/column that was lexxed in the stream. This is used in error messages to give the user a range
to look for the error
-
char *munge_string(char *str)
-
Convert a string with escape sequences into a standard C string.
-
int get_number(struct scml_token *st)
-
Convert a number in the text stream and store its value in st.
-
int scan(int kind, const char *str)
-
Scan forward in the stream until the condition is satisfied, the cursor will then be placed one character after
the cause of the stop.
-
struct scml_token get_token()
-
This is the main function for lexxing, it will walk through the stream returning tokens until it hits the end of
the stream and returns SCML_DONE
8.2.8 scml_parser
The scml_parser is a simple (and not very good) parser for scml tokens returned by an scml_stream_pos. It works by
giving the class an scml_stream_pos and then calling parse. If the parse was successful a token sequence will be returned
which can then be executed.
-
void set_stream_pos(struct scml_stream_pos *ssp), struct scml_stream_pos
*get_stream_pos()
-
Set the stream that the parser should get tokens from.
-
struct scml_token_sequence *parse()
-
The main parsing function, it will ask the stream position object for tokens and then construct an scml_
token_sequence from them.
-
struct scml_token collapse(struct scml_token_stack *values, struct scml_token *oper)
-
An internal function used when parsing. Basically, it just looks at the operator and pulls any operands off of
the stack, and then pushes the operator connected to the operands onto the values stack.
8.2.9 scml_handler
The scml_handler is a structure for binding a name to a C/C++ function, making it easy to bind to a function from an scml
level.
8.2.10 scml_handler_table
The scml_handler_table class is used for tracking and indexing all of the functions available.
-
void add_handler(struct scml_handler *sh), void rem_handler(cosnt char *name)
-
Add/remove a handler in the table
-
struct scml_handler *find_handler(const char *name)
-
Find a handler in the table with the given name
8.2.11 scml_cmd_definition
The scml_cmd_definition class is used for describing an scml command, its name, parameters, and definition.
-
char *tag_ref(), static struct scml_cmd_definition *ptr(char *ref)
-
Encode/decode the object as a TAG_REF compatible string
-
void set_name(const char *name), const char *get_name()
-
Set/get the name of the command
-
void set_opt_params(tag_list *tl), tag_list *get_opt_params()
-
Set/get the list of optional parameters
-
void set_req_params(tag_list *tl), tag_list *get_req_params()
-
Set/get the list of required parameters
-
void set_flags(int flags), int get_flags()
-
Set/get the flags for the object
-
void set_handler(struct scml_handler *sh), struct scml_handler *get_handler()
-
Set/get the handler for this command.
-
void set_token_sequence(struct scml_token_sequence *sts), struct scml_token_sequence
*get_token_sequence()
-
Set/get the token sequence that defines this command
-
int execute(struct scml_token *st, struct scml_context *sc)
-
Execute the handler with the token and context
8.2.12 scml_escape/scml_escape_table
The scml_escape class is used for describing an scml escape, the name and value. The escapes are then tracked in an
scml_escape_table.
-
void add_escape(struct scml_escape *se), void rem_escape(cnst char *name)
-
Add/remove an escape definition in the table
-
void find_escape(const char *name)
-
Find an escape definition with the given name
8.2.13 scml_scope
The scml_scope class is used to represent a scope in scml, it holds definitions, child scopes, and escapes. Static variables and
command definitions are held in the values tag list, but escapes are held separately.
-
struct scml_scope *get_parent()
-
Return the parent scope
-
void set_name(const char *name), const char *get_name()
-
Set/get the name of the scope
-
void set_escape_table(struct scml_escape_table *setable), struct scml_escape_table
*get_escape_table()
-
Set/get the escape table associated with this scope
-
void add_child(struct scml_scope *ss)
-
Add a child scope
-
struct scml_scope *find_child(const char *name)
-
Find a child scope
-
void add_cmd_definition(struct scml_cmd_definition *scd), void rem_cmd_definition(struct
scml_cmd_definition *scd)
-
Add/remove a command definition in the scope
-
struct scml_cmd_definition *find_cmd_definition(struct scml_scope **scope, const char
*name)
-
Return the command definition with the given name or NULL if it was not found. The scope it was actually
found in is set in scope.
-
tag_list *get_values()
-
Return the tag_list with the list of global variables
-
void print()
-
Debug printf
-
static struct scml_scope *make_root_scope()
-
Construct an scml_scope that is suitable for use as the root scope. Basically, it just adds all the builtin
command definitions
8.2.14 scml_context
The scml_context class is used to execute scml code and actually generate the resulting output. The primary function is
format_sequence which takes a token sequence (e.g., from an scml_parser::parse()) and then executes the contents,
printing out text tokens and evaluating expressions. Execution of scml commands will create a child context for which the
command executes in, of course, if the command is implemented by a C function it can manipulate its parent context, allowing
for great flexibility.
-
void set_flags(int flags), int get_flags()
-
Set/get the flags for the context
-
void set_parent(struct scml_context *parent), struct scml_context *get_parent()
-
Set/get the parent context
-
void set_stream_pos(struct scml_stream_pos *ss), struct scml_stream_pos *get_stream_pos()
-
Set/get the scml_stream_pos object which was used in lexxing the code
-
void set_cmd_def(struct scml_cmd_definition *def), struct scml_cmd_definition
*get_cmd_def()
-
Set/get the command definition associated with this context
-
void set_lvalues(tag_list *tl), tag_list *get_lvalues()
-
Set/get the left hand side values
-
void set_rvalues(tag_list *tl), tag_list *get_rvalues()
-
Set/get the right hand side values
-
void set_scope(struct scml_scope *sc), struct scml_scope *get_scope()
-
Set/get the current scope
-
void set_indent_size(int size), int get_indent_size()
-
Set/get the indent size. This size corresponds to how much indentation there is before a macro call, so that any
preformatted text in the macro will be aligned with the beginning of the call instead completely left justified.
-
void set_offset_size(int size), int get_offset_size()
-
Set/get the offset size. This size corresponds to the difference between the start of a line and the position of
a pre command. The size is then used to determine when to start printing leading spaces in a line, thus, it is
possible to indent scml code with having the indentation affect the output.
-
int print_token(struct scml_token *st)
-
Prints out the contents of a token. The token should already have been evaluated so only tokens with literal
values should be passed to the function.
-
void print_indent(int size)
-
A simple function to print an indentation of the given size.
-
void locate_scope(struct scml_token *st, struct scml_scope **inout_scope, const char
**out_id)
-
-
int equalize_tokens(struct scml_token *dest, struct scml_token *src, int count)
-
Converts the count sized array of tokens into the simplest common type. The process is to strip all of the
tokens in src and then try to promote them all to the same type. If a common type was found, it will be
returned and dest will have the converted tokens, otherwise SCML_ERROR is returned.
-
struct scml_token eval_token(struct scml_token *st)
-
Evaluate a token expression to produce a token that has a printable value, except for commands, which have
to be handled by exec_token.
-
void token_lvalue(struct scml_token *st, tag_item **out_ti, int *out_index)
-
Get the lvalue of a token. Since all variables are really just tags, the result is a pointer to the tag_item and,
if it is an array, the index into the array.
-
int handle_params(struct scml_token *cmd)
-
Processes the SCML_NT_COMMAND token to evaluate each argument and set its value in the context.
-
void tag_type(struct scml_token *t_type, tag_data_kind *out_kind, int *out_size)
-
Determine the pres_c tag type needed to hold an scml value.
-
struct scml_cmd_definition *find_cmd(struct scml_scope **cmd_scope, struct scml_token
*st)
-
Find the command definition referenced by the token
-
void define(struct scml_context *sc)
-
Handles the define command which is used to define other commands, escapes, and variables. This is
executed on the parent context and a child context is passed in which contains all of the arguments. The
method will then pull any arguments of interest from the sc context and define the object.
-
void undefine(struct scml_context *sc)
-
Undefine an object that was previously defined.
-
void rename(struct scml_context *sc)
-
Rename an object
-
int ifdef(struct scml_context *sc, struct scml_token_sequence *sub, int defined)
-
Similar to the define method, this will handle the ifdef and ifndef builtin commands. If the defined
parameter is true then it will act like ifdef and execute the contents if the object is defined, otherwise it acts
like ifndef and executes when it is not defined.
-
struct scml_token_sequence *get_contents(struct scml_cmd_definition *def, struct
scml_token_sequence *sts, int start)
-
Return the contents of a bracketed command in a token sequence. The passed in token sequence and start
parameter indicate where the start of the command contents is located which the function then scans through
looking for the terminator. Once the terminator is located it will create a new token sequence and set it to
contain the contents.
-
struct scml_token_sequence *partition_sequence(struct scml_cmd_definition *def, struct
scml_cmd_definition *partition, struct scml_token_sequence *sts)
-
Partitions a token sequence around a given command. The passed in sequence will be adjusted to contain the
first part of the partition and a new sequence will be created with the second part. This is useful for bracketed
commands that have several different sections. For example, it can be used to separate the true and false
branches of an if test, or for the cases in a switch.
-
int exec_token(struct scml_token_sequence *sts, int *index, struct scml_token *st)
-
Execute a token, if the token is a command it will consume the tokens of the body, if it is bracketed, and then
call the handler for the command. If the token is a simple value then it will be printed.
-
int format_sequence(struct scml_token_sequence *sts)
-
Executes the commands and prints out any text in a token sequence.
-
int exec_cmd(const char *cmd_name, tag_list *locals, ...)
-
Manually execute a command. The locals argument is set as the parent tag list of the context that the
command is executed in so it is possible to get at the values. The varargs portion of the call is used to add
values directly to the context and is a formatted set of values. The first value is a string corresponding to the
name of a variable, next there is a tag_kind specifying the type of the variable, and finally a tag_data_u
which contains the value of the variable.
-
static void set_handler_table(struct scml_handler_table *sht), static struct
scml_handler_table *get_handler_table()
-
Set/get the handler table where handlers specified in defines will be found. This table is global to all contexts
so only one table needs to be constructed.
-
void print(int level)
-
Print a stack trace for debugging
8.2.15 Command Handler Functions
Any builtin commands not handled by the scml_context are handled by C functions. These functions are defined in
c/pbe/lib/scml_context.cc and then a number of scml_handlers are created for each and added to the handler table in
the scml_context.
-
int c_defvar_handler(struct scml_token *st, struct scml_context *sc)
-
Handles the defvar command by walking the child tokens of st and evaluating them and adding the tags
they create to the context.
-
int c_ignore_handler(struct scml_token *st, struct scml_context *sc)
-
Handles the ignore command by walking the list of child tokens and evaluating them, without doing anything
else. The evaluated expressions are expected to create some kind of side effect.
-
int c_scope_handler(struct scml_token *st, struct scml_context *sc)
-
Handles the scope command by changing to, or creating the scope with the given name. The parent scope is
set to the new scope and then the parent context executes contents of the scope on behalf of the child.
-
int c_macro_handler(struct scml_token *st, struct scml_context *sc)
-
Handles the macro command by creating a new command that will execute the contents of the macro when
used.
-
int c_macro_executer(struct scml_token *st, struct scml_context *sc)
-
Handles commands that have been created using the macro command. It will evaluate the arguments and then
get the macro body from the command definition and execute the scml code.
-
int c_if_handler(struct scml_token *st, struct scml_context *sc)
-
Handles the if command by evaluating children[1] of st to get a value to test for being true or false. Then
it will try to partition the contents of the command around the else command. Finally, it tests the expression
and executes the true or false branch accordingly.
-
int c_else_handler(struct scml_token *st, struct scml_context *sc)
-
Handles the else command by signalling a runtime error since the command is only meant for use as a
partition inside of an if.
-
int c_for_handler(struct scml_token *st, struct scml_context *sc)
-
Handles the for command by creating the loop variable in the context and then executing the contents for
however long has been specified.
-
int c_pre_handler(struct scml_token *st, struct scml_context *sc)
-
Handles the pre command by setting the SCF_PREFORMATTED flag and the offset of size of the contents block
in the parent before using the parent to execute the contents.
-
int c_aliascmd_handler(struct scml_token *st, struct scml_context *sc)
-
Handles the aliascmd command by building a new command with the same definition as the command
specified by the handler argument.
-
int c_include_handler(struct scml_token *st, struct scml_context *sc)
-
Handles the include command by executing the file specified by the file argument. The file is searched
for in the include directory list of the stream the current file was read.
-
int c_retarget_handler(struct scml_token *st, struct scml_context *sc)
-
Change the output file to the one specified by the `output' parameter, which should resolve to an scml_
stream.
-
int c_create_stream_handler(struct scml_token *st, struct scml_context *sc)
-
Create an scml_stream object and set its file to the one specified by the `path' argument, or stdout if path is
empty. The object will then be added as a static variable to the current scope with the name specified by the
`name' argument. This is a big hack.
8.2.16 Miscellaneous Functions
These functions are just simple helper functions to make life a little easier.
-
void scml_alert(struct scml_stream_pos *ssp, int alert_flags, const char *format, ...)
-
A printf like function for reporting errors/warnings when executing scml code. The stream position argument
is optional and currently only used to print out the column and line number of code that is causing the
problem. The alert_flags argument is a bitfield used to describe the alert. The possible flags are:
-
SAF_WARNING
-
This is a warning
-
SAF_ERROR
-
This is an error
-
SAF_INTERNAL
-
The problem is internal
-
SAF_GENERAL
-
The problem cannot be classified
-
SAF_IO
-
The problem is with I/O
-
SAF_LEXICAL
-
The problem is with lexxing
-
SAF_PARSE
-
The problem is with parsing
-
SAF_TYPE
-
The problem is with bad/inconsistent types
-
SAF_RUNTIME
-
The problem is with the execution environment
-
int init_scml()
- Does any
initialization of scml structures. Currently, this just initializes the handler table with all of the builtin command
handlers.
-
const char **scml_std_include_dirs()
- Returns
an array of strings which are paths that it can find include files. The array is the same kind that fopen_search so it
can be used directly with that functions.
-
int scml_hash_name(const char *name, int table_size)
- Produces
a hash number for name in a table the size of table_size
-
int scml_parse_cmdline_defines(struct scml_scope *root_scope, flag_value_seq *defs)
- Evaluates
the flag value sequence to create the desired scml variables. The flags are expected to be formatted strings of the
form, `var=expr', where `var' is the variable name, and expr is the typed value of the expr. If there is no expression
then a boolean variable is defined and set to true.
-
int scml_execute_str(struct scml_scope *root_scope, char *code_desc, char *code, tag_list
*tl)
- Execute
scml `code' in the `root_scope' with `tl' holding any implicit values. The `code_desc' argument is
used as the description for the scml_stream so that one can figure out what scml code caused an
error.
-
struct scml_stream_pos *scml_execute_defs_file(struct scml_scope *root_scope, const char
**include_dirs, const char *file_desc, const char *file_name)
- Execute a
file that will only define objects and not produce any output.
-
int scml_code_cast_handler(int indent, cast_handler *ch)
- A routine
for executing scml code that is encapsulated by a cast_handler object. The cast_handler is set up with this
routine as the handler and four arguments for the scml code. The arguments in order are the code description for the
scml_stream, the scml code itself, a TAG_REF encoded pointer to an scml_scope to execute in, and finally a
tag_list of implicit arguments.