diff --git a/src/main/java/com/webcodepro/applecommander/util/applesoft/TokenReader.java b/src/main/java/com/webcodepro/applecommander/util/applesoft/TokenReader.java index 56be8a1..ebf0ec2 100644 --- a/src/main/java/com/webcodepro/applecommander/util/applesoft/TokenReader.java +++ b/src/main/java/com/webcodepro/applecommander/util/applesoft/TokenReader.java @@ -96,17 +96,25 @@ public class TokenReader { } return Optional.of(Token.comment(line, sb.toString())); } - // Optional and exceptions don't play well. :-/ - if (opt.isPresent() && opt.get().parts.size() > 1) { - // Pull next token and see if it is the 2nd part ("MID$" == "MID", "$"; checking for the "$") - next(depth-1) - .filter(t -> opt.get().parts.get(1).equals(t.text)) - .orElseThrow(() -> new IOException("Expecting: " + opt.get().parts)); + // If we found an Applesoft token, handle it special + if (opt.isPresent()) { + if (opt.get().parts.size() > 1) { + // Pull next token and see if it is the 2nd part ("MID$" == "MID", "$"; checking for the "$") + next(depth-1) + .filter(t -> opt.get().parts.get(1).equals(t.text)) + .orElseThrow(() -> new IOException("Expecting: " + opt.get().parts)); + } return Optional.of(Token.keyword(line, opt.get())); + } else { + // Found an identifier. Need to find X, X%, X$, X(, X$(, X%( patterns. + String sval = tokenizer.sval; + tokenizer.nextToken(); + if (tokenizer.ttype == '%' || tokenizer.ttype == '$') { + sval += (char)tokenizer.ttype; + } + tokenizer.pushBack(); + return Optional.of(Token.ident(line, sval)); } - return Optional.of(opt - .map(kw -> Token.keyword(line, kw)) - .orElse(Token.ident(line, tokenizer.sval))); case '"': return Optional.of(Token.string(line, tokenizer.sval)); case '(': diff --git a/src/main/java/com/webcodepro/applecommander/util/applesoft/Visitors.java b/src/main/java/com/webcodepro/applecommander/util/applesoft/Visitors.java index 07bd573..3ea2e3d 100644 --- a/src/main/java/com/webcodepro/applecommander/util/applesoft/Visitors.java +++ b/src/main/java/com/webcodepro/applecommander/util/applesoft/Visitors.java @@ -3,6 +3,10 @@ package com.webcodepro.applecommander.util.applesoft; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -57,6 +61,10 @@ public class Visitors { public static ReassignmentVisitor reassignVisitor(Map reassignments) { return new ReassignmentVisitor(reassignments); } + + public static Visitor variableReportVisitor() { + return new VariableReportVisitor(); + } private static class PrettyPrintVisitor implements Visitor { private PrintStream printStream; @@ -336,4 +344,46 @@ public class Visitors { return newStatement; } } + + private static class VariableReportVisitor implements Visitor { + private Map> refs = new HashMap<>(); + private int currentLineNumber = -1; + + @Override + public Program visit(Program program) { + Program p = Visitor.super.visit(program); + refs.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .forEach(this::print); + return p; + } + private void print(Map.Entry> e) { + System.out.printf("%-8s ", e.getKey()); + int c = 0; + for (int i : e.getValue()) { + System.out.printf("%d, ", i); + if (c++ > 10) { + c = 0; + System.out.printf("\n "); + } + } + System.out.println(); + } + + @Override + public Line visit(Line line) { + currentLineNumber = line.lineNumber; + return Visitor.super.visit(line); + } + + @Override + public Token visit(Token token) { + if (token.type == Type.IDENT) { + refs.merge(token.text, + new ArrayList<>(Arrays.asList(currentLineNumber)), + (a,b) -> { a.addAll(b); return a; }); + } + return Visitor.super.visit(token); + } + } } diff --git a/src/main/java/io/github/applecommander/bastokenizer/Main.java b/src/main/java/io/github/applecommander/bastokenizer/Main.java index 0354c0c..4c22f53 100644 --- a/src/main/java/io/github/applecommander/bastokenizer/Main.java +++ b/src/main/java/io/github/applecommander/bastokenizer/Main.java @@ -43,6 +43,9 @@ public class Main implements Callable { @Option(names = { "-a", "--address" }, description = "Base address for program", showDefaultValue = Visibility.ALWAYS, converter = IntegerTypeConverter.class) private int address = 0x801; + @Option(names = { "--variables" }, description = "Generate a variable report") + private boolean showVariableReport; + @Option(names = { "-p", "--pipe" }, description = "Pipe binary output to stdout.") private boolean pipeOutput; @@ -88,10 +91,10 @@ public class Main implements Callable { optimizations.clear(); optimizations.addAll(Arrays.asList(Optimization.values())); } - if (pipeOutput && (hexFormat || copyFormat || prettyPrint || listPrint || showTokens)) { + if (pipeOutput && (hexFormat || copyFormat || prettyPrint || listPrint || showTokens || showVariableReport)) { System.err.println("The pipe option blocks any other stdout options."); return false; - } else if (!(pipeOutput || hexFormat || copyFormat || prettyPrint || listPrint || showTokens || outputFile != null)) { + } else if (!(pipeOutput || hexFormat || copyFormat || prettyPrint || listPrint || showTokens || showVariableReport || outputFile != null)) { System.err.println("What do you want to do?"); return false; } @@ -114,6 +117,9 @@ public class Main implements Callable { if (prettyPrint || listPrint) { program.accept(Visitors.printBuilder().prettyPrint(prettyPrint).build()); } + if (showVariableReport) { + program.accept(Visitors.variableReportVisitor()); + } byte[] data = Visitors.byteVisitor(address).dump(program); if (hexFormat) { diff --git a/src/test/resources/circles.bas b/src/test/resources/circles.bas new file mode 100644 index 0000000..206db07 --- /dev/null +++ b/src/test/resources/circles.bas @@ -0,0 +1,32 @@ +TEXT +NEW +10 GOTO 100 +20 REM DRAW CIRCLE ROUTINE +30 FOR A = 0 TO PT +40 X = X(A) * SZ:Y = Y(A) * SZ +50 HPLOT XO + X,YO + Y +60 HPLOT XO - X,YO + Y +70 HPLOT XO + X,YO - Y +80 HPLOT XO - X,YO - Y +90 NEXT A +95 RETURN +100 REM MAIN PROGRAM +110 HGR +115 C(0)=1:C(1)=2:C(2)=3:C(3)=5:C(4)=6:C(5)=7 +120 HOME : VTAB 21: INVERSE : PRINT "JUST A MOMENT": NORMAL +130 PI = 3.14159 +140 PT = 30: DIM X(PT),Y(PT) +150 FOR A = 0 TO PT +160 B = PI * (A / (PT * 2)) +170 X(A) = SIN (B) +180 Y(A) = COS (B) +190 NEXT A +200 HOME : VTAB 21 +210 FOR Q = 1 TO 100 +215 C = 6 * RND(1) : HCOLOR= C(C) +220 SZ = 10 + (40 * RND (1)) +230 XO = (279 - SZ*2) * RND (1) + SZ +240 YO = (159 - SZ*2) * RND (1) + SZ +250 GOSUB 30 +260 NEXT Q +RUN