/**
 * 简单的解析器,只是做了字符串替换,然后使用new Function生成函数
 */ namespace drunk . Parser { import Cache = drunk . Cache ; export interface IGetter { ( event ? , element ? , global ? ) : any ; filters ? : Array < Filter . IFilterDef > ; dynamic ? : boolean ; isInterpolate ? : boolean ; } export interface ISetter { ( viewModel , value ) : any ; } interface IFilterCache { input : string ; filters : Array < Filter . IFilterDef > ; } const globalName = "$global" ; const eventName = "$event" ; const elementName = "$el" ; const valueName = "__value" ; const contextName = "this" ; const proxyOperation = contextName + ".$proxy" ; const getHandlerOperation = contextName + ".__getHandler" ; const reservedKeywords = [ 'break' , 'case' , 'catch' , 'continue' , 'debugger' , 'default' , 'delete' , 'do' , 'else' , 'finally' , 'for' , 'function' , 'if' , 'in' , 'instanceof' , 'new' , 'return' , 'switch' , 'this' , 'throw' , 'try' , 'typeof' , 'var' , 'void' , 'while' , 'class' , 'null' , 'undefined' , 'true' , 'false' , 'with' , eventName , elementName , globalName , 'let' , 'abstract' , 'import' , 'yield' , 'arguments' ] ; const tokenCache = new Cache < any [ ] > ( 200 ) ; const getterCache = new Cache < IGetter > ( 200 ) ; const setterCache = new Cache < ISetter > ( 200 ) ; const filterCache = new Cache < IFilterCache > ( 200 ) ; const expressionCache = new Cache < IGetter > ( 200 ) ; const identifierCache = new Cache < any > ( 200 ) ; const interpolateGetterCache = new Cache < IGetter > ( 200 ) ; const reIdentifier = /("|').*?\1|[a-zA-Z$_][a-z0-9A-Z$_]*/g ; const reFilter = /("|').*?\1|\|\||\|\s*([a-zA-Z$_][a-z0-9A-Z$_]*)(:[^|]*)?/g ; const reInterpolate = /\{\{((.|\n)+?)\}\}/g ; const reBrackets = /^\([^)]*\)/ ; const reObjectKey = /[{,]\s*$/ ; const reColon = /^\s*:/ ; const reAnychar = /\S+/ ; const reThisProperties = /\bthis\.([_$[A-Za-z0-9]+)|\bthis\[\s*("|')(.+?)\2\s*\]/g ; /**
     *  解析filter定义
     */ function parseFilterDef ( str , skipSetter = false ) { if ( ! filterCache . get ( str ) ) { let def = [ ] ; let idx ; str . replace ( reFilter , ( $0 , quote , name , args , i ) => { if ( ! name ) { return $0 ; } if ( idx == null ) { idx = i ; } let param ; if ( args ) { param = parseGetter ( '[' + args . slice ( 1 ) + ']' , false , true ) ; } def . push ( { name : name , param : param } ) ; } ) ; if ( ! def . length ) { return ; } filterCache . set ( str , { input : str . slice ( 0 , idx ) . trim ( ) , filters : def } ) ; } return filterCache . get ( str ) ; } /**
     *  断言非空字符串
     */ function assertNotEmptyString ( target , message ) { if ( ! ( typeof target === 'string' && reAnychar . test ( target ) ) ) { throw new Error ( `${ message }: 表达式不能为空` ) ; } } /**
     *  是否是对象的key
     */ function isObjectKey ( str ) { return str . match ( reObjectKey ) != null ; } /**
     *  前一个字符是否是冒号
     */ function isColon ( str ) { return str . match ( reColon ) != null ; } /**
     *  是否是一个方法调用
     */ function isCallFunction ( str ) { return str . match ( reBrackets ) != null ; } /**
     *  解析所有的标记并对表达式进行格式化
     */ function parseIdentifier ( str ) { let cache = identifierCache . get ( str ) ; if ( ! cache ) { let index = 0 ; let proxies = [ ] ; let identifiers = [ ] ; let formated = str . replace ( reIdentifier , function ( x , p , i ) { if ( p === '"' || p === "'" || str [ i - 1 ] === '.' ) { index = i + x . length ; return x ; } let prevStr = str . slice ( index , i ) ; let nextStr = str . slice ( i + x . length ) ; index = i + x . length ; if ( isColon ( nextStr ) && isObjectKey ( prevStr ) ) { return x ; } if ( reservedKeywords . indexOf ( x ) > - 1 ) { return x ; } if ( isCallFunction ( nextStr ) ) { return getHandlerOperation + ' && ' + getHandlerOperation + '("' + x + '")' ; } if ( identifiers . indexOf ( x ) < 0 ) { proxies . push ( '  ' + proxyOperation + '("' + x + '")' ) ; identifiers . push ( x ) ; } return contextName + '.' + x ; } ) ; cache = { proxies : identifiers . length ? ( 'if (' + proxyOperation + ') {\n' + proxies . join ( ';\n' ) + ';\n}\n' ) : '' , formated , identifiers } ; identifierCache . set ( str , cache ) ; } return cache ; } /**
     *  创建函数
     */ function createFunction ( expression , ... args ) { try { return Function . apply ( Function , args ) ; } catch ( err ) { console . error ( `"${ expression }"解析失败,尝试解析后的结果为\n` , args [ args . length - 1 ] ) ; throw err ; } } param expression export function parse ( expression ) { assertNotEmptyString ( expression , `[Parser.parse]解析表达式失败` ) ; let fn = expressionCache . get ( expression ) ; if ( ! fn ) { let detail = parseIdentifier ( expression ) ; let fnBody = detail . proxies + "return (" + detail . formated + ");" ; fn = createFunction ( expression , eventName , elementName , globalName , fnBody ) ; expressionCache . set ( expression , fn ) ; } return fn ; } param expression param isInterpolate param skipFilter export function parseGetter ( expression , isInterpolate ? , skipFilter ? ) { assertNotEmptyString ( expression , `[Parser.parseGetter]创建getter失败` ) ; if ( isInterpolate ) { return parseInterpolate ( expression ) ; } let getter = getterCache . get ( expression ) ; if ( ! getter ) { let input = expression ; let filter ; if ( ! skipFilter && ( filter = parseFilterDef ( expression ) ) ) { input = filter . input ; } let detail = parseIdentifier ( input ) ; let fnBody = detail . proxies + "try{return (" + detail . formated + ");}catch(e){}" ; getter = createFunction ( expression , eventName , elementName , globalName , fnBody ) ; getter . dynamic = ! ! detail . identifiers . length ; getter . filters = filter ? filter . filters : null ; getterCache . set ( expression , getter ) ; } return getter ; } param expression export function parseSetter ( expression ) { assertNotEmptyString ( expression , `[Parser.parseSetter]创建setter失败` ) ; let setter = setterCache . get ( expression ) ; if ( ! setter ) { let detail = parseIdentifier ( expression ) ; let fnBody = detail . proxies + "return (" + detail . formated + " = " + valueName + ");" ; setter = createFunction ( expression , valueName , fnBody ) ; setterCache . set ( expression , setter ) ; } return setter ; } param expression param justTokens export function parseInterpolate ( expression ) ; export function parseInterpolate ( expression , justTokens ) ; export function parseInterpolate ( expression , justTokens ? ) { console . assert ( hasInterpolation ( expression ) , `[Parser.parseInterpolate]非法表达式` , expression ) ; let tokens = tokenCache . get ( expression ) ; if ( ! tokens ) { tokens = [ ] ; let index = 0 ; let length = expression . length ; expression . replace ( reInterpolate , ( $0 , exp , $2 , i ) => { if ( i > index ) { tokens . push ( expression . slice ( index , i ) ) ; } tokens . push ( { expression : exp . trim ( ) } ) ; index = i + $0 . length ; return $0 ; } ) ; if ( index < length && index !== 0 ) { tokens . push ( expression . slice ( index ) ) ; } tokenCache . set ( expression , tokens ) ; } if ( ! tokens . length ) { return ; } return justTokens ? tokens : tokensToGetter ( tokens , expression ) ; } param str export function hasInterpolation ( str ) { return typeof str === 'string' && str . match ( reAnychar ) !== null && str . match ( reInterpolate ) !== null ; } function tokensToGetter ( tokens , expression ) { let getter = interpolateGetterCache . get ( expression ) ; if ( ! getter ) { let dynamic = false ; let filters = [ ] ; tokens = tokens . map ( function ( item , i ) { if ( typeof item === 'string' ) { filters [ i ] = null ; return item ; } if ( item && item . expression != null ) { getter = parseGetter ( item . expression ) ; filters [ i ] = getter . filters ; if ( ! getter . dynamic ) { return getter . call ( null ) ; } dynamic = true ; return getter ; } console . error ( `非法的token:\n` , item ) ; } ) ; getter = function ( ) { return tokens . map ( item => { if ( typeof item === 'string' ) { return item ; } return item . call ( this ) ; } ) ; } ; getter . dynamic = dynamic ; getter . filters = filters ; getter . isInterpolate = true ; interpolateGetterCache . set ( expression , getter ) ; } return getter ; } export function getProxyProperties ( expression ) { let properties = [ ] ; expression . toString ( ) . replace ( reThisProperties , ( $0 , $1 , $2 , $3 ) => { if ( $1 ) { properties . push ( $1 ) ; } else if ( $3 ) { properties . push ( $3 ) ; } return $0 ; } ) ; return properties ; } }