Skip to content

Commit fef1ca5

Browse files
authored
Merge branch 'master' into features/mame
2 parents fe4f874 + 2eae385 commit fef1ca5

File tree

10 files changed

+247
-49
lines changed

10 files changed

+247
-49
lines changed

.github/workflows/build-zxbstudio.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ jobs:
2929

3030
- name: Build project
3131
run: dotnet build ZXBasicStudio.sln --configuration Release --no-restore
32+
33+
- name: Run tests
34+
run: dotnet test
3235

3336
- name: Publish for Linux
3437
run: |

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,4 +360,7 @@ MigrationBackup/
360360
.ionide/
361361

362362
# Fody - auto-generated XML schema
363-
FodyWeavers.xsd
363+
FodyWeavers.xsd
364+
365+
# tmp/ dir
366+
tmp/

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,26 @@ Documentation will come in the near future, take a look at the book [Boriel Basi
1111

1212
Have fun!
1313

14+
## Projects in this solution
15+
16+
This solution contains several projects that work together to provide the ZX Basic Studio experience:
17+
18+
- **ZXBStudio**: The main cross-platform Integrated Development Environment (IDE) built with [Avalonia](https://avaloniaui.net/).
19+
- **CoreSpectrum**: The core ZX Spectrum emulation library, providing hardware simulation.
20+
- **Z80dotNet**: A Z80 processor simulator that serves as the heart of the emulator.
21+
- **HeadlessEmulator**: A command-line (CLI) version of the emulator for running Spectrum programs without a GUI.
22+
- **Bufdio**: An audio handling library using FFmpeg for sound and tape I/O support.
23+
- **Common**: Shared logic and UI components used across the different projects.
24+
- **ZXBSInstaller**: A cross-platform installer for the IDE, built with Avalonia.
25+
- **ZXBasicStudioSite**: The source for the ZX Basic Studio website, built with Blazor WebAssembly.
26+
- **RomReconstructor** & **Rom128Reconstructor**: Specialized tools for reconstructing ZX Spectrum 48K and 128K ROMs.
27+
- **MsBox.Avalonia**: A customized message box library for the Avalonia user interface.
28+
- **ZXBasicStudioTest** & **TestZ80**: Unit tests and testing tools for ensuring the stability and correctness of the IDE and emulator.
29+
1430
## ZX Basic Studio Team
15-
- Development team:
31+
32+
### Development team:
33+
1634
- El Dr. Gusman
1735
- Boriel
1836
- Duefectu

ZXBStudio/BuildSystem/ZXBasicMap.cs

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public ZXBasicMap(ZXCodeFile MainFile, IEnumerable<ZXCodeFile> AllFiles, string
179179
ParseInputParameters(funcMatch.Groups[5].Value, currentFunction.InputParameters);
180180

181181
if (funcMatch.Groups[7].Success)
182-
currentFunction.ReturnType = StorageFromString(funcMatch.Groups[5].Value, currentFunction.Name);
182+
currentFunction.ReturnType = StorageFromString(funcMatch.Groups[7].Value, currentFunction.Name);
183183
else
184184
currentFunction.ReturnType = ZXVariableStorage.F;
185185

@@ -202,8 +202,7 @@ public ZXBasicMap(ZXCodeFile MainFile, IEnumerable<ZXCodeFile> AllFiles, string
202202
if (varNameDef.Contains("(")) //array
203203
{
204204
string varName = varNameDef.Substring(0, varNameDef.IndexOf("(")).Trim();
205-
206-
if (!jointLines.Skip(buc + 1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_]){varName}($|[^a-zA-Z0-9_])", RegexOptions.Multiline)))
205+
if (!jointLines.Skip(buc + 1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_$]){Regex.Escape(varName)}($|[^a-zA-Z0-9_$])", RegexOptions.Multiline)))
207206
continue;
208207

209208
string[] dims = varNameDef.Substring(varNameDef.IndexOf("(") + 1).Replace(")", "").Split(",", StringSplitOptions.RemoveEmptyEntries);
@@ -219,8 +218,7 @@ public ZXBasicMap(ZXCodeFile MainFile, IEnumerable<ZXCodeFile> AllFiles, string
219218
foreach (var vName in varNames)
220219
{
221220
string varName = vName.Trim();
222-
223-
if (!jointLines.Skip(buc + 1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_]){varName}($|[^a-zA-Z0-9_])", RegexOptions.Multiline)))
221+
if (!jointLines.Skip(buc + 1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_$]){Regex.Escape(varName)}($|[^a-zA-Z0-9_$])", RegexOptions.Multiline)))
224222
continue;
225223

226224
var storage = StorageFromString(dimMatch.Groups[5].Value, varName);
@@ -291,15 +289,15 @@ public ZXBasicMap(ZXCodeFile MainFile, IEnumerable<ZXCodeFile> AllFiles, string
291289
//Search for the var in the sub/function that the location points to
292290
if (location.LocationType == ZXBasicLocationType.Sub)
293291
{
294-
var sub = subs.Where(s => s.Name == location.Name).FirstOrDefault();
292+
var sub = subs.FirstOrDefault(s => string.Equals(s.Name, location.Name, StringComparison.OrdinalIgnoreCase));
295293
if(sub != null)
296-
foundVar = sub.LocalVariables.Where(v => v.Name == varName).FirstOrDefault();
294+
foundVar = sub.LocalVariables.FirstOrDefault(v => string.Equals(v.Name, varName, StringComparison.OrdinalIgnoreCase));
297295
}
298296
else
299297
{
300-
var func = functions.Where(f => f.Name == location.Name).FirstOrDefault();
298+
var func = functions.FirstOrDefault(f => string.Equals(f.Name, location.Name, StringComparison.OrdinalIgnoreCase));
301299
if (func != null)
302-
foundVar = func.LocalVariables.Where(v => v.Name == varName).FirstOrDefault();
300+
foundVar = func.LocalVariables.FirstOrDefault(v => string.Equals(v.Name, varName, StringComparison.OrdinalIgnoreCase));
303301
}
304302
}
305303

@@ -322,15 +320,15 @@ public ZXBasicMap(ZXCodeFile MainFile, IEnumerable<ZXCodeFile> AllFiles, string
322320
//(to avoid the very unprobable case where the same var is defined in different files in locations that match the same range)
323321
if (possibleLocation.LocationType == ZXBasicLocationType.Sub)
324322
{
325-
var sub = subs.Where(s => s.Name == possibleLocation.Name).FirstOrDefault();
323+
var sub = subs.FirstOrDefault(s => string.Equals(s.Name, possibleLocation.Name, StringComparison.OrdinalIgnoreCase));
326324
if (sub != null)
327-
foundVar = sub.LocalVariables.Where(v => v.Name == varName && !v.Unused).FirstOrDefault();
325+
foundVar = sub.LocalVariables.FirstOrDefault(v => string.Equals(v.Name, varName, StringComparison.OrdinalIgnoreCase) && !v.Unused);
328326
}
329327
else
330328
{
331-
var func = functions.Where(f => f.Name == possibleLocation.Name).FirstOrDefault();
329+
var func = functions.FirstOrDefault(f => string.Equals(f.Name, possibleLocation.Name, StringComparison.OrdinalIgnoreCase));
332330
if (func != null)
333-
foundVar = func.LocalVariables.Where(v => v.Name == varName && !v.Unused).FirstOrDefault();
331+
foundVar = func.LocalVariables.FirstOrDefault(v => string.Equals(v.Name, varName, StringComparison.OrdinalIgnoreCase) && !v.Unused);
334332
}
335333

336334
//If the criteria finds a var, return it
@@ -359,11 +357,7 @@ void GetSubVars(ZXBasicSub Sub, string[] Lines)
359357
if (varNameDef.Contains("(")) //array
360358
{
361359
string varName = varNameDef.Substring(0, varNameDef.IndexOf("(")).Trim();
362-
363-
//Ignore unused vars (vars that are found only on its dim line, there may be the improbable
364-
//case where a var is defined and used in the same line using a colon and not used
365-
//anywhere else, but that would be an awful code :) )
366-
if (!Lines.Skip(buc+1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_]){varName}($|[^a-zA-Z0-9_])", RegexOptions.Multiline)))
360+
if (!Lines.Skip(buc+1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_$]){Regex.Escape(varName)}($|[^a-zA-Z0-9_$])", RegexOptions.Multiline)))
367361
continue;
368362

369363
string[] dims = varNameDef.Substring(varNameDef.IndexOf("(") + 1).Replace(")", "").Split(",", StringSplitOptions.RemoveEmptyEntries);
@@ -379,9 +373,7 @@ void GetSubVars(ZXBasicSub Sub, string[] Lines)
379373
foreach (var vName in varNames)
380374
{
381375
string varName = vName.Trim();
382-
383-
//Ignore unused vars
384-
if (!Lines.Skip(buc+1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_]){varName}($|[^a-zA-Z0-9_])", RegexOptions.Multiline)))
376+
if (!Lines.Skip(buc+1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_$]){Regex.Escape(varName)}($|[^a-zA-Z0-9_$])", RegexOptions.Multiline)))
385377
continue;
386378

387379
var storage = StorageFromString(dimMatch.Groups[5].Value, varName);
@@ -415,15 +407,15 @@ public List<ZXBasicLocation> GetBuildLocations(ZXCodeFile CodeFile)
415407

416408
if (subMatch != null && subMatch.Success)
417409
{
418-
loc = new ZXBasicLocation { Name = subMatch.Groups[2].Value.Trim(), LocationType = ZXBasicLocationType.Sub, FirstLine = buc, File = Path.Combine(CodeFile.Directory, CodeFile.TempFileName) };
410+
loc = new ZXBasicLocation { Name = subMatch.Groups[4].Value.Trim(), LocationType = ZXBasicLocationType.Sub, FirstLine = buc, File = Path.Combine(CodeFile.Directory, CodeFile.TempFileName) };
419411
continue;
420412
}
421413

422414
var funcMatch = regFunc.Match(line);
423415

424416
if (funcMatch != null && funcMatch.Success)
425417
{
426-
loc = new ZXBasicLocation { Name = funcMatch.Groups[2].Value.Trim(), LocationType = ZXBasicLocationType.Function, FirstLine = buc, File = Path.Combine(CodeFile.Directory, CodeFile.TempFileName) };
418+
loc = new ZXBasicLocation { Name = funcMatch.Groups[4].Value.Trim(), LocationType = ZXBasicLocationType.Function, FirstLine = buc, File = Path.Combine(CodeFile.Directory, CodeFile.TempFileName) };
427419
continue;
428420
}
429421
}
@@ -465,7 +457,7 @@ public bool ContainsBuildDim(ZXCodeFile CodeFile, string VarName, int LineNumber
465457
if (LineNumber >= lines.Length)
466458
return false;
467459

468-
return Regex.IsMatch(lines[LineNumber], $"(\\s|,){VarName}(\\s|,|\\(|$)", RegexOptions.Multiline);
460+
return Regex.IsMatch(lines[LineNumber], $"(\\s|,){Regex.Escape(VarName)}(\\s|,|\\(|$)", RegexOptions.Multiline);
469461
}
470462

471463
private static void ParseInputParameters(string ParameterString, List<ZXBasicParameter> Storage)

ZXBStudio/BuildSystem/ZXVariableMap.cs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public ZXVariableMap(string ICFile, string MapFile, ZXBasicMap BasicMap)
4242
}
4343

4444

45-
private void ProcessGlobalVariables(string icContent, string mapContent, ZXBasicMap BasicMap)
45+
internal void ProcessGlobalVariables(string icContent, string mapContent, ZXBasicMap BasicMap)
4646
{
4747
int splitIndex = icContent.IndexOf("--- end of user code ---");
4848

@@ -60,7 +60,7 @@ private void ProcessGlobalVariables(string icContent, string mapContent, ZXBasic
6060
string varName = m.Groups[1].Value;
6161
string bVarName = varName.Substring(1);
6262

63-
var basicVar = BasicMap.GlobalVariables.FirstOrDefault(v => v.Name == bVarName);
63+
var basicVar = BasicMap.GlobalVariables.FirstOrDefault(v => string.Equals(v.Name.TrimEnd('$'), bVarName.TrimEnd('$'), StringComparison.OrdinalIgnoreCase));
6464

6565
if (basicVar == null)
6666
continue;
@@ -78,7 +78,7 @@ private void ProcessGlobalVariables(string icContent, string mapContent, ZXBasic
7878

7979
ZXVariable newVar = new ZXVariable
8080
{
81-
Name = bVarName,
81+
Name = basicVar.Name,
8282
Address = new ZXVariableAddress { AddressType = ZXVariableAddressType.Absolute, AddressValue = addr },
8383
Scope = ZXVariableScope.GlobalScope,
8484
VariableType = ZXVariableType.Flat,
@@ -98,7 +98,7 @@ private void ProcessGlobalVariables(string icContent, string mapContent, ZXBasic
9898

9999
string bVarName = varName.Substring(1);
100100

101-
var basicVar = BasicMap.GlobalVariables.FirstOrDefault(v => v.Name == bVarName);
101+
var basicVar = BasicMap.GlobalVariables.FirstOrDefault(v => string.Equals(v.Name.TrimEnd('$'), bVarName.TrimEnd('$'), StringComparison.OrdinalIgnoreCase));
102102

103103
if (basicVar == null)
104104
continue;
@@ -133,7 +133,7 @@ private void ProcessGlobalVariables(string icContent, string mapContent, ZXBasic
133133

134134
ZXVariable newVar = new ZXVariable
135135
{
136-
Name = bVarName,
136+
Name = basicVar.Name,
137137
Address = new ZXVariableAddress { AddressType = ZXVariableAddressType.Absolute, AddressValue = addr },
138138
Scope = ZXVariableScope.GlobalScope,
139139
VariableType = ZXVariableType.Array,
@@ -144,7 +144,7 @@ private void ProcessGlobalVariables(string icContent, string mapContent, ZXBasic
144144
}
145145
}
146146

147-
private void ProcessLocalVariables(string icContent, string mapContent, ZXBasicMap BasicMap)
147+
internal void ProcessLocalVariables(string icContent, string mapContent, ZXBasicMap BasicMap)
148148
{
149149
int splitIndex = icContent.IndexOf("--- end of user code ---");
150150

@@ -179,10 +179,9 @@ private void ProcessLocalVariables(string icContent, string mapContent, ZXBasicM
179179

180180
ZXVariableScope currentScope = new ZXVariableScope { ScopeName = locName, StartAddress = startAddr, EndAddress = endAddr };
181181

182-
ZXBasicSub? sub = BasicMap.Subs.Where(m => m.Name == locName).FirstOrDefault();
183-
182+
ZXBasicSub? sub = BasicMap.Subs.FirstOrDefault(m => string.Equals(m.Name.TrimEnd('$'), locName.TrimEnd('$'), StringComparison.OrdinalIgnoreCase));
184183
if (sub == null)
185-
sub = BasicMap.Functions.Where(m => m.Name == locName).FirstOrDefault();
184+
sub = BasicMap.Functions.FirstOrDefault(m => string.Equals(m.Name.TrimEnd('$'), locName.TrimEnd('$'), StringComparison.OrdinalIgnoreCase));
186185

187186
//Function params
188187
if (sub != null)

ZXBStudio/ZXBasicStudio.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
<Deterministic>False</Deterministic>
1616
<Version>1.8.0.2</Version>
1717
</PropertyGroup>
18+
19+
<ItemGroup>
20+
<InternalsVisibleTo Include="ZXBasicStudioTest" />
21+
</ItemGroup>
1822
<ItemGroup>
1923
<AvaloniaXaml Remove="LanguageDefinitions\**" />
2024
<Compile Remove="LanguageDefinitions\**" />

0 commit comments

Comments
 (0)