license import * as chars from "../ast/chars" ; import { DEFAULT_INTERPOLATION_CONFIG , InterpolationConfig } from "../ast/interpolation_config" ; import { escapeRegExp } from "../ast/parse_util" ; import { AST , ASTWithSource , AstVisitor , Binary , BindingPipe , Chain , Conditional , EmptyExpr , FunctionCall , ImplicitReceiver , Interpolation , KeyedRead , KeyedWrite , LiteralArray , LiteralMap , LiteralMapKey , LiteralPrimitive , MethodCall , NonNullAssert , ParseSpan , ParserError , PrefixNot , PropertyRead , PropertyWrite , Quote , SafeMethodCall , SafePropertyRead , TemplateBinding } from "./ast" ; import { EOF , Lexer , Token , TokenType , isIdentifier , isQuote } from "./lexer" ; export class SplitInterpolation { constructor ( public strings , public expressions , public offsets ) { } } export class TemplateBindingParseResult { constructor ( public templateBindings , public warnings , public errors ) { } } function _createInterpolateRegExp ( config ) { const pattern = escapeRegExp ( config . start ) + "([\\s\\S]*?)" + escapeRegExp ( config . end ) ; return new RegExp ( pattern , "g" ) ; } export class Parser { private errors : ParserError [ ] = [ ] ; constructor ( private _lexer ) { } parseAction ( input , location , interpolationConfig = DEFAULT_INTERPOLATION_CONFIG ) { this . _checkNoInterpolation ( input , location , interpolationConfig ) ; const sourceToLex = this . _stripComments ( input ) ; const tokens = this . _lexer . tokenize ( this . _stripComments ( input ) ) ; const ast = new ParseAST ( input , location , tokens , sourceToLex . length , true , this . errors , input . length - sourceToLex . length ) . parseChain ( ) ; return new ASTWithSource ( ast , input , location , this . errors ) ; } parseBinding ( input , location , interpolationConfig = DEFAULT_INTERPOLATION_CONFIG ) { const ast = this . _parseBindingAst ( input , location , interpolationConfig ) ; return new ASTWithSource ( ast , input , location , this . errors ) ; } parseSimpleBinding ( input , location , interpolationConfig = DEFAULT_INTERPOLATION_CONFIG ) { const ast = this . _parseBindingAst ( input , location , interpolationConfig ) ; const errors = SimpleExpressionChecker . check ( ast ) ; if ( errors . length > 0 ) { this . _reportError ( `Host binding expression cannot contain ${ errors . join ( " " ) }` , input , location ) ; } return new ASTWithSource ( ast , input , location , this . errors ) ; } private _reportError ( message , input , errLocation , ctxLocation ? ) { this . errors . push ( new ParserError ( message , input , errLocation , ctxLocation ) ) ; } private _parseBindingAst ( input , location , interpolationConfig ) { const quote = this . _parseQuote ( input , location ) ; if ( quote != null ) { return quote ; } this . _checkNoInterpolation ( input , location , interpolationConfig ) ; const sourceToLex = this . _stripComments ( input ) ; const tokens = this . _lexer . tokenize ( sourceToLex ) ; return new ParseAST ( input , location , tokens , sourceToLex . length , false , this . errors , input . length - sourceToLex . length ) . parseChain ( ) ; } private _parseQuote ( input , location ) { if ( input === null ) { return null ; } const prefixSeparatorIndex = input . indexOf ( ":" ) ; if ( prefixSeparatorIndex === - 1 ) { return null ; } const prefix = input . substring ( 0 , prefixSeparatorIndex ) . trim ( ) ; if ( ! isIdentifier ( prefix ) ) { return null ; } const uninterpretedExpression = input . substring ( prefixSeparatorIndex + 1 ) ; return new Quote ( new ParseSpan ( 0 , input . length ) , prefix , uninterpretedExpression , location ) ; } parseTemplateBindings ( prefixToken , input , location ) { const tokens = this . _lexer . tokenize ( input ) ; if ( prefixToken ) { const prefixTokens = this . _lexer . tokenize ( prefixToken ) . map ( t => { t . index = 0 ; return t ; } ) ; tokens . unshift ( ... prefixTokens ) ; } return new ParseAST ( input , location , tokens , input . length , false , this . errors , 0 ) . parseTemplateBindings ( ) ; } parseInterpolation ( input , location , interpolationConfig = DEFAULT_INTERPOLATION_CONFIG ) { const split = this . splitInterpolation ( input , location , interpolationConfig ) ; if ( split === null ) { return null ; } const expressions = [ ] ; for ( let i = 0 ; i < split . expressions . length ; ++ i ) { const expressionText = split . expressions [ i ] ; const sourceToLex = this . _stripComments ( expressionText ) ; const tokens = this . _lexer . tokenize ( sourceToLex ) ; const ast = new ParseAST ( input , location , tokens , sourceToLex . length , false , this . errors , split . offsets [ i ] + ( expressionText . length - sourceToLex . length ) ) . parseChain ( ) ; expressions . push ( ast ) ; } return new ASTWithSource ( new Interpolation ( new ParseSpan ( 0 , input === null ? 0 : input . length ) , split . strings , expressions ) , input , location , this . errors ) ; } splitInterpolation ( input , location , interpolationConfig = DEFAULT_INTERPOLATION_CONFIG ) { const regexp = _createInterpolateRegExp ( interpolationConfig ) ; const parts = input . split ( regexp ) ; if ( parts . length <= 1 ) { return null ; } const strings = [ ] ; const expressions = [ ] ; const offsets = [ ] ; let offset = 0 ; for ( let i = 0 ; i < parts . length ; i ++ ) { const part = parts [ i ] ; if ( i % 2 === 0 ) { strings . push ( part ) ; offset += part . length ; } else if ( part . trim ( ) . length > 0 ) { offset += interpolationConfig . start . length ; expressions . push ( part ) ; offsets . push ( offset ) ; offset += part . length + interpolationConfig . end . length ; } else { this . _reportError ( "Blank expressions are not allowed in interpolated strings" , input , `at column ${ this . _findInterpolationErrorColumn ( parts , i , interpolationConfig ) } in` , location ) ; expressions . push ( "$implict" ) ; offsets . push ( offset ) ; } } return new SplitInterpolation ( strings , expressions , offsets ) ; } wrapLiteralPrimitive ( input , location ) { return new ASTWithSource ( new LiteralPrimitive ( new ParseSpan ( 0 , input === null ? 0 : input . length ) , input ) , input , location , this . errors ) ; } private _stripComments ( input ) { const i = this . _commentStart ( input ) ; return i != null ? input . substring ( 0 , i ) . trim ( ) : input ; } private _commentStart ( input ) { let outerQuote = null ; for ( let i = 0 ; i < input . length - 1 ; i ++ ) { const char = input . charCodeAt ( i ) ; const nextChar = input . charCodeAt ( i + 1 ) ; if ( char === chars . $SLASH && nextChar === chars . $SLASH && outerQuote === null ) { return i ; } if ( outerQuote === char ) { outerQuote = null ; } else if ( outerQuote === null && isQuote ( char ) ) { outerQuote = char ; } } return null ; } private _checkNoInterpolation ( input , location , interpolationConfig ) { const regexp = _createInterpolateRegExp ( interpolationConfig ) ; const parts = input . split ( regexp ) ; if ( parts . length > 1 ) { this . _reportError ( `Got interpolation (${ interpolationConfig . start }${ interpolationConfig . end }) where expression was expected` , input , `at column ${ this . _findInterpolationErrorColumn ( parts , 1 , interpolationConfig ) } in` , location ) ; } } private _findInterpolationErrorColumn ( parts , partInErrIdx , interpolationConfig ) { let errLocation = "" ; for ( let j = 0 ; j < partInErrIdx ; j ++ ) { errLocation += j % 2 === 0 ? parts [ j ] : `${ interpolationConfig . start }${ parts [ j ] }${ interpolationConfig . end }` ; } return errLocation . length ; } } export class ParseAST { private rparensExpected = 0 ; private rbracketsExpected = 0 ; private rbracesExpected = 0 ; index = 0 ; constructor ( public input , public location , public tokens , public inputLength , public parseAction , private errors , private offset ) { } peek ( offset ) { const i = this . index + offset ; return i < this . tokens . length ? this . tokens [ i ] : EOF ; } get next ( ) : Token { return this . peek ( 0 ) ; } get inputIndex ( ) : number { return this . index < this . tokens . length ? this . next . index + this . offset : this . inputLength + this . offset ; } span ( start ) { return new ParseSpan ( start , this . inputIndex ) ; } advance ( ) { this . index ++ ; } optionalCharacter ( code ) { if ( this . next . isCharacter ( code ) ) { this . advance ( ) ; return true ; } else { return false ; } } peekKeywordLet ( ) { return this . next . isKeywordLet ( ) ; } peekKeywordAs ( ) { return this . next . isKeywordAs ( ) ; } expectCharacter ( code ) { if ( this . optionalCharacter ( code ) ) { return ; } this . error ( `Missing expected ${ String . fromCharCode ( code ) }` ) ; } optionalOperator ( op ) { if ( this . next . isOperator ( op ) ) { this . advance ( ) ; return true ; } else { return false ; } } expectOperator ( operator ) { if ( this . optionalOperator ( operator ) ) { return ; } this . error ( `Missing expected operator ${ operator }` ) ; } expectIdentifierOrKeyword ( ) { const n = this . next ; if ( ! n . isIdentifier ( ) && ! n . isKeyword ( ) ) { this . error ( `Unexpected token ${ n }, expected identifier or keyword` ) ; return "" ; } this . advance ( ) ; return n . toString ( ) as string ; } expectIdentifierOrKeywordOrString ( ) { const n = this . next ; if ( ! n . isIdentifier ( ) && ! n . isKeyword ( ) && ! n . isString ( ) ) { this . error ( `Unexpected token ${ n }, expected identifier, keyword, or string` ) ; return "" ; } this . advance ( ) ; return n . toString ( ) as string ; } parseChain ( ) { const exprs = [ ] ; const start = this . inputIndex ; while ( this . index < this . tokens . length ) { const expr = this . parsePipe ( ) ; exprs . push ( expr ) ; if ( this . optionalCharacter ( chars . $SEMICOLON ) ) { if ( ! this . parseAction ) { this . error ( "Binding expression cannot contain chained expression" ) ; } while ( this . optionalCharacter ( chars . $SEMICOLON ) ) { } } else if ( this . index < this . tokens . length ) { this . error ( `Unexpected token '${ this . next }'` ) ; } } if ( exprs . length === 0 ) { return new EmptyExpr ( this . span ( start ) ) ; } if ( exprs . length === 1 ) { return exprs [ 0 ] ; } return new Chain ( this . span ( start ) , exprs ) ; } parsePipe ( ) { let result = this . parseExpression ( ) ; if ( this . optionalOperator ( "|" ) ) { if ( this . parseAction ) { this . error ( "Cannot have a pipe in an action expression" ) ; } do { const name = this . expectIdentifierOrKeyword ( ) ; const args = [ ] ; while ( this . optionalCharacter ( chars . $COLON ) ) { args . push ( this . parseExpression ( ) ) ; } result = new BindingPipe ( this . span ( result . span . start ) , result , name , args ) ; } while ( this . optionalOperator ( "|" ) ) ; } return result ; } parseExpression ( ) { return this . parseConditional ( ) ; } parseConditional ( ) { const start = this . inputIndex ; const result = this . parseLogicalOr ( ) ; if ( this . optionalOperator ( "?" ) ) { const yes = this . parsePipe ( ) ; let no ; if ( ! this . optionalCharacter ( chars . $COLON ) ) { const end = this . inputIndex ; const expression = this . input . substring ( start , end ) ; this . error ( `Conditional expression ${ expression } requires all 3 expressions` ) ; no = new EmptyExpr ( this . span ( start ) ) ; } else { no = this . parsePipe ( ) ; } return new Conditional ( this . span ( start ) , result , yes , no ) ; } else { return result ; } } parseLogicalOr ( ) { let result = this . parseLogicalAnd ( ) ; while ( this . optionalOperator ( "||" ) ) { const right = this . parseLogicalAnd ( ) ; result = new Binary ( this . span ( result . span . start ) , "||" , result , right ) ; } return result ; } parseLogicalAnd ( ) { let result = this . parseEquality ( ) ; while ( this . optionalOperator ( "&&" ) ) { const right = this . parseEquality ( ) ; result = new Binary ( this . span ( result . span . start ) , "&&" , result , right ) ; } return result ; } parseEquality ( ) { let result = this . parseRelational ( ) ; while ( this . next . type === TokenType . Operator ) { const operator = this . next . strValue ; switch ( operator ) { case "==" : case "===" : case "!=" : case "!==" : this . advance ( ) ; const right = this . parseRelational ( ) ; result = new Binary ( this . span ( result . span . start ) , operator , result , right ) ; continue ; } break ; } return result ; } parseRelational ( ) { let result = this . parseAdditive ( ) ; while ( this . next . type === TokenType . Operator ) { const operator = this . next . strValue ; switch ( operator ) { case "<" : case ">" : case "<=" : case ">=" : this . advance ( ) ; const right = this . parseAdditive ( ) ; result = new Binary ( this . span ( result . span . start ) , operator , result , right ) ; continue ; } break ; } return result ; } parseAdditive ( ) { let result = this . parseMultiplicative ( ) ; while ( this . next . type === TokenType . Operator ) { const operator = this . next . strValue ; switch ( operator ) { case "+" : case "-" : this . advance ( ) ; const right = this . parseMultiplicative ( ) ; result = new Binary ( this . span ( result . span . start ) , operator , result , right ) ; continue ; } break ; } return result ; } parseMultiplicative ( ) { let result = this . parsePrefix ( ) ; while ( this . next . type === TokenType . Operator ) { const operator = this . next . strValue ; switch ( operator ) { case "*" : case "%" : case "/" : this . advance ( ) ; const right = this . parsePrefix ( ) ; result = new Binary ( this . span ( result . span . start ) , operator , result , right ) ; continue ; } break ; } return result ; } parsePrefix ( ) { if ( this . next . type === TokenType . Operator ) { const start = this . inputIndex ; const operator = this . next . strValue ; let result ; switch ( operator ) { case "+" : this . advance ( ) ; return this . parsePrefix ( ) ; case "-" : this . advance ( ) ; result = this . parsePrefix ( ) ; return new Binary ( this . span ( start ) , operator , new LiteralPrimitive ( new ParseSpan ( start , start ) , 0 ) , result ) ; case "!" : this . advance ( ) ; result = this . parsePrefix ( ) ; return new PrefixNot ( this . span ( start ) , result ) ; } } return this . parseCallChain ( ) ; } parseCallChain ( ) { let result = this . parsePrimary ( ) ; while ( true ) { if ( this . optionalCharacter ( chars . $PERIOD ) ) { result = this . parseAccessMemberOrMethodCall ( result , false ) ; } else if ( this . optionalOperator ( "?." ) ) { result = this . parseAccessMemberOrMethodCall ( result , true ) ; } else if ( this . optionalCharacter ( chars . $LBRACKET ) ) { this . rbracketsExpected ++ ; const key = this . parsePipe ( ) ; this . rbracketsExpected -- ; this . expectCharacter ( chars . $RBRACKET ) ; if ( this . optionalOperator ( "=" ) ) { const value = this . parseConditional ( ) ; result = new KeyedWrite ( this . span ( result . span . start ) , result , key , value ) ; } else { result = new KeyedRead ( this . span ( result . span . start ) , result , key ) ; } } else if ( this . optionalCharacter ( chars . $LPAREN ) ) { this . rparensExpected ++ ; const args = this . parseCallArguments ( ) ; this . rparensExpected -- ; this . expectCharacter ( chars . $RPAREN ) ; result = new FunctionCall ( this . span ( result . span . start ) , result , args ) ; } else if ( this . optionalOperator ( "!" ) ) { result = new NonNullAssert ( this . span ( result . span . start ) , result ) ; } else { return result ; } } } parsePrimary ( ) { const start = this . inputIndex ; if ( this . optionalCharacter ( chars . $LPAREN ) ) { this . rparensExpected ++ ; const result = this . parsePipe ( ) ; this . rparensExpected -- ; this . expectCharacter ( chars . $RPAREN ) ; return result ; } else if ( this . next . isKeywordNull ( ) ) { this . advance ( ) ; return new LiteralPrimitive ( this . span ( start ) , null ) ; } else if ( this . next . isKeywordUndefined ( ) ) { this . advance ( ) ; return new LiteralPrimitive ( this . span ( start ) , void 0 ) ; } else if ( this . next . isKeywordTrue ( ) ) { this . advance ( ) ; return new LiteralPrimitive ( this . span ( start ) , true ) ; } else if ( this . next . isKeywordFalse ( ) ) { this . advance ( ) ; return new LiteralPrimitive ( this . span ( start ) , false ) ; } else if ( this . next . isKeywordThis ( ) ) { this . advance ( ) ; return new ImplicitReceiver ( this . span ( start ) ) ; } else if ( this . optionalCharacter ( chars . $LBRACKET ) ) { this . rbracketsExpected ++ ; const elements = this . parseExpressionList ( chars . $RBRACKET ) ; this . rbracketsExpected -- ; this . expectCharacter ( chars . $RBRACKET ) ; return new LiteralArray ( this . span ( start ) , elements ) ; } else if ( this . next . isCharacter ( chars . $LBRACE ) ) { return this . parseLiteralMap ( ) ; } else if ( this . next . isIdentifier ( ) ) { return this . parseAccessMemberOrMethodCall ( new ImplicitReceiver ( this . span ( start ) ) , false ) ; } else if ( this . next . isNumber ( ) ) { const value = this . next . toNumber ( ) ; this . advance ( ) ; return new LiteralPrimitive ( this . span ( start ) , value ) ; } else if ( this . next . isString ( ) ) { const literalValue = this . next . toString ( ) ; this . advance ( ) ; return new LiteralPrimitive ( this . span ( start ) , literalValue ) ; } else if ( this . index >= this . tokens . length ) { this . error ( `Unexpected end of expression: ${ this . input }` ) ; return new EmptyExpr ( this . span ( start ) ) ; } else { this . error ( `Unexpected token ${ this . next }` ) ; return new EmptyExpr ( this . span ( start ) ) ; } } parseExpressionList ( terminator ) { const result = [ ] ; if ( ! this . next . isCharacter ( terminator ) ) { do { result . push ( this . parsePipe ( ) ) ; } while ( this . optionalCharacter ( chars . $COMMA ) ) ; } return result ; } parseLiteralMap ( ) { const keys = [ ] ; const values = [ ] ; const start = this . inputIndex ; this . expectCharacter ( chars . $LBRACE ) ; if ( ! this . optionalCharacter ( chars . $RBRACE ) ) { this . rbracesExpected ++ ; do { const quoted = this . next . isString ( ) ; const key = this . expectIdentifierOrKeywordOrString ( ) ; keys . push ( { key , quoted } ) ; this . expectCharacter ( chars . $COLON ) ; values . push ( this . parsePipe ( ) ) ; } while ( this . optionalCharacter ( chars . $COMMA ) ) ; this . rbracesExpected -- ; this . expectCharacter ( chars . $RBRACE ) ; } return new LiteralMap ( this . span ( start ) , keys , values ) ; } parseAccessMemberOrMethodCall ( receiver , isSafe = false ) { const start = receiver . span . start ; const id = this . expectIdentifierOrKeyword ( ) ; if ( this . optionalCharacter ( chars . $LPAREN ) ) { this . rparensExpected ++ ; const args = this . parseCallArguments ( ) ; this . expectCharacter ( chars . $RPAREN ) ; this . rparensExpected -- ; const span = this . span ( start ) ; return isSafe ? new SafeMethodCall ( span , receiver , id , args ) : new MethodCall ( span , receiver , id , args ) ; } else { if ( isSafe ) { if ( this . optionalOperator ( "=" ) ) { this . error ( "The '?.' operator cannot be used in the assignment" ) ; return new EmptyExpr ( this . span ( start ) ) ; } else { return new SafePropertyRead ( this . span ( start ) , receiver , id ) ; } } else { if ( this . optionalOperator ( "=" ) ) { if ( ! this . parseAction ) { this . error ( "Bindings cannot contain assignments" ) ; return new EmptyExpr ( this . span ( start ) ) ; } const value = this . parseConditional ( ) ; return new PropertyWrite ( this . span ( start ) , receiver , id , value ) ; } else { return new PropertyRead ( this . span ( start ) , receiver , id ) ; } } } } parseCallArguments ( ) { if ( this . next . isCharacter ( chars . $RPAREN ) ) { return [ ] ; } const positionals = [ ] ; do { positionals . push ( this . parsePipe ( ) ) ; } while ( this . optionalCharacter ( chars . $COMMA ) ) ; return positionals as BindingPipe [ ] ; } /**
     * An identifier, a keyword, a string with an optional `-` inbetween.
     */ expectTemplateBindingKey ( ) { let result = "" ; let operatorFound = false ; do { result += this . expectIdentifierOrKeywordOrString ( ) ; operatorFound = this . optionalOperator ( "-" ) ; if ( operatorFound ) { result += "-" ; } } while ( operatorFound ) ; return result . toString ( ) ; } parseTemplateBindings ( ) { const bindings = [ ] ; let prefix = null ! ; const warnings = [ ] ; while ( this . index < this . tokens . length ) { const start = this . inputIndex ; let keyIsVar = this . peekKeywordLet ( ) ; if ( keyIsVar ) { this . advance ( ) ; } const rawKey = this . expectTemplateBindingKey ( ) ; let key = rawKey ; if ( ! keyIsVar ) { if ( prefix === null ) { prefix = key ; } else { key = prefix + key [ 0 ] . toUpperCase ( ) + key . substring ( 1 ) ; } } this . optionalCharacter ( chars . $COLON ) ; let name = null ! ; let expression = null ! ; if ( keyIsVar ) { if ( this . optionalOperator ( "=" ) ) { name = this . expectTemplateBindingKey ( ) ; } else { name = "$implicit" ; } } else if ( this . peekKeywordAs ( ) ) { const letStart = this . inputIndex ; this . advance ( ) ; name = rawKey ; key = this . expectTemplateBindingKey ( ) ; keyIsVar = true ; } else if ( this . next !== EOF && ! this . peekKeywordLet ( ) ) { const st = this . inputIndex ; const ast = this . parsePipe ( ) ; const source = this . input . substring ( st - this . offset , this . inputIndex - this . offset ) ; expression = new ASTWithSource ( ast , source , this . location , this . errors ) ; } bindings . push ( new TemplateBinding ( this . span ( start ) , key , keyIsVar , name , expression ) ) ; if ( this . peekKeywordAs ( ) && ! keyIsVar ) { const letStart = this . inputIndex ; this . advance ( ) ; const letName = this . expectTemplateBindingKey ( ) ; bindings . push ( new TemplateBinding ( this . span ( letStart ) , letName , true , key , null ! ) ) ; } if ( ! this . optionalCharacter ( chars . $SEMICOLON ) ) { this . optionalCharacter ( chars . $COMMA ) ; } } return new TemplateBindingParseResult ( bindings , warnings , this . errors ) ; } error ( message , index = null ) { this . errors . push ( new ParserError ( message , this . input , this . locationText ( index ) , this . location ) ) ; this . skip ( ) ; } private locationText ( index = null ) { if ( index === null ) { index = this . index ; } return index < this . tokens . length ? `at column ${ this . tokens [ index ] . index + 1 } in` : `at the end of the expression` ; } private skip ( ) { let n = this . next ; while ( this . index < this . tokens . length && ! n . isCharacter ( chars . $SEMICOLON ) && ( this . rparensExpected <= 0 || ! n . isCharacter ( chars . $RPAREN ) ) && ( this . rbracesExpected <= 0 || ! n . isCharacter ( chars . $RBRACE ) ) && ( this . rbracketsExpected <= 0 || ! n . isCharacter ( chars . $RBRACKET ) ) ) { if ( this . next . isError ( ) ) { this . errors . push ( new ParserError ( this . next . toString ( ) ! , this . input , this . locationText ( ) , this . location ) ) ; } this . advance ( ) ; n = this . next ; } } } class SimpleExpressionChecker implements AstVisitor { static check ( ast ) { const s = new SimpleExpressionChecker ( ) ; ast . visit ( s ) ; return s . errors ; } errors : string [ ] = [ ] ; visitImplicitReceiver ( ast , context ) { } visitInterpolation ( ast , context ) { } visitLiteralPrimitive ( ast , context ) { } visitPropertyRead ( ast , context ) { } visitPropertyWrite ( ast , context ) { } visitSafePropertyRead ( ast , context ) { } visitMethodCall ( ast , context ) { } visitSafeMethodCall ( ast , context ) { } visitFunctionCall ( ast , context ) { } visitLiteralArray ( ast , context ) { this . visitAll ( ast . expressions ) ; } visitLiteralMap ( ast , context ) { this . visitAll ( ast . values ) ; } visitBinary ( ast , context ) { } visitPrefixNot ( ast , context ) { } visitNonNullAssert ( ast , context ) { } visitConditional ( ast , context ) { } visitPipe ( ast , context ) { this . errors . push ( "pipes" ) ; } visitKeyedRead ( ast , context ) { } visitKeyedWrite ( ast , context ) { } visitAll ( asts ) { return asts . map ( node => node . visit ( this ) ) ; } visitChain ( ast , context ) { } visitQuote ( ast , context ) { } }