diff --git a/.vscode/settings.json b/.vscode/settings.json index afd1bd7..01b3dba 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -51,5 +51,7 @@ } }, ] - } + }, + "terminal.integrated.fontSize": 16, + "editor.fontSize": 16 } \ No newline at end of file diff --git a/easyc.ts b/easyc.ts index d0e5a20..a533551 100644 --- a/easyc.ts +++ b/easyc.ts @@ -6,7 +6,7 @@ import child_process from "node:child_process"; const { execFileSync } = child_process; -function indent(str: string, n: number): string { +function indent(str: string, n: number = 1): string { return str .split(/\r?\n/) .map((line) => " ".repeat(n) + line) @@ -343,7 +343,7 @@ class ArrayType extends Type { zero(code: string[]): string { if (!this.dynamic) return "{0}"; const r = this.hi.context().R(); - code.push(`void *${r} AUTOFREE = malloc(sizeof(${this.type.c()}) * (${this.sz()}));`); + code.push(`void *${r} AUTOFREE_ARRAY = malloc(sizeof(${this.type.c()}) * (${this.sz()}));`); return `{ .data = ${r} }`; } typedef = (alias: string) => `typedef ${this.c()} ${alias}`; @@ -714,6 +714,7 @@ class SET extends Statement { `type mismatch in SET: ${type.constructor.name} !== ${this.expression.type.constructor.name} at ${this.token}` ); } + const value = this.expression.v(code); code.push(`${reference} = ${value};`); } @@ -858,8 +859,11 @@ class INPUT extends Statement { const { type, reference } = expand_variable_reference(variable, variable_reference, code); if (type instanceof StringType) { - code.push(`scanf("%4095s", ${reference}.data);`); - code.push(`${reference}.sz = strlen(${reference}.data);`); + code.push(`{`); + code.push(indent(`char buf[4096];`, 1)); + code.push(indent(`scanf("%4095s", buf);`, 1)); + code.push(indent(`${reference} = make_string(buf, strlen(buf));`, 1)); + code.push(`}`); } else if (type instanceof IntegerType) code.push(`scanf("%d", &${reference});`); else if (type instanceof RealType) code.push(`scanf("%lf", &${reference});`); else throw new GenerateError(`unsupported variable '${variable}' type in INPUT at ${variable.token}`); @@ -1058,10 +1062,18 @@ function string_compare(left: Expression, right: Expression, operation: string, if (operation !== "==" && operation !== "!=") return null; function is_string_type(e: Expression): [boolean, string | null] { - if (!(e instanceof VariableReference)) return [false, null]; - const variable = find_existing_variable(e); - const { type, reference } = expand_variable_reference(variable, e, []); - return [type instanceof StringType, reference]; + if (e instanceof VariableReference) { + const variable = find_existing_variable(e); + const { type, reference } = expand_variable_reference(variable, e, []); + return [type instanceof StringType, reference]; + } + if (e.type instanceof StringType) return [true, e.v(code)]; + if (e instanceof FunctionCall) { + const func = e.context().functions[e.name]; + if (!func) throw new GenerateError(`unknown function '${e.name}' at ${e.token}`); + if (func.type instanceof StringType) return [true, e.v(code)]; + } + return [false, null]; } const [left_string, left_reference] = is_string_type(left); @@ -1070,7 +1082,15 @@ function string_compare(left: Expression, right: Expression, operation: string, if (left_string || right_string) { const cmp = operation === "!=" ? "!=" : "=="; const r = left.context().R(); - code.push(`const int ${r} = strcmp(${left_reference}.data, ${right_reference}.data) ${cmp} 0;`); + const left_sz = `${left_reference}.sz`; + const right_sz = `${right_reference}.sz`; + console.log(right_reference); + code.push( + `const int ${r} = ` + + `${left_sz} == ${right_sz} ` + + `&& ` + + `memcmp(${left_reference}.data, ${right_reference}.data, ${left_sz}) ${cmp} 0;` + ); return r; } return null; @@ -1291,6 +1311,8 @@ function expand_variable_reference(variable: Variable, variable_reference: Varia let type: Type = variable.type; let reference = variable.name; + const owner = { name: variable.name, type: variable.type }; + for (const part of variable_reference.parts) { if (part instanceof VariableSubscript) { if (type instanceof AliasType) type = type.reference_type; @@ -1329,7 +1351,7 @@ function expand_variable_reference(variable: Variable, variable_reference: Varia throw new GenerateError(`unexpected variable reference part '${part}' at ${variable.token}`); } } - return { type, reference }; + return { type, reference, owner }; } function find_existing_variable(v: VariableReference): Variable { diff --git a/life.easy b/life.easy index 3b8bd4e..faf6450 100644 --- a/life.easy +++ b/life.easy @@ -118,7 +118,7 @@ PROGRAM Life: CALL evolution; - CALL rt_pause(0.3); + CALL runtime_pause(0.3); IF i MOD 10 = 0 THEN CALL glider(40, 10); diff --git a/runtime.c b/runtime.c index f00429a..2119ebb 100644 --- a/runtime.c +++ b/runtime.c @@ -15,23 +15,7 @@ void free(void *ptr); #define TRUE 1 #define FALSE 0 -#define AUTOFREE __attribute__((cleanup(free_array))) - -typedef struct -{ - void *data; -} ARRAY; - -void free_array(void *ptr) -{ - ARRAY *array = (ARRAY *)ptr; - void *data = array->data; - if (data) - { - free(data); - array->data = NULL; - } -} +#define AUTOFREE_ARRAY __attribute__((cleanup(free_array))) static inline void $sleep(double seconds) { @@ -49,7 +33,7 @@ typedef struct { int sz; int immutable; - char data[4096]; + char *data; } STR; typedef struct @@ -57,17 +41,103 @@ typedef struct const STR *filename; int line; int character; -} Location; +} L; + +typedef struct string_list +{ + struct string_list *next; + STR *str; +} string_list; + +static string_list *strings = NULL; + +void print_string(STR s, const char *prefix) +{ + char *buf = alloca(s.sz + 1); + memcpy(buf, s.data, s.sz); + buf[s.sz] = '\0'; + printf("%s: '%s'\n", prefix, buf); +} -int FIX(double v) { return (int)v; } +STR make_string(const char *data, int sz) +{ + void *ptr = malloc(sz); + if (!ptr) + { + fprintf(stderr, "make_string|data: out of memory\n"); + exit(1); + } + STR *str = malloc(sizeof(STR)); + if (!str) + { + fprintf(stderr, "make_string|str: out of memory\n"); + exit(1); + } + *str = (STR){.sz = sz, .data = ptr, .immutable = 0}; + memcpy(str->data, data, sz); + + string_list *reference = malloc(sizeof(string_list)); + if (!reference) + { + fprintf(stderr, "make_string|list: out of memory\n"); + exit(1); + } + reference->str = str; + reference->next = strings; + strings = reference; + return *str; +} + +void free_strings() +{ + string_list *list = strings; + while (list) + { + string_list *next = list->next; + if (list->str->data && !list->str->immutable) + { + memset(list->str->data, 0, list->str->sz); + free(list->str->data); + list->str->data = NULL; + + memset(list->str, 0, sizeof(STR)); + free(list->str); + list->str = NULL; + } + free(list); + list = next; + } + strings = NULL; +} + +typedef struct +{ + void *data; +} ARRAY; + +void free_array(void *ptr) +{ + ARRAY *array = (ARRAY *)ptr; + if (array->data) + { + free(array->data); + array->data = NULL; + } +} + +int FIX(double v) +{ + return (int)v; +} double FLOAT(int v) { return (double)v; } double FLOOR(double v) { return (double)((int)v); } int LENGTH(STR s) { return s.sz; } STR CHARACTER(int c) { - STR v = {.sz = 1, .data = {c, 0}}; - return v; + char cbuf = (char)c; + STR s = make_string(&cbuf, 1); + return s; } STR SUBSTR(STR from, int start, int length) @@ -99,8 +169,7 @@ STR SUBSTR(STR from, int start, int length) fprintf(stderr, "substr (%s): start + length > size (%d + %d = %d > %d)\n", buf, start, length, start + length, from.sz); exit(1); } - STR v = {.sz = length}; - memcpy(v.data, from.data + start, length); + STR v = make_string(from.data + start, length); return v; } @@ -132,20 +201,20 @@ STR $concat(const char *fmt, ...) else if (*fmt == 'S') { const STR *arg = va_arg(args, STR *); - n = arg->sz; + n = (int)sizeof(buf) - sz < arg->sz ? (int)sizeof(buf) - sz : arg->sz; memcpy(buf + sz, arg->data, n); } else if (*fmt == 'A') { const STR arg = va_arg(args, STR); - n = arg.sz; + n = (int)sizeof(buf) - sz < arg.sz ? (int)sizeof(buf) - sz : arg.sz; memcpy(buf + sz, arg.data, n); } sz += n; } va_end(args); - STR v = {.sz = sz}; - memcpy(v.data, buf, sz); + + STR v = make_string(buf, sz); return v; } @@ -190,7 +259,7 @@ void $output(const char *fmt, ...) putchar('\n'); } -void rt_pause(double seconds) +void runtime_pause(double seconds) { $sleep(seconds); } @@ -206,15 +275,16 @@ void $index(int index, int lo, int hi, const STR *filename, int line, int charac int main_program(); -int main() +void $exit() { - main_program(); - return 0; + free_strings(); + exit(0); } -void $exit() +int main() { - exit(0); + main_program(); + $exit(); } #pragma clang diagnostic ignored "-Wincompatible-library-redeclaration" diff --git a/tests/array_1000/x/test.c b/tests/array_1000/x/test.c index 814f829..6caad4b 100644 --- a/tests/array_1000/x/test.c +++ b/tests/array_1000/x/test.c @@ -32,12 +32,12 @@ int main_program() $output("i", b.data[(1000) - (1000)]); n = 1000; { - void *$r1 AUTOFREE = malloc(sizeof(double) * (n - n + 1)); + void *$r1 AUTOFREE_ARRAY = malloc(sizeof(double) * (n - n + 1)); struct { double *data; } c = { .data = $r1 }; - void *$r2 AUTOFREE = malloc(sizeof(struct + void *$r2 AUTOFREE_ARRAY = malloc(sizeof(struct { double data[1 - 0 + 1]; }) * (n - n + 1)); diff --git a/tests/array_dynamic/x/test.c b/tests/array_dynamic/x/test.c index e735d5f..a3ff7ad 100644 --- a/tests/array_dynamic/x/test.c +++ b/tests/array_dynamic/x/test.c @@ -13,7 +13,7 @@ int main_program() { sz = 1024; { - void *$r1 AUTOFREE = malloc(sizeof(int) * (sz - 1 + 1)); + void *$r1 AUTOFREE_ARRAY = malloc(sizeof(int) * (sz - 1 + 1)); struct { int *data; diff --git a/tests/bf/x/test.c b/tests/bf/x/test.c index 2e7e7d5..5dc3669 100644 --- a/tests/bf/x/test.c +++ b/tests/bf/x/test.c @@ -38,19 +38,19 @@ STR bf(STR program) if (!(pc <= $r2)) break; const STR $r4 = SUBSTR(program, pc, 1); ch = $r4; - const int $r5 = strcmp(ch.data, $2.data) == 0; + const int $r5 = ch.sz == $2.sz && memcmp(ch.data, $2.data, ch.sz) == 0; if ($r5) { const int $r6 = (ptr + 1); ptr = $r6; } - const int $r7 = strcmp(ch.data, $3.data) == 0; + const int $r7 = ch.sz == $3.sz && memcmp(ch.data, $3.data, ch.sz) == 0; if ($r7) { const int $r8 = (ptr - 1); ptr = $r8; } - const int $r9 = strcmp(ch.data, $4.data) == 0; + const int $r9 = ch.sz == $4.sz && memcmp(ch.data, $4.data, ch.sz) == 0; if ($r9) { $index(ptr, 0, 299, &$F, 26, 33); @@ -58,7 +58,7 @@ STR bf(STR program) const int $r10 = (tape.data[(ptr) - (0)] + 1); tape.data[(ptr) - (0)] = $r10; } - const int $r11 = strcmp(ch.data, $5.data) == 0; + const int $r11 = ch.sz == $5.sz && memcmp(ch.data, $5.data, ch.sz) == 0; if ($r11) { $index(ptr, 0, 299, &$F, 27, 33); @@ -66,7 +66,7 @@ STR bf(STR program) const int $r12 = (tape.data[(ptr) - (0)] - 1); tape.data[(ptr) - (0)] = $r12; } - const int $r13 = strcmp(ch.data, $6.data) == 0; + const int $r13 = ch.sz == $6.sz && memcmp(ch.data, $6.data, ch.sz) == 0; if ($r13) { $index(ptr, 0, 299, &$F, 28, 57); @@ -74,7 +74,7 @@ STR bf(STR program) const STR $r14 = $concat("AA", out, $r15); out = $r14; } - const int $r16 = strcmp(ch.data, $7.data) == 0; + const int $r16 = ch.sz == $7.sz && memcmp(ch.data, $7.data, ch.sz) == 0; if ($r16) { $index(ptr, 0, 299, &$F, 30, 17); @@ -90,13 +90,13 @@ STR bf(STR program) pc = $r19; const STR $r20 = SUBSTR(program, pc, 1); ch = $r20; - const int $r21 = strcmp(ch.data, $7.data) == 0; + const int $r21 = ch.sz == $7.sz && memcmp(ch.data, $7.data, ch.sz) == 0; if ($r21) { const int $r22 = (depth + 1); depth = $r22; } - const int $r23 = strcmp(ch.data, $8.data) == 0; + const int $r23 = ch.sz == $8.sz && memcmp(ch.data, $8.data, ch.sz) == 0; if ($r23) { const int $r24 = (depth - 1); @@ -106,7 +106,7 @@ STR bf(STR program) } } } - const int $r25 = strcmp(ch.data, $8.data) == 0; + const int $r25 = ch.sz == $8.sz && memcmp(ch.data, $8.data, ch.sz) == 0; if ($r25) { $index(ptr, 0, 299, &$F, 40, 17); @@ -122,13 +122,13 @@ STR bf(STR program) pc = $r28; const STR $r29 = SUBSTR(program, pc, 1); ch = $r29; - const int $r30 = strcmp(ch.data, $8.data) == 0; + const int $r30 = ch.sz == $8.sz && memcmp(ch.data, $8.data, ch.sz) == 0; if ($r30) { const int $r31 = (depth + 1); depth = $r31; } - const int $r32 = strcmp(ch.data, $7.data) == 0; + const int $r32 = ch.sz == $7.sz && memcmp(ch.data, $7.data, ch.sz) == 0; if ($r32) { const int $r33 = (depth - 1); diff --git a/tests/character/x/test.output b/tests/character/x/test.output index e8f83fa..15f0979 100644 --- a/tests/character/x/test.output +++ b/tests/character/x/test.output @@ -1,5 +1,5 @@ 02 -1 +1 123! abc diff --git a/tests/input/x/test.c b/tests/input/x/test.c index c662a60..fa846d0 100644 --- a/tests/input/x/test.c +++ b/tests/input/x/test.c @@ -15,8 +15,11 @@ int main_program() { scanf("%d", &i); scanf("%lf", &f); - scanf("%4095s", s.data); - s.sz = strlen(s.data); + { + char buf[4096]; + scanf("%4095s", buf); + s = make_string(buf, strlen(buf)); + } const STR $r1 = $concat("AiA", $0, i, $1); $output("A", $r1); const STR $r2 = $concat("ArA", $2, f, $1); diff --git a/tests/sieve/x/test.c b/tests/sieve/x/test.c index f03a84a..777a046 100644 --- a/tests/sieve/x/test.c +++ b/tests/sieve/x/test.c @@ -84,7 +84,7 @@ int main_program() const int $r1 = (topnum > 0); if ($r1) { - void *$r2 AUTOFREE = malloc(sizeof(int) * (topnum - 1 + 1)); + void *$r2 AUTOFREE_ARRAY = malloc(sizeof(int) * (topnum - 1 + 1)); struct { int *data; diff --git a/tests/string/test.easy b/tests/string/test.easy index 8280bfa..a5af2bf 100644 --- a/tests/string/test.easy +++ b/tests/string/test.easy @@ -4,14 +4,14 @@ PROGRAM String: SET s := "abcXYZ"; SET b := s = "abcXYZ"; SET b := "abcXYZ" = s; - SET b := "abcXYZ" = "abcXYZ"; - SET b := s = s; + SET b := "abcXYZ" = "abcXYZ-*"; + SET b := s = SUBSTR(s, 0, 6); SET s := "abcXYZ"; SET b := s <> "abcXYZ"; SET b := "abcXYZ" <> s; - SET b := "abcXYZ" <> "abcXYZ"; - SET b := s <> s; + SET b := "abcXYZ" <> "abcXYZ-*"; + // SET b := s <> SUBSTR(s, 0, 6); OUTPUT SUBSTR("12345", 1, 2); OUTPUT SUBSTR(s, 1, 2); diff --git a/tests/string/x/test.c b/tests/string/x/test.c index 67c89ab..a4269f2 100644 --- a/tests/string/x/test.c +++ b/tests/string/x/test.c @@ -6,34 +6,34 @@ typedef STR STRING; STR s = {0}; int b = 0; STR $0 = { .data = "abcXYZ", .sz = 6, .immutable = 1 }; -STR $1 = { .data = "12345", .sz = 5, .immutable = 1 }; -STR $2 = { .data = "abc", .sz = 3, .immutable = 1 }; +STR $1 = { .data = "abcXYZ-*", .sz = 8, .immutable = 1 }; +STR $2 = { .data = "12345", .sz = 5, .immutable = 1 }; +STR $3 = { .data = "abc", .sz = 3, .immutable = 1 }; STR $F = { .data = "tests/string/test.easy", .sz = 22, .immutable = 1 }; int main_program() { s = $0; - const int $r1 = strcmp(s.data, $0.data) == 0; + const int $r1 = s.sz == $0.sz && memcmp(s.data, $0.data, s.sz) == 0; b = $r1; - const int $r2 = strcmp($0.data, s.data) == 0; + const int $r2 = $0.sz == s.sz && memcmp($0.data, s.data, $0.sz) == 0; b = $r2; - const int $r3 = strcmp($0.data, $0.data) == 0; + const int $r3 = $0.sz == $1.sz && memcmp($0.data, $1.data, $0.sz) == 0; b = $r3; - const int $r4 = strcmp(s.data, s.data) == 0; - b = $r4; - s = $0; - const int $r5 = strcmp(s.data, $0.data) != 0; + const STR $r4 = SUBSTR(s, 0, 6); + const int $r5 = s.sz == $r4.sz && memcmp(s.data, $r4.data, s.sz) == 0; b = $r5; - const int $r6 = strcmp($0.data, s.data) != 0; + s = $0; + const int $r6 = s.sz == $0.sz && memcmp(s.data, $0.data, s.sz) != 0; b = $r6; - const int $r7 = strcmp($0.data, $0.data) != 0; + const int $r7 = $0.sz == s.sz && memcmp($0.data, s.data, $0.sz) != 0; b = $r7; - const int $r8 = strcmp(s.data, s.data) != 0; + const int $r8 = $0.sz == $1.sz && memcmp($0.data, $1.data, $0.sz) != 0; b = $r8; - const STR $r9 = SUBSTR($1, 1, 2); + const STR $r9 = SUBSTR($2, 1, 2); $output("A", $r9); const STR $r10 = SUBSTR(s, 1, 2); $output("A", $r10); const STR $r12 = SUBSTR(s, 3, 2); - const STR $r11 = $concat("AA", $2, $r12); + const STR $r11 = $concat("AA", $3, $r12); $output("A", $r11); } diff --git a/tests/string_alloc/test.easy b/tests/string_alloc/test.easy new file mode 100644 index 0000000..da3c07a --- /dev/null +++ b/tests/string_alloc/test.easy @@ -0,0 +1,25 @@ +PROGRAM StringAlloc: + TYPE T IS STRUCTURE + FIELD x IS STRING + END STRUCTURE; + + DECLARE t T; + DECLARE huge ARRAY[10485760] OF STRING; // 1024 * 1024 * 1 + DECLARE i INTEGER; + + FUNCTION abc T: + DECLARE a T; + SET a.x := "xyz"; + SET a.x := CHARACTER(100); + RETURN a; + END FUNCTION abc; + + SET t := abc(); + OUTPUT t.x, CHARACTER(101), " "; + + FOR i := 0 TO 10485759 DO + SET huge[i] := "abcdefghijklm" || "nopqrstuvwxyz"; + END FOR; + +END PROGRAM StringAlloc; + diff --git a/tests/string_alloc/x/test.c b/tests/string_alloc/x/test.c new file mode 100644 index 0000000..7e1796e --- /dev/null +++ b/tests/string_alloc/x/test.c @@ -0,0 +1,44 @@ +#include "runtime.c" +typedef int INTEGER; +typedef double REAL; +typedef int BOOLEAN; +typedef STR STRING; +typedef struct +{ + STR x; +} T; +T t = {0}; +struct +{ + STR data[10485760 - 0 + 1]; +} huge = {0}; +int i = 0; +STR $0 = { .data = "xyz", .sz = 3, .immutable = 1 }; +STR $1 = { .data = " ", .sz = 2, .immutable = 1 }; +STR $F = { .data = "tests/string_alloc/test.easy", .sz = 28, .immutable = 1 }; +STR $3 = { .data = "abcdefghijklm", .sz = 13, .immutable = 1 }; +STR $4 = { .data = "nopqrstuvwxyz", .sz = 13, .immutable = 1 }; +T abc() +{ + T a = {0}; + a.x = $0; + const STR $r4 = CHARACTER(100); + a.x = $r4; + return a; +} +int main_program() +{ + const T $r1 = abc(); + t = $r1; + const STR $r2 = CHARACTER(101); + $output("AAA", t.x, $r2, $1); + i = 0; + while (1) + { + if (!(i <= 10485759)) break; + $index(i, 0, 10485760, &$F, 21, 14); + const STR $r3 = $concat("AA", $3, $4); + huge.data[(i) - (0)] = $r3; + i += 1; + } +} diff --git a/tests/string_alloc/x/test.output b/tests/string_alloc/x/test.output new file mode 100644 index 0000000..590ad7c --- /dev/null +++ b/tests/string_alloc/x/test.output @@ -0,0 +1 @@ +de