/// <summary> /// Find expression type in function context /// </summary> /// <param name="expression">To evaluate</param> /// <param name="context">In context</param> /// <param name="inClass">In class</param> /// <param name="complete">Complete (sub-expression) or partial (dot-completion) evaluation</param> /// <returns>Class/member struct</returns> static private ASResult EvalExpression(string expression, ASExpr context, ASClass inClass, bool complete) { DebugConsole.Trace("** EvalExpression"); DebugConsole.Trace(expression); ASResult notFound = new ASResult(); Match mSub = null; string[] tokens = expression.Split('.'); // eval first token string token = tokens[0]; ASResult head; if (token.StartsWith("#")) { mSub = re_sub.Match(token); if (mSub.Success) { string subExpr = context.SubExpressions[ Convert.ToInt16(mSub.Groups["index"].Value) ]; // parse sub expression subExpr = subExpr.Substring(1,subExpr.Length-2).Trim(); ASExpr subContext = new ASExpr(); subContext.SubExpressions = ExtractedSubex = new StringCollection(); subExpr = re_balancedParenthesis.Replace(subExpr, new MatchEvaluator(ExtractSubex)); Match m = re_refineExpression.Match(subExpr); if (!m.Success) return notFound; subExpr = re_dot.Replace( re_whiteSpace.Replace(m.Value, " ") , ".").Trim(); int space = subExpr.LastIndexOf(' '); if (space > 0) subExpr = subExpr.Substring(space+1); // eval sub expression head = EvalExpression(subExpr, subContext, inClass, true); if (head.Member != null) head.Class = ASContext.FindClassFromName(head.Member.Type, head.Class); } else { token = token.Substring(token.IndexOf('~')+1); head = EvalVariable(token, context, inClass); } } else head = EvalVariable(token, context, inClass); // no head, exit if (head.IsNull()) return notFound; if (!head.IsStatic) DebugConsole.Trace(0+" "+token+":"+head.Class.ClassName); else if (head.Member != null) DebugConsole.Trace(0+" "+token+":"+head.Class.ClassName); else DebugConsole.Trace(0+" "+token+"="+head.Class.ClassName); // eval tail int n = tokens.Length; if (!complete) n--; // context ASResult step = head; ASClass resultClass = head.Class; // look for static or dynamic members? FlagType mask = (head.IsStatic) ? FlagType.Static : FlagType.Dynamic; // look for public only members? ASClass curClass = ASContext.CurrentClass; if (!FriendClasses(curClass, step.Class)) mask |= FlagType.Public; // explore for (int i=1; i<n; i++) { resultClass = step.Class; token = tokens[i]; DebugConsole.Trace(i+" "+token+" "+mask); FindMember(token, resultClass, step, mask); if (step.Class == null) return step; if (!step.IsStatic) //(resultClass.Flags != FlagType.Package) && ((step.Class.Flags & FlagType.Class) == 0)) { if ((mask & FlagType.Static) > 0) { mask -= FlagType.Static; mask |= FlagType.Dynamic; } } if (!FriendClasses(curClass, step.Class)) mask |= FlagType.Public; } // eval package if (step.Class.Flags == FlagType.Package) { DebugConsole.Trace("Complete package "+step.Class.ClassName); step.Class.Package = ASContext.FindPackage(step.Class.ClassName, true); } return step; }
/// <summary> /// Find Actionscript expression at cursor position /// TODO Improve this method /// </summary> /// <param name="sci">Scintilla Control</param> /// <param name="position">Cursor position</param> /// <returns></returns> static private ASExpr GetExpression(ScintillaNet.ScintillaControl Sci, int position) { ASExpr expression = new ASExpr(); int startPos = position; expression.Position = position; expression.separator = ' '; // get last expression (until ';') excluding comments int stylemask = (1 << Sci.StyleBits) -1; int style = (position > 0) ? Sci.StyleAt(position-1) & stylemask : 0; bool ignoreKey = false; if (style == 19) { DebugConsole.Trace("Ignore keyword"); ignoreKey = true; } StringBuilder sb = new StringBuilder(); char c; while ((position > 0) && (style != 19 || ignoreKey)) { position--; style = Sci.StyleAt(position) & stylemask; if (IsTextStyle(style) || ignoreKey) { c = (char)Sci.CharAt(position); if (c == ';') { expression.separator = c; break; } else if (c == '\n' || c == '\r') { if (sb.ToString().Trim().Length == 0) break; } sb.Insert(0,c); if (ignoreKey && IsTextStyle(style)) ignoreKey = false; } // we found a keyword else if (style == 19) { expression.separator = ' '; int keywordPos = position; string keyword = GetWordLeft(Sci, ref keywordPos); if ((keyword == "function") || (keyword == "get") || (keyword == "set")) { // we found a function declaration string test = sb.ToString().Trim(); // ignore anonymous function if ((keyword == "function") && test.StartsWith("(")) { keyword = null; break; } // guess context more precisely bool hasBraces = (test.IndexOf('{') >= 0); test = re_balancedBraces.Replace(test, ";"); // is it inside the function? if (test.IndexOf('{') >= 0) { c = ' '; while ((position < startPos) && (sb.Length > 0)) { position++; c = (char)Sci.CharAt(position); sb.Remove(0,1); if (c == '{') break; } expression.separator = c; } // is it NOT inside function parameters? else if (test.IndexOf(')') >= 0) { if (hasBraces) { expression.separator = '}'; expression.Value = ""; return expression; } else { // is it before the return type declaration? int colon = test.LastIndexOf(':'); if ((colon < 0) || (colon < test.LastIndexOf(')'))) // this is invalid return expression; } } // inside function parameters? else { int colon = test.LastIndexOf(':'); if ((colon < 0) || (colon < test.LastIndexOf(','))) return expression; } } else expression.Keyword = keyword; // note that we found a "case" statement //else if (keyword == "case") expression.Keyword = "case"; DebugConsole.Trace("Stopped at '"+keyword+"'"); DebugConsole.Trace("Raw '"+sb.ToString()+"'"); break; } } position++; string expr = sb.ToString(); sb = null; if (expr.Length > 0 && (expr[expr.Length-1] <= 32 && Sci.CharAt(startPos) != '(')) { expr = ""; expression.separator = ' '; expression.Position = Sci.CurrentPos; } else expression.PositionExpression = position; // refine last expression if (expr.Length > 0) { expr = re_balancedBraces.Replace(expr, ";"); expression.SubExpressions = ExtractedSubex = new StringCollection(); expr = re_balancedParenthesis.Replace(expr, new MatchEvaluator(ExtractSubex)); //DebugConsole.Trace("Raw '"+expr+"' @"+expression.PositionExpression); Match m = re_refineExpression.Match(expr); if (!m.Success) return expression; if (m.Index > 0) { expression.separator = expr[m.Index-1]; expression.PositionExpression = position += m.Index; // treat ':' as ';' after a case statement if (expression.separator == ':' && expression.Keyword == "case") expression.separator = ';'; expression.Keyword = null; } //DebugConsole.Trace("Refined '"+m.Value+"' @"+m.Index); expr = re_dot.Replace( re_whiteSpace.Replace(m.Value, " ") , "."); expr = re_subexMarker.Replace(expr, "#").Trim(); int space = Math.Max(expr.LastIndexOf(' '), expr.LastIndexOf(';')); if (space > 0) { expression.separator = ' '; expr = expr.Substring(space+1); } //ErrorHandler.ShowInfo("Clean '"+expr+"' @"+expression.PositionExpression); } expression.Value = expr; // get context function body int braceCount = 0; StringBuilder body = new StringBuilder(); StringBuilder context = new StringBuilder(); while (braceCount >= 0) { while (position > 0) { position--; style = Sci.StyleAt(position) & stylemask; if (IsTextStyleEx(style)) { c = (char)Sci.CharAt(position); if (c == '}') { body.Insert(0,c); braceCount++; } else if ((c == '{') && (--braceCount < 0)) break; if (braceCount == 0) body.Insert(0,c); } } if (braceCount >= 0) break; // get context function definition while (position > 0) { position--; style = Sci.StyleAt(position) & stylemask; if (IsTextStyleEx(style)) { c = (char)Sci.CharAt(position); if ((c == ';') || (c == '}') || (c == '{')) break; context.Insert(0,c); } } expression.ContextFunction = context.ToString(); // ignore dynamic function definition if (!re_validFunction.IsMatch(expression.ContextFunction)) { // stop if we reached the class definition if (re_classDefinition.IsMatch(expression.ContextFunction)) { expression.ContextFunction = null; expression.PositionContext = 0; break; } // continue search for function definition body.Insert(0,'{').Insert(0,context.ToString()); context = new StringBuilder(); position++; braceCount++; } else { expression.ContextBody = body.ToString(); expression.PositionContext = position+1; } } // result LastExpression = expression; return expression; }
/// <summary> /// Parse function body for local var definitions /// TODO ASComplete: parse coma separated local vars definitions /// </summary> /// <param name="expression">Expression source</param> /// <returns>Local vars dictionnary (name, type)</returns> static public ASMemberList ParseLocalVars(ASExpr expression) { ASMemberList vars = new ASMemberList(); if ((expression.ContextBody == null) || (expression.ContextBody.Length == 0)) return vars; // parse MatchCollection mcVars = re_variable.Matches(";"+expression.ContextBody); Match mVar; Match mType; string type; ASMember var; foreach(Match m in mcVars) { mVar = re_splitVariable.Match(m.Value); if (!mVar.Success) continue; mType = re_variableType.Match(mVar.Groups["type"].Value); if (mType.Success) type = mType.Groups["type"].Value; else type = "Object"; var = new ASMember(); var.Flags = FlagType.Variable | FlagType.Dynamic; var.Name = mVar.Groups["pname"].Value; var.Type = type; vars.Add(var); } // method parameters vars.Merge( ParseMethodParameters(expression.ContextFunction) ); return vars; }
/// <summary> /// Find variable type in function context /// </summary> /// <param name="token">Variable name</param> /// <param name="context">In context</param> /// <param name="inClass">In class</param> /// <returns>Class/member struct</returns> static private ASResult EvalVariable(string token, ASExpr context, ASClass inClass) { DebugConsole.Trace("EvalVariable "+token); ASResult result = new ASResult(); // local vars if (context.LocalVars != null) foreach(ASMember var in context.LocalVars) { if (var.Name == token) { result.inClass = null; result.Class = ASContext.FindClassFromName(var.Type, inClass); result.Member = var; return result; } } // method parameters if (context.ContextFunction != null) { Match param = Regex.Match(context.ContextFunction, "[(,][\\s]*"+Regex.Escape(token)+"[\\s:,)]"); if (param.Success) { //DebugConsole.Trace("Method param "+token); param = Regex.Match(context.ContextFunction, "[(,][\\s]*"+Regex.Escape(token)+"[\\s]*:[\\s]*(?<type>[^\\s,)]*)"); if (param.Success && (param.Groups["type"].Value.Length > 0)) { //DebugConsole.Trace("Type "+param.Groups["type"].Value); result.Class = ASContext.FindClassFromName(param.Groups["type"].Value, inClass); } return result; } } // class members FindMember(token, inClass, result, 0); if (!result.IsNull()) return result; // top-level elements if (!ASContext.TopLevel.IsVoid()) { FindMember(token, ASContext.TopLevel, result, 0); if (!result.IsNull()) return result; // special _levelN if (token.StartsWith("_") && re_level.IsMatch(token)) { result.Class = ASContext.FindClassFromName("MovieClip", null); return result; } } // classes int p = token.IndexOf('#'); string ctoken = (p > 0) ? token.Substring(0,p) : token; ASClass aClass = ASContext.FindClassFromName(ctoken, inClass); if (!aClass.IsVoid()) { result.Class = aClass; result.IsStatic = (p < 0); return result; } // packages folders ASMemberList package = ASContext.FindPackage(token, false); if (package != null) { result.Class = new ASClass(); result.Class.ClassName = token; result.Class.Flags = FlagType.Package; result.Class.Package = package; result.IsStatic = true; } return result; }