@@ -32,9 +32,10 @@ import TexError from './TexError.js';
3232import { MmlNode , AbstractMmlNode } from '../../core/MmlTree/MmlNode.js' ;
3333import { ParseInput , ParseResult } from './Types.js' ;
3434import ParseOptions from './ParseOptions.js' ;
35- import { StackItem , EnvList } from './StackItem.js' ;
35+ import { BaseItem , StackItem , EnvList } from './StackItem.js' ;
3636import { Token } from './Token.js' ;
3737import { OptionList } from '../../util/Options.js' ;
38+ import { TexConstant } from './TexConstants.js' ;
3839
3940
4041/**
@@ -66,6 +67,11 @@ export default class TexParser {
6667 */
6768 public currentCS : string = '' ;
6869
70+ /**
71+ * A stack to save the string positions when we restart the parser.
72+ */
73+ private saveI : number = 0 ;
74+
6975 /**
7076 * @constructor
7177 * @param {string } _string The string to parse.
@@ -88,6 +94,7 @@ export default class TexParser {
8894 this . stack = new Stack ( this . itemFactory , ENV , inner ? isInner : true ) ;
8995 this . Parse ( ) ;
9096 this . Push ( this . itemFactory . create ( 'stop' ) ) ;
97+ this . updateResult ( this . string , this . i ) ;
9198 this . stack . env = ENV ;
9299 }
93100
@@ -135,10 +142,14 @@ export default class TexParser {
135142 * @return {ParseResult } The output of the parsing function.
136143 */
137144 public parse ( kind : HandlerType , input : ParseInput ) : ParseResult {
138- return this . configuration . handlers . get ( kind ) . parse ( input ) ;
145+ const i = this . saveI ;
146+ this . saveI = this . i ;
147+ let result = this . configuration . handlers . get ( kind ) . parse ( input ) ;
148+ this . updateResult ( input [ 1 ] , i ) ;
149+ this . saveI = i ;
150+ return result ;
139151 }
140152
141-
142153 /**
143154 * Maps a token to its "parse value" if it exists.
144155 * @param {HandlerType } kind Configuration name.
@@ -195,6 +206,11 @@ export default class TexParser {
195206 * @param {StackItem|MmlNode } arg The new item.
196207 */
197208 public Push ( arg : StackItem | MmlNode ) {
209+ if ( arg instanceof BaseItem ) {
210+ arg . startI = this . saveI ;
211+ arg . stopI = this . i ;
212+ arg . startStr = this . string ;
213+ }
198214 if ( arg instanceof AbstractMmlNode && arg . isInferred ) {
199215 this . PushAll ( arg . childNodes ) ;
200216 } else {
@@ -223,6 +239,7 @@ export default class TexParser {
223239 }
224240 let node = this . stack . Top ( ) . First ;
225241 this . configuration . popParser ( ) ;
242+ node . attributes . set ( TexConstant . Attr . LATEX , this . string ) ;
226243 return node ;
227244 }
228245
@@ -511,5 +528,111 @@ export default class TexParser {
511528 return this . configuration . nodeFactory . create ( kind , ...rest ) ;
512529 }
513530
531+ /**
532+ * Finalizes the LaTeX for the topmost Mml element on the stack after parsing
533+ * has been completed.
534+ *
535+ * @param {string } input The LaTeX input string for the parser.
536+ * @param {number } old The last parsing position.
537+ */
538+ // Currently works without translating environments that generate typesetting.
539+ private updateResult ( input : string , old : number ) {
540+ let node = this . stack . Prev ( true ) as MmlNode ;
541+ if ( ! node ) {
542+ return ;
543+ }
544+ // TODO: This can probably be removed once processed. But needs more
545+ // testing.
546+ let existing = node . attributes . get ( TexConstant . Attr . LATEXITEM ) ;
547+ if ( existing !== undefined ) {
548+ node . attributes . set ( TexConstant . Attr . LATEX , existing ) ;
549+ return ;
550+ }
551+ old = old < this . saveI ? this . saveI : old ;
552+ let str = old !== this . i ? this . string . slice ( old , this . i ) : input ;
553+ str = str . trim ( ) ;
554+ if ( ! str ) {
555+ return ;
556+ }
557+ if ( input === '\\' ) {
558+ str = '\\' + str ;
559+ }
560+ // These are the cases to handle sub and superscripts.
561+ if ( node . attributes . get ( TexConstant . Attr . LATEX ) === '^' && str !== '^' ) {
562+ if ( str === '}' ) {
563+ this . composeBraces ( node . childNodes [ 2 ] ) ;
564+ } else {
565+ node . childNodes [ 2 ] . attributes . set ( TexConstant . Attr . LATEX , str ) ;
566+ }
567+ if ( node . childNodes [ 1 ] ) {
568+ const sub = node . childNodes [ 1 ] . attributes . get ( TexConstant . Attr . LATEX ) ;
569+ this . composeLatex ( node , `_${ sub } ^` , 0 , 2 ) ;
570+ } else {
571+ this . composeLatex ( node , '^' , 0 , 2 ) ;
572+ }
573+ return ;
574+ }
575+ if ( node . attributes . get ( TexConstant . Attr . LATEX ) === '_' && str !== '_' ) {
576+ if ( str === '}' ) {
577+ this . composeBraces ( node . childNodes [ 1 ] ) ;
578+ } else {
579+ node . childNodes [ 1 ] . attributes . set ( TexConstant . Attr . LATEX , str ) ;
580+ }
581+ if ( node . childNodes [ 2 ] ) {
582+ const sub = node . childNodes [ 2 ] . attributes . get ( TexConstant . Attr . LATEX ) ;
583+ this . composeLatex ( node , `^${ sub } _` , 0 , 1 ) ;
584+ } else {
585+ this . composeLatex ( node , '_' , 0 , 1 ) ;
586+ }
587+ return ;
588+ }
589+ if ( str === '}' ) {
590+ this . composeBraces ( node ) ;
591+ return ;
592+ }
593+ node . attributes . set ( TexConstant . Attr . LATEX , str ) ;
594+ }
595+
596+ /**
597+ * Composing the LaTeX expression for sub or superscript elements.
598+ *
599+ * @param {MmlNode } node The Mml node.
600+ * @param {string } comp Intermediate string.
601+ * @param {number } pos1 Position of child for lefthand side of string.
602+ * @param {number } pos2 Position of child for righthand side of string.
603+ */
604+ private composeLatex (
605+ node : MmlNode , comp : string , pos1 : number , pos2 : number ) {
606+ const expr = node . childNodes [ pos1 ] . attributes . get ( TexConstant . Attr . LATEX ) + comp +
607+ node . childNodes [ pos2 ] . attributes . get ( TexConstant . Attr . LATEX ) ;
608+ node . attributes . set ( TexConstant . Attr . LATEX , expr ) ;
609+ }
610+
611+ /**
612+ * Adds the LaTeX content for this node as a braced expression.
613+ *
614+ * @param {MmlNode } atom The current Mml node.
615+ */
616+ private composeBraces ( atom : MmlNode ) {
617+ if ( ! atom ) return ;
618+ let str = this . composeBracedContent ( atom ) ;
619+ atom . attributes . set ( TexConstant . Attr . LATEX , `{${ str } }` ) ;
620+ }
621+
622+ /**
623+ * Composes the content of a braced expression.
624+ *
625+ * @param {MmlNode } atom The current Mml node.
626+ */
627+ private composeBracedContent ( atom : MmlNode ) {
628+ let children = atom . childNodes [ 0 ] ?. childNodes ;
629+ let expr = '' ;
630+ for ( const child of children ) {
631+ let att = ( child . attributes ?. get ( TexConstant . Attr . LATEX ) || '' ) as string ;
632+ if ( ! att ) continue ;
633+ expr += ( expr && expr . match ( / [ a - z A - Z ] $ / ) && att . match ( / ^ [ a - z A - Z ] / ) ) ? ' ' + att : att ;
634+ }
635+ return expr ;
636+ }
514637
515638}
0 commit comments