utcparser.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2025 by Michael Van Canneyt ([email protected])
  4. Test EBNF Parser
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit utcparser;
  12. interface
  13. uses
  14. SysUtils, classes, fpcunit, testregistry,
  15. ebnf.tree,
  16. ebnf.parser;
  17. type
  18. { TTestEBNFParser }
  19. TTestEBNFParser = class(TTestCase)
  20. private
  21. FGrammar: TEBNFGrammar;
  22. FParser: TEBNFParser;
  23. protected
  24. procedure TearDown; override;
  25. procedure AssertEquals(const Msg : String; aExpected, aActual : TEBNFElementType); overload;
  26. procedure CheckEquals(aExpected, aActual : TEBNFElementType; const Msg : String = ''); overload;
  27. Property Parser : TEBNFParser Read FParser Write FParser;
  28. Property Grammar : TEBNFGrammar Read FGrammar Write FGrammar;
  29. published
  30. procedure TestOneRuleOneTermOneFactor;
  31. procedure TestOneRuleOneTermTwoFactors;
  32. procedure TestOneRuleTwoTermsOneFactorEach;
  33. procedure TestOneRuleTwoTermsTwoFactorsEach;
  34. procedure TestTwoRulesOneTermOneFactorEach;
  35. procedure TestRuleWithOptionalGroup;
  36. procedure TestRuleWithRepetitionGroup;
  37. procedure TestRuleWithParenthesizedGroup;
  38. procedure TestRuleWithSpecialSequence;
  39. procedure TestDuplicateRuleError;
  40. procedure TestMissingEqualsError;
  41. procedure TestMissingSemicolonError;
  42. procedure TestUnexpectedTokenInFactorError;
  43. end;
  44. implementation
  45. uses typinfo;
  46. procedure TTestEBNFParser.AssertEquals(const Msg: String; aExpected, aActual: TEBNFElementType);
  47. begin
  48. AssertEquals(Msg,GetEnumName(typeInfo(TEBNFElementType),ord(aExpected)),
  49. GetEnumName(typeInfo(TEBNFElementType),ord(aActual)));
  50. end;
  51. procedure TTestEBNFParser.CheckEquals(aExpected, aActual: TEBNFElementType; const Msg: String);
  52. begin
  53. AssertEquals(Msg,aExpected,aActual);
  54. end;
  55. procedure TTestEBNFParser.TearDown;
  56. begin
  57. FreeAndNil(FParser);
  58. FreeAndNil(FGrammar);
  59. inherited TearDown;
  60. end;
  61. procedure TTestEBNFParser.TestOneRuleOneTermOneFactor;
  62. var
  63. EBNFSource: string;
  64. Rule: TEBNFRule;
  65. Expression: TEBNFExpression;
  66. Term: TEBNFTerm;
  67. Factor: TEBNFFactor;
  68. begin
  69. EBNFSource := 'rule1 = "literal" ;';
  70. Parser := TEBNFParser.Create(EBNFSource);
  71. Grammar := Parser.Parse;
  72. CheckEquals(1, Grammar.ChildCount, 'Expected 1 rule in grammar');
  73. Rule:=Grammar.Rules['rule1'];
  74. CheckNotNull(Rule, 'Rule object should not be nil');
  75. CheckEquals(etExpression, Rule.Expression.NodeType, 'Rule expression should be of type anExpression');
  76. Expression := TEBNFExpression(Rule.Expression);
  77. CheckEquals(1, Expression.ChildCount, 'Expected 1 term in expression');
  78. CheckEquals(etTerm, Expression.Terms[0].NodeType, 'Expression term should be of type anTerm');
  79. Term := TEBNFTerm(Expression.Terms[0]);
  80. CheckEquals(1, Term.ChildCount, 'Expected 1 factor in term');
  81. CheckEquals(etFactorStringLiteral, Term.Factors[0].NodeType, 'Term factor should be of type anFactorStringLiteral');
  82. Factor := TEBNFFactor(Term.Factors[0]);
  83. CheckEquals('literal', Factor.Value, 'Factor value should be "literal"');
  84. end;
  85. procedure TTestEBNFParser.TestOneRuleOneTermTwoFactors;
  86. var
  87. EBNFSource: string;
  88. Rule: TEBNFRule;
  89. Expression: TEBNFExpression;
  90. Term: TEBNFTerm;
  91. Factor1, Factor2: TEBNFFactor;
  92. begin
  93. EBNFSource := 'rule2 = identifier1 "literal2" ;';
  94. Parser := TEBNFParser.Create(EBNFSource);
  95. Grammar := Parser.Parse;
  96. CheckEquals(1, Grammar.ChildCount, 'Expected 1 rule in grammar');
  97. Rule:=Grammar.Rules['rule2'];
  98. CheckNotNull(Rule, 'Expected rule "rule2" to exist');
  99. Expression := TEBNFExpression(Rule.Expression);
  100. CheckEquals(1, Expression.ChildCount, 'Expected 1 term in expression');
  101. Term := TEBNFTerm(Expression.Terms[0]);
  102. CheckEquals(2, Term.ChildCount, 'Expected 2 factors in term');
  103. Factor1 := TEBNFFactor(Term.Factors[0]);
  104. CheckEquals(etFactorIdentifier, Factor1.NodeType, 'First factor should be identifier');
  105. CheckEquals('identifier1', Factor1.Value, 'First factor value should be "identifier1"');
  106. Factor2 := TEBNFFactor(Term.Factors[1]);
  107. CheckEquals(etFactorStringLiteral, Factor2.NodeType, 'Second factor should be string literal');
  108. CheckEquals('literal2', Factor2.Value, 'Second factor value should be "literal2"');
  109. end;
  110. procedure TTestEBNFParser.TestOneRuleTwoTermsOneFactorEach;
  111. var
  112. EBNFSource: string;
  113. Rule: TEBNFRule;
  114. Expression: TEBNFExpression;
  115. Term1, Term2: TEBNFTerm;
  116. Factor1, Factor2: TEBNFFactor;
  117. begin
  118. EBNFSource := 'rule3 = factorA | factorB ;';
  119. Parser := TEBNFParser.Create(EBNFSource);
  120. Grammar := Parser.Parse;
  121. CheckEquals(1, Grammar.ChildCount, 'Expected 1 rule in grammar');
  122. Rule:=Grammar.Rules['rule3'];
  123. ChecknotNull(Rule, 'Expected rule "rule3" to exist');
  124. Expression := TEBNFExpression(Rule.Expression);
  125. CheckEquals(2, Expression.ChildCount, 'Expected 2 terms in expression');
  126. // First term
  127. Term1 := TEBNFTerm(Expression.Terms[0]);
  128. CheckEquals(1, Term1.ChildCount, 'Expected 1 factor in first term');
  129. Factor1 := TEBNFFactor(Term1.Factors[0]);
  130. CheckEquals(etFactorIdentifier, Factor1.NodeType, 'First term factor should be identifier');
  131. CheckEquals('factorA', Factor1.Value, 'First term factor value should be "factorA"');
  132. // Second term
  133. Term2 := TEBNFTerm(Expression.Terms[1]);
  134. CheckEquals(1, Term2.ChildCount, 'Expected 1 factor in second term');
  135. Factor2 := TEBNFFactor(Term2.Factors[0]);
  136. CheckEquals(etFactorIdentifier, Factor2.NodeType, 'Second term factor should be identifier');
  137. CheckEquals('factorB', Factor2.Value, 'Second term factor value should be "factorB"');
  138. end;
  139. procedure TTestEBNFParser.TestOneRuleTwoTermsTwoFactorsEach;
  140. var
  141. EBNFSource: string;
  142. Rule: TEBNFRule;
  143. Expression: TEBNFExpression;
  144. Term1, Term2: TEBNFTerm;
  145. Factor: TEBNFFactor;
  146. begin
  147. EBNFSource := 'rule4 = (id1 "lit1") | (id2 "lit2") ;';
  148. Parser := TEBNFParser.Create(EBNFSource);
  149. Grammar := Parser.Parse;
  150. CheckEquals(1, Grammar.ChildCount, 'Expected 1 rule in grammar');
  151. Rule:=Grammar.Rules['rule4'];
  152. CheckNotNull(Rule, 'Expected rule "rule4" to exist');
  153. Expression := TEBNFExpression(Rule.Expression);
  154. CheckEquals(2, Expression.ChildCount, 'Expected 2 terms in expression');
  155. // First term (group)
  156. Term1 := TEBNFTerm(Expression.Terms[0]);
  157. CheckEquals(1, Term1.ChildCount, 'Expected 1 factor (group) in first term');
  158. Factor := TEBNFFactor(Term1.Factors[0]);
  159. CheckEquals(etFactorGroup, Factor.NodeType, 'Factor should be a group');
  160. CheckNotNull(Factor.InnerNode, 'Inner node of group should not be nil');
  161. // Check inner expression of first group
  162. CheckEquals(etExpression, Factor.InnerNode.NodeType, 'Inner node should be an expression');
  163. CheckEquals(1, TEBNFExpression(Factor.InnerNode).ChildCount, 'Inner expression should have 1 term');
  164. CheckEquals(2, TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[0]).ChildCount, 'Inner term should have 2 factors');
  165. CheckEquals('id1', TEBNFFactor(TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[0]).Factors[0]).Value, 'Inner factor 1 value');
  166. CheckEquals('lit1', TEBNFFactor(TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[0]).Factors[1]).Value, 'Inner factor 2 value');
  167. // Second term (group)
  168. Term2 := TEBNFTerm(Expression.Terms[1]);
  169. CheckEquals(1, Term2.ChildCount, 'Expected 1 factor (group) in second term');
  170. Factor := TEBNFFactor(Term2.Factors[0]);
  171. CheckEquals(etFactorGroup, Factor.NodeType, 'Factor should be a group');
  172. CheckNotNull(Factor.InnerNode, 'Inner node of group should not be nil');
  173. // Check inner expression of second group
  174. CheckEquals(etExpression, Factor.InnerNode.NodeType, 'Inner node should be an expression');
  175. CheckEquals(1, TEBNFExpression(Factor.InnerNode).ChildCount, 'Inner expression should have 1 term');
  176. CheckEquals(2, TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[0]).ChildCount, 'Inner term should have 2 factors');
  177. CheckEquals('id2', TEBNFFactor(TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[0]).Factors[0]).Value, 'Inner factor 1 value');
  178. CheckEquals('lit2', TEBNFFactor(TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[0]).Factors[1]).Value, 'Inner factor 2 value');
  179. end;
  180. procedure TTestEBNFParser.TestTwoRulesOneTermOneFactorEach;
  181. var
  182. EBNFSource: string;
  183. Rule1, Rule2: TEBNFRule;
  184. begin
  185. EBNFSource :=
  186. 'ruleA = "first" ;' + sLineBreak +
  187. 'ruleB = identifierB ;';
  188. Parser := TEBNFParser.Create(EBNFSource);
  189. Grammar := Parser.Parse;
  190. CheckEquals(2, Grammar.ChildCount, 'Expected 2 rules in grammar');
  191. // Check ruleA
  192. Rule1:=Grammar.Rules['ruleA'];
  193. CheckNotNull(Rule1, 'Expected rule "ruleA" to exist');
  194. CheckEquals(etExpression, Rule1.Expression.NodeType, 'RuleA expression type');
  195. CheckEquals(1, TEBNFExpression(Rule1.Expression).ChildCount, 'RuleA expression terms count');
  196. CheckEquals(1, TEBNFTerm(TEBNFExpression(Rule1.Expression).Terms[0]).ChildCount, 'RuleA term factors count');
  197. CheckEquals(etFactorStringLiteral, TEBNFFactor(TEBNFTerm(TEBNFExpression(Rule1.Expression).Terms[0]).Factors[0]).NodeType, 'RuleA factor type');
  198. CheckEquals('first', TEBNFFactor(TEBNFTerm(TEBNFExpression(Rule1.Expression).Terms[0]).Factors[0]).Value, 'RuleA factor value');
  199. // Check ruleB
  200. Rule2:=Grammar.Rules['ruleB'];
  201. CheckNotNull(Rule2, 'Expected rule "ruleB" to exist');
  202. CheckEquals(etExpression, Rule2.Expression.NodeType, 'RuleB expression type');
  203. CheckEquals(1, TEBNFExpression(Rule2.Expression).ChildCount, 'RuleB expression terms count');
  204. CheckEquals(1, TEBNFTerm(TEBNFExpression(Rule2.Expression).Terms[0]).ChildCount, 'RuleB term factors count');
  205. CheckEquals(etFactorIdentifier, TEBNFFactor(TEBNFTerm(TEBNFExpression(Rule2.Expression).Terms[0]).Factors[0]).NodeType, 'RuleB factor type');
  206. CheckEquals('identifierB', TEBNFFactor(TEBNFTerm(TEBNFExpression(Rule2.Expression).Terms[0]).Factors[0]).Value, 'RuleB factor value');
  207. end;
  208. procedure TTestEBNFParser.TestRuleWithOptionalGroup;
  209. var
  210. EBNFSource: string;
  211. Rule: TEBNFRule;
  212. Expression: TEBNFExpression;
  213. Term: TEBNFTerm;
  214. Factor: TEBNFFactor;
  215. begin
  216. EBNFSource := 'optional_rule = [ "optional_part" ] ;';
  217. Parser := TEBNFParser.Create(EBNFSource);
  218. Grammar := Parser.Parse;
  219. Rule:=Grammar.Rules['optional_rule'];
  220. CheckNotNull(Rule, 'Expected rule "optional_rule"');
  221. Expression := TEBNFExpression(Rule.Expression);
  222. Term := TEBNFTerm(Expression.Terms[0]);
  223. Factor := TEBNFFactor(Term.Factors[0]);
  224. CheckEquals(etFactorOptional, Factor.NodeType, 'Factor should be an optional group');
  225. CheckNotNull(Factor.InnerNode, 'Optional group should have an inner node');
  226. CheckEquals(etExpression, Factor.InnerNode.NodeType, 'Inner node should be an expression');
  227. CheckEquals(1, TEBNFExpression(Factor.InnerNode).ChildCount, 'Inner expression should have 1 term');
  228. CheckEquals(1, TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[0]).ChildCount, 'Inner term should have 1 factor');
  229. CheckEquals(etFactorStringLiteral, TEBNFFactor(TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[0]).Factors[0]).NodeType, 'Inner factor type');
  230. CheckEquals('optional_part', TEBNFFactor(TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[0]).Factors[0]).Value, 'Inner factor value');
  231. end;
  232. procedure TTestEBNFParser.TestRuleWithRepetitionGroup;
  233. var
  234. EBNFSource: string;
  235. Rule: TEBNFRule;
  236. Expression: TEBNFExpression;
  237. Term: TEBNFTerm;
  238. Factor: TEBNFFactor;
  239. begin
  240. EBNFSource := 'repeat_rule = { identifier_to_repeat } ;';
  241. Parser := TEBNFParser.Create(EBNFSource);
  242. Grammar := Parser.Parse;
  243. Rule:=Grammar.Rules['repeat_rule'];
  244. CheckNotNull(Rule, 'Expected rule "repeat_rule"');
  245. Expression := TEBNFExpression(Rule.Expression);
  246. Term := TEBNFTerm(Expression.Terms[0]);
  247. Factor := TEBNFFactor(Term.Factors[0]);
  248. CheckEquals(etFactorRepetition, Factor.NodeType, 'Factor should be a repetition group');
  249. CheckNotNull(Factor.InnerNode, 'Repetition group should have an inner node');
  250. CheckEquals(etExpression, Factor.InnerNode.NodeType, 'Inner node should be an expression');
  251. CheckEquals(1, TEBNFExpression(Factor.InnerNode).ChildCount, 'Inner expression should have 1 term');
  252. CheckEquals(1, TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[0]).ChildCount, 'Inner term should have 1 factor');
  253. CheckEquals(etFactorIdentifier, TEBNFFactor(TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[0]).Factors[0]).NodeType, 'Inner factor type');
  254. CheckEquals('identifier_to_repeat', TEBNFFactor(TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[0]).Factors[0]).Value, 'Inner factor value');
  255. end;
  256. procedure TTestEBNFParser.TestRuleWithParenthesizedGroup;
  257. var
  258. EBNFSource: string;
  259. Rule: TEBNFRule;
  260. Expression: TEBNFExpression;
  261. Term: TEBNFTerm;
  262. Factor: TEBNFFactor;
  263. begin
  264. EBNFSource := 'group_rule = ( "part_one" | "part_two" ) ;';
  265. Parser := TEBNFParser.Create(EBNFSource);
  266. Grammar := Parser.Parse;
  267. Rule:=Grammar.Rules['group_rule'];
  268. CheckNotNull(Rule, 'Expected rule "group_rule"');
  269. Expression := TEBNFExpression(Rule.Expression);
  270. Term := TEBNFTerm(Expression.Terms[0]);
  271. Factor := TEBNFFactor(Term.Factors[0]);
  272. CheckEquals(etFactorGroup, Factor.NodeType, 'Factor should be a parenthesized group');
  273. CheckNotNull(Factor.InnerNode, 'Group should have an inner node');
  274. CheckEquals(etExpression, Factor.InnerNode.NodeType, 'Inner node should be an expression');
  275. CheckEquals(2, TEBNFExpression(Factor.InnerNode).ChildCount, 'Inner expression should have 2 terms'); // Because of '|'
  276. CheckEquals(1, TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[0]).ChildCount, 'First inner term should have 1 factor');
  277. CheckEquals('part_one', TEBNFFactor(TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[0]).Factors[0]).Value, 'First inner factor value');
  278. CheckEquals(1, TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[1]).ChildCount, 'Second inner term should have 1 factor');
  279. CheckEquals('part_two', TEBNFFactor(TEBNFTerm(TEBNFExpression(Factor.InnerNode).Terms[1]).Factors[0]).Value, 'Second inner factor value');
  280. end;
  281. procedure TTestEBNFParser.TestRuleWithSpecialSequence;
  282. var
  283. EBNFSource: string;
  284. Rule: TEBNFRule;
  285. Expression: TEBNFExpression;
  286. Term: TEBNFTerm;
  287. Factor: TEBNFFactor;
  288. begin
  289. EBNFSource := 'special_rule = ? "this is a comment" ? ;';
  290. Parser := TEBNFParser.Create(EBNFSource);
  291. Grammar := Parser.Parse;
  292. Rule := Grammar.Rules['special_rule'];
  293. CheckNotNull(Rule, 'Expected rule "special_rule"');
  294. Expression := TEBNFExpression(Rule.Expression);
  295. Term := TEBNFTerm(Expression.Terms[0]);
  296. Factor := TEBNFFactor(Term.Factors[0]);
  297. CheckEquals(etFactorSpecialSequence, Factor.NodeType, 'Factor should be a special sequence');
  298. CheckEquals('this is a comment', Factor.Value, 'Special sequence value should match');
  299. end;
  300. procedure TTestEBNFParser.TestDuplicateRuleError;
  301. var
  302. EBNFSource: string;
  303. begin
  304. EBNFSource :=
  305. 'ruleA = "first" ;' + sLineBreak +
  306. 'ruleA = "second" ;'; // Duplicate rule definition
  307. Parser := TEBNFParser.Create(EBNFSource);
  308. try
  309. Parser.Parse;
  310. Fail('Expected an exception for duplicate rule');
  311. except
  312. on E: Exception do
  313. Check(Pos('Duplicate rule identifier: ruleA', E.Message) > 0, 'Expected "Duplicate rule identifier" error message');
  314. end;
  315. end;
  316. procedure TTestEBNFParser.TestMissingEqualsError;
  317. var
  318. EBNFSource: string;
  319. begin
  320. EBNFSource := 'rule_bad "literal" ;'; // Missing '='
  321. Parser := TEBNFParser.Create(EBNFSource);
  322. try
  323. Parser.Parse;
  324. Fail('Expected an exception for missing equals sign');
  325. except
  326. on E: Exception do
  327. Check(Pos('Expected ttEquals, but found ttStringLiteral', E.Message) > 0, 'Expected "Expected ttEquals" error message');
  328. end;
  329. end;
  330. procedure TTestEBNFParser.TestMissingSemicolonError;
  331. var
  332. EBNFSource: string;
  333. begin
  334. EBNFSource := 'rule_bad = "literal" '; // Missing ';'
  335. Parser := TEBNFParser.Create(EBNFSource);
  336. try
  337. Parser.Parse;
  338. Fail('Expected an exception for missing semicolon');
  339. except
  340. on E: Exception do
  341. Check(Pos('Expected ttSemicolon, but found ttEOF', E.Message) > 0, 'Expected "Expected ttSemicolon" error message');
  342. end;
  343. end;
  344. procedure TTestEBNFParser.TestUnexpectedTokenInFactorError;
  345. var
  346. EBNFSource: string;
  347. begin
  348. EBNFSource := 'rule_bad = = ;'; // '==' is not a valid factor
  349. Parser := TEBNFParser.Create(EBNFSource);
  350. try
  351. Parser.Parse;
  352. Fail('Expected an exception for unexpected token in factor');
  353. except
  354. on E: Exception do
  355. Check(Pos('Unexpected token for factor: ttEquals', E.Message) > 0, 'Expected "Unexpected token for factor" error message');
  356. end;
  357. end;
  358. initialization
  359. RegisterTest(TTestEBNFParser);
  360. end.