Skip to content

Commit 0e7f09a

Browse files
author
Steve Ives
committed
Merge branch 'master' of https://github.com/Synergex/CodeGen
2 parents e95fa14 + 636614a commit 0e7f09a

File tree

5 files changed

+272
-4
lines changed

5 files changed

+272
-4
lines changed

CodeGenEngine/LoopExpander.dbl

Lines changed: 267 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ namespace CodeGen.Engine
6262

6363
loopProcessors.Add("FIELD_LOOP", processFieldLoop)
6464
loopProcessors.Add("KEY_LOOP", processKeyLoop)
65+
loopProcessors.Add("KEY_LOOP_UNIQUE", processKeyLoopUnique)
6566
loopProcessors.Add("ALTERNATE_KEY_LOOP", processAlternateKeyLoop)
67+
loopProcessors.Add("ALTERNATE_KEY_LOOP_UNIQUE", processAlternateKeyLoopUnique)
6668
loopProcessors.Add("FOREIGN_KEY_LOOP", processForeignKeyLoop)
6769
loopProcessors.Add("PRIMARY_KEY", processPrimaryKeyLoop)
6870
loopProcessors.Add("UNIQUE_KEY", processUniqueKeyLoop)
@@ -186,6 +188,138 @@ namespace CodeGen.Engine
186188

187189
endmethod
188190

191+
private static method processKeyLoopUnique, void
192+
required in node,@LoopNode
193+
required in template,@FileNode
194+
required in loops,@IEnumerable<LoopNode>
195+
required in expander,@ITreeNodeVisitor
196+
proc
197+
;;This is like a key loop, except that it will exclude any keys that
198+
;;use the exact same segments as a key that has already been processed.
199+
200+
data loop = ^as(node, KeyLoopNode)
201+
data context = template.Context
202+
203+
if (template.Context.CurrentStructure.Keys.Count == 0)
204+
throw new ApplicationException(String.Format("The <{0}> loop at line {1} in template {2} can't be processed because structure {3} has no keys!", node.OpenToken.Value, node.OpenToken.StartLineNumber, template.Context.CurrentTemplateBaseName, template.Context.CurrentStructure.Name))
205+
206+
data ix, int
207+
208+
loop.MaxIndex = template.Context.CurrentStructure.Keys.Count<RpsKey>(lambda(keyItem) { keyItem.KeyType == RpsKeyType.Access }) -1
209+
210+
loop.FirstUniqueKeyNumber = -1
211+
for ix from 0 thru template.Context.CurrentStructure.Keys.Count - 1
212+
begin
213+
;;Skip any Foreign keys
214+
if (template.Context.CurrentStructure.Keys[ix].KeyType == RpsKeyType.Foreign)
215+
nextloop
216+
;;Look for the first unique key
217+
if (template.Context.CurrentStructure.Keys[ix].Duplicates == RpsKeyDuplicates.NoDuplicates)
218+
begin
219+
loop.FirstUniqueKeyNumber = ix
220+
exitloop
221+
end
222+
end
223+
224+
context.CurrentTask.DebugLog(String.Format(" - {0,-30} -> {1} keys", string.Format("<{0}>", loop.OpenToken.Value), loop.MaxIndex + 1))
225+
226+
data prevKeysSegments = new List<RpsKeySegmentCollection>()
227+
data otherKeySegments, @RpsKeySegmentCollection
228+
229+
;;Iterate through all the keys in the structure
230+
for ix from 0 thru template.Context.CurrentStructure.Keys.Count - 1
231+
begin
232+
;; Skip any foreign keys
233+
if (template.Context.CurrentStructure.Keys[ix].KeyType == RpsKeyType.Foreign)
234+
nextloop
235+
236+
;;If we get here we have an access key. Assume it's a duplicate and we'll look for a difference
237+
data segmentsMatchAnotherKey = false
238+
239+
;;If we havent processed any other keys yet then we can't have a duplicate, so this one gets included
240+
if (prevKeysSegments.Count > 0)
241+
begin
242+
;;We have processed other keys, so make sure the segments of this key don't match any we have already processed
243+
data segmentsMatchThisKey, boolean
244+
data thisKeySegments, @RpsKeySegmentCollection, template.Context.CurrentStructure.Keys[ix].Segments
245+
246+
data iy, int
247+
for iy from 0 thru prevKeysSegments.Count - 1
248+
begin
249+
;;Assume we have a duplicate, look for a difference
250+
segmentsMatchThisKey = true
251+
otherKeySegments = prevKeysSegments[iy]
252+
253+
;;Same number of segments?
254+
if (thisKeySegments.Count != otherKeySegments.Count) then
255+
begin
256+
;;Different segment count, not a duplicate
257+
segmentsMatchThisKey = false
258+
end
259+
else
260+
begin
261+
;;Same segment count, look at each segment
262+
data thisKeySeg, @RpsKeySegment
263+
data otherKeySeg, @RpsKeySegment
264+
265+
data iz, int
266+
data allSegmentsMatch = true
267+
268+
;;Iterate through the segments looking for a diff
269+
for iz from 0 thru thisKeySegments.Count - 1
270+
begin
271+
thisKeySeg = thisKeySegments[iz]
272+
otherKeySeg = otherKeySegments[iz]
273+
274+
using thisKeySeg.SegmentType select
275+
(RpsKeySegmentType.Field),
276+
begin
277+
if ((otherKeySeg.SegmentType != RpsKeySegmentType.Field) || (!otherKeySeg.Field.Equals(thisKeySeg.Field)))
278+
begin
279+
allSegmentsMatch = false
280+
end
281+
end
282+
(RpsKeySegmentType.Literal),
283+
begin
284+
if ((otherKeySeg.SegmentType != RpsKeySegmentType.Literal) || (!otherKeySeg.LiteralValue.Equals(thisKeySeg.LiteralValue)))
285+
begin
286+
allSegmentsMatch = false
287+
end
288+
end
289+
(RpsKeySegmentType.External),
290+
begin
291+
throw new ApplicationException(String.Format("The <{0}> loop at line {1} in template {2} can't be processed because an unsupported key segment type of 'External' was encountered!", node.OpenToken.Value, node.OpenToken.StartLineNumber, template.Context.CurrentTemplateBaseName))
292+
end
293+
(RpsKeySegmentType.RecordNumber),
294+
begin
295+
throw new ApplicationException(String.Format("The <{0}> loop at line {1} in template {2} can't be processed because an unsupported key segment type of 'Record Number' was encountered!", node.OpenToken.Value, node.OpenToken.StartLineNumber, template.Context.CurrentTemplateBaseName))
296+
end
297+
endusing
298+
end
299+
300+
if (allSegmentsMatch)
301+
begin
302+
segmentsMatchAnotherKey = true
303+
exitloop
304+
end
305+
end
306+
end
307+
end
308+
309+
if (!segmentsMatchAnotherKey)
310+
begin
311+
;; Then process as normal
312+
loop.CurrentKey = template.Context.CurrentStructure.Keys[ix]
313+
loop.CurrentIndex = ix
314+
315+
expander.Visit(node.Body)
316+
317+
prevKeysSegments.Add(loop.CurrentKey.Segments)
318+
end
319+
end
320+
321+
endmethod
322+
189323
private static method processAlternateKeyLoop, void
190324
required in node,@LoopNode
191325
required in template,@FileNode
@@ -220,7 +354,7 @@ namespace CodeGen.Engine
220354

