/*description: { Parses an XSL file, building a parse tree that conforms to the following logic structure: \begin{itemize} \item \TODO\ \end{itemize} It recognizes some XSL elements (not all yet), such as: \begin{itemize} \item \samp{xsl:template} \item \samp{xsl:foreach} \item \samp{xsl:value-of} \item \samp{xsl:sort} \item \samp{xsl:when} \item \samp{xsl:choose} \item \samp{xsl:otherwise} \end{itemize} XPath is parsed completely according to the specifications for \samp{XSLT 1.0}. If you encounter bugs or limitations, please send a mail to \email{\WebSite\ }. } */ XSLparser ::= #ignore(XML) [ "" ]? #continue '<' IDENTIFIER:"xsl" ':' IDENTIFIER:{"stylesheet", "transform"}:sTag XMLAttribute("", "version") XMLAttribute("xmlns", "xsl") '>' => insert this.type = "stylesheet"; [XMLContent(this)]* "' #empty ; XMLAttribute(sExtension : value, sName : value) : value ::= [ #check(sExtension) IDENTIFIER:sID #check(sExtension == sID) ':' ]? IDENTIFIER:sID #check(sName == sID) '=' #continue #readCString:XMLAttribute ; XMLContent(myXSLNode : node) ::= !['<' '/' IDENTIFIER:"xsl"] #!ignore [ ![#ignore(XML) '<' '/' IDENTIFIER:"xsl"] [ ~[ '<' #ignore(XML) ['/']? IDENTIFIER:"xsl" ] ]*:sText => if sText { local iIndex = findLastString(sText, "\n"); if $iIndex >= 0$ { increment(iIndex); local sRemains = subString(sText, iIndex); trim(sRemains); if !sRemains set sText = leftString(sText, iIndex); } pushItem myXSLNode.content = sText; } => local sExtension; [ '<' #ignore(XML) IDENTIFIER:"xsl" #continue ':' IDENTIFIER:sName XMLElement<"xsl:" + sName>(myXSLNode) ]? ]+ ; XMLElement<"xsl:template">(myXSLNode : node) ::= #continue => pushItem myXSLNode.content; => localref myTemplate = myXSLNode.content#back; => insert myTemplate.type = "template"; [#insert(myTemplate.match) XPath("match", myTemplate.match)]? '>' [XMLContent(myTemplate)]? '<' '/' "xsl" ':' "template" '>' ; XMLElement<"xsl:for-each">(myXSLNode : node) ::= #continue => pushItem myXSLNode.content; => localref myForeach = myXSLNode.content#back; => insert myForeach.type = "for-each"; XPath("select", myForeach.select) '>' [XMLContent(myForeach)]? '<' '/' "xsl" ':' "for-each" '>' ; XMLElement<"xsl:value-of">(myXSLNode : node) ::= #continue => pushItem myXSLNode.content; => localref myValueOf = myXSLNode.content#back; => insert myValueOf.type = "value-of"; XPath("select", myValueOf.select) '/' '>' ; XMLElement<"xsl:sort">(myXSLNode : node) ::= => if myXSLNode.type != "for-each" && myXSLNode.type != "apply-templates" error("'xsl:sort' is valid under 'for-each' or 'apply-templates' only"); #continue XPath("select", myXSLNode.sort) '/' '>' ; XMLElement<"xsl:choose">(myXSLNode : node) ::= #continue '>' => pushItem myXSLNode.content; => localref myChooseNode = myXSLNode.content#back; => insert myChooseNode.type = "choose"; XMLContent(myChooseNode) => if !existVariable(myChooseNode.when) error("'xsl:when' expected in 'xsl:choose'"); => if !existVariable(myChooseNode.otherwise) error("'xsl:otherwise' expected in 'xsl:choose'"); '<' '/' "xsl" ':' "choose" '>' ; XMLElement<"xsl:when">(myXSLNode : node) ::= => if myXSLNode.type != "choose" || existVariable(myXSLNode.when) error("unexpected tag 'xsl:when'"); #continue XBooleanExpr("test", myXSLNode.test) '>' [XMLContent(myXSLNode.when)]? '<' '/' "xsl" ':' "when" '>' ; XMLElement<"xsl:otherwise">(myXSLNode : node) ::= => if myXSLNode.type != "choose" || existVariable(myXSLNode.otherwise) error("unexpected tag 'xsl:otherwise'"); #continue '>' [XMLContent(myXSLNode.otherwise)]? '<' '/' "xsl" ':' "otherwise" '>' ; //--------------- // lexical tokens //--------------- IDENTIFIER ::= #!ignore #readIdentifier [['-' | '.'] #readIdentifier]*; ///////////////////////////////////////////////////////////////////////////////// // XPath ///////////////////////////////////////////////////////////////////////////////// XBooleanExpr(sAttribute : value, expr : node) ::= XPath(sAttribute, expr); XPath(sAttribute : value, expr : node) ::= #readIdentifier:sAttribute '=' #continue => local cEnd; ["\"":cEnd | "'":cEnd] XPathExpr(expr, cEnd) #readText(cEnd) ; XPathExpr(expr : node, env : node) ::= expression(expr, env); //-------------- // Location path //-------------- locationPath(expr : node, env : node) ::= absoluteLocationPath(expr, env) | #insert(expr.right) relativeLocationPath(expr.right, env) => insert expr.type = "/"; => insert expr.left.type = "current"; ; absoluteLocationPath(expr : node, env : node) ::= '/' [ #insert(expr.right) relativeLocationPath(expr.right, env) => insert expr.type = "/"; => insert expr.left.type = "root"; | => insert expr.type = "root"; ] | "//" #continue => insert expr.type = "//"; => insert expr.left.type = "root"; relativeLocationPath(expr.right, env); relativeLocationPath(expr : node, env : node) ::= step(expr, env) [ ['/' | "//"]:sOperator #continue => slideNodeContent(expr, left); => insert expr.type = sOperator; step(expr.right, env) ]* ; step(expr : node, env : node) ::= =>local bAttribute; [ axisName:sAxis "::" => insert expr.type = "axis"; => insert expr.axis = sAxis; | '@' => set bAttribute = true; ]? nodeTest(expr, env) => if bAttribute insert expr.is_attribute = true; [#pushItem(expr.predicates) predicate(expr.predicates#back, env)]* | ".." => insert expr.type = ".."; | '.' => insert expr.type = "."; ; nodeTest(expr : node, env : node) ::= nameTest(expr, env) | nodeType:sNodeType '(' ')' => insert expr.type = "nodeType"; => insert expr.nodeType = sNodeType; | "processing-instruction" '(' #continue => insert expr.type = "test-p-i"; literal ')' ; nameTest(expr : node, env : node) ::= '*' => insert expr.type = "*"; | NCName:sName ':' '*' => insert expr.type = ":*"; => insert expr.namespace = sName; | QName:sName => insert expr.type = "name"; => insert expr.name = sName; ; //------------ // Expressions //------------ predicate(expr : node, env : node) ::= '[' #continue #ignore(blanks) => insert expr.type = "[]"; expression(expr.predicate, env) ']'; expression(expr : node, env : node) ::= orExpr(expr, env); orExpr(expr : node, env : node) ::= andExpr(expr, env) [ #readIdentifier:"or" #continue => slideNodeContent(expr, left); => insert expr.type = "or"; andExpr(expr.right, env) ]*; andExpr(expr : node, env : node) ::= equalityExpr(expr, env) [ #readIdentifier:"and" #continue => slideNodeContent(expr, left); => insert expr.type = "and"; equalityExpr(expr.right, env) ]*; equalityExpr(expr : node, env : node) ::= relationalExpr(expr, env) [ ['=' | "!="]:sOperator #continue => slideNodeContent(expr, left); => insert expr.type = sOperator; relationalExpr(expr.right, env) ]*; relationalExpr(expr : node, env : node) ::= additiveExpr(expr, env) [ multiplyOperator:sOperator #continue => slideNodeContent(expr, left); => insert expr.type = sOperator; additiveExpr(expr.right, env) ]*; additiveExpr(expr : node, env : node) ::= multiplicativeExpr(expr, env) [ ['+' | '-']:sOperator #continue => slideNodeContent(expr, left); => insert expr.type = sOperator; multiplicativeExpr(expr.right, env) ]*; multiplicativeExpr(expr : node, env : node) ::= unaryExpr(expr, env) [ ['*' | #readIdentifier:{"div", "mod"}]:sOperator #continue => slideNodeContent(expr, left); => insert expr.type = sOperator; unaryExpr(expr.right, env) ]*; unaryExpr(expr : node, env : node) ::= unionExpr(expr, env) | '-' #insert(expr.expression) unaryExpr(expr.expression, env) => insert expr.type = "opposite"; ; unionExpr(expr : node, env : node) ::= pathExpr(expr, env) [ '|' #continue => slideNodeContent(expr, left); => insert expr.type = "|"; pathExpr(expr.right, env) ]*; pathExpr(expr : node, env : node) ::= locationPath(expr, env) | filterExpr(expr, env) [ ["//" | '/']:sOperator #continue => slideNodeContent(expr, left); => insert expr.type = sOperator; relativeLocationPath(expr.right, env) ]?; filterExpr(expr : node, env : node) ::= primaryExpr(expr, env) [#pushItem(expr.predicates) predicate(expr.predicates#back, env)]*; primaryExpr(expr : node, env : node) ::= variableReference:expr.variable => insert expr.type = "$"; | '(' #continue expression(expr, env) ')' | literal:sLiteral => insert expr.type = "''"; => insert expr.literal = coreString(sLiteral, 1, 1); | number:sNumber => insert expr.type = "0"; => insert expr.number = sNumber; | functionCall(expr, env) ; functionCall(expr : node, env : node) ::= => local minParams; => local paramTypes; functionName(minParams, paramTypes):sFunction '(' #continue => insert expr.type = "()"; => insert expr.function = sFunction; [ #pushItem(expr.arguments) expression(expr.arguments#back, env) [ ',' #continue => pushItem expr.arguments; expression(expr.arguments#back, env) ]* ]? ')' => if $getArraySize(expr.arguments) > getArraySize(paramTypes)$ error("too much arguments passed to the function '" + sFunction + "'"); => if $getArraySize(expr.arguments) < minParams$ error("not enough arguments passed to the function '" + sFunction + "'"); ; //--------------- // lexical tokens //--------------- variableReference : value ::= '$' QName:variableReference; literal ::= "'" #continue ->"'" | "\"" #continue ->"\""; number ::= #readNumeric; axisName ::= IDENTIFIER: { "ancestor", "ancestor-or-self", "attribute", "child", "descendant", "descendant-or-self", "following", "following-sibling", "namespace", "parent", "preceding", "preceding-sibling", "self" }; nodeType ::= IDENTIFIER:{"comment", "text", "processing-instruction", "node"}; multiplyOperator : value ::= ["<=" | ">=" | '<' | '>']:multiplyOperator | "<" => set multiplyOperator = '<'; | ">" => set multiplyOperator = '>'; ; QName ::= [prefix ':']? localPart; prefix ::= NCName; localPart ::= NCName; NCName ::= IDENTIFIER;