Adding command-line. Closes #6.

This commit is contained in:
Rob Greene 2018-05-13 15:41:23 -05:00
parent f008a950c3
commit 19f3fe56bf
4 changed files with 176 additions and 22 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@
/.settings/
/.classpath
/.project
pom.xml.versionsBackup
*.bin

52
pom.xml
View File

@ -1,16 +1,62 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.sf.applecommander</groupId>
<artifactId>bastokenizer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<version>0.1.0-SNAPSHOT</version>
<name>AppleSoft BASIC Tokenizer</name>
<description>Experiments with generating an AppleSoft B/BAS tokenized "binary".</description>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
<timestamp>${maven.build.timestamp}</timestamp>
</properties>
<dependencies>
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
<version>[3.0,3.1)</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
<useUniqueVersions>false</useUniqueVersions>
</manifest>
<manifestEntries>
<Implementation-Version>${project.version} (${timestamp})</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.0.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,5 +1,6 @@
package com.webcodepro.applecommander.util.applesoft;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
@ -24,12 +25,18 @@ public class TokenReader {
private Reader reader;
private StreamTokenizer tokenizer;
/** A handy method to generate a list of Tokens from a file. */
/** A handy method to generate a list of Tokens from a file name. */
public static Queue<Token> tokenize(String filename) throws FileNotFoundException, IOException {
try (FileReader fileReader = new FileReader(filename)) {
return tokenize(fileReader);
}
}
/** A handy method to generate a list of Tokens from a file. */
public static Queue<Token> tokenize(File file) throws FileNotFoundException, IOException {
try (FileReader fileReader = new FileReader(file)) {
return tokenize(fileReader);
}
}
/** A handy method to generate a list of Tokens from an InputStream. */
public static Queue<Token> tokenize(InputStream inputStream) throws IOException {
try (InputStreamReader streamReader = new InputStreamReader(inputStream)) {

View File

@ -1,37 +1,115 @@
package io.github.applecommander.bastokenizer;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Queue;
import java.util.concurrent.Callable;
import com.webcodepro.applecommander.util.applesoft.Parser;
import com.webcodepro.applecommander.util.applesoft.Program;
import com.webcodepro.applecommander.util.applesoft.Token;
import com.webcodepro.applecommander.util.applesoft.TokenReader;
/** A simple driver for the tokenizer for a sample and rudimentary test. */
public class Main {
public static void main(String[] args) throws FileNotFoundException, IOException {
if (args.length != 1) {
System.err.println("Please include a file to work on.");
System.exit(1);
}
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Help.Visibility;
import picocli.CommandLine.ITypeConverter;
import picocli.CommandLine.IVersionProvider;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
Queue<Token> tokens = TokenReader.tokenize(args[0]);
System.out.println(tokens.toString());
Parser parser = new Parser(tokens);
Program program = parser.parse();
program.prettyPrint(System.out);
int address = 0x801;
byte[] data = program.toBytes(address);
print(address, data, false);
print(address, data, true);
/** A command-line interface to the AppleSoft BAS tokenizer libraries. */
@Command(description = "Transforms an AppleSoft program from text back to it's tokenized state.",
name = "bt", mixinStandardHelpOptions = true,
versionProvider = Main.VersionProvider.class)
public class Main implements Callable<Void> {
@Option(names = { "-o", "--output" }, description = "Write binary output to file.")
private File outputFile;
@Option(names = { "-x", "--hex"}, description = "Generate a binary hex dump for debugging.")
private boolean hexFormat;
@Option(names = { "-c", "--copy"}, description = "Generate a copy/paste form of output for testing in an emulator.")
private boolean copyFormat;
@Option(names = { "-a", "--address" }, description = "Base address for program", showDefaultValue = Visibility.ALWAYS, converter = IntegerTypeConverter.class)
private int address = 0x801;
@Option(names = { "-p", "--pipe" }, description = "Pipe binary output to stdout.")
private boolean pipeOutput;
@Option(names = "--pretty", description = "Pretty print structure as bastokenizer understands it.")
private boolean prettyPrint;
@Option(names = "--tokens", description = "Dump token list to stdout for debugging.")
private boolean showTokens;
@Parameters(index = "0", description = "AppleSoft BASIC program to process.")
private File sourceFile;
public static void main(String[] args) throws FileNotFoundException, IOException {
CommandLine.call(new Main(), args);
}
public static void print(int address, byte[] data, boolean forApple) {
@Override
public Void call() throws FileNotFoundException, IOException {
if (checkParameters()) {
process();
}
return null; // To satisfy object "Void"
}
/** A basic test to ensure parameters are somewhat sane. */
public boolean checkParameters() {
if (pipeOutput && (hexFormat || copyFormat || prettyPrint || showTokens)) {
System.err.println("The pipe option blocks any other stdout options.");
return false;
} else if (!(pipeOutput || hexFormat || copyFormat || prettyPrint || showTokens || outputFile != null)) {
System.err.println("What do you want to do?");
return false;
}
return true;
}
/** General CLI processing. */
public void process() throws FileNotFoundException, IOException {
Queue<Token> tokens = TokenReader.tokenize(sourceFile);
if (showTokens) {
System.out.println(tokens.toString());
}
Parser parser = new Parser(tokens);
Program program = parser.parse();
if (prettyPrint) {
program.prettyPrint(System.out);
}
byte[] data = program.toBytes(address);
if (hexFormat) {
hexDump(address, data, false);
}
if (copyFormat) {
hexDump(address, data, true);
}
if (outputFile != null) {
Files.write(outputFile.toPath(), data);
}
if (pipeOutput) {
System.out.write(data);
}
}
/** Dump data to stdout in various formats. */
public void hexDump(int address, byte[] data, boolean forApple) {
final int line = 16;
int offset = 0;
if (forApple) {
int end = address + data.length;
System.out.printf("0067: %02x %02x %02x %02x\n", address&0xff, address>>8, end&0xff, end>>8);
System.out.printf("%04x: 00\n", address-1);
}
while (offset < data.length) {
System.out.printf("%04x: ", address);
for (int i=0; i<line; i++) {
@ -58,6 +136,27 @@ public class Main {
System.out.printf("\n");
address += line;
}
}
/** Display version information. Note that this is dependent on Maven configuration. */
public static class VersionProvider implements IVersionProvider {
public String[] getVersion() {
return new String[] { Main.class.getPackage().getImplementationVersion() };
}
}
/** Add support for "$801" and "0x801" instead of just decimal like 2049. */
public static class IntegerTypeConverter implements ITypeConverter<Integer> {
@Override
public Integer convert(String value) throws Exception {
if (value == null) {
return null;
} else if (value.startsWith("$")) {
return Integer.valueOf(value.substring(1), 16);
} else if (value.startsWith("0x") || value.startsWith("0X")) {
return Integer.valueOf(value.substring(2), 16);
} else {
return Integer.valueOf(value);
}
}
}
}