Structure Reference

This is a reference manual for making structures. It covers all of the information about valid structure actions, parameters, and the specific interactions with styles.

Concepts

Structures are defined by a Lua table. Lua's table mechanism is a lot like JavaScript's similar constructs; it makes for a handy data storage format. And since it is a Lua table, it doesn't have to be "parsed;" it's just a valid Lua object. This also allows parts of tables to be used in different places, and for tables to effectively be "copied" by simply using another variable.

A structure is built as a nested series of tables. So you would define one like this:

local my_struct =
{
  { type="foo", },
  { type="bar", },
}

The contents of the base structure is an array of values. Once you have created the table, the actual data structure is created as follows:

local Structure = require "Structure"

Structure.BuildStructure(my_struct)

The return value is a special object that should be returned along with your style.

Each element of a structure is called an action. The type of action is defined by the type field. Using an invalid type will result in a build-time error (ie: BuildStructure will error). This makes it easy to do some basic verification by simply executing your structure's script. That won't verify things like whether a function exists or the correctness of parameters, but at least you know you've spelled your action types correctly.

Each type can have a number of attributes. Some attributes are specific to certain types, while others can be used anywhere.

Actions can be nested within other actions. For some actions, it generally does not make sense to nest them, while others only make sense if they contain other actions. The outer action can influence how inner actions are processed. For example, actions that iterate over something will execute their contents once per iteration. There are actions that will execute their contents only if a style-provided function returns true. And so forth.

When a structure is processed, each action will be executed in the order provided. Do note that no steps are taken to prevent infinite recursion/iteration; if your structure uses a table which uses another table which itself contains the first table, that is a legal table in Lua. Attempting to process such a structure with BuildStructure will halt with a Lua stack overflow or execute infinitely.

Context

There is a notion of a current context. The context represents all of the information currently available to an action (and therefore whatever style functions it calls). Some actions will augment the current context with new information. These take the form of parameters passed to style functions.

If an action provides some new context variable(s), then all child actions may access them. Actions that provide new context variables will provide them to all child actions contained within that action.

Some actions require certain context variables to be available. If they are not, then a runtime error will result. For example, the function iterator action iterates over all of the functions within the current extension or version (both of which are context variables). If no extension or version is available, then the action will fail to execute.

Similarly, actions cannot modify already existing context variables. So you cannot nest actions that provide the same context variable(s).

In the reference table below, there will be text that says, "must be within the scope of X." This means that the action needs to be inside an action that provides the context variable(s) X.

Current Style

When style functions need to be called, the system will use the current style to do so. The style will be the table provided to the generation system.

However, it is often convenient for the user to put different functions within tables in a style. For example, you could have a function called WriteExtVariable. When generating your header file, you want it to write an extern declaration, but in your source file, it should write it as a non-extern definition. What you can do is define a pair of sub-tables of your main style table. A table called header would have one version of WriteExtVariable, while the table called source would have another version.

Thus, you could use the same structural element to process both. Like this:

local piece =
{
  { type="ext-iter",
    { type="write", name="ExtVariable(hFile, extName, spec, options)", },
  }
}

local my_struct =
{
  { type="group", style="header"
    piece,
  }
  { type="group", style="source"
    piece,
  }
}

When the system goes to find WriteExtVariable, it will first check the most recently set sub-style. If it does not find the function there, it will check the next one. And the next. Until it comes to the main style table. If it's not there, then it errors out. Thus, every additional substyle increases the number of functions available; it never reduces them.

