mirror of
https://github.com/AppleCommander/AppleCommander.git
synced 2026-10-24 23:02:27 +00:00
Restructured and (hopefully) have all pathways configured.
This commit is contained in:
1 parent
827d61163d
commit
029bea8ffb
1 file changed
+52
-38
+52
-38
@@ -23,6 +23,7 @@ import com.webcodepro.applecommander.storage.FileEntry;
|
||||
import com.webcodepro.applecommander.storage.FileFilter;
|
||||
import com.webcodepro.applecommander.storage.FormattedDisk;
|
||||
import com.webcodepro.applecommander.util.filestreamer.FileStreamer;
|
||||
import com.webcodepro.applecommander.util.filestreamer.FileTuple;
|
||||
import io.github.applecommander.acx.base.ReusableCommandOptions;
|
||||
import io.github.applecommander.acx.converter.DiskConverter;
|
||||
import org.applecommander.bastools.api.Configuration;
|
||||
@@ -35,13 +36,16 @@ import picocli.CommandLine.*;
|
||||
import picocli.CommandLine.Model.CommandSpec;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Perform "checking" of a program that was printed in a magazine.
|
||||
@@ -49,6 +53,8 @@ import java.util.function.Function;
|
||||
*/
|
||||
@Command(name = "proof", description = "Proof-read/calculate program checksums (as printed in magazines) on file.")
|
||||
public abstract class AbstractProofCommand extends ReusableCommandOptions {
|
||||
private static final Logger LOG = Logger.getLogger(AbstractProofCommand.class.getName());
|
||||
|
||||
// This is different from the base options as the disk entry is optional.
|
||||
@Option(names = { "-d", "--disk" }, description = "Image to process [$ACX_DISK_NAME].",
|
||||
converter = DiskConverter.class, defaultValue = "${ACX_DISK_NAME}")
|
||||
@@ -65,54 +71,27 @@ public abstract class AbstractProofCommand extends ReusableCommandOptions {
|
||||
private String programName;
|
||||
|
||||
public int handle(Function<Configuration,Object> proofReaderFn) throws Exception {
|
||||
final List<String> basicTypes = List.of("B", "BAS");
|
||||
File sourceFile = null;
|
||||
int startAddress = 0x801;
|
||||
byte[] tokenizedProgram = null;
|
||||
if (disks != null && !disks.isEmpty()) {
|
||||
// The current BT API only supports physical files, so we need to transmogrify any program on disk to a temp file.
|
||||
Optional<FileEntry> opt = FileStreamer.forDisks(disks)
|
||||
.matchGlobs(programName)
|
||||
.stream()
|
||||
.filter(tuple -> tuple.isFile() && basicTypes.contains(tuple.fileEntry.getFiletype()))
|
||||
.map(tuple -> tuple.fileEntry)
|
||||
.findFirst();
|
||||
FileEntry fileEntry = opt.orElseThrow(() -> {
|
||||
var msg = String.format("Program '%s' not found on disk.", programName);
|
||||
return new FileNotFoundException(msg);
|
||||
});
|
||||
startAddress = fileEntry.getAddress();
|
||||
tokenizedProgram = fileEntry.getFileData();
|
||||
// Generate source text - we may need it depending on the selected proofer/checker.
|
||||
FileFilter filter = fileEntry.getSuggestedFilter();
|
||||
String code = new String(filter.filter(fileEntry));
|
||||
sourceFile = File.createTempFile("proof-", ".bas");
|
||||
sourceFile.deleteOnExit();
|
||||
// TODO file creation
|
||||
}
|
||||
else {
|
||||
sourceFile = new File(programName);
|
||||
}
|
||||
|
||||
// Note that the "sourceFile" is used for printing.
|
||||
Configuration.Builder builder = Configuration.builder()
|
||||
.sourceFile(new File(programName));
|
||||
if (debugFlag) builder.debugStream(System.out);
|
||||
|
||||
Queue<Token> tokens = ModernTokenReader.tokenize(sourceFile);
|
||||
Parser parser = new Parser(tokens);
|
||||
Program program = parser.parse();
|
||||
// Build is configured differently depending on if we find it in a disk image or as source.
|
||||
Optional<FileEntry> optFileEntry = findFileEntry();
|
||||
optFileEntry.ifPresent(fileEntry -> {
|
||||
builder.startAddress(fileEntry.getAddress());
|
||||
});
|
||||
|
||||
Object checker = proofReaderFn.apply(builder.build());
|
||||
switch (checker) {
|
||||
case ApplesoftInputBufferProofReader inputBufferProofReader -> {
|
||||
inputBufferProofReader.addProgram(program);
|
||||
inputBufferProofReader.addProgram(getProgram(optFileEntry));
|
||||
}
|
||||
case ApplesoftTokenizedProofReader tokenizedProofReader -> {
|
||||
if (tokenizedProgram != null) {
|
||||
tokenizedProofReader.addBytes(tokenizedProgram);
|
||||
if (optFileEntry.isPresent()) {
|
||||
tokenizedProofReader.addBytes(optFileEntry.map(FileEntry::getFileData).orElseThrow());
|
||||
}
|
||||
else { // We fall back to the source code if we don't have tokens
|
||||
tokenizedProofReader.addProgram(program);
|
||||
tokenizedProofReader.addProgram(getProgram(optFileEntry));
|
||||
}
|
||||
}
|
||||
default -> throw new RuntimeException("Unknown type of proofer/checker: " + checker);
|
||||
@@ -120,6 +99,41 @@ public abstract class AbstractProofCommand extends ReusableCommandOptions {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Program getProgram(Optional<FileEntry> optFileEntry) throws IOException {
|
||||
File sourceFile = optFileEntry.map(fileEntry -> {
|
||||
LOG.warning("BASIC source generated from tokenized program. May not match proof-reader codes.");
|
||||
FileFilter filter = fileEntry.getSuggestedFilter();
|
||||
try {
|
||||
File tmpFile = File.createTempFile("proof-", ".bas");
|
||||
tmpFile.deleteOnExit();
|
||||
try (FileOutputStream out = new FileOutputStream(tmpFile)) {
|
||||
out.write(filter.filter(fileEntry));
|
||||
}
|
||||
return tmpFile;
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}).orElse(new File(programName));
|
||||
|
||||
Queue<Token> tokens = ModernTokenReader.tokenize(sourceFile);
|
||||
Parser parser = new Parser(tokens);
|
||||
return parser.parse();
|
||||
}
|
||||
|
||||
public Optional<FileEntry> findFileEntry() {
|
||||
final List<String> basicTypes = List.of("A", "BAS");
|
||||
if (disks != null && !disks.isEmpty()) {
|
||||
return FileStreamer.forDisks(disks)
|
||||
.matchGlobs(programName)
|
||||
.stream()
|
||||
.filter(FileTuple::isFile)
|
||||
.map(tuple -> tuple.fileEntry)
|
||||
.filter(fileEntry -> basicTypes.contains(fileEntry.getFiletype()))
|
||||
.findFirst();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Command(hidden = true, name = "proof")
|
||||
public static class HiddenProofCommand extends AbstractProofCommand implements Callable<Integer> {
|
||||
public static final Map<String,Function<Configuration,Object>> PROOF_READER_FNS = Map.of(
|
||||
|
||||
Reference in new issue
Block a user