diff --git a/src/ZoneCodeGenerator/Domain/ReferenceTypePointer.cs b/src/ZoneCodeGenerator/Domain/ReferenceTypePointer.cs index 2cb9d6de..b9d59abc 100644 --- a/src/ZoneCodeGenerator/Domain/ReferenceTypePointer.cs +++ b/src/ZoneCodeGenerator/Domain/ReferenceTypePointer.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using ZoneCodeGenerator.Domain.Evaluation; namespace ZoneCodeGenerator.Domain @@ -8,7 +10,20 @@ namespace ZoneCodeGenerator.Domain public static IEvaluation DefaultCount = new OperandStatic(1); public IEvaluation Count { get; set; } = DefaultCount; + public IEvaluation[] CountByArrayIndex { get; set; } + public bool HasCountByArrayIndex => CountByArrayIndex != null; - public bool IsArray => !Count.IsStatic || Count.EvaluateNumeric() > 1; + private static bool EvaluationIsArray(IEvaluation eval) + { + return !eval.IsStatic || eval.EvaluateNumeric() > 1; + } + + public bool IsArray(int index) + { + return EvaluationIsArray(HasCountByArrayIndex ? CountByArrayIndex[index] : Count); + } + + public bool AnyIsArray => + HasCountByArrayIndex ? CountByArrayIndex.Any(EvaluationIsArray) : EvaluationIsArray(Count); } -} +} \ No newline at end of file diff --git a/src/ZoneCodeGenerator/Generating/Computations/MemberComputations.cs b/src/ZoneCodeGenerator/Generating/Computations/MemberComputations.cs index 9faddbd1..e2507ad4 100644 --- a/src/ZoneCodeGenerator/Generating/Computations/MemberComputations.cs +++ b/src/ZoneCodeGenerator/Generating/Computations/MemberComputations.cs @@ -20,12 +20,12 @@ namespace ZoneCodeGenerator.Generating.Computations public bool ContainsSinglePointerReference => information.Member.VariableType.References.Any() && information.Member.VariableType.References.Last() is ReferenceTypePointer pointerReference - && !pointerReference.IsArray; + && !pointerReference.AnyIsArray; public bool ContainsArrayPointerReference => information.Member.VariableType.References.Any() && information.Member.VariableType.References.Last() is ReferenceTypePointer pointerReference - && pointerReference.IsArray; + && pointerReference.AnyIsArray; public bool ContainsPointerArrayReference => ContainsSinglePointerReference && (IsArray && PointerDepthIsOne || !IsArray && PointerDepthIsTwo); @@ -73,11 +73,16 @@ namespace ZoneCodeGenerator.Generating.Computations public bool IsNotDefaultNormalBlock => information.Block != null && !(information.Block.IsNormal && information.Block.IsDefault); + public bool IsTempBlock => information.Block != null && information.Block.IsTemp; + public bool IsRuntimeBlock => information.Block != null && information.Block.IsRuntime; + public bool IsFirstMember => - information.Parent.OrderedMembers.FirstOrDefault(member => !member.IsLeaf && !member.Computations.ShouldIgnore) == information; + information.Parent.OrderedMembers.FirstOrDefault(member => + !member.IsLeaf && !member.Computations.ShouldIgnore) == information; public bool IsLastMember => - information.Parent.OrderedMembers.LastOrDefault(member => !member.IsLeaf && !member.Computations.ShouldIgnore) == information; + information.Parent.OrderedMembers.LastOrDefault(member => + !member.IsLeaf && !member.Computations.ShouldIgnore) == information; public MemberReferenceComputations References => new MemberReferenceComputations(information); diff --git a/src/ZoneCodeGenerator/Generating/Computations/MemberReferenceComputations.cs b/src/ZoneCodeGenerator/Generating/Computations/MemberReferenceComputations.cs index 3f0a28c6..de9bf38f 100644 --- a/src/ZoneCodeGenerator/Generating/Computations/MemberReferenceComputations.cs +++ b/src/ZoneCodeGenerator/Generating/Computations/MemberReferenceComputations.cs @@ -10,6 +10,7 @@ namespace ZoneCodeGenerator.Generating.Computations { private readonly MemberInformation information; private readonly List referenceIndices; + private readonly int combinedIndex; public ReferenceType Reference => referenceIndices.Count < information.Member.VariableType.References.Count ? information.Member.VariableType.References[referenceIndices.Count] @@ -32,21 +33,29 @@ namespace ZoneCodeGenerator.Generating.Computations .Select(i => new MemberReferenceComputations(information, referenceIndices.Concat(new[] {i}))); public bool IsSinglePointer => Reference is ReferenceTypePointer referenceTypePointer - && !referenceTypePointer.IsArray + && !referenceTypePointer.IsArray(combinedIndex) && !FollowingReferences.OfType().Any(); public bool IsArrayPointer => Reference is ReferenceTypePointer referenceTypePointer - && referenceTypePointer.IsArray + && referenceTypePointer.IsArray(combinedIndex) && !FollowingReferences.OfType().Any(); - public IEvaluation ArrayPointerCountEvaluation => Reference is ReferenceTypePointer referenceTypePointer - ? referenceTypePointer.Count - : null; + public IEvaluation ArrayPointerCountEvaluation + { + get + { + if (!(Reference is ReferenceTypePointer pointer)) + return null; + + return pointer.HasCountByArrayIndex ? pointer.CountByArrayIndex[combinedIndex] : pointer.Count; + } + } public bool IsPointerArray => - (Reference is ReferenceTypePointer referenceTypePointer && referenceTypePointer.IsArray || + (Reference is ReferenceTypePointer referenceTypePointer && referenceTypePointer.IsArray(combinedIndex) || Reference is ReferenceTypeArray) - && NextReference is ReferenceTypePointer nextReferencePointer && !nextReferencePointer.IsArray; + && NextReference is ReferenceTypePointer nextReferencePointer && + !nextReferencePointer.IsArray(combinedIndex); public IEvaluation PointerArrayCountEvaluation => NextReference is ReferenceTypePointer referenceTypePointer ? referenceTypePointer.Count @@ -56,12 +65,29 @@ namespace ZoneCodeGenerator.Generating.Computations { this.information = information; referenceIndices = new List(); + combinedIndex = 0; } private MemberReferenceComputations(MemberInformation information, IEnumerable referenceIndices) { this.information = information; this.referenceIndices = new List(referenceIndices); + + var arraySizes = information.Member.VariableType.References + .OfType() + .Select(array => array.ArraySize) + .ToList(); + var indexDepth = 0; + combinedIndex = 0; + foreach (var referenceIndex in this.referenceIndices) + { + var sizePerIndexInCurrentDepth = arraySizes.Count <= indexDepth + 1 + ? 1 + : arraySizes.Skip(indexDepth + 1).Aggregate((i1, i2) => i1 * i2); + + combinedIndex += referenceIndex * sizePerIndexInCurrentDepth; + indexDepth++; + } } } } \ No newline at end of file diff --git a/src/ZoneCodeGenerator/Generating/Templates/Loading/ArrayPointer.stg b/src/ZoneCodeGenerator/Generating/Templates/Loading/ArrayPointer.stg index 3575d1d1..6a04218c 100644 --- a/src/ZoneCodeGenerator/Generating/Templates/Loading/ArrayPointer.stg +++ b/src/ZoneCodeGenerator/Generating/Templates/Loading/ArrayPointer.stg @@ -7,7 +7,7 @@ $TypeVarName(structure.Type)$->$member.Member.Name$$PrintArrayIndices(reference) // $member.Member.VariableType.Type.Alignment$ $\n$ -$if(member.StructureType && !member.StructureType.IsLeaf)$ +$if(member.StructureType && !member.StructureType.IsLeaf && !member.Computations.IsRuntimeBlock)$ $TypeVarName(member.Member.VariableType.Type)$ = $TypeVarName(structure.Type)$->$member.Member.Name$$PrintArrayIndices(reference)$;$\n$ LoadArray_$member.Member.VariableType.Type.Name$(true, $PrintEvaluation(reference.ArrayPointerCountEvaluation)$); diff --git a/src/ZoneCodeGenerator/Generating/Templates/ZoneLoad.stg b/src/ZoneCodeGenerator/Generating/Templates/ZoneLoad.stg index f43ea7fa..56945c81 100644 --- a/src/ZoneCodeGenerator/Generating/Templates/ZoneLoad.stg +++ b/src/ZoneCodeGenerator/Generating/Templates/ZoneLoad.stg @@ -73,8 +73,11 @@ namespace $context.Game$ } >> -LoadMember(context, member) ::= << -Loading member $member.Member.Name$ +LoadMemberReference_Array(context, structure, member, reference) ::= << +$first(reference.ArrayEntries):{entry | $LoadMemberReference(context, structure, member, entry)$}$ +$rest(reference.ArrayEntries):{entry | + +$LoadMemberReference(context, structure, member, entry)$}$ >> LoadMemberReference(context, structure, member, reference) ::= <% @@ -88,8 +91,10 @@ $elseif(reference.IsArray && !reference.NextReference)$ $LoadEmbeddedArray(context, structure, member, reference)$ $elseif(!reference.Reference)$ $LoadEmbedded(context, structure, member, reference)$ +$elseif(reference.IsArray)$ +$LoadMemberReference_Array(context, structure, member, reference)$ $else$ -// $member.Member.Name$ +// LoadMemberReference failed @ $member.Member.Name$ $endif$ %> diff --git a/src/ZoneCodeGenerator/Parsing/CommandFile/Tests/TestCount.cs b/src/ZoneCodeGenerator/Parsing/CommandFile/Tests/TestCount.cs index 9f3a1887..f67f6798 100644 --- a/src/ZoneCodeGenerator/Parsing/CommandFile/Tests/TestCount.cs +++ b/src/ZoneCodeGenerator/Parsing/CommandFile/Tests/TestCount.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using ZoneCodeGenerator.Domain; +using ZoneCodeGenerator.Domain.Evaluation; using ZoneCodeGenerator.Domain.Information; using ZoneCodeGenerator.Parsing.Matching; using ZoneCodeGenerator.Parsing.Matching.Matchers; @@ -14,27 +15,65 @@ namespace ZoneCodeGenerator.Parsing.CommandFile.Tests private StructureInformation referencedType; private const string TokenTypeName = "typeName"; private const string TokenPointerResolve = "pointerResolve"; + private const string TokenArrayIndex = "arrayIndex"; // set count ; private static readonly TokenMatcher[] matchers = { new MatcherLiteral("set"), new MatcherLiteral("count"), - new MatcherGroupLoop(MatcherGroupLoop.LoopMode.ZeroOneMultiple, new MatcherLiteral("*").WithName(TokenPointerResolve)), + new MatcherGroupLoop(MatcherGroupLoop.LoopMode.ZeroOneMultiple, + new MatcherLiteral("*").WithName(TokenPointerResolve)), new MatcherTypename().WithName(TokenTypeName), + new MatcherGroupLoop(MatcherGroupLoop.LoopMode.ZeroOneMultiple, + new MatcherArray().WithName(TokenArrayIndex)), new MatcherWithTag(TagEvaluation), new MatcherLiteral(";") }; public TestCount() : base(matchers) { + } + private void SetCountByArrayIndex(MemberInformation referencedMember, ReferenceTypePointer pointer, + IEvaluation evaluation) + { + if (pointer.CountByArrayIndex == null) + { + pointer.CountByArrayIndex = new IEvaluation[referencedMember.Member.VariableType.References + .OfType().Select(array => array.ArraySize).Aggregate((i1, i2) => i1 * i2)]; + } + + var arraySizes = referencedMember.Member.VariableType.References + .OfType() + .Select(array => array.ArraySize) + .ToList(); + var index = 0; + var indexDepth = 0; + string s; + while ((s = NextMatch(TokenArrayIndex)) != null) + { + var specifiedIndex = int.Parse(s); + + if (specifiedIndex >= arraySizes[indexDepth]) + { + throw new TestFailedException($"Specified index '{specifiedIndex}' is out of bounds at type '{referencedMember.Parent.Type.FullName}::{referencedMember.Member.Name}'"); + } + + var sizePerIndexInCurrentDepth = arraySizes.Count <= indexDepth + 1 ? + 1 : arraySizes.Skip(indexDepth + 1).Aggregate((i1, i2) => i1 * i2); + + index += specifiedIndex * sizePerIndexInCurrentDepth; + indexDepth++; + } + + pointer.CountByArrayIndex[index] = evaluation; } protected override void ProcessMatch(ICommandParserState state) { var typeName = NextMatch(TokenTypeName); - var typeNameParts = typeName.Split(new[] { "::" }, StringSplitOptions.None); + var typeNameParts = typeName.Split(new[] {"::"}, StringSplitOptions.None); if (state.DataTypeInUse != null && state.GetMembersFromParts(typeNameParts, state.DataTypeInUse, out var typeMembers)) { @@ -67,10 +106,21 @@ namespace ZoneCodeGenerator.Parsing.CommandFile.Tests .FirstOrDefault(); if (reference != null) - reference.Count = evaluation; + { + if (HasMatcherTokens(TokenArrayIndex)) + { + SetCountByArrayIndex(referencedMember, reference, evaluation); + } + else + { + reference.Count = evaluation; + } + } else + { throw new TestFailedException( $"Member '{referencedMember.Member.Name}' of type '{referencedMember.Member.VariableType.Type.FullName}' cannot have its count set due to it not having a pointer reference"); + } } protected override IEnumerable GetUsedTypes(ICommandParserState state) @@ -78,10 +128,10 @@ namespace ZoneCodeGenerator.Parsing.CommandFile.Tests if (state.DataTypeInUse != null && state.DataTypeInUse != referencedType) { - return new[] { state.DataTypeInUse, referencedType }; + return new[] {state.DataTypeInUse, referencedType}; } - return new[] { referencedType }; + return new[] {referencedType}; } } } \ No newline at end of file