-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathLogCreatorLineFormatter.cs
More file actions
163 lines (135 loc) · 4.77 KB
/
LogCreatorLineFormatter.cs
File metadata and controls
163 lines (135 loc) · 4.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
using System;
using System.Collections.Generic;
using System.IO;
namespace Diz.LogWriter;
public class LogCreatorLineFormatter
{
public record ColumnFormat
{
public string Value { get; init; }
public int? LengthOverride { get; init; }
public bool IsLiteral { get; init; }
public bool IgnoreOffset { get; init; }
public int? SanitizeOffset(int offset)
{
return IgnoreOffset || offset == -1 ? null : offset;
}
}
public string FormatString { get; }
public List<ColumnFormat> ColumnFormats { get; private set; }
private readonly IReadOnlyDictionary<string, AssemblyPartialLineGenerator> generators;
public LogCreatorLineFormatter(string lineFormatStr, IReadOnlyDictionary<string, AssemblyPartialLineGenerator> generators)
{
this.generators = generators;
FormatString = lineFormatStr;
Parse();
}
public LogCreatorLineFormatter(string lineFormatStr) : this(lineFormatStr, AssemblyGeneratorRegistration.Create()) {}
// every line printed in a .asm file is done so by variable substitution according to a format string.
//
// example:
// in a format string like this:
// "%label:-22% %code:37%;%pc%|%bytes%|%ia%; %comment%";
//
// you might get output like this:
//
// CODE_808000: LDA.W Test_Data,X ;808000|BD5B80 |80805B;
//
// GetParameter() takes a ROM offset and the name of a "parameter" i.e. one of the labels in that format string
// like "label", "code", "pc", "bytes", etc. There are also special params that start with a % sign, like
// "%empty", "%map", "%bankcross" etc.
//
//
// It will look for a function in LogCreator tagged with an AssemblerHandler attribute that matches the
// parameter passed in.
private void Parse()
{
var output = new List<ColumnFormat>();
var split = FormatString.Split('%');
if (split.Length % 2 == 0)
throw new InvalidDataException("Format string has a non-even amount of % signs");
for (var i = 0; i < split.Length; i++)
{
var isLiteral = i % 2 == 0;
ParseOneItem(isLiteral, output, split[i]);
}
ColumnFormats = output;
}
private void ParseOneItem(bool isLiteral, ICollection<ColumnFormat> output, string token)
{
try
{
var newItem = isLiteral
? ParseStringLiteral(token)
: ParseFormatItem(token);
if (newItem != null)
output.Add(newItem);
}
catch (Exception ex)
{
ex.Data.Add("LineFormatterToken", token);
throw;
}
}
private ColumnFormat ParseFormatItem(string token)
{
var (itemValue, overrideLenStr) = ParseItemValue(token);
var lengthOverride = GetLengthOverride(overrideLenStr);
EnsureValidGeneratorExistsFor(itemValue);
return new ColumnFormat
{
Value = itemValue,
LengthOverride = lengthOverride,
};
}
private static (string val, string overrideLen) ParseItemValue(string token)
{
var indexOfColon = token.IndexOf(':');
// default, length comes from the attribute, generator not involved
// example: "%label%"
if (indexOfColon < 0)
return (token, null);
// override, length comes from the format string
// example: for token "%label:-22%", length would be "-22"
return (
token.Substring(0, indexOfColon),
token.Substring(indexOfColon + 1)
);
}
private static int? GetLengthOverride(string overrideLenStr)
{
if (overrideLenStr == null)
return null;
if (!int.TryParse(overrideLenStr, out var lenOverride))
throw new InvalidDataException($"Invalid length");
return lenOverride;
}
private void EnsureValidGeneratorExistsFor(string itemValue)
{
var validGenerator = generators.TryGetValue(itemValue, out _);
if (!validGenerator)
throw new InvalidDataException($"Can't find assembly generator");
}
private static ColumnFormat ParseStringLiteral(string token)
{
if (string.IsNullOrEmpty(token))
return null;
return new ColumnFormat
{
Value = token,
IsLiteral = true
};
}
public static bool Validate(string formatStr)
{
try
{
var unused = new LogCreatorLineFormatter(formatStr);
return true;
}
catch (Exception)
{
return false;
}
}
}