ZoneCodeGenerator: Make use of the parsed tokens in TestCount and TestCondition

This commit is contained in:
Jan 2019-11-10 18:04:00 +01:00
parent 2bcb776bbf
commit f2cc95ee32
15 changed files with 275 additions and 41 deletions

View File

@ -1,14 +1,25 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using ZoneCodeGenerator.Domain.Information;
using System.Threading.Tasks;
namespace ZoneCodeGenerator.Domain.Evaluation namespace ZoneCodeGenerator.Domain.Evaluation
{ {
class OperandDynamic : IEvaluation class OperandDynamic : IEvaluation
{ {
public StructureInformation Structure { get; }
public IList<MemberInformation> ReferencedMemberChain { get; }
public IList<int> ArrayIndices { get; }
public bool IsStatic => false; public bool IsStatic => false;
public OperandDynamic(StructureInformation structure, IEnumerable<MemberInformation> memberChain)
{
Structure = structure;
ReferencedMemberChain = new List<MemberInformation>(memberChain);
ArrayIndices = new List<int>();
}
public int EvaluateNumeric() public int EvaluateNumeric()
{ {
throw new Exception("A dynamic operand cannot be evaluated."); throw new Exception("A dynamic operand cannot be evaluated.");
@ -16,7 +27,7 @@ namespace ZoneCodeGenerator.Domain.Evaluation
public override string ToString() public override string ToString()
{ {
return "dynamic"; return $"{Structure.Type.FullName}::{string.Join("::", ReferencedMemberChain.Select(information => information.Member.Name))}{string.Concat(ArrayIndices.Select(i => $"[{i}]"))}";
} }
} }
} }

View File

@ -1,16 +1,20 @@
namespace ZoneCodeGenerator.Domain.StructureInformation using ZoneCodeGenerator.Domain.Evaluation;
namespace ZoneCodeGenerator.Domain.Information
{ {
class MemberInformation class MemberInformation
{ {
public StructureInformation StructureType { get; } public StructureInformation StructureType { get; }
public Variable Member { get; set; } public Variable Member { get; set; }
public bool IsScriptString { get; set; } public bool IsScriptString { get; set; }
public IEvaluation Condition { get; set; }
public MemberInformation(Variable member, StructureInformation structureType) public MemberInformation(Variable member, StructureInformation structureType)
{ {
Member = member; Member = member;
StructureType = structureType; StructureType = structureType;
IsScriptString = false; IsScriptString = false;
Condition = null;
} }
} }
} }

View File

@ -4,7 +4,7 @@ using System.Linq;
using ZoneCodeGenerator.Domain.FastFileStructure; using ZoneCodeGenerator.Domain.FastFileStructure;
using ZoneCodeGenerator.Persistence; using ZoneCodeGenerator.Persistence;
namespace ZoneCodeGenerator.Domain.StructureInformation namespace ZoneCodeGenerator.Domain.Information
{ {
class StructureInformation class StructureInformation
{ {

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using ZoneCodeGenerator.Domain.StructureInformation; using ZoneCodeGenerator.Domain.Information;
using ZoneCodeGenerator.Interface; using ZoneCodeGenerator.Interface;
using ZoneCodeGenerator.Persistence; using ZoneCodeGenerator.Persistence;

View File

@ -3,7 +3,7 @@ using System.IO;
using System.Linq; using System.Linq;
using ZoneCodeGenerator.Domain; using ZoneCodeGenerator.Domain;
using ZoneCodeGenerator.Domain.FastFileStructure; using ZoneCodeGenerator.Domain.FastFileStructure;
using ZoneCodeGenerator.Domain.StructureInformation; using ZoneCodeGenerator.Domain.Information;
using ZoneCodeGenerator.Interface; using ZoneCodeGenerator.Interface;
using ZoneCodeGenerator.Persistence; using ZoneCodeGenerator.Persistence;

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using ZoneCodeGenerator.Domain; using ZoneCodeGenerator.Domain;
using ZoneCodeGenerator.Domain.FastFileStructure; using ZoneCodeGenerator.Domain.FastFileStructure;
using ZoneCodeGenerator.Domain.Information;
using ZoneCodeGenerator.Persistence; using ZoneCodeGenerator.Persistence;
namespace ZoneCodeGenerator.Parsing.CommandFile namespace ZoneCodeGenerator.Parsing.CommandFile
@ -10,6 +11,10 @@ namespace ZoneCodeGenerator.Parsing.CommandFile
string Game { get; set; } string Game { get; set; }
IReadOnlyDataRepository Repository { get; } IReadOnlyDataRepository Repository { get; }
List<FastFileBlock> FastFileBlocks { get; } List<FastFileBlock> FastFileBlocks { get; }
DataTypeWithMembers DataTypeInUse { get; set; } StructureInformation DataTypeInUse { get; set; }
bool GetMembersFromParts(string[] parts, StructureInformation baseType, out List<MemberInformation> members);
bool GetTypenameAndMembersFromParts(string[] parts, out StructureInformation typeInformation,
out List<MemberInformation> members);
} }
} }

View File

@ -1,6 +1,9 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq;
using ZoneCodeGenerator.Domain; using ZoneCodeGenerator.Domain;
using ZoneCodeGenerator.Domain.FastFileStructure; using ZoneCodeGenerator.Domain.FastFileStructure;
using ZoneCodeGenerator.Domain.Information;
using ZoneCodeGenerator.Interface; using ZoneCodeGenerator.Interface;
using ZoneCodeGenerator.Parsing.CommandFile.Tests; using ZoneCodeGenerator.Parsing.CommandFile.Tests;
using ZoneCodeGenerator.Parsing.Testing; using ZoneCodeGenerator.Parsing.Testing;
@ -25,7 +28,7 @@ namespace ZoneCodeGenerator.Parsing.CommandFile.Impl
public string Game { get; set; } public string Game { get; set; }
public IReadOnlyDataRepository Repository { get; } public IReadOnlyDataRepository Repository { get; }
public List<FastFileBlock> FastFileBlocks { get; } public List<FastFileBlock> FastFileBlocks { get; }
public DataTypeWithMembers DataTypeInUse { get; set; } public StructureInformation DataTypeInUse { get; set; }
public CommandParserState(IReadOnlyDataRepository repository) public CommandParserState(IReadOnlyDataRepository repository)
{ {
@ -50,5 +53,76 @@ namespace ZoneCodeGenerator.Parsing.CommandFile.Impl
return true; return true;
} }
private bool FindTypenameParts(string[] parts, out DataTypeWithMembers dataTypeWithMembers, out int typeNamePartCount)
{
typeNamePartCount = 1;
while (typeNamePartCount <= parts.Length)
{
var currentTypeName = string.Join("::", parts, 0, typeNamePartCount);
var foundDataType = Repository.GetDataTypeByName(currentTypeName);
if (foundDataType != null)
{
if (!(foundDataType is DataTypeWithMembers foundDataTypeWithMembers))
{
throw new TestFailedException($"Referenced type '{currentTypeName}' needs to be a data type with members to be used in an evaluation.");
}
dataTypeWithMembers = foundDataTypeWithMembers;
return true;
}
typeNamePartCount++;
}
dataTypeWithMembers = null;
return false;
}
public bool GetMembersFromParts(string[] parts, StructureInformation baseType, out List<MemberInformation> members)
{
members = new List<MemberInformation>();
var currentStructure = baseType;
foreach (var part in parts)
{
var member = currentStructure.OrderedMembers.FirstOrDefault(information =>
information.Member.Name.Equals(part));
if (member == null)
{
members = null;
return false;
}
members.Add(member);
currentStructure = member.StructureType;
}
return true;
}
public bool GetTypenameAndMembersFromParts(string[] parts, out StructureInformation typeInformation,
out List<MemberInformation> members)
{
if (!FindTypenameParts(parts, out var type, out var typeNamePartCount))
{
typeInformation = null;
members = null;
return false;
}
typeInformation = Repository.GetInformationFor(type);
if (typeInformation == null)
{
members = null;
return false;
}
var memberParts = new string[parts.Length - typeNamePartCount];
Array.Copy(parts, typeNamePartCount, memberParts, 0, memberParts.Length);
return GetMembersFromParts(memberParts, typeInformation, out members);
}
} }
} }

