import { compileFragment } from "./compiler" ; import { Rule , Compiler , InputNodeExpr , Token , nodeBaseDefault , Reshape , Seq , Ref , Not , Or , Eof , Repeat , Atom , Regex , InternalParser , REF , SEQ , NOT , OR , REPEAT , TOKEN , REGEX , ATOM , EOF , PairOpen , PAIR_OPEN , PairClose , PAIR_CLOSE , } from "./types" ; let cnt = 2 ; const genId = ( ) => cnt ++ ; export function createRef ( refId , reshape ? ) { return { ... nodeBaseDefault , id : genId ( ) , kind : REF , ref : refId , reshape , } as Ref ; } const toNode = ( input ) : Rule => { if ( typeof input === "object" ) { return input ; } if ( typeof input === "number" ) { return $ref ( input ) ; } return typeof input === "string" ? $token ( input ) : input ; } ; const registeredPatterns = [ ] ; export const $close = ( compiler ) => { const nodes = [ ] ; registeredPatterns . forEach ( ( [ rootId , nodeCreator ] ) => { const node = nodeCreator ( ) ; const resolvedNode = toNode ( node ) ; const parser = compileFragment ( resolvedNode , compiler , rootId ) ; compiler . parsers . set ( rootId , parser ) ; compiler . definitions . set ( rootId , resolvedNode ) ; nodes . push ( resolvedNode ) ; } ) ; registeredPatterns . length = 0 ; nodes . length = 0 ; } ; let _defCounter = 2 ; export function $def ( nodeCreator ) { const id = _defCounter ++ ; registeredPatterns . push ( [ id as any , nodeCreator ] ) ; return id ; } export function $ref ( refId , reshape ? ) { return { ... nodeBaseDefault , id : genId ( ) , kind : REF , ref : refId , reshape , } as Ref ; } export function $seq ( children , reshape ? ) { let nodes = [ ] ; nodes = children . map ( ( child ) : Rule => { if ( Array . isArray ( child ) ) { const [ key , ex ] = child ; return $param ( key , toNode ( ex ) ) ; } else { return toNode ( child ) ; } } ) ; return { ... nodeBaseDefault , reshape , id : genId ( ) , kind : SEQ , children : nodes , } as Seq ; } export function $not ( children , reshape ? ) { const childNodes = children . map ( toNode ) ; return { ... nodeBaseDefault , kind : NOT , patterns : childNodes , reshape , id : genId ( ) , } as Not ; } function findFirstNonOptionalRule ( seq ) { if ( seq . kind === SEQ ) { for ( const child of seq . children ) { if ( child . optional ) continue ; if ( child . kind === SEQ ) { return findFirstNonOptionalRule ( child ) ; } else { return child ; } } } return undefined ; } function buildHeadTable ( rule ) { switch ( rule . kind ) { case PAIR_CLOSE : throw new Error ( ) ; case PAIR_OPEN : { return [ rule . pattern ] ; } case ATOM : case REGEX : case NOT : case REF : case EOF : case TOKEN : return [ rule ] ; case REPEAT : { return buildHeadTable ( rule . pattern ) ; } case SEQ : { const head = findFirstNonOptionalRule ( rule ) ; return head ? buildHeadTable ( head ) : [ ] ; } case OR : { return rule . patterns . map ( ( pat ) => buildHeadTable ( pat ) ) . flat ( ) ; } } } export function $or ( patterns , reshape ? ) { if ( patterns . length === 1 ) { return toNode ( patterns [ 0 ] ) ; } const builtPatterns = patterns . map ( toNode ) as Array < Seq | Token | Ref > ; const heads = builtPatterns . map ( buildHeadTable ) . flat ( ) ; return { ... nodeBaseDefault , kind : OR , heads , patterns : builtPatterns , reshape , id : genId ( ) , } as Or ; } export function $repeat ( pattern , minmax ? , reshape ? ) { const [ min = 0 , max = undefined ] = minmax ?? [ ] ; return { ... nodeBaseDefault , id : genId ( ) , kind : REPEAT , pattern : toNode ( pattern ) , min , max , reshape , } ; } export function $token ( expr , reshape ? ) { return { ... nodeBaseDefault , id : genId ( ) , kind : TOKEN , expr , reshape , } ; } const regexCache = { } ; export function $regex ( expr , reshape ? ) { if ( regexCache [ expr ] ) return regexCache [ expr ] ; return ( regexCache [ expr ] = { ... nodeBaseDefault , id : genId ( ) , kind : REGEX , expr , reshape , } ) ; } export function $pairOpen ( rule ) { return { ... nodeBaseDefault , id : genId ( ) , kind : PAIR_OPEN , pattern : toNode ( rule ) , } ; } export function $pairClose ( rule ) { return { ... nodeBaseDefault , id : genId ( ) , kind : PAIR_CLOSE , pattern : toNode ( rule ) , } ; } export function $r ( strings , name ? ) { return $regex ( strings . join ( "" ) ) ; } export function $eof ( ) { return { ... nodeBaseDefault , id : genId ( ) , kind : EOF , reshape : undefined , } ; } export function $atom ( parser ) { return { ... nodeBaseDefault , id : genId ( ) , kind : ATOM , parse : parser , } ; } export function $param < T extends Rule > ( key , node ) { return { ... toNode ( node ) , key } as T ; } export function $skip < T extends Rule > ( input ) { return { ... toNode ( input ) , skip : true } as T ; } export function $skip_opt < T extends Rule > ( input ) { return { ... toNode ( input ) , skip : true , optional : true } as T ; } export function $repeat_seq ( input , minmax ? , reshape ? ) { return $repeat ( $seq ( input ) , minmax , reshape ) ; } export function $opt < T extends Rule > ( input ) { return { ... toNode ( input ) , optional : true } as T ; } export function $repeat_seq1 ( input ) { return $repeat ( $seq ( input ) , [ 1 ] ) ; }