Skip to content

Commit

Permalink
Implemented lstring, longs, words and bytes functions
Browse files Browse the repository at this point in the history
  • Loading branch information
maccasoft committed Nov 15, 2023
1 parent 1393229 commit 574aea6
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5087,6 +5087,88 @@ void testString() throws Exception {
+ "", compile(text));
}

@Test
void testLString() throws Exception {
String text = ""
+ "PUB main() | a\n"
+ "\n"
+ " a := lstring(\"1234\", 13, 10)\n"
+ "\n"
+ "";

Assertions.assertEquals(""
+ "' Object header (var size 4)\n"
+ "00000 00000 08 00 00 80 Method main @ $00008 (0 parameters, 0 returns)\n"
+ "00004 00004 14 00 00 00 End\n"
+ "' PUB main() | a\n"
+ "00008 00008 01 (stack size)\n"
+ "' a := lstring(\"1234\", 13, 10)\n"
+ "00009 00009 9E 07 06 31 32 LSTRING\n"
+ "0000E 0000E 33 34 0D 0A\n"
+ "00012 00012 F0 VAR_WRITE LONG DBASE+$00000 (short)\n"
+ "00013 00013 04 RETURN\n"
+ "", compile(text));
}

@Test
void testBytesWordsLongs() throws Exception {
String text = ""
+ "PUB main() | a, b, c\n"
+ "\n"
+ " a := bytes(\"1234\", 13, 10)\n"
+ " b := words(\"1234\", 13, 10)\n"
+ " c := longs(\"1234\", 13, 10)\n"
+ "\n"
+ " a := bytes(\"1234\", long 13, 10)\n"
+ " b := words(\"1234\", byte 13, 10)\n"
+ " c := longs(\"1234\", word 13, 10)\n"
+ "\n"
+ "";

Assertions.assertEquals(""
+ "' Object header (var size 4)\n"
+ "00000 00000 08 00 00 80 Method main @ $00008 (0 parameters, 0 returns)\n"
+ "00004 00004 70 00 00 00 End\n"
+ "' PUB main() | a, b, c\n"
+ "00008 00008 03 (stack size)\n"
+ "' a := bytes(\"1234\", 13, 10)\n"
+ "00009 00009 9E 06 31 32 33 BYTES\n"
+ "0000E 0000E 34 0D 0A\n"
+ "00011 00011 F0 VAR_WRITE LONG DBASE+$00000 (short)\n"
+ "' b := words(\"1234\", 13, 10)\n"
+ "00012 00012 9E 0C 31 00 32 WORDS\n"
+ "00017 00017 00 33 00 34 00\n"
+ "0001C 0001C 0D 00 0A 00\n"
+ "00020 00020 F1 VAR_WRITE LONG DBASE+$00001 (short)\n"
+ "' c := longs(\"1234\", 13, 10)\n"
+ "00021 00021 9E 18 31 00 00 LONGS\n"
+ "00026 00026 00 32 00 00 00\n"
+ "0002B 0002B 33 00 00 00 34\n"
+ "00030 00030 00 00 00 0D 00\n"
+ "00035 00035 00 00 0A 00 00\n"
+ "0003A 0003A 00\n"
+ "0003B 0003B F2 VAR_WRITE LONG DBASE+$00002 (short)\n"
+ "' a := bytes(\"1234\", long 13, 10)\n"
+ "0003C 0003C 9E 09 31 32 33 BYTES\n"
+ "00041 00041 34 0D 00 00 00\n"
+ "00046 00046 0A\n"
+ "00047 00047 F0 VAR_WRITE LONG DBASE+$00000 (short)\n"
+ "' b := words(\"1234\", byte 13, 10)\n"
+ "00048 00048 9E 0B 31 00 32 WORDS\n"
+ "0004D 0004D 00 33 00 34 00\n"
+ "00052 00052 0D 0A 00\n"
+ "00055 00055 F1 VAR_WRITE LONG DBASE+$00001 (short)\n"
+ "' c := longs(\"1234\", word 13, 10)\n"
+ "00056 00056 9E 16 31 00 00 LONGS\n"
+ "0005B 0005B 00 32 00 00 00\n"
+ "00060 00060 33 00 00 00 34\n"
+ "00065 00065 00 00 00 0D 00\n"
+ "0006A 0006A 0A 00 00 00\n"
+ "0006E 0006E F2 VAR_WRITE LONG DBASE+$00002 (short)\n"
+ "0006F 0006F 04 RETURN\n"
+ "", compile(text));
}