View File

@ -1,12 +1,19 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using ZoneCodeGenerator.Domain;
using ZoneCodeGenerator.Domain.Evaluation; using ZoneCodeGenerator.Domain.Evaluation;
using ZoneCodeGenerator.Domain.Information;
using ZoneCodeGenerator.Parsing.Matching; using ZoneCodeGenerator.Parsing.Matching;
using ZoneCodeGenerator.Parsing.Matching.Matchers; using ZoneCodeGenerator.Parsing.Matching.Matchers;
using ZoneCodeGenerator.Parsing.Testing;
namespace ZoneCodeGenerator.Parsing.CommandFile.Tests namespace ZoneCodeGenerator.Parsing.CommandFile.Tests
{ {
class TestCondition : TestWithEvaluation class TestCondition : TestWithEvaluation
{ {
private StructureInformation referencedType;
private const string TagAlways = "always"; private const string TagAlways = "always";
private const string TagNever = "never"; private const string TagNever = "never";
private const string TypeNameToken = "typeName"; private const string TypeNameToken = "typeName";
@ -27,11 +34,33 @@ namespace ZoneCodeGenerator.Parsing.CommandFile.Tests
public TestCondition() : base(matchers) public TestCondition() : base(matchers)
{ {
} }
protected override void ProcessMatch(ICommandParserState state) protected override void ProcessMatch(ICommandParserState state)
{ {
var typeName = NextMatch(TypeNameToken);
var typeNameParts = typeName.Split(new[] {"::"}, StringSplitOptions.None);
if (state.DataTypeInUse != null
&& state.GetMembersFromParts(typeNameParts, state.DataTypeInUse, out var typeMembers))
{
referencedType = state.DataTypeInUse;
}
else if (state.GetTypenameAndMembersFromParts(typeNameParts, out referencedType, out typeMembers))
{
// Do nothing
}
else
{
throw new TestFailedException($"Could not find type/members '{typeName}'");
}
if (typeMembers == null
|| !typeMembers.Any())
{
throw new TestFailedException("Can only set conditions for members and not for types.");
}
IEvaluation evaluation; IEvaluation evaluation;
switch (NextTag()) switch (NextTag())
{ {
@ -51,7 +80,19 @@ namespace ZoneCodeGenerator.Parsing.CommandFile.Tests
throw new Exception("Unexpected Tag in TestCondition"); throw new Exception("Unexpected Tag in TestCondition");
} }
var referencedMember = typeMembers.Last();
referencedMember.Condition = evaluation;
}
protected override IEnumerable<StructureInformation> GetUsedTypes(ICommandParserState state)
{
if (state.DataTypeInUse != null
&& state.DataTypeInUse != referencedType)
{
return new[] {state.DataTypeInUse, referencedType};
}
return new[] {referencedType};
} }
} }
} }

