--- # number-sets are a little messy in that the base was converted to a number-set, so we have to match that (simple) case last - name: pos-neg-number-sets tag: number-sets match: "count(*)=2 and *[2][text()='+' or text()='-']" replace: - test: if: "$Verbosity!='Terse'" then: - t: "the" # phrase('the' square root of 25 equals 5) - bookmark: "*[2]/@id" - test: - if: "*[2][text()='+']" then: [t: "positive"] # phrase(set of all 'positive' integers less than 10) else: [t: "negative"] # phrase(set of all 'negative' integers less than minus 10) - bookmark: "*[1]/@id" - test: - if: "*[1][text()='ℂ']" then: [t: "complex numbers"] # phrase('complex numbers' consist of two parts) - else_if: "*[1][text()='ℕ']" then: [t: "natural numbers"] # phrase('natural numbers' are numbers from 1 to infinity) - else_if: "*[1][text()='ℚ']" then: [t: "rational numbers"] # phrase('rational numbers' are the fraction of 2 integers) - else_if: "*[1][text()='ℝ']" then: [t: "real numbers"] # phrase('real numbers' can be both positive and negative) - else_if: "*[1][text()='ℤ']" then: [t: "integers"] # phrase(positive 'integers' are natural numbers above 0) else: [x: "*[1][text()]"] # shouldn't happen - name: dimension-number-sets # should be single digit integer at this point (e.g, R^3) tag: number-sets match: "count(*)=2" replace: - bookmark: "*[1]/@id" - test: - if: "*[1][text()='ℂ']" then: [t: "C"] # phrase(the letter 'C' used to represent complex number) - else_if: "*[1][text()='ℕ']" then: [t: "N"] # phrase(the letter 'N' may represent natural numbers) - else_if: "*[1][text()='ℚ']" then: [t: "Q"] # phrase(the letter 'Q' may represent rational numbers) - else_if: "*[1][text()='ℝ']" then: [t: "R"] # phrase(the letter 'R' may represent real numbers) - else_if: "*[1][text()='ℤ']" then: [t: "Z"] # phrase(the letter 'Z' may represent integers) else: [x: "*[1][text()]"] # shouldn't happen - bookmark: "*[2]/@id" - x: "*[2]" - name: simple-number-sets tag: number-sets match: "count(*)=0" replace: - bookmark: "@id" - test: - if: "text()='ℂ'" then: [t: "the complex numbers"] # phrase('the complex numbers' include 2 parts) - else_if: "text()='ℕ'" then: [t: "the natural numbers"] # phrase('the natural numbers' begin at 1) - else_if: "text()='ℚ'" then: [t: "the rational numbers"] # phrase('the rational numbers' are the fraction of 2 integers) - else_if: "text()='ℝ'" then: [t: "the real numbers"] # phrase('the real numbers' can be both positive and negative) - else_if: "text()='ℤ'" then: [t: "the integers"] # phrase('the integers' are natural numbers above 0) else: [x: "text()"] # shouldn't happen - name: real-part tag: real-part match: "." replace: - bookmark: "@id" - t: "the real part" # phrase('the real part' of a complex number does not include the imaginary part) - name: imaginary-part tag: imaginary-part match: "." replace: - bookmark: "@id" - t: "the imaginary part" # phrase('the imaginary part' is part of a complex number) # rules on scripted vertical bars ('evaluated at') - name: evaluated-at-2 tag: evaluate match: "count(*)=2" replace: - x: "*[1]" - pause: auto - t: "evaluated at" # phrase(results were 'evaluated at' a given point) - pause: auto - x: "*[2]" - name: evaluated-at-3 tag: evaluate match: "count(*)=3" replace: - x: "*[1]" - pause: auto - t: "evaluated at" # phrase(results were 'evaluated at' this point) - pause: auto - x: "*[3]" - t: "minus the same expression evaluated at" # phrase(this result is 'minus the same expression evaluated at' an earlier point) - x: "*[2]" - name: binomial tag: binomial match: "count(*)=2 and not(@data-intent-property)" replace: - x: "*[1]" - t: "choose" # phrase(you can 'choose' a number at random) - x: "*[2]" - name: permutation tag: permutation-symbol match: "count(*)=2 and not(@data-intent-property)" replace: - x: "*[2]" - t: "permutations of" # phrase(the solution involves several 'permutations of' values) - x: "*[1]" - name: intervals tag: [open-interval, open-closed-interval, closed-interval, closed-open-interval] match: "count(*)=2 and not(@data-intent-property)" replace: - test: if: "$Verbosity!='Terse'" then: - t: "the" # phrase('the' square root of 25 equals 5) - x: "translate(name(.),'-', ' ')" - test: if: "$Verbosity!='Terse'" then: - t: "from" # phrase(subtracting 5 'from' 10 gives 5) - x: "*[1]" - t: "to" # phrase(adding 6 'to' 6 equals 12) - x: "*[2]" else: - x: "*[1]" - t: "comma" # phrase(use a 'comma' to divide large numbers or as a decimal point) - x: "*[2]" - name: default-point tag: point match: "count(*)=2 and not(@data-intent-property)" replace: - test: if: "$Verbosity!='Terse'" then: - t: "the" # phrase('the' square root of 25 equals 5) - t: "point" # phrase(a decimal 'point' indicates the fraction component of a number) - x: "*[1]" - t: "comma" # phrase(use a 'comma' to divide large numbers or as a decimal point) - x: "*[2]" - name: absolute-value tag: absolute-value match: "count(*)=1 and not(@data-intent-property)" replace: - test: if: "$Verbosity='Terse'" then: [t: "absolute value"] # phrase(the 'absolute value' of a number represents its distance from 0) else: [t: "the absolute value of"] # phrase('the absolute value of' a number represents its distance from 0) - x: "*[1]" - test: if: "IsNode(*[1], 'leaf') or $Impairment != 'Blindness'" then: [pause: short] else: [pause: short, t: "end absolute value", pause: short] # phrase(show 'end absolute value' position) - name: negative tag: negative match: "count(*)=1 and not(@data-intent-property)" replace: - bookmark: "./@id" - t: "negative" # phrase('negative' numbers are those less than 0) - x: "*[1]" - name: positive tag: positive match: "count(*)=1 and not(@data-intent-property)" replace: - bookmark: "./@id" - t: "positive" # phrase(multiplying two negatives results in a 'positive' number) - x: "*[1]" - name: minus tag: minus match: "count(*)=1 and not(@data-intent-property)" replace: - bookmark: "./@id" - t: "minus" # phrase('negative' numbers are those less than 0) - x: "*[1]" - name: plus tag: plus match: "count(*)=1 and not(@data-intent-property)" replace: - bookmark: "./@id" - t: "plus" # phrase(multiplying two negatives results in a 'positive' number) - x: "*[1]" - name: subscript tag: sub match: "count(*)=2 and not(@data-intent-property)" replace: - x: "*[1]" - test: if: "not($Verbosity='Terse' and DEBUG(*[2])[self::m:mn and not(DEBUG(translate(., '.,', '')!=.))])" # just say "x 1" for terse vs "x sub 1" then: [t: "sub"] # phrase(x 'sub' 2) - x: "*[2]" - test: if: "not(IsNode(*[2],'leaf')) and $Impairment = 'Blindness'" then: - test: if: "$Verbosity='Verbose'" then: [t: "end subscript"] # phrase(this is the 'end subscript' position) else: [t: "end sub"] # phrase(this is the 'end sub' position) - pause: short - name: bigop-both tag: large-op match: "count(*) = 3" replace: - test: if: "$Verbosity!='Terse'" then: [t: "the"] # phrase('the' square root of 25 equals 5) - x: "*[1]" - t: "from" # phrase(subtracting 5 'from' 10 gives 5) - x: "*[2]" - t: "to" # phrase(adding 6 'to' 6 equals 12) - x: "*[3]" - test: if: "following-sibling::*" then: [t: "of"] # phrase(the square root 'of' 25 equals 5) - name: bigop-under tag: large-op match: "count(*)=2 and not(@data-intent-property)" replace: - test: if: "$Verbosity!='Terse'" then: [t: "the"] # phrase('the' square root of 25 equals 5) - x: "*[1]" - t: "over" # phrase(2 'over' 3 equals two thirds) - x: "*[2]" - test: if: "following-sibling::*" then: [t: "of"] # phrase(the square root 'of' 25 equals 5) - name: largeop tag: mrow match: "count(*)=2 and IsInDefinition(*[1], 'LargeOperators')" replace: - test: if: "$Verbosity!='Terse'" then: [t: "the"] # phrase('the' square root of 25 equals 5) - x: "*[1]" - t: "of" # phrase(the square root 'of' 25 equals 5) - x: "*[2]" - name: limit tag: limit match: "count(*)=2 and not(@data-intent-property)" replace: - test: if: "$Verbosity!='Terse'" then: [t: "the"] # phrase('the' limit as x approaches 1) - test: - if: "*[1][.='lim']" then: [t: "limit"] - else_if: "*[1][.='limsup']" then_test: if: "$Verbosity='Terse'" then: [t: "lim sup"] else: [t: "limit superior"] - else_if: "*[1][.='liminf']" then_test: if: "$Verbosity='Terse'" then: [t: "lim inf"] else: [t: "limit inferior"] - else: [x: "*[1]"] - t: "as" # phrase(the limit 'as' x approaches 1) - x: "*[2]" - pause: short - name: vector tag: modified-variable match: "count(*)=2 and *[2][text()='→']" replace: - t: "vector" # phrase(the 'vector' reflects size and direction) - x: "*[1]" - name: default tag: modified-variable match: "count(*)=2 and not(@data-intent-property)" replace: - x: "*[1]" - x: "*[2]" - pause: short - name: default # handles single, double, etc., prime tag: [skip-super, say-super] match: "count(*)=2" replace: - x: "*[1]" - test: if: "name(.)='say-super'" then_test: if: "$Verbosity='Terse'" then: [t: "super"] # phrase(this is a 'super' set of numbers) else: [t: "superscript"] # phrase(a 'superscript' number indicates raised to a power) - x: "*[2]" - pause: short - name: msubsup-skip-super # handles single, double, etc., prime tag: [skip-super, say-super] match: "count(*)=3" replace: - x: "*[1]" - test: if: "$Verbosity='Verbose'" then: [t: "subscript"] # phrase(a 'subscript' may be used to indicate an index) else: [t: "sub"] # phrase(the result is 'sub' optimal) - x: "*[2]" - test: if: "not(IsNode(*[2],'leaf') and $Impairment = 'Blindness')" then: - test: if: "$Verbosity='Verbose'" then: [t: "end subscript"] # phrase(this is the 'end subscript' position) else: [t: "end sub"] # phrase(this is the 'end sub' position) - pause: short else_test: if: "*[2][self::m:mi]" # need a pause in "x sub k prime" so the prime is not associated with the 'k' then: [pause: short] - test: if: "name(.)='say-super'" then_test: if: "$Verbosity='Verbose'" then: [t: "superscript"] # phrase(a 'superscript' number indicates raised to a power) else: [t: "super"] # phrase(this is a 'super' set of numbers) - x: "*[3]" - pause: short - name: sin tag: mi match: "text()='sin'" replace: - bookmark: "@id" - t: "sine" # phrase(the 'sine' of the angle) - name: cos tag: mi match: "text()='cos'" replace: - bookmark: "@id" - test: if: "$Verbosity='Terse'" then: [t: "cos"] # phrase('cos' is the abbreviation for cosine) else: [t: "cosine"] # phrase(find the 'cosine' in a right-angle triangle) - name: tan tag: mi match: "text()='tan' or text()='tg'" replace: - bookmark: "@id" - test: if: "$Verbosity='Terse'" then: [t: "tan"] # phrase(the 'tan' is the ratio of the opposite to the adjacent side of a right-angled triangle) else: [t: "tangent"] # phrase(a 'tangent' is a straight line that touches a curve) - name: sec tag: mi match: "text()='sec'" replace: - bookmark: "@id" - test: if: "$Verbosity='Terse'" then: [t: "seek"] # phrase(to 'seek' a solution) else: [t: "secant"] # phrase(a 'secant' intersects a curve at two or more points) - name: csc tag: mi match: "text()='csc'" replace: - bookmark: "@id" - test: if: "$Verbosity='Terse'" then: [t: "coseek"] # phrase(we will 'cosecant' a solution) else: [t: "cosecant"] # phrase(the 'cosecant' is the reciprocal of the secant) - name: cot tag: mi match: "text()='cot'" replace: - bookmark: "@id" - test: if: "$Verbosity='Terse'" then: [t: "cotan"] # phrase(find the 'cotangent' in a right-angle triangle) else: [t: "cotangent"] # phrase(the 'cotangent' is the reciprocal of the tangent) - name: sinh tag: mi match: "text()='sinh'" replace: - bookmark: "@id" - test: if: "$Verbosity='Terse'" then: [t: "sinch"] # phrase(the word 'sinch' is an abbreviation for hyperbolic sine) else: [t: "hyperbolic sine"] # phrase(the 'hyperbolic sine' is used in mathematics) - name: cosh tag: mi match: "text()='cosh'" replace: - bookmark: "@id" - test: if: "$Verbosity='Terse'" then: [t: "cosh"] # phrase('cosh' is an abbreviation of hyperbolic cosine) else: [t: "hyperbolic cosine"] # phrase(the 'hyperbolic cosine' is a mathematical function) - name: tanh tag: mi match: "text()='tanh'" replace: - bookmark: "@id" - test: if: "$Verbosity='Terse'" then: [t: "tanch"] # phrase('tanch' is shorthand for hyperbolic tangent) else: [t: "hyperbolic tangent"] # phrase('hyperbolic tangent' is a mathematical function) - name: sech tag: mi match: "text()='sech'" replace: - bookmark: "@id" - test: if: "$Verbosity='Terse'" then: [t: "sheck"] # phrase('sheck' is shorthand for hyperbolic secant) else: [t: "hyperbolic secant"] # phrase('hyperbolic secant' is a mathematical function) - name: csch tag: mi match: "text()='csch'" replace: - bookmark: "@id" - test: if: "$Verbosity='Terse'" then: [t: "cosheck"] # phrase('cosheck' is shorthand for hyperbolic cosecant) else: [t: "hyperbolic cosecant"] # phrase('hyperbolic cosecant' is a mathematical function) - name: coth tag: mi match: "text()='coth'" replace: - bookmark: "@id" - test: if: "$Verbosity='Terse'" then: [t: "cotanch"] # phrase('cotanch' is shorthand for hyperbolic cotangent) else: [t: "hyperbolic cotangent"] # phrase(the 'hyperbolic cotangent' is a mathematical function) - name: exponential tag: mi match: "text()='exp'" replace: - bookmark: "@id" - test: if: "$Verbosity='Terse'" then: [t: "exp"] # phrase('exp' means exponential function) else: [t: "exponential"] # phrase('exponential' function) - name: covariance tag: mi match: "text()='Cov'" replace: - bookmark: "@id" - test: if: "$Verbosity='Terse'" then: [t: "Cov"] # phrase('Cov' is shorthand for the covariance function) else: [t: "covariance"] # phrase('covariance' function) - # handle both log and ln name: log tag: mrow variables: [log_is_simple: "IsNode(*[3],'simple')"] match: - "count(*)=3 and" - "*[1][self::m:mi][text()='log' or text()='ln'] and" - "*[2][self::m:mo][text()='⁡']" replace: - bookmark: "*[1]/@id" - test: if: "$log_is_simple" then_test: - if: "*[1][text()='log']" then: [t: "log"] # phrase(the 'log' function is used in mathematics) - else_if: "$Verbosity='Terse'" then: [spell: "'ln'"] else: [t: "natural log"] # phrase(the 'natural log' function is used in mathematics) else: - test: if: "$Verbosity!='Terse' and not(log_is_simple)" then: [t: "the"] # phrase('the' square root of 25 equals 5) - test: - if: "*[1][text()='log']" then: [t: "log"] # phrase(the 'log' function is used in mathematics) - else_if: "$Verbosity='Terse'" then: [spell: "'ln'"] else: [t: "natural log"] # phrase(the 'natural log' function is used in mathematics) - t: "of" # phrase(5 is the square root 'of' 25) - pause: short - x: "*[3]" - name: log-base tag: log-base match: "not(@data-intent-property)" replace: - bookmark: "@id" - test: if: "$Verbosity!='Terse'" then: [t: "the"] # phrase('the' square root of 25 equals 5) - t: "log base" # phrase(the 'log base' is often base 10) - x: "*[1]" - name: log-base-power tag: log-base-power match: "not(@data-intent-property)" replace: - bookmark: "@id" - test: if: "$Verbosity!='Terse'" then: [t: "the"] # phrase('the' square root of 25 equals 5) - t: "log base" # phrase(the 'log base' is often base 10) - x: "*[1]" - pause: medium - test: - if: "*[2][text()=2]" then: [t: "squared"] - else_if: "*[2][text()=3]" then: [t: "cubed"] else: # don't bother with special cases as this isn't likely to happen - t: "raised to the" # phrase(x 'raised to the' second power) - x: "*[2]" - t: "power" # phrase(x raised to the second 'power') - pause: short - name: multi-line # that eliminates the need for the if: else_if: ... # IDEA: set a variable with the word to say for the row (e.g., RowLabel = Row/Case/Line/...) tag: [piecewise, system-of-equations, lines] match: "." variables: # Wikipedia has some tables where all the entire first column is empty (e.g., https://en.wikipedia.org/wiki/List_of_trigonometric_identities) - LineCount: "count(*[not(contains(@data-intent-property, ':continued-row:'))])" - NextLineIsContinuedRow: "false()" # default value - IsColumnSilent: true() replace: - x: "$LineCount" - test: - if: "self::m:piecewise" then: [t: "case"] # phrase(this is the first 'case' of three cases) - else_if: "self::m:system-of-equations" then: [t: "equation"] # phrase(this is the first 'equation' of three equations) else: [t: "line"] # phrase(this is the first 'line' of three lines) - test: - if: "$LineCount != 1" then: [ct: "s"] # plural - pause: short - x: "*" - pause: long - name: default-multiline tag: [mtr, mlabeledtr] match: "parent::m:piecewise or parent::m:system-of-equations or parent::m:lines" variables: [NextLineIsContinuedRow: "following-sibling::*[1][contains(@data-intent-property, ':continued-row:')]"] replace: - test: if: "not($LineCount=1 or contains(@data-intent-property, ':continued-row:'))" then: - test: - if: "parent::m:piecewise" then: [t: "case"] # phrase('case' 1 of 10 cases) - else_if: "parent::m:system-of-equations" then: [t: "equation"] # phrase('equation' 1 of 10 equations) else: [t: "line"] # phrase('line 1 of 10 lines) - x: "count(preceding-sibling::*[not(contains(@data-intent-property, ':continued-row:'))]) + 1" - test: if: .[self::m:mlabeledtr] then: - t: "with label" # phrase(the diagram is complete 'with label') - x: "*[1]/*" - test: if: "not(contains(@data-intent-property, ':continued-row:'))" then: [pause: medium] - test: if: .[self::m:mlabeledtr] then: [x: "*[position()>1]"] else: {x: "*"} - name: default-multiline tag: mtd match: "parent::*[parent::m:piecewise or parent::m:system-of-equations or parent::m:lines]" variables: [LongPause: "$SpeechStyle = 'ClearSpeak' and $ClearSpeak_MultiLinePausesBetweenColumns = 'Long'"] replace: - test: if: "IsInDefinition(*[1], 'ComparisonOperators')" then: [pause: short] - test: if: "*[1][@data-added!='missing-content']" then: [x: " *"] - test: # no pause after each element; medium pause if last element in a row; long pause for last element in matrix unless ClearSpeak override - if: "count(following-sibling::*) = 0 and not($NextLineIsContinuedRow)" then_test: if: "count(../following-sibling::*) > 0" then_test: if: "$LongPause" then: [pause: long] else: [pause: medium] else_test: if: "IsInDefinition(*[1], 'ComparisonOperators')" then: [pause: short] else: [pause: auto] # Matrix/Determinant rules # matrix and determinant are the same other than "matrix"/"determinant" based on the bracketing chars # the pausing logic is pushed down to the # the rules either speak the s (to get "row n") or the s. "column n" spoken if $IsColumnSilent is false - name: 1x1-matrix tag: [matrix, determinant] variables: [IsColumnSilent: true()] match: "count(*)=1 and *[self::m:mtr][count(*) = 1]" replace: - ot: "the" # phrase('the' 1 by 1 matrix M) - t: "1 by 1" # phrase(the '1 by 1' matrix) - test: if: "self::m:determinant" # just need to check the first bracket since we know it must be (, [, or | then: [t: "determinant"] # phrase(the 2 by 2 'determinant')) else: [t: "matrix"] # phrase(the 2 by 2 'matrix') - t: "with entry" # phrase(the 2 by 2 matrix 'with entry' x) - x: "*[1]/*" # simpler reading methods for smaller matrices if the entries are simple - name: 2-or-3x1-matrix tag: matrix variables: [IsColumnSilent: true()] match: - "$ClearSpeak_Matrix != 'SpeakColNum' and " # "simple" isn't used for this preference - "*[self::m:mtr][count(*) = 1] and " # one column - count(*)<=3 and # at least two rows - IsNode(*/*/*,'simple') # IsNode() returns true if all the nodes are simple replace: - t: "the" # phrase('the' 2 by 2 matrix M) - x: count(*) - t: "by 1 column" # phrase(the 2 'by 1 column' matrix) - test: if: "$ClearSpeak_Matrix = 'Vector' or $ClearSpeak_Matrix = 'EndVector'" then: [t: "vector"] # phrase(the 2 by 2 'vector') else: [t: "matrix"] # phrase(the 2 by 2 'matrix') - pause: long - x: "*/*" - test: if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" then: - t: "end" # phrase('end' of matrix) - test: if: $ClearSpeak_Matrix = 'EndVector' then: [t: "vector"] # phrase(the 2 column 'vector') else: [t: "matrix"] # phrase(the 2 by 2 'matrix') - name: default-column-matrix tag: matrix variables: [IsColumnSilent: true()] match: "*[self::m:mtr][count(*) = 1]" replace: - t: "the" # phrase('the' 2 by 2 matrix M) - x: "count(*)" - t: "by 1 column" # phrase(the 2 'by 1 column' matrix) - test: if: "$ClearSpeak_Matrix = 'Vector' or $ClearSpeak_Matrix = 'EndVector'" then: [t: "vector"] # phrase(the 2 column 'vector') else: [t: "matrix"] # phrase(the 2 by 2 'matrix') - pause: long - x: "*" # select the rows (mtr) - test: if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" then: [t: "end matrix"] # phrase(the 'end of matrix' has been reached) - name: 1x2-or-3-matrix tag: matrix variables: [IsColumnSilent: "$SpeechStyle = 'SimpleSpeak' or ($SpeechStyle = 'ClearSpeak' and $ClearSpeak_Matrix != 'SpeakColNum')"] match: - "$ClearSpeak_Matrix != 'SpeakColNum' and " # "simple" isn't used for this preference - count(*)=1 and # one row - count(*[1]/*)<=3 and # at least two cols - IsNode(*/*/*,'simple') # IsNode() returns true if all the nodes are simple replace: - t: "the 1 by" # phrase('the 1 by' 2 matrix) - x: count(*/*) - t: "row" # phrase(the 1 by 4 'row' matrix) - test: if: "$ClearSpeak_Matrix = 'Vector' or $ClearSpeak_Matrix = 'EndVector'" then: [t: "vector"] # phrase('the 1 by' 2 row 'vector') else: [t: "matrix"] # phrase('the 1 by' 2 'matrix') - pause: long - x: "*/*" - test: if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" then: - t: "end" # phrase(the 'end' of matrix has been reached) - test: if: $ClearSpeak_Matrix = 'EndMatrix' then: [t: "matrix"] # phrase(the 2 by 2 'matrix') else: [t: "vector"] # phrase(the 2 by 1 'vector') - name: default-row-matrix tag: matrix variables: [IsColumnSilent: "$SpeechStyle = 'ClearSpeak' and $ClearSpeak_Matrix = 'SilentColNum'"] match: "count(*)=1" # one row replace: - t: "the 1 by" # phrase('the 1 by' 2 matrix) - x: "count(*/*)" - t: "row" # phrase(the 1 by 2 'row' matrix) - test: if: "$ClearSpeak_Matrix = 'Vector' or $ClearSpeak_Matrix = 'EndVector'" then: [t: "vector"] # phrase(the 2 by 1 'vector') else: [t: "matrix"] # phrase(the 2 by 2 'matrix') - pause: long - pause: medium - x: "*/*" # select the cols (mtd) - test: if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" then: - t: "end" # phrase(the 'end' of matrix has been reached) - test: if: $ClearSpeak_Matrix = 'EndMatrix' then: [t: "matrix"] # phrase(the 2 by 2 'matrix') else: [t: "vector"] # phrase(the 2 by 1 'vector') - name: simple-small-matrix tag: [matrix, determinant] match: - "$ClearSpeak_Matrix != 'SpeakColNum' and " # "simple" isn't used for this preference - (count(*)<=3 and count(*[1]/*)<=3) and # no bigger than a 3x3 matrix - IsNode(*/*/*,'simple') # IsNode() returns true if all the nodes are simple variables: [IsColumnSilent: "$SpeechStyle = 'SimpleSpeak' or ($SpeechStyle = 'ClearSpeak' and $ClearSpeak_Matrix != 'SpeakColNum')"] replace: - t: "the" # phrase('the' 1 by 2 matrix M) - x: count(*) - t: "by" # phrase(the 1 'by' 2 matrix) - x: count(*[self::m:mtr][1]/*) - test: if: "self::m:determinant" then: [t: "determinant"] # phrase(the 2 by 2 'determinant') else: [t: "matrix"] # phrase(the 2 by 2 'matrix') - pause: long - x: "*" - test: if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" then: - t: "end" # phrase(the 'end' of matrix has been reached) - test: if: "self::m:determinant" then: [t: "determinant"] # phrase(the 2 by 2 'determinant') else: [t: "matrix"] # phrase(the 2 by 2 'matrix') - name: default-matrix tag: [matrix, determinant] variables: [IsColumnSilent: "$SpeechStyle = 'ClearSpeak' and $ClearSpeak_Matrix = 'SilentColNum'"] match: "not(@data-intent-property)" replace: - t: "the" # phrase('the' 1 by 2 matrix M) - x: "count(*)" - t: "by" # phrase(the 1 'by' 2 matrix) - x: "count(*[self::m:mtr][1]/*)" - test: if: "self::m:determinant" then: [t: "determinant"] # phrase(the 2 by 2 'determinant') else: [t: "matrix"] # phrase(the 2 by 2 'matrix') - pause: long - x: "*" - test: if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" then: - t: "end" # phrase(the 'end' of matrix has been reached) - test: if: "self::m:determinant" then: [t: "determinant"] # phrase(the 2 by 2 'determinant') else: [t: "matrix"] # phrase(the 2 by 2 'matrix's) - name: chemistry-msub tag: [chemical-formula] match: "*[1][text()='msub']" replace: - x: "*[2]" - test: if: "$Verbosity='Verbose'" then: [t: "subscript"] # phrase(H 'subscript' 2) else_test: if: "$Verbosity='Medium'" then: [t: "sub"] # phrase(H 'sub' 2) - x: "*[3]" - name: dimension-by tag: mrow match: dimension-product replace: - insert: nodes: "*" replace: [t: "by", pause: auto] # phrase(3 'by' 5 matrix) - name: chemistry-msup tag: [chemical-formula] match: "count(*)=3 and *[1][text()='msup']" replace: - x: "*[2]" - test: if: "$Verbosity='Verbose'" then: [t: "superscript"] # phrase(H 'superscript' 2) else_test: if: "$Verbosity='Medium'" then: [t: "super"] # phrase(H 'super' 2) - x: "*[3]" - test: if: "following-sibling::*[1][text()='+' or text()='-']" # a little lazy -- assumes chemistry superscripts end with + or - then: [pause: medium] - # There currently is no way to do sub/super for n-ary number of args # Instead, we just deal with up to two prescripts and up to four postscripts (repeating blocks of similar code [UGLY!]) # This hopefully covers all reasonable cases... name: chemistry-scripts tag: [chemical-formula, chemical-nuclide] variables: # computing the number of postscripts is messy because of being optionally present -- we use "mod" to get the count right - Prescripts: "m:mprescripts/following-sibling::*" - NumChildren: "count(*)" # need to stash this since the count is wrong inside '*[...]' below - Postscripts: "*[position()>1 and position() < (last() + ($NumChildren mod 2) -count($Prescripts))]" match: . # should only be msubsup or mmultiscripts at this point replace: - test: if: "$Prescripts" # we have at least one pre sub/super then: # nuclide: speak the superscript first - test: if: "not($Prescripts[2][self::m:none])" then: - test: if: "$Verbosity='Verbose'" then: [t: "superscript"] # phrase(H 'superscript' 2) else_test: if: "$Verbosity='Medium'" then: [t: "super"] # phrase(H 'super' 2) - x: "$Prescripts[2]" - pause: "short" - test: if: "not($Prescripts[1][self::m:none])" then: - test: if: "$Verbosity='Verbose'" then: [t: "subscript"] # phrase(a 'subscript' may be used to indicate an index) else_test: if: "$Verbosity='Medium'" then: [t: "sub"] # phrase(here is a 'sub' total) - x: "$Prescripts[1]" - pause: "short" - test: if: "count($Prescripts) > 2" # can this happen for chemistry??? we allow for one *extra* pre sub/super pair then: - test: if: "not($Prescripts[4][self::m:none])" then: - test: if: "$Verbosity='Verbose'" then: [t: "superscript"] # phrase(H 'superscript' 2) else_test: if: "$Verbosity='Medium'" then: [t: "super"] # phrase(H 'super' 2) - x: "$Prescripts[4]" - pause: "short" - test: if: "not($Prescripts[3][self::m:none])" then: - test: if: "$Verbosity='Verbose'" then: [t: "subscript"] # phrase(H 'subscript' 2) else_test: if: "$Verbosity='Medium'" then: [t: "sub"] # phrase(H 'sub' 2) - x: "$Prescripts[3]" - pause: "short" - x: "*[1]" # base - test: if: "$Postscripts" then: - test: if: "not($Postscripts[1][self::m:none])" then: - test: if: "$Verbosity='Verbose'" then: [t: "subscript"] # phrase(phrase(H 'subscript' 2) else_test: if: "$Verbosity='Medium'" then: [t: "sub"] # phrase(phrase(H 'sub' 2) - x: "$Postscripts[1]" - pause: "short" - test: if: "not($Postscripts[2][self::m:none])" then: - test: if: "$Verbosity='Verbose'" then: [t: "superscript"] # phrase(H 'superscript' 2) else_test: if: "$Verbosity='Medium'" then: [t: "super"] # phrase(H 'super' 2) - x: "$Postscripts[2]" - pause: "short" - test: if: "count($Postscripts) > 2" then: - test: if: "not($Postscripts[3][self::m:none])" then: - test: if: "$Verbosity='Verbose'" then: [t: "subscript"] # phrase(H 'subscript' 2) else_test: if: "$Verbosity='Medium'" then: [t: "sub"] # phrase(H 'sub' 2) - x: "$Postscripts[3]" - pause: "short" - test: if: "not($Postscripts[4][self::m:none])" then: - test: if: "$Verbosity='Verbose'" then: [t: "superscript"] # phrase(H 'superscript' 2) else_test: if: "$Verbosity='Medium'" then: [t: "super"] # phrase(H 'super' 2) - x: "$Postscripts[4]" - pause: "short" - test: if: "count($Postscripts) > 4" then: - test: if: "not($Postscripts[5][self::m:none])" then: - test: if: "$Verbosity='Verbose'" then: [t: "subscript"] # phrase(H 'subscript' 2) else_test: if: "$Verbosity='Medium'" then: [t: "sub"] # phrase(H 'sub' 2) - x: "$Postscripts[5]" - pause: "short" - test: if: "not($Postscripts[6][self::m:none])" then: - test: if: "$Verbosity='Verbose'" then: [t: "superscript"] # phrase(H 'superscript' 2) else_test: if: "$Verbosity='Medium'" then: [t: "super"] # phrase(H 'super' 2) - x: "$Postscripts[6]" - pause: "short" - test: if: "count($Postscripts) > 6" then: - test: if: "not($Postscripts[7][self::m:none])" then: - test: if: "$Verbosity='Verbose'" then: [t: "subscript"] # phrase(H 'subscript' 2) else_test: if: "$Verbosity='Medium'" then: [t: "sub"] # phrase(H 'sub' 2) - x: "$Postscripts[7]" - pause: "short" - test: if: "not($Postscripts[8][self::m:none])" then: - test: if: "$Verbosity='Verbose'" then: [t: "superscript"] # phrase(H 'superscript' 2) else_test: if: "$Verbosity='Medium'" then: [t: "super"] # phrase(H 'super' 2) - x: "$Postscripts[8]" - pause: "short" - test: if: "$Postscripts[last()][not(self::m:none)] and following-sibling::*[1][text()='+' or text()='-']" then: [pause: medium] - name: chemistry tag: chemical-equation match: "." replace: - x: "*" - name: chemical-element tag: chemical-element match: "." replace: - bookmark: "@id" - spell: text() - pause: short - name: chemical-state tag: chemical-state match: "count(*)=1" replace: - bookmark: "*[1]/@id" - test: - if: ".='s'" then: [t: "solid"] # phrase(Boron is a 'solid' in its natural state) - else_if: ".='l'" then: [t: "liquid"] # phrase(water is a 'liquid') - else_if: ".='g'" then: [t: "gas"] # phrase(hydrogen is a 'gas' ) else: [t: "aqueous"] # phrase(an 'aqueous' solution is contained in water) - pause: short - name: chemical-formula-operator-bond tag: chemical-formula-operator match: "@data-chemical-bond" replace: # FIX: this might be better/more efficient if in unicode.yaml - bookmark: "@id" - test: - if: "text()='-' or text() = ':'" then: [t: "single bond"] # phrase(a 'single bond' is formed when two atoms share one pair of electrons) - else_if: "text()='=' or text() = '∷'" then: [t: "double bond"] # phrase(a 'double bond' may occur when two atoms share two pairs of electrons) - else_if: "text()='≡'" then: [t: "triple bond"] # phrase(a 'triple bond' occurs when two atoms share three pairs of electrons) - else_if: "text()='≣'" then: [t: "quadruple bond"] # phrase(a 'quadruple bond' occurs when two atoms share four pairs of electrons) else: [x: "text()"] - name: chemical-formula-operator tag: chemical-formula-operator match: "." replace: x: "text()" - name: chemical-arrow-operator tag: chemical-arrow-operator match: "." replace: # FIX: this might be better/more efficient if in unicode.yaml - bookmark: "@id" - test: - if: ".='→' or .='⟶'" then_test: if: "$Verbosity='Terse'" then: [t: "forms"] # phrase(hydrogen and oxygen 'forms' water ) else: [t: "reacts to form"] # phrase(hydrogen and oxygen 'reacts to form' water) - else_if: ".='⇌' or .='\u1f8d2'" then: [t: "is in equilibrium with"] # phrase(a reactant 'is in equilibrium with' a product) - else_if: ".='\u1f8d4'" then: [t: "is in equilibrium biased to the left with"] # phrase(the reactant 'is in equilibrium biased to the left with' the product) - else_if: ".='\u1f8d3'" then: [t: "is in equilibrium biased to the right with"] # phrase(the reactant 'is in equilibrium biased to the right with' the product) else: [x: "*"] - name: chemical-equation-operator tag: chemical-equation-operator match: "." replace: - bookmark: "@id" - x: "text()" - name: none tag: none match: "../../*[self::m:chemical-formula or self::m:chemical-nuclide]" replace: - t: "" # don't say anything - name: ignore-intent-wrapper tag: intent-wrapper match: "." replace: - x: "*"