Skip to main content

%Compiler.Util.Flo

FLO This class can be instantiated to produce an object that is useful for code generation. The object contains a graph (the flo graph) where the root node of the graph represents a code generation 'task'. Each node has code a code attribute and a block type attribute. The block type can be 0 (none), 1 (conditional block), or 2 (loop). Conditional and Loop nodes also have a block terminator to support various styles of looping (do...while, while...., for... etc). Flo also maintains a node stack and a symbolStack. The top of the node stack is always the current node and any code lines generated are placed in the current node unless otherwise specified. This allows the code generator to optionally maintain "bookmarks" so that code can be generated 'out of order'. Each node in a flo may also own symbol tables. Some code generators need to push a new symbol table onto a stack, for example, nested procedure calls might require that a new symbol table be created. The stack of symbol tables (symbolStack) is just a stack of nodes that need new a new symbol 'scope'. Symbols defined at higher stack levels are always visible to lower levels (higher in the symbol stack) but higher nodes or sibling nodes cannot see the symbols in siblings or lower nodes. Code generation is handled by calls to the queueCode method. Each line of code to be generated is passed to queueCode and it is placed at the end of the node's code attribute. If the node is specified (bookmarks) then the code is placed in that node, otherwise the current node (top of the nodeStack) is used. When flo is asked to dequeue the code value the first thing that is done is to 'compile' the symbol tables. Each symbol may contain references to other symbols. Each symbol table is processed using a topological sort to resolve nested symbol references. Then the flo graph is traversed using bfs traversal and the code attribute from each node is output. Each code line is scanned and any symbol references are resolved before it is output. The sequence in which the code is output is such that the code attribute of the current node is always output prior to that of its children. That means that bookmarked code generation will always preceed the code in the child nodes. When this is not desired it is necessary to branch the graph. 'Branching the graph' pops two nodes from the node stack, creates a new node that is a sibling of the second node popped and pushes that sibling onto the node stack. The effect is that code generated to the current node will then be placed in a sibling and will occur in the output code after the children of the original node. This branching occurs automatically when the node stack is popped and the new current node (top of the nodeStack) already has children. ###; TODO: an undesirable side effect of this automatic branching is that the new sibling node cannot see the symbol tables in the original node. that is only a problem if that node is a 'new scope' node and is currently on the symbolStack. For now the problem is handled by automatically creating a new child node of every 'new scope' node. That child is only popped when the end of the new scope is reached and then two nodes are popped so that any automatic sibling branching occurs above the new scope node. The use of symbols by the code generator can greatly reduce the complexity of the generator but care should be used. Symbols should be used when the definition of the symbol is not known to all places where the symbol is referenced. For example, if a code generator needs to reference some object whose implementation is not know then define a symbol for the object along with the implementation. All other references to the object in generated code simply reference the symbol.

Property Inventory

Method Inventory

Properties

