plugins/forth.zc
Rama main · réplica de lectura del repositorio original.
import "../std/plugin.zc"
include <math.h>
include <stdio.h>
include <stdlib.h>
include <string.h>
let stack: [long; 256];
let sp: int = 0;
let fstack: [double; 256];
let fsp: int = 0;
let zmemory: [long; 4096];
let zhere: int = 0;
let loop_stack: [long; 32];
let lsp: int = 0;
struct ForthWord {
name: [char; 32],
body: [char; 512]
}
let forth_dict: [ForthWord; 128];
let forth_dict_count: int = 0;
fn push(v: long) {
if (sp < 256) {
stack[sp] = v;
sp = sp + 1;
}
}
fn pop() -> long {
if (sp > 0) {
sp = sp - 1;
return stack[sp];
}
return 0;
}
fn peek() -> long {
if (sp > 0) {
return stack[sp - 1];
}
return 0;
}
fn fpush(v: double) {
if (fsp < 256) {
fstack[fsp] = v;
fsp = fsp + 1;
}
}
fn fpop() -> double {
if (fsp > 0) {
fsp = fsp - 1;
return fstack[fsp];
}
return 0.0;
}
fn fpeek() -> double {
if (fsp > 0) {
return fstack[fsp - 1];
}
return 0.0;
}
fn next_token(ptr: char**) -> char* {
if (ptr == (char**)0 || *ptr == (char*)0) {
return (char*)0;
}
let p = *ptr;
while (p[0] != 0 && (p[0] == ' ' || p[0] == '\t' || p[0] == '\n' || p[0] == '\r')) {
p = &p[1];
}
if (p[0] == 0) {
*ptr = p;
return (char*)0;
}
let start = p;
while (p[0] != 0 && p[0] != ' ' && p[0] != '\t' && p[0] != '\n' && p[0] != '\r') {
p = &p[1];
}
if (p[0] != 0) {
p[0] = 0;
*ptr = &p[1];
} else {
*ptr = p;
}
return start;
}
fn skip_tokens_until(ptr: char**, target: char*, alt: char*) {
while (1) {
let t = next_token(ptr);
if (t == (char*)0) {
return;
}
if (strcmp(t, target) == 0) {
return;
}
if (alt != (char*)0 && strcmp(t, alt) == 0) {
return;
}
}
}
fn eval_forth_token(token: char*, scan_ptr: char**, out_ptr: char**, api: ZApi*) {
if (strcmp(token, "+") == 0) {
push(pop() + pop());
} else if (strcmp(token, "-") == 0) {
let b = pop();
push(pop() - b);
} else if (strcmp(token, "*") == 0) {
push(pop() * pop());
} else if (strcmp(token, "/") == 0) {
let b = pop();
push(if (b != 0) pop() / b else 0);
} else if (strcmp(token, "%") == 0) {
let b = pop();
push(if (b != 0) pop() % b else 0);
} else if (strcmp(token, "<<") == 0) {
let b = pop();
push(pop() << (int)b);
} else if (strcmp(token, ">>") == 0) {
let b = pop();
push(pop() >> (int)b);
} else if (strcmp(token, "&") == 0) {
push(pop() & pop());
} else if (strcmp(token, "|") == 0) {
push(pop() | pop());
} else if (strcmp(token, "^") == 0) {
push(pop() ^ pop());
} else if (strcmp(token, "~") == 0) {
push(~pop());
} else if (strcmp(token, "=") == 0) {
push(if (pop() == pop()) 1 else 0);
} else if (strcmp(token, ">") == 0) {
let b = pop();
push(if (pop() > b) 1 else 0);
} else if (strcmp(token, "<") == 0) {
let b = pop();
push(if (pop() < b) 1 else 0);
} else if (strcmp(token, "choose") == 0) {
let f = pop();
let t = pop();
let c = pop();
push(if (c != 0) t else f);
} else if (strcmp(token, "if") == 0) {
if (pop() == 0) {
skip_tokens_until(scan_ptr, "then", "else");
}
} else if (strcmp(token, "else") == 0) {
skip_tokens_until(scan_ptr, "then", (char*)0);
} else if (strcmp(token, "then") == 0) {
// Marker
} else if (strcmp(token, "(") == 0) {
while (1) {
let t = next_token(scan_ptr);
if (t == (char*)0 || strchr(t, (int)')') != (char*)0 || strchr(t, (int)'\r') != (char*)0) {
break;
}
}
} else if (strcmp(token, "do") == 0) {
let start = pop();
let limit = pop();
let rest = *scan_ptr;
let temp = strdup(rest);
let curs = temp;
let depth = 0;
let end_p = (char*)0;
while (1) {
let t = next_token(&curs);
if (t == (char*)0) break;
if (strcmp(t, "do") == 0) depth = depth + 1;
if (strcmp(t, "loop") == 0) {
if (depth == 0) {
end_p = curs;
break;
}
depth = depth - 1;
}
}
if (end_p == (char*)0) {
api.error(api, "'do' without matching 'loop' in Forth");
return;
}
let len = (int)(end_p - temp);
*scan_ptr = &rest[len];
let body_len = (size_t)len;
let body = (char*)malloc(body_len + 1);
strncpy(body, rest, body_len);
body[len] = 0;
for i in start .. limit {
if (lsp < 32) {
loop_stack[lsp] = i;
lsp = lsp + 1;
} else {
api.error(api, "Forth Loop Stack Overflow");
return;
}
let runnable = strdup(body);
let rc = runnable;
while (1) {
let rt = next_token(&rc);
if (rt == (char*)0) break;
eval_forth_token(rt, &rc, out_ptr, api);
}
free(runnable);
lsp = lsp - 1;
}
free(body);
free(temp);
} else if (strcmp(token, "loop") == 0) {
// Marker
} else if (strcmp(token, "i") == 0) {
if (lsp > 0) {
push(loop_stack[lsp - 1]);
} else {
api.error(api, "'i' used outside of loop in Forth");
}
} else if (strcmp(token, "f+") == 0) {
fpush(fpop() + fpop());
} else if (strcmp(token, "f-") == 0) {
let b = fpop();
fpush(fpop() - b);
} else if (strcmp(token, "f*") == 0) {
fpush(fpop() * fpop());
} else if (strcmp(token, "f/") == 0) {
let b = fpop();
fpush(if (fabs(b) > 1e-15) fpop() / b else 0.0);
} else if (strcmp(token, "fsqrt") == 0) {
fpush(sqrt(fpop()));
} else if (strcmp(token, "dup") == 0) {
push(peek());
} else if (strcmp(token, "drop") == 0) {
pop();
} else if (strcmp(token, "swap") == 0) {
let a = pop();
let b = pop();
push(a);
push(b);
} else if (strcmp(token, "rot") == 0) {
let a = pop();
let b = pop();
let c = pop();
push(b);
push(a);
push(c);
} else if (strcmp(token, "-rot") == 0) {
let a = pop();
let b = pop();
let c = pop();
push(a);
push(c);
push(b);
} else if (strcmp(token, "fdup") == 0) {
fpush(fpeek());
} else if (strcmp(token, "fdrop") == 0) {
fpop();
} else if (strcmp(token, "fswap") == 0) {
let a = fpop();
let b = fpop();
fpush(a);
fpush(b);
} else if (strcmp(token, "i>f") == 0) {
fpush((double)pop());
} else if (strcmp(token, "f>i") == 0) {
push((long)fpop());
} else if (strcmp(token, "emit") == 0) {
let n = sprintf(*out_ptr, "%ld", pop());
*out_ptr = &(*out_ptr)[n];
} else if (strcmp(token, "femit") == 0) {
let n = sprintf(*out_ptr, "%f", fpop());
*out_ptr = &(*out_ptr)[n];
} else if (strcmp(token, ",") == 0) {
let n = sprintf(*out_ptr, ", ");
*out_ptr = &(*out_ptr)[n];
} else if (strcmp(token, ".") == 0) {
api.note(api, "Debug [Forth]: Int = %ld", peek());
} else if (strcmp(token, "f.") == 0) {
api.note(api, "Debug [Forth]: Float = %f", fpeek());
} else if (strcmp(token, ".\"") == 0) {
let p = *scan_ptr;
if (p[0] == ' ') p = &p[1];
let end_p = strchr(p, (int)'"');
if (end_p != (char*)0) {
let len = (int)(end_p - p);
strncpy(*out_ptr, p, (size_t)len);
*out_ptr = &(*out_ptr)[len];
(*out_ptr)[0] = 0;
*scan_ptr = &end_p[1];
} else {
api.error(api, "Unterminated string literal in Forth");
}
} else if (strcmp(token, "s\"") == 0) {
let p = *scan_ptr;
if (p[0] == ' ') p = &p[1];
let end_p = strchr(p, (int)'"');
if (end_p != (char*)0) {
let len = (int)(end_p - p);
let start_addr = zhere;
for i in 0 .. len {
zmemory[zhere] = (long)p[i];
zhere = zhere + 1;
}
push((long)start_addr);
push((long)len);
*scan_ptr = &end_p[1];
} else {
api.error(api, "Unterminated string literal (s\") in Forth");
}
} else if (strcmp(token, "c@") == 0) {
let addr = (int)pop();
push(zmemory[addr]);
} else if (strcmp(token, "!") == 0) {
let addr = (int)pop();
let val = pop();
zmemory[addr] = val;
} else if (strcmp(token, "@") == 0) {
let addr = (int)pop();
push(zmemory[addr]);
} else if (strcmp(token, "?") == 0) {
let addr = (int)pop();
api.note(api, "Debug [Forth]: @%d = %ld", addr, zmemory[addr]);
} else if (strcmp(token, "allot") == 0) {
zhere = zhere + (int)pop();
} else if (strcmp(token, "variable") == 0) {
let name = next_token(scan_ptr);
if (name == (char*)0) {
api.error(api, "Forth 'variable' directive missing name");
return;
}
if (forth_dict_count < 128) {
strncpy(&forth_dict[forth_dict_count].name[0], name, 31);
sprintf(&forth_dict[forth_dict_count].body[0], "%d", zhere);
forth_dict_count = forth_dict_count + 1;
zhere = zhere + 1;
}
} else if (strcmp(token, "constant") == 0) {
let val = pop();
let name = next_token(scan_ptr);
if (name == (char*)0) {
api.error(api, "Forth 'constant' directive missing name");
return;
}
if (forth_dict_count < 128) {
strncpy(&forth_dict[forth_dict_count].name[0], name, 31);
sprintf(&forth_dict[forth_dict_count].body[0], "%ld", val);
forth_dict_count = forth_dict_count + 1;
}
} else {
if (strchr(token, (int)'.') != (char*)0) {
let end_p: char*;
let fval = strtod(token, &end_p);
if (end_p[0] == 0) {
fpush(fval);
return;
}
}
let end_p: char*;
let val = strtol(token, &end_p, 0);
if (end_p[0] == 0) {
push(val);
} else {
let found = 0;
for i in forth_dict_count - 1 .. -1 step -1 {
if (strcmp(&forth_dict[i].name[0], token) == 0) {
let body_copy: [char; 512];
strncpy(&body_copy[0], &forth_dict[i].body[0], 511);
let sub_scan = &body_copy[0];
while (1) {
let sub_token = next_token(&sub_scan);
if (sub_token == (char*)0) break;
eval_forth_token(sub_token, &sub_scan, out_ptr, api);
}
found = 1;
break;
}
}
if (found == 0) {
let n = sprintf(*out_ptr, "%s ", token);
*out_ptr = &(*out_ptr)[n];
}
}
}
}
fn process_forth_source(src: char*, out_ptr: char**, api: ZApi*) {
let cursor = src;
let state = 0;
let name: [char; 32];
let body: [char; 512];
body[0] = 0;
while (1) {
let token = next_token(&cursor);
if (token == (char*)0) break;
if (strcmp(token, "#end") == 0) break;
if (state == 0) {
if (strcmp(token, ":") == 0) {
state = 1;
} else {
eval_forth_token(token, &cursor, out_ptr, api);
}
} else if (state == 1) {
strncpy(&name[0], token, 31);
name[31] = 0;
body[0] = 0;
state = 2;
} else if (state == 2) {
if (strcmp(token, ";") == 0) {
if (forth_dict_count < 128) {
strncpy(&forth_dict[forth_dict_count].name[0], &name[0], 31);
strncpy(&forth_dict[forth_dict_count].body[0], &body[0], 511);
forth_dict_count = forth_dict_count + 1;
}
state = 0;
} else {
if (strlen(&body[0]) > 0) {
strcat(&body[0], " ");
}
if (strlen(&body[0]) + strlen(token) < 512) {
strcat(&body[0], token);
}
}
}
}
}
fn load_prelude() {
let prelude = ": squared fdup f* ; : hypot squared fswap squared f+ fsqrt ; : over swap dup rot rot ; : panic 0 1 - exit ; ";
let dummy_buf: [char; 1024];
dummy_buf[0] = 0;
let ptr = &dummy_buf[0];
let prelude_copy = strdup(prelude);
process_forth_source(prelude_copy, &ptr, (ZApi*)0);
free(prelude_copy);
}
fn forth_transpile(input_body: char*, api: ZApi*) {
let cap = 8192;
let output_buffer = (char*)malloc((size_t)cap);
if (output_buffer == (char*)0) return;
output_buffer[0] = 0;
let write_ptr = output_buffer;
let input_copy = strdup(input_body);
static let prelude_loaded = 0;
if (prelude_loaded == 0) {
load_prelude();
prelude_loaded = 1;
}
process_forth_source(input_copy, &write_ptr, api);
free(input_copy);
fputs(output_buffer, api.out);
free(output_buffer);
}
let forth_plugin = ZPlugin {
name: "forth",
handler: forth_transpile
};
@export
fn z_plugin_init() -> ZPlugin* {
return &forth_plugin;
}