Skip to content

Commit affe515

Browse files
committed
A much better version for elementary math.
Canonicalize now creates an mstack with only msrow, mscarries, and msline. They are all padded with `<none/>` to be an even grid. All entries of msrow and mscarries are normalized to having single entries. Ids are added to all the synthesized mn, none, and mscarry elements. They are done algorithmically, so they should be guessable (useful for writing the tests). This grid layout makes things easier, especially to for navigation. The speech rules all work. Some navigation rules fail. They have __not__ been updated so they need to be fixed first before working on navigate.yaml.
1 parent 78106c7 commit affe515

8 files changed

Lines changed: 698 additions & 300 deletions

File tree

‎Rules/Languages/en/SharedRules/elem-math.yaml‎

Lines changed: 67 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -83,35 +83,32 @@
8383
- name: mstack-default
8484
tag: mstack
8585
match: "."
86-
variables:
87-
- Operator: "@data-operator"
88-
- StackAlign: "@stackalign"
8986
replace:
9087
- bookmark: "@id"
9188
- test:
92-
- if: "$StackAlign='center'"
89+
- if: "@stackalign='center'"
9390
then: [t: "centered"]
94-
- else_if: "$StackAlign='left'"
91+
- else_if: "@stackalign='left'"
9592
then: [t: "left-aligned"]
9693
- test:
97-
- if: "$Operator='+'"
94+
- if: "@data-operator='+'"
9895
then:
9996
- t: "addition problem"
100-
- t: ";"
101-
- else_if: "$Operator='-' or $Operator='−'"
97+
- pause: long
98+
- else_if: "@data-operator='-' or @data-operator='−'"
10299
then:
103100
- t: "subtraction problem"
104-
- t: ";"
105-
- else_if: "$Operator='×'"
101+
- pause: long
102+
- else_if: "@data-operator='×'"
106103
then:
107104
- t: "multiplication problem"
108-
- t: ";"
105+
- pause: long
109106
else: [t: "rows of numbers"]
110107
- x: "*"
111108
- pause: 500
112109
- bookmark: "@id"
113110
- test:
114-
if: "$Operator=' ' or not($Operator)"
111+
if: "@data-operator=' ' or not(@data-operator)"
115112
then: [t: "end of numbers"]
116113
else: [t: "end of problem"]
117114
- pause: 200
@@ -175,11 +172,12 @@
175172

176173
- name: elem-math-mn-decimal
177174
tag: mn
178-
match: "@data-decimalpoint and contains(., @data-decimalpoint) and string-length(.) - string-length(translate(., @data-decimalpoint, '')) = 1 and
179-
(@data-decimalpoint != $DecimalSeparators or @data-decimalpoint = ',') and
175+
match: "$DecPt!='' and contains(., $DecPt) and string-length(.) - string-length(translate(., $DecPt, '')) = 1 and
176+
($DecPt != $DecimalSeparators or $DecPt = ',') and
180177
((parent::*[self::m:mlongdiv] and count(preceding-sibling::*) < 3) or @data-elem-whole='true')"
181178
variables:
182-
- DecSep: "@data-decimalpoint"
179+
- DecPt: "IfThenElse(parent::m:msrow/@data-decimalpoint, parent::m:msrow/@data-decimalpoint, IfThenElse(parent::m:mscarries/@data-decimalpoint, parent::m:mscarries/@data-decimalpoint, IfThenElse(ancestor::m:mstack/@data-decimalpoint, ancestor::m:mstack/@data-decimalpoint, ancestor::m:mlongdiv/@data-decimalpoint)))"
180+
- DecSep: "$DecPt"
183181
replace:
184182
- bookmark: "@id"
185183
- x: "concat(translate(translate(substring-before(., $DecSep), ',', ' '), '\u00A0\u202F', ' '), $DecimalSeparators, translate(translate(substring-after(., $DecSep), ',', ' '), '\u00A0\u202F', ' '))"
@@ -201,12 +199,14 @@
201199
- name: elem-math-repeating-decimal-mn
202200
tag: mn
203201
match: "@data-repeating-decimal-digit='true'"
202+
variables:
203+
- DecPt: "IfThenElse(parent::m:msrow/@data-decimalpoint, parent::m:msrow/@data-decimalpoint, IfThenElse(ancestor::m:mstack/@data-decimalpoint, ancestor::m:mstack/@data-decimalpoint, '.'))"
204204
replace:
205205
- bookmark: "@id"
206206
- test:
207-
- if: ".='.' and @data-decimalpoint='.'"
207+
- if: ".='.' and $DecPt='.'"
208208
then: [t: "point"]
209-
- else_if: ".=',' and @data-decimalpoint=','"
209+
- else_if: ".=',' and $DecPt=','"
210210
then: [t: "comma"]
211211
- else_if: ".=' '"
212212
then: [t: "space"]
@@ -233,6 +233,7 @@
233233
- t: "in the"
234234
- x: "DefinitionValue(string($Column), 'Speech', 'ElemMathPlace')"
235235
- t: "column"
236+
# FIX: this looks like an AI-generated mess
236237
- test:
237238
- if: "following-sibling::*[self::m:mn] and ../following-sibling::*[1][self::m:mscarries]"
238239
then: [pause: 200]
@@ -334,149 +335,57 @@
334335