property codeContextStack as %Integer;
Property methods: codeContextStackDisplayToLogical(), codeContextStackGet(), codeContextStackIsValid(), codeContextStackLogicalToDisplay(), codeContextStackNormalize(), codeContextStackSet()
property currentNode as %Integer;
Property methods: currentNodeDisplayToLogical(), currentNodeGet(), currentNodeIsValid(), currentNodeLogicalToDisplay(), currentNodeNormalize(), currentNodeSet()
property defaultContext as %String [ InitialExpression = "p" ];
Property methods: defaultContextDisplayToLogical(), defaultContextGet(), defaultContextIsValid(), defaultContextLogicalToDisplay(), defaultContextLogicalToOdbc(), defaultContextNormalize(), defaultContextSet()
property errorQueue as %Integer;
Property methods: errorQueueDisplayToLogical(), errorQueueGet(), errorQueueIsValid(), errorQueueLogicalToDisplay(), errorQueueNormalize(), errorQueueSet()
property flodata as %String [ MultiDimensional ];
Property methods: flodataDisplayToLogical(), flodataGet(), flodataIsValid(), flodataLogicalToDisplay(), flodataLogicalToOdbc(), flodataNormalize(), flodataSet()
property nodeStack as %Integer;
Property methods: nodeStackDisplayToLogical(), nodeStackGet(), nodeStackIsValid(), nodeStackLogicalToDisplay(), nodeStackNormalize(), nodeStackSet()
property queuedSymbolQ as %Integer;
Property methods: queuedSymbolQDisplayToLogical(), queuedSymbolQGet(), queuedSymbolQIsValid(), queuedSymbolQLogicalToDisplay(), queuedSymbolQNormalize(), queuedSymbolQSet()
property symbolStack as %Integer;
The symbolStack is a stack of nodes for symbol scoping. BEGIN establishes a new symbol scope and every END pops to the previous scope. Users of FLO need to push/pop the symbolStack when needed by providing an indicator when pushing a new node onto the nodeStack.
Property methods: symbolStackDisplayToLogical(), symbolStackGet(), symbolStackIsValid(), symbolStackLogicalToDisplay(), symbolStackNormalize(), symbolStackSet()
property topNode as %Integer;
Property methods: topNodeDisplayToLogical(), topNodeGet(), topNodeIsValid(), topNodeLogicalToDisplay(), topNodeNormalize(), topNodeSet()

Methods

