PageRenderTime 59ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/trunk/Qlab 0.1/Qlab/ParserCore/Tools/gplex-distro-1.1.4/project/GPLEX/Minimizer.cs

#
C# | 316 lines | 202 code | 18 blank | 96 comment | 41 complexity | 382e9f7b70511c98c121decb46cee038 MD5 | raw file
Possible License(s): MIT
  1. // Gardens Point Scanner Generator
  2. // Copyright (c) K John Gough, QUT 2006-2010
  3. // (see accompanying GPLEXcopyright.rtf)
  4. using System;
  5. using System.IO;
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. using System.Diagnostics.CodeAnalysis;
  9. using System.Globalization;
  10. using System.Text;
  11. using QUT.Gplex.Parser;
  12. namespace QUT.Gplex.Automaton
  13. {
  14. internal class PartitionBlock
  15. {
  16. private int symbolsLeft; // Number of symbols left on the "pair-list" for this block.
  17. private int generation; // The current splitting generation.
  18. private int predCount; // Number of predecessors from the current generation.
  19. internal PartitionBlock twinBlk; // During a split, the two fragments reference each other.
  20. internal LinkedList<DFSA.DState> members;
  21. public int Sym { get { return symbolsLeft; } set { symbolsLeft = value; } }
  22. public int Gen { get { return generation; } set { generation = value; } }
  23. public int PredCount { get { return predCount; } set { predCount = value; } }
  24. public int MemberCount { get { return members.Count; } }
  25. public DFSA.DState FirstMember { get { return members.First.Value; } }
  26. /// <summary>
  27. /// Add the given node to the linked list
  28. /// </summary>
  29. /// <param name="node"></param>
  30. private void AddNode(LinkedListNode<DFSA.DState> node) { this.members.AddLast(node); }
  31. /// <summary>
  32. /// Add a new node to the list, with value given by the dSt
  33. /// </summary>
  34. /// <param name="dSt"></param>
  35. internal void AddState(DFSA.DState dSt)
  36. {
  37. LinkedListNode<DFSA.DState> node = new LinkedListNode<DFSA.DState>(dSt);
  38. dSt.listNode = node;
  39. this.members.AddLast(node);
  40. }
  41. /// <summary>
  42. /// Move the node with value dSt from this partition to blk.
  43. /// </summary>
  44. /// <param name="dSt">value to be moved</param>
  45. /// <param name="blk">destination partition</param>
  46. internal void MoveMember(DFSA.DState dSt, PartitionBlock blk)
  47. {
  48. // Assert: dSt must belong to LinkedList this.members
  49. LinkedListNode<DFSA.DState> node = dSt.listNode;
  50. this.members.Remove(node);
  51. this.predCount--;
  52. blk.AddNode(node);
  53. }
  54. internal PartitionBlock(int symbolCardinality) {
  55. symbolsLeft = symbolCardinality; // Default cardinality of symbol alphabet.
  56. members = new LinkedList<DFSA.DState>();
  57. }
  58. }
  59. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Minimizer")]
  60. // Apparently "minimizer" is not in the FxCop dictionary? It should be!
  61. public class Minimizer
  62. {
  63. private DFSA dfsa;
  64. private Stack<PartitionBlock> worklist = new Stack<PartitionBlock>();
  65. PartitionBlock otherStates;
  66. PartitionBlock startStates;
  67. List<PartitionBlock> acceptStates = new List<PartitionBlock>();
  68. List<PartitionBlock> allBlocks = new List<PartitionBlock>();
  69. internal Minimizer(DFSA dfsa) {
  70. this.dfsa = dfsa;
  71. otherStates = MkNewBlock();
  72. startStates = MkNewBlock();
  73. }
  74. PartitionBlock MkNewBlock()
  75. {
  76. PartitionBlock blk = new PartitionBlock(dfsa.MaxSym);
  77. allBlocks.Add(blk);
  78. return blk;
  79. }
  80. /// <summary>
  81. /// The initial partitions are formed by the following
  82. /// rules:
  83. /// Every accept state shares a partition with all other
  84. /// accept states that have the same semantic action.
  85. /// All other states go in the partition "otherStates".
  86. ///
  87. /// Addendum, Version 0.3: start states must be kept out
  88. /// of the otherStates partition, otherwise prefixes can
  89. /// be chopped off by, for example, concluding that states
  90. /// {1,3} are equivalent in the following FSA. They are,
  91. /// but the computation of yytext is broken if you merge!
  92. ///
  93. /// a b c d
  94. /// (start state) 1---->2---->3---->4-----(5) (accept state)
  95. ///
  96. /// c d
  97. /// (start state) 1---->7---->(5) (accept state)
  98. ///
  99. /// The inverse transition function is computed at the same
  100. /// time as the initial partition is performed.
  101. /// </summary>
  102. /// <param name="list">The list of all DStates</param>
  103. internal void PopulatePartitions(List<DFSA.DState> list)
  104. {
  105. PartitionBlock blk = null;
  106. dfsa.origLength = list.Count;
  107. for (int i = 1; i < list.Count; i++)
  108. {
  109. DFSA.DState dSt = list[i];
  110. if (dSt.accept != null)
  111. blk = FindPartition(dSt);
  112. else if (dSt.isStart)
  113. blk = startStates;
  114. else
  115. blk = otherStates;
  116. blk.AddState(dSt);
  117. dSt.block = blk;
  118. // now create the inverse transition function
  119. for (int j = 0; j < dfsa.MaxSym; j++)
  120. {
  121. DFSA.DState pred = dSt.GetNext(j);
  122. if (pred != null) pred.AddPredecessor(dSt, j);
  123. }
  124. }
  125. // Now add the eofState as an accept state.
  126. // This is *despite* the state not having an accept reference!
  127. blk = MkNewBlock();
  128. acceptStates.Add(blk);
  129. blk.AddState(list[0]);
  130. list[0].block = blk;
  131. // And now finally initialize the pair list.
  132. worklist.Push(startStates);
  133. worklist.Push(otherStates); // EXPERIMENTAL
  134. foreach (PartitionBlock lst in acceptStates) worklist.Push(lst);
  135. }
  136. /// <summary>
  137. /// Find an existing partition block with which dSt is compatible,
  138. /// or construct a new partition into which dSt can be placed.
  139. /// </summary>
  140. /// <param name="dSt"></param>
  141. /// <returns></returns>
  142. PartitionBlock FindPartition(DFSA.DState dSt)
  143. {
  144. foreach (PartitionBlock blk in acceptStates)
  145. {
  146. // Assert every partition in acceptStates has at least one member.
  147. // Assert every member has the same semantic action.
  148. //
  149. // This would be a simple matter except for right context. Is such
  150. // cases the action is an input backup, then the user action. For a
  151. // pattern R1/R2 the regex R1.R2 (concatenation) is recognized
  152. // and the buffer backed up to the position of the '/'.
  153. // In the case of R1 of fixed length N we do "yyless(N);"
  154. // In the case of R2 of fixed length N we do "yyless(yyleng-N);"
  155. // If the first state in the partition has both lengths fixed
  156. // we must choose one or the other backup action, and only add
  157. // other states that are compatible with that choice.
  158. DFSA.DState first = blk.FirstMember;
  159. if (DFSA.SpansEqual(first.accept.aSpan, dSt.accept.aSpan))
  160. if (!first.HasRightContext && !dSt.HasRightContext)
  161. return blk;
  162. else
  163. {
  164. if (first.lhCntx > 0 && first.lhCntx == dSt.lhCntx)
  165. // From now on only add states with matching lhs length
  166. { first.rhCntx = 0; dSt.rhCntx = 0; return blk; }
  167. if (first.rhCntx > 0 && first.rhCntx == dSt.rhCntx)
  168. // From now on only add states with matching rhs length
  169. { first.lhCntx = 0; dSt.lhCntx = 0; return blk; }
  170. }
  171. }
  172. PartitionBlock nxt = MkNewBlock();
  173. acceptStates.Add(nxt);
  174. return nxt;
  175. }
  176. /// <summary>
  177. /// Maps old dfsa states to new states in the minimized set.
  178. /// </summary>
  179. /// <param name="dSt">The state to be mapped</param>
  180. /// <returns>The replacement state</returns>
  181. internal static DFSA.DState PMap(DFSA.DState dSt)
  182. {
  183. PartitionBlock blk = dSt.block as PartitionBlock;
  184. if (blk.MemberCount == 1) return dSt;
  185. else return blk.FirstMember;
  186. }
  187. /// <summary>
  188. /// Refine the partitions by splitting incompatible states
  189. /// </summary>
  190. internal void RefinePartitions()
  191. {
  192. int generation = 0;
  193. while (worklist.Count > 0)
  194. {
  195. List<DFSA.DState> predSet = null;
  196. //
  197. // We are going to split all blocks with respect to a
  198. // particular (Block, Symbol) pair.
  199. // Fetch the pair components.
  200. //
  201. // In order to avoid having to reset the predCount ints
  202. // all the time we keep a generation count to indicate to
  203. // which pass through the loop the counts apply.
  204. //
  205. PartitionBlock blk = worklist.Peek();
  206. int sym = blk.Sym - 1;
  207. if (sym < 0) {
  208. worklist.Pop(); // Remove block from the worklist.
  209. continue; // Go around again to get new block.
  210. }
  211. generation++;
  212. predSet = new List<DFSA.DState>();
  213. //
  214. // Form a set of all those states that have a
  215. // next-state in "blk" on symbol "sym".
  216. // Note that despite the nested loops the
  217. // complexity is only N == number of states.
  218. //
  219. foreach (DFSA.DState dSt in blk.members)
  220. {
  221. if (dSt.HasPredecessors())
  222. {
  223. List<DFSA.DState> prds = dSt.GetPredecessors(sym);
  224. if (prds != null && prds.Count > 0)
  225. foreach (DFSA.DState pSt in prds)
  226. // The same predecessor state might appear on the
  227. // list for more than one state of the blk.members.
  228. // States should only appear once in the predSet "set"
  229. if (pSt.listOrd != generation) // This predecessor is not in the set already
  230. {
  231. PartitionBlock predBlk = (pSt.block as PartitionBlock);
  232. pSt.listOrd = generation;
  233. if (predBlk.Gen != generation)
  234. {
  235. predBlk.Gen = generation;
  236. predBlk.PredCount = 0;
  237. }
  238. predSet.Add(pSt); // Add the predecessor to the set
  239. predBlk.PredCount++;
  240. }
  241. }
  242. }
  243. blk.Sym--; // "Remove" (blk,sym) from the pair list.
  244. //
  245. // Now, if the predecessor set is not empty,
  246. // we split all blocks with respect to (blk,sym)
  247. //
  248. if (predSet.Count != 0)
  249. {
  250. List<PartitionBlock> splits = new List<PartitionBlock>();
  251. foreach (DFSA.DState lSt in predSet)
  252. {
  253. PartitionBlock tBlk = null;
  254. PartitionBlock lBlk = (lSt.block as PartitionBlock);
  255. if (lBlk.PredCount != lBlk.MemberCount) // Need to split this block
  256. {
  257. tBlk = lBlk.twinBlk;
  258. if (tBlk == null)
  259. {
  260. tBlk = MkNewBlock();
  261. splits.Add(tBlk);
  262. lBlk.twinBlk = tBlk;
  263. tBlk.twinBlk = lBlk;
  264. }
  265. lBlk.MoveMember(lSt, tBlk);
  266. lSt.block = tBlk;
  267. }
  268. }
  269. foreach (PartitionBlock tBlk in splits)
  270. {
  271. PartitionBlock lBlk = tBlk.twinBlk;
  272. PartitionBlock push = null;
  273. if (lBlk.Sym == 0) // lBlk is not currently on the list so push smaller block
  274. {
  275. if (lBlk.MemberCount < tBlk.MemberCount)
  276. push = lBlk;
  277. else
  278. push = tBlk;
  279. }
  280. else // lBlk is on the list with Sym > 0
  281. {
  282. push = tBlk;
  283. if (lBlk.MemberCount < tBlk.MemberCount)
  284. {
  285. // tBlk has the larger cardinality, so give it the smaller Sym value
  286. tBlk.Sym = lBlk.Sym;
  287. lBlk.Sym = dfsa.MaxSym;
  288. }
  289. }
  290. worklist.Push(push);
  291. lBlk.twinBlk = null;
  292. tBlk.twinBlk = null;
  293. }
  294. }
  295. } // end of while loop...
  296. }
  297. }
  298. }