--- - name: simple-number-set tag: mi match: "string-length(text())=1 and translate(text(), 'ℂℕℚℝℤ', '')=''" # tricky way to match any of the letters instead of using 'or' replace: - intent: name: number-sets children: [x: "text()"] - name: sup-number-set tag: msup match: - "*[1][string-length(text())=1 and translate(., 'ℂℕℚℝℤ', '')=''] and" - "*[2][string-length(text())=1 and (.='+' or .='-' or self::m:mn)]" replace: - intent: name: number-sets children: - x: "*[1]" - x: "*[2]" - name: real-part tag: mi match: "(.='Re' or .='re' or .='ℜ') and following-sibling::*[1][.='\u2061']" # before function apply replace: - intent: name: real-part children: [] - name: imaginary-part tag: mi match: "(.='Im' or .='im' or .='ℑ') and following-sibling::*[1][.='\u2061']" # before function apply replace: - intent: name: imaginary-part children: [] - name: roman_numeral tag: [mi, mn, mtext] match: "@data-number" replace: - intent: name: mn children: [x: "@data-number"] - name: positive-or-negative tag: mrow match: "count(*)=2 and *[1][self::m:mo][.='+' or .='-']" variables: - AtLeftEdge: "EdgeNode(., 'left', 'mtd')" - AtLeftEdgeOfMTD: "name($AtLeftEdge) = 'mtd'" - ContinuedColumn: "$TableProperty='lines' or $TableProperty='system-of-equations' or $TableProperty='piecewise'" - InContinuedRow: "parent::m:mtd[parent::m:mtr[@data-intent-property[contains(., ':continued-row:')]]]" - FirstColumn: "$AtLeftEdge/../*[1]=$AtLeftEdge" # first child of mtr... replace: - test: # don't use positive/negative if a continued data cell if: "$AtLeftEdgeOfMTD and $ContinuedColumn and (not($FirstColumn) or $InContinuedRow)" then: - intent: xpath-name: "IfThenElse(*[1][self::m:mo][.='+'], 'plus', 'minus')" attrs: "data-intent-property='concat(data-intent-property, \":infix:\")'" children: [x: "*[2]"] else: - intent: xpath-name: "IfThenElse(*[1][self::m:mo][.='+'], 'plus', 'minus')" attrs: "data-intent-property='concat(data-intent-property, \":prefix:\")'" children: [x: "*[2]"] - # (a,b) has many interpretations; (a, b] (etc) have fewer interpretations. # as an interval, it represents a set and hence a clue that it is an interval is that a set operator comes # before or after it. '=' is also common. # They also commonly stand by themselves, but so does the interpretation as a point or gcd, so we don't include (xxx, yyy) in the inference # Note [x,y] is also a commutator. # Intervals are never contained in other intervals, so rule them out if there is an ancestor that could be an interval name: interval tag: mrow match: - "count(*)=3 and " # FIX: consider adding ]...[ versions - "(*[1][.='(' or .='['] and *[3][.=')' or .=']']) and" # match bracketing - "(*[2][count(*)=3 and *[2][.=',']]) and" # inside should have ',' # intervals are not part of set notation (e.g, { (x,y)∈L | ...}) nor are they nested - "not(ancestor::*[IsBracketed(., '{', '}') or IsBracketed(., '[', ']') or IsBracketed(., '(', ')')]) and " - "not(descendant::*[IsBracketed(., '{', '}') or IsBracketed(., '[', ']') or IsBracketed(., '(', ')')]) and " # FIX: if both the first and third children of *[2] are mn, then make sure first <= third - "(" - " not(IsBracketed(., '(', ')')) or " # (.,.) is very ambiguous -- need more clues - " $ClearSpeak_Paren = 'Interval' or " # pref is set - " *[2]/*[1][contains(., '∞')] or " # starts with infinity - " *[2]/*[3][contains(., '∞')] or " # ends with infinity - " preceding-sibling::*[1][self::m:mo and ( .='=' or IsInDefinition(., 'SubsetOperators') )] or " # context hint - " following-sibling::*[1][self::m:mo and ( .='=' or IsInDefinition(., 'SubsetOperators') )]" # context hint - ")" replace: - test: - if: "*[1][.='('] and *[3][.=')']" then: - intent: name: "open-interval" children: [x: "*[2]/*[1]", x: "*[2]/*[3]"] - else_if: "*[1][.='('] and *[3][.=']']" then: - intent: name: "open-closed-interval" children: [x: "*[2]/*[1]", x: "*[2]/*[3]"] - else_if: "*[1][.='['] and *[3][.=']']" then: - intent: name: "closed-interval" children: [x: "*[2]/*[1]", x: "*[2]/*[3]"] else: - intent: name: "closed-open-interval" children: [x: "*[2]/*[1]", x: "*[2]/*[3]"] - name: binomial-frac tag: mrow # "translate()" is used because it might be "0em" and we need to get rid of the "em" match: "IsBracketed(., '(', ')') and *[2][self::m:mfrac][translate(@linethickness, 'abcdefghijklmnopqrstuvwxyz', '')=0]" replace: - intent: name: binomial attrs: "data-intent-property='concat(data-intent-property, \":infix:\")'" children: - x: "*[2]/*[1]" - x: "*[2]/*[2]" - # C with pre-subscript 'n' and post-subscript 'm'. The 'n' can be either in the sub or super position # See https://en.wikipedia.org/wiki/Binomial_coefficient#History_and_notation name: binomial-mmultiscripts tag: mmultiscripts variables: - Prescripts: "m:mprescripts/following-sibling::*" - Postscripts: "m:mprescripts/preceding-sibling::*[position() < last()]" match: - "*[1][self::m:mi and .='C'] and" - "count($Prescripts)=2 and ($Prescripts[1][self::m:none] or $Prescripts[2][self::m:none]) and " - "count($Postscripts)=2 and $Postscripts[2][self::m:none]" replace: - intent: name: binomial attrs: "data-intent-property='concat(data-intent-property, \":infix:\")'" children: - x: "IfThenElse(name($Prescripts[2])='none', $Prescripts[1], $Prescripts[2])" - x: "$Postscripts[1]" - name: binomial-msub # C_{n,k} tag: msub match: - "*[1][self::m:mi and .='C'] and" - "*[2][self::m:mrow and count(*)=3 and *[2][.=',']]" replace: - intent: name: binomial attrs: "data-intent-property='concat(data-intent-property, \":infix:\")'" children: - x: "*[2]/*[1]" - x: "*[2]/*[3]" - # P with pre-sub or superscript 'n' and post-subscript 'k' (https://en.wikipedia.org/wiki/Permutation#k-permutations_of_n) name: permutation-mmultiscripts tag: mmultiscripts variables: - Prescripts: "m:mprescripts/following-sibling::*" - Postscripts: "m:mprescripts/preceding-sibling::*[position() < last()]" match: - "*[1][self::m:mi and .='P'] and" - "count($Prescripts)=2 and ($Prescripts[1][self::m:none] or $Prescripts[2][self::m:none]) and " - "count($Postscripts)=2 and $Postscripts[2][self::m:none]" replace: - intent: name: pochhammer attrs: "data-intent-property='concat(data-intent-property, \":infix:\")'" children: - test: if: "$Prescripts[1][self::m:none]" then: [x: "$Prescripts[2]"] else: [x: "$Prescripts[1]"] - x: "$Postscripts[1]" - # P with superscript 'n' and subscript 'k' (https://en.wikipedia.org/wiki/Permutation#k-permutations_of_n) name: permutation-msubsup tag: msubsup match: - "*[1][self::m:mi and .='P']" replace: - intent: name: pochhammer attrs: "data-intent-property='concat(data-intent-property, \":infix:\")'" children: - x: "*[3]" - x: "*[2]" # rules on scripted vertical bars ('evaluated at') - name: evaluated-at-msub tag: mrow match: "count(*)=2 and *[2][self::m:msub and *[1][self::m:mo][.='|']]" replace: - intent: name: "evaluate" attrs: "data-intent-property='concat(data-intent-property, \":infix:\")'" children: - x: "*[1]" - x: "*[2]/*[2]" - name: evaluated-at-msubsup tag: mrow match: "count(*)=2 and *[2][self::m:msubsup and *[1][self::m:mo][.='|']]" replace: - intent: name: "evaluate" children: - x: "*[1]" - x: "*[2]/*[2]" - x: "*[2]/*[3]" # it is also used outside of brackets - name: bracketed-evaluated-at tag: msubsup match: "IsBracketed(*[1], '[', ']')" replace: - intent: name: "evaluate" children: - x: "*[1]/*[1]" - x: "*[2]" - x: "*[3]" - # vertical bars otherwise # could also be cardinality, length, ... name: absolute-value tag: mrow match: "count(*) = 3 and not(*[2][self::m:mtable]) and IsBracketed(., '|', '|')" replace: - intent: name: "absolute-value" attrs: "data-intent-property='concat(data-intent-property, \":function:\")'" children: [x: "*[2]"] - name: default tag: msqrt match: "." replace: - intent: name: "square-root" attrs: "data-intent-property='concat(data-intent-property, \":function:\")'" children: [x: "*[1]"] - name: sqrt tag: mroot match: "*[2][self::m:mn and .='2']" replace: - intent: name: "square-root" attrs: "data-intent-property='concat(data-intent-property, \":function:\")'" children: [x: "*[1]"] - name: default tag: mroot match: "." replace: - intent: name: "root" children: - x: "*[1]" - x: "*[2]" - # handle log with a base name: log-with-base tag: [msub, msubsup] match: "*[1][self::m:mi and .='log']" replace: - test: if: "self::m:msub" then: - intent: name: "logarithm-with-base" attrs: "data-intent-property='concat(data-intent-property, \":prefix\")'" children: [x: "*[2]"] # grab the base else: - intent: name: "power" attrs: "data-intent-property='concat(data-intent-property, \":infix:\")'" children: - intent: name: "logarithm-with-base" attrs: "data-intent-property='concat(data-intent-property, \":prefix:\")'" children: [x: "*[2]"] # grab the base - x: "*[3]" # grab the exponent - name: bigop tag: [msub, munder] match: "IsInDefinition(*[1], 'LargeOperators') or contains(@data-intent-property, ':largeop:')" replace: - intent: name: "large-op" # Fix: the name in the spreadsheet needs updating/fixing children: - x: "*[1]" - x: "*[2]" - name: limit tag: [msub, munder] variables: [NoSpacesBase: "translate(*[1], ' \u00A0\u2004\u2005\u2006\u2007\u2008\u2009\u200A','')"] # Sometimes these have (weird) spaces in them match: "*[1][$NoSpacesBase='lim' or $NoSpacesBase='limsup' or $NoSpacesBase='liminf']" replace: - intent: xpath-name: "IfThenElse($NoSpacesBase='lim', 'limit', IfThenElse($NoSpacesBase='limsup', 'limit-sup', 'limit-inf'))" attrs: "data-intent-property='concat(data-intent-property, \":prefix:\")'" children: [x: "*[2]"] - name: repeating-decimal tag: mrow match: "count(*)=3 and *[1][self::m:mn] and *[2][.='\u2062'] and *[3][self::m:mover][*[1][self::m:mn] and *[2][.='\u00AF']]" replace: - intent: name: "repeating-decimal" children: - x: "*[1]" - x: "*[3]/*[1]" - name: modified-var tag: mover # breve, check, dot, double-dot, triple-dot, quadruple-dot, grave, hat, tilde, line/bar match: "*[1][self::m:mi] and *[2][translate(., '\u0306\u030c.\u00A8\u02D9\u20DB\u20DC`^~¯_', '')='']" replace: - intent: name: "modified-variable" attrs: "data-intent-property='concat(data-intent-property, \":silent:\")'" children: - x: "*[1]" - x: "*[2]" - name: vector tag: mover match: "*[1][self::m:mi] and *[2][.='→' or .='⇀']" replace: - intent: name: "vector" attrs: "data-intent-property='concat(data-intent-property, \":prefix:\")'" children: - x: "*[1]" - # this captures the output for the mhchem's "<=>", "<<=>", and "<=>>" output (there are no Unicode arrows for them) # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/NSoiffer/MathCAT/issues/60) name: chemistry-mhchem-equilibrium-arrow tag: mover match: - "*[1][substring(., 1, 1)='↽'] and" - "*[2][substring(., string-length(), 1)='⇀']" replace: - intent: name: "chemical-arrow-operator" children: - test: if: "*[1][self::m:mrow]" then_test: if: "*[2][self::m:mrow]" then: [t: "\u1f8d2"] # this is currently unassigned and may get used by UTC at some point (<=>) else: [t: "\u1f8d4"] # this is currently unassigned and may get used by UTC at some point (<<=>) else: [t: "\u1f8d3"] # this is currently unassigned and may get used by UTC at some point (<==>>) - # need to include the name of the element so the rules know whether to say "sub" or "super" name: chemistry tag: [msub, msup] match: "@data-chem-formula" replace: - intent: name: "chemical-formula" children: # have to list individual children because "*" results in an internal error (children aren't flat) - x: "name(.)" - x: "*[1]" - x: "*[2]" - name: chemistry-prescripts tag: mmultiscripts match: "@data-chem-formula" replace: - test: - if: "count(*)=4 or count(*)=6" then: - intent: name: "chemical-nuclide" children: - x: "*" else: # FIX: what other cases are there??? - intent: name: "chemical-formula" children: - x: "*" - name: chemical-element tag: [mi, mtext] match: "@data-chem-element" replace: - intent: name: "chemical-element" children: - x: "text()" - name: unit tag: [mi, mtext] match: "@class='MathML-unit' or contains(@data-intent-property, ':unit')" replace: - intent: name: "unit" attrs: "data-intent-property='\":unit:\"'" # the value ":unit:" needs to be an xpath string, hence the escaped quotes children: - x: "text()" - # this is attempt to catch units when some has created a non-italic word that matches a unit # other mtext unit cases were caught above. # This has a guard to make sure it is positionally a unit. # Currently only checking for a simple mrow like "3 s or 3/4 km" # Other checks would be an mfrac with a unit in both parts (complicated to check) or msup with a unit in the base and that inside an mrow name: mtext-inference-unit tag: [mtext, mi] match: "not(@data-chem-element) and (self::m:mtext or string-length(.) > 1 or @mathvariant='normal') and (parent::*[1][self::m:mrow and count(*)=3 and *[2][.='\u2062'] and *[1][self::m:mn or (self::m:mfrac and *[1][self::m:mn] and *[2][self::m:mn])]]) and count(preceding-sibling::*) = 2 and (DefinitionValue(., 'Speech', 'SIUnits') != '' or DefinitionValue(., 'Speech', 'UnitsWithoutPrefixes') != '' or DefinitionValue(., 'Speech', 'EnglishUnits') != '' or (string-length(.) >= 3 and substring(., 1, 2) = 'da' and DefinitionValue(substring(., 3), 'Speech', 'SIUnits') != '') or (string-length(.) >= 2 and DefinitionValue(substring(., 1, 1), 'Speech', 'SIPrefixes') != '' and DefinitionValue(substring(., 2), 'Speech', 'SIUnits') != ''))" replace: - intent: name: "unit" attrs: "data-intent-property='\":unit:\"'" # the value ":unit:" needs to be an xpath string, hence the escaped quotes children: - x: "text()" - name: chemical-formula-op tag: [mo] match: "@data-chem-formula-op" replace: - intent: name: "chemical-formula-operator" children: - x: "text()" - name: chemical-arrow-op tag: [mo] # FIX: this is a duplicate of the list in chemistry.rs. Probably should pull out and add to definitions.yaml match: "@data-chem-equation-op and translate(., '→➔←⟶⟵⤻⇋⇌↿↾⇃⇂⥮⥯⇷⇸⤉⤈⥂⥄', '')=''" replace: - intent: name: "chemical-arrow-operator" children: - x: "text()" - name: chemical-equation-op tag: [mo] match: "@data-chem-equation-op" replace: - intent: name: "chemical-equation-operator" children: - x: "text()" - name: chemistry-concentration tag: mrow match: "ancestor-or-self::*[@data-chem-equation] and IsBracketed(., '[', ']')" replace: - intent: name: "chemistry-concentration" attrs: "data-intent-property='concat(data-intent-property, \":function:\")'" children: - x: "*[2]" - name: chemistry-state tag: mrow match: - "(@data-chem-formula or @data-chem-equation) and" - "IsBracketed(., '(', ')') and" - "*[2][.='s' or .='l' or .='g' or .='aq']" replace: - intent: name: "chemical-state" children: - x: "*[2]" # This needs to be before the simple "x prime" rule # minutes/seconds or feet/inches # If ' or " follows a number, then it is not "prime", but is a unit # Note the ASCII ' and " are converted to prime during canonicalization if in a superscript # Handles single, double, primes, and also double quote, which don't have to be in an msup # The rules are: # 1. If the prime follows a degree sign with a number (in various forms) or letter after it, # then it is minutes/seconds # 2. Else, if it follows a *number* (in various forms), then it feet/inches # 3. Else it is 'prime' # any ? ( count(match)==2 && # (name(match)=="mrow" || name(match)=="msup") && # (MatchString($1, "mo", "'") || MatchString($1, "mo", "′") || // apostrophe or prime # MatchString($1, "mo", "″") || MatchString($1, "mo", "\"")) && // double quote or double prime # ( # // '1': degree sign check # ( has_previous(match) && # ( (MatchString($1, "mo", "′") && # ( name(previous(match))=="msup" || # (name(previous(match))=="mrow" && count(previous(match))==2)) && # MatchString(previous(match)[1], "mo", "°")) || # ( has_previous(previous(match)) && # (MatchString($1, "mo", "″") || MatchString($1, "mo", "\"")) && # ( name(previous(match, 2))=="msup" || # (name(previous(match, 2))=="mrow" && count(previous(match, 2))==2)) && # MatchString(previous(match, 2)[1], "mo", "°")) # ) # ) || # # // '2': number or numeric fraction in front # name($0)=="mn" || # (name($0)=="mfrac" && name($0[0])=="mn" && name($0[1])=="mn") # ) # ) # => structure( # $0, # UIWord([$1[0],$1[-1]]) # {ruleRef="RR_unitsBase"; # singular=MatchString($0, "mn", "1"); # // if this follows a degree sign, then it is an angle notation (min/secs) regardless of the pref setting # // we have to either look at the previous entry for mins or the one prior to that for secs # pref= ( has_previous(match) && # ( (MatchString($1, "mo", "′") && # ( name(previous(match))=="msup" || # (name(previous(match))=="mrow" && count(previous(match))==2)) && # MatchString(previous(match)[1], "mo", "°")) || # ( has_previous(previous(match)) && # (MatchString($1, "mo", "″") || MatchString($1, "mo", "\"")) && # ( name(previous(match, 2))=="msup" || # (name(previous(match, 2))=="mrow" && count(previous(match, 2))==2)) && # MatchString(previous(match, 2)[1], "mo", "°")) # ) # ) ? "Angle" : "Length"; # } # ); # Pseudo-script characters are characters such as "degree sign" ('°') that are raised but in MathML should be in a superscript. # They are not spoken as if in a superscript (e.g "x degrees", not "x superscripts degrees") - name: skip-super tag: [msup, msubsup] match: "*[last()][translate(., '′″‴⁗\"†‡°*', '')='' or .=\"'\"]" # you can't have both ' and " in an xpath string replace: - intent: name: "skip-super" attrs: "data-intent-property='concat(data-intent-property, \":silent:\")'" children: [x: "*"] - name: mo-super tag: [msup, msubsup] match: "*[last()][self::m:mo]" replace: - intent: name: "say-super" attrs: "data-intent-property='concat(data-intent-property, \":infix:\")'" children: [x: "*"] # rules for functions raised to a power # these could have been written on 'mrow' but putting them on msup seems more specific # to see if it is a function, we look right to see if the following sibling is apply-function - name: function-inverse tag: msup match: - "*[2][self::m:mrow][count(*)=2] and" # exponent is an mrow with children... - "*[2]/*[1][self::m:mo][.='-'] and" # '-' - "*[2]/*[2][self::m:mn][.=1] and" # and '1' - "*[1][self::m:mi] and IsInDefinition(*[1], 'TrigFunctionNames') and" # base is a trig function name (e.g, sin, sinh) - "following-sibling::*[1][self::m:mo][.='\u2061']" # and msup is followed by invisible function apply replace: - intent: name: "inverse-function" children: - x: "*[1]" - name: bigop tag: [msubsup, munderover] match: "IsInDefinition(*[1], 'LargeOperators') or contains(@data-intent-property, ':largeop:')" replace: - intent: name: "large-op" # Fix: the name in the spreadsheet needs updating/fixing children: - x: "*[1]" - x: "*[2]" - x: "*[3]" - name: set tag: mrow # sets have { }s and should have at least one of ",∈∉|" (vertical bar is "such that") or be the empty set match: "IsBracketed(., '{', '}') and (count(*)=2 or (count(*)=3 and IsNode(*[2], 'leaf')) or . != translate(., ',∈∉|:', ''))" replace: - intent: name: "set" children: - test: if: count(*) = 2 then: [] else: [x: "*[2]"] - name: default tag: msub match: "." replace: - intent: name: "indexed-by" attrs: "data-intent-property='concat(data-intent-property, \":infix:\")'" children: - x: "*[1]" - x: "*[2]" - # this adds a :unit property to the fraction if its children are units -- simplifies things downstream # we have to this is the attr calculation because at that point, the children have been replaced so they will be marked as units if there are units # at the point of 'match', the replacement has yet to be done so they may not have inferred units. # this catches s^2 as in m/s^2 name: default tag: msup match: "." replace: - intent: name: "power" attrs: "data-intent-property='IfThenElse(*[1][contains(@data-intent-property, \":unit:\")], \":unit:\", \"\")'" children: - x: "*[1]" - x: "*[2]" - name: default tag: msubsup match: "." replace: - intent: name: "power" children: - intent: name: "indexed-by" attrs: "data-intent-property='concat(data-intent-property, \":infix:\")'" children: - x: "*[1]" - x: "*[2]" - x: "*[3]" - # this adds a :unit property to the fraction if its children are units -- simplifies things downstream # we have to this is the attr calculation because at that point, the children have been replaced so they will be marked as units if there are units # at the point of 'match', the replacement has yet to be done so they may not have inferred units. name: default tag: mfrac match: "." replace: - intent: name: "fraction" attrs: "data-intent-property='IfThenElse(*[1][contains(@data-intent-property, \":unit:\")] and DEBUG(*[2])[contains(@data-intent-property, \":unit:\")], \":unit:\", \"\")'" children: - x: "*[1]" - x: "*[2]" # generic mtable: treat as multiline equations of some sort # if a property is set, handle that first - name: mtable-property-is-set tag: mtable # any 'intent' property set overrides the inferred property variables: [TableProperty: "IfThenElse(contains(@data-intent-property, ':array:'), 'array', IfThenElse(contains(@data-intent-property, ':matrix:'), 'matrix', IfThenElse(contains(@data-intent-property, ':determinant:'), 'determinant', IfThenElse(contains(@data-intent-property, ':piecewise:'), 'piecewise', IfThenElse(contains(@data-intent-property, ':system-of-equations:'), 'system-of-equations', IfThenElse(contains(@data-intent-property, ':lines:'), 'lines', $TableProperty))))))"] # '' is what to say if none match" match: "$TableProperty !=''" replace: - intent: xpath-name: "$TableProperty" children: - x: "*" - name: "piecewise" tag: mrow match: "count(*)=2 and *[1][.='{'] and *[2][self::m:mtable and not( contains(@data-intent-property, ':array:') or contains(@data-intent-property, ':matrix:') or contains(@data-intent-property, ':determinant:') or contains(@data-intent-property, 'system-of-equations:') or contains(@data-intent-property, ':lines:') ) ]" replace: - with: variables: [TableProperty: "'piecewise'"] replace: [x: "*[2]"] - name: matrix tag: mrow match: - "count(*)=3 and (IsBracketed(., '(', ')') or IsBracketed(., '[', ']')) and *[2][self::m:mtable and not( contains(@data-intent-property, ':piecewise:') or contains(@data-intent-property, ':matrix:') or contains(@data-intent-property, ':determinant:') or contains(@data-intent-property, 'system-of-equations:') or contains(@data-intent-property, ':lines:') ) ]" replace: - with: variables: [TableProperty: "'matrix'"] replace: [x: "*[2]"] - name: determinant tag: mrow match: - "count(*)=3 and IsBracketed(., '|', '|') and *[2][self::m:mtable and not( contains(@data-intent-property, ':array:') or contains(@data-intent-property, ':matrix:') or contains(@data-intent-property, ':piecewise:') or contains(@data-intent-property, 'system-of-equations:') or contains(@data-intent-property, ':lines:') ) ]" replace: - with: variables: [TableProperty: "'determinant'"] replace: [x: "*[2]"] - name: mtable-equations-property tag: mtable match: "count(*) > 0 and *[1][contains(translate(., '=≠<>≤≥≦≧', '='), '=')]" replace: - with: variables: - TableProperty: "'system-of-equations'" replace: - intent: name: "system-of-equations" children: - x: "*" - name: mtable-lines-property tag: mtable match: "count(*) > 0 and not(descendant::*[self::m:mo and not(.='-' or .='+')])" replace: - with: variables: - TableProperty: "'lines'" replace: - intent: name: "lines" children: - x: "*" - # merge all the mtd into a single mtd name: consolidate-labeled-mtd-entries tag: [mlabeledtr, mtr] # Note: child hasn't been processed, so 'intent' hasn't been processed/turned into data-intent-property match: "count(*) > 1 and ($TableProperty = 'piecewise' or $TableProperty = 'system-of-equations' or $TableProperty = 'lines') and (self::m:mlabeledtr or *[contains(@intent, ':equation-label')])" replace: - intent: name: "mlabeledtr" children: - x: "*[contains(@intent, ':equation-label')]" - intent: name: "mtd" attrs: "id=*[not(contains(@intent, ':equation-label'))][1]/*[1]/@id" children: - intent: name: "mrow" attrs: "id=*[not(contains(@intent, ':equation-label'))][1]/*[1]/@id" children: [x: "*[not(contains(@intent, ':equation-label'))]"] - # convert a mtr to a mlabeledtr name: mlabeledtr tag: mtr # Note: child hasn't been processed, so 'intent' hasn't been processed/turned into data-intent-property match: "count(*) > 1 and *[contains(@intent, ':equation-label')]" replace: - intent: name: "mlabeledtr" children: - x: "*[contains(@intent, ':equation-label')]" - x: "*[not(contains(@intent, ':equation-label'))]" - # merge all the mtd into a single mtd name: consolidate-mtd-entries tag: mtr match: "count(*) > 1 and ($TableProperty = 'piecewise' or $TableProperty = 'system-of-equations' or $TableProperty = 'lines')" replace: - intent: name: "mtr" attrs: "id='@id'" children: - intent: name: "mtd" attrs: "id='*[1]/@id'" children: - intent: name: "mrow" attrs: "id='*[2]/@id'" children: [x: "*"] - # We need to preserve any intent properties that are on the mtd (e.g., :pause) # Note: at this point "intent" has been processed so we look at @data-intent-property name: mtd-to-mrow tag: mtd match: "($TableProperty = 'piecewise' or $TableProperty = 'system-of-equations' or $TableProperty = 'lines') and not(contains(@data-intent-property, ':equation-label'))" replace: - test: if: "@data-intent-property" then: - intent: name: "mrow" attrs: "id=@id data-intent-property=@data-intent-property" children: [x: "*"] else: - x: "*"