View File

@ -1,11 +1,17 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using ZoneCodeGenerator.Domain;
using ZoneCodeGenerator.Domain.Information;
using ZoneCodeGenerator.Parsing.Matching; using ZoneCodeGenerator.Parsing.Matching;
using ZoneCodeGenerator.Parsing.Matching.Matchers; using ZoneCodeGenerator.Parsing.Matching.Matchers;
using ZoneCodeGenerator.Parsing.Testing;
namespace ZoneCodeGenerator.Parsing.CommandFile.Tests namespace ZoneCodeGenerator.Parsing.CommandFile.Tests
{ {
class TestCount : TestWithEvaluation class TestCount : TestWithEvaluation
{ {
private StructureInformation referencedType;
private const string TypeNameToken = "typeName"; private const string TypeNameToken = "typeName";
// set count <typename> <calculationStatement>; // set count <typename> <calculationStatement>;
@ -25,12 +31,53 @@ namespace ZoneCodeGenerator.Parsing.CommandFile.Tests
protected override void ProcessMatch(ICommandParserState state) protected override void ProcessMatch(ICommandParserState state)
{ {
if (!NextTag().Equals(TagEvaluation)) var typeName = NextMatch(TypeNameToken);
var typeNameParts = typeName.Split(new[] { "::" }, StringSplitOptions.None);
if (state.DataTypeInUse != null
&& state.GetMembersFromParts(typeNameParts, state.DataTypeInUse, out var typeMembers))
{ {
throw new Exception("Expected first count tag to be a calculation statement"); referencedType = state.DataTypeInUse;
}
else if (state.GetTypenameAndMembersFromParts(typeNameParts, out referencedType, out typeMembers))
{
// Do nothing
}
else
{
throw new TestFailedException($"Could not find type/members '{typeName}'");
} }
if (typeMembers == null
|| !typeMembers.Any())
{
throw new TestFailedException("Can only set count for members and not for types.");
}
if (!NextTag().Equals(TagEvaluation))
throw new Exception("Expected first count tag to be a calculation statement");
var evaluation = ProcessEvaluation(state); var evaluation = ProcessEvaluation(state);
var referencedMember = typeMembers.Last();
var reference = referencedMember.Member.VariableType.References.OfType<ReferenceTypePointer>()
.LastOrDefault();
if (reference != null)
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<StructureInformation> GetUsedTypes(ICommandParserState state)
{
if (state.DataTypeInUse != null
&& state.DataTypeInUse != referencedType)
{
return new[] { state.DataTypeInUse, referencedType };
}
return new[] { referencedType };
} }
} }
} }

View File

@ -1,4 +1,5 @@
using ZoneCodeGenerator.Domain; using System;
using ZoneCodeGenerator.Domain;
using ZoneCodeGenerator.Parsing.Matching; using ZoneCodeGenerator.Parsing.Matching;
using ZoneCodeGenerator.Parsing.Matching.Matchers; using ZoneCodeGenerator.Parsing.Matching.Matchers;
using ZoneCodeGenerator.Parsing.Testing; using ZoneCodeGenerator.Parsing.Testing;
@ -35,7 +36,14 @@ namespace ZoneCodeGenerator.Parsing.CommandFile.Tests
throw new TestFailedException($"To use data type '{typeName}' it must either be a struct or a union."); throw new TestFailedException($"To use data type '{typeName}' it must either be a struct or a union.");
} }
state.DataTypeInUse = dataTypeWithMembersToUse; var dataTypeToUseInformation = state.Repository.GetInformationFor(dataTypeWithMembersToUse);
if (dataTypeToUseInformation == null)
{
throw new Exception($"Could not find information for type '{dataTypeWithMembersToUse.FullName}'");
}
state.DataTypeInUse = dataTypeToUseInformation;
} }
} }
} }

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using ZoneCodeGenerator.Domain.Evaluation; using ZoneCodeGenerator.Domain.Evaluation;
using ZoneCodeGenerator.Domain.Information;
using ZoneCodeGenerator.Parsing.Matching; using ZoneCodeGenerator.Parsing.Matching;
using ZoneCodeGenerator.Parsing.Matching.Matchers; using ZoneCodeGenerator.Parsing.Matching.Matchers;
using ZoneCodeGenerator.Parsing.Testing; using ZoneCodeGenerator.Parsing.Testing;
@ -72,32 +73,70 @@ namespace ZoneCodeGenerator.Parsing.CommandFile.Tests
AddTaggedMatcher(evaluation); AddTaggedMatcher(evaluation);
} }
private IEvaluation ProcessOperandNumber(ICommandParserState state)
{
var numberString = NextMatch(TokenOperandNumber);
return new OperandStatic(int.Parse(numberString));
}
protected abstract IEnumerable<StructureInformation> GetUsedTypes(ICommandParserState state);
private IEvaluation ProcessOperandTypename(ICommandParserState state)
{
var typenameString = NextMatch(TokenOperandTypename);
var arrayIndexStrings = new List<string>();
while (PeekTag().Equals(TagOperandArray))
{
NextTag();
arrayIndexStrings.Add(NextMatch(TokenOperandArray));
}
var nameParts = typenameString.Split(new[] { "::" }, StringSplitOptions.None);
List<MemberInformation> referencedMemberChain = null;
var referencedType = GetUsedTypes(state)
.FirstOrDefault(usedType => state.GetMembersFromParts(nameParts, usedType, out referencedMemberChain));
if (referencedType == null)
{
if (!state.GetTypenameAndMembersFromParts(nameParts, out referencedType,
out referencedMemberChain))
{
throw new TestFailedException($"Could not evaluate '{typenameString}'.");
}
}
if (!referencedMemberChain.Any())
{
throw new TestFailedException($"Typename '{typenameString}' needs to reference a member at this place.");
}
var operandDynamic = new OperandDynamic(referencedType, referencedMemberChain);
foreach (var arrayIndexString in arrayIndexStrings)
{
operandDynamic.ArrayIndices.Add(int.Parse(arrayIndexString));
}
return operandDynamic;
}
private IEvaluation ProcessOperand(ICommandParserState state) private IEvaluation ProcessOperand(ICommandParserState state)
{ {
var operandTypeTag = NextTag(); var operandTypeTag = NextTag();
if (operandTypeTag.Equals(TagOperandNumber)) if (operandTypeTag.Equals(TagOperandNumber))
{ {
var numberString = NextMatch(TokenOperandNumber); return ProcessOperandNumber(state);
return new OperandStatic(int.Parse(numberString));
} }
else if(operandTypeTag.Equals(TagOperandTypename))
if(operandTypeTag.Equals(TagOperandTypename))
{ {
var typenameString = NextMatch(TokenOperandTypename); return ProcessOperandTypename(state);
var arrayIndexStrings = new List<string>();
while (PeekTag().Equals(TagOperandArray))
{
NextTag();
arrayIndexStrings.Add(NextMatch(TokenOperandArray));
}
return new OperandDynamic();
}
else
{
throw new Exception("Unknown Operand Type");
} }
throw new Exception("Unknown Operand Type");
} }
private OperationType ProcessOperationType(ICommandParserState state) private OperationType ProcessOperationType(ICommandParserState state)

