Skip to content

Commit 9a2a4a5

Browse files
committed
Implement FOR/NEXT loops
1 parent faf9c1c commit 9a2a4a5

16 files changed

+358
-7
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ install(DIRECTORY ${PROJECT_SOURCE_DIR}/examples DESTINATION share/${PROJECT_NAM
4444
include(CTest)
4545
enable_testing()
4646

47-
set (functional_tests abs coinflip division fib functions hello int_ mod newton pemdas remainder shell squares)
47+
set (functional_tests abs coinflip division fib for functions hello int_ mod newton pemdas remainder shell squares)
4848
foreach(test ${functional_tests})
4949
add_test(NAME ${test}_exec COMMAND sh -c "${PROJECT_BINARY_DIR}/${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/tests/${test}.bas > ${PROJECT_BINARY_DIR}/tests_${test}.out")
5050
add_test(NAME ${test}_check COMMAND ${CMAKE_COMMAND} -E compare_files ${PROJECT_SOURCE_DIR}/tests/${test}.ex ${PROJECT_BINARY_DIR}/tests_${test}.out)

LANG.md

+17
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,23 @@ Example:
7676
20 INPUT A,B
7777
30 PRINT "The product of ",A," and ",B," is ",A*B
7878

79+
### FOR var = expression TO expression (STEP expression) / NEXT I
80+
81+
Evaluates the first 'expression' and assigns the result to the index variable
82+
'var' when entering the loop. Before each iteration, the second expression is
83+
evaluated and compared to the index variable. If the limit is not exceeded,
84+
another iteration takes place. At the end of each iteration, the step
85+
'expression' is evaluated and added to the index variable. If a step 'expression'
86+
is not provided, the step defaults to `1`.
87+
88+
Example:
89+
90+
10 PRINT "Squares of integers from 1 to 10"
91+
20 FOR I = 1 TO 10
92+
30 PRINT I^2
93+
40 NEXT I
94+
50 END
95+
7996
### GOSUB expression
8097

8198
Evaluates 'expression' and jumps to the corresponding line number after saving the current line number. It may appear with with or without whitespace between `GO` and `SUB`. For example, both `GOSUB 100` and `GO SUB 100` are equivalent.

TODO.md

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ Enhancements:
44

55
Implement additional statements:
66

7-
* FOR/NEXT
87
* READ/DATA/RESTORE
98
* ON .. GOTO
109
* DEF

include/number.h

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ struct number *new_number_from_float(float f);
4141
struct number *parse_number(struct tokenizer *t);
4242
struct number *clone_number(struct number *n);
4343
struct number *add_number(struct number *x, struct number *y);
44+
struct number *subtract_number(struct number *x, struct number *y);
4445
struct number *multiply_number(struct number *x, struct number *y);
4546
struct number *divide_number(struct number *x, struct number *y);
4647
struct number *idivide_number(struct number *x, struct number *y);

include/runtime.h

+10
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,20 @@
2222
struct number;
2323
struct line;
2424

25+
struct for_state {
26+
struct number *limit;
27+
struct number *step;
28+
int target;
29+
};
30+
2531
struct line *runtime_get_first_line(void);
2632
struct line *runtime_get_line(int number);
2733
void runtime_set_line(struct line *item);
2834
void runtime_rm_line(int number);
35+
int runtime_get_line_after_nearest_next(int for_line_number, char var);
36+
37+
void runtime_set_for_state(char v, struct number *limit, struct number *step, int target);
38+
struct for_state *runtime_get_for_state(char v);
2939

3040
void runtime_reset(void);
3141
int runtime_continue(void);

include/statement.h

+9
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ struct statement {
5454
struct gosub_statement {
5555
struct expression *expression;
5656
} gosub_stmt;
57+
struct for_statement {
58+
struct var *var;
59+
struct expression *initial;
60+
struct expression *limit;
61+
struct expression *step;
62+
} for_stmt;
63+
struct next_statement {
64+
struct var *var;
65+
} next_stmt;
5766
struct rem_statement {
5867
struct rem *rem;
5968
} rem_stmt;

include/tokenizer.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
enum token_types {
2323
PRINT, IF, THEN, GOTO, INPUT, LET, GOSUB, RETURN, END, REM, RANDOMIZE,
2424
CLEAR, LIST, RENUM, RUN, STOP, TROFF, TRON, CLS, SHELL, BEEP,
25-
RND, TIME,
25+
RND, TIME, FOR, TO, STEP, NEXT,
2626
SIN, COS, TAN, COT, ATN, EXP, LOG, ABS, SGN, SQR, INT_,
2727
LTEQ, LTGT, LT, GTEQ, GTLT, GT, EQ,
2828
PLUS, MINUS, TIMES, DIVIDE, IDIVIDE, MOD,

src/number.c

+34
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,40 @@ struct number *add_number(struct number *x, struct number *y) {
135135
return clone_number(&r);
136136
}
137137

138+
struct number *subtract_number(struct number *x, struct number *y) {
139+
140+
struct number *neg;
141+
struct number *yneg;
142+
struct number *r;
143+
144+
if (x == NULL || y == NULL) {
145+
return NULL;
146+
}
147+
148+
neg = new_number_from_int(-1);
149+
if (neg == NULL) {
150+
return NULL;
151+
}
152+
153+
yneg = multiply_number(neg, y);
154+
if (yneg == NULL) {
155+
free_number(neg);
156+
return NULL;
157+
}
158+
159+
free_number(neg);
160+
161+
r = add_number(x, yneg);
162+
if (r == NULL) {
163+
free_number(yneg);
164+
return NULL;
165+
}
166+
167+
free_number(yneg);
168+
return r;
169+
170+
}
171+
138172
struct number *multiply_number(struct number *x, struct number *y) {
139173
struct number r;
140174

src/runtime.c

+56
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626

2727
#include "line.h"
2828
#include "number.h"
29+
#include "statement.h"
30+
#include "tokenizer.h"
31+
#include "var.h"
2932

3033
#include "runtime.h"
3134

@@ -51,6 +54,21 @@ struct line *runtime_get_line(int number) {
5154
return NULL;
5255
}
5356

57+
int runtime_get_line_after_nearest_next(int for_line_number, char var) {
58+
59+
struct line *cur;
60+
61+
for (cur = runtime_get_line(for_line_number); cur != NULL; cur = cur->next) {
62+
if (cur->statement->type == NEXT && cur->statement->u.next_stmt.var->value == var) {
63+
return cur->next == NULL ? -1 : cur->next->number;
64+
}
65+
66+
}
67+
68+
return -1;
69+
70+
}
71+
5472
void runtime_rm_line(int number) {
5573
struct line *list = lines;
5674
struct line *head = list;
@@ -149,6 +167,36 @@ struct number *runtime_get_var(char var) {
149167

150168
/* *** *** *** */
151169

170+
#define NFORSTATES 26
171+
struct for_state for_states[NFORSTATES] = {
172+
{ NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 },
173+
{ NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 },
174+
{ NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 },
175+
{ NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 },
176+
{ NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 },
177+
{ NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 },
178+
{ NULL, NULL, 0 }, { NULL, NULL, 0 }
179+
};
180+
181+
void runtime_set_for_state(char var, struct number *limit, struct number *step, int target) {
182+
int i = (var - 'A') % NFORSTATES;
183+
184+
free_number(for_states[i].limit);
185+
for_states[i].limit = NULL;
186+
free_number(for_states[i].step);
187+
for_states[i].step = NULL;
188+
189+
for_states[i].limit = clone_number(limit);
190+
for_states[i].step = clone_number(step);
191+
for_states[i].target = target;
192+
}
193+
194+
struct for_state *runtime_get_for_state(char var) {
195+
return &for_states[(var - 'A') % NFORSTATES];
196+
}
197+
198+
/* *** *** *** */
199+
152200
int done = 0;
153201

154202
void runtime_stop(void) {
@@ -225,6 +273,14 @@ void runtime_reset(void) {
225273
vars[i] = NULL;
226274
}
227275

276+
for (i = 0; i < NFORSTATES; i++) {
277+
free_number(for_states[i].limit);
278+
for_states[i].limit = NULL;
279+
free_number(for_states[i].step);
280+
for_states[i].step = NULL;
281+
for_states[i].target = 0;
282+
}
283+
228284
if (lines != NULL) {
229285
free_line(lines);
230286
lines = NULL;

0 commit comments

Comments
 (0)