// Copyright (c) 1996-1999 Microsoft Corporation ------ state of the system --------- Each parsing of an open brace should change the state of the system. As a feature, option, switch(feature), case(option) or other construct is parsed this is noted. this construct stack allows us to select the appropriate context for parsing: is this a local keyword to this option (construct) or is it EXTERN:? What tree structure should be built for this construct? then the parsing code should continue parsing tokens as normal. There will be tables that state what the local keywords are for each situation. constructs: UIGroup, Feature, Option, Switch, Case, Commands, Font, OEM state stack: each state is of the form: state / symbol The stack is empty at the root level. state allowed transitions may contain root UIGroup any global attributes Feature Switch Commands Font OEM UIGroup Feature none UIGroup Feature Switch feature attributes Options Switch Case none Options Switch option attributes relocatable Global Attributes Case Switch relocatable local Attributes of immediately enclosing construct outside of Switch. relocatable Global Attributes Commands none command attributes ShortCommands none cmdName:invocation Font none font attributes OEM none oem attributes Note: Commands and Fonts are considered relocatable Global Attributes Tables: root attributes (divide into relocatable and non) feature attributes () option attributes () command attributes font attributes oem attributes Tables of allowed transitions: Rules: how to construct a tree and where to plant the tree for a local or global attribute ---- implementation of this state machine ------- typedef enum {CONSTRUCT, LOCAL, GLOBAL, INVALID_CONSTRUCT, INVALID_LOCAL, INVALID_GLOBAL, INVALID_UNRECOGNIZED, COMMENT, EOF } keywordClass ; GroundState() { STATE StateStack[] ; for(1) { extract Keyword(keyword) class = ClassifyKeyword(keyword) switch (class) { case (CONSTRUCT): parseSymbol(symbol) ; parseOpenBrace() ; // somewhere we need to register symbol // and allocate memory for structure // and return ptr or index to new // or existing structure changeState(keyword) ; case (COMMENT): absorbCommentLine() ; case (LOCAL) : ProcessLocalAttribute(keyword) ; case (GLOBAL) : ProcessGlobalAttribute(keyword) ; case (SPECIAL) : ProcessSpecialAttribute(keyword) ; case (EOF) return(1); default: ErrorHandling() ; } if(rc == FATAL) return(1); } } class = ClassifyKeyword(keyword) { if(commentline) return(COMMENT) ; if(EOF) return(EOF) ; The current state determines which sets of keywords are allowed. state = DetermineCurrentState() implement this table: for each state there is a list of all the keywords arranged in a fixed order (by keyword ID) each keyword is assigned a classification: Valid Constructs InValid Constructs Valid Local Attribute InValid Local Attribute Valid Global Attribute InValid Global Attribute Valid Special Attribute InValid Special Attribute if(keyword not found it table) return(INVALID_UNRECOGNIZED) ; return(classTable[keyword][state]) ; } typedef enum {ROOT, UIGROUP, FEATURE, SWITCH, OPTIONS, CASE_ROOT, CASE_FEATURE, CASE_OPTION, COMMAND, SHORT_COMMAND, FONT, OEM, any other passive construct} STATES ; Sample Table KEYWORD STATES ----> *Command *UIGroup *Switch *CaseRoot *CaseOption *Font *Root *Feature *Options *CaseFeature *ShortCmd *OEM UIGroup : VC VC IC IC IC IC IC IC IC IC IC IC Feature : VC VC IC IC IC IC IC IC IC IC IC IC Switch : VC IC VC IC VC VC VC VC IC IC IC IC Options : IC IC VC IC IC IC IC IC IC IC IC IC Case : IC IC IC VC IC IC IC IC IC IC IC IC Command : VC IC IC IC VC VC VC VC IC IC IC IC Font : VC IC IC IC VC VC VC VC IC IC IC IC OEM : VC IC IC IC IC IC IC IC IC IC IC IC UIConstraints : IS IS VS IS VS IS IS IS IS IS IS IS note: UIConstraints appearing in a Feature is treated differently than appearing under Options. The processing of UIConstraints causes one, two or many elements to be added to the Constraints Array. This is in stark contrast to normal keywords hence the classification of Special. state stack: each state is of the form: state / symbol DetermineCurrentState() { // this state is only used to determine // which catagories of keywords are // assigned which TYPES in ClassifyKeyword(). if(CurState == 0) return(ROOT) ; // No further processing needed. return(stateStack[CurState - 1].state) ; } changeState(keyword, symbol, mode) { // Change needed: shortcut *Command // does not initiate a state change. This should // be treated as a special keyword. // mode determines if the *Command keyword // introduces a normal command construct or // the short version. switch(keyword) { case (*UIGroup): addState(UIGROUP, symbol); case (*Feature): addState(FEATURE, symbol); case (*Switch): addState(SWITCH, symbol); case (*Option): addState(OPTIONS, symbol); case (*Font): addState(FONT, symbol); case (*OEM): addState(OEM, symbol); case (*Command): { if(mode == short) addState(SHORT_CMD, symbol); else addState(COMMAND, symbol); } case (*Case): { if(stateStack[CurState - 2].state == ROOT || stateStack[CurState - 2].state == CASE_ROOT) addState(CASE_ROOT, symbol); if(stateStack[CurState - 2].state == FEATURE || stateStack[CurState - 2].state == CASE_FEATURE) addState(CASE_FEATURE, symbol); if(stateStack[CurState - 2].state == OPTIONS || stateStack[CurState - 2].state == CASE_OPTIONS) addState(CASE_OPTIONS, symbol); } } } // these two functions will grow an appropriate // tree for each keyword based on the StateStack // and plant the tree in the appropriate attribute // field in the appropriate structure, (index) etc. // or add a branch to an existing tree, // and set the value at the node of the tree. ProcessLocalAttribute(keyword) ; ProcessGlobalAttribute(keyword, lpvalue) { locate entry in Dglobals corresponding to keyword. OR the current branch (constructed using the state stack) with any existing tree pointed to by entry in Dglobals. if this branch exists in the existing tree, locate the offset in heap and overwrite that by (lptype)(lpRef + attributeTree[i].offset) = (lptype)lpvalue ; otherwise offset = lpCurHeapPos - lpRef ; lpCurHeapPos += sizeof(datatype) ; Note: an attribute tree should not be constructed piecemeal. It is an error if the tree is subsequently redefined/elaborated using a different feature nesting order. } may need to keep a table for each keyword with the following info per keyword. keyword, datastructure, offset within datastructure, sizeof(data)