View File

@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using ZoneCodeGenerator.Domain; using ZoneCodeGenerator.Domain;
using ZoneCodeGenerator.Domain.FastFileStructure; using ZoneCodeGenerator.Domain.FastFileStructure;
using ZoneCodeGenerator.Domain.StructureInformation; using ZoneCodeGenerator.Domain.Information;
namespace ZoneCodeGenerator.Persistence namespace ZoneCodeGenerator.Persistence
{ {

View File

@ -2,7 +2,7 @@
using System.Linq; using System.Linq;
using ZoneCodeGenerator.Domain; using ZoneCodeGenerator.Domain;
using ZoneCodeGenerator.Domain.FastFileStructure; using ZoneCodeGenerator.Domain.FastFileStructure;
using ZoneCodeGenerator.Domain.StructureInformation; using ZoneCodeGenerator.Domain.Information;
namespace ZoneCodeGenerator.Persistence namespace ZoneCodeGenerator.Persistence
{ {

View File

@ -4,7 +4,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq; using Moq;
using ZoneCodeGenerator; using ZoneCodeGenerator;
using ZoneCodeGenerator.Domain; using ZoneCodeGenerator.Domain;
using ZoneCodeGenerator.Domain.StructureInformation; using ZoneCodeGenerator.Domain.Information;
using ZoneCodeGenerator.Parsing; using ZoneCodeGenerator.Parsing;
using ZoneCodeGenerator.Parsing.CommandFile; using ZoneCodeGenerator.Parsing.CommandFile;
using ZoneCodeGenerator.Parsing.CommandFile.Tests; using ZoneCodeGenerator.Parsing.CommandFile.Tests;

View File

@ -4,6 +4,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq; using Moq;
using ZoneCodeGenerator.Domain; using ZoneCodeGenerator.Domain;
using ZoneCodeGenerator.Domain.FastFileStructure; using ZoneCodeGenerator.Domain.FastFileStructure;
using ZoneCodeGenerator.Domain.Information;
using ZoneCodeGenerator.Parsing; using ZoneCodeGenerator.Parsing;
using ZoneCodeGenerator.Parsing.CommandFile; using ZoneCodeGenerator.Parsing.CommandFile;
using ZoneCodeGenerator.Parsing.CommandFile.Tests; using ZoneCodeGenerator.Parsing.CommandFile.Tests;
@ -18,7 +19,7 @@ namespace ZoneCodeGeneratorTests.Parsing.CommandFile.Tests
private Mock<IReadOnlyDataRepository> repositoryMock; private Mock<IReadOnlyDataRepository> repositoryMock;
private Mock<ICommandParserState> parserStateMock; private Mock<ICommandParserState> parserStateMock;
private DataTypeWithMembers dataTypeWithMembers; private StructureInformation usedType;
private Mock<ILexer> lexerMock; private Mock<ILexer> lexerMock;
private int tokenOffset; private int tokenOffset;
@ -29,7 +30,7 @@ namespace ZoneCodeGeneratorTests.Parsing.CommandFile.Tests
{ {
parserStateMock = new Mock<ICommandParserState>(); parserStateMock = new Mock<ICommandParserState>();
dataTypeWithMembers = null; usedType = null;
tokenOffset = 0; tokenOffset = 0;
tokens = new List<string>(); tokens = new List<string>();
@ -39,6 +40,9 @@ namespace ZoneCodeGeneratorTests.Parsing.CommandFile.Tests
parserStateMock.SetupGet(state => state.Repository) parserStateMock.SetupGet(state => state.Repository)
.Returns(() => repositoryMock.Object); .Returns(() => repositoryMock.Object);
repositoryMock.Setup(repository => repository.GetInformationFor(It.IsAny<DataTypeWithMembers>()))
.Returns((DataTypeWithMembers type) => new StructureInformation(type));
lexerMock.Setup(lexer => lexer.PeekToken(It.IsAny<int>())) lexerMock.Setup(lexer => lexer.PeekToken(It.IsAny<int>()))
.Returns((int index) => tokens.ElementAtOrDefault(index + tokenOffset)); .Returns((int index) => tokens.ElementAtOrDefault(index + tokenOffset));
lexerMock.Setup(lexer => lexer.NextToken()) lexerMock.Setup(lexer => lexer.NextToken())
@ -47,9 +51,9 @@ namespace ZoneCodeGeneratorTests.Parsing.CommandFile.Tests
.Callback((int count) => tokenOffset += count); .Callback((int count) => tokenOffset += count);
parserStateMock.SetupGet(state => state.DataTypeInUse) parserStateMock.SetupGet(state => state.DataTypeInUse)
.Returns(() => dataTypeWithMembers); .Returns(() => usedType);
parserStateMock.SetupSet(state => state.DataTypeInUse = It.IsAny<DataTypeWithMembers>()) parserStateMock.SetupSet(state => state.DataTypeInUse = It.IsAny<StructureInformation>())
.Callback((DataTypeWithMembers type) => dataTypeWithMembers = type); .Callback((StructureInformation type) => usedType = type);
} }
[TestMethod] [TestMethod]
@ -70,7 +74,8 @@ namespace ZoneCodeGeneratorTests.Parsing.CommandFile.Tests
Assert.AreEqual(TokenTestResult.Match, test.PerformTest(parserStateMock.Object, lexerMock.Object)); Assert.AreEqual(TokenTestResult.Match, test.PerformTest(parserStateMock.Object, lexerMock.Object));
Assert.AreEqual(6, test.ConsumedTokenCount); Assert.AreEqual(6, test.ConsumedTokenCount);
Assert.AreEqual(assetTypeToUse, dataTypeWithMembers); Assert.IsNotNull(assetTypeToUse);
Assert.AreEqual(usedType.Type, assetTypeToUse);
} }
[TestMethod] [TestMethod]