2020-12-30 03:06:50 +00:00
|
|
|
package com.bytezone.diskbrowser.applefile;
|
|
|
|
|
2021-02-23 10:06:42 +00:00
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_BACKSPACE;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_COLON;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_COMMA;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_CR;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_DOLLAR;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_LEFT_BRACKET;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_LF;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_MINUS;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_PERCENT;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_QUOTE;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_RIGHT_BRACKET;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.getIndent;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.isControlCharacter;
|
2021-02-21 08:03:26 +00:00
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.isDigit;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.isHighBitSet;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.isLetter;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.isPossibleNumber;
|
|
|
|
import static com.bytezone.diskbrowser.utilities.Utility.isPossibleVariable;
|
|
|
|
|
2020-12-30 03:06:50 +00:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
|
|
|
|
2021-02-26 02:52:32 +00:00
|
|
|
import com.bytezone.diskbrowser.applefile.ApplesoftBasicProgram.Alignment;
|
2021-02-23 10:06:42 +00:00
|
|
|
import com.bytezone.diskbrowser.utilities.HexFormatter;;
|
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-02-26 02:52:32 +00:00
|
|
|
int equalsPosition; // used for aligning the equals sign
|
2021-02-25 22:16:14 +00:00
|
|
|
int endPosition; // not sure yet
|
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<> ();
|
2021-01-14 11:55:57 +00:00
|
|
|
|
2021-02-20 21:42:40 +00:00
|
|
|
private final List<String> variables = 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-14 11:55:57 +00:00
|
|
|
|
2021-01-13 22:47:10 +00:00
|
|
|
private final List<Integer> constantsInt = new ArrayList<> ();
|
|
|
|
private final List<Float> constantsFloat = new ArrayList<> ();
|
2020-12-30 03:06:50 +00:00
|
|
|
|
2021-01-14 11:55:57 +00:00
|
|
|
private final List<String> stringsText = 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-13 22:47:10 +00:00
|
|
|
int ptr = startPtr;
|
2021-01-03 03:04:24 +00:00
|
|
|
byte firstByte = buffer[startPtr];
|
2020-12-30 03:06:50 +00:00
|
|
|
|
2021-01-18 00:06:17 +00:00
|
|
|
if (isToken (firstByte))
|
2021-01-05 03:53:28 +00:00
|
|
|
{
|
2020-12-30 03:06:50 +00:00
|
|
|
doToken (firstByte);
|
2021-01-13 22:47:10 +00:00
|
|
|
if (is (TOKEN_REM) || is (TOKEN_DATA)) // no further processing
|
2021-01-05 03:53:28 +00:00
|
|
|
return;
|
2021-02-20 20:50:18 +00:00
|
|
|
|
|
|
|
if (is (TOKEN_CALL))
|
|
|
|
ptr = startPtr + callTarget.length ();
|
|
|
|
else
|
|
|
|
ptr = startPtr + 1;
|
2021-01-05 03:53:28 +00:00
|
|
|
}
|
2021-01-13 22:47:10 +00:00
|
|
|
else
|
2021-01-05 03:42:59 +00:00
|
|
|
{
|
2021-02-21 08:03:26 +00:00
|
|
|
if (isDigit (firstByte)) // split IF xx THEN nnn
|
2021-01-13 22:47:10 +00:00
|
|
|
{
|
|
|
|
addXref (getLineNumber (buffer, startPtr), gotoLines);
|
|
|
|
return;
|
|
|
|
}
|
2021-02-26 02:52:32 +00:00
|
|
|
else if (isLetter (firstByte)) // variable name
|
2021-02-20 20:50:18 +00:00
|
|
|
setEqualsPosition ();
|
|
|
|
else if (isEndOfLine (firstByte)) // empty subline
|
2021-01-18 00:06:17 +00:00
|
|
|
return;
|
2021-01-18 19:47:01 +00:00
|
|
|
else // probably Beagle Bros 0D or 0A
|
2021-02-22 10:11:52 +00:00
|
|
|
System.out.printf ("%s unexpected bytes at line %5d:%n%s%n", parent.parent.name,
|
|
|
|
parent.lineNumber, HexFormatter.formatNoHeader (buffer, startPtr, length));
|
2021-01-05 03:42:59 +00:00
|
|
|
}
|
2020-12-30 03:06:50 +00:00
|
|
|
|
|
|
|
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;
|
2021-01-14 11:55:57 +00:00
|
|
|
int stringPtr = 0;
|
2021-01-04 10:13:31 +00:00
|
|
|
|
|
|
|
int max = startPtr + length - 1;
|
2021-02-21 23:24:34 +00:00
|
|
|
while (isEndOfLine (buffer[max]))
|
2021-01-04 10:13:31 +00:00
|
|
|
--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
|
|
|
|
2021-01-11 02:01:01 +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;
|
|
|
|
}
|
|
|
|
|
2021-01-11 02:01:01 +00:00
|
|
|
if (b == TOKEN_DEF)
|
2021-01-04 10:13:31 +00:00
|
|
|
{
|
2021-01-11 02:01:01 +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
|
|
|
{
|
2021-01-11 02:01:01 +00:00
|
|
|
assert !inDefine;
|
2021-01-04 10:13:31 +00:00
|
|
|
inFunction = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-01-13 22:47:10 +00:00
|
|
|
if (inQuote)
|
|
|
|
{
|
2021-02-23 10:06:42 +00:00
|
|
|
if (b == ASCII_QUOTE) // ignore strings
|
2021-01-14 11:55:57 +00:00
|
|
|
{
|
2021-01-13 22:47:10 +00:00
|
|
|
inQuote = false;
|
2021-01-15 22:10:19 +00:00
|
|
|
addString (stringPtr, ptr);
|
2021-01-14 11:55:57 +00:00
|
|
|
}
|
2021-01-13 22:47:10 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-02-23 10:06:42 +00:00
|
|
|
if (b == ASCII_QUOTE)
|
2021-01-13 22:47:10 +00:00
|
|
|
{
|
|
|
|
inQuote = true;
|
2021-01-14 11:55:57 +00:00
|
|
|
stringPtr = ptr;
|
2021-01-11 02:01:01 +00:00
|
|
|
continue;
|
2021-01-13 22:47:10 +00:00
|
|
|
}
|
2021-01-11 02:01:01 +00:00
|
|
|
|
2021-02-21 08:03:26 +00:00
|
|
|
if (isPossibleVariable (b) || isPossibleNumber (b))
|
2021-01-12 00:34:29 +00:00
|
|
|
{
|
2021-02-21 08:03:26 +00:00
|
|
|
if (var.isEmpty () && isPossibleNumber (b) && buffer[ptr - 2] == TOKEN_MINUS)
|
2021-01-13 20:29:36 +00:00
|
|
|
var = "-";
|
|
|
|
|
2020-12-30 03:06:50 +00:00
|
|
|
var += (char) b;
|
2021-01-13 20:29:36 +00:00
|
|
|
|
2021-01-13 22:47:10 +00:00
|
|
|
// allow for PRINT A$B$
|
2021-02-23 10:06:42 +00:00
|
|
|
if ((b == ASCII_DOLLAR || b == ASCII_PERCENT) // var name end
|
|
|
|
&& buffer[ptr] != ASCII_LEFT_BRACKET) // not an array
|
2021-01-12 00:34:29 +00:00
|
|
|
{
|
|
|
|
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-11 02:01:01 +00:00
|
|
|
{
|
2021-01-04 10:13:31 +00:00
|
|
|
checkFunction (var, b);
|
2021-01-11 02:01:01 +00:00
|
|
|
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 = "";
|
|
|
|
}
|
|
|
|
}
|
2021-01-05 03:53:28 +00:00
|
|
|
|
2021-01-16 00:15:27 +00:00
|
|
|
if (inQuote)
|
|
|
|
addString (stringPtr, ptr); // unterminated string
|
|
|
|
else
|
|
|
|
checkVar (var, (byte) 0); // unprocessed variable or number
|
2020-12-30 03:06:50 +00:00
|
|
|
}
|
|
|
|
|
2021-02-20 20:50:18 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
private boolean isEndOfLine (byte b)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
2021-02-23 10:06:42 +00:00
|
|
|
return b == 0 || b == ASCII_COLON;
|
2021-02-20 20:50:18 +00:00
|
|
|
}
|
|
|
|
|
2021-01-15 22:10:19 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
private void addString (int stringPtr, int ptr)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
2021-01-16 00:15:27 +00:00
|
|
|
stringsText.add (new String (buffer, stringPtr - 1, ptr - stringPtr + 1));
|
2021-01-15 22:10:19 +00:00
|
|
|
}
|
|
|
|
|
2021-01-04 10:13:31 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
private void checkFunction (String var, byte terminator)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
2021-02-23 10:06:42 +00:00
|
|
|
assert terminator == 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-02-21 08:03:26 +00:00
|
|
|
if (!isLetter ((byte) var.charAt (0)))
|
2021-01-05 03:24:23 +00:00
|
|
|
{
|
2021-01-13 22:47:10 +00:00
|
|
|
if (is (TOKEN_GOTO) || is (TOKEN_GOSUB) || is (TOKEN_ON) || is (TOKEN_ONERR))
|
2021-01-13 22:57:57 +00:00
|
|
|
return; // ignore line numbers
|
2021-01-13 22:47:10 +00:00
|
|
|
addNumber (var);
|
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-02-23 10:06:42 +00:00
|
|
|
if (terminator == ASCII_LEFT_BRACKET)
|
2021-01-04 10:13:31 +00:00
|
|
|
{
|
|
|
|
if (!arrays.contains (var))
|
|
|
|
arrays.add (var);
|
|
|
|
}
|
2021-02-20 21:42:40 +00:00
|
|
|
else if (!variables.contains (var))
|
|
|
|
variables.add (var);
|
2021-01-14 11:55:57 +00:00
|
|
|
}
|
|
|
|
|
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:
|
2021-02-20 20:50:18 +00:00
|
|
|
setEqualsPosition ();
|
2020-12-30 03:06:50 +00:00
|
|
|
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-02-23 10:06:42 +00:00
|
|
|
while (p < max && buffer[p] != TOKEN_GOTO && buffer[p] != 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-02-23 10:06:42 +00:00
|
|
|
if (buffer[startPtr + 1] == TOKEN_GOTO)
|
2021-01-01 00:49:42 +00:00
|
|
|
{
|
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-02-20 09:33:53 +00:00
|
|
|
callTarget = getCallTarget ();
|
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
|
|
|
|
2021-02-23 10:06:42 +00:00
|
|
|
int leftBracket = getPosition (lineBuffer, 1, ASCII_LEFT_BRACKET);
|
|
|
|
int rightBracket = getPosition (lineBuffer, leftBracket + 1, ASCII_RIGHT_BRACKET);
|
2021-01-04 10:13:31 +00:00
|
|
|
|
|
|
|
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;
|
2021-01-13 22:47:10 +00:00
|
|
|
|
|
|
|
case TOKEN_DATA:
|
2021-01-14 22:37:17 +00:00
|
|
|
for (String chunk : new String (getBuffer ()).split (","))
|
2021-01-13 22:47:10 +00:00
|
|
|
{
|
2021-01-14 22:37:17 +00:00
|
|
|
chunk = chunk.trim ();
|
|
|
|
if (chunk.isEmpty ())
|
|
|
|
continue;
|
2021-02-25 22:16:14 +00:00
|
|
|
|
2021-01-13 22:47:10 +00:00
|
|
|
b = (byte) chunk.charAt (0);
|
2021-02-23 10:06:42 +00:00
|
|
|
if (isPossibleNumber (b) || b == ASCII_MINUS)
|
2021-01-15 22:10:19 +00:00
|
|
|
{
|
|
|
|
if (!addNumber (chunk))
|
|
|
|
stringsText.add (chunk);
|
|
|
|
}
|
2021-01-14 11:55:57 +00:00
|
|
|
else
|
2021-01-18 19:47:01 +00:00
|
|
|
stringsText.add (chunk);
|
2021-01-13 22:47:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
2021-01-15 22:10:19 +00:00
|
|
|
private boolean addNumber (String var)
|
2021-01-13 22:47:10 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2021-02-20 21:42:40 +00:00
|
|
|
if (var.indexOf ('.') < 0) // no decimal point
|
2021-01-13 22:47:10 +00:00
|
|
|
{
|
|
|
|
int varInt = Integer.parseInt (var);
|
|
|
|
if (!constantsInt.contains (varInt))
|
|
|
|
constantsInt.add (varInt);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float varFloat = Float.parseFloat (var);
|
|
|
|
if (!constantsFloat.contains (varFloat))
|
|
|
|
constantsFloat.add (varFloat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (NumberFormatException nfe)
|
|
|
|
{
|
2021-01-15 22:10:19 +00:00
|
|
|
return false;
|
2020-12-30 03:06:50 +00:00
|
|
|
}
|
2021-01-15 22:10:19 +00:00
|
|
|
return true;
|
2020-12-30 03:06:50 +00:00
|
|
|
}
|
|
|
|
|
2021-01-04 10:13:31 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2021-02-20 09:33:53 +00:00
|
|
|
private String getCallTarget ()
|
2021-01-12 00:34:29 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
StringBuilder text = new StringBuilder ();
|
|
|
|
|
|
|
|
int ptr = startPtr + 1;
|
|
|
|
int max = startPtr + length - 1;
|
2021-02-20 21:42:40 +00:00
|
|
|
|
2021-01-12 00:34:29 +00:00
|
|
|
while (ptr < max)
|
|
|
|
{
|
2021-01-16 22:59:01 +00:00
|
|
|
byte b = buffer[ptr++];
|
2021-01-18 00:06:17 +00:00
|
|
|
if (isToken (b))
|
2021-01-16 22:59:01 +00:00
|
|
|
text.append (tokens[b & 0x7F]);
|
2021-02-23 10:06:42 +00:00
|
|
|
else if (b == ASCII_COMMA) // end of call target
|
2021-02-20 09:33:53 +00:00
|
|
|
break;
|
2021-01-12 00:34:29 +00:00
|
|
|
else
|
2021-01-16 22:59:01 +00:00
|
|
|
text.append ((char) b);
|
2021-01-12 00:34:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2021-02-20 21:42:40 +00:00
|
|
|
|
2021-01-04 10:13:31 +00:00
|
|
|
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;
|
|
|
|
|
2021-02-23 10:06:42 +00:00
|
|
|
while (ptr < buffer.length && buffer[ptr] != 0 && buffer[ptr] != ASCII_COLON)
|
2020-12-30 03:06:50 +00:00
|
|
|
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)
|
|
|
|
{
|
2021-01-15 22:10:19 +00:00
|
|
|
System.out.printf ("NFE2: %s%n", s);
|
2020-12-30 03:06:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return lineNumbers;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
private int getLineNumber (byte[] buffer, int ptr)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
int lineNumber = 0;
|
2021-01-08 02:03:12 +00:00
|
|
|
|
2021-02-21 08:03:26 +00:00
|
|
|
while (ptr < buffer.length && isDigit (buffer[ptr]))
|
2021-01-08 02:03:12 +00:00
|
|
|
lineNumber = lineNumber * 10 + (buffer[ptr++] & 0xFF) - 0x30;
|
|
|
|
|
2020-12-30 03:06:50 +00:00
|
|
|
return lineNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
boolean isImpliedGoto ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
2021-02-21 08:03:26 +00:00
|
|
|
return (isDigit (buffer[startPtr]));
|
2020-12-30 03:06:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Record the position of the equals sign so it can be aligned with adjacent lines.
|
2021-02-26 02:52:32 +00:00
|
|
|
// Illegal lines could have a variable name with no equals sign.
|
2020-12-30 03:06:50 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2021-02-20 20:50:18 +00:00
|
|
|
private void setEqualsPosition ()
|
2020-12-30 03:06:50 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
2021-02-26 02:52:32 +00:00
|
|
|
int p = startPtr;
|
|
|
|
int max = startPtr + length;
|
|
|
|
|
|
|
|
while (++p < max)
|
|
|
|
if (buffer[p] == TOKEN_EQUALS)
|
|
|
|
{
|
|
|
|
String expandedLine = toString ();
|
|
|
|
equalsPosition = expandedLine.indexOf ('=');
|
|
|
|
endPosition = expandedLine.length ();
|
|
|
|
if (expandedLine.endsWith (":"))
|
|
|
|
endPosition--;
|
|
|
|
break;
|
|
|
|
}
|
2020-12-30 03:06:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
boolean isJoinableRem ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
2021-02-25 05:59:41 +00:00
|
|
|
return is (TOKEN_REM) && isNotFirst ();
|
2020-12-30 03:06:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
boolean isFirst ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
return (parent.linePtr + 4) == startPtr;
|
|
|
|
}
|
|
|
|
|
2021-02-25 05:59:41 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
boolean isNotFirst ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
return !isFirst ();
|
|
|
|
}
|
|
|
|
|
2020-12-30 03:06:50 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
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)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
2021-02-25 22:16:14 +00:00
|
|
|
int ptr = startPtr;
|
2020-12-30 03:06:50 +00:00
|
|
|
int max = startPtr + length;
|
2020-12-31 09:50:53 +00:00
|
|
|
|
2021-02-25 22:16:14 +00:00
|
|
|
while (++ptr < max)
|
|
|
|
if (buffer[ptr] == token)
|
2020-12-30 03:06:50 +00:00
|
|
|
return true;
|
2021-02-25 22:16:14 +00:00
|
|
|
|
2020-12-30 03:06:50 +00:00
|
|
|
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++)
|
2021-01-18 00:06:17 +00:00
|
|
|
if (isToken (buffer[p]))
|
2020-12-30 03:06:50 +00:00
|
|
|
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;
|
2021-01-16 22:59:01 +00:00
|
|
|
int max = startPtr + length - 1;
|
2021-01-21 11:02:45 +00:00
|
|
|
|
|
|
|
if (isFirst ())
|
|
|
|
{
|
2021-02-21 23:24:34 +00:00
|
|
|
if (containsBackspaces (ptr, max)) // probably going to erase the line number
|
|
|
|
{
|
|
|
|
// apple format uses left-justified line numbers so the length varies
|
|
|
|
text.setLength (0);
|
|
|
|
text.append (String.format (" %d REM ", parent.lineNumber)); // mimic apple
|
|
|
|
}
|
|
|
|
else
|
|
|
|
text.append (" REM ");
|
2021-01-21 11:02:45 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
text.append ("REM ");
|
2020-12-30 03:06:50 +00:00
|
|
|
|
2021-01-16 22:59:01 +00:00
|
|
|
while (ptr < max)
|
2020-12-30 03:06:50 +00:00
|
|
|
{
|
2020-12-31 09:50:53 +00:00
|
|
|
switch (buffer[ptr])
|
|
|
|
{
|
2021-02-23 10:06:42 +00:00
|
|
|
case ASCII_BACKSPACE:
|
2020-12-31 09:50:53 +00:00
|
|
|
if (text.length () > 0)
|
|
|
|
text.deleteCharAt (text.length () - 1);
|
|
|
|
break;
|
|
|
|
|
2021-02-23 10:06:42 +00:00
|
|
|
case ASCII_CR:
|
2020-12-31 09:50:53 +00:00
|
|
|
text.append ("\n");
|
|
|
|
break;
|
|
|
|
|
2021-02-23 10:06:42 +00:00
|
|
|
case ASCII_LF:
|
|
|
|
int indent = getIndent (text);
|
2021-01-21 11:02:45 +00:00
|
|
|
text.append ("\n");
|
|
|
|
for (int i = 0; i < indent; i++)
|
|
|
|
text.append (" ");
|
|
|
|
break;
|
|
|
|
|
2020-12-31 09:50:53 +00:00
|
|
|
default:
|
|
|
|
text.append ((char) buffer[ptr]); // do not mask with 0xFF
|
|
|
|
}
|
|
|
|
|
2020-12-30 03:06:50 +00:00
|
|
|
ptr++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-21 23:24:34 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
private boolean containsBackspaces (int ptr, int max)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
while (ptr < max)
|
2021-02-23 10:06:42 +00:00
|
|
|
if (buffer[ptr++] == ASCII_BACKSPACE)
|
2021-02-21 23:24:34 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-30 03:06:50 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2021-02-26 02:52:32 +00:00
|
|
|
public String getAlignedText (Alignment alignment)
|
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-02-26 02:52:32 +00:00
|
|
|
if (alignment.equalsPosition == 0 || is (TOKEN_REM))
|
|
|
|
return line.toString ();
|
|
|
|
|
|
|
|
int alignEqualsPos = alignment.equalsPosition;
|
|
|
|
int targetLength = endPosition - equalsPosition;
|
|
|
|
|
2021-01-03 03:04:24 +00:00
|
|
|
// insert spaces before '=' until it lines up with the other assignment lines
|
2021-02-26 02:52:32 +00:00
|
|
|
while (alignEqualsPos-- > equalsPosition)
|
|
|
|
line.insert (equalsPosition, ' ');
|
2021-02-25 22:16:14 +00:00
|
|
|
|
2021-02-26 02:52:32 +00:00
|
|
|
if (line.charAt (line.length () - 1) == ':')
|
|
|
|
while (targetLength++ <= alignment.targetLength)
|
|
|
|
line.append (" ");
|
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;
|
2021-02-23 10:06:42 +00:00
|
|
|
if (buffer[startPtr + len] == ASCII_COLON || buffer[startPtr + len] == 0)
|
2021-01-03 05:06:51 +00:00
|
|
|
len--;
|
|
|
|
byte[] buffer2 = new byte[len];
|
2020-12-30 03:06:50 +00:00
|
|
|
System.arraycopy (buffer, startPtr + 1, buffer2, 0, buffer2.length);
|
2021-02-20 21:42:40 +00:00
|
|
|
|
2021-01-03 05:06:51 +00:00
|
|
|
return buffer2;
|
2020-12-30 03:06:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
2021-02-20 21:42:40 +00:00
|
|
|
private boolean isToken (byte b)
|
2020-12-30 03:06:50 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
2021-02-21 08:03:26 +00:00
|
|
|
return isHighBitSet (b);
|
2020-12-30 03:06:50 +00:00
|
|
|
}
|
|
|
|
|
2021-01-18 00:06:17 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2021-02-20 21:42:40 +00:00
|
|
|
List<String> getVariables ()
|
2021-01-18 00:06:17 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
2021-02-20 21:42:40 +00:00
|
|
|
return variables;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
List<String> getFunctions ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
return functions;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
List<String> getArrays ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
return arrays;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
List<Integer> getGotoLines ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
return gotoLines;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
List<Integer> getGosubLines ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
return gosubLines;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
List<Integer> getConstantsInt ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
return constantsInt;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
List<Float> getConstantsFloat ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
return constantsFloat;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
List<String> getStringsText ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
return stringsText;
|
2021-01-18 00:06:17 +00:00
|
|
|
}
|
|
|
|
|
2020-12-30 03:06:50 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
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-18 00:06:17 +00:00
|
|
|
if (isToken (b))
|
2020-12-30 03:06:50 +00:00
|
|
|
{
|
|
|
|
if (line.length () > 0 && line.charAt (line.length () - 1) != ' ')
|
|
|
|
line.append (' ');
|
|
|
|
int val = b & 0x7F;
|
2021-01-11 02:01:01 +00:00
|
|
|
if (b != TOKEN_THEN || ApplesoftBasicProgram.basicPreferences.showThen)
|
|
|
|
line.append (ApplesoftConstants.tokens[val] + " ");
|
2020-12-30 03:06:50 +00:00
|
|
|
}
|
2021-02-23 10:06:42 +00:00
|
|
|
// else if (Utility.isControlCharacter (b))
|
|
|
|
// line.append (ApplesoftBasicProgram.basicPreferences.showCaret
|
|
|
|
// ? "^" + (char) (b + 64) : "?");
|
|
|
|
else if (!isControlCharacter (b))
|
2020-12-30 03:06:50 +00:00
|
|
|
line.append ((char) b);
|
|
|
|
}
|
|
|
|
|
|
|
|
return line;
|
|
|
|
}
|
2021-02-20 21:42:40 +00:00
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
@Override
|
|
|
|
public String toString ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
return toStringBuilder ().toString ();
|
|
|
|
}
|
2020-12-30 03:06:50 +00:00
|
|
|
}
|