Indeed, the style parameter itself uses similar resolution methods. For example, you could have a second table inside the header table also given the name source (so it's full name relative to the main style is header.source). If you are inside the header style and then ask for the source style, you will get the header.source sub-style.

This is a very useful tool for making large structure construction more manageable.

Action Reference

This is a list of all of the actions and their associated attributes.

When an action says, "Provides access to the X variable," this also means that the action cannot nest with any action that provides the X variable. Including itself.

System

These actions are system-level actions. They can be used in any context, and they don't have anything to do (directly at least) with writing data or even the spec data at all.

group:

Represents a collection of actions. This has no real semantics; it's mainly used as the base table for variables that contain actions. Every table in the structure must have an action, so this is used to group them. They can also have conditional attributes and such.

filter:

Will execute its child actions only if the named style function returns true. The default parameters are () (ie: no parameters).

  • name: The base name of the function that does filtering. The full name will be prefixed by "Filter".
  • neg: If this is true, then the meaning of the return value is inverted. That is, if the function returns true, then it will not process the children. This is useful for reusing filter functions in opposite ways without having to write a new one.

context:

Adds a user-defined value to the current context. This will create a new parameter within the scope of this action that any function can reference. The default parameter list is ().

  • key: The string name of the new context variable. Required. Must end in the "_" character, to ensure that it doesn't conflict with system context variables.
  • data: A Lua value that represents the new context variable's data.
  • name: The base name of the function to call to fill in the context variable. The full name will be prefixed by "State".
  • dispose: The base name of a function that takes the context variable (and only the context variable, so no parameter selection). This will be called when the variable is disposed of. This would be for clean-up work, like for file closing and such. The full name will be prefixed by "Dispose".

Either name or data must be defined. data takes priority if both are defined.

Provides access to the parameter named by key. Note that this means you can't nest context's that provide the same variable.

call:

Calls a given style function. The default parameter list is ().

  • name: The name of the function to call. The name will be exactly as specified.

File

These actions are for dealing with file data. Creating files, writing data to files, etc.

file:

Creates a tabbed-file. See the TabbedFile.lua module to understand what this is. The default function parameters are: (basename, options)

  • name: The exact name of the style function to call that returns the full pathname of the file to create

Provides access to the hFile variable.

Note that you really need the basename variable. At the very least, you should extract the directory name from it, so that the user can provide a proper directory name. You can use util.ParsePath for this. Thus, your code should look like:

function style_name.GetFilenameFunc(basename, ...)
  local base, dir = util.ParsePath(basename)
  local filename = --compute filename here.
  return dir .. filename
end

block:

Writes the beginning of a block before executing its children, and writes the ending of a block after executing the children. The default function parameters are: (hFile, spec, options)

  • name: The base name of a pair of functions to call to write the beginning and end of the block. The block beginning function's name will be prefixed by "WriteBlockBegin"; similarly the ending function is prefixed with "WriteBlockEnd"

Must be within the scope of hFile.

write:

Calls a function to write something to the file. The default parameters to the function are (hFile, specData, spec, options).

  • name: The base name of the function that does the actual writing. The full name will be prefixed by "Write".

Must be within the scope of hFile.

blank:

Writes a blank line to the file.

Must be within the scope of hFile.

Iterators

These actions are for iterating over lists of data in the specification.

ext-iter:

Executes its children once for every extension that the user has explicitly asked to generate code for.

Provides access to the extName parameter.

version-iter:

Executes its children once for every version in the specification that is less than or equal to the one the user asked for. If we are processing a specification that doesn't have any versions (ie: isn't OpenGL), then none of the children will be executed.

Provides access to the version parameter.

sub-version-iter:

Execute its children once for every version less than or equal to the current version parameter.

Must be within the scope of version. Provides access to the sub_version parameter.

This is useful for generating a group of functions or files that do something for each version, and for each version of OpenGL <= that version. For example, you can have a bunch of headers that only provide functions/enums for features introduced within that version. Then, you can generate a file for each version, which includes headers only for the contents of everything up to that version of OpenGL and not just within it.

enum-iter:

Executes its children once for every enumerator within scope. "Scope" is defined by the extName and/or version parameters. If extName is available, then it will iterate over all enums in the extension. If version is available, then it will iterate over all enums that were introduced within that particular version (and only them). If both are available, extName takes priority.

Must be within the scope of extName or version. Provides access to the enum and enumTable parameters.

func-iter:

Executes its children once for every function within scope. Scope is defined as for enum-iter.

Must be within the scope of extName or version. Provides access to the func parameters.

enum-seen:

This is a special action which records which enumerators were iterated over within its scope. Every time an enum-iter finishes processing an enumerator, the enumerator name that was processed will be recorded in enumSeen. That allows the user to be able to detect if this is the first time (inside this scope) that the enum was processed. This helps styles execute statelessly.

To see if an enumerator was processed already, use enumSeen[enum.name]. The value stored there will be a string containing the extension name or the version that it was most recently seen within.

Provides access to the enumSeen parameter.

Note that if you place a filter within an enum-iter block that is in an enum-seen scope, the filter's active/inactive status will not affect whether enumSeen will be updated. It doesn't matter if nothing was written; the enumerator being iterated over is enough.

If that's not good enough, if you need the filter mechanism to be respected, you can use context and call actions to create the equivalent. The context would create some key as a new table, and the call would be used to update the table with an enumerator.

func-seen:

Works like enum-seen, except for functions.

Provides access to the funcSeen parameter.

Common Attributes

Actions can have attributes. What follows is a list of attributes that can be used in any action (or at least in a lot of them).

name:

Part or all of a function name to call. The name, when possibly augmented with action-specific text, will used to fetch a function from the various styles and sub-styles as stated in the Current Style section. If the name is followed by a parenthesized list of parameters, then these parameters will be used in that order to call the function(s). If a particular parameter is not available in this context, a runtime error will occur. Each action that defines a name also defines a default set of parameters for functions that don't provide their own parameter list.

