Break loop.

This commit is contained in:
retoor 2025-11-23 13:53:36 +01:00
parent 63f9eb48a4
commit 050e85b72a
17 changed files with 708 additions and 36 deletions

View File

@ -1,6 +1,6 @@
CC = gcc
CFLAGS = -Wall -Wextra -O2 -Isrc
LDFLAGS = -lm
LDFLAGS = -lm -lpthread
SRC_DIR = src
TEST_DIR = tests

20
TODO2.md Normal file
View File

@ -0,0 +1,20 @@
# TO BUILD
1. Read the whole source code in src.
2. Add File I/O Functions to the scripting language
2.1 Add working tests within the tests directory.
3. Implement async I/O functions for File I/O / Socket I/O / Async I/O. The basic asyncio for methods should be we working using threads so it's a multicore application. It must be a safe way of threading but still performance.
3.1 Add working tests within the tests directory.
3.2 Add a nice example script source file (.rc) to the examples directory that shows file I/O, async I/O, and socket I/O.
4. Add double data type to the language
4.1 Add working tests within the tests directory.
5. Implement break and continue statements in the language
5.1 Add working tests within the tests directory.
6. Update Makefile, README.md, and TUTORIAL.md.
# IMPORTANT
1. Consistency (By researching how other things are implemented)
2. Performance (But readability is also important)
3. Ease of use for the user of the new language (No weird caveats)
4. Safety (for using the langauge by defensive coding)

View File