221355
for ix from 0 thru template.Context.CurrentStructure.Keys.Count - 1
222356
begin
223-
;; Skip the first key and any alternate keys
357+
;; Skip the first key and any foreign keys
224358
if ((ix == 0) || (template.Context.CurrentStructure.Keys[ix].KeyType == RpsKeyType.Foreign))
225359
nextloop
226360

@@ -233,6 +367,138 @@ namespace CodeGen.Engine
233367

234368
endmethod
235369

370+
private static method processAlternateKeyLoopUnique, void
371+
required in node,@LoopNode
372+
required in template,@FileNode
373+
required in loops,@IEnumerable<LoopNode>
374+
required in expander,@ITreeNodeVisitor
375+
proc
376+
;;This is like an alternate key loop, except that it will exclude any keys that
377+
;;use the exact same segments as a key that has already been processed.
378+
379+
data loop = ^as(node, KeyLoopNode)
380+
data context = template.Context
381+
382+
if (template.Context.CurrentStructure.Keys.Count == 0)
383+
throw new ApplicationException(String.Format("The <{0}> loop at line {1} in template {2} can't be processed because structure {3} has no keys!", node.OpenToken.Value, node.OpenToken.StartLineNumber, template.Context.CurrentTemplateBaseName, template.Context.CurrentStructure.Name))
384+
385+
data ix, int
386+
387+
loop.MaxIndex = template.Context.CurrentStructure.Keys.Count<RpsKey>(lambda(keyItem) { keyItem.KeyType == RpsKeyType.Access }) -1
388+
389+
loop.FirstUniqueKeyNumber = -1
390+
for ix from 0 thru template.Context.CurrentStructure.Keys.Count - 1
391+
begin
392+
;;Skip any Foreign keys
393+
if (template.Context.CurrentStructure.Keys[ix].KeyType == RpsKeyType.Foreign)
394+
nextloop
395+
;;Look for the first unique key
396+
if (template.Context.CurrentStructure.Keys[ix].Duplicates == RpsKeyDuplicates.NoDuplicates)
397+
begin
398+
loop.FirstUniqueKeyNumber = ix
399+
exitloop
400+
end
401+
end
402+
403+
context.CurrentTask.DebugLog(String.Format(" - {0,-30} -> {1} keys", string.Format("<{0}>", loop.OpenToken.Value), loop.MaxIndex + 1))
404+
405+
data prevKeysSegments = new List<RpsKeySegmentCollection>()
406+
data otherKeySegments, @RpsKeySegmentCollection
407+
408+
;;Iterate through all the keys in the structure
409+
for ix from 0 thru template.Context.CurrentStructure.Keys.Count - 1
410+
begin
411+
;; Skip the first key and any foreign keys
412+
if ((ix == 0) || (template.Context.CurrentStructure.Keys[ix].KeyType == RpsKeyType.Foreign))
413+
nextloop
414+
415+
;;If we get here we have an alternate access key. Assume it's a duplicate and we'll look for a difference
416+
data segmentsMatchAnotherKey = false
417+
418+
;;If we havent processed any other keys yet then we can't have a duplicate, so this one gets included
419+
if (prevKeysSegments.Count > 0)
420+
begin
421+
;;We have processed other keys, so make sure the segments of this key don't match any we have already processed
422+
data segmentsMatchThisKey, boolean
423+
data thisKeySegments, @RpsKeySegmentCollection, template.Context.CurrentStructure.Keys[ix].Segments
424+
425+
data iy, int
426+
for iy from 0 thru prevKeysSegments.Count - 1
427+
begin
428+
;;Assume we have a duplicate, look for a difference
429+
segmentsMatchThisKey = true
430+
otherKeySegments = prevKeysSegments[iy]
431+
432+
;;Same number of segments?
433+
if (thisKeySegments.Count != otherKeySegments.Count) then
434+
begin
435+
;;Different segment count, not a duplicate
436+
segmentsMatchThisKey = false
437+
end
438+
else
439+
begin
440+
;;Same segment count, look at each segment
441+
data thisKeySeg, @RpsKeySegment
442+
data otherKeySeg, @RpsKeySegment
443+
444+
data iz, int
445+
data allSegmentsMatch = true
446+
447+
;;Iterate through the segments looking for a diff
448+
for iz from 0 thru thisKeySegments.Count - 1
449+
begin
450+
thisKeySeg = thisKeySegments[iz]
451+
otherKeySeg = otherKeySegments[iz]
452+
453+
using thisKeySeg.SegmentType select
454+
(RpsKeySegmentType.Field),
455+
begin
456+
if ((otherKeySeg.SegmentType != RpsKeySegmentType.Field) || (!otherKeySeg.Field.Equals(thisKeySeg.Field)))
457+
begin
458+
allSegmentsMatch = false
459+
end
460+
end
461+
(RpsKeySegmentType.Literal),
462+
begin
463+
if ((otherKeySeg.SegmentType != RpsKeySegmentType.Literal) || (!otherKeySeg.LiteralValue.Equals(thisKeySeg.LiteralValue)))
464+
begin
465+
allSegmentsMatch = false
466+
end
467+
end
468+
(RpsKeySegmentType.External),
469+
begin
470+
throw new ApplicationException(String.Format("The <{0}> loop at line {1} in template {2} can't be processed because an unsupported key segment type of 'External' was encountered!", node.OpenToken.Value, node.OpenToken.StartLineNumber, template.Context.CurrentTemplateBaseName))
471+
end
472+
(RpsKeySegmentType.RecordNumber),
473+
begin
474+
throw new ApplicationException(String.Format("The <{0}> loop at line {1} in template {2} can't be processed because an unsupported key segment type of 'Record Number' was encountered!", node.OpenToken.Value, node.OpenToken.StartLineNumber, template.Context.CurrentTemplateBaseName))
475+
end
476+
endusing
477+
end
478+
479+
if (allSegmentsMatch)
480+
begin
481+
segmentsMatchAnotherKey = true
482+
exitloop
483+
end
484+
end
485+
end
486+
end
487+
488+
if (!segmentsMatchAnotherKey)
489+
begin
490+
;; Then process as normal
491+
loop.CurrentKey = template.Context.CurrentStructure.Keys[ix]
492+
loop.CurrentIndex = ix
493+
494+
expander.Visit(node.Body)
495+
496+
prevKeysSegments.Add(loop.CurrentKey.Segments)
497+
end
498+
end
499+
500+
endmethod
501+
236502
private static method processForeignKeyLoop, void
237503
required in node,@LoopNode
238504
required in template,@FileNode

