This state machine was based off of the formal grammar given in readme.txt STATE MACHINE (where STATE = START, to begin with): =================================================== #0 START: ATTR_NAME="\0t", MODE=ATTR_VAL x (: PUSH x else: set STATE=NODE and jump to the start of the NODE state's logic #1 GROUP_END: x ): POP x else: throw error "missing operator" #2 NODE: x #: FLUSH, ATTR_NAME="id", MODE=ATTR_VAL x .: FLUSH, ATTR_NAME="className", MODE=ATTR_VAL x [: FLUSH, STATE=ATTR_NAME x {: CREATE, PARENT=last_node_created, ATTR_NAME="nodeValue", MODE=ATTR_VAL, STATE=TEXT -> remove "\0t" attribute, if it is active and empty x (: throw error "missing operator" x else: if MODE=null, throw error "missing operator"; CONSUME #1-2 START/NODE/GROUP_END: x W: skip character x >: if STATE != GROUP_END, CREATE; STATE=START, PARENT=last_node_created x ^: if STATE != GROUP_END, CREATE; STATE=START, PARENT=PARENT.parentNode; throw error if parent is null x +: if STATE != GROUP_END, CREATE; STATE=START x ): if STATE != GROUP_END, CREATE; STATE=GROUP_END, POP x E: if group/parentheses stack is not empty, throw error "missing parentheses" if STATE != GROUP_END, CREATE; stop parsing... #3 STRING: x "': STATE=STR_STATE x else: CONSUME x E: throw error "missing matching quote" #4 TEXT: x W: skip character }: CREATE (empty node), STATE=GROUP_END x "': STR_STATE=TEXT_WS, STATE=STRING x else: CONSUME, STATE=TEXT_WS x E: "missing closing curly brace" #5 TEXT_WS: x W: if TEXT_WS_START = 0, set TEXT_WS_START to the current length of the string builder; CONSUME x }: CREATE, STATE=GROUP_END x "': TEXT_WS_START=0, STR_STATE=TEXT_WS, STATE=STRING x else: TEXT_WS_START=0, CONSUME x E: "missing closing curly brace" #6-7 ATTR_NAME/ATTR_EQUALS: x W: skip character x ]: MODE=null, STATE=NODE x =: if STATE != ATTR_EQUALS, throw error "missing attribute name"; STATE=ATTR_VAL x "': MODE=ATTR_NAME, ATTR_STATE=ATTR_EQUALS, STR_STATE=ATTR_DEF, STATE=STRING x else: MODE=ATTR_NAME, CONSUME, ATTR_STATE=ATTR_EQUALS, STATE=ATTR_DEF x E: throw error "missing closing bracket" #8 ATTR_VAL: x W: skip character x ]E: throw error "missing attribute value" x =: throw error "invalid '=' in attribute list" x "': MODE=ATTR_VAL, ATTR_STATE=ATTR_NAME, STR_STATE=ATTR_DEF, STATE=STRING x else: MODE=ATTR_VAL, CONSUME, ATTR_STATE=ATTR_NAME, STATE=ATTR_DEF #9 ATTR_DEF: x =: if ATTR_STATE = ATTR_NAME, throw "invalid '=' in attribute list"; FLUSH, STATE=ATTR_VAL x W: FLUSH, STATE=ATTR_STATE x ]: FLUSH, MODE=null, STATE=NODE x "': STR_STATE=ATTR_DEF, STATE=STRING x else: CONSUME x E: throw error "missing closing bracket" ACTIONS: =========== CONSUME: \: skip character and add next character to end of string builder %: consume next characters that match [-\._a-zA-Z0-9], until the next "%" if you encounter a character that does not match this set, reset the character pointer and add "%" to the string builder otherwise, store the template variable and add a placeholder "" in the string builder. If the template var is anonymous, set it's stored name as "\0"+(ANON_VARS++); save the template variable's location in a hash table else: add to end of string builder FLUSH: MODE = null: do nothing MODE = ATTR_NAME: if the string builder contains no template variables, store it as the current ATTR_NAME variable; otherwise, store it in a special attribute named "\0n"+SPECIAL_COUNT; set the current ATTR_NAME to "\0v"+(SPECIAL_COUNT++); finally, store true as the temporary attribute value of ATTR_NAME, if this is the first time creating this attribute MODE = ATTR_VAL: store the string builder as the value for the current ATTR_NAME; if a value already exists for this ATTR_NAME: - if the value === true, replace the value, - otherwise, concatenate the string builder to the existing one, with a space character in between Finally, reset the string builder and set the MODE to null CREATE: First, FLUSH. if "\0t" is undefined, we know this is a text node: trim the nodeValue attribute value's whitespace -> use TEXT_WS_START as the starting point for trimming create a DOM textNode with the single nodeValue attribute otherwise, we need to create an HTML node: create a node using "\0t" as the tagname; if \0t is not a non-empty string, use "div" loop through all the attributes in the attribute hash we built: if attribute begins with "\0" (special attributes): save the entry in a cache for templating, unless the attribute is "\0t" and the value is not an array otherwise: if the value == true, use "createAttribute" to set the value of the node if the value is an array (template value), save it in a cache for doing templating otherwise, set the value of the attribute using whatever methods are available Append node to PARENT Reset SPECIAL_COUNT to 0 PUSH: Push PARENT node to a stack POP: if the stack is empty, throw an error "unmatched parenthetical"; otherwise, pop PARENT node from a stack Abbreviations: W: whitespace, [\t\n\r\v\f ] E: end of input text Parser variables: STATE: state the parser is in Note: ATTR_STATE and STR_STATE can be removed if STATE is a stack variable ATTR_STATE: state to revert to, after we finish consuming an attribute name/value (ATTR_DEF) STR_STATE: state to revert to, after we finish consuming a string PARENT: parent node we will append the next node to MODE: what type of node definition does the string builder define SPECIAL_COUNT: how many attribute names contain template variables for the current node ANON_VARS: number of anonymous variables used ATTR_NAME: the name of the attribute we will use to store the attribute value in Implied parser variables: - string builder that can hold a stack of strings - hash table to hold node attributes - variable to hold the current node's tagname - hash table to hold template variables and their usage locations - stack to hold stored PARENT nodes (for grouping/parentheses) - variable to hold which type of quote to match while in state STRING (" or ') Character Codes: *space*: 32 _: 95 a: 97 z: 122 A: 65 Z: 90 \t: 9 \n: 10 \v: 11 \f: 12 \r: 13 (: 40 ): 41 [: 91 ]: 93 {: 123 }: 125 #: 35 .: 46 +: 43 >: 62 ^: 94 ': 39 ": 34 =: 61 %: 37 \: 92 whitespace = (n == 32 || n > 8 && n < 14) template = (n == 95 || n > 64 && n < 91 || n > 96 && n < 123) Parser implementation notes: - uses charCode comparison; it is slightly faster - avoids any closure calls, as they are twice as slow - uses a string builder with String.fromCharCode To compile this class without error checking, run: script.replace(/\/\/ERR:(.|[\r\n])*?;/g, "");