PageRenderTime 57ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/External/Plugins/ASCompletion/Model/ASFileParser.cs

http://flashdevelop.googlecode.com/
C# | 1538 lines | 1265 code | 122 blank | 151 comment | 668 complexity | 3c766f883597272b824b948e4dda89d9 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, GPL-2.0, BSD-3-Clause, Apache-2.0, LGPL-2.0, BSD-2-Clause

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. *
  3. * User: Philippe Elsass
  4. * Date: 18/03/2006
  5. * Time: 19:03
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.IO;
  10. using System.Text.RegularExpressions;
  11. using ASCompletion.Completion;
  12. using ASCompletion.Context;
  13. namespace ASCompletion.Model
  14. {
  15. #region Token class
  16. class Token
  17. {
  18. public int Position;
  19. public int Line;
  20. public string Text;
  21. public Token()
  22. {
  23. }
  24. public Token(Token copy)
  25. {
  26. Text = copy.Text;
  27. Line = copy.Line;
  28. Position = copy.Position;
  29. }
  30. override public string ToString()
  31. {
  32. return Text;
  33. }
  34. }
  35. #endregion
  36. #region TypeDefinitionKind enum
  37. /// <summary>
  38. /// Contributor: i.o.
  39. /// </summary>
  40. public enum TypeDefinitionKind : uint
  41. {
  42. Null = 0,
  43. Simple = 1,
  44. TypedArray = 2,
  45. TypedCallback = 3,
  46. TypedObject = 4
  47. }
  48. //
  49. #endregion
  50. #region TypeCommentUtils class
  51. /// <summary>
  52. /// Contributor: i.o.
  53. /// </summary>
  54. public class TypeCommentUtils
  55. {
  56. //---------------------
  57. // FIELDS
  58. //---------------------
  59. public static string ObjectType = "Object"; // will differ in haxe
  60. private static Random random = new Random(123456);
  61. //---------------------
  62. // PUBLIC METHODS
  63. //---------------------
  64. /// <summary>
  65. /// Type-comment parsing into model (source and destination)
  66. /// </summary>
  67. public static TypeDefinitionKind Parse(string comment, MemberModel model)
  68. {
  69. return Parse(comment, model, false);
  70. }
  71. public static TypeDefinitionKind Parse(string comment, MemberModel model, bool detectKindOnly)
  72. {
  73. if (model != null && comment != null && comment != "")
  74. {
  75. switch (model.Type)
  76. {
  77. case "Array":
  78. return ParseTypedArray(comment, model, detectKindOnly);
  79. case "Function":
  80. return ParseTypedCallback(comment, model, detectKindOnly);
  81. }
  82. if (model.Type == ObjectType)
  83. return ParseTypedObject(comment, model, detectKindOnly);
  84. }
  85. return TypeDefinitionKind.Null;
  86. }
  87. /// <summary>
  88. /// Typed object parsing
  89. /// </summary>
  90. public static TypeDefinitionKind ParseTypedObject(string comment, MemberModel model)
  91. {
  92. return ParseTypedObject(comment, model, false);
  93. }
  94. public static TypeDefinitionKind ParseTypedObject(string comment, MemberModel model, bool detectKindOnly)
  95. {
  96. if (model != null && comment != null && comment != "")
  97. {
  98. Match m = ASFileParserRegexes.ValidObjectType.Match(comment);
  99. if (m.Success)
  100. {
  101. if (!detectKindOnly)
  102. model.Type = TypeCommentUtils.ObjectType + "@" + m.Groups["type"].Value;
  103. return TypeDefinitionKind.TypedObject;
  104. }
  105. }
  106. return TypeDefinitionKind.Null;
  107. }
  108. /// <summary>
  109. /// Typed array parsing
  110. /// </summary>
  111. public static TypeDefinitionKind ParseTypedArray(string comment, MemberModel model)
  112. {
  113. return ParseTypedArray(comment, model, false);
  114. }
  115. public static TypeDefinitionKind ParseTypedArray(string comment, MemberModel model, bool detectKindOnly)
  116. {
  117. if (model != null && comment != null && comment != "")
  118. {
  119. Match m = ASFileParserRegexes.ValidTypeName.Match(comment);
  120. if (m.Success)
  121. {
  122. if (!detectKindOnly)
  123. model.Type = "Array@" + m.Groups["type"].Value;
  124. return TypeDefinitionKind.TypedArray;
  125. }
  126. }
  127. return TypeDefinitionKind.Null;
  128. }
  129. /// <summary>
  130. /// Typed callbck parsing
  131. /// </summary>
  132. public static TypeDefinitionKind ParseTypedCallback(string comment, MemberModel model)
  133. {
  134. return ParseTypedCallback(comment, model, false);
  135. }
  136. public static TypeDefinitionKind ParseTypedCallback(string comment, MemberModel model, bool detectKindOnly)
  137. {
  138. if (model != null && comment != null && comment != ""
  139. && (model.Flags & FlagType.Function) == 0)
  140. {
  141. MemberModel fnModel = extractTypedCallbackModel(comment);
  142. if (fnModel != null)
  143. {
  144. if (!detectKindOnly)
  145. {
  146. model.Type = fnModel.Type;
  147. model.Flags |= FlagType.Function;
  148. model.Parameters = fnModel.Parameters;
  149. if (model.Access == 0)
  150. {
  151. if (model.Namespace == "internal")
  152. {
  153. if ((model.Access & Visibility.Public) == 0)
  154. model.Access = Visibility.Internal;
  155. model.Namespace = "";
  156. }
  157. else model.Access = Visibility.Public;
  158. }
  159. }
  160. return TypeDefinitionKind.TypedCallback;
  161. }
  162. }
  163. return TypeDefinitionKind.Null;
  164. }
  165. //---------------------
  166. // PRIVATE METHODS
  167. //---------------------
  168. /// <summary>
  169. /// String randomer
  170. /// </summary>
  171. private static string getRandomStringRepl()
  172. {
  173. random.NextDouble();
  174. return "StringRepl" + random.Next(0xFFFFFFF).ToString();
  175. }
  176. /// <summary>
  177. /// TypedCallback model extracting
  178. /// </summary>
  179. private static MemberModel extractTypedCallbackModel(string comment)
  180. {
  181. if (comment == null || comment.Length == 0)
  182. return null;
  183. int idxBraceOp = comment.IndexOf("(");
  184. int idxBraceCl = comment.IndexOf(")");
  185. if (idxBraceOp != 0 || idxBraceCl < 1)
  186. return null;
  187. // replace strings by temp replacements
  188. MatchCollection qStrMatches = ASFileParserRegexes.QuotedString.Matches(comment);
  189. Dictionary<String, String> qStrRepls = new Dictionary<string, string>();
  190. int i = qStrMatches.Count;
  191. while (i-- > 0)
  192. {
  193. String strRepl = getRandomStringRepl();
  194. qStrRepls.Add(strRepl, qStrMatches[i].Value);
  195. comment = comment.Substring(0, qStrMatches[i].Index) + strRepl + comment.Substring(qStrMatches[i].Index + qStrMatches[i].Length);
  196. }
  197. // refreshing
  198. idxBraceOp = comment.IndexOf("(");
  199. idxBraceCl = comment.IndexOf(")");
  200. if (idxBraceOp != 0 || comment.LastIndexOf("(") != idxBraceOp
  201. || idxBraceCl < 0 || comment.LastIndexOf(")") != idxBraceCl)
  202. return null;
  203. MemberModel fm = new MemberModel("unknown", "*", FlagType.Function, Visibility.Default);
  204. fm.Parameters = new List<MemberModel>();
  205. // return type
  206. Match m = ASFileParserRegexes.FunctionType.Match(comment.Substring(idxBraceCl));
  207. if (m.Success)
  208. fm.Type = m.Groups["fType"].Value;
  209. // parameters
  210. String pBody = comment.Substring(idxBraceOp, 1 + idxBraceCl - idxBraceOp);
  211. MatchCollection pMatches = ASFileParserRegexes.Parameter.Matches(pBody);
  212. int l = pMatches.Count;
  213. for (i = 0; i < l; i++)
  214. {
  215. string pName = pMatches[i].Groups["pName"].Value;
  216. if (pName != null && pName.Length > 0)
  217. {
  218. foreach (KeyValuePair<String,String> replEntry in qStrRepls)
  219. {
  220. if (pName.IndexOf(replEntry.Key) > -1)
  221. {
  222. pName = "[COLOR=#F00][I]InvalidName[/I][/COLOR]";
  223. break;
  224. }
  225. }
  226. }
  227. string pType = pMatches[i].Groups["pType"].Value;
  228. if (pType != null && pType.Length > 0)
  229. {
  230. foreach (KeyValuePair<String,String> replEntry in qStrRepls)
  231. {
  232. if (pType.IndexOf(replEntry.Key) > -1)
  233. {
  234. pType = "[COLOR=#F00][I]InvalidType[/I][/COLOR]";
  235. break;
  236. }
  237. }
  238. }
  239. string pVal = pMatches[i].Groups["pVal"].Value;
  240. if (pVal != null && pVal.Length > 0)
  241. {
  242. if (qStrRepls.ContainsKey(pVal))
  243. {
  244. pVal = qStrRepls[pVal];
  245. }
  246. else
  247. {
  248. foreach (KeyValuePair<String,String> replEntry in qStrRepls)
  249. {
  250. if (pVal.IndexOf(replEntry.Key) > -1)
  251. {
  252. pVal = "[COLOR=#F00][I]InvalidValue[/I][/COLOR]";
  253. break;
  254. }
  255. }
  256. }
  257. }
  258. else
  259. {
  260. pVal = null;
  261. }
  262. MemberModel pModel = new MemberModel();
  263. pModel.Name = pName;
  264. pModel.Type = pType;
  265. pModel.Value = pVal;
  266. pModel.Flags = FlagType.ParameterVar;
  267. pModel.Access = Visibility.Default;
  268. fm.Parameters.Add(pModel);
  269. }
  270. return fm;
  271. }
  272. }
  273. //
  274. #endregion
  275. #region ASFileParserRegexes class
  276. //
  277. public class ASFileParserRegexes
  278. {
  279. public static readonly Regex Spaces = new Regex("\\s+", RegexOptions.Compiled);
  280. public static readonly Regex Region = new Regex(@"^(#|{)[ ]?region[:\\s]*(?<name>[^\r\n]*)", RegexOptions.Compiled);
  281. public static readonly Regex QuotedString = new Regex("(\"(\\\\.|[^\"\\\\])*\")|('(\\\\.|[^'\\\\])*')", RegexOptions.Compiled);
  282. public static readonly Regex FunctionType = new Regex(@"\)\s*\:\s*(?<fType>[\w\$\.\<\>\@]+)", RegexOptions.Compiled);
  283. public static readonly Regex ValidTypeName = new Regex("^(\\s*of\\s*)?(?<type>[\\w.\\$]*)$", RegexOptions.Compiled);
  284. public static readonly Regex ValidObjectType = new Regex("^(?<type>[\\w.,\\$]*)$", RegexOptions.Compiled);
  285. public static readonly Regex Import = new Regex("^[\\s]*import[\\s]+(?<package>[\\w.]+)",
  286. ASFileParserRegexOptions.MultilineComment);
  287. public static readonly Regex Parameter = new Regex(@"[\(,]\s*((?<pName>(\.\.\.)?[\w\$]+)\s*(\:\s*(?<pType>[\w\$\*\.\<\>\@]+))?(\s*\=\s*(?<pVal>[^\,\)]+))?)",
  288. RegexOptions.Compiled);
  289. public static readonly Regex BalancedBraces = new Regex("{[^{}]*(((?<Open>{)[^{}]*)+((?<Close-Open>})[^{}]*)+)*(?(Open)(?!))}",
  290. ASFileParserRegexOptions.SinglelineComment);
  291. private const string typeChars = @"[\w\$][\w\d\$]*";
  292. private const string typeClsf = @"(\s*(?<Classifier>" + typeChars + @"(\." + typeChars + ")*" + @"(\:\:?" + typeChars + ")?" + @")\s*)";
  293. private const string typeComment = @"(\s*\/\*(?<Comment>.*)\*\/\s*)";
  294. public static readonly Regex TypeDefinition = new Regex(@"^((" + typeClsf + typeComment + ")|(" + typeComment + typeClsf + ")|(" + typeClsf + "))$",
  295. RegexOptions.Compiled);
  296. }
  297. //
  298. #endregion
  299. #region ASFileParserRegexOptions class
  300. //
  301. public class ASFileParserRegexOptions
  302. {
  303. public const RegexOptions MultilineComment = RegexOptions.Compiled | RegexOptions.Multiline;
  304. public const RegexOptions SinglelineComment = RegexOptions.Compiled | RegexOptions.Singleline;
  305. }
  306. //
  307. #endregion
  308. #region ASFileParserUtils class
  309. //
  310. public class ASFileParserUtils
  311. {
  312. /// <summary>
  313. /// Contributor: i.o.
  314. /// Description: Extracts from plain string a type classifier and type comment
  315. /// Example:
  316. /// typeDefinition: "Array/*String*/" or "Array[spaces]/*String*/" or "/*String*/Array"
  317. /// typeClassifier: "Array"
  318. /// typeComment: "String"
  319. /// </summary>
  320. public static bool ParseTypeDefinition(string typeDefinition, out string typeClassifier, out string typeComment)
  321. {
  322. typeClassifier = null;
  323. typeComment = null;
  324. if (String.IsNullOrEmpty(typeDefinition))
  325. return false;
  326. Match m = ASFileParserRegexes.TypeDefinition.Match(typeDefinition);
  327. if (!m.Success)
  328. return false;
  329. typeClassifier = m.Groups["Classifier"].Value;
  330. if (m.Groups["Comment"].Success)
  331. typeComment = m.Groups["Comment"].Value;
  332. return true;
  333. }
  334. public static TypeDefinitionKind ParseTypeDefinitionInto(string typeDefinition, MemberModel model)
  335. {
  336. return ParseTypeDefinitionInto(typeDefinition, model, true, true);
  337. }
  338. public static TypeDefinitionKind ParseTypeDefinitionInto(string typeDefinition, MemberModel model, bool parseCommon, bool parseGeneric)
  339. {
  340. if (String.IsNullOrEmpty(typeDefinition))
  341. return TypeDefinitionKind.Null;
  342. if (typeDefinition.IndexOf("/*") < 0 || typeDefinition.IndexOf("*/") < 0)
  343. {
  344. if (!parseCommon)
  345. return TypeDefinitionKind.Null;
  346. model.Type = typeDefinition.Replace(":", ".");
  347. if (model.Type.IndexOf('$') > 0) model.Type = model.Type.Replace("$", ".<") + ">";
  348. return TypeDefinitionKind.Simple;
  349. }
  350. if (!parseGeneric)
  351. return TypeDefinitionKind.Null;
  352. string typeClassifier;
  353. string typeComment;
  354. if (!ASFileParserUtils.ParseTypeDefinition(typeDefinition, out typeClassifier, out typeComment))
  355. return TypeDefinitionKind.Null;
  356. model.Type = typeClassifier;
  357. return TypeCommentUtils.Parse(typeComment, model);
  358. }
  359. }
  360. //
  361. #endregion
  362. /// <summary>
  363. /// Old & clumsy AS2/AS3/haxe file parser - beware!
  364. /// </summary>
  365. public class ASFileParser
  366. {
  367. #region public methods
  368. static private PathModel cachedPath;
  369. static private DateTime cacheLastWriteTime;
  370. static public void ParseCacheFile(PathModel inPath, string file, IASContext inContext)
  371. {
  372. lock (typeof(ASFileParser))
  373. {
  374. cachedPath = inPath;
  375. ParseFile(file, inContext);
  376. cachedPath = null;
  377. }
  378. }
  379. static public FileModel ParseFile(string file, IASContext inContext)
  380. {
  381. FileModel fileModel = new FileModel(file);
  382. fileModel.Context = inContext;
  383. return ParseFile(fileModel);
  384. }
  385. static public FileModel ParseFile(FileModel fileModel)
  386. {
  387. string src = "";
  388. // parse file
  389. if (fileModel.FileName.Length > 0)
  390. {
  391. if (File.Exists(fileModel.FileName))
  392. {
  393. src = PluginCore.Helpers.FileHelper.ReadFile(fileModel.FileName);
  394. ASFileParser parser = new ASFileParser();
  395. fileModel.LastWriteTime = File.GetLastWriteTime(fileModel.FileName);
  396. if (cachedPath != null)
  397. cacheLastWriteTime = fileModel.LastWriteTime;
  398. parser.ParseSrc(fileModel, src);
  399. }
  400. // the file is not available (for the moment?)
  401. else if (Path.GetExtension(fileModel.FileName).Length > 0)
  402. {
  403. fileModel.OutOfDate = true;
  404. }
  405. }
  406. // this is a package
  407. else
  408. {
  409. // ignore
  410. }
  411. return fileModel;
  412. }
  413. #endregion
  414. #region parser context
  415. const int COMMENTS_BUFFER = 4096;
  416. const int TOKEN_BUFFER = 1024;
  417. const int VALUE_BUFFER = 1024;
  418. // parser context
  419. private FileModel model;
  420. private int version;
  421. private bool haXe;
  422. private bool tryPackage;
  423. private bool hasPackageSection;
  424. private FlagType context;
  425. private FlagType modifiers;
  426. private FlagType curModifiers;
  427. //private int modifiersPos;
  428. private int line;
  429. private int modifiersLine;
  430. private bool foundColon;
  431. private bool inParams;
  432. private bool inEnum;
  433. private bool inTypedef;
  434. private bool inGeneric;
  435. private bool inValue;
  436. private bool inConst;
  437. private bool inType;
  438. private bool inAnonType;
  439. private bool flattenNextBlock;
  440. private FlagType foundKeyword;
  441. private Token valueKeyword;
  442. private MemberModel valueMember;
  443. private Token curToken;
  444. private Token prevToken;
  445. private MemberModel curMember;
  446. private MemberModel curMethod;
  447. private Visibility curAccess;
  448. private string curNamespace;
  449. private ClassModel curClass;
  450. private string lastComment;
  451. private string curComment;
  452. private bool isBlockComment;
  453. private ContextFeatures features;
  454. #endregion
  455. #region tokenizer
  456. public bool ScriptMode;
  457. public ContextFeatures Features
  458. {
  459. get { return features; }
  460. }
  461. public ASFileParser()
  462. {
  463. features = new ContextFeatures();
  464. }
  465. /// <summary>
  466. /// Rebuild a file model with the source provided
  467. /// </summary>
  468. /// <param name="fileModel">Model</param>
  469. /// <param name="ba">Source</param>
  470. ///
  471. public void ParseSrc(FileModel fileModel, string ba)
  472. {
  473. ParseSrc(fileModel, ba, true);
  474. }
  475. public void ParseSrc(FileModel fileModel, string ba, bool allowBaReExtract)
  476. {
  477. //TraceManager.Add("Parsing " + Path.GetFileName(fileModel.FileName));
  478. model = fileModel;
  479. model.OutOfDate = false;
  480. model.CachedModel = false;
  481. // pre-filtering
  482. if (allowBaReExtract && model.HasFiltering && model.Context != null)
  483. ba = model.Context.FilterSource(fileModel.FileName, ba);
  484. model.InlinedIn = null;
  485. model.InlinedRanges = null;
  486. // language features
  487. if (model.Context != null) features = model.Context.Features;
  488. model.Imports.Clear();
  489. model.Classes.Clear();
  490. model.Members.Clear();
  491. model.Namespaces.Clear();
  492. model.Regions.Clear();
  493. model.PrivateSectionIndex = 0;
  494. model.Package = "";
  495. model.MetaDatas = null;
  496. // state
  497. int len = ba.Length;
  498. if (len < 0)
  499. return;
  500. int i = 0;
  501. line = 0;
  502. // when parsing cache file including multiple files
  503. resetParser:
  504. char c1;
  505. char c2;
  506. int matching = 0;
  507. bool isInString = false;
  508. int inString = 0;
  509. int braceCount = 0;
  510. bool inCode = true;
  511. // comments
  512. char[] commentBuffer = new char[COMMENTS_BUFFER];
  513. int commentLength = 0;
  514. lastComment = null;
  515. curComment = null;
  516. // tokenisation
  517. tryPackage = true;
  518. hasPackageSection = false;
  519. haXe = model.haXe;
  520. TypeCommentUtils.ObjectType = haXe ? "Dynamic" : "Object";
  521. version = (haXe) ? 4 : 1;
  522. curToken = new Token();
  523. prevToken = new Token();
  524. int tokPos = 0;
  525. int tokLine = 0;
  526. curMethod = null;
  527. curMember = null;
  528. valueKeyword = null;
  529. valueMember = null;
  530. curModifiers = 0;
  531. curNamespace = "internal";
  532. curAccess = 0;
  533. char[] buffer = new char[TOKEN_BUFFER];
  534. int length = 0;
  535. char[] valueBuffer = new char[VALUE_BUFFER];
  536. int valueLength = 0;
  537. int paramBraceCount = 0;
  538. int paramTempCount = 0;
  539. int paramParCount = 0;
  540. int paramSqCount = 0;
  541. bool hadWS = true;
  542. bool hadDot = false;
  543. inParams = false;
  544. inEnum = false;
  545. inTypedef = false;
  546. inValue = false;
  547. inConst = false;
  548. inType = false;
  549. inGeneric = false;
  550. inAnonType = false;
  551. bool addChar = false;
  552. int evalToken = 0;
  553. //bool evalKeyword = true;
  554. context = 0;
  555. modifiers = 0;
  556. foundColon = false;
  557. bool handleDirectives = features.hasDirectives || cachedPath != null;
  558. bool inlineDirective = false;
  559. while (i < len)
  560. {
  561. c1 = ba[i++];
  562. isInString = (inString > 0);
  563. /* MATCH COMMENTS / STRING LITERALS */
  564. switch (matching)
  565. {
  566. // look for comment block/line and preprocessor commands
  567. case 0:
  568. if (!isInString)
  569. {
  570. // new comment
  571. if (c1 == '/' && i < len)
  572. {
  573. c2 = ba[i];
  574. if (c2 == '/')
  575. {
  576. // Check if this this is a /// comment
  577. if (i + 1 < len && ba[i + 1] == '/')
  578. {
  579. // This is a /// comment
  580. matching = 4;
  581. isBlockComment = true;
  582. i++;
  583. }
  584. else
  585. {
  586. // This is a regular comment
  587. matching = 1;
  588. isBlockComment = false;
  589. }
  590. inCode = false;
  591. i++;
  592. continue;
  593. }
  594. else if (c2 == '*')
  595. {
  596. isBlockComment = (i + 1 < len && ba[i + 1] == '*');
  597. matching = 2;
  598. inCode = false;
  599. i++;
  600. while (i < len - 1)
  601. {
  602. c2 = ba[i];
  603. if (c2 == '*' && ba[i + 1] != '/') i++;
  604. else break;
  605. }
  606. continue;
  607. }
  608. }
  609. // don't look for comments in strings
  610. else if (c1 == '"')
  611. {
  612. isInString = true;
  613. inString = 1;
  614. }
  615. else if (c1 == '\'')
  616. {
  617. isInString = true;
  618. inString = 2;
  619. }
  620. // preprocessor statements
  621. else if (c1 == '#' && handleDirectives)
  622. {
  623. int ls = i - 2;
  624. inlineDirective = false;
  625. while (ls > 0)
  626. {
  627. c2 = ba[ls--];
  628. if (c2 == 10 || c2 == 13) break;
  629. else if (c2 > 32) { inlineDirective = true; break; }
  630. }
  631. c2 = ba[i];
  632. if (i < 2 || ba[i - 2] < 33 && c2 >= 'a' && c2 <= 'z')
  633. {
  634. matching = 3;
  635. inCode = false;
  636. continue;
  637. }
  638. }
  639. }
  640. // end of string
  641. else if (isInString)
  642. {
  643. if (c1 == '\\') { i++; continue; }
  644. else if (c1 == 10 || c1 == 13) inString = 0;
  645. else if ((inString == 1) && (c1 == '"')) inString = 0;
  646. else if ((inString == 2) && (c1 == '\'')) inString = 0;
  647. // extract "include" declarations
  648. if (inString == 0 && length == 7 && context == 0)
  649. {
  650. string token = new string(buffer, 0, length);
  651. if (token == "include")
  652. {
  653. string inc = ba.Substring(tokPos, i - tokPos);
  654. if (model.MetaDatas == null) model.MetaDatas = new List<ASMetaData>();
  655. ASMetaData meta = new ASMetaData("Include");
  656. meta.ParseParams(inc);
  657. model.MetaDatas.Add(meta);
  658. }
  659. }
  660. }
  661. break;
  662. // skip commented line
  663. case 1:
  664. if (c1 == 10 || c1 == 13)
  665. {
  666. // ignore single comments
  667. commentLength = 0;
  668. inCode = true;
  669. matching = 0;
  670. }
  671. break;
  672. // skip commented block
  673. case 2:
  674. if (c1 == '*')
  675. {
  676. bool end = false;
  677. while (i < len)
  678. {
  679. c2 = ba[i];
  680. if (c2 == '\\') { i++; continue; }
  681. if (c2 == '/')
  682. {
  683. end = true;
  684. break;
  685. }
  686. else if (c2 == '*') i++;
  687. else break;
  688. }
  689. if (end)
  690. {
  691. lastComment = (commentLength > 0) ? new string(commentBuffer, 0, commentLength) : null;
  692. // TODO parse for TODO statements?
  693. commentLength = 0;
  694. inCode = true;
  695. matching = 0;
  696. i++;
  697. continue;
  698. }
  699. }
  700. break;
  701. // directive/preprocessor statement
  702. case 3:
  703. if (c1 == 10 || c1 == 13 || (inlineDirective && c1 <= 32))
  704. {
  705. if (commentLength > 0)
  706. {
  707. string directive = new string(commentBuffer, 0, commentLength);
  708. if (directive.StartsWith("if"))
  709. {
  710. inCode = true;
  711. }
  712. else if (directive.StartsWith("else"))
  713. {
  714. inCode = true;
  715. }
  716. else inCode = true;
  717. // FD cache custom directive
  718. if (cachedPath != null && directive.StartsWith("file-cache "))
  719. {
  720. // parsing done!
  721. FinalizeModel();
  722. // next model
  723. string realFile = directive.Substring(11);
  724. FileModel newModel = new FileModel(realFile, cacheLastWriteTime);
  725. newModel.CachedModel = true;
  726. newModel.Context = model.Context;
  727. haXe = newModel.haXe;
  728. if (!cachedPath.HasFile(realFile) && File.Exists(realFile))
  729. {
  730. newModel.OutOfDate = (File.GetLastWriteTime(realFile) > cacheLastWriteTime);
  731. cachedPath.AddFile(newModel);
  732. }
  733. model = newModel;
  734. goto resetParser; // loop
  735. }
  736. }
  737. else inCode = true;
  738. commentLength = 0;
  739. matching = 0;
  740. }
  741. break;
  742. // We are inside a /// comment
  743. case 4:
  744. {
  745. bool end = false;
  746. bool skipAhead = false;
  747. // See if we just ended a line
  748. if (2 <= i && (ba[i - 2] == 10 || ba[i - 2] == 13))
  749. {
  750. // Check ahead to the next line, see if it has a /// comment on it too.
  751. // If it does, we want to continue the comment with that line. If it
  752. // doesn't, then this comment is finished and we will set end to true.
  753. for (int j = i + 1; j < len; ++j)
  754. {
  755. // Skip whitespace
  756. char twoBack = ba[j - 2];
  757. if (' ' != twoBack && '\t' != twoBack)
  758. {
  759. if ('/' == twoBack && '/' == ba[j - 1] && '/' == ba[j])
  760. {
  761. // There is a comment ahead. Move up to it so we can gather the
  762. // rest of the comment
  763. i = j + 1;
  764. skipAhead = true;
  765. break;
  766. }
  767. else
  768. {
  769. // Not a comment! We're done!
  770. end = true;
  771. break;
  772. }
  773. }
  774. }
  775. }
  776. if (end)
  777. {
  778. // The comment is over and we want to write it out
  779. lastComment = (commentLength > 0) ? new string(commentBuffer, 0, commentLength).Trim() : null;
  780. commentLength = 0;
  781. inCode = true;
  782. matching = 0;
  783. // Back up i so we can start gathering comments from right after the line break
  784. --i;
  785. continue;
  786. }
  787. if (skipAhead)
  788. {
  789. // We just hit another /// and are skipping up to right after it.
  790. continue;
  791. }
  792. break;
  793. }
  794. }
  795. /* LINE/COLUMN NUMBER */
  796. if (c1 == 10 || c1 == 13)
  797. {
  798. if (cachedPath == null) line++; // cache breaks line count
  799. if (c1 == 13 && i < len && ba[i] == 10) i++;
  800. }
  801. /* SKIP CONTENT */
  802. if (!inCode)
  803. {
  804. // store comments
  805. if (matching == 2 || (matching == 3 && handleDirectives) || matching == 4)
  806. {
  807. if (commentLength < COMMENTS_BUFFER) commentBuffer[commentLength++] = c1;
  808. }
  809. else if (matching == 1 && (c1 == '#' || c1 == '{'))
  810. {
  811. commentBuffer[commentLength++] = c1;
  812. while (i < len)
  813. {
  814. c2 = ba[i];
  815. if (commentLength < COMMENTS_BUFFER) commentBuffer[commentLength++] = c2;
  816. if (c2 == 10 || c2 == 13)
  817. break;
  818. i++;
  819. }
  820. string comment = new String(commentBuffer, 0, commentLength);
  821. Match match = ASFileParserRegexes.Region.Match(comment);
  822. if (match.Success)
  823. {
  824. string regionName = match.Groups["name"].Value.Trim();
  825. MemberModel region = new MemberModel(regionName, String.Empty, FlagType.Declaration, Visibility.Default);
  826. region.LineFrom = region.LineTo = line;
  827. model.Regions.Add(region);
  828. }
  829. }
  830. continue;
  831. }
  832. else if (isInString)
  833. {
  834. // store parameter default value
  835. if (inValue && valueLength < VALUE_BUFFER)
  836. valueBuffer[valueLength++] = c1;
  837. continue;
  838. }
  839. if (braceCount > 0 && !inValue)
  840. {
  841. if (c1 == '/')
  842. {
  843. LookupRegex(ref ba, ref i);
  844. }
  845. else if (c1 == '}')
  846. {
  847. lastComment = null;
  848. braceCount--;
  849. if (braceCount == 0 && curMethod != null)
  850. {
  851. curMethod.LineTo = line;
  852. curMethod = null;
  853. }
  854. }
  855. else if (c1 == '{') braceCount++;
  856. // escape next char
  857. else if (c1 == '\\') i++;
  858. continue;
  859. }
  860. /* PARSE DECLARATION VALUES/TYPES */
  861. if (inValue)
  862. {
  863. bool stopParser = false;
  864. bool valueError = false;
  865. if (inType && !inAnonType && !inGeneric && !Char.IsLetterOrDigit(c1) && ".{}-><".IndexOf(c1) < 0)
  866. {
  867. inType = false;
  868. inValue = false;
  869. inGeneric = false;
  870. valueLength = 0;
  871. length = 0;
  872. context = 0;
  873. }
  874. else if (c1 == '{')
  875. {
  876. paramBraceCount++;
  877. stopParser = true;
  878. }
  879. else if (c1 == '}')
  880. {
  881. if (paramBraceCount > 0) { paramBraceCount--; stopParser = true; }
  882. else valueError = true;
  883. }
  884. else if (c1 == '(')
  885. {
  886. paramParCount++;
  887. stopParser = true;
  888. }
  889. else if (c1 == ')')
  890. {
  891. if (paramParCount > 0) { paramParCount--; stopParser = true; }
  892. else valueError = true;
  893. }
  894. else if (c1 == '[') paramSqCount++;
  895. else if (c1 == ']')
  896. {
  897. if (paramSqCount > 0) { paramSqCount--; stopParser = true; }
  898. else valueError = true;
  899. }
  900. else if (c1 == '<')
  901. {
  902. if (inType) inGeneric = true;
  903. paramTempCount++;
  904. }
  905. else if (c1 == '>')
  906. {
  907. if (ba[i - 2] == '-') { /*haxe method signatures*/ }
  908. else if (paramTempCount > 0)
  909. {
  910. paramTempCount--;
  911. stopParser = true;
  912. }
  913. else valueError = true;
  914. }
  915. else if (c1 == '/')
  916. {
  917. int i0 = i;
  918. if (LookupRegex(ref ba, ref i))
  919. {
  920. valueBuffer[valueLength++] = '/';
  921. for (; i0 < i; i0++)
  922. if (valueLength < VALUE_BUFFER - 1) valueBuffer[valueLength++] = ba[i0];
  923. valueBuffer[valueLength++] = '/';
  924. continue;
  925. }
  926. }
  927. else if (inValue && (inParams || inType || inConst)
  928. && c1 == '/' && valueLength == 0) // lookup native regex
  929. {
  930. int itemp = i;
  931. valueBuffer[valueLength++] = '/';
  932. while (valueLength < VALUE_BUFFER && i < len)
  933. {
  934. c1 = ba[i++];
  935. if (c1 == '\n' || c1 == '\r')
  936. {
  937. valueLength = 0;
  938. i = itemp;
  939. break;
  940. }
  941. valueBuffer[valueLength++] = c1;
  942. if (c1 == '\\' && i < len)
  943. {
  944. c1 = ba[i++];
  945. valueBuffer[valueLength++] = c1;
  946. }
  947. else if (c1 == '/') break;
  948. }
  949. }
  950. else if ((c1 == ':' || c1 == ',') && paramBraceCount > 0) stopParser = true;
  951. // end of value
  952. if ((valueError || (!stopParser && paramBraceCount == 0 && paramParCount == 0 && paramSqCount == 0 && paramTempCount == 0))
  953. && (c1 == ',' || c1 == ';' || c1 == '}' || c1 == '\r' || c1 == '\n' || (inParams && c1 == ')') || inType))
  954. {
  955. if (!inType && (!inValue || c1 != ','))
  956. {
  957. length = 0;
  958. context = 0;
  959. }
  960. inValue = false;
  961. inGeneric = false;
  962. //if (valueLength < VALUE_BUFFER) valueBuffer[valueLength++] = c1;
  963. }
  964. // in params, store the default value
  965. else if ((inParams || inType || inConst) && valueLength < VALUE_BUFFER)
  966. {
  967. if (c1 <= 32)
  968. {
  969. if (valueLength > 0 && valueBuffer[valueLength - 1] != ' ')
  970. valueBuffer[valueLength++] = ' ';
  971. }
  972. else valueBuffer[valueLength++] = c1;
  973. }
  974. // detect keywords
  975. if (!Char.IsLetterOrDigit(c1))
  976. {
  977. // escape next char
  978. if (c1 == '\\' && i < len)
  979. {
  980. c1 = ba[i++];
  981. if (valueLength < VALUE_BUFFER) valueBuffer[valueLength++] = c1;
  982. continue;
  983. }
  984. if (stopParser) continue;
  985. else if (valueError && c1 == ')') inValue = false;
  986. else if (inType && inGeneric && (c1 == '<' || c1 == '.')) continue;
  987. else if (inAnonType) continue;
  988. hadWS = true;
  989. }
  990. }
  991. // store type / parameter value
  992. if (!inValue && valueLength > 0)
  993. {
  994. string param = /*(valueBuffer[0] == '{' && valueBuffer[0] != '[') ? "..."
  995. :*/ new string(valueBuffer, 0, valueLength);
  996. // get text before the last keyword found
  997. if (valueKeyword != null)
  998. {
  999. int p = param.LastIndexOf(valueKeyword.Text);
  1000. if (p > 0) param = param.Substring(0, p).TrimEnd();
  1001. }
  1002. if (curMember == null)
  1003. {
  1004. if (inType)
  1005. {
  1006. prevToken.Text = curToken.Text;
  1007. prevToken.Line = curToken.Line;
  1008. prevToken.Position = curToken.Position;
  1009. curToken.Text = param;
  1010. curToken.Line = tokLine;
  1011. curToken.Position = tokPos;
  1012. EvalToken(true, true/*false*/, i - 1 - valueLength);
  1013. evalToken = 0;
  1014. }
  1015. }
  1016. else if (inType)
  1017. {
  1018. foundColon = false;
  1019. if (haXe)
  1020. {
  1021. if (param.EndsWith("}") || param.Contains(">"))
  1022. {
  1023. param = ASFileParserRegexes.Spaces.Replace(param, "");
  1024. param = param.Replace(",", ", ");
  1025. param = param.Replace("->", " -> ");
  1026. }
  1027. }
  1028. curMember.Type = param;
  1029. }
  1030. // AS3 const or method parameter's default value
  1031. else if (version > 2 && (curMember.Flags & FlagType.Variable) > 0)
  1032. {
  1033. if (inParams || inConst) curMember.Value = param;
  1034. curMember.LineTo = line;
  1035. if (c1 == '\r' || c1 == '\n') curMember.LineTo--;
  1036. if (inConst && c1 != ',')
  1037. {
  1038. context = 0;
  1039. inConst = false;
  1040. }
  1041. }
  1042. //
  1043. valueLength = 0;
  1044. if (!inParams && !(inConst && context != 0) && c1 != '{') continue;
  1045. else length = 0;
  1046. }
  1047. /* TOKENIZATION */
  1048. // whitespace
  1049. if (c1 <= 32)
  1050. {
  1051. hadWS = true;
  1052. continue;
  1053. }
  1054. // a dot can be in an identifier
  1055. if (c1 == '.')
  1056. {
  1057. if (length > 0 || (inParams && version == 3))
  1058. {
  1059. hadWS = false;
  1060. hadDot = true;
  1061. addChar = true;
  1062. if (!inValue && context == FlagType.Variable && !foundColon)
  1063. {
  1064. bool keepContext = inParams && (length == 0 || buffer[0] == '.');
  1065. if (!keepContext) context = 0;
  1066. }
  1067. }
  1068. else continue;
  1069. }
  1070. else
  1071. {
  1072. // should we evaluate the token?
  1073. if (hadWS && !hadDot && !inGeneric && length > 0)
  1074. {
  1075. evalToken = 1;
  1076. }
  1077. hadWS = false;
  1078. hadDot = false;
  1079. bool shortcut = true;
  1080. // valid char for keyword
  1081. if (c1 >= 'a' && c1 <= 'z')
  1082. {
  1083. addChar = true;
  1084. }
  1085. else
  1086. {
  1087. // valid chars for identifiers
  1088. if (c1 >= 'A' && c1 <= 'Z')
  1089. {
  1090. addChar = true;
  1091. }
  1092. else if (c1 == '$' || c1 == '_')
  1093. {
  1094. addChar = true;
  1095. }
  1096. else if (length > 0)
  1097. {
  1098. if (c1 >= '0' && c1 <= '9')
  1099. {
  1100. addChar = true;
  1101. }
  1102. else if (c1 == '*' && context == FlagType.Import)
  1103. {
  1104. addChar = true;
  1105. }
  1106. // haXe generics
  1107. else if (features.hasGenerics && c1 == '<')
  1108. {
  1109. if (!inValue && i > 2 && length > 1 && i < len - 3
  1110. && Char.IsLetterOrDigit(ba[i - 3]) && (Char.IsLetter(ba[i]) || (haXe && ba[i] == '{'))
  1111. && (Char.IsLetter(buffer[0]) || buffer[0] == '_'))
  1112. {
  1113. if (curMember == null)
  1114. {
  1115. evalToken = 0;
  1116. inGeneric = true;
  1117. addChar = true;
  1118. }
  1119. else
  1120. {
  1121. evalToken = 0;
  1122. inGeneric = true;
  1123. inValue = true;
  1124. inType = true;
  1125. inAnonType = false;
  1126. valueLength = 0;
  1127. for (int j = 0; j < length; j++)

Large files files are truncated, but you can click here to view the full file