dmolony-DiskBrowser/src/com/bytezone/diskbrowser/applefile/SubLine.java

592 lines
17 KiB
Java
Raw Normal View History

2020-12-30 03:06:50 +00:00
package com.bytezone.diskbrowser.applefile;
import java.util.ArrayList;
import java.util.List;
2021-01-08 02:03:12 +00:00
import com.bytezone.diskbrowser.utilities.Utility;;
2020-12-30 03:06:50 +00:00
// -----------------------------------------------------------------------------------//
2021-01-08 07:52:47 +00:00
public class SubLine implements ApplesoftConstants
2020-12-30 03:06:50 +00:00
// -----------------------------------------------------------------------------------//
{
SourceLine parent;
2021-01-03 05:06:51 +00:00
2021-01-08 07:52:47 +00:00
byte[] buffer;
2020-12-30 03:06:50 +00:00
int startPtr;
int length;
2021-01-08 02:03:12 +00:00
2020-12-30 03:06:50 +00:00
String[] nextVariables;
String forVariable = "";
2021-01-08 02:03:12 +00:00
2021-01-03 03:04:24 +00:00
int equalsPosition; // used for aligning the equals sign
2021-01-08 02:03:12 +00:00
2021-01-04 10:13:31 +00:00
String functionArgument;
String functionName;
2021-01-08 02:03:12 +00:00
String callTarget;
2020-12-30 03:06:50 +00:00
2021-01-03 03:04:24 +00:00
private final List<Integer> gotoLines = new ArrayList<> ();
private final List<Integer> gosubLines = new ArrayList<> ();
private final List<String> symbols = new ArrayList<> ();
2021-01-04 10:13:31 +00:00
private final List<String> functions = new ArrayList<> ();
private final List<String> arrays = new ArrayList<> ();
2021-01-05 03:24:23 +00:00
private final List<Integer> constants = new ArrayList<> ();
2020-12-30 03:06:50 +00:00
// ---------------------------------------------------------------------------------//
SubLine (SourceLine parent, int startPtr, int length)
// ---------------------------------------------------------------------------------//
{
this.parent = parent;
this.startPtr = startPtr;
this.length = length;
this.buffer = parent.buffer;
2021-01-10 06:18:48 +00:00
2021-01-03 03:04:24 +00:00
byte firstByte = buffer[startPtr];
2020-12-30 03:06:50 +00:00
if (Utility.isHighBitSet (firstByte))
2021-01-05 03:53:28 +00:00
{
2020-12-30 03:06:50 +00:00
doToken (firstByte);
2021-01-13 20:29:36 +00:00
if (is (TOKEN_REM) || is (TOKEN_DATA))
2021-01-05 03:53:28 +00:00
return;
}
2020-12-30 03:06:50 +00:00
else if (Utility.isDigit (firstByte))
2021-01-05 03:42:59 +00:00
{
2020-12-30 03:06:50 +00:00
doDigit ();
2021-01-05 03:42:59 +00:00
return;
}
2020-12-30 03:06:50 +00:00
else
doAlpha ();
int ptr = startPtr;
String var = "";
2021-01-10 06:18:48 +00:00
2020-12-30 03:06:50 +00:00
boolean inQuote = false;
2021-01-04 10:13:31 +00:00
boolean inFunction = false;
boolean inDefine = false;
int max = startPtr + length - 1;
if (buffer[max] == 0)
--max;
if (buffer[max] == Utility.ASCII_COLON)
--max;
while (ptr <= max)
2020-12-30 03:06:50 +00:00
{
2021-01-03 03:04:24 +00:00
byte b = buffer[ptr++];
2020-12-30 03:06:50 +00:00
if (inDefine) // ignore the name and argument
2021-01-04 10:13:31 +00:00
{
2021-01-08 02:03:12 +00:00
if (b == TOKEN_EQUALS)
2021-01-04 10:13:31 +00:00
inDefine = false;
continue;
}
if (b == TOKEN_DEF)
2021-01-04 10:13:31 +00:00
{
inDefine = true;
2021-01-04 10:13:31 +00:00
continue;
}
2021-01-08 02:03:12 +00:00
if (b == TOKEN_FN)
2021-01-04 10:13:31 +00:00
{
assert !inDefine;
2021-01-04 10:13:31 +00:00
inFunction = true;
continue;
}
if (inQuote && b != Utility.ASCII_QUOTE) // ignore strings
continue;
2021-01-12 00:34:29 +00:00
if (Utility.isPossibleVariable (b)) // A-Z 0-9 $ %
{
2021-01-13 20:29:36 +00:00
if (var.isEmpty () && buffer[ptr - 2] == TOKEN_MINUS && Utility.isDigit (b))
var = "-";
2020-12-30 03:06:50 +00:00
var += (char) b;
2021-01-13 20:29:36 +00:00
2021-01-12 00:34:29 +00:00
if ((b == Utility.ASCII_DOLLAR || b == Utility.ASCII_PERCENT) // var name end
&& buffer[ptr] != Utility.ASCII_LEFT_BRACKET) // not an array
{
checkVar (var, b);
var = "";
}
}
2020-12-30 03:06:50 +00:00
else
{
2021-01-04 10:13:31 +00:00
if (inFunction)
{
2021-01-04 10:13:31 +00:00
checkFunction (var, b);
inFunction = false;
}
2021-01-04 10:13:31 +00:00
else
checkVar (var, b);
2021-01-13 20:29:36 +00:00
2020-12-30 03:06:50 +00:00
var = "";
if (b == Utility.ASCII_QUOTE)
inQuote = !inQuote;
}
}
2021-01-05 03:53:28 +00:00
2020-12-30 03:06:50 +00:00
checkVar (var, (byte) 0);
}
2021-01-10 06:18:48 +00:00
// ---------------------------------------------------------------------------------//
private void doDigit ()
// ---------------------------------------------------------------------------------//
{
int targetLine = getLineNumber (buffer, startPtr);
addXref (targetLine, gotoLines);
}
// ---------------------------------------------------------------------------------//
private void doAlpha ()
// ---------------------------------------------------------------------------------//
{
recordEqualsPosition ();
}
2021-01-04 10:13:31 +00:00
// ---------------------------------------------------------------------------------//
private void checkFunction (String var, byte terminator)
// ---------------------------------------------------------------------------------//
{
assert terminator == Utility.ASCII_LEFT_BRACKET;
2021-01-05 03:53:28 +00:00
2021-01-04 10:13:31 +00:00
if (!functions.contains (var))
functions.add (var);
}
2020-12-30 03:06:50 +00:00
// ---------------------------------------------------------------------------------//
2021-01-03 03:04:24 +00:00
private void checkVar (String var, byte terminator)
2020-12-30 03:06:50 +00:00
// ---------------------------------------------------------------------------------//
{
if (var.length () == 0)
return;
2021-01-04 10:13:31 +00:00
if (!Utility.isLetter ((byte) var.charAt (0)))
2021-01-05 03:24:23 +00:00
{
2021-01-08 02:03:12 +00:00
if (is (TOKEN_GOTO) || is (TOKEN_GOSUB) || is (TOKEN_ON))
2021-01-05 03:42:59 +00:00
return;
int varInt = Integer.parseInt (var);
if (!constants.contains (varInt))
constants.add (varInt);
2021-01-04 10:13:31 +00:00
return;
2021-01-05 03:24:23 +00:00
}
2021-01-04 10:13:31 +00:00
2021-01-08 02:03:12 +00:00
if (is (TOKEN_DEF) && (var.equals (functionName) || var.equals (functionArgument)))
2021-01-04 10:13:31 +00:00
return;
2021-01-03 03:04:24 +00:00
if (terminator == Utility.ASCII_LEFT_BRACKET)
2021-01-04 10:13:31 +00:00
{
if (!arrays.contains (var))
arrays.add (var);
}
2021-01-08 07:52:47 +00:00
else if (!symbols.contains (var))
2021-01-03 03:04:24 +00:00
symbols.add (var);
}
// ---------------------------------------------------------------------------------//
List<String> getSymbols ()
// ---------------------------------------------------------------------------------//
{
return symbols;
}
2021-01-04 10:13:31 +00:00
// ---------------------------------------------------------------------------------//
List<String> getFunctions ()
// ---------------------------------------------------------------------------------//
{
return functions;
}
// ---------------------------------------------------------------------------------//
List<String> getArrays ()
// ---------------------------------------------------------------------------------//
{
return arrays;
}
2021-01-03 03:04:24 +00:00
// ---------------------------------------------------------------------------------//
List<Integer> getGotoLines ()
// ---------------------------------------------------------------------------------//
{
return gotoLines;
}
// ---------------------------------------------------------------------------------//
List<Integer> getGosubLines ()
// ---------------------------------------------------------------------------------//
{
return gosubLines;
2020-12-30 03:06:50 +00:00
}
2021-01-05 03:24:23 +00:00
// ---------------------------------------------------------------------------------//
List<Integer> getConstants ()
// ---------------------------------------------------------------------------------//
{
return constants;
}
2020-12-30 03:06:50 +00:00
// ---------------------------------------------------------------------------------//
private void doToken (byte b)
// ---------------------------------------------------------------------------------//
{
switch (b)
{
2021-01-08 02:03:12 +00:00
case TOKEN_FOR:
2020-12-30 03:06:50 +00:00
int p = startPtr + 1;
2021-01-08 02:03:12 +00:00
while (buffer[p] != TOKEN_EQUALS)
2021-01-03 03:04:24 +00:00
forVariable += (char) buffer[p++];
2020-12-30 03:06:50 +00:00
break;
2021-01-08 02:03:12 +00:00
case TOKEN_NEXT:
2020-12-30 03:06:50 +00:00
if (length == 2) // no variables
nextVariables = new String[0];
else
{
2021-01-03 03:04:24 +00:00
String varList = new String (buffer, startPtr + 1, length - 2);
2020-12-30 03:06:50 +00:00
nextVariables = varList.split (",");
}
break;
2021-01-08 02:03:12 +00:00
case TOKEN_LET:
2020-12-30 03:06:50 +00:00
recordEqualsPosition ();
break;
2021-01-08 02:03:12 +00:00
case TOKEN_GOTO:
2021-01-03 03:04:24 +00:00
int targetLine = getLineNumber (buffer, startPtr + 1);
2020-12-30 03:06:50 +00:00
addXref (targetLine, gotoLines);
break;
2021-01-08 02:03:12 +00:00
case TOKEN_GOSUB:
2021-01-03 03:04:24 +00:00
targetLine = getLineNumber (buffer, startPtr + 1);
2020-12-30 03:06:50 +00:00
addXref (targetLine, gosubLines);
break;
2021-01-08 02:03:12 +00:00
case TOKEN_ON:
2020-12-30 03:06:50 +00:00
p = startPtr + 1;
int max = startPtr + length - 1;
2021-01-03 03:04:24 +00:00
while (p < max && buffer[p] != ApplesoftConstants.TOKEN_GOTO
&& buffer[p] != ApplesoftConstants.TOKEN_GOSUB)
2020-12-30 03:06:50 +00:00
p++;
2021-01-03 03:04:24 +00:00
switch (buffer[p++])
2020-12-30 03:06:50 +00:00
{
2021-01-08 02:03:12 +00:00
case TOKEN_GOSUB:
2021-01-03 03:04:24 +00:00
for (int destLine : getLineNumbers (buffer, p))
2020-12-30 03:06:50 +00:00
addXref (destLine, gosubLines);
break;
2021-01-08 02:03:12 +00:00
case TOKEN_GOTO:
2021-01-03 03:04:24 +00:00
for (int destLine : getLineNumbers (buffer, p))
2020-12-30 03:06:50 +00:00
addXref (destLine, gotoLines);
break;
default:
System.out.println ("GOTO / GOSUB not found");
}
break;
2021-01-01 00:49:42 +00:00
2021-01-08 02:03:12 +00:00
case TOKEN_ONERR:
2021-01-01 00:49:42 +00:00
if (buffer[startPtr + 1] == ApplesoftConstants.TOKEN_GOTO)
{
2021-01-03 03:04:24 +00:00
targetLine = getLineNumber (buffer, startPtr + 2);
2021-01-01 00:49:42 +00:00
addXref (targetLine, gotoLines);
}
break;
2021-01-03 05:06:51 +00:00
2021-01-08 02:03:12 +00:00
case TOKEN_CALL:
2021-01-12 00:34:29 +00:00
callTarget = getExpression ();
2021-01-04 10:13:31 +00:00
break;
2021-01-08 02:03:12 +00:00
case TOKEN_DEF:
2021-01-12 00:34:29 +00:00
byte[] lineBuffer = getBuffer ();
2021-01-08 02:03:12 +00:00
assert lineBuffer[0] == TOKEN_FN;
2021-01-04 10:13:31 +00:00
int leftBracket = getPosition (lineBuffer, 1, Utility.ASCII_LEFT_BRACKET);
int rightBracket =
getPosition (lineBuffer, leftBracket + 1, Utility.ASCII_RIGHT_BRACKET);
functionName = new String (lineBuffer, 1, leftBracket - 1);
functionArgument =
new String (lineBuffer, leftBracket + 1, rightBracket - leftBracket - 1);
functions.add (functionName);
2021-01-03 05:06:51 +00:00
break;
2020-12-30 03:06:50 +00:00
}
}
2021-01-04 10:13:31 +00:00
// ---------------------------------------------------------------------------------//
2021-01-12 00:34:29 +00:00
private String getExpression ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
int ptr = startPtr + 1;
int max = startPtr + length - 1;
while (ptr < max)
{
if (Utility.isHighBitSet (buffer[ptr]))
text.append (tokens[buffer[ptr] & 0x7F]);
else
text.append ((char) buffer[ptr]);
++ptr;
}
return text.toString ();
}
// ---------------------------------------------------------------------------------//
2021-01-04 10:13:31 +00:00
private int getPosition (byte[] buffer, int start, byte value)
// ---------------------------------------------------------------------------------//
{
for (int i = start; i < buffer.length; i++)
if (buffer[i] == value)
return i;
return -1;
}
2020-12-30 03:06:50 +00:00
// ---------------------------------------------------------------------------------//
2021-01-03 03:04:24 +00:00
private void addXref (int targetLine, List<Integer> list)
2020-12-30 03:06:50 +00:00
// ---------------------------------------------------------------------------------//
{
2021-01-03 03:04:24 +00:00
if (!list.contains (targetLine))
list.add (targetLine);
2020-12-30 03:06:50 +00:00
}
// ---------------------------------------------------------------------------------//
private List<Integer> getLineNumbers (byte[] buffer, int ptr)
// ---------------------------------------------------------------------------------//
{
List<Integer> lineNumbers = new ArrayList<> ();
int start = ptr;
while (ptr < buffer.length && buffer[ptr] != 0 && buffer[ptr] != Utility.ASCII_COLON)
ptr++;
String s = new String (buffer, start, ptr - start);
String[] chunks = s.split (",");
try
{
for (String chunk : chunks)
lineNumbers.add (Integer.parseInt (chunk));
}
catch (NumberFormatException e)
{
System.out.printf ("NFE: %s%n", s);
}
return lineNumbers;
}
// ---------------------------------------------------------------------------------//
private int getLineNumber (byte[] buffer, int ptr)
// ---------------------------------------------------------------------------------//
{
int lineNumber = 0;
2021-01-08 02:03:12 +00:00
while (ptr < buffer.length && Utility.isDigit (buffer[ptr]))
lineNumber = lineNumber * 10 + (buffer[ptr++] & 0xFF) - 0x30;
2020-12-30 03:06:50 +00:00
return lineNumber;
}
// ---------------------------------------------------------------------------------//
boolean isImpliedGoto ()
// ---------------------------------------------------------------------------------//
{
2021-01-03 03:04:24 +00:00
byte b = buffer[startPtr];
2020-12-30 03:06:50 +00:00
if (Utility.isHighBitSet (b))
return false;
return (Utility.isDigit (b));
}
// Record the position of the equals sign so it can be aligned with adjacent lines.
// ---------------------------------------------------------------------------------//
private void recordEqualsPosition ()
// ---------------------------------------------------------------------------------//
{
int p = startPtr + 1;
int max = startPtr + length;
2021-01-08 02:03:12 +00:00
while (buffer[p] != TOKEN_EQUALS && p < max)
2020-12-30 03:06:50 +00:00
p++;
2021-01-08 02:03:12 +00:00
if (buffer[p] == TOKEN_EQUALS)
2021-01-03 03:04:24 +00:00
equalsPosition = toString ().indexOf ('='); // use expanded line
2020-12-30 03:06:50 +00:00
}
// ---------------------------------------------------------------------------------//
boolean isJoinableRem ()
// ---------------------------------------------------------------------------------//
{
2021-01-08 02:03:12 +00:00
return is (TOKEN_REM) && !isFirst ();
2020-12-30 03:06:50 +00:00
}
// ---------------------------------------------------------------------------------//
boolean isFirst ()
// ---------------------------------------------------------------------------------//
{
return (parent.linePtr + 4) == startPtr;
}
// ---------------------------------------------------------------------------------//
boolean is (byte token)
// ---------------------------------------------------------------------------------//
{
2021-01-03 03:04:24 +00:00
return buffer[startPtr] == token;
2020-12-30 03:06:50 +00:00
}
// ---------------------------------------------------------------------------------//
boolean has (byte token)
// ---------------------------------------------------------------------------------//
{
int ptr = startPtr + 1;
int max = startPtr + length;
2020-12-31 09:50:53 +00:00
2020-12-30 03:06:50 +00:00
while (ptr < max)
{
2021-01-03 03:04:24 +00:00
if (buffer[ptr++] == token)
2020-12-30 03:06:50 +00:00
return true;
}
return false;
}
// ---------------------------------------------------------------------------------//
boolean isEmpty ()
// ---------------------------------------------------------------------------------//
{
return length == 1 && buffer[startPtr] == 0;
}
// ---------------------------------------------------------------------------------//
boolean containsToken ()
// ---------------------------------------------------------------------------------//
{
// ignore first byte, check the rest for tokens
for (int p = startPtr + 1, max = startPtr + length; p < max; p++)
if (Utility.isHighBitSet (buffer[p]))
return true;
return false;
}
// ---------------------------------------------------------------------------------//
boolean containsControlChars ()
// ---------------------------------------------------------------------------------//
{
for (int p = startPtr + 1, max = startPtr + length; p < max; p++)
{
int c = buffer[p] & 0xFF;
if (c == 0)
break;
if (c < 32)
return true;
}
return false;
}
// ---------------------------------------------------------------------------------//
void addFormattedRem (StringBuilder text)
// ---------------------------------------------------------------------------------//
{
int ptr = startPtr + 1;
int max = startPtr + length - 2;
while (ptr <= max)
{
2020-12-31 09:50:53 +00:00
switch (buffer[ptr])
{
case Utility.ASCII_BACKSPACE:
if (text.length () > 0)
text.deleteCharAt (text.length () - 1);
break;
case Utility.ASCII_CR:
text.append ("\n");
break;
default:
text.append ((char) buffer[ptr]); // do not mask with 0xFF
}
2020-12-30 03:06:50 +00:00
ptr++;
}
}
// ---------------------------------------------------------------------------------//
2021-01-03 03:04:24 +00:00
public String getAlignedText (int alignEqualsPos)
2020-12-30 03:06:50 +00:00
// ---------------------------------------------------------------------------------//
{
2021-01-03 03:04:24 +00:00
StringBuilder line = toStringBuilder (); // get line
2020-12-30 03:06:50 +00:00
2021-01-03 03:04:24 +00:00
// insert spaces before '=' until it lines up with the other assignment lines
2021-01-08 02:03:12 +00:00
if (!is (TOKEN_REM))
2021-01-03 03:04:24 +00:00
while (alignEqualsPos-- > equalsPosition)
line.insert (equalsPosition, ' ');
2020-12-30 03:06:50 +00:00
return line.toString ();
}
// ---------------------------------------------------------------------------------//
2021-01-03 05:06:51 +00:00
public byte[] getBuffer ()
2020-12-30 03:06:50 +00:00
// ---------------------------------------------------------------------------------//
{
2021-01-03 05:06:51 +00:00
int len = length - 1;
if (buffer[startPtr + len] == Utility.ASCII_COLON || buffer[startPtr + len] == 0)
len--;
byte[] buffer2 = new byte[len];
2020-12-30 03:06:50 +00:00
System.arraycopy (buffer, startPtr + 1, buffer2, 0, buffer2.length);
2021-01-03 05:06:51 +00:00
return buffer2;
2020-12-30 03:06:50 +00:00
}
// ---------------------------------------------------------------------------------//
@Override
public String toString ()
// ---------------------------------------------------------------------------------//
{
return toStringBuilder ().toString ();
}
// ---------------------------------------------------------------------------------//
public StringBuilder toStringBuilder ()
// ---------------------------------------------------------------------------------//
{
StringBuilder line = new StringBuilder ();
// All sublines end with 0 or : except IF lines that are split into two
int max = startPtr + length - 1;
if (buffer[max] == 0)
--max;
if (isImpliedGoto () && !ApplesoftBasicProgram.basicPreferences.showThen)
line.append ("GOTO ");
for (int p = startPtr; p <= max; p++)
{
byte b = buffer[p];
2021-01-10 22:04:08 +00:00
if (Utility.isHighBitSet (b)) // token
2020-12-30 03:06:50 +00:00
{
if (line.length () > 0 && line.charAt (line.length () - 1) != ' ')
line.append (' ');
int val = b & 0x7F;
// if (val < ApplesoftConstants.tokens.length)
// {
if (b != TOKEN_THEN || ApplesoftBasicProgram.basicPreferences.showThen)
line.append (ApplesoftConstants.tokens[val] + " ");
// }
2020-12-30 03:06:50 +00:00
}
else if (Utility.isControlCharacter (b))
line.append (ApplesoftBasicProgram.basicPreferences.showCaret
2020-12-31 09:50:53 +00:00
? "^" + (char) (b + 64) : ".");
2020-12-30 03:06:50 +00:00
else
line.append ((char) b);
}
return line;
}
}