@Test
void testLookup() throws Exception {
String text = ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,21 @@ void testFunctionArgumentsAssignment() {
+ "", parse(text));
}

@Test
void testFunctionArgumentsOverride() {
String text = "a := function(1, byte 2, word 3)";
Assertions.assertEquals(""
+ "[:=]\n"
+ " +-- [a]\n"
+ " +-- [function]\n"
+ " +-- [1]\n"
+ " +-- [byte]\n"
+ " +-- [2]\n"
+ " +-- [word]\n"
+ " +-- [3]\n"
+ "", parse(text));
}

@Test
void testFunctionExpressionAssignment() {
String text = "a := function1() + function2() * function3()";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,12 @@ For CASE_FAST to compile, the match values/ranges must be unique constants which

<entry name="strsize" insert="strsize(addr)"><![CDATA[<b>STRSIZE(Addr) : Size</b><br/><br/>Count bytes in zero-terminated string at Addr, return string size, not including zero terminator]]></entry>
<entry name="strcomp" insert="strcomp(addra, addrb)"><![CDATA[<b>STRCOMP(AddrA, AddrB) : Match</b><br/><br/>Compare zero-terminated strings at AddrA and AddrB, return -1 if match or 0 if mismatch]]></entry>
<entry name="string" insert="string(&quot;text&quot;,9)"><![CDATA[<b>STRING("Text",9) : StringAddress</b><br/><br/>Compose a zero-terminated string (quoted characters and values 1..255 allowed), return address of string]]></entry>
<entry name="strcopy" insert="STRCOPY(destination, source, max)"><![CDATA[<b>STRCOPY(Destination, Source, Max)</b><br/><br/>Copy a zero-terminated string of up to Max characters from Source to Destination. The copied string will occupy up to Max+1 bytes, including the zero terminator.]]></entry>
<entry name="string" insert="string(&quot;text&quot;)"><![CDATA[<b>STRING("Text",9) : StringAddress</b><br/><br/>Compose a zero-terminated string (quoted characters and values 1..255 allowed), return address of string.]]></entry>
<entry name="lstring" insert="lstring(&quot;text&quot;)"><![CDATA[<b>LSTRING("Hello",0,"Terve",0) : StringAddress</b><br/><br/>Compose a length-headed string (quoted characters and values 0..255), return address of string.]]></entry>
<entry name="bytes" insert="bytes()"><![CDATA[<b>BYTES($80,$09,$77,WORD $1234,LONG -1)</b><br/><br/>Compose a string of bytes, return address of string. WORD/LONG size overrides allowed.]]></entry>
<entry name="words" insert="words()"><![CDATA[<b>WORDS(1_000,10_000,50_000,LONG $12345678)</b><br/><br/>Compose a string of words, return address of string. BYTE/LONG size overrides allowed.]]></entry>
<entry name="longs" insert="longs()"><![CDATA[<b>LONGS(1e-6,1e-3,1.0,1e3,1e6,-50,BYTE $FF)</b><br/><br/>Compose a string of longs, return address of string. BYTE/WORD size overrides allowed.]]></entry>

<entry name="lookup" insert="lookup(index: v1, v2..v3, etc)"><![CDATA[<b>LOOKUP(Index: v1, v2..v3, etc) : Value</b><br/><br/>Lookup value (values and ranges allowed) using 1-based index, return value (0 if index out of range)]]></entry>
<entry name="lookupz" insert="lookupz(index: v1, v2..v3, etc)"><![CDATA[<b>LOOKUPZ(Index: v1, v2..v3, etc) : Value</b><br/><br/>Lookup value (values and ranges allowed) using 0-based index, return value (0 if index out of range)]]></entry>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,30 @@ public Number getNumber() {
throw new RuntimeException("Not a number.");
}

public byte[] getByte() {
return new byte[] {
getNumber().byteValue()
};
}

public byte[] getWord() {
int value = getNumber().intValue();
return new byte[] {
(byte) (value & 0xFF),
(byte) ((value >> 8) & 0xFF)
};
}

public byte[] getLong() {
int value = getNumber().intValue();
return new byte[] {
(byte) (value & 0xFF),
(byte) ((value >> 8) & 0xFF),
(byte) ((value >> 16) & 0xFF),
(byte) ((value >> 24) & 0xFF)
};
}

public boolean isString() {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import com.maccasoft.propeller.CompilerException;
Expand Down Expand Up @@ -323,32 +324,52 @@ else if (arg.getType() == Token.STRING) {
source.add(new Bytecode(context, 0x23, "LOOKDONE"));
source.add(end);
}
else if ("STRING".equalsIgnoreCase(node.getText())) {
StringBuilder sb = new StringBuilder();
else if ("STRING".equalsIgnoreCase(node.getText()) || "LSTRING".equalsIgnoreCase(node.getText())) {
ByteArrayOutputStream sb = new ByteArrayOutputStream();
for (Spin2StatementNode child : node.getChilds()) {
if (child.getType() == Token.STRING) {
String s = child.getText().substring(1);
sb.append(s.substring(0, s.length() - 1));
sb.write(s.substring(0, s.length() - 1).getBytes());
}
else if (child.getType() == Token.NUMBER) {
NumberLiteral expression = new NumberLiteral(child.getText());
sb.append((char) expression.getNumber().intValue());
sb.write((byte) expression.getNumber().intValue());
}
else {
try {
Expression expression = buildConstantExpression(context, child);
if (!expression.isConstant()) {
throw new CompilerException("expression is not constant", child.getToken());
}
sb.append((char) expression.getNumber().intValue());
sb.write((byte) expression.getNumber().intValue());
} catch (Exception e) {
throw new CompilerException("expression is not constant", child.getToken());
}
}
}
sb.append((char) 0x00);
if (sb.size() > 254) {
logMessage(new CompilerException(CompilerException.ERROR, "string data cannot exceed 254 bytes", node.getTokens()));
}
if ("STRING".equalsIgnoreCase(node.getText())) {
sb.write(0x00);
}
byte[] code = sb.toByteArray();

ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write(0x9E);
if ("LSTRING".equalsIgnoreCase(node.getText())) {
os.write(code.length + 1);
}
os.write(code.length);
os.writeBytes(code);
source.add(new Bytecode(context, os.toByteArray(), node.getText().toUpperCase()));
}
else if (("BYTES".equalsIgnoreCase(node.getText()) || "WORDS".equalsIgnoreCase(node.getText()) || "LONGS".equalsIgnoreCase(node.getText())) && node.getChildCount() != 0) {
byte[] code = compileTypes(context, node, node.getText().substring(0, node.getText().length() - 1));
if (code.length > 255) {
logMessage(new CompilerException(CompilerException.ERROR, "data cannot exceed 255 bytes", node.getTokens()));
}

byte[] code = sb.toString().getBytes();
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write(0x9E);
os.write(code.length);
Expand Down Expand Up @@ -2273,4 +2294,80 @@ else if ("!".equalsIgnoreCase(node.getText())) {
}
}

byte[] compileTypes(Context context, Spin2StatementNode node, String type) throws Exception {
ByteArrayOutputStream os = new ByteArrayOutputStream();

Iterator<Spin2StatementNode> iter = node.getChilds().iterator();
while (iter.hasNext()) {
Spin2StatementNode child = iter.next();

String overrideType = type;
if ("BYTE".equalsIgnoreCase(child.getText()) || "WORD".equalsIgnoreCase(child.getText()) || "LONG".equalsIgnoreCase(child.getText())) {
overrideType = child.getText();
if (child.getChildCount() != 1) {
throw new CompilerException("syntax error", child.getToken());
}
child = child.getChild(0);
}
else if ("BYTES".equalsIgnoreCase(child.getText()) || "WORDS".equalsIgnoreCase(child.getText()) || "LONGS".equalsIgnoreCase(child.getText())) {
type = overrideType = child.getText().substring(0, child.getText().length() - 1);
if (child.getChildCount() != 1) {
throw new CompilerException("syntax error", child.getToken());
}
child = child.getChild(0);
}

if (child.getType() == Token.STRING) {
String s = child.getText().substring(1);
byte[] b = s.substring(0, s.length() - 1).getBytes();
for (int i = 0; i < b.length; i++) {
os.write(b[i]);
if ("WORD".equalsIgnoreCase(overrideType)) {
os.write(0x00);
}
else if ("LONG".equalsIgnoreCase(overrideType)) {
os.write(0x00);
os.write(0x00);
os.write(0x00);
}
}
}
else {
Expression expression;
if (child.getType() == Token.NUMBER) {
expression = new NumberLiteral(child.getText());
}
else {
try {
expression = buildConstantExpression(context, child);
if (!expression.isConstant()) {
throw new CompilerException("expression is not constant", child.getTokens());
}
} catch (CompilerException e) {
throw e;
} catch (Exception e) {
throw new CompilerException("expression is not constant", child.getTokens());
}
}
if ("BYTE".equalsIgnoreCase(overrideType)) {
if (expression.getNumber().intValue() < -0x80 || expression.getNumber().intValue() > 0xFF) {
logMessage(new CompilerException(CompilerException.WARNING, "byte value range from -$80 to $FF", child.getTokens()));
}
os.write(expression.getByte());
}
else if ("WORD".equalsIgnoreCase(overrideType)) {
if (expression.getNumber().intValue() < -0x8000 || expression.getNumber().intValue() > 0xFFFF) {
logMessage(new CompilerException(CompilerException.WARNING, "word value range from -$8000 to $FFFF", child.getTokens()));
}
os.write(expression.getWord());
}
else if ("LONG".equalsIgnoreCase(overrideType)) {
os.write(expression.getLong());
}
}
}

return os.toByteArray();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ public Argument(Spin2StatementNode node) {
this.data = node.data;
this.keyedData = node.keyedData;
}

public Argument(Token token) {
super(token);
}
}

public Spin2StatementNode(Token token) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ public class Spin2TokenMarker extends SourceTokenMarker {
keywords.put("STRCOMP", TokenId.FUNCTION);
keywords.put("STRCOPY", TokenId.FUNCTION);
keywords.put("STRING", TokenId.FUNCTION);
keywords.put("LSTRING", TokenId.FUNCTION);
keywords.put("BYTES", TokenId.FUNCTION);
keywords.put("WORDS", TokenId.FUNCTION);
keywords.put("LONGS", TokenId.FUNCTION);
keywords.put("GETCRC", TokenId.FUNCTION);

keywords.put("LOOKUP", TokenId.FUNCTION);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,11 @@ else if (peek().type != Token.OPERATOR && peek().getText().startsWith(".")) {
}
}
}
else if ("byte".equalsIgnoreCase(node.getText()) || "word".equalsIgnoreCase(node.getText()) || "long".equalsIgnoreCase(node.getText())) {
node.addChild(parseLevel(parseAtom(), 0, false));
return node;
}

if (postEffect.contains(peek().getText())) {
Token postToken = peek();
if (!"?".equals(postToken.getText()) || postToken.start == (token.stop + 1)) {
Expand Down

0 comments on commit 574aea6

Please sign in to comment.