style:

Adds a new sub-style scoping. It fetches the style table using the rules specified above in the Current Style section.

optional:

Normally, the inability to resolve a function name to a function will result in an error. If this attribute is set to true however, then the failure to resolve a function will simply mean that the function is not called. All children are processed as though the function had been called normally, UNLESS it is a filter action. In that case, the filter will be assumed to have returned false, meaning that children will not be processed (unless neg is also set, in which case it will flip that into true, thus always processing children if the function isn't present).

first:

If it is set to true, this action (and any of its children) will only be processed the first time through the most recent iteration loop. This only works with the most recent iteration loop.

The interaction with filters can play havoc with this. first will only work when it is the numerically first time the node is seen within the most recent iterator. If a filter filters out the first time through, then the first action and its children will never be executed.

last:

If it is set to true, this action (and any of its children) will only be processed the last time through the most recent iteration loop. The same caveats apply with respect to filters.

value:

If this is set, then the given Lua value will be passed to any functions called by this action as the value parameter. Note that this parameter is not inherited. This parameter is not given to any child actions; only the action this value is set on will have access to this value.

The value must be a string. However, to allow the string to use various names defined by the system, the string can have special codes in it. Any use of "%" in the string will denote the beginning of a special name. Thus, you shouldn't do things like this: "some%string," this will make the system think that %string is a special name. If you use "%" by itself, with a space or non-identifier characters after it, then it will remain a % sign. So "some% string" is fine, as is "some%-string".

The special names are just context variable names, only a limited subset of them. Of the standard context variables, the only ones you can use are those which are naturally strings (like version and extName) or those which are reasonably convertible to strings (enum would return it's basic string name, with no prefixing).

The main purpose of this is to be able to print messages easily, like comments that say that extension X's declarations begin here.

User-defined context variables can also be used, but they must be either strings or tables. If they are tables, then they either must have a __tostring metamethod, or they must have a member function called _ValueResolve function. This function only takes the table as a parameter.

cond:

These are special conditionals that act like single-action filters. Unlike regular filters, they are pre-defined by the system. When they are false, the current action and its children will not be processed.

There are a fixed set of conditions. Most of them match iterator names. For these, they are considered to pass if the corresponding iterator would execute at least once. Also, since they are based on iterators, they can only be used in the same context that their corresponding iterators can:

  • ext-iter
  • version-iter
  • core-ext-iter
  • core-ext-cull-iter
  • enum-iter
  • func-iter
  • core-funcs: Returns true if the spec has core functions/enumerators at all. Basically, checks if the specification is OpenGL and not WGL or GLX. Technically version-iter would do the same job, but in a less obvious way.

Standard Context Variables

Here are a list of the various standard context variables, with references to the actions that provide them.

Again, DO NOT MODIFY THEM! You can call member functions on them and inspect them. But unless they're user-defined parameters, do not directly change their tables.

specData:

This is the entire data containing every enumeration, typedef, function, etc for the specification (OpenGL, WGL, GLX). It is a massive struct, and it's format is complex.

You can see what specData looks like by reading glspecs/glspec.lua. specData is derived from this table, but with some modifications. The modifications are detailed in the comments at the top of modules/LoadLuaSpec.lua. They're primarily convenience stuff, to make it easier to find enums and functions by name and so forth.

Normally, you should not need to look at this data structure directly. So include it in a parameter list sparingly.

This parameter is always available.

spec:

This is a struct containing functions used to get specification-specific strings. This allows the style of writing to be mostly independent of things like whether it is writing to OpenGL or WGL. For example, if you have the base name of an enumeration, and you want to prefix it with the spec-defined prefix for enumerations, you do this:

spec.EnumNamePrefix() .. enumName

There are a number of spec functions that return prefix strings or other spec-based strings. You can find these in the modules/Specs.lua file, with a comment stating what each one returns.

There are also some functions that provide general information: list of versions, list of core extensions, a string containing the definitions for the OpenGL function to load function pointers, etc. These lists are all stored in the data directory, using the file format data/<specName>_spec<data>.lua.

This parameter is always available.

options:

The options data structure. It contains the options processed from the command-line. Among these is extensions, the list of extension names that are being explicitly written. You can tell which spec is being used with options.spec, but it's better to rely on the spec parameter as seen below.

This parameter is always available.

basename:

The base filename provided by the user, relative to the current working directory. You will need this to generate files in the directory the user wants for file action types.

This parameter is always available.

value:

A user-defined value. This parameter is only available if the action itself has the value attribute. Child actions of actions that have value attributes will not inherit them.

hFile:

A TabbedFile. This is a special kind of Lua IO file. It supports all of the Lua IO methods (DO NOT CLOSE IT!), but it's designed to handle indentation. As such, it extends the Lua IO with specialized routines to auto-indent individual lines. This allows the system to determine the indention to use based on command-line options. The assumption with almost all of the writing functions is that each write is an individual line.

This parameter is provided by the file action.

Note that you must use it with Lua's member calling conventions (hFile:write, for example). If you try to use io.write, things will break.

hFile:inc and hFile:dec are functions that increment/decrement the current indention level. The indention can be preserved with hFile:push and restored with hFile:pop.

The TabbedFile also offers the ability to do string formatting directly into the write. hFile:fmt takes a format string and some parameters, forwards the parameters to string.format, then writes that string as a line.

There are block writing functions, hFile:writeblock and hFile:fmtblock. These functions will split the written string into individual lines and indent each one.

If you don't want indented writes, then use hFile:rawwrite and hFile:rawfmt.

extName:

The base name of an extension. Usually paired with specData, as the name alone isn't terribly useful. You can get the list of enumerators and functions defined by this extension with specData[extName].enums/funcs. These are not named of enumerators and functions; they're the actual part of the specData that defines everything about that enum/func.

This parameter is provided by the ext-iter, core-ext-iter, and core-ext-cull-iter actions.

version:

A string (remember this) that contains the version of interest. Since it's a string, you need to apply tonumber to it to get a proper number.

This parameter is provided by the version-iter action.

sub_version:

A string (remember this) that contains a version between the first version and the current version version.

This parameter is provided by the sub-version-iter action.

enum:

This is an enumeration. Not the name of an enumeration; the enumeration itself. It contains the name enum.name, but it also contains versioning information and so forth. It is an entry from the specData.enumerations table.

This parameter is provided by the enum-iter action.

If you want to get the value of an enumerator, you cannot simply use enum.value. You need the enum and the enumTable (see below). Then, you use common.ResolveEnumValue, where common is the CommonStyle module table.

enumTable:

A table of enums, indexed by enumeration name. It comes from specData.enumtable.

This parameter is provided by the enum-iter action.

func:

This is a function. As with enum, it is not merely the name of a function (that's func.name); it is the function itself. It is an element taken from the specData.funcData.functions array. It contains many properties of a function.

This parameter is provided by the func-iter action.

If you want to get the parameters (C-style) in a function, you can use common.GetFuncParamList, which requires a typename (see below). Otherwise, you would have to deal with the many difficulties of pulling a viable parameter list from a func.

enumSeen:

A table indexed by enumerator name. If an entry is present, then the enumerator was already seen at least once before.

This parameter is provided by the func-seen action.

funcSeen:

A table indexed by function name. If an entry is present, then the function was already seen at least once before.

This parameter is provided by the func-seen action.

Common Structure

The module CommonStruct represents reuseable components for your structure. You can use them as you see fit. Each of the following is a function within the CommonStruct table. These are useful for more complex iteration mechanism than what the standard structure iterators provide. They call functions with specific names, but you can use the style scoping mechanism to put the right styles into position to make them work.

Extensions:

This function takes no arguments and returns a group of actions will iterate over every extension the user asked to export. For each extension, it will call a function named WriteExtension(hFile, extName, spec, options) to write each component.

Obviously, since it is writing things, it needs to be used within a file action. Also, since it uses the ext-iter action, it cannot be used within an ext-iter action.

Enumerators:

This function takes no arguments and returns a group of actions that will iterate over every enumerator in every extension and version that the user asked to export. This will respect core/compatibility. For each enumerator, it will call a function named WriteEnumerator(hFile, enum, enumTable, spec, options, enumSeen) to write each enumerator.

It will first iterate over the enumerators in extensions that the user asked for. Each extension will be in its own group, optionally writing a header for each using WriteSmallHeader(hFile, value, options) (so if you wish to suppress the header, make this an empty function on the current style). Then it will iterate over all of the versions. For each version, it will iterate over any core extension enums that were not explicitly requested (writing a header for each). Then it will iterate over that version's non-core-extension enums.

This must be used within the scope of hFile, and since it uses extension, version, and enumerator iterators, it can't be in scope of any of those. It also uses enum-seen.

Functions:

This function optionally takes one argument and returns a group of actions that will iterate over every function in every extension and version that the user asked to export. This will respect core/compatibility. For each function, it will call a function named WriteFunction(hFile, func, spec, options, funcSeen) to write each function.

It will iterate over functions in the same group order as with enumerators. It will write headers where appropriate, which can be omitted.

This must be used within the scope of hFile, and since it uses extension, version, and function iterators, it can't be in scope of any of those. It also uses func-seen.

Updated

Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.