/TypeCobol/Compiler/Types/PictureValidator.Automata.cs

https://github.com/TypeCobolTeam/TypeCobol · C# · 299 lines · 245 code · 21 blank · 33 comment · 91 complexity · c07da0f96340187ffd0dbd3aa79cb564 MD5 · raw file

  1. using System.Collections.Generic;
  2. namespace TypeCobol.Compiler.Types
  3. {
  4. public partial class PictureValidator
  5. {
  6. private partial class Automata
  7. {
  8. private readonly PictureValidator _validator;
  9. public int Digits { get; private set; }
  10. public int RealDigits { get; private set; }
  11. public bool IsSigned { get; private set; }
  12. public int Scale { get; private set; }
  13. public int Size { get; private set; }
  14. public Automata(PictureValidator validator)
  15. {
  16. _validator = validator;
  17. }
  18. public bool Run(Character[] sequence, PictureCategory category, List<string> validationMessages)
  19. {
  20. ComputeFloatingInsertionRange(sequence, category, out int firstFloatingIndex, out int lastFloatingIndex);
  21. //Initialization
  22. var sequenceEnumerator = new SequenceEnumerator(sequence, firstFloatingIndex, lastFloatingIndex);
  23. State state = State.Initial;
  24. int vCount = 0;
  25. bool zSeen = false;
  26. bool starSeen = false;
  27. bool csSignSizeAdded = false;
  28. while (sequenceEnumerator.MoveNext())
  29. {
  30. Character c = sequenceEnumerator.Current;
  31. System.Diagnostics.Debug.Assert(c != null);
  32. //Check transition from current state to next
  33. if (!state.CheckTransition(c.SpecialChar, sequenceEnumerator, out State nextState))
  34. {
  35. validationMessages.Add(string.Format(INVALID_SYMBOL_POSITION, _validator.SC2String(c.SpecialChar)));
  36. return false;
  37. }
  38. //Check current symbol
  39. if (!ValidateSymbol(c))
  40. {
  41. return false;
  42. }
  43. //Go to next state and check reflexive transition
  44. state = nextState;
  45. if (c.Count > 1 && !state.CheckReflexiveTransition())
  46. {
  47. validationMessages.Add(string.Format(INVALID_SYMBOL_POSITION, _validator.SC2String(c.SpecialChar)));
  48. return false;
  49. }
  50. }
  51. return true;
  52. //Perform validation on a given symbol and update Digits/Scale/Size
  53. bool ValidateSymbol(Character c)
  54. {
  55. switch (c.SpecialChar)
  56. {
  57. case SC.PLUS:
  58. case SC.MINUS:
  59. Digits += c.Count - 1;
  60. IsSigned = true;
  61. break;
  62. case SC.Z:
  63. case SC.STAR:
  64. if (c.SpecialChar == SC.Z)
  65. zSeen = true;
  66. else
  67. starSeen = true;
  68. if (zSeen && starSeen)
  69. {
  70. validationMessages.Add(Z_STAR_MUTUALLY_EXCLUSIVE);
  71. return false;
  72. }
  73. if (sequenceEnumerator.IsLast)
  74. {
  75. // Check that sequence contains only DOT, COMMA or Z or * or CS
  76. foreach (var s in sequence)
  77. {
  78. if (s.SpecialChar != SC.B &&
  79. s.SpecialChar != SC.ZERO &&
  80. s.SpecialChar != SC.SLASH &&
  81. s.SpecialChar != SC.COMMA &&
  82. s.SpecialChar != SC.DOT &&
  83. s.SpecialChar != SC.CS &&
  84. s.SpecialChar != SC.Z &&
  85. s.SpecialChar != SC.STAR &&
  86. s.SpecialChar != SC.PLUS &&
  87. s.SpecialChar != SC.MINUS)
  88. return false;
  89. }
  90. }
  91. Digits += c.Count;
  92. if (vCount > 0)
  93. {
  94. Scale += c.Count;
  95. }
  96. break;
  97. case SC.CR:
  98. case SC.DB:
  99. IsSigned = true;
  100. break;
  101. case SC.CS:
  102. Digits += c.Count - 1;
  103. break;
  104. case SC.NINE:
  105. Digits += c.Count;
  106. RealDigits += c.Count;
  107. if (vCount > 0)
  108. {
  109. //We have seen the decimal point --> digits are in the decimal part
  110. Scale += c.Count;
  111. }
  112. break;
  113. case SC.S:
  114. if (!sequenceEnumerator.IsFirst)
  115. {
  116. validationMessages.Add(SYMBOL_S_MUST_BE_THE_FIRST);
  117. return false;
  118. }
  119. IsSigned = true;
  120. break;
  121. case SC.DOT:
  122. case SC.V:
  123. vCount += c.Count;
  124. if (vCount > 1)
  125. {
  126. validationMessages.Add(MULTIPLE_V);
  127. return false;
  128. }
  129. break;
  130. case SC.P:
  131. if (!ValidateP())
  132. return false;
  133. Digits += c.Count;
  134. Scale += vCount > 0 ? c.Count : -c.Count;
  135. break;
  136. }
  137. //Update total size
  138. switch (c.SpecialChar)
  139. {
  140. case SC.S:
  141. Size += _validator.IsSeparateSign ? 1 : 0;
  142. break;
  143. case SC.V:
  144. case SC.P:
  145. break;
  146. case SC.DB:
  147. case SC.CR:
  148. Size += c.Count * 2;
  149. break;
  150. case SC.CS:
  151. System.Diagnostics.Debug.Assert(_validator._currencyDescriptor != null);
  152. if (!csSignSizeAdded)
  153. {
  154. //First CS adds the sign length
  155. Size += _validator._currencyDescriptor.Sign.Length;
  156. csSignSizeAdded = true;
  157. Size += c.Count - 1; //Each subsequent CS counts for 1 character
  158. }
  159. else
  160. {
  161. Size += c.Count; //Each subsequent CS counts for 1 character
  162. }
  163. break;
  164. case SC.N:
  165. case SC.G:
  166. Size += c.Count * 2;
  167. break;
  168. default:
  169. Size += c.Count;
  170. break;
  171. }
  172. return true;
  173. }
  174. /*
  175. * Validate the position of the P character.
  176. * The Symbol P specifies a scaling position and implies an assumed decimal point
  177. * (to the left of the Ps if the Ps are leftmost PICTURE characters,
  178. * to the right of the Ps if the Ps are rightmost PICTURE characters).
  179. *
  180. * If we say that the character ^ means the beginning of the PICTURE sequence and $ means the end of the PICTURE sequence,
  181. * only the following situations are valid for P:
  182. * ^P | ^VP | ^SP | ^SVP | P$ | PV$
  183. */
  184. bool ValidateP()
  185. {
  186. if (sequenceEnumerator.IsFirst || sequenceEnumerator.IsLast)
  187. {
  188. vCount += sequenceEnumerator.Index == 0 ? 1 : 0; //Assume decimal point symbol V at the beginning
  189. return true;//^P | P$;
  190. }
  191. if (sequenceEnumerator.Index == 1 && (sequence[0].SpecialChar == SC.S || sequence[0].SpecialChar == SC.V))
  192. {
  193. vCount += 1;//Assume decimal point symbol V at the beginning
  194. return true;//^SP | ^VP
  195. }
  196. if (sequenceEnumerator.Index == 2 && sequence[0].SpecialChar == SC.S && sequence[1].SpecialChar == SC.V)
  197. {
  198. vCount += 1;//Assume decimal point symbol V at the beginning
  199. return true;//^SVP
  200. }
  201. if (sequenceEnumerator.Index == sequence.Length - 2 && sequence[sequence.Length - 1].SpecialChar == SC.V)
  202. {
  203. return true;//PV$
  204. }
  205. //validation failed
  206. validationMessages.Add(WRONG_P_POSITION);
  207. return false;
  208. }
  209. }
  210. /// <summary>
  211. /// Compute indexes of the Floating Insertion String. That is to the left most and the right most of the characters
  212. /// CS, + or -.
  213. /// </summary>
  214. /// <param name="sequence">The on which to perform the computation</param>
  215. /// <param name="category">Data category computed for the sequence</param>
  216. /// <param name="firstFloatingIndex">[out] First Floating Index</param>
  217. /// <param name="lastFloatingIndex">[out] Last Floating Index</param>
  218. private void ComputeFloatingInsertionRange(Character[] sequence, PictureCategory category, out int firstFloatingIndex, out int lastFloatingIndex)
  219. {
  220. firstFloatingIndex = lastFloatingIndex = -1;
  221. if (category != PictureCategory.NumericEdited) return;//Floating insertion is valid only for NumericEdited items
  222. int lastNonSimpleIndex = -1;
  223. SC floatChar = (SC)(-1); //The float character that corresponds to the first index either CS, + or -.
  224. int i;
  225. for (i = 0; i < sequence.Length; i++)
  226. {
  227. Character c = sequence[i];
  228. if (firstFloatingIndex == -1 && (c.SpecialChar == SC.PLUS || c.SpecialChar == SC.MINUS || c.SpecialChar == SC.CS))
  229. {
  230. if (lastNonSimpleIndex >= 0 && sequence[lastNonSimpleIndex].SpecialChar == c.SpecialChar)
  231. {
  232. firstFloatingIndex = lastNonSimpleIndex;
  233. floatChar = c.SpecialChar;
  234. continue;
  235. }
  236. if (c.Count > 1)
  237. {
  238. firstFloatingIndex = i;
  239. floatChar = c.SpecialChar;
  240. continue;
  241. }
  242. }
  243. if (firstFloatingIndex == -1 && !IsSimpleInsertionCharacter(c.SpecialChar))
  244. {
  245. lastNonSimpleIndex = i;
  246. }
  247. else if (firstFloatingIndex >= 0 && !(IsSimpleInsertionCharacter(c.SpecialChar) || c.SpecialChar == floatChar))
  248. {
  249. lastFloatingIndex = i - 1;
  250. break;
  251. }
  252. }
  253. if (i >= sequence.Length && firstFloatingIndex >= 0)
  254. {
  255. //We have reached the end of the sequence with a first index and no lastIndex ==>
  256. //Set the last index to the last character of the sequence
  257. lastFloatingIndex = sequence.Length - 1;
  258. return;
  259. }
  260. if (!(i < sequence.Length && (sequence[i].SpecialChar == SC.DOT || sequence[i].SpecialChar == SC.V)))
  261. {
  262. //The last index does not precede the DecimalPoint separator position
  263. return;
  264. }
  265. //If the last index precedes the DecimalPoint position so all characters including the decimal point
  266. //that are not simple characters or the floating character must be part of the right most index
  267. for (i = i + 1; i < sequence.Length; i++)
  268. {
  269. Character c = sequence[i];
  270. if (!(IsSimpleInsertionCharacter(c.SpecialChar) || c.SpecialChar == floatChar))
  271. return;
  272. }
  273. lastFloatingIndex = i - 1;
  274. bool IsSimpleInsertionCharacter(SC c) => c == SC.B || c == SC.ZERO || c == SC.SLASH || c == SC.COMMA;
  275. }
  276. }
  277. }
  278. }