import { filter , first , isArray , isUndefined , keys , map , mapKeys , NumericDictionary , values } from 'lodash' ; import { ASTNode , BinaryASTNode , ContextProps , Location , NodeContext , Position , TokenContext } from './AST' ; import { RokuBRSParser } from './Parser' ; const parser = new RokuBRSParser ( [ ] ) ; const Visitor = parser . getBaseCstVisitorConstructor ( ) ; export class BaseVisitor extends Visitor { constructor ( ) { super ( ) ; } public visit ( cstNode , param ? ) { if ( isArray ( cstNode ) ) { cstNode = cstNode [ 0 ] ; } if ( isUndefined ( cstNode ) ) { return undefined ; } return this [ cstNode . fullName || cstNode . name ] ( cstNode . children , param ) ; } protected byLine ( elements = [ ] ) { return mapKeys < ASTNode > ( this . asArray ( elements ) , ( el ) : number => { return el . loc && el . loc . start . line ; } ) ; } protected asArray ( value = [ ] ) { if ( ! value ) { return [ ] ; } return isArray ( value ) ? filter ( value ) : [ value ] ; } protected mergeTrailing ( elements = [ ] , trailing = [ ] ) { const linedTrailing = this . byLine ( trailing ) ; const merger = ( el ) : ASTNode => { const line = el . loc && el . loc . start . line ; if ( linedTrailing [ line ] ) { el . trailing = linedTrailing [ line ] ; } return el ; } ; return map < ASTNode > ( elements , merger ) as any ; } protected Location ( head , tail ) { if ( ! head || ! tail ) { return { loc : null , range : null } ; } const range = [ null , null ] ; const loc = { start : { line : null , column : null } , end : { line : null , column : null } } ; if ( head . loc !== null ) { loc . start = head . loc . start ; } if ( tail . loc !== null ) { loc . end = tail . loc . end ; } if ( head . range !== null ) { range [ 0 ] = head . range [ 0 ] ; } if ( tail . range !== null ) { range [ 1 ] = tail . range [ 1 ] ; } return { loc , range } ; } protected RenderNode ( node ) { const visitor = subNode => { return this . visit ( subNode ) ; } ; const mapped = filter ( map ( node , subNode => visitor ( subNode ) ) ) ; return mapped . length === 1 ? mapped [ 0 ] : mapped ; } protected mergeOperands ( from = [ ] , to = [ ] , dividers = [ ] ) { while ( from . length ) { to . push ( dividers . shift ( ) ) ; to . push ( from . shift ( ) ) ; } return filter ( to ) ; } protected flatListExpression ( type , operator , left , right ) { const head = isArray ( left ) ? first ( left ) : left ; const tail = isArray ( right ) ? first ( right ) : right ; const binary = { type , operator , left , right , ... this . Location ( head , tail ) } ; return this . asNode ( binary , { } ) ; } protected mapArguments ( ctx , cb ) { const _ = { } ; map ( ctx , ( node , key ) => { this . isToken ( node ) ? ( _ [ key ] = this . RenderToken ( node ) ) : ( _ [ key ] = this . RenderNode ( node ) ) ; } ) ; if ( cb ) { return cb ( _ ) ; } return null ; } protected isToken ( node ) { return node && node [ 0 ] && node [ 0 ] . tokenType ; } protected singleNode ( ctx ) { const data = this . singleArgument ( ctx ) ; return this . asNode ( data as ASTNode , ctx ) ; } protected singleArgument ( ctx ) { const names = keys ( ctx ) ; if ( names . length !== 1 ) { return false ; } return this . mapArguments ( ctx , nodes => first ( values ( nodes ) ) ) ; } protected Position ( line , column ) { return { line , column } ; } protected RenderToken ( node ) { const mapper = ( { startLine , startColumn , image , startOffset , tokenType } ) : ASTNode => { const length = image . length ; const start = this . Position ( startLine , startColumn ) ; const end = this . Position ( startLine , startColumn + length ) ; return { loc : { source : image , start , end } , range : [ startOffset , startOffset + length ] , type : tokenType . tokenName } ; } ; const mapped = map ( node , mapper ) ; return mapped . length === 1 ? mapped [ 0 ] : mapped ; } protected asNode ( data , _ ) { return data ; } }