import { DocItem , FileDoc } from "./types" ; import { CompletionItem , CompletionItemKind } from "vscode-languageserver" ; const enum AstType { Identifier = "Identifier" , MemberExpression = "MemberExpression" , VarargLiteral = "VarargLiteral" } /**
 * 工具类
 * 分析ast结构，生成文档类
 */ class AstParser0 { private _globals : DocItem [ ] ; private _locals : DocItem [ ] ; private _alias : { name : string [ ] ; alias : string ; isLocal : boolean ; } [ ] = [ ] ; constructor ( ) { } param names param list param isNewVirtual private findOrCreateItem ( names , list , isNewVirtual = false ) { if ( names . length == 0 ) { return null ; } let lastItem ; for ( let i = 0 ; i < names . length ; i ++ ) { const name = names [ i ] ; let has = false ; for ( let j = 0 ; j < list . length ; j ++ ) { let item = list [ j ] ; if ( item . name == name ) { if ( item . refer ) { item = item . refer ; } if ( ! item . items ) { item . items = [ ] ; } list = item . items ; lastItem = item ; has = true ; break ; } } if ( ! has ) { let completion = < CompletionItem > { } ; completion . label = name ; completion . kind = CompletionItemKind . Variable ; let newItem = < DocItem > { } ; newItem . name = name ; newItem . completion = completion ; newItem . items = [ ] ; if ( isNewVirtual ) { newItem . virtual = true ; } list . push ( newItem ) ; list = newItem . items ; lastItem = newItem ; } } return lastItem ; } private getTopList ( isLocal ) { if ( isLocal ) { return this . _locals ; } return this . _globals ; } param item param base private add2DocItem ( item , base ) { if ( ! base ) { return ; } if ( base . refer ) { base = base . refer ; } this . add2List ( item , base . items ) ; } param item param list private add2List ( item , list ) { for ( let i = 0 ; i < list . length ; i ++ ) { const element = list [ i ] ; if ( element . name == item . name ) { element . completion = item . completion ; element . refer = item . refer ; return ; } } list . push ( item ) ; } param data private genVar ( data , refer ) { let identifier = this . getChain ( data , [ ] ) ; var names = identifier . names ; if ( names . length == 0 ) { return ; } let name = names . pop ( ) ; let completion = < CompletionItem > { } ; completion . label = name ; completion . kind = CompletionItemKind . Variable ; completion . detail = name ; let docItem = < DocItem > { } ; docItem . name = name ; docItem . loc = this . p_loc ( data [ "loc" ] ) ; docItem . completion = completion ; if ( refer . names . length == 1 && names . length == 1 && refer . names [ 0 ] == name ) { docItem . virtual = true ; } else { docItem . refer = this . findOrCreateItem ( refer . names , this . getTopList ( refer . isLocal ) , true ) ; } let list = this . getTopList ( identifier . isLocal ) ; let base = this . findOrCreateItem ( names , list , true ) ; if ( base ) { docItem . completion . detail = "(" + base . name + ") " + docItem . completion . detail ; this . add2DocItem ( docItem , base ) ; } else { this . add2List ( docItem , list ) ; } } param data private genFunc ( data ) { let identifier = this . getChain ( data [ "identifier" ] , [ ] ) ; var names = identifier . names ; if ( names . length == 0 ) { return ; } let name = names . pop ( ) ; let params = this . getFuncParams ( data [ "parameters" ] ) ; let params2 = "" ; let params3 = "" ; for ( let i = 0 ; i < params . length ; i ++ ) { const p = params [ i ] ; if ( i != 0 ) { params2 += ", " ; params3 += ", " ; } params2 += "${" + ( i + 1 ) + ":" + p + "}" ; params3 += p ; } let completion = < CompletionItem > { } ; completion . label = name ; completion . kind = CompletionItemKind . Function ; completion . insertTextFormat = 2 ; completion . insertText = name + "(" + params2 + ")" ; completion . detail = name + "(" + params3 + ")" ; let docItem = < DocItem > { } ; docItem . name = name ; docItem . loc = this . p_loc ( data [ "loc" ] ) ; docItem . completion = completion ; let list = this . getTopList ( identifier . isLocal ) ; let base = this . findOrCreateItem ( names , list , true ) ; if ( base ) { docItem . completion . detail = "(" + base . name + ") " + docItem . completion . detail ; this . add2DocItem ( docItem , base ) ; } else { this . add2List ( docItem , list ) ; } this . parseList ( data [ "body" ] ) ; } param data private getChain ( data , names ) { var reObj = { names : [ ] , isLocal : true } ; if ( ! data ) { return reObj ; } let type = data [ "type" ] ; if ( type == AstType . Identifier ) { names . unshift ( data [ "name" ] ) ; reObj . names = names ; reObj . isLocal = data [ "isLocal" ] ; } else if ( type == AstType . MemberExpression ) { names . unshift ( data [ "identifier" ] [ "name" ] ) ; return this . getChain ( data [ "base" ] , names ) ; } return reObj ; } param params private getFuncParams ( params ) { let ps = [ ] ; for ( let i = 0 ; i < params . length ; i ++ ) { const p = params [ i ] ; let type = p [ "type" ] ; if ( type == "Identifier" ) { ps . push ( p [ "name" ] ) ; } else if ( type == AstType . VarargLiteral ) { ps . push ( "..." ) ; } } return ps ; } param data private checkGlobalAssign ( data ) { let type = data [ "type" ] ; let isLocal = data [ "isLocal" ] ; if ( type == AstType . Identifier ) { return ! isLocal ; } else if ( type == AstType . MemberExpression ) { return this . checkGlobalAssign ( data [ "base" ] ) ; } return false ; } param data public parseDoc ( data ) { this . _globals = [ ] ; this . _locals = [ ] ; this . _alias = [ ] ; this . parseNode ( data ) ; let file = < FileDoc > { } ; file . globals = this . _globals ; file . locals = this . _locals ; return file ; } param data public parseNode ( data ) { let type = data [ "type" ] ; if ( this [ "p_" + type ] != null ) { this [ "p_" + type ] ( data ) ; } else { console . log ( "<warning>Unkown type: " + type ) ; } } private parseList ( list ) { for ( let i = 0 ; i < list . length ; i ++ ) { this . parseNode ( list [ i ] ) ; } } private p_loc ( data ) { return [ data [ "start" ] [ "line" ] , data [ "start" ] [ "column" ] ] ; } private p_Identifier ( data ) { } private p_NumericLiteral ( data ) { } private p_StringLiteral ( data ) { } private p_NilLiteral ( data ) { } private p_BooleanLiteral ( data ) { } private p_VarargLiteral ( data ) { } private p_UnaryExpression ( data ) { this . parseNode ( data [ "argument" ] ) ; } private p_BinaryExpression ( data ) { this . parseNode ( data [ "left" ] ) ; this . parseNode ( data [ "right" ] ) ; } private p_LogicalExpression ( data ) { this . p_BinaryExpression ( data ) ; } private p_CallExpression ( data ) { this . parseNode ( data [ "base" ] ) ; this . parseList ( data [ "arguments" ] ) ; } private p_MemberExpression ( data ) { } private p_StringCallExpression ( data ) { } private p_TableCallExpression ( data ) { } private p_TableConstructorExpression ( data ) { } private p_TableValue ( data ) { } private p_TableKeyString ( data ) { } private p_TableKey ( data ) { } private p_IndexExpression ( data ) { } private p_LocalStatement ( data ) { if ( this . isFuncDeclare ( data ) ) { return ; } let vars = data [ "variables" ] ; let inits = data [ "init" ] ; for ( let i = 0 ; i < vars . length ; i ++ ) { let var1 = vars [ i ] ; var refer = { names : [ ] , isLocal : true } ; if ( i < inits . length ) { let value = inits [ i ] ; if ( var1 [ "type" ] == AstType . Identifier ) { if ( value [ "type" ] == AstType . Identifier || value [ "type" ] == AstType . MemberExpression ) { refer = this . getChain ( value , [ ] ) ; } else if ( value [ "type" ] == "LogicalExpression" ) { refer = this . getChain ( value [ "left" ] , [ ] ) ; } else if ( value [ "type" ] == "CallExpression" && value [ "base" ] [ "name" ] == "require" ) { continue ; } } } this . genVar ( var1 , refer ) ; } } private isFuncDeclare ( data ) { let vars = data [ "variables" ] ; let inits = data [ "init" ] ; if ( vars . length == 1 && inits . length == 1 && inits [ 0 ] [ "type" ] == "FunctionDeclaration" ) { let funcObj = inits [ 0 ] ; funcObj [ "identifier" ] = vars [ 0 ] ; this . genFunc ( funcObj ) ; return true ; } return false ; } private p_AssignmentStatement ( data ) { this . p_LocalStatement ( data ) ; } private p_IfStatement ( data ) { this . parseList ( data [ "clauses" ] ) ; } private p_IfClause ( data ) { this . parseList ( data [ "body" ] ) ; } private p_ElseifClause ( data ) { this . parseList ( data [ "body" ] ) ; } private p_ElseClause ( data ) { this . parseList ( data [ "body" ] ) ; } private p_DoStatement ( data ) { this . parseList ( data [ "body" ] ) ; } private p_WhileStatement ( data ) { this . parseList ( data [ "body" ] ) ; } private p_RepeatStatement ( data ) { this . parseList ( data [ "body" ] ) ; } private p_ForNumericStatement ( data ) { this . parseList ( data [ "body" ] ) ; } private p_ForGenericStatement ( data ) { this . parseList ( data [ "body" ] ) ; } private p_BreakStatement ( data ) { } private p_ReturnStatement ( data ) { } private p_CallStatement ( data ) { return this . parseNode ( data [ "expression" ] ) ; } private p_FunctionDeclaration ( data ) { this . genFunc ( data ) ; this . parseList ( data [ "body" ] ) ; } private p_Comment ( data ) { } private p_Chunk ( data ) { this . parseList ( data [ "body" ] ) ; let list = data [ "body" ] ; if ( list . length > 0 ) { let last = list [ list . length - 1 ] ; let type = last [ "type" ] ; if ( type == "ReturnStatement" ) { let vars = last [ "arguments" ] ; if ( vars . length == 1 && vars [ 0 ] [ "type" ] == AstType . Identifier ) { let name = vars [ 0 ] [ "name" ] ; for ( let i = 0 ; i < this . _locals . length ; i ++ ) { const item = this . _locals [ i ] ; if ( item . name == name ) { item . completion . kind = CompletionItemKind . Module ; this . _locals . splice ( i , 1 ) ; this . _globals . push ( item ) ; break ; } } } } } } } param data export function parseDoc ( data ) { let parser = new AstParser0 ( ) ; return parser . parseDoc ( data ) ; }