890 lines
30 KiB
YAML
890 lines
30 KiB
YAML
---
|
||
-
|
||
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: "*"
|