import { AST , checkLit , builders } from './core' ; import { lang , ZebuLanguageReturning , ZebuLanguage , createLanguage , } from './lang' ; import { assertUnreachable } from './util' ; const { alt , seq , ident } = builders ; type Fixity = 'left' | 'right' | 'pre' | 'post' ; type SeqFn = ( ... xs : unknown [ ] ) => unknown ; type OpExpr = { pattern : string [ ] ; fn : SeqFn ; } ; type Rule = { fixity : Fixity ; operators : OpExpr [ ] ; } ; export const op = lang `
  Program   = Rule* RootRule? : 
    ${ ( rules , root ) => createLanguage ( buildAST ( rules , root ) ) };
  Rule      = Fixity Expr+ : 
    ${ ( fixity , operators ) : Rule => ( { fixity , operators } ) };
  Expr      = Pattern ":" value : 
    ${ ( pattern , _ , fn ) : OpExpr => ( { pattern , fn } ) };
  Pattern   = value+;
  RootRule  = "root" identifier : ${ ( _ , x ) => x };
  Fixity    = "left" | "right" | "pre" | "post";
` as ZebuLanguageReturning < ZebuLanguage > ; export function buildAST ( rules , rootExpr ) { const ruleset = { type : 'ruleset' , rules : [ ] } ; let next = ident ( '0' ) ; for ( const [ i , rule ] of rules . entries ( ) ) { const self = ident ( String ( i ) ) ; next = ident ( String ( i + 1 ) ) ; const push = ( fn , ... exprs ) => ruleset . rules . push ( { name : String ( i ) , expr : alt ( seq ( fn , ... exprs ) , next ) , } ) ; const opAlts = alt ( ... rule . operators . map ( ( { pattern , fn } ) => seq ( ( ) => fn , ... pattern . map ( checkLit ) ) ) ) ; switch ( rule . fixity ) { case 'left' : push ( ( l , op , r ) => op ( l , r ) , self , opAlts , next ) ; break ; case 'right' : push ( ( l , op , r ) => op ( l , r ) , next , opAlts , self ) ; break ; case 'pre' : push ( ( op , r ) => op ( r ) , opAlts , self ) ; break ; case 'post' : push ( ( l , op ) => op ( l ) , self , opAlts ) ; break ; default : assertUnreachable ( rule . fixity ) ; } } const rootASTNode = rootExpr ? ident ( rootExpr ) : builders . terminal ( 'value' ) ; ruleset . rules . push ( { name : ( next as AST & { type : 'terminal' ; } ) . value , expr : alt ( builders . structure ( '(' , ident ( '0' ) , ')' ) , rootASTNode ) , } ) ; return ruleset ; }