method DequeueFlo(intNode, strTabs, code, procBlock)
method ExtractSymbolDependency(referenceContext, expr, ByRef dependency) as %Status
method LookupSymbol(ByRef intNode As %Integer, strContext As %String, strSymbol, ByRef found, ByRef resolveOperator) as %CacheString
Search the FloGraph for a symbol defined at intNode or a higher node. If found, then found is returned as true and the symbol definition is returned.
method ResolveCodeLine(expr, sourceLocation, intNode, strContext, ignoreSymbolError, resolved) as %Status
ResolveCodeLine() parses source for symbol references. Any references are replaced the the definition from the compiled symbol table. The compiled symbol table for the requested context is search and, if found, the symbol value replaces the symbol reference in the source line. If the symbol cannot be found then the symbol reference is replaced with an {UNRESOLVED:} message. There is also a resolveOperator that controls how the replacement proceeds. If the resolveOperator is "C" then the resolved symbol value is spliced into the source line as a constant. Otherwise, the symbol is literally replaced with the symbol value.
method addHostVariable(hostVar As %CacheString = "", symbol As %String = "", symbolContext As %String = "", node As %Integer = "", type As %CacheString = "", ByRef symbolFound As %Integer) as %String
addHostVariable() This method adds a new host variable to a symbol context at a given node. If the symbol that this host variable is 'bound' to already exists then nothing is done and symbolFound is returned as true. Otherwise, the host variable is added and bound to the requested symbol. If the host variable name supplied is not unique then it is mangled until a unique name is found. If no hostVar is specified then a default hostVar root name of zzcv is used.
method addNode(parent As %Integer, blockType As %Integer = 0, blockEnd As %CacheString = "", pNodeType As %String = "%node", pBlockLink As %Integer = "") as %Integer
addNode() This method adds a new node to the floGraph. Normally, pushNode is the only caller of this method but some code generators might want to exert some control over the graph structure. Calling this method directly places the responsibility for the code sequence in the hands of the caller.
method addSymbol(node As %Integer = "", symbolContext As %String = "", symbol As %String, expression As %CacheString, type As %CacheString = "", resolveOperator As %String = "") as %Integer
addSymbol() Non-recursive function to add a symbol if not already defined
method branchNode(nodeCount As %Integer = 1, blockType As %Integer = 0, pNodeType As %String = "") as %Integer
branchNode() Pop nodeCount nodes off the node stack. Push a new node that is a child of the resulting top node onto the stack. Make it a block if blockType is TRUE.
method compileSymbols(intNode)
classmethod getPublicSymbolType(symbolContext As %String, symbolName As %CacheString) as %CacheString
method getSymbolType(symbolContext As %String, symbolName As %CacheString, symbolNode As %Integer) as %CacheString
classmethod lookupPublicSymbol(strContext, symbolName As %CacheString, found, ByRef resolveOperator) as %CacheString
method nodeInBlock(node As %Integer = "", pBlockType As %Integer = "") as %Boolean
nodeInBlock() returns TRUE if the current node is subordinate to a block of type = PBlockType.
method nodeInLoop(node As %Integer = "") as %Boolean
nodeInLoop() returns TRUE if the current node is subordinate to a LOOP block type.
method nodeInNoReturnValue(node As %Integer = "") as %Boolean
nodeInNoReturnValue() returns TRUE if this node is in a block type that does not support a QUIT with value.
method popCodeContext() as %Integer
popCodeContext() restores the currentNode pointer to the node at the top of the i%codeContextStack stack.
method popNodes(nodeCount As %Integer = 1) as %Integer
popNodes() Pop nodeCount nodes off the node stack. If the new top node has children then add another child to that node, pop it from the stack and push the new child onto the stack.
method popSymbolStack(nodeCount As %Integer = 1) as %Integer
popSymbolStack() Pop the symbol stack. This is typically done when the end of the current symbol scope is reached. Note that problems can occur when branching occurs at odd times - either automatic because of sibling nodes and code sequencing conflicts or with manual branching.
method pushCodeContext(pCodeNode As %Integer = 0) as %Integer
pushCodeContext() is an interesting method. Think of a code as a stream that is flowing into a single node in Flo. All node switching methods are about parent of the current node or children of the current node. What pushCodeContext() does is to redirect the code stream into a Flo node that might not have any connection to the currentNode. The currentNode is pushed onto the codeContext stack so that popCodeContext can restore the code stream to the prior codeContextStack node. WARNING - be careful with CodeContext as pushNode/branchNode etc. do not honor the code context stack. They push/pop the node stack directly.
method pushNode(blockType As %Integer, blockEnd As %CacheString = "", pushSymbolStack As %Integer = 0, pNodeType As %String = "%node") as %Integer
pushNode() creates a new node in the Flo that is a child of the currentNode. The newly pushed node becomes the currentNode. if pushSymbolStack is TRUE then the symbol stack is also pushed so the symbols defined at this node level are not visible to Flo nodes higher in the tree.
method pushSymbolStack(tNode As %Integer = 0) as %Integer
pushSymbolStack() Push the symbol stack.
method queueCode(code As %CacheString, sourceLocation As %CacheString = "")
queueCode() This method adds a code line to the current floNode
method queueCodeArray(ByRef code As %CacheString, sourceLocation As %CacheString = "")
queueCodeArray() This method adds a code line to the current floNode
method queueCodeToBookMark(bookMark As %Integer, code As %CacheString, sourceLocation As %CacheString = "")
queueCodeToBookMark() This method adds a code line to a book marked node. Note that code queued to a node that has children will appear in the dequeued code stream before any code dequeued from those children.
method queueSymbolRequest(symbolContext As %String = "", symbolName As %String)
queueSymbolRequest() Place a symbol in the deferred symbol Queue for later definition. The symbol is actually copied from some other place (higher in the graph) to the requestNode. The idea is that the higher definition is unresolvable, unreachable or maybe it would resolve to a different expression if not defined in the requestNode.
method setSymbolExpression(node As %Integer = "", symbolContext As %String = "", symbol As %String, expression As %CacheString) as %Boolean
setSymbolExpression() Non-recursive function to set a symbol's expression. Symbol must already be defined
classmethod strrep(str, replace, with) as %CacheString
method visit(pNode As %Integer, pAOV As %Integer, pStack As %Integer, pSymbolId As %String(MAXLEN="")) as %Status

Inherited Members

Inherited Methods

FeedbackOpens in a new tab