335336
# --- msrow / msline (general) ---
336337

337-
- name: msrow-stack-trailing-operator
338-
tag: msrow
339-
match:
340-
- "ancestor::m:mstack and not(ancestor::m:mlongdiv) and"
341-
- "following-sibling::*[self::m:msline] and"
342-
- "not(preceding-sibling::*[self::m:msline]) and"
343-
- "not(*[self::m:none]) and not(*[self::m:mo][.='×']) and"
344-
- "*[last()][self::m:mo] and (preceding-sibling::*[self::m:mn] or preceding-sibling::*[self::m:msrow])"
345-
replace:
346-
- x: "*[last()][self::m:mo]"
347-
- x: "./*[self::m:mn]"
348-
349-
- name: msrow-stack-leading-operator
350-
tag: msrow
351-
match:
352-
- "ancestor::m:mstack and not(ancestor::m:mlongdiv) and"
353-
- "following-sibling::*[self::m:msline] and"
354-
- "not(preceding-sibling::*[self::m:msline]) and"
355-
- "not(*[self::m:none]) and not(*[self::m:mo][.='×']) and"
356-
- "*[1][self::m:mo] and (preceding-sibling::*[self::m:mn] or preceding-sibling::*[self::m:msrow])"
357-
replace:
358-
- x: "*[self::m:mo]"
359-
- x: "./*[self::m:mn]"
360-
361338
- name: msrow-stack-before-msline
362339
tag: msrow
363340
match:
364-
- "ancestor::m:mstack and not(ancestor::m:mlongdiv) and"
365-
- "following-sibling::*[self::m:msline] and"
366-
- "not(preceding-sibling::*[self::m:msline]) and"
367-
- "not(*[self::m:none]) and not(*[self::m:mo][.='×'])"
368-
replace:
369-
- x: "./*[self::m:mn]"
370-
- test:
371-
if: "*[self::m:mo] and following-sibling::*[self::m:msrow]"
372-
then: [x: "*[self::m:mo]"]
373-
374-
- name: msrow-operator-on-right
375-
tag: msrow
376-
match: "count(*) > 1 and *[last()][self::m:mo]"
377-
replace:
378-
- x: "*[last()]"
379-
- x: "*[position()!=last()]"
380-
381-
- name: msrow-default-zero-shift
382-
tag: msrow
383-
match:
384-
- "number(IfThenElse(@data-position, @data-position, IfThenElse(@position, @position, 0)))=0"
341+
- "ancestor::m:mstack and not(ancestor::m:mlongdiv)"
385342
variables:
386-
- AfterMsline: "preceding-sibling::*[self::m:msline] or (preceding-sibling::*[self::m:msrow] and preceding-sibling::*[self::m:msrow]/preceding-sibling::*[self::m:msline])"
387-
- BetweenMslines: "preceding-sibling::*[self::m:msline] and following-sibling::*[self::m:msline]"
388343
- RowPos: "number(IfThenElse(@data-position, @data-position, IfThenElse(@position, @position, 0)))"
389-
- IsLastPartialRow: "following-sibling::*[1][self::m:msline]"
390-
- FollowingIsLastPartial: "following-sibling::*[1][self::m:msrow or self::m:mn][following-sibling::*[1][self::m:msline]]"
391-
- FollowingRowPos: "number(IfThenElse(following-sibling::*[1]/@data-position, following-sibling::*[1]/@data-position, IfThenElse(following-sibling::*[1]/@position, following-sibling::*[1]/@position, 0)))"
392-
- ShiftAmount: "IfThenElse($IsLastPartialRow, 0, IfThenElse($FollowingIsLastPartial, $FollowingRowPos, $RowPos + 1))"
344+
- Operator: "ancestor::m:mstack/@data-operator"
393345
replace:
394346
- test:
395-
- if: "$BetweenMslines and $IsLastPartialRow"
396-
then:
397-
- x: "*"
398-
- t: ";"
399-
- else_if: "$BetweenMslines"
347+
- if: "$RowPos != 0"
400348
then:
401-
- x: "*"
402-
- t: ","
403-
- t: "shifted to the left"
404-
- x: "$ShiftAmount"
349+
- pause: medium
405350
- test:
406-
if: "$ShiftAmount=1"
407-
then: [t: "digit"]
408-
else: [t: "digits"]
409-
- test:
410-
- if: "following-sibling::*[self::m:msrow or self::m:mn][following-sibling::*[self::m:msrow or self::m:mn]] or ./*[@data-elem-block-parts='true']"
411-
then: [t: ";"]
412-
- else_if: "$AfterMsline"
413-
then:
414-
- x: "*"
415-
- t: ";"
416-
- else: [x: "*"]
417-
418-
- name: msrow-default-shifted
419-
tag: msrow
420-
match:
421-
- "not(parent::*[self::m:mlongdiv]) and"
422-
- "number(IfThenElse(@data-position, @data-position, IfThenElse(@position, @position, 0)))!=0"
423-
variables:
424-
- RowPos: "number(IfThenElse(@data-position, @data-position, IfThenElse(@position, @position, 0)))"
425-
- AfterMsline: "preceding-sibling::*[self::m:msline] or (preceding-sibling::*[self::m:msrow] and preceding-sibling::*[self::m:msrow]/preceding-sibling::*[self::m:msline])"
426-
- BetweenMslines: "preceding-sibling::*[self::m:msline] and following-sibling::*[self::m:msline]"
427-
- IsLastPartialRow: "following-sibling::*[1][self::m:msline]"
428-
- FollowingIsLastPartial: "following-sibling::*[1][self::m:msrow or self::m:mn][following-sibling::*[1][self::m:msline]]"
429-
- FollowingRowPos: "number(IfThenElse(following-sibling::*[1]/@data-position, following-sibling::*[1]/@data-position, IfThenElse(following-sibling::*[1]/@position, following-sibling::*[1]/@position, 0)))"
430-
- ShiftAmount: "IfThenElse($IsLastPartialRow, 0, IfThenElse($FollowingIsLastPartial, $FollowingRowPos, $RowPos + 1))"
431-
replace:
432-
- test:
433-
- if: "$BetweenMslines and $IsLastPartialRow"
434-
then:
435-
- x: "*"
436-
- t: ";"
437-
- else_if: "$BetweenMslines"
438-
then:
439-
- x: "*"
440-
- t: ","
441-
- t: "shifted to the left"
442-
- x: "$ShiftAmount"
351+
if: "$RowPos > 0"
352+
then:
353+
- t: "shifted to the left"
354+
- x: "$RowPos"
355+
else:
356+
- t: "shifted to the right"
357+
- x: "-$RowPos"
443358
- test:
444-
if: "$ShiftAmount=1"
359+
if: "$RowPos=1 or $RowPos=-1"
445360
then: [t: "digit"]
446361
else: [t: "digits"]
447-
- test:
448-
- if: "following-sibling::*[self::m:msrow or self::m:mn][following-sibling::*[self::m:msrow or self::m:mn]] or ./*[@data-elem-block-parts='true']"
449-
then: [t: ";"]
450-
- else_if: "$AfterMsline"
362+
- pause: short
363+
- x: "ElemMathRowDigits(.)"
364+
- test:
365+
# speak the operator if before the the first msline, but not after it or directly before it (1+2+3 equals ...)
366+
# if it is a multiplication problem, then the second chunk will be addition, but it won't be marked as such
367+
if: "$Operator and following-sibling::*[following-sibling::m:msline] and not(following-sibling::*[1][self::m:msline])"
451368
then:
452-
- x: "*"
453-
- t: ";"
454-
- else:
455-
- pause: 500
456-
- t: "shifted to the left"
457-
- x: "$RowPos"
369+
- pause: short
458370
- test:
459-
if: "$RowPos=1"
460-
then:
461-
- t: "digit"
462-
- t: ","
463-
else: [t: "digits"]
464-
- x: "*"
465-
- t: ";"
371+
if: "preceding-sibling::*[self::m:msline]"
372+
then: [x: "'+'"] # second chunk of multiplication problem
373+
else: [x: "$Operator"]
374+
- pause: short
466375