CodeGenEngine/Parser.dbl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ namespace CodeGen.Engine
524524
("SELECTION_LOOP"),
525525
loop = new SelectionLoopNode() {OpenToken = tokens[startIndex], Body = new List<ITreeNode>()}
526526

527-
("KEY_LOOP", "ALTERNATE_KEY_LOOP", "FOREIGN_KEY_LOOP", "PRIMARY_KEY", "UNIQUE_KEY"),
527+
("KEY_LOOP", "KEY_LOOP_UNIQUE", "ALTERNATE_KEY_LOOP", "ALTERNATE_KEY_LOOP_UNIQUE", "FOREIGN_KEY_LOOP", "PRIMARY_KEY", "UNIQUE_KEY"),
528528
loop = new KeyLoopNode() {OpenToken = tokens[startIndex], Body = new List<ITreeNode>()}
529529

530530
("SEGMENT_LOOP", "SEGMENT_LOOP_FILTER", "FIRST_SEGMENT", "SECOND_SEGMENT"),

CodeGenEngine/Tokenizer.dbl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,9 @@ namespace CodeGen.Engine
164164
&
165165
& { new TokenMeta() {Name = "FIELD_LOOP", TypeOfToken = TokenType.Loop, IsPaired = true, Validity = TokenValidity.NotInLoop | TokenValidity.StructureLoop | TokenValidity.ParameterLoop, RequiresRepository = true} },
166166
& { new TokenMeta() {Name = "KEY_LOOP", TypeOfToken = TokenType.Loop, IsPaired = true, Validity = TokenValidity.NotInLoop | TokenValidity.StructureLoop, RequiresRepository = true} },
167+
& { new TokenMeta() {Name = "KEY_LOOP_UNIQUE", TypeOfToken = TokenType.Loop, IsPaired = true, Validity = TokenValidity.NotInLoop | TokenValidity.StructureLoop, RequiresRepository = true} },
167168
& { new TokenMeta() {Name = "ALTERNATE_KEY_LOOP", TypeOfToken = TokenType.Loop, IsPaired = true, Validity = TokenValidity.NotInLoop | TokenValidity.StructureLoop, RequiresRepository = true} },
169+
& { new TokenMeta() {Name = "ALTERNATE_KEY_LOOP_UNIQUE", TypeOfToken = TokenType.Loop, IsPaired = true, Validity = TokenValidity.NotInLoop | TokenValidity.StructureLoop, RequiresRepository = true} },
168170
& { new TokenMeta() {Name = "FOREIGN_KEY_LOOP", TypeOfToken = TokenType.Loop, IsPaired = true, Validity = TokenValidity.NotInLoop | TokenValidity.StructureLoop, RequiresRepository = true} },
169171
& { new TokenMeta() {Name = "PRIMARY_KEY", TypeOfToken = TokenType.Loop, IsPaired = true, Validity = TokenValidity.NotInLoop | TokenValidity.StructureLoop | TokenValidity.FieldLoop | TokenValidity.RelationLoop, RequiresRepository = true} },
170172
& { new TokenMeta() {Name = "UNIQUE_KEY", TypeOfToken = TokenType.Loop, IsPaired = true, Validity = TokenValidity.NotInLoop | TokenValidity.StructureLoop, RequiresRepository = true} },

CurrentRelease.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<feed xmlns="http://www.w3.org/2005/Atom">
3-
<title type="text">5.5.2.0</title>
3+
<title type="text">5.5.3.0</title>
44
</feed>

HarmonyCoreExtensions/CustomRelationSpec.dbl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ namespace HarmonyCoreExtensions
7272
;;; <summary>
7373
;;; If data is present in the FromKey, is a match to a record REQUIRED in "ToStructure"
7474
;;; </summary>
75-
public readwrite property RequiresMatch, boolean, true
75+
public readwrite property RequiresMatch, boolean
7676

7777
;;; <summary>
7878
;;; The encoded lookup for the back relation if one is present

0 commit comments

Comments
 (0)