@ -73,11 +73,20 @@ void statement() {
pc++;
while (pc < MAX_TOK && pc < tk_idx && tokens[pc].type != '}' && tokens[pc].type != 0) {
statement();
if (ax == -999) break;
if (ax == -999 || ax == -998 || ax == -997) {
int brace = 1;
while (brace > 0 && pc < MAX_TOK && pc < tk_idx && tokens[pc].type != 0) {
if (tokens[pc].type == '{') brace++;
if (tokens[pc].type == '}') brace--;
pc++;
}
return;
}
}
match('}');
}
else if (tokens[pc].type == Int || tokens[pc].type == Char) {
else if (tokens[pc].type == Int || tokens[pc].type == Char || tokens[pc].type == Double) {
int var_type = tokens[pc].type;
pc++;
while (pc < MAX_TOK && pc < tk_idx && tokens[pc].type != ';') {
while (pc < MAX_TOK && pc < tk_idx && tokens[pc].type == '*') pc++;
@ -94,6 +103,7 @@ void statement() {
int addr = sp;
Symbol *s = &locals[loc_cnt++];
strncpy(s->name, t->text, t->val); s->name[t->val] = 0;
s->type = var_type;
s->addr = addr;
s->is_array = 0;
@ -129,42 +139,45 @@ void statement() {
match(')');
if (cond) {
statement();
if (ax == -999) return;
if (ax == -999 || ax == -998 || ax == -997) return;
if (pc < MAX_TOK && pc < tk_idx && tokens[pc].type == Else) { pc++; skip_block(); }
} else {
skip_block();
if (pc < MAX_TOK && pc < tk_idx && tokens[pc].type == Else) {
pc++;
statement();
if (ax == -999) return;
if (ax == -999 || ax == -998 || ax == -997) return;
}
}
}
else if (tokens[pc].type == While) {
pc++;
int loop_start = pc;
match('(');
long cond = expression();
match(')');
if (!cond) {
skip_block();
} else {
int body_start;
int iteration_count = 0;
while (1) {
if (++iteration_count > 1000000) {
error("Potential infinite loop detected");
}
statement();
if (ax == -999) return;
int save_pc = pc;
if (loop_start >= MAX_TOK || loop_start >= tk_idx) {
error("Loop start out of bounds");
}
pc = loop_start;
match('(');
cond = expression();
long cond = expression();
match(')');
if (!cond) { pc = save_pc; break; }
if (!cond) {
skip_block();
break;
}
body_start = pc;
statement();
if (ax == -999) return;
if (ax == -998) {
ax = 0;
pc = body_start;
skip_block();
break;
}
if (ax == -997) {
ax = 0;
}
}
}
@ -175,6 +188,16 @@ void statement() {
match(';');
ax = -999;
}
else if (tokens[pc].type == Break) {
pc++;
match(';');
ax = -998;
}
else if (tokens[pc].type == Continue) {
pc++;
match(';');
ax = -997;
}
else if (tokens[pc].type == Printf) {
pc++;
match('(');
@ -187,11 +210,16 @@ void statement() {
char *p = fmt;
while (*p) {
if (*p == '%' && (p[1] == 'd' || p[1] == 's')) {
if (*p == '%' && (p[1] == 'd' || p[1] == 's' || p[1] == 'f')) {
p++;
match(',');
long val = expression();
if (*p == 'd') printf("%ld", val);
else if (*p == 'f') {
union { double d; long l; } u;
u.l = val;
printf("%f", u.d);
}
else if (*p == 's') {
char *str = (char*)val;
if (str) printf("%s", str);
@ -214,7 +242,7 @@ void scan_functions() {
int i = 0;
while (i < MAX_TOK && i < tk_idx && tokens[i].type != 0) {
if (i + 2 < MAX_TOK && i + 2 < tk_idx &&
(tokens[i].type == Int || tokens[i].type == Char) &&
(tokens[i].type == Int || tokens[i].type == Char || tokens[i].type == Double) &&
tokens[i+1].type == Id && tokens[i+2].type == '(') {
if (func_cnt >= 100) {
@ -227,7 +255,7 @@ void scan_functions() {
i += 3;
int params = 0;
while(i < MAX_TOK && i < tk_idx && tokens[i].type != ')') {
if (tokens[i].type == Int || tokens[i].type == Char) {
if (tokens[i].type == Int || tokens[i].type == Char || tokens[i].type == Double) {
params++;
i++;
while (i < MAX_TOK && i < tk_idx && tokens[i].type == '*') i++;

View File

@ -448,6 +448,274 @@ long native_endswith(long *args, int argc) {
return strcmp(str + str_len - suffix_len, suffix) == 0;
}
long native_fopen(long *args, int argc) {
if (!args || argc < 2) {
return 0;
}
char *filename = (char*)args[0];
char *mode = (char*)args[1];
if (!filename || !mode) {
return 0;
}
FILE *f = fopen(filename, mode);
return (long)f;
}
long native_fclose(long *args, int argc) {
if (!args || argc < 1) {
return -1;
}
FILE *f = (FILE*)args[0];
if (!f) {
return -1;
}
return fclose(f);
}
long native_fread(long *args, int argc) {
if (!args || argc < 3) {
return -1;
}
FILE *f = (FILE*)args[0];
int addr = (int)args[1];
int size = (int)args[2];
if (!f || addr < 0 || addr >= MEM_SIZE || size <= 0) {
return -1;
}
if (addr + size > MEM_SIZE) {
size = MEM_SIZE - addr;
}
char temp_buf[8192];
if (size > 8192) size = 8192;
int result = fread(temp_buf, 1, size, f);
if (result > 0) {
for (int i = 0; i < result && addr + i < MEM_SIZE; i++) {
memory[addr + i] = temp_buf[i];
}
}
return result;
}
long native_fwrite(long *args, int argc) {
if (!args || argc < 3) {
return -1;
}
FILE *f = (FILE*)args[0];
long buf_arg = args[1];
int size = (int)args[2];
if (!f || size <= 0) {
return -1;
}
if (buf_arg > MEM_SIZE * 8 || buf_arg < 0) {
return fwrite((char*)buf_arg, 1, size, f);
}
if (buf_arg >= MEM_SIZE) {
return -1;
}
char temp_buf[8192];
if (size > 8192) size = 8192;
if (buf_arg + size > MEM_SIZE) {
size = MEM_SIZE - buf_arg;
}
for (int i = 0; i < size && buf_arg + i < MEM_SIZE; i++) {
temp_buf[i] = (char)memory[buf_arg + i];
}
return fwrite(temp_buf, 1, size, f);
}
long native_fgets(long *args, int argc) {
static char empty_str[] = "";
if (!args || argc < 2) {
return (long)empty_str;
}
FILE *f = (FILE*)args[0];
int max_size = (int)args[1];
if (!f || max_size <= 0) {
return (long)empty_str;
}
if (str_pool_idx >= STR_POOL_SIZE) {
error("String pool overflow");
return (long)empty_str;
}
char *result = &str_pool[str_pool_idx];
if (max_size > STR_POOL_SIZE - str_pool_idx) {
max_size = STR_POOL_SIZE - str_pool_idx;
}
if (fgets(result, max_size, f) == NULL) {
return (long)empty_str;
}
int len = strlen(result);
str_pool_idx += len + 1;
return (long)result;
}
long native_fputs(long *args, int argc) {
if (!args || argc < 2) {
return -1;
}
FILE *f = (FILE*)args[0];
char *str = (char*)args[1];
if (!f || !str) {
return -1;
}
return fputs(str, f);
}
long native_feof(long *args, int argc) {
if (!args || argc < 1) {
return 1;
}
FILE *f = (FILE*)args[0];
if (!f) {
return 1;
}
return feof(f);
}
long native_ftell(long *args, int argc) {
if (!args || argc < 1) {
return -1;
}
FILE *f = (FILE*)args[0];
if (!f) {
return -1;
}
return ftell(f);
}
long native_fseek(long *args, int argc) {
if (!args || argc < 3) {
return -1;
}
FILE *f = (FILE*)args[0];
long offset = args[1];
int whence = (int)args[2];
if (!f) {
return -1;
}
return fseek(f, offset, whence);
}
long native_fremove(long *args, int argc) {
if (!args || argc < 1) {
return -1;
}
char *filename = (char*)args[0];
if (!filename) {
return -1;
}
return remove(filename);
}
long native_frename(long *args, int argc) {
if (!args || argc < 2) {
return -1;
}
char *oldname = (char*)args[0];
char *newname = (char*)args[1];
if (!oldname || !newname) {
return -1;
}
return rename(oldname, newname);
}
long native_SEEK_SET(long *args, int argc) {
return SEEK_SET;
}
long native_SEEK_CUR(long *args, int argc) {
return SEEK_CUR;
}
long native_SEEK_END(long *args, int argc) {
return SEEK_END;
}
long native_int_to_double(long *args, int argc) {
if (!args || argc < 1) {
return 0;
}
union { double d; long l; } u;
u.d = (double)args[0];
return u.l;
}
long native_double_to_int(long *args, int argc) {
if (!args || argc < 1) {
return 0;
}
union { double d; long l; } u;
u.l = args[0];
return (long)u.d;
}
long native_double_add(long *args, int argc) {
if (!args || argc < 2) {
return 0;
}
union { double d; long l; } u1, u2, result;
u1.l = args[0];
u2.l = args[1];
result.d = u1.d + u2.d;
return result.l;
}
long native_double_sub(long *args, int argc) {
if (!args || argc < 2) {
return 0;
}
union { double d; long l; } u1, u2, result;
u1.l = args[0];
u2.l = args[1];
result.d = u1.d - u2.d;
return result.l;
}
long native_double_mul(long *args, int argc) {
if (!args || argc < 2) {
return 0;
}
union { double d; long l; } u1, u2, result;
u1.l = args[0];
u2.l = args[1];
result.d = u1.d * u2.d;
return result.l;
}
long native_double_div(long *args, int argc) {
if (!args || argc < 2) {
return 0;
}
union { double d; long l; } u1, u2, result;
u1.l = args[0];
u2.l = args[1];
if (u2.d != 0.0) {
result.d = u1.d / u2.d;
} else {
result.d = 0.0;
}
return result.l;
}
void register_native_functions() {
register_native_func("socket", native_socket);
register_native_func("bind", native_bind);
@ -475,4 +743,24 @@ void register_native_functions() {
register_native_func("replace", native_replace);
register_native_func("startswith", native_startswith);
register_native_func("endswith", native_endswith);
register_native_func("fopen", native_fopen);
register_native_func("fclose", native_fclose);
register_native_func("fread", native_fread);
register_native_func("fwrite", native_fwrite);
register_native_func("fgets", native_fgets);
register_native_func("fputs", native_fputs);
register_native_func("feof", native_feof);
register_native_func("ftell", native_ftell);
register_native_func("fseek", native_fseek);
register_native_func("fremove", native_fremove);
register_native_func("frename", native_frename);
register_native_func("SEEK_SET", native_SEEK_SET);
register_native_func("SEEK_CUR", native_SEEK_CUR);
register_native_func("SEEK_END", native_SEEK_END);
register_native_func("int_to_double", native_int_to_double);
register_native_func("double_to_int", native_double_to_int);
register_native_func("double_add", native_double_add);
register_native_func("double_sub", native_double_sub);
register_native_func("double_mul", native_double_mul);
register_native_func("double_div", native_double_div);
}

View File

@ -35,6 +35,12 @@ long factor() {
pc++;
return t->val;
}
else if (t->type == Dbl) {
pc++;
union { double d; long l; } u;
u.d = t->dval;
return u.l;
}
else if (t->type == Str) {
pc++;
return (long)t->text;
@ -256,6 +262,26 @@ long relational() {
return val;
}
long logical_and() {
long val = relational();
while (pc < MAX_TOK && tokens[pc].type == And) {
pc++;
long val2 = relational();
val = val && val2;
}
return val;
}
long logical_or() {
long val = logical_and();
while (pc < MAX_TOK && tokens[pc].type == Or) {
pc++;
long val2 = logical_and();
val = val || val2;
}
return val;
}
long expression() {
if (pc >= MAX_TOK) return 0;
if (tokens[pc].type == '*') {
@ -327,5 +353,5 @@ long expression() {
}
}
return relational();
return logical_or();
}

View File

@ -2,6 +2,8 @@
#define PARSER_H
long expression();
long logical_or();
long logical_and();
long relational();
long add();
long term();

View File

@ -34,11 +34,14 @@ void tokenize(char *src) {
if (!strcmp(buf, "int")) t->type = Int;
else if (!strcmp(buf, "char")) t->type = Char;
else if (!strcmp(buf, "double")) t->type = Double;
else if (!strcmp(buf, "if")) t->type = If;
else if (!strcmp(buf, "else")) t->type = Else;
else if (!strcmp(buf, "while")) t->type = While;
else if (!strcmp(buf, "return")) t->type = Return;
else if (!strcmp(buf, "printf")) t->type = Printf;
else if (!strcmp(buf, "break")) t->type = Break;
else if (!strcmp(buf, "continue")) t->type = Continue;
else t->type = Id;
t->val = len;
@ -46,9 +49,20 @@ void tokenize(char *src) {
continue;
}
if (isdigit(*s)) {
if (isdigit(*s) || (*s == '.' && s[1] && isdigit(s[1]))) {
char *start = s;
int has_dot = 0;
while (*s && (isdigit(*s) || (*s == '.' && !has_dot))) {
if (*s == '.') has_dot = 1;
s++;
}
if (has_dot) {
t->type = Dbl;
t->dval = strtod(start, NULL);
} else {
t->type = Num;
t->val = strtol(s, &s, 10);
t->val = strtol(start, NULL, 10);
}
continue;
}

View File

@ -8,13 +8,14 @@
#define STR_POOL_SIZE 100000
enum {
Num = 128, Str, Id, Int, Char, Else, If, While, Return, Printf,
Assign, Eq, Ne, Lt, Gt, Le, Ge, Or, And, Inc, Dec
Num = 128, Dbl, Str, Id, Int, Char, Double, Else, If, While, Return, Printf,
Assign, Eq, Ne, Lt, Gt, Le, Ge, Or, And, Inc, Dec, Break, Continue
};
typedef struct {
int type;
long val;
double dval;
char *text;
} Token;

View File

@ -0,0 +1,81 @@
int main() {
printf("=== Break and Continue Tests ===\n");
printf("Test 1: Break statement\n");
int count = 0;
while (count < 10) {
count = count + 1;
if (count == 5) {
printf("Breaking at count = %d\n", count);
break;
}
printf("count = %d\n", count);
}
printf("Final count after break: %d\n", count);
printf("PASS: Break works\n");
printf("Test 2: Continue statement\n");
int i = 0;
int sum = 0;
while (i < 10) {
i = i + 1;
if (i == 3 || i == 7) {
printf("Skipping i = %d\n", i);
continue;
}
sum = sum + i;
printf("Adding i = %d, sum = %d\n", i, sum);
}
printf("Final sum (skipped 3 and 7): %d\n", sum);
printf("PASS: Continue works\n");
printf("Test 3: Break in nested if\n");
int j = 0;
while (j < 10) {
j = j + 1;
if (j > 3) {
if (j == 6) {
printf("Breaking at j = %d (nested if)\n", j);
break;
}
}
printf("j = %d\n", j);
}
printf("Final j: %d\n", j);
printf("PASS: Nested break works\n");
printf("Test 4: Continue in nested if\n");
int k = 0;
int even_sum = 0;
while (k < 10) {
k = k + 1;
if (k > 0) {
int rem = k - (k / 2) * 2;
if (rem == 1) {
continue;
}
}
even_sum = even_sum + k;
}
printf("Sum of even numbers 1-10: %d\n", even_sum);
printf("PASS: Nested continue works\n");
printf("Test 5: Multiple breaks and continues\n");
int n = 0;
int result = 0;
while (n < 20) {
n = n + 1;
if (n < 5) {
continue;
}
if (n > 15) {
break;
}
result = result + 1;
}
printf("Numbers counted between 5 and 15: %d\n", result);
printf("PASS: Multiple break/continue works\n");
printf("\n=== All Break/Continue Tests Completed ===\n");
return 0;
}

14
tests/break_test1.rc Normal file
View File

@ -0,0 +1,14 @@
int main() {
printf("=== Break Test ===\n");
int count = 0;
while (count < 10) {
count = count + 1;
if (count == 5) {
printf("Breaking at count = %d\n", count);
break;
}
printf("count = %d\n", count);
}
printf("Final count: %d\n", count);
return 0;
}

13
tests/break_test2.rc Normal file
View File

@ -0,0 +1,13 @@
int main() {
printf("=== Break Test ===\n");
int count = 0;
while (count < 10) {
count = count + 1;
if (count == 5) {
break;
}
printf("count = %d\n", count);
}
printf("Final count: %d\n", count);
return 0;
}

14
tests/continue_or_test.rc Normal file
View File

@ -0,0 +1,14 @@
int main() {
printf("Test OR with continue\n");
int i = 0;
while (i < 10) {
i = i + 1;
if (i == 3 || i == 7) {
printf("Skip %d\n", i);
continue;
}
printf("i = %d\n", i);
}
printf("Done\n");
return 0;
}

View File

@ -0,0 +1,14 @@
int main() {
printf("Start\n");
int i = 0;
while (i < 5) {
i = i + 1;
if (i == 3) {
printf("Skipping i = %d\n", i);
continue;
}
printf("i = %d\n", i);
}
printf("Done\n");
return 0;
}

53
tests/double_test.rc Normal file
View File

@ -0,0 +1,53 @@
int main() {
printf("=== Double Data Type Tests ===\n");
printf("Test 1: Double variable declaration and assignment\n");
double pi = 3.14159;
printf("pi = %f\n", pi);
printf("PASS: Double variable works\n");
printf("Test 2: Double arithmetic using helper functions\n");
double a = 10.5;
double b = 2.5;
printf("a = %f, b = %f\n", a, b);
double sum = double_add(a, b);
printf("a + b = %f\n", sum);
double diff = double_sub(a, b);
printf("a - b = %f\n", diff);
double prod = double_mul(a, b);
printf("a * b = %f\n", prod);
double quot = double_div(a, b);
printf("a / b = %f\n", quot);
printf("PASS: Double arithmetic works\n");
printf("Test 3: Type conversions\n");
int x = 42;
double dx = int_to_double(x);
printf("int %d converted to double: %f\n", x, dx);
double y = 99.9;
int iy = double_to_int(y);
printf("double %f converted to int: %d\n", y, iy);
printf("PASS: Type conversions work\n");
printf("Test 4: Double with mathematical functions\n");
double num = 16.0;
double sq = sqrt(double_to_int(num));
double sq_double = int_to_double(sq);
printf("sqrt(%f) = %f\n", num, sq_double);
printf("PASS: Math functions work with doubles\n");
printf("Test 5: Complex calculation\n");
double radius = 5.0;
double pi2 = 3.14159;
double area = double_mul(pi2, double_mul(radius, radius));
printf("Circle area (r=%f): %f\n", radius, area);
printf("PASS: Complex calculations work\n");
printf("\n=== All Double Tests Completed ===\n");
return 0;
}

82
tests/file_io_test.rc Normal file
View File

@ -0,0 +1,82 @@
int main() {
printf("=== File I/O Tests ===\n");
printf("Test 1: Writing to a file\n");
int f = fopen("test_output.txt", "w");
if (f == 0) {
printf("ERROR: Could not open file for writing\n");
return 1;
}
fputs(f, "Hello, File I/O!\n");
fputs(f, "This is line 2.\n");
fputs(f, "Testing file operations.\n");
fclose(f);
printf("PASS: File written successfully\n");
printf("Test 2: Reading from file using fgets\n");
f = fopen("test_output.txt", "r");
if (f == 0) {
printf("ERROR: Could not open file for reading\n");
return 1;
}
char *line1 = fgets(f, 256);
printf("Line 1: %s", line1);
char *line2 = fgets(f, 256);
printf("Line 2: %s", line2);
char *line3 = fgets(f, 256);
printf("Line 3: %s", line3);
fclose(f);
printf("PASS: File read successfully\n");
printf("Test 3: File position operations\n");
f = fopen("test_output.txt", "r");
if (f == 0) {
printf("ERROR: Could not open file\n");
return 1;
}
int pos = ftell(f);
printf("Initial position: %d\n", pos);
fseek(f, 7, SEEK_SET());
pos = ftell(f);
printf("Position after seek: %d\n", pos);
char *partial = fgets(f, 256);
printf("Read after seek: %s", partial);
fclose(f);
printf("PASS: File positioning works\n");
printf("Test 4: End of file detection\n");
f = fopen("test_output.txt", "r");
if (f == 0) {
printf("ERROR: Could not open file\n");
return 1;
}
int line_count = 0;
while (feof(f) == 0) {
char *line = fgets(f, 256);
if (strlen(line) > 0) {
line_count = line_count + 1;
}
}
printf("Total lines read: %d\n", line_count);
fclose(f);
printf("PASS: EOF detection works\n");
printf("Test 5: File rename operation\n");
int result = frename("test_output.txt", "test_renamed.txt");
if (result == 0) {
printf("PASS: File renamed successfully\n");
} else {
printf("ERROR: File rename failed\n");
}
printf("Test 6: File removal\n");
result = fremove("test_renamed.txt");
if (result == 0) {
printf("PASS: File removed successfully\n");
} else {
printf("ERROR: File removal failed\n");
}
printf("\n=== All File I/O Tests Completed ===\n");
return 0;
}

9
tests/or_test.rc Normal file
View File

@ -0,0 +1,9 @@
int main() {
printf("Test OR\n");
int i = 3;
if (i == 3 || i == 7) {
printf("Match\n");
}
printf("Done\n");
return 0;
}

View File

@ -0,0 +1,13 @@
int main() {
printf("Start\n");
int i = 0;
while (i < 5) {
i = i + 1;
printf("i = %d\n", i);
if (i == 3) {
break;
}
}
printf("After loop, i = %d\n", i);
return 0;
}