467376
- name: msline-default-zero
468377
tag: msline
469378
match: "number(IfThenElse(@data-position, @data-position, IfThenElse(@position, @position, 0)))=0"
470379
replace:
471380
- test:
472381
- if: "not(following-sibling::*) and preceding-sibling::*[1][self::m:msrow][*[self::m:none]]"
473-
then: [t: ";"]
382+
then: [pause: long]
474383
- else: [pause: 200]
475384
- t: "equals"
476385
- test:
477386
if: "following-sibling::*[1][self::m:mscarries]"
478387
then: [pause: 200]
479-
else: [t: ";"]
388+
else: [pause: long]
480389

481390
- name: msline-default-shifted
482391
tag: msline
@@ -541,8 +450,8 @@
541450
- name: none-in-mscarries
542451
tag: none
543452
match:
544-
- "parent::*[self::m:mscarries] and count(following-sibling::*) > 0 and"
545-
- "(../@crossout and ../@crossout!='none') or count(preceding-sibling::*)=0"
453+
- "parent::*[self::m:mscarries] and not(@data-changed='added') and count(following-sibling::*) > 0 and"
454+
- "(../@crossout and ../@crossout!='none') or (not(@data-changed='added') and count(preceding-sibling::*[not(@data-changed='added')])=0)"
546455
variables:
547456
- Column: "number(@data-elem-carry-column)"
548457
replace:
@@ -647,23 +556,24 @@
647556
- Operator: "ancestor::m:mstack/@data-operator"
648557
replace:
649558
- x: "*"
559+
# FIX: this is an AI-generated mess
650560
- test:
651561
- if: "following-sibling::*[1][self::m:mscarries] and ($Operator='-' or $Operator='−')"
652-
then: [t: ";"]
562+
then: [pause: long]
653563
- else_if: "following-sibling::*[1][self::m:mscarries] and $Operator='×'"
654564
then:
655565
- t: "next row"
656-
- t: ";"
566+
- pause: long
657567
- else_if: "@data-elem-carry-terminator='mscarry'"
658568
then: [pause: 200]
659569
- else_if: "@data-elem-carry-terminator='none' and ($Operator='-' or $Operator='−' or $Operator='+')"
660-
then: [t: ";"]
570+
then: [pause: long]
661571
- else_if: "($Operator='-' or $Operator='−') and not(following-sibling::*[1][self::m:mscarries])"
662572
then: [pause: 200]
663573
- else_if: "$Operator='×' and not(following-sibling::*[1][self::m:mscarries]) and (following-sibling::*[self::m:mn] or following-sibling::*[self::m:msrow])"
664574
then: [pause: 200]
665575
- else_if: "$Operator='×' and not(following-sibling::*[1][self::m:mscarries]) and not(following-sibling::*[self::m:mn] or following-sibling::*[self::m:msrow])"
666-
then: [t: ";"]
576+
then: [pause: long]
667577
- else: [pause: 200]
668578

