@@ -406,8 +406,8 @@ class Assembler {
406406 this . emit ( instr . br , w . labelidx ( depth ) ) ;
407407 }
408408
409- brTable ( labels , defaultIdx ) {
410- this . emit ( w . instr . br_table , w . vec ( labels . map ( i => w . labelidx ( i ) ) ) , w . labelidx ( defaultIdx ) ) ;
409+ brTable ( labels , defaultLabelidx ) {
410+ this . emit ( w . instr . br_table , w . vec ( labels ) , defaultLabelidx ) ;
411411 }
412412
413413 return ( ) {
@@ -417,25 +417,29 @@ class Assembler {
417417 }
418418
419419 // Emit a dense jump table (switch-like) using br_table.
420- switch ( bt , condThunk , caseThunks , defaultThunk ) {
420+ switch ( bt , discrimThunk , numCases , caseCb , defaultThunk ) {
421421 const startStackHeight = this . _blockStack . length ;
422422
423- // Emit one block per case…
424- caseThunks . forEach ( _ => this . _blockOnly ( bt ) ) ;
423+ const labels = [ ] ;
425424
426- const labels = caseThunks . map ( ( _ , i ) => w . labelidx ( i ) ) ;
425+ // Emit one block per case…
426+ for ( let i = 0 ; i < numCases ; i ++ ) {
427+ this . _blockOnly ( bt ) ;
428+ labels . push ( w . labelidx ( i ) ) ;
429+ }
427430
428431 // …and one inner block containing the condition and the br_table.
429432 this . block ( w . blocktype . empty , ( ) => {
430- condThunk ( ) ;
433+ discrimThunk ( ) ;
431434 this . brTable ( labels , w . labelidx ( labels . length ) ) ;
432435 } ) ;
433- caseThunks . forEach ( ( fn , i ) => {
436+
437+ for ( let i = 0 ; i < numCases ; i ++ ) {
434438 const depth = labels . length - ( i + 1 ) ;
435- fn ( depth ) ;
439+ caseCb ( i , depth ) ;
436440 this . break ( depth ) ; // Jump to end.
437441 this . _endBlock ( ) ;
438- } ) ;
442+ }
439443 assert ( this . _blockStack . length === startStackHeight ) ;
440444 }
441445
@@ -820,7 +824,7 @@ export class Compiler {
820824 case pexprs . Terminal :
821825 return ir . terminal ( exp . obj ) ;
822826 case pexprs . UnicodeChar :
823- return ir . unicodeChar ( exp . codePoint ) ;
827+ return ir . unicodeChar ( exp . category ) ;
824828 default :
825829 throw new Error ( `not handled: ${ exp . constructor . name } ` ) ;
826830 }
@@ -849,12 +853,12 @@ export class Compiler {
849853 asm . addFunction ( `$${ name } ` , [ w . valtype . i32 ] , [ w . valtype . i32 ] , ( ) => {
850854 asm . addLocal ( 'ret' , w . valtype . i32 ) ;
851855 asm . addLocal ( 'tmp' , w . valtype . i32 ) ;
856+ const values = this . liftedTerminals . values ( ) ;
852857 asm . switch (
853858 w . blocktype . empty ,
854859 ( ) => asm . localGet ( '__arg0' ) ,
855- this . liftedTerminals
856- . values ( )
857- . map ( str => depth => this . emitTerminal ( ir . terminal ( str ) , depth ) ) ,
860+ values . length ,
861+ ( i , depth ) => this . emitTerminal ( ir . terminal ( values [ i ] ) , depth ) ,
858862 ( ) => asm . emit ( w . instr . unreachable ) ,
859863 ) ;
860864 asm . localGet ( 'ret' ) ;
@@ -1125,28 +1129,30 @@ export class Compiler {
11251129 emitDispatch ( { child : exp , patterns} ) {
11261130 const { asm} = this ;
11271131
1128- const cases = patterns . map ( ( actuals , i ) => ( ) => {
1132+ const handleCase = i => {
11291133 // Substitute the params to get the concrete expression that
11301134 // needs to be inserted here.
1131- let newExp = ir . substituteParams ( exp , actuals ) ;
1135+ let newExp = ir . substituteParams ( exp , patterns [ i ] ) ;
11321136 if ( newExp . type === 'Apply' ) {
11331137 // If the application has arguments, we need to dispatch to the
11341138 // correct specialized version of the rule.
11351139 newExp = ir . apply ( ir . specializedName ( newExp ) ) ;
11361140 }
11371141 this . emitPExpr ( newExp ) ;
1138- } ) ;
1139- if ( cases . length === 1 ) {
1140- cases [ 0 ] ( ) ; // No need for a switch.
1142+ } ;
1143+ if ( patterns . length === 1 ) {
1144+ handleCase ( 0 ) ; // No need for a switch.
11411145 return ;
11421146 }
1143- assert ( cases . length > 1 ) ;
1147+ assert ( patterns . length > 1 ) ;
11441148
11451149 asm . switch (
11461150 w . blocktype . empty ,
11471151 ( ) => asm . localGet ( '__arg0' ) ,
1148- cases ,
1152+ patterns . length ,
1153+ handleCase ,
11491154 ( ) => {
1155+ asm . emit ( 'herre' ) ;
11501156 asm . emit ( w . instr . unreachable ) ;
11511157 } ,
11521158 ) ;
@@ -1195,7 +1201,7 @@ export class Compiler {
11951201 case 'Range' : this . emitRange ( exp ) ; break ;
11961202 case 'Plus' : this . emitPlus ( exp ) ; break ;
11971203 case 'Terminal' : this . emitTerminal ( exp ) ; break ;
1198- case 'UnicodeChar' : this . emitFail ( ) ; break ; // TODO: Handle this properly
1204+ case 'UnicodeChar' : this . emitUnicodeChar ( exp ) ; break ;
11991205 case 'Param' :
12001206 // Fall through (Params should not exist at codegen time).
12011207 default :
@@ -1422,7 +1428,37 @@ export class Compiler {
14221428 asm . newTerminalNodeWithSavedPos ( ) ;
14231429 asm . localSet ( 'ret' ) ;
14241430 }
1431+
1432+ emitUnicodeChar ( exp ) {
1433+ const { asm} = this ;
1434+ this . maybeEmitSpaceSkipping ( ) ;
1435+
1436+ const handleDefault = ( ) => {
1437+ // TODO: Implement the slow case by calling out to the host.
1438+ asm . emit ( w . instr . unreachable ) ;
1439+ } ;
1440+
1441+ // TODO: Add support for more categories, by calling out to the host.
1442+ assert ( [ 'Ll' , 'Lu' , 'Ltmo' ] . includes ( exp . category ) ) ;
1443+
1444+ const caseCb = ( i , depth ) => {
1445+ const c = String . fromCharCode ( i ) ;
1446+ if (
1447+ ( exp . category === 'Lu' && 'A' <= c && c <= 'Z' ) ||
1448+ ( exp . category === 'Ll' && 'a' <= c && c <= 'z' )
1449+ ) {
1450+ asm . incPos ( ) ;
1451+ asm . newTerminalNodeWithSavedPos ( ) ;
1452+ asm . localSet ( 'ret' ) ;
1453+ } else {
1454+ asm . setRet ( 0 ) ;
1455+ }
1456+ asm . break ( depth ) ;
1457+ } ;
1458+ asm . switch ( w . blocktype . empty , ( ) => asm . currCharCode ( ) , 128 , caseCb , handleDefault ) ;
1459+ }
14251460}
1461+
14261462// Memory layout:
14271463// - First page is for the PExpr stack (origPos, etc.), growing downards.
14281464// - 2nd page is for input buffer (max 64k for now).
0 commit comments