--- - name: intent-literal-silent tag: [mi, mo, mn] match: "contains(@data-intent-property, ':silent:')" # say nothing replace: [] # handling of negative numbers that come from 'intent' is hard -- we do something that is close to right here - name: intent-literal-negative-number tag: mn match: "starts-with(text(), '-')" replace: - T: "減" # phrase(x 'minus' y) - x: "translate(text(), '-_', '')" - name: default tag: square-root match: "." replace: - test: if: "$Verbosity!='Terse'" then: {T: ""} # phrase('the' square root of x) - T: "根號" # phrase(the 'square root' of x) - x: "*[1]" - test: if: "$Verbosity!='Terse'" then: {T: ""} # phrase(the square root 'of' x) else: {pause: short} - test: if: IsNode(*[1], 'leaf') then: [{pause: short}] else: [{T: "結束根號"}, {pause: medium}] # phrase(start the square root of x 'end of root') - name: default tag: root match: "." replace: - T: "根號" - x: "*[1]" - test: if: "$Verbosity!='Terse'" then: [{T: "的"}] # phrase(the square root 'of' x) - test: if: "*[2][self::m:mn]" then_test: - if: "*[2][text()='2']" then: [{T: "平方根"}] # phrase(the 'square root' of x) - else_if: "*[2][text()='3']" then: [{T: "立方根"}] # phrase(the 'cube root' of x) - else_if: "*[2][not(contains(., '.'))]" then: [{x: "*[2]"}, {T: "次方根"}] # phrase(the square 'root' of x) - else: [{x: "*[2]"}, {T: "次方根"}] else: - test: if: "*[2][self::m:mi][string-length(.)=1]" then: - x: "*[2]" #- pronounce: [{text: "-th"}, {ipa: "θ"}, {sapi5: "th"}, {eloquence: "T"}] else: [{x: "*[2]"}] - T: "次方根" # phrase(the square 'root' of) # Fraction rules # Mixed numbers mostly "just work" because the invisible char reads as "and" and other parts read properly on their own #- name: common-fraction # tag: fraction # match: # - "*[1][self::m:mn][not(contains(., '.')) and text()<20] and" # - "*[2][self::m:mn][not(contains(., '.')) and 2<= text() and text()<=10]" # replace: [{x: ToCommonFraction(.)}] #- name: common-fraction-mixed-number # tag: fraction # match: # - "preceding-sibling::*[1][self::m:mo][text()='⁤'] and" # preceding element is invisible plus # - "*[1][self::m:mn][not(contains(., '.'))] and" # - "*[2][self::m:mn][not(contains(., '.'))]" # replace: [{x: ToCommonFraction(.)}] - name: simple # don't include nested fractions. E.g, fraction a plus b over c + 1 end fraction" is ambiguous # by simplistic SimpleSpeak's rules "b over c" is a fraction, but if we say nested fractions # are never simple, then any 'over' applies only to enclosing "fraction...end fraction" pair. tag: fraction match: - "(IsNode(*[1],'leaf') and IsNode(*[2],'leaf')) and" - "not(ancestor::*[name() != 'mrow'][1]/self::m:fraction)" # FIX: can't test for mrow -- what should be used??? replace: - x: "*[2]" - T: "分之" # phrase(the fraction 3 'over' 4) - x: "*[1]" #- pause: short - name: default tag: fraction match: "." replace: - T: "分數" # phrase(the 'fraction' 3 over 4) #- pause: short - x: "*[2]" - test: if: "not(IsNode(*[2],'leaf'))" then: [{pause: short}] - T: "分之" # phrase(the fraction 3 'over' 4) - test: if: "not(IsNode(*[1],'leaf'))" then: [{pause: short}] - x: "*[1]" #- pause: short - T: "結束分數" # phrase(start 7 over 8 'end of fraction') - pause: medium # 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: inverse-function tag: inverse-function match: "." replace: - T: "反" # phrase(the 'inverse' of f) - x: "*[1]" - name: function-squared-or-cubed tag: power match: - "*[2][self::m:mn][text()='2' or text()='3'] and" - "following-sibling::*[1][self::m:mo][text()='⁡']" #invisible function apply replace: - x: "*[1]" - bookmark: "*[2]/@id" - test: if: "*[2][text()=2]" then: {T: "平方"} # phrase(5 'squared' equals 25) else: {T: "立方"} # phrase(5 'cubed' equals 125) - name: function-power tag: power match: - "following-sibling::*[1][self::m:mo][text()='⁡']" #invisible function apply replace: - test: if: "$Verbosity!='Terse'" then: {T: ""} # # phrase('the' fourth power of 10) - bookmark: "*[2]/@id" - x: "*[1]" - T: "的" - test: if: "*[2][self::m:mn][not(contains(., '.'))]" then: [{x: "*[2]"}] else: [{x: "*[2]"}] - T: "次方" # phrase(the fourth 'power of' 2) - pause: short # non-function rules for power - name: squared-or-cubed tag: power match: "*[2][self::m:mn][text()='2' or text()='3']" replace: - x: "*[1]" - bookmark: "*[2]/@id" - test: if: "*[2][text()=2]" then: {T: "平方"} # phrase(5 'squared' equals 25) else: {T: "立方"} # phrase(5 'cubed' equals 125) - name: simple-integer tag: power match: "*[2][self::m:mn][not(contains(., '.'))]" replace: - x: "*[1]" - T: "的" # phrase(15 raised 'to the' second power equals 225) - test: if: "*[2][.>0]" then: {x: "*[2]"} else: {x: "*[2]"} - T: "次方" - name: simple-negative-integer tag: power match: - "*[2][self::m:negative and" - " *[1][self::m:mn][not(contains(., '.'))]]" replace: - x: "*[1]" - T: "的" # phrase(15 raised 'to the' second power equals 225) - x: "*[2]" - T: "次方" - name: simple-var tag: power match: "*[2][self::m:mi][string-length(.)=1]" replace: - x: "*[1]" - T: "的" # phrase(15 raised 'to the' second power equals 225) - x: "*[2]" #- pronounce: [{text: "-th"}, {ipa: "θ"}, {sapi5: "th"}, {eloquence: "T"}] - T: "次方" - name: simple tag: power match: "IsNode(*[2], 'leaf')" replace: - x: "*[1]" - T: "的" # phrase(15 raised 'to the' second power equals 225) - x: "*[2]" - T: "次方" - name: nested # it won't end in "power" if the exponent is simple enough # FIX: not that important, but this misses the case where the nested exp is a negative integer (change test if this is fixed) # ending nested exponents with "...power power" sounds bad tag: power match: - "*[2][" - " (self::m:power and not(IsNode(*[2], 'leaf'))) or" # non-simple nested superscript - " self::m:mrow[*[last()][self::m:power[not(IsNode(*[2], 'leaf'))]]]" # same as above but at the end of an mrow # FIX: need to figure out linear replacement - " ]" replace: - x: "*[1]" - T: "的" # phrase(15 'raised to the' second power equals 225) - x: "*[2]" - T: "次方" #- pause: short #- t: "結束指數" # phrase(start 2 raised to the exponent 4 'end of exponent') - name: default tag: power match: "." replace: - x: "*[1]" - T: "的" # phrase(15 'raised to the' second power equals 225) - x: "*[2]" - T: "次方" # phrase(15 raised to the second 'power' equals 225) # # Some rules on mrows # - name: set tag: set match: "." replace: - test: - if: "count(*)=0" then: - test: if: "$Verbosity!='Terse'" then: {T: ""} # phrase('the' empty set) - T: "空集" # phrase(when a set contains no value it is called an 'empty set' and this is valid) - else_if: "count(*[1]/*)=3 and *[1]/*[2][self::m:mo][text()=':' or text()='|' or text()='∣']" then: - test: if: "$Verbosity!='Terse'" then: {T: ""} # phrase('the' set of all integers) - T: "集合" # phrase(the 'set of all' positive integers less than 10) - x: "*[1]/*[1]" - T: "滿足" # phrase(x 'such that' x is less than y) - x: "*[1]/*[3]" else: - test: if: "$Verbosity!='Terse'" then: {T: ""} # phrase('the' set of integers) - T: "集合" # phrase(here is a 'set' of numbers) - x: "*[1]" - name: times tag: mo match: # say "times" when invisible times is followed by parens or a superscript that has a base with parens or "|"s # if we aren't sure if it is times or not, don't say anything - "text()='⁢' and" - "not(@data-function-guess) and (" - "not(ancestor-or-self::*[contains(@data-intent-property, ':literal:')]) and " - " following-sibling::*[1][" - " IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|') or self::m:binomial or" # followed by parens - " ( (self::m:msup or self::m:msub or self::m:msubsup or self::m:power) and " # followed by msup, etc. - " *[1][self::m:mrow][IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|')]" # base has parens - " )" - " ]" # other possibility is the preceding element has parens (but not the following) - " or " - " preceding-sibling::*[1][" - " IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|')]" # followed by parens - " )" replace: - T: "乘" # phrase(7 'times' 5 equals 35) 乘 的發音不正確 - name: no-say-parens tag: mrow match: - "parent::*[not(self::m:msup) and not(self::m:msub) and not(self::m:msubsup) and not(self::m:power) and" - " not(self::m:math) ] and " # rule out [x] standing alone - "( IsBracketed(., '(', ')') or IsBracketed(., '[', ']') ) and " - "( IsNode(*[2], 'simple') ) and" - "not(ancestor-or-self::*[contains(@data-intent-property, ':literal:')])" # missing clause: 'a positive fraction that is spoken as an ordinal # (either by the Ordinal preference or by the default rules_' replace: - x: "*[2]" - include: "SharedRules/geometry.yaml" - include: "SharedRules/linear-algebra.yaml" - include: "SharedRules/calculus.yaml" - include: "SharedRules/general.yaml" - include: "SharedRules/default.yaml"