669579
# --- per-digit mn in multi-column msrow ---
@@ -673,10 +583,11 @@
673583
match: "parent::m:msrow and (ancestor::m:mstack or ancestor::m:mlongdiv) and @data-elem-column and not(@data-elem-whole='true')"
674584
variables:
675585
- Column: "number(@data-elem-column)"
676-
- DecimalPoint: "@data-decimalpoint"
586+
- DecimalPoint: "parent::m:msrow/@data-decimalpoint"
677587
- InLongdiv: "ancestor::m:mlongdiv"
678588
replace:
679589
- bookmark: "@id"
590+
# FIX: this seems like an AI-generated mess
680591
- test:
681592
- if: ".=',' and $DecimalPoint!=','"
682593
then: [t: "comma"]
@@ -687,21 +598,18 @@
687598
- else_if: ".!='.' or $DecimalPoint='.'"
688599
then: [x: "translate(., $BlockSeparators, '')"]
689600
- test:
690-
- if: "not($InLongdiv)"
691-
then: []
692-
- else:
693-
- pause: 100
694-
- test:
695-
- if: "not($InLongdiv)"
696-
then: []
697-
- else_if: "$Column=-1"
698-
then: [t: "decimal point"]
699-
- else_if: "DefinitionValue(string($Column), 'Speech', 'ElemMathPlace') != ''"
700-
then:
701-
- t: "in the"
702-
- x: "DefinitionValue(string($Column), 'Speech', 'ElemMathPlace')"
703-
- t: "column"
704-
- else:
705-
- t: "in the ten to the"
706-
- x: "$Column"
707-
- t: "column"
601+
- if: "$InLongdiv"
602+
then:
603+
- pause: short
604+
- test:
605+
- if: "$Column=-1"
606+
then: [t: "decimal point"]
607+
- else_if: "DefinitionValue(string($Column), 'Speech', 'ElemMathPlace') != ''"
608+
then:
609+
- t: "in the"
610+
- x: "DefinitionValue(string($Column), 'Speech', 'ElemMathPlace')"
611+
- t: "column"
612+
- else:
613+
- t: "in the ten to the"
614+
- x: "$Column"
615+
- t: "column"

0 commit comments

Comments
 (0)