cache;
17 |
18 | public FormatterCache() {
19 | cache = new Object2ObjectOpenHashMap<>();
20 | }
21 |
22 | public IFormatter get(String format) {
23 | return cache.computeIfAbsent(format, Formatter::getFormatter);
24 | }
25 | }
26 |
27 | public interface IFormatter {
28 | String format(Object o);
29 | boolean supportsNumeric();
30 | boolean supportsString();
31 | }
32 |
33 | /**
34 | * Examples:
35 | *
36 | * **$##.## formats -21.2 to -$**21.20
37 | *
38 | *
39 | * '#' specifies 1 digit position.
40 | *
41 | * '.' specifies decimal point.
42 | *
43 | * ',' adds comma in formatted number.
44 | *
45 | * First optional prefix:
46 | * '+' prefix will add a sign prefix.
47 | * '-' prefix will add a minus prefix for negative number.
48 | *
49 | * Next optional prefix:
50 | * '**' causes leading spaces to be filled with '*' and specifies 2 more digit positions.
51 | * '**$' adds dollar prefix, causes leading spaces to be filled with '*' and
52 | * specifies 2 more digit positions.
53 | * '$$' add dollar prefix and specifies 1 more difit position.
54 | *
55 | * First optional suffix:
56 | * '+' suffix will add a sign suffix.
57 | * '-' suffix will add a minus suffix for negative number.
58 | *
59 | * Next optional suffix:
60 | * '^^^^' suffix indicates scientific notation.
61 | *
62 | *
63 | */
64 | public static final class NumberFormatter implements IFormatter {
65 | private final DecimalFormat decimalFormat;
66 | private final boolean scientific;
67 | private final boolean signPrefix;
68 | private final boolean signSuffix;
69 | private final boolean minusSuffix;
70 | private final boolean shouldFill;
71 | private final boolean dollar;
72 |
73 | public NumberFormatter(String format) {
74 | // Handle prefix '+' or '-'
75 | if (format.startsWith("+")) {
76 | signPrefix = true;
77 | format = format.substring(1);
78 | } else if (format.startsWith("-")) {
79 | signPrefix = false;
80 | format = format.substring(1);
81 | } else {
82 | signPrefix = false;
83 | }
84 |
85 | // Handle suffix '+' or '-'
86 | if (format.endsWith("+")) {
87 | signSuffix = true;
88 | minusSuffix = false;
89 | format = format.substring(0, format.length() - 1);
90 | } else if (format.endsWith("-")) {
91 | signSuffix = false;
92 | minusSuffix = true;
93 | format = format.substring(0, format.length() - 1);
94 | } else {
95 | signSuffix = false;
96 | minusSuffix = false;
97 | }
98 |
99 | // Handle scientific notation
100 | if (format.endsWith("^^^^")) {
101 | format = format.replace("^^^^", "E00");
102 | scientific = true;
103 | } else {
104 | scientific = false;
105 | }
106 |
107 | // Replace # with 0 so that result is filled with 0 prefixes
108 | format = format.replace("#", "0");
109 |
110 | // Handle **, **$, and $$ prefix
111 | boolean dollar = false;
112 | int numToFill = 0;
113 | int removeFromFormat = 0;
114 | if (format.startsWith("**$")) {
115 | numToFill = 2;
116 | removeFromFormat = 3;
117 | dollar = true;
118 | } else if (format.startsWith("**")) {
119 | numToFill = 2;
120 | removeFromFormat = 2;
121 | } else if (format.startsWith("$$")) {
122 | numToFill = 1;
123 | removeFromFormat = 2;
124 | dollar = true;
125 | }
126 | if (removeFromFormat > 0) {
127 | format = format.substring(removeFromFormat);
128 | }
129 | this.dollar = dollar;
130 | this.shouldFill = numToFill > 0;
131 |
132 | if (numToFill > 0) {
133 | var b = new byte[numToFill];
134 | Arrays.fill(b, 0, numToFill, (byte) '0');
135 | format = new String(b) + format;
136 | }
137 |
138 | this.decimalFormat = new DecimalFormat(format);
139 | }
140 |
141 | @Override
142 | public String format(Object o) {
143 | if (o instanceof Long) {
144 | return format((long) o);
145 | } else if (o instanceof Double) {
146 | return format((double) o);
147 | } else {
148 | throw new PuffinBasicInternalError(
149 | NumberFormatter.class.getSimpleName()
150 | + ": data type mismatch: " + o.getClass()
151 | );
152 | }
153 | }
154 |
155 | @Override
156 | public boolean supportsNumeric() {
157 | return true;
158 | }
159 |
160 | @Override
161 | public boolean supportsString() {
162 | return false;
163 | }
164 |
165 | public String format(long value) {
166 | boolean isNegative = value < 0;
167 | if (isNegative) {
168 | value = -value;
169 | }
170 | return format(decimalFormat.format(value), isNegative);
171 | }
172 |
173 | public String format(double value) {
174 | boolean isNegative = value < 0;
175 | if (isNegative) {
176 | value = -value;
177 | }
178 | return format(decimalFormat.format(value), isNegative);
179 | }
180 |
181 | private String format(String result, boolean isNegative) {
182 | // Handle scientific
183 | if (scientific) {
184 | if (!result.contains("E-")) {
185 | result = result.replace("E", "E+");
186 | }
187 | }
188 |
189 | // If ** or **$ is set, replace leading 0s with *s.
190 | // If ** or **$ is not set, remove leading 0s.
191 | var dest = new byte[result.length()];
192 | boolean checkForLeadingZero = true;
193 | int fillToLoc = -1;
194 | for (int i = 0; i < result.length(); i++) {
195 | var c = result.charAt(i);
196 | if ((c >= '1' && c <= '9')) {
197 | checkForLeadingZero = false;
198 | }
199 | if (checkForLeadingZero) {
200 | if (c == '0') {
201 | c = '*';
202 | fillToLoc = i;
203 | }
204 | }
205 | // Copy char to dest
206 | dest[i] = (byte) c;
207 | }
208 | if (fillToLoc >= 0) {
209 | result = shouldFill ? new String(dest) : new String(dest).substring(fillToLoc + 1);
210 | if (result.startsWith(",")) {
211 | result = result.substring(1);
212 | }
213 | }
214 |
215 | // Add $ prefix
216 | if (dollar) {
217 | result = '$' + result;
218 | }
219 | // Add sign prefix
220 | if (signPrefix) {
221 | result = (isNegative ? '-' : '+') + result;
222 | }
223 | // Add minus prefix
224 | if (isNegative && !minusSuffix && !result.startsWith("-")) {
225 | result = '-' + result;
226 | }
227 | // Add sign suffix
228 | if (signSuffix) {
229 | result = result + (isNegative ? '-' : '+');
230 | }
231 | // Add minus suffix
232 | if (isNegative && minusSuffix) {
233 | result = result + '-';
234 | }
235 |
236 | return result;
237 | }
238 | }
239 |
240 | public static final class FirstCharFormatter implements IFormatter {
241 | @Override
242 | public String format(Object o) {
243 | if (o instanceof String) {
244 | var str = (String) o;
245 | return str.isEmpty() ? "" : str.substring(0, 1);
246 | } else {
247 | throw new PuffinBasicInternalError(
248 | FirstCharFormatter.class.getSimpleName()
249 | + ": data type mismatch: " + o.getClass()
250 | );
251 | }
252 | }
253 |
254 | @Override
255 | public boolean supportsNumeric() {
256 | return false;
257 | }
258 |
259 | @Override
260 | public boolean supportsString() {
261 | return true;
262 | }
263 | }
264 |
265 | public static final class NSpacesFormatter implements IFormatter {
266 | private final int length;
267 |
268 | public NSpacesFormatter(String format) {
269 | if (format.length() >= 2) {
270 | length = format.length();
271 | var spaces = format.substring(1, format.length() - 1);
272 | for (int i = 0; i < spaces.length(); i++) {
273 | if (spaces.charAt(i) != ' ') {
274 | throw new PuffinBasicRuntimeError(
275 | ILLEGAL_FUNCTION_PARAM,
276 | "Expected spaces in n+2 spaces formatter, but found: " + spaces.charAt(i)
277 | );
278 | }
279 | }
280 | } else {
281 | throw new PuffinBasicRuntimeError(
282 | ILLEGAL_FUNCTION_PARAM,
283 | "Bad n+2 formatter string: " + format
284 | );
285 | }
286 | }
287 |
288 | @Override
289 | public String format(Object o) {
290 | if (o instanceof String) {
291 | var str = (String) o;
292 | var strlen = str.length();
293 | if (strlen > this.length) {
294 | return str.substring(0, this.length);
295 | } else {
296 | byte[] bytes = new byte[this.length];
297 | System.arraycopy(str.getBytes(), 0, bytes, 0, str.length());
298 | java.util.Arrays.fill(bytes, str.length(), length, (byte) ' ');
299 | return new String(bytes);
300 | }
301 | } else {
302 | throw new PuffinBasicInternalError(
303 | FirstCharFormatter.class.getSimpleName()
304 | + ": data type mismatch: " + o.getClass()
305 | );
306 | }
307 | }
308 |
309 | @Override
310 | public boolean supportsNumeric() {
311 | return false;
312 | }
313 |
314 | @Override
315 | public boolean supportsString() {
316 | return true;
317 | }
318 | }
319 |
320 | public static final class VarLenStringFormatter implements IFormatter {
321 | @Override
322 | public String format(Object o) {
323 | if (o instanceof String) {
324 | return (String) o;
325 | } else {
326 | throw new PuffinBasicInternalError(
327 | FirstCharFormatter.class.getSimpleName()
328 | + ": data type mismatch: " + o.getClass()
329 | );
330 | }
331 | }
332 |
333 | @Override
334 | public boolean supportsNumeric() {
335 | return false;
336 | }
337 |
338 | @Override
339 | public boolean supportsString() {
340 | return true;
341 | }
342 | }
343 |
344 | public static IFormatter getFormatter(String format) {
345 | if (format.equals("!")) {
346 | return new FirstCharFormatter();
347 | } else if (format.equals("&")) {
348 | return new VarLenStringFormatter();
349 | } else if (format.startsWith("\\") && format.endsWith("\\")) {
350 | return new NSpacesFormatter(format);
351 | } else {
352 | return new NumberFormatter(format);
353 | }
354 | }
355 |
356 | public static String printFormatInt32(int value) {
357 | return value < 0 ? value + " " : " " + value + " ";
358 | }
359 |
360 | public static String printFormatInt64(long value) {
361 | return value < 0 ? value + " " : " " + value + " ";
362 | }
363 |
364 | public static String printFormatFloat32(float value) {
365 | return value < 0 ? value + " " : " " + value + " ";
366 | }
367 |
368 | public static String printFormatFloat64(double value) {
369 | return value < 0 ? value + " " : " " + value + " ";
370 | }
371 |
372 | public static String printFormatString(String value) {
373 | return value;
374 | }
375 |
376 | public static String writeFormatInt32(int value) {
377 | return String.valueOf(value);
378 | }
379 |
380 | public static String writeFormatInt64(long value) {
381 | return String.valueOf(value);
382 | }
383 |
384 | public static String writeFormatFloat32(float value) {
385 | return String.valueOf(value);
386 | }
387 |
388 | public static String writeFormatFloat64(double value) {
389 | return String.valueOf(value);
390 | }
391 |
392 | public static String writeFormatString(String value) {
393 | if (value.contains("\"")) {
394 | // Expects unescaped quotes
395 | value = value.replaceAll("\"", "\\\"");
396 | }
397 | return "\"" + value + "\"";
398 | }
399 | }
400 |
--------------------------------------------------------------------------------
/src/main/java/org/puffinbasic/runtime/Numbers.java:
--------------------------------------------------------------------------------
1 | package org.puffinbasic.runtime;
2 |
3 | import org.puffinbasic.error.PuffinBasicSemanticError;
4 |
5 | import java.util.function.Supplier;
6 |
7 | import static org.puffinbasic.error.PuffinBasicSemanticError.ErrorCode.BAD_NUMBER;
8 |
9 | public class Numbers {
10 |
11 | public static int parseInt32(String value, Supplier lineSupplier) {
12 | try {
13 | return Integer.parseInt(value);
14 | } catch (NumberFormatException e) {
15 | throw new PuffinBasicSemanticError(
16 | BAD_NUMBER,
17 | lineSupplier.get(),
18 | "Failed to parse number as int32: " + value
19 | );
20 | }
21 | }
22 |
23 | public static int parseInt32(String value, int base, Supplier lineSupplier) {
24 | try {
25 | return Integer.parseInt(value, base);
26 | } catch (NumberFormatException e) {
27 | throw new PuffinBasicSemanticError(
28 | BAD_NUMBER,
29 | lineSupplier.get(),
30 | "Failed to parse number as int32: " + value
31 | );
32 | }
33 | }
34 |
35 | public static long parseInt64(String value, Supplier lineSupplier) {
36 | try {
37 | return Long.parseLong(value);
38 | } catch (NumberFormatException e) {
39 | throw new PuffinBasicSemanticError(
40 | BAD_NUMBER,
41 | lineSupplier.get(),
42 | "Failed to parse number as int64: " + value
43 | );
44 | }
45 | }
46 |
47 | public static long parseInt64(String value, int base, Supplier lineSupplier) {
48 | try {
49 | return Long.parseLong(value, base);
50 | } catch (NumberFormatException e) {
51 | throw new PuffinBasicSemanticError(
52 | BAD_NUMBER,
53 | lineSupplier.get(),
54 | "Failed to parse number as int64: " + value
55 | );
56 | }
57 | }
58 |
59 | public static float parseFloat32(String value, Supplier lineSupplier) {
60 | try {
61 | return Float.parseFloat(value);
62 | } catch (NumberFormatException e) {
63 | throw new PuffinBasicSemanticError(
64 | BAD_NUMBER,
65 | lineSupplier.get(),
66 | "Failed to parse number as float32: " + value
67 | );
68 | }
69 | }
70 |
71 | public static double parseFloat64(String value, Supplier lineSupplier) {
72 | try {
73 | return Double.parseDouble(value);
74 | } catch (NumberFormatException e) {
75 | throw new PuffinBasicSemanticError(
76 | BAD_NUMBER,
77 | lineSupplier.get(),
78 | "Failed to parse number as float64: " + value
79 | );
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/org/puffinbasic/runtime/PrintBuffer.java:
--------------------------------------------------------------------------------
1 | package org.puffinbasic.runtime;
2 |
3 | import it.unimi.dsi.fastutil.bytes.ByteArrayList;
4 | import it.unimi.dsi.fastutil.bytes.ByteList;
5 | import org.puffinbasic.file.PuffinBasicFile;
6 |
7 | public class PrintBuffer {
8 |
9 | private static final byte SPACE = (byte) ' ';
10 | private final ByteList buffer;
11 | private int cursor;
12 |
13 | public PrintBuffer() {
14 | this.buffer = new ByteArrayList();
15 | }
16 |
17 | public void appendAtCursor(String value) {
18 | for (int i = buffer.size(); i < cursor + value.length(); i++) {
19 | buffer.add(SPACE);
20 | }
21 | for (int i = 0; i < value.length(); i++) {
22 | buffer.set(cursor++, (byte) value.charAt(i));
23 | }
24 | }
25 |
26 | public void flush(PuffinBasicFile file) {
27 | for (int i = 0; i < buffer.size(); i++) {
28 | file.writeByte(buffer.getByte(i));
29 | }
30 | for (int i = 0; i < buffer.size(); i++) {
31 | buffer.set(i, SPACE);
32 | }
33 | buffer.clear();
34 | cursor = 0;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/org/puffinbasic/runtime/SoundState.java:
--------------------------------------------------------------------------------
1 | package org.puffinbasic.runtime;
2 |
3 | import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
4 | import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
5 | import org.puffinbasic.error.PuffinBasicRuntimeError;
6 |
7 | import javax.sound.sampled.AudioInputStream;
8 | import javax.sound.sampled.AudioSystem;
9 | import javax.sound.sampled.Clip;
10 | import javax.sound.sampled.DataLine;
11 | import java.io.File;
12 | import java.util.concurrent.ExecutorService;
13 | import java.util.concurrent.Executors;
14 | import java.util.concurrent.atomic.AtomicInteger;
15 |
16 | import static org.puffinbasic.error.PuffinBasicRuntimeError.ErrorCode.ILLEGAL_FUNCTION_PARAM;
17 | import static org.puffinbasic.error.PuffinBasicRuntimeError.ErrorCode.IO_ERROR;
18 |
19 | public class SoundState implements AutoCloseable {
20 |
21 | private static final class ClipState implements AutoCloseable {
22 | final AudioInputStream stream;
23 | final Clip clip;
24 |
25 | ClipState(AudioInputStream stream, Clip clip) {
26 | this.stream = stream;
27 | this.clip = clip;
28 | }
29 |
30 | @Override
31 | public void close() throws Exception {
32 | clip.close();
33 | stream.close();
34 | }
35 | }
36 |
37 | private final ExecutorService executor;
38 | private final AtomicInteger counter;
39 | private final Int2ObjectMap state;
40 |
41 | SoundState() {
42 | this.executor = Executors.newSingleThreadExecutor();
43 | this.counter = new AtomicInteger();
44 | this.state = new Int2ObjectOpenHashMap<>();
45 | }
46 |
47 | public int load(String file) {
48 | var future = executor.submit(() -> {
49 | var audioFile = new File(file);
50 | final AudioInputStream audioStream;
51 | try {
52 | audioStream = AudioSystem.getAudioInputStream(audioFile);
53 | } catch (Exception e) {
54 | throw new PuffinBasicRuntimeError(
55 | IO_ERROR,
56 | "Failed to load audio file: " + file + ", error: " + e.getMessage()
57 | );
58 | }
59 | var format = audioStream.getFormat();
60 | var info = new DataLine.Info(Clip.class, format);
61 | final Clip clip;
62 | try {
63 | clip = (Clip) AudioSystem.getLine(info);
64 | clip.open(audioStream);
65 | } catch (Exception e) {
66 | throw new PuffinBasicRuntimeError(
67 | IO_ERROR,
68 | "Failed to get/open line from audio: " + file + ", error: " + e.getMessage()
69 | );
70 | }
71 | var id = counter.incrementAndGet();
72 | state.put(id, new ClipState(audioStream, clip));
73 | return id;
74 | });
75 | try {
76 | return future.get();
77 | } catch (Exception e) {
78 | throw new PuffinBasicRuntimeError(
79 | IO_ERROR,
80 | "Failed to get id from loaded audio: " + file + ", error: " + e.getMessage()
81 | );
82 | }
83 | }
84 |
85 | private ClipState get(int id) {
86 | var s = state.get(id);
87 | if (s == null) {
88 | throw new PuffinBasicRuntimeError(
89 | ILLEGAL_FUNCTION_PARAM,
90 | "Failed to get sound clip for id: " + id
91 | );
92 | }
93 | return s;
94 | }
95 |
96 | public void play(int id) {
97 | executor.submit(() -> {
98 | var clip = get(id).clip;
99 | if (clip.isRunning()) {
100 | clip.stop();
101 | }
102 | clip.setFramePosition(0);
103 | clip.start();
104 | });
105 | }
106 |
107 | public void stop(int id) {
108 | executor.submit(() -> {
109 | var clip = get(id).clip;
110 | if (clip.isRunning()) {
111 | clip.stop();
112 | }
113 | });
114 | }
115 |
116 | public void loop(int id) {
117 | executor.submit(() -> {
118 | var clip = get(id).clip;
119 | if (clip.isRunning()) {
120 | clip.stop();
121 | }
122 | clip.setFramePosition(0);
123 | clip.loop(Clip.LOOP_CONTINUOUSLY);
124 | });
125 | }
126 |
127 | @Override
128 | public void close() {
129 | state.values().forEach(s -> s.clip.close());
130 | executor.shutdownNow();
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/main/java/org/puffinbasic/runtime/Types.java:
--------------------------------------------------------------------------------
1 | package org.puffinbasic.runtime;
2 |
3 | import org.puffinbasic.domain.PuffinBasicSymbolTable;
4 | import org.puffinbasic.domain.STObjects;
5 | import org.puffinbasic.domain.STObjects.ArrayType;
6 | import org.puffinbasic.domain.STObjects.PuffinBasicAtomTypeId;
7 | import org.puffinbasic.domain.STObjects.STLValue;
8 | import org.puffinbasic.error.PuffinBasicRuntimeError;
9 | import org.puffinbasic.error.PuffinBasicSemanticError;
10 | import org.puffinbasic.parser.PuffinBasicIR;
11 | import org.puffinbasic.parser.PuffinBasicIR.Instruction;
12 |
13 | import java.util.function.Supplier;
14 |
15 | import static org.puffinbasic.domain.STObjects.PuffinBasicAtomTypeId.INT32;
16 | import static org.puffinbasic.domain.STObjects.PuffinBasicAtomTypeId.INT64;
17 | import static org.puffinbasic.domain.STObjects.PuffinBasicAtomTypeId.STRING;
18 | import static org.puffinbasic.domain.STObjects.PuffinBasicTypeId.ARRAY;
19 | import static org.puffinbasic.domain.STObjects.PuffinBasicTypeId.SCALAR;
20 | import static org.puffinbasic.error.PuffinBasicRuntimeError.ErrorCode.BAD_FIELD;
21 | import static org.puffinbasic.error.PuffinBasicSemanticError.ErrorCode.DATA_TYPE_MISMATCH;
22 |
23 | public class Types {
24 |
25 | public static void copy(PuffinBasicSymbolTable symbolTable, Instruction instruction) {
26 | var fromEntry = symbolTable.get(instruction.op1);
27 | var toEntry = symbolTable.get(instruction.op2);
28 | toEntry.getValue().assign(fromEntry.getValue());
29 | }
30 |
31 | public static void paramCopy(PuffinBasicSymbolTable symbolTable, Instruction instruction) {
32 | var fromEntry = symbolTable.get(instruction.op1);
33 | var toEntry = symbolTable.get(instruction.op2);
34 | if (toEntry.getType().getTypeId() == SCALAR) {
35 | toEntry.getValue().assign(fromEntry.getValue());
36 | } else if (toEntry.isLValue()) {
37 | ((STLValue) toEntry).setValue(fromEntry.getValue());
38 | } else {
39 | throw new PuffinBasicRuntimeError(
40 | BAD_FIELD,
41 | "Expected LValue, but found: " + toEntry.getType()
42 | );
43 | }
44 | }
45 |
46 | public static void varref(PuffinBasicSymbolTable symbolTable, Instruction instruction) {
47 | var src = symbolTable.get(instruction.op1);
48 | var dst = symbolTable.get(instruction.op2);
49 | if (dst.isLValue()) {
50 | ((STLValue) dst).setValue(src.getValue());
51 | } else {
52 | throw new PuffinBasicRuntimeError(
53 | BAD_FIELD,
54 | "Expected LValue, but found: " + dst.getType()
55 | );
56 | }
57 | }
58 |
59 | public static String unquote(String txt) {
60 | if (txt == null || txt.isEmpty()) {
61 | return txt;
62 | } else {
63 | if (txt.length() > 1 && txt.charAt(0) == '"' && txt.charAt(txt.length() - 1) == '"') {
64 | return txt.substring(1, txt.length() - 1);
65 | } else {
66 | return "";
67 | }
68 | }
69 | }
70 |
71 | public static void assertString(
72 | PuffinBasicAtomTypeId dt, Supplier line
73 | ) {
74 | if (dt != STRING) {
75 | throw new PuffinBasicSemanticError(
76 | DATA_TYPE_MISMATCH,
77 | line.get(),
78 | "Expected String type but found: " + dt
79 | );
80 | }
81 | }
82 |
83 | public static void assertNumeric(
84 | PuffinBasicAtomTypeId dt, Supplier line
85 | ) {
86 | if (dt == STRING) {
87 | throw new PuffinBasicSemanticError(
88 | DATA_TYPE_MISMATCH,
89 | line.get(),
90 | "Expected numeric type but found String!"
91 | );
92 | }
93 | }
94 |
95 | public static void assertIntType(
96 | PuffinBasicAtomTypeId dt, Supplier line
97 | ) {
98 | if (dt != INT32 && dt !=INT64) {
99 | throw new PuffinBasicSemanticError(
100 | DATA_TYPE_MISMATCH,
101 | line.get(),
102 | "Expected int type but found: " + dt
103 | );
104 | }
105 | }
106 |
107 | public static void assertNumeric(
108 | PuffinBasicAtomTypeId dt1, PuffinBasicAtomTypeId dt2, Supplier line
109 | ) {
110 | if (dt1 == STRING || dt2 == STRING) {
111 | throw new PuffinBasicSemanticError(
112 | DATA_TYPE_MISMATCH,
113 | line.get(),
114 | "Expected numeric type but found String!"
115 | );
116 | }
117 | }
118 |
119 | public static void assertBothStringOrNumeric(
120 | PuffinBasicAtomTypeId dt1, PuffinBasicAtomTypeId dt2, Supplier line
121 | ) {
122 | if ((dt1 != STRING || dt2 != STRING)
123 | && (dt1 == STRING || dt2 == STRING))
124 | {
125 | throw new PuffinBasicSemanticError(
126 | DATA_TYPE_MISMATCH,
127 | line.get(),
128 | "Expected either both numeric or both string type but found: "
129 | + dt1 + " and " + dt2
130 | );
131 | }
132 | }
133 |
134 | public static PuffinBasicAtomTypeId upcast(
135 | PuffinBasicAtomTypeId dt1 , PuffinBasicAtomTypeId dt2, Supplier line
136 | ) {
137 | assertNumeric(dt1, dt2, line);
138 | if (dt1 == PuffinBasicAtomTypeId.DOUBLE || dt2 == PuffinBasicAtomTypeId.DOUBLE) {
139 | return PuffinBasicAtomTypeId.DOUBLE;
140 | } else if (dt1 == PuffinBasicAtomTypeId.INT64 || dt2 == PuffinBasicAtomTypeId.INT64) {
141 | return PuffinBasicAtomTypeId.INT64;
142 | } else if (dt1 == PuffinBasicAtomTypeId.FLOAT || dt2 == PuffinBasicAtomTypeId.FLOAT) {
143 | return PuffinBasicAtomTypeId.FLOAT;
144 | } else {
145 | return PuffinBasicAtomTypeId.INT32;
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/test/java/org/puffinbasic/IntegrationTest.java:
--------------------------------------------------------------------------------
1 | package org.puffinbasic;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 | import org.puffinbasic.PuffinBasicInterpreterMain.UserOptions;
6 | import org.puffinbasic.error.PuffinBasicRuntimeError;
7 | import org.puffinbasic.runtime.Environment;
8 | import org.puffinbasic.runtime.Environment.SystemEnv;
9 |
10 | import java.io.BufferedInputStream;
11 | import java.io.ByteArrayOutputStream;
12 | import java.io.IOException;
13 | import java.io.InputStream;
14 | import java.io.PrintStream;
15 | import java.net.URL;
16 | import java.nio.file.Files;
17 | import java.nio.file.Path;
18 | import java.time.Instant;
19 |
20 | import static org.junit.Assert.assertEquals;
21 | import static org.puffinbasic.PuffinBasicInterpreterMain.interpretAndRun;
22 | import static org.puffinbasic.error.PuffinBasicRuntimeError.ErrorCode.IO_ERROR;
23 |
24 | public class IntegrationTest {
25 |
26 | private Environment env;
27 |
28 | @Before
29 | public void setup() {
30 | env = new SystemEnv();
31 | }
32 |
33 | @Test
34 | public void testForLoop() {
35 | runTest("forloop.bas", "forloop.bas.output");
36 | }
37 |
38 | @Test
39 | public void testNestedForLoop() {
40 | runTest("nested_forloop.bas", "nested_forloop.bas.output");
41 | }
42 |
43 | @Test
44 | public void testScalarVariable() {
45 | runTest("scalar_var.bas", "scalar_var.bas.output");
46 | }
47 |
48 | @Test
49 | public void testArrayVariable() {
50 | runTest("array_var.bas", "array_var.bas.output");
51 | }
52 |
53 | @Test
54 | public void testArrayCopy() {
55 | runTest("array_copy.bas", "array_copy.bas.output");
56 | }
57 |
58 | @Test
59 | public void testArrayFunc() {
60 | runTest("array_func.bas", "array_func.bas.output");
61 | }
62 |
63 | @Test
64 | public void testWhile() {
65 | runTest("while.bas", "while.bas.output");
66 | }
67 |
68 | @Test
69 | public void testExpr() {
70 | runTest("expr.bas", "expr.bas.output");
71 | }
72 |
73 | @Test
74 | public void testFunc() {
75 | runTest("func.bas", "func.bas.output");
76 | }
77 |
78 | @Test
79 | public void testFunc2() {
80 | runTest("func2.bas", "func2.bas.output");
81 | }
82 |
83 | @Test
84 | public void testIf() {
85 | runTest("if.bas", "if.bas.output");
86 | }
87 |
88 | @Test
89 | public void testIfThenBegin() {
90 | runTest("ifthenbegin.bas", "ifthenbegin.bas.output");
91 | }
92 |
93 | @Test
94 | public void testReadData() {
95 | runTest("readdata.bas", "readdata.bas.output");
96 | }
97 |
98 | @Test
99 | public void testGosub() {
100 | runTest("gosub.bas", "gosub.bas.output");
101 | }
102 |
103 | @Test
104 | public void testGosublabel() {
105 | runTest("gosublabel.bas", "gosublabel.bas.output");
106 | }
107 |
108 | @Test
109 | public void testGotolabel() {
110 | runTest("gotolabel.bas", "gotolabel.bas.output");
111 | }
112 |
113 | @Test
114 | public void testDef() {
115 | runTest("def.bas", "def.bas.output");
116 | }
117 |
118 | @Test
119 | public void testUdf() {
120 | runTest("udf.bas", "udf.bas.output");
121 | }
122 |
123 | @Test
124 | public void testStrStmt() {
125 | runTest("strstmt.bas", "strstmt.bas.output");
126 | }
127 |
128 | @Test
129 | public void testPrintUsing() {
130 | runTest("printusing.bas", "printusing.bas.output");
131 | }
132 |
133 | @Test
134 | public void testWrite() {
135 | runTest("write.bas", "write.bas.output");
136 | }
137 |
138 | @Test
139 | public void testSwap() {
140 | runTest("swap.bas", "swap.bas.output");
141 | }
142 |
143 | @Test
144 | public void testRef() {
145 | runTest("ref.bas", "ref.bas.output");
146 | }
147 |
148 | @Test
149 | public void testRandomAccessFile() throws IOException {
150 | String tmpdir = System.getProperty("java.io.tmpdir");
151 | String filename = "puffin_basic_test_random_access_file_"
152 | + Instant.now().getEpochSecond() + ".data";
153 | env.set("TEST_TMP_DIR", tmpdir);
154 | env.set("TEST_FILENAME", filename);
155 | runTest("randomaccessfile.bas", "randomaccessfile.bas.output");
156 | Files.delete(Path.of(tmpdir, filename));
157 | }
158 |
159 | @Test
160 | public void testSequentialAccessFile() throws IOException {
161 | String tmpdir = System.getProperty("java.io.tmpdir");
162 | String filename = "puffin_basic_test_sequential_access_file_"
163 | + Instant.now().getEpochSecond() + ".data";
164 | env.set("TEST_TMP_DIR", tmpdir);
165 | env.set("TEST_SEQ_FILENAME", filename);
166 | runTest("sequentialaccessfile.bas", "sequentialaccessfile.bas.output");
167 | Files.delete(Path.of(tmpdir, filename));
168 | }
169 |
170 | @Test
171 | public void testStruct() {
172 | runTest("struct.bas", "struct.bas.output");
173 | }
174 |
175 | @Test
176 | public void testList() {
177 | runTest("list.bas", "list.bas.output");
178 | }
179 |
180 | @Test
181 | public void testSet() {
182 | runTest("set.bas", "set.bas.output");
183 | }
184 |
185 | @Test
186 | public void testDict() {
187 | runTest("dict.bas", "dict.bas.output");
188 | }
189 |
190 | private void runTest(String source, String output) {
191 | var bos = new ByteArrayOutputStream();
192 | var out = new PrintStream(bos);
193 | interpretAndRun(
194 | UserOptions.ofTest(),
195 | loadSourceCodeFromResource(source),
196 | out,
197 | env);
198 | out.close();
199 |
200 | assertEquals(
201 | loadOutputFromResource(output),
202 | new String(bos.toByteArray())
203 | );
204 | }
205 |
206 | private String loadSourceCodeFromResource(String filename) {
207 | return loadResource(
208 | getClass().getClassLoader().getResource(filename));
209 | }
210 |
211 | private String loadOutputFromResource(String filename) {
212 | return loadResource(
213 | getClass().getClassLoader().getResource(filename));
214 | }
215 |
216 | private static String loadResource(URL resource) {
217 | try (InputStream in = new BufferedInputStream(resource.openStream())) {
218 | return new String(in.readAllBytes());
219 | } catch (IOException e) {
220 | throw new PuffinBasicRuntimeError(
221 | IO_ERROR,
222 | "Failed to read file: " + resource + ", error: " + e.getMessage()
223 | );
224 | }
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/src/test/resources/array_copy.bas:
--------------------------------------------------------------------------------
1 | 10 DIM A%(3, 4)
2 | 20 A%(0, 0) = 10 : A%(2, 2) = 5
3 | 30 A%(0, 0) = A%(2, 2)
4 | 40 PRINT A%(0, 0), A%(2, 2)
5 |
--------------------------------------------------------------------------------
/src/test/resources/array_copy.bas.output:
--------------------------------------------------------------------------------
1 | 5 5
2 |
--------------------------------------------------------------------------------
/src/test/resources/array_func.bas:
--------------------------------------------------------------------------------
1 | 10 DIM A%(3, 5)
2 | 20 DIM B%(3, 5)
3 | 30 ARRAYFILL A%, 10
4 | 40 ARRAYFILL B%, 2
5 | 50 PRINT A%(2, 4), B%(2, 4)
6 | 60 ARRAYCOPY A%, B%
7 | 70 PRINT B%(2, 4)
8 | 80 DIM C%(6)
9 | 90 C%(0) = 10 : C%(1) = 2 : C%(3) = -1 : C%(4) = 7 : C%(5) = 20
10 | 100 FOR I% = 0 TO 5
11 | 110 PRINT C%(I%),
12 | 120 NEXT I% : PRINT ""
13 | 130 ARRAY1DSORT C%
14 | 140 FOR I% = 0 TO 5
15 | 150 PRINT C%(I%),
16 | 160 NEXT I% : PRINT ""
17 | 170 PRINT ARRAY1DBINSEARCH(C%, 2), ARRAY1DBINSEARCH(C%, 3)
18 | 180 PRINT ARRAY1DMEAN(C%), ARRAY1DSUM(C%), ARRAY1DSTD(C%), ARRAY1DMEDIAN(C%), ARRAY1DPCT(C%, 90), ARRAY1DMIN(C%), ARRAY1DMAX(C%)
19 | 190 DIM D%(5, 3)
20 | 250 GOSUB 2000 : GOSUB 1000 ' INIT and PRINT D%
21 | 260 FOR S% = 0 TO 6
22 | 270 PRINT S%
23 | 280 ARRAY2DSHIFTVER D%, S%
24 | 290 GOSUB 1000 ' PRINT D%
25 | 300 GOSUB 2000 ' REINIT D%
26 | 310 NEXT
27 | 320 FOR S% = -1 TO -6 STEP -1
28 | 330 PRINT S%
29 | 340 ARRAY2DSHIFTVER D%, S%
30 | 350 GOSUB 1000 ' PRINT D%
31 | 360 GOSUB 2000 ' REINIT D%
32 | 370 NEXT
33 | 380 DIM E%(3, 5)
34 | 390 GOSUB 4000 : GOSUB 3000 ' INIT and PRINT E%
35 | 400 FOR S% = 0 TO 6
36 | 410 PRINT S%
37 | 420 ARRAY2DSHIFTHOR E%, S%
38 | 430 GOSUB 3000 ' PRINT E%
39 | 440 GOSUB 4000 ' REINIT E%
40 | 450 NEXT
41 | 460 FOR S% = -1 TO -6 STEP -1
42 | 470 PRINT S%
43 | 480 ARRAY2DSHIFTHOR E%, S%
44 | 490 GOSUB 3000 ' PRINT E%
45 | 500 GOSUB 4000 ' REINIT E%
46 | 510 NEXT
47 | 520 ' Test COPY1D
48 | 530 DIM F%(5)
49 | 540 FOR K% = 0 TO 4
50 | 550 F%(K%) = K% + 1
51 | 560 NEXT
52 | 570 ARRAY1DCOPY F%, 1, F%, 3, 2
53 | 580 FOR K% = 0 TO 4
54 | 590 PRINT F%(K%),
55 | 600 NEXT : PRINT ""
56 | 990 END
57 | 1000 ' PRINT D%
58 | 1010 FOR I% = 0 TO 4
59 | 1020 FOR J% = 0 TO 2
60 | 1030 PRINT D%(I%, J%),
61 | 1040 NEXT : PRINT ""
62 | 1050 NEXT
63 | 1060 RETURN
64 | 2000 ' INIT D%
65 | 2010 FOR I% = 0 TO 4
66 | 2020 FOR J% = 0 TO 2
67 | 2030 D%(I%, J%) = 10 * (I% + 1) + J% + 1
68 | 2040 NEXT
69 | 2050 NEXT
70 | 2060 RETURN
71 | 3000 ' PRINT E%
72 | 3010 FOR I% = 0 TO 2
73 | 3020 FOR J% = 0 TO 4
74 | 3030 PRINT E%(I%, J%),
75 | 3040 NEXT : PRINT ""
76 | 3050 NEXT
77 | 3060 RETURN
78 | 4000 ' INIT E%
79 | 4010 FOR I% = 0 TO 2
80 | 4020 FOR J% = 0 TO 4
81 | 4030 E%(I%, J%) = 10 * (I% + 1) + J% + 1
82 | 4040 NEXT
83 | 4050 NEXT
84 | 4060 RETURN
85 |
--------------------------------------------------------------------------------
/src/test/resources/array_func.bas.output:
--------------------------------------------------------------------------------
1 | 10 2
2 | 10
3 | 10 2 0 -1 7 20
4 | -1 0 2 7 10 20
5 | 2 -4
6 | 6.333333333333333 38.0 7.916228058025278 4.5 20.0 -1 20
7 | 11 12 13
8 | 21 22 23
9 | 31 32 33
10 | 41 42 43
11 | 51 52 53
12 | 0
13 | 11 12 13
14 | 21 22 23
15 | 31 32 33
16 | 41 42 43
17 | 51 52 53
18 | 1
19 | 0 0 0
20 | 11 12 13
21 | 21 22 23
22 | 31 32 33
23 | 41 42 43
24 | 2
25 | 0 0 0
26 | 0 0 0
27 | 11 12 13
28 | 21 22 23
29 | 31 32 33
30 | 3
31 | 0 0 0
32 | 0 0 0
33 | 0 0 0
34 | 11 12 13
35 | 21 22 23
36 | 4
37 | 0 0 0
38 | 0 0 0
39 | 0 0 0
40 | 0 0 0
41 | 11 12 13
42 | 5
43 | 11 12 13
44 | 21 22 23
45 | 31 32 33
46 | 41 42 43
47 | 51 52 53
48 | 6
49 | 0 0 0
50 | 11 12 13
51 | 21 22 23
52 | 31 32 33
53 | 41 42 43
54 | -1
55 | 21 22 23
56 | 31 32 33
57 | 41 42 43
58 | 51 52 53
59 | 0 0 0
60 | -2
61 | 31 32 33
62 | 41 42 43
63 | 51 52 53
64 | 0 0 0
65 | 0 0 0
66 | -3
67 | 41 42 43
68 | 51 52 53
69 | 0 0 0
70 | 0 0 0
71 | 0 0 0
72 | -4
73 | 51 52 53
74 | 0 0 0
75 | 0 0 0
76 | 0 0 0
77 | 0 0 0
78 | -5
79 | 11 12 13
80 | 21 22 23
81 | 31 32 33
82 | 41 42 43
83 | 51 52 53
84 | -6
85 | 21 22 23
86 | 31 32 33
87 | 41 42 43
88 | 51 52 53
89 | 0 0 0
90 | 11 12 13 14 15
91 | 21 22 23 24 25
92 | 31 32 33 34 35
93 | 0
94 | 11 12 13 14 15
95 | 21 22 23 24 25
96 | 31 32 33 34 35
97 | 1
98 | 0 11 12 13 14
99 | 0 21 22 23 24
100 | 0 31 32 33 34
101 | 2
102 | 0 0 11 12 13
103 | 0 0 21 22 23
104 | 0 0 31 32 33
105 | 3
106 | 0 0 0 11 12
107 | 0 0 0 21 22
108 | 0 0 0 31 32
109 | 4
110 | 0 0 0 0 11
111 | 0 0 0 0 21
112 | 0 0 0 0 31
113 | 5
114 | 11 12 13 14 15
115 | 21 22 23 24 25
116 | 31 32 33 34 35
117 | 6
118 | 0 11 12 13 14
119 | 0 21 22 23 24
120 | 0 31 32 33 34
121 | -1
122 | 12 13 14 15 0
123 | 22 23 24 25 0
124 | 32 33 34 35 0
125 | -2
126 | 13 14 15 0 0
127 | 23 24 25 0 0
128 | 33 34 35 0 0
129 | -3
130 | 14 15 0 0 0
131 | 24 25 0 0 0
132 | 34 35 0 0 0
133 | -4
134 | 15 0 0 0 0
135 | 25 0 0 0 0
136 | 35 0 0 0 0
137 | -5
138 | 11 12 13 14 15
139 | 21 22 23 24 25
140 | 31 32 33 34 35
141 | -6
142 | 12 13 14 15 0
143 | 22 23 24 25 0
144 | 32 33 34 35 0
145 | 1 2 3 2 3
146 |
--------------------------------------------------------------------------------
/src/test/resources/array_var.bas:
--------------------------------------------------------------------------------
1 | 10 DIM A%(3,4)
2 | 20 for I = 0 to 2
3 | 30 for J = 0 to 3
4 | 40 A%(I, J) = (I + 1) * (J + 1)
5 | 50 NEXT : NEXT
6 | 60 for I = 0 to 2
7 | 70 PRINT A%(I,0), A%(I,1), A%(I,2), A%(I,3)
8 | 80 NEXT
9 | 90 d1% = 5 : d2% = 2 : DIM B%(d1%, d2%)
10 | 100 PRINT LEN(B%), LEN(B%, 0), LEN(B%, 1)
11 |
--------------------------------------------------------------------------------
/src/test/resources/array_var.bas.output:
--------------------------------------------------------------------------------
1 | 1 2 3 4
2 | 2 4 6 8
3 | 3 6 9 12
4 | 5 5 2
5 |
--------------------------------------------------------------------------------
/src/test/resources/def.bas:
--------------------------------------------------------------------------------
1 | 10 DEFINT A-B, C-C
2 | 20 DEFLNG D-E, F-F
3 | 30 DEFSNG G-H, I-I
4 | 40 DEFDBL J-K, L-L
5 | 50 DEFSTR M-N, O-O
6 | 60 A = 1 : D = 111111111111@ : G = 1.2 : J = 2.3 : M = "AA"
7 | 70 PRINT A, D, G, J, M
8 |
--------------------------------------------------------------------------------
/src/test/resources/def.bas.output:
--------------------------------------------------------------------------------
1 | 1 111111111111 1.2 2.299999952316284 AA
2 |
--------------------------------------------------------------------------------
/src/test/resources/dict.bas:
--------------------------------------------------------------------------------
1 | PRINT "DICT of STRING to INT32"
2 |
3 | DICT<$,%> dict1
4 |
5 | dict1.put("a", 65)
6 | dict1.put("b", 66)
7 | PRINT LEN(dict1)
8 |
9 | auto d1val = dict1.keys()
10 | FOR I% = 0 TO LEN(d1val) - 1
11 | PRINT d1val(I%),
12 | NEXT : PRINT ""
13 |
14 | PRINT dict1.getOrDefault("a", -1)
15 | PRINT dict1.getOrDefault("c", -1)
16 | PRINT LEN(dict1)
17 |
18 | PRINT dict1.removeKey("a")
19 | PRINT dict1.getOrDefault("a", -1)
20 | PRINT LEN(dict1)
21 |
22 | PRINT dict1.containsKey("a")
23 | PRINT dict1.containsKey("b")
24 |
25 | dict1.clear()
26 | PRINT LEN(dict1)
27 |
28 | PRINT "DICT of INT32 to STRING"
29 |
30 | DICT<%,$> dict2
31 |
32 | dict2.put(1, "a")
33 | dict2.put(2, "b")
34 | PRINT LEN(dict2)
35 |
36 | auto d2val = dict2.keys()
37 | FOR I% = 0 TO LEN(d2val) - 1
38 | PRINT d2val(I%),
39 | NEXT : PRINT ""
40 |
41 | PRINT dict2.getOrDefault(1, "")
42 | PRINT dict2.getOrDefault(2, "")
43 | PRINT LEN(dict2)
44 |
45 | PRINT dict2.removeKey(1)
46 | PRINT dict2.getOrDefault(1, "")
47 | PRINT LEN(dict2)
48 |
49 | PRINT dict2.containsKey(1)
50 | PRINT dict2.containsKey(2)
51 |
52 | dict2.clear()
53 | PRINT LEN(dict2)
54 |
--------------------------------------------------------------------------------
/src/test/resources/dict.bas.output:
--------------------------------------------------------------------------------
1 | DICT of STRING to INT32
2 | 2
3 | ab
4 | 65
5 | -1
6 | 2
7 | -1
8 | -1
9 | 1
10 | 0
11 | -1
12 | 0
13 | DICT of INT32 to STRING
14 | 2
15 | 2 1
16 | a
17 | b
18 | 2
19 | -1
20 |
21 | 1
22 | 0
23 | -1
24 | 0
25 |
--------------------------------------------------------------------------------
/src/test/resources/expr.bas:
--------------------------------------------------------------------------------
1 | 10 print (10 + 4) * (9 - 2)
2 | 20 PRINT 6 ^ 2
3 | 30 PRINT 5 MOD 2
4 | 40 PRINT 10# / 3
5 | 50 PRINT 10 \ 3
6 | 60 print 10 < 20, 10 <= 20, 10 = 20, 10 <> 20, 10 > 20, 10 >= 20
7 | 70 print NOT 0, NOT -1
8 | 80 PRINT 0 or 0, 0 or -1, -1 or 0, -1 or -1
9 | 90 PRINT 0 and 0, 0 and -1, -1 and 0, -1 AND -1
10 | 100 PRINT 0 xor 0, 0 xor -1, -1 xor 0, -1 XOR -1
11 | 110 PRINT 0 eqv 0, 0 EQV -1, -1 eqv 0, -1 eqv -1
12 | 120 PRINT 0 imp 0, 0 IMP -1, -1 imp 0, -1 IMP -1
13 | 130 PRINT "A" + "BC"
14 | 140 A% = 2
15 | 150 PRINT -A%
16 | 160 print -10
17 | 170 print "ABC"
18 | 180 PRINT 1 OR 2
19 | 190 PRINT 7 AND 5
20 | 200 PRINT 5 XOR 3
21 | 210 PRINT &H10
22 | 220 PRINT &10
23 | 230 PRINT &O10
24 | 240 PRINT 16 >> 2
25 | 250 PRINT 16 << 2
26 |
--------------------------------------------------------------------------------
/src/test/resources/expr.bas.output:
--------------------------------------------------------------------------------
1 | 98
2 | 36
3 | 1
4 | 3.3333333333333335
5 | 3
6 | -1 -1 0 -1 0 0
7 | -1 0
8 | 0 -1 -1 -1
9 | 0 0 0 -1
10 | 0 -1 -1 0
11 | -1 0 0 -1
12 | -1 -1 0 -1
13 | ABC
14 | -2
15 | -10
16 | ABC
17 | 3
18 | 5
19 | 6
20 | 16
21 | 8
22 | 8
23 | 4
24 | 64
25 |
--------------------------------------------------------------------------------
/src/test/resources/forloop.bas:
--------------------------------------------------------------------------------
1 | 10 FOR I% = 1 to 10 STEP 2
2 | 20 PRINT I%
3 | 30 NEXT I%
4 |
--------------------------------------------------------------------------------
/src/test/resources/forloop.bas.output:
--------------------------------------------------------------------------------
1 | 1
2 | 3
3 | 5
4 | 7
5 | 9
6 |
--------------------------------------------------------------------------------
/src/test/resources/func.bas:
--------------------------------------------------------------------------------
1 | 10 PRINT ABS(-2), ABS(2), ABS(0)
2 | 20 PRINT ASC("A"), ASC("B")
3 | 30 PRINT sin(1), cos(1), tan(1), atn(1)
4 | 40 PRINT SQR(9), "log=", LOG(0.1)
5 | 50 PRINT CINT(2.3), CLNG(2.3), CSNG(2.3), CDBL(2.3)
6 | 60 PRINT INT(2.3), INT(-2.3)
7 | 70 PRINT FIX(2.3), FIX(-2.3)
8 | 80 PRINT SGN(10), SGN(0), SGN(-10)
9 | 90 PRINT CVI(MKI$(23)), CVL(MKL$(23)), CVS(MKS$(23.1)), CVD(MKD$(23.1))
10 | 100 PRINT STR$(102), SPACE$(4), STR$(102)
11 | 110 PRINT LEN("123")
12 | 120 PRINT HEX$(16), OCT$(8)
13 | 130 PRINT LEFT$("ABCD", 2), LEFT$("1234", 4)
14 | 140 PRINT RIGHT$("ABCD", 2), RIGHT$("1234", 4)
15 | 150 PRINT MID$("123456", 2, 2)
16 | 160 PRINT STRING$(3, "#"), STRING$(3, "ABC")
17 | 170 PRINT INSTR("12FOO34FOO", "FOO")
18 | 180 PRINT RND > 0
19 | 190 PRINT TIMER > 0
20 | 200 PRINT asin(1), acos(0.5), sinh(1), cosh(1), tanh(1)
21 | 210 PRINT exp(1), exp(-1), log10(2)
22 | 220 PRINT EULERE(), PI()
23 | 230 PRINT TORAD(180), TODEG(PI())
24 | 240 PRINT FLOOR(2.3), FLOOR(-2.3), CEIL(2.3), CEIL(-2.3), ROUND(2.3), ROUND(-2.3)
25 | 250 PRINT MIN(2, 3), MAX(2, 3)
26 | 260 AUTO T = SPLIT$("A,BB,CC", ",")
27 | 270 PRINT LEN(T)
28 | 280 FOR I% = 0 TO LEN(T) - 1
29 | 290 PRINT T(I%)
30 | 300 NEXT
31 |
--------------------------------------------------------------------------------
/src/test/resources/func.bas.output:
--------------------------------------------------------------------------------
1 | 2 2 0
2 | 65 66
3 | 0.8414709848078965 0.5403023058681398 1.5574077246549023 0.7853981633974483
4 | 3.0 log=-2.3025850780928847
5 | 2 2 2.3 2.299999952316284
6 | 2.0 -3.0
7 | 2.0 -2.0
8 | 1 0 -1
9 | 23 23 23.1 23.100000381469727
10 | 102 102
11 | 3
12 | 1010
13 | AB1234
14 | CD1234
15 | 23
16 | ###AAA
17 | 3
18 | -1
19 | -1
20 | 1.5707963267948966 1.0471975511965979 1.1752011936438014 1.543080634815244 0.7615941559557649
21 | 2.718281828459045 0.36787944117144233 0.3010299956639812
22 | 2.718281828459045 3.141592653589793
23 | 3.141592653589793 180.0
24 | 2.0 -3.0 3.0 -2.0 2.0 -2.0
25 | 2 3
26 | 3
27 | A
28 | BB
29 | CC
30 |
--------------------------------------------------------------------------------
/src/test/resources/func2.bas:
--------------------------------------------------------------------------------
1 | PRINT "DECLARE FUNCTION"
2 |
3 | FUNCTION fun1# (X, Y) {
4 | Z = X + Y
5 | RETURN Z
6 | }
7 |
8 | FUNCTION fun2 (X, Y) {
9 | IF Y = 0 THEN RETURN 0
10 | Z = X / Y
11 | RETURN Z
12 | }
13 |
14 | PRINT "CALL FUNCTION"
15 | PRINT fun1#(2, 3)
16 | PRINT fun2(2, 3)
17 | PRINT fun2(2, 0)
18 |
19 | PRINT "ARRAY TEST"
20 |
21 | FUNCTION initArray (DIM X(0), n, v) {
22 | FOR I% = 0 TO n - 1
23 | X(I%) = v
24 | NEXT
25 | RETURN 0
26 | }
27 |
28 | DIM A%(10)
29 | initArray(A%, LEN(A%), 10)
30 |
31 | FOR I% = 0 TO LEN(A%) - 1
32 | PRINT A%(I%)
33 | NEXT
34 |
--------------------------------------------------------------------------------
/src/test/resources/func2.bas.output:
--------------------------------------------------------------------------------
1 | DECLARE FUNCTION
2 | CALL FUNCTION
3 | 5.0
4 | 0.6666666666666666
5 | 0.0
6 | ARRAY TEST
7 | 10
8 | 10
9 | 10
10 | 10
11 | 10
12 | 10
13 | 10
14 | 10
15 | 10
16 | 10
17 |
--------------------------------------------------------------------------------
/src/test/resources/gosub.bas:
--------------------------------------------------------------------------------
1 | 10 PRINT "10"
2 | 20 gosub 100
3 | 30 PRINT "30"
4 | 40 END
5 | 100 REM SUBROUTINE
6 | 110 PRINT "GOSUB"
7 | 120 RETURN
8 |
--------------------------------------------------------------------------------
/src/test/resources/gosub.bas.output:
--------------------------------------------------------------------------------
1 | 10
2 | GOSUB
3 | 30
4 |
--------------------------------------------------------------------------------
/src/test/resources/gosublabel.bas:
--------------------------------------------------------------------------------
1 | GOSUB "sub1"
2 | GOSUB "sub2"
3 | END
4 | LABEL "sub1"
5 | PRINT "sub1"
6 | RETURN
7 | LABEL "sub2"
8 | PRINT "sub2"
9 | RETURN
10 |
--------------------------------------------------------------------------------
/src/test/resources/gosublabel.bas.output:
--------------------------------------------------------------------------------
1 | sub1
2 | sub2
3 |
--------------------------------------------------------------------------------
/src/test/resources/gotolabel.bas:
--------------------------------------------------------------------------------
1 | PRINT "A"
2 | GOTO "label1"
3 | END
4 | LABEL "label1"
5 | PRINT "label1"
6 |
--------------------------------------------------------------------------------
/src/test/resources/gotolabel.bas.output:
--------------------------------------------------------------------------------
1 | A
2 | label1
3 |
--------------------------------------------------------------------------------
/src/test/resources/if.bas:
--------------------------------------------------------------------------------
1 | 10 A# = 10.0
2 | 20 IF A# < 20.0 THEN PRINT "LESS" ELSE PRINT "MORE"
3 | 30 PRINT "XXX"
4 | 40 IF A# > 20.0 GOTO 70 ELSE PRINT "MORE"
5 | 50 A# = A# + 2
6 | 60 GOTO 40
7 | 70 PRINT "XXX"
8 |
--------------------------------------------------------------------------------
/src/test/resources/if.bas.output:
--------------------------------------------------------------------------------
1 | LESS
2 | XXX
3 | MORE
4 | MORE
5 | MORE
6 | MORE
7 | MORE
8 | MORE
9 | XXX
10 |
--------------------------------------------------------------------------------
/src/test/resources/ifthenbegin.bas:
--------------------------------------------------------------------------------
1 | A% = 20
2 | PRINT "A=", A%
3 |
4 | PRINT "TEST1"
5 | IF A% < 30 THEN BEGIN
6 | PRINT A%, "LT 20"
7 | END IF
8 | PRINT "TEST1 DONE"
9 |
10 | PRINT "TEST2"
11 | IF A% > 30 THEN BEGIN
12 | PRINT A%, "GT 20"
13 | END IF
14 | PRINT "TEST2 DONE"
15 |
16 | PRINT "TEST3"
17 | IF A% < 20 THEN BEGIN
18 | PRINT A%, "LT 20"
19 | ELSE BEGIN
20 | PRINT A%, "GE 20"
21 | END IF
22 | PRINT "TEST3 DONE"
23 |
24 | PRINT "TEST4"
25 | IF A% <= 20 THEN BEGIN
26 | PRINT A%, "LE 20"
27 | ELSE BEGIN
28 | PRINT A%, "GT 20"
29 | END IF
30 | PRINT "TEST4 DONE"
31 |
32 | PRINT "TEST5"
33 | FOR I% = 19 TO 21
34 | IF I% < 20 THEN BEGIN
35 | PRINT I%, "LT 20"
36 | IF I% = 19 THEN BEGIN
37 | PRINT I%, "EQ 19"
38 | ELSE BEGIN
39 | PRINT I%, "NE 19"
40 | END IF
41 | ELSE BEGIN
42 | PRINT I%, "GE 20"
43 | IF I% = 20 THEN BEGIN
44 | PRINT I%, "EQ 20"
45 | ELSE BEGIN
46 | PRINT I%, "NE 20"
47 | END IF
48 | END IF
49 | NEXT
50 | PRINT "TEST5 DONE"
51 |
--------------------------------------------------------------------------------
/src/test/resources/ifthenbegin.bas.output:
--------------------------------------------------------------------------------
1 | A= 20
2 | TEST1
3 | 20 LT 20
4 | TEST1 DONE
5 | TEST2
6 | TEST2 DONE
7 | TEST3
8 | 20 GE 20
9 | TEST3 DONE
10 | TEST4
11 | 20 LE 20
12 | TEST4 DONE
13 | TEST5
14 | 19 LT 20
15 | 19 EQ 19
16 | 20 GE 20
17 | 20 EQ 20
18 | 21 GE 20
19 | 21 NE 20
20 | TEST5 DONE
21 |
--------------------------------------------------------------------------------
/src/test/resources/list.bas:
--------------------------------------------------------------------------------
1 | PRINT "LIST of STRING"
2 |
3 | LIST<$> list1
4 | PRINT len(list1)
5 |
6 | list1.append("a")
7 | list1.append("b")
8 | PRINT list1.get(0)
9 | PRINT list1.get(1)
10 | PRINT len(list1)
11 |
12 | auto l1val = list1.values()
13 | FOR I% = 0 TO LEN(l1val) - 1
14 | PRINT l1val(I%),
15 | NEXT : PRINT ""
16 |
17 | list1.insert(0, "c")
18 | PRINT list1.get(0)
19 | PRINT len(list1)
20 |
21 | list1.clear()
22 | PRINT len(list1)
23 |
24 | PRINT "LIST of INT32"
25 |
26 | LIST<%> list2
27 | PRINT len(list2)
28 |
29 | list2.append(1)
30 | list2.append(2)
31 | PRINT list2.get(0)
32 | PRINT list2.get(1)
33 | PRINT len(list2)
34 |
35 | auto l2val = list2.values()
36 | FOR I% = 0 TO LEN(l2val) - 1
37 | PRINT l2val(I%),
38 | NEXT : PRINT ""
39 |
40 | list2.insert(0, 3)
41 | PRINT list2.get(0)
42 | PRINT len(list2)
43 |
44 | list2.clear()
45 | PRINT len(list2)
46 |
47 | PRINT "LIST of INT64"
48 |
49 | LIST<@> list3
50 | PRINT len(list3)
51 |
52 | list3.append(10)
53 | list3.append(20)
54 | PRINT list3.get(0)
55 | PRINT list3.get(1)
56 | PRINT len(list3)
57 |
58 | auto l3val = list3.values()
59 | FOR I% = 0 TO LEN(l3val) - 1
60 | PRINT l3val(I%),
61 | NEXT : PRINT ""
62 |
63 | list3.insert(0, 30)
64 | PRINT list3.get(0)
65 | PRINT len(list3)
66 |
67 | list3.clear()
68 | PRINT len(list3)
69 |
70 | PRINT "LIST of FLOAT32"
71 |
72 | LIST list4
73 | PRINT len(list4)
74 |
75 | list4.append(1.1)
76 | list4.append(2.1)
77 | PRINT list4.get(0)
78 | PRINT list4.get(1)
79 | PRINT len(list4)
80 |
81 | auto l4val = list4.values()
82 | FOR I% = 0 TO LEN(l4val) - 1
83 | PRINT l4val(I%),
84 | NEXT : PRINT ""
85 |
86 | list4.insert(0, 3.1)
87 | PRINT list4.get(0)
88 | PRINT len(list4)
89 |
90 | list4.clear()
91 | PRINT len(list4)
92 |
93 | PRINT "LIST of FLOAT64"
94 |
95 | LIST<#> list5
96 | PRINT len(list5)
97 |
98 | list5.append(10.1)
99 | list5.append(20.1)
100 | PRINT list5.get(0)
101 | PRINT list5.get(1)
102 | PRINT len(list5)
103 |
104 | auto l5val = list5.values()
105 | FOR I% = 0 TO LEN(l5val) - 1
106 | PRINT l5val(I%),
107 | NEXT : PRINT ""
108 |
109 | list5.insert(0, 30.1)
110 | PRINT list5.get(0)
111 | PRINT len(list5)
112 |
113 | list5.clear()
114 | PRINT len(list5)
115 |
116 | LIST list6
117 | DIM D0%(5)
118 | DIM D1%(5)
119 | D0%(0) = 10
120 | D1%(0) = 20
121 | list6.append(D0%)
122 | list6.append(D1%)
123 | AUTO x0 = list6.get(0)
124 | AUTO x1 = list6.get(1)
125 | PRINT x0(0), x1(0)
126 |
127 | STRUCT Struct1 { X$ }
128 | Struct1 s1 {}
129 | s1.X$ = "A10"
130 | LIST list7
131 | list7.append(s1)
132 | AUTO x70 = list7.get(0)
133 | PRINT x70.X$
134 |
--------------------------------------------------------------------------------
/src/test/resources/list.bas.output:
--------------------------------------------------------------------------------
1 | LIST of STRING
2 | 0
3 | a
4 | b
5 | 2
6 | ab
7 | c
8 | 3
9 | 0
10 | LIST of INT32
11 | 0
12 | 1
13 | 2
14 | 2
15 | 1 2
16 | 3
17 | 3
18 | 0
19 | LIST of INT64
20 | 0
21 | 10
22 | 20
23 | 2
24 | 10 20
25 | 30
26 | 3
27 | 0
28 | LIST of FLOAT32
29 | 0
30 | 1.1
31 | 2.1
32 | 2
33 | 1.1 2.1
34 | 3.1
35 | 3
36 | 0
37 | LIST of FLOAT64
38 | 0
39 | 10.100000381469727
40 | 20.100000381469727
41 | 2
42 | 10.100000381469727 20.100000381469727
43 | 30.100000381469727
44 | 3
45 | 0
46 | 10 20
47 | A10
48 |
--------------------------------------------------------------------------------
/src/test/resources/nested_forloop.bas:
--------------------------------------------------------------------------------
1 | 10 FOR I% = 10 to 1 STEP -1
2 | 20 FOR J% = 1 to 10
3 | 30 PRINT I%, "x", J%, "=", I%*J%
4 | 40 NEXT J%, I%
5 |
--------------------------------------------------------------------------------
/src/test/resources/nested_forloop.bas.output:
--------------------------------------------------------------------------------
1 | 10 x 1 = 10
2 | 10 x 2 = 20
3 | 10 x 3 = 30
4 | 10 x 4 = 40
5 | 10 x 5 = 50
6 | 10 x 6 = 60
7 | 10 x 7 = 70
8 | 10 x 8 = 80
9 | 10 x 9 = 90
10 | 10 x 10 = 100
11 | 9 x 1 = 9
12 | 9 x 2 = 18
13 | 9 x 3 = 27
14 | 9 x 4 = 36
15 | 9 x 5 = 45
16 | 9 x 6 = 54
17 | 9 x 7 = 63
18 | 9 x 8 = 72
19 | 9 x 9 = 81
20 | 9 x 10 = 90
21 | 8 x 1 = 8
22 | 8 x 2 = 16
23 | 8 x 3 = 24
24 | 8 x 4 = 32
25 | 8 x 5 = 40
26 | 8 x 6 = 48
27 | 8 x 7 = 56
28 | 8 x 8 = 64
29 | 8 x 9 = 72
30 | 8 x 10 = 80
31 | 7 x 1 = 7
32 | 7 x 2 = 14
33 | 7 x 3 = 21
34 | 7 x 4 = 28
35 | 7 x 5 = 35
36 | 7 x 6 = 42
37 | 7 x 7 = 49
38 | 7 x 8 = 56
39 | 7 x 9 = 63
40 | 7 x 10 = 70
41 | 6 x 1 = 6
42 | 6 x 2 = 12
43 | 6 x 3 = 18
44 | 6 x 4 = 24
45 | 6 x 5 = 30
46 | 6 x 6 = 36
47 | 6 x 7 = 42
48 | 6 x 8 = 48
49 | 6 x 9 = 54
50 | 6 x 10 = 60
51 | 5 x 1 = 5
52 | 5 x 2 = 10
53 | 5 x 3 = 15
54 | 5 x 4 = 20
55 | 5 x 5 = 25
56 | 5 x 6 = 30
57 | 5 x 7 = 35
58 | 5 x 8 = 40
59 | 5 x 9 = 45
60 | 5 x 10 = 50
61 | 4 x 1 = 4
62 | 4 x 2 = 8
63 | 4 x 3 = 12
64 | 4 x 4 = 16
65 | 4 x 5 = 20
66 | 4 x 6 = 24
67 | 4 x 7 = 28
68 | 4 x 8 = 32
69 | 4 x 9 = 36
70 | 4 x 10 = 40
71 | 3 x 1 = 3
72 | 3 x 2 = 6
73 | 3 x 3 = 9
74 | 3 x 4 = 12
75 | 3 x 5 = 15
76 | 3 x 6 = 18
77 | 3 x 7 = 21
78 | 3 x 8 = 24
79 | 3 x 9 = 27
80 | 3 x 10 = 30
81 | 2 x 1 = 2
82 | 2 x 2 = 4
83 | 2 x 3 = 6
84 | 2 x 4 = 8
85 | 2 x 5 = 10
86 | 2 x 6 = 12
87 | 2 x 7 = 14
88 | 2 x 8 = 16
89 | 2 x 9 = 18
90 | 2 x 10 = 20
91 | 1 x 1 = 1
92 | 1 x 2 = 2
93 | 1 x 3 = 3
94 | 1 x 4 = 4
95 | 1 x 5 = 5
96 | 1 x 6 = 6
97 | 1 x 7 = 7
98 | 1 x 8 = 8
99 | 1 x 9 = 9
100 | 1 x 10 = 10
101 |
--------------------------------------------------------------------------------
/src/test/resources/printusing.bas:
--------------------------------------------------------------------------------
1 | 10 PRINT USING "!"; "123"; "456"
2 | 20 PRINT USING "&"; "123"; "456"
3 | 30 PRINT USING "\ \"; "123456"; "abcdef"
4 | 40 PRINT using "###.##"; 2.3
5 | 50 PRINT using "**###.##"; 2.3
6 | 60 PRINT using "**$###.##"; 2.3
7 | 70 PRINT using "+###.##+"; 2.3
8 | 80 PRINT using "+###.##+"; -2.3
9 | 90 PRINT using "###.##^^^^"; 2.3
10 | 100 PRINT USING "###,###.##"; 123456.789
11 |
--------------------------------------------------------------------------------
/src/test/resources/printusing.bas.output:
--------------------------------------------------------------------------------
1 | 14
2 | 123456
3 | 123abc
4 | 2.30
5 | ****2.30
6 | $****2.30
7 | +2.30+
8 | -2.30-
9 | 230.00E-02
10 | 123,456.79
11 |
--------------------------------------------------------------------------------
/src/test/resources/randomaccessfile.bas:
--------------------------------------------------------------------------------
1 | 10 FILE$ = ENVIRON$("TEST_TMP_DIR") + "/" + ENVIRON$("TEST_FILENAME")
2 | 30 OPEN "R", #1, FILE$, 24
3 | 40 FIELD#1, 8 AS A$, 8 AS B$, 8 AS C$
4 | 50 FOR I% = 1 TO 5
5 | 60 LSET A$ = STR$(I%)
6 | 70 LSET B$ = STR$(I% + 1)
7 | 80 LSET C$ = STR$(I% + 2)
8 | 90 PUT #1
9 | 100 PRINT LOC(1), LOF(1)
10 | 110 NEXT I%
11 | 120 FOR I% = 1 TO 5
12 | 130 GET #1, I% - 1
13 | 140 PRINT A$, B$, C$, LOC(1), LOF(1)
14 | 150 NEXT
15 | 160 CLOSE
16 |
--------------------------------------------------------------------------------
/src/test/resources/randomaccessfile.bas.output:
--------------------------------------------------------------------------------
1 | 1 24
2 | 2 48
3 | 3 72
4 | 4 96
5 | 5 120
6 | 1 2 3 1 120
7 | 2 3 4 2 120
8 | 3 4 5 3 120
9 | 4 5 6 4 120
10 | 5 6 7 5 120
11 |
--------------------------------------------------------------------------------
/src/test/resources/readdata.bas:
--------------------------------------------------------------------------------
1 | 10 READ A%, B$, C@, D#, E!
2 | 20 DATA 10, "aaa", 1234566678, 2.3, 1.2
3 | 30 PRINT A%, B$, C@, D#, E!
4 | 40 RESTORE
5 | 50 PRINT A%, B$, C@, D#, E!
6 |
--------------------------------------------------------------------------------
/src/test/resources/readdata.bas.output:
--------------------------------------------------------------------------------
1 | 10 aaa 1234566678 2.299999952316284 1.2
2 | 10 aaa 1234566678 2.299999952316284 1.2
3 |
--------------------------------------------------------------------------------
/src/test/resources/ref.bas:
--------------------------------------------------------------------------------
1 | PRINT "SCALAR INT32"
2 | A% = 10
3 | AUTO B1 = A%
4 | PRINT A%, B1
5 | B1 = 3
6 | PRINT A%, B1
7 |
8 | PRINT "SCALAR INT64"
9 | A@ = 10
10 | AUTO B2 = A@
11 | PRINT A@, B2
12 | B2 = 3
13 | PRINT A@, B2
14 |
15 | PRINT "SCALAR FLOAT32"
16 | A! = 10
17 | AUTO B3 = A!
18 | PRINT A!, B3
19 | B3 = 3
20 | PRINT A!, B3
21 |
22 | PRINT "SCALAR FLOAT64"
23 | A# = 10
24 | AUTO B4 = A#
25 | PRINT A#, B4
26 | B4 = 3
27 | PRINT A#, B4
28 |
29 | PRINT "SCALAR STRING"
30 | A$ = "10"
31 | AUTO B5 = A$
32 | PRINT A$, B5
33 | B5 = "3"
34 | PRINT A$, B5
35 |
36 | PRINT "ARRAY INT32"
37 | DIM AA%(2,3)
38 | AA%(1,1) = 10
39 | AUTO AB = AA%
40 | PRINT AA%(1,1)
41 | PRINT AB(1,1)
42 | AB(1,1) = 3
43 | PRINT AA%(1,1)
44 | PRINT AB(1,1)
45 |
--------------------------------------------------------------------------------
/src/test/resources/ref.bas.output:
--------------------------------------------------------------------------------
1 | SCALAR INT32
2 | 10 10
3 | 3 3
4 | SCALAR INT64
5 | 10 10
6 | 3 3
7 | SCALAR FLOAT32
8 | 10.0 10.0
9 | 3.0 3.0
10 | SCALAR FLOAT64
11 | 10.0 10.0
12 | 3.0 3.0
13 | SCALAR STRING
14 | 1010
15 | 33
16 | ARRAY INT32
17 | 10
18 | 10
19 | 3
20 | 3
21 |
--------------------------------------------------------------------------------
/src/test/resources/scalar_var.bas:
--------------------------------------------------------------------------------
1 | 10 A$ = "TEST STRING"
2 | 20 B% = 10 ' int32
3 | 30 C@ = 200@ ' int64
4 | 40 D! = 1.2 ' float32
5 | 50 E# = 2.2 ' float64
6 | 60 PRINT A$, B%, C@, D!, E#
7 |
--------------------------------------------------------------------------------
/src/test/resources/scalar_var.bas.output:
--------------------------------------------------------------------------------
1 | TEST STRING 10 200 1.2 2.200000047683716
2 |
--------------------------------------------------------------------------------
/src/test/resources/sequentialaccessfile.bas:
--------------------------------------------------------------------------------
1 | 10 FILE$ = ENVIRON$("TEST_TMP_DIR") + "/" + ENVIRON$("TEST_SEQ_FILENAME")
2 | 20 OPEN "O", #1, FILE$
3 | 30 FOR I% = 1 TO 5
4 | 40 WRITE#1, "ABC" + STR$(I%), 123 + I%, 456@ + I%, 1.2 + I%
5 | 50 NEXT
6 | 60 FOR I% = 1 TO 5
7 | 70 PRINT#1, CHR$(34), "ABC" + STR$(I%), CHR$(34), ",", 123 + I%, ",", 456@ + I%, ",", 1.2 + I%
8 | 80 NEXT
9 | 90 CLOSE #1
10 | 100 OPEN FILE$ FOR INPUT AS #1
11 | 110 FOR I% = 1 TO 10
12 | 120 INPUT#1, A$, B%, C@, D#
13 | 130 PRINT A$, B%, C@, D#
14 | 140 NEXT
15 | 150 CLOSE
16 |
--------------------------------------------------------------------------------
/src/test/resources/sequentialaccessfile.bas.output:
--------------------------------------------------------------------------------
1 | ABC1 124 457 2.2
2 | ABC2 125 458 3.2
3 | ABC3 126 459 4.2
4 | ABC4 127 460 5.2
5 | ABC5 128 461 6.2
6 | ABC1 124 457 2.2
7 | ABC2 125 458 3.2
8 | ABC3 126 459 4.2
9 | ABC4 127 460 5.2
10 | ABC5 128 461 6.2
11 |
--------------------------------------------------------------------------------
/src/test/resources/set.bas:
--------------------------------------------------------------------------------
1 | PRINT "SET of STRING"
2 |
3 | SET<$> set1
4 |
5 | set1.add("a")
6 | set1.add("a")
7 | PRINT LEN(set1)
8 |
9 | PRINT set1.contains("a")
10 |
11 | auto s1val = set1.values()
12 | FOR I% = 0 TO LEN(s1val) - 1
13 | PRINT s1val(I%),
14 | NEXT : PRINT ""
15 |
16 | set1.remove("a")
17 | PRINT LEN(set1)
18 |
19 | set1.clear()
20 | PRINT LEN(set1)
21 |
22 | PRINT "SET of INT32"
23 |
24 | SET<%> set2
25 |
26 | set2.add(10)
27 | set2.add(20)
28 | PRINT LEN(set2)
29 |
30 | PRINT set2.contains(10)
31 |
32 | set2.remove(10)
33 | PRINT LEN(set2)
34 |
35 | set2.clear()
36 | PRINT LEN(set2)
37 |
38 | PRINT "SET of INT64"
39 |
40 | SET<@> set3
41 |
42 | set3.add(100)
43 | set3.add(200)
44 | PRINT LEN(set3)
45 |
46 | PRINT set3.contains(100)
47 |
48 | set3.remove(100)
49 | PRINT LEN(set3)
50 |
51 | set3.clear()
52 | PRINT LEN(set3)
53 |
54 | PRINT "SET of FLOAT32"
55 |
56 | SET set4
57 |
58 | set4.add(10.1)
59 | set4.add(20.1)
60 | PRINT LEN(set4)
61 |
62 | PRINT set4.contains(10.1)
63 |
64 | set4.remove(10.1)
65 | PRINT LEN(set4)
66 |
67 | set4.clear()
68 | PRINT LEN(set4)
69 |
70 | PRINT "SET of FLOAT64"
71 |
72 | SET<#> set5
73 |
74 | set5.add(100.1)
75 | set5.add(200.1)
76 | PRINT LEN(set5)
77 |
78 | PRINT set5.contains(100.1)
79 |
80 | set5.remove(100.1)
81 | PRINT LEN(set5)
82 |
83 | set5.clear()
84 | PRINT LEN(set5)
85 |
--------------------------------------------------------------------------------
/src/test/resources/set.bas.output:
--------------------------------------------------------------------------------
1 | SET of STRING
2 | 1
3 | -1
4 | a
5 | 0
6 | 0
7 | SET of INT32
8 | 2
9 | -1
10 | 1
11 | 0
12 | SET of INT64
13 | 2
14 | -1
15 | 1
16 | 0
17 | SET of FLOAT32
18 | 2
19 | -1
20 | 1
21 | 0
22 | SET of FLOAT64
23 | 2
24 | -1
25 | 1
26 | 0
27 |
--------------------------------------------------------------------------------
/src/test/resources/strstmt.bas:
--------------------------------------------------------------------------------
1 | 10 A$ = SPACE$(20)
2 | 20 RSET A$ = "123"
3 | 30 B$ = SPACE$(20)
4 | 40 LSET B$ = "123"
5 | 50 PRINT A$
6 | 60 PRINT B$
7 | 70 MM$ = "KANSAS CITY, MO, USA"
8 | 80 MID$(MM$, 14) = "KS"
9 | 90 PRINT MM$
10 |
--------------------------------------------------------------------------------
/src/test/resources/strstmt.bas.output:
--------------------------------------------------------------------------------
1 | 123
2 | 123
3 | KANSAS CITY, KS, USA
4 |
--------------------------------------------------------------------------------
/src/test/resources/struct.bas:
--------------------------------------------------------------------------------
1 | ' Test struct
2 |
3 | PRINT "STRUCT1"
4 |
5 | STRUCT struct1 { A% , B% }
6 | struct1 s1 {}
7 | struct1 s2 {}
8 |
9 | PRINT s1.A%, s2.A%
10 |
11 | s1.A% = 2
12 | s2.A% = 10
13 | PRINT s1.A%, s2.A%
14 |
15 | s1.A% = s2.A%
16 | PRINT s1.A%, s2.A%
17 |
18 | s1.A% = 11
19 | s2.A% = 12
20 | s2 = s1
21 | PRINT s1.A%, s2.A%
22 |
23 | PRINT "STRUCT2"
24 |
25 | STRUCT struct2 { C% , struct1 child }
26 | struct2 s3 {}
27 | struct2 s4 {}
28 |
29 | s3.child.A% = 100
30 | s3.C% = 50
31 | PRINT s3.child.A%, s3.C%
32 |
33 | PRINT "STRUCT3"
34 |
35 | STRUCT struct3 { A% , DIM B%(2,5) }
36 | struct3 s31 {}
37 | s31.B%(1, 4) = 10
38 | PRINT s31.B%(1, 4)
39 |
40 | PRINT "STRUCT4"
41 |
42 | STRUCT struct4 { A%, B@, C!, D#, E$, DIM ARR%(2,3), LIST<$> l1, SET<%> s1, DICT<%, #> d1 }
43 | struct4 s41 {}
44 | s41.A% = 1
45 | s41.B@ = 2
46 | s41.C! = 3
47 | s41.D# = 4
48 | s41.E$ = "str"
49 | s41.ARR%(1, 1) = 5
50 | s41.l1.append("abc")
51 | s41.s1.add(23)
52 | s41.d1.put(10, 2.5)
53 | PRINT s41.A%, s41.B@, s41.C!, s41.D#, s41.E$, s41.ARR%(1, 1), s41.l1.get(0), s41.s1.contains(23), s41.d1.getOrDefault(10, 0)
54 |
--------------------------------------------------------------------------------
/src/test/resources/struct.bas.output:
--------------------------------------------------------------------------------
1 | STRUCT1
2 | 0 0
3 | 2 10
4 | 10 10
5 | 11 11
6 | STRUCT2
7 | 100 50
8 | STRUCT3
9 | 10
10 | STRUCT4
11 | 1 2 3.0 4.0 str 5 abc-1 2.5
12 |
--------------------------------------------------------------------------------
/src/test/resources/swap.bas:
--------------------------------------------------------------------------------
1 | 10 A% = 10 : B% = 20
2 | 20 PRINT A%, B%
3 | 30 SWAP A%, B%
4 | 40 PRINT A%, B%
5 | 50 A# = 10 : B# = 20
6 | 60 PRINT A#, B#
7 | 70 SWAP A#, B#
8 | 80 PRINT A#, B#
9 | 90 A! = 10 : B! = 20
10 | 100 PRINT A!, B!
11 | 110 SWAP A!, B!
12 | 120 PRINT A!, B!
13 | 130 A@ = 10 : B@ = 20
14 | 140 PRINT A@, B@
15 | 150 SWAP A@, B@
16 | 160 PRINT A@, B@
17 | 170 A$ = "10" : B$ = "20"
18 | 180 PRINT A$, B$
19 | 190 SWAP A$, B$
20 | 200 PRINT A$, B$
21 |
--------------------------------------------------------------------------------
/src/test/resources/swap.bas.output:
--------------------------------------------------------------------------------
1 | 10 20
2 | 20 10
3 | 10.0 20.0
4 | 20.0 10.0
5 | 10.0 20.0
6 | 20.0 10.0
7 | 10 20
8 | 20 10
9 | 1020
10 | 2010
11 |
--------------------------------------------------------------------------------
/src/test/resources/udf.bas:
--------------------------------------------------------------------------------
1 | 10 DEF FNsq(X) = X * X
2 | 20 DEF FNmul(X, Y) = X * Y
3 | 30 PRINT FNsq(10), FNsq(2)
4 | 40 PRINT FNmul(2, 3)
5 |
--------------------------------------------------------------------------------
/src/test/resources/udf.bas.output:
--------------------------------------------------------------------------------
1 | 100.0 4.0
2 | 6.0
3 |
--------------------------------------------------------------------------------
/src/test/resources/while.bas:
--------------------------------------------------------------------------------
1 | 10 LET I% = 1
2 | 20 WHILE I% <= 3
3 | 30 PRINT I%
4 | 40 I% = I% + 1
5 | 50 WEND
6 |
--------------------------------------------------------------------------------
/src/test/resources/while.bas.output:
--------------------------------------------------------------------------------
1 | 1
2 | 2
3 | 3
4 |
--------------------------------------------------------------------------------
/src/test/resources/write.bas:
--------------------------------------------------------------------------------
1 | 10 WRITE "ABC", 12, 32@, 1.2
2 | 20 WRITE "abc", 12, 32@, 1.2
3 |
--------------------------------------------------------------------------------
/src/test/resources/write.bas.output:
--------------------------------------------------------------------------------
1 | "ABC",12,32,1.2
2 | "abc",12,32,1.2
3 |
--------------------------------------------------------------------------------