Ctrl K
ComunidadGuías…

plugins/lisp.zc

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot

import "../std/plugin.zc"

include <ctype.h>
include <stdio.h>
include <stdlib.h>
include <string.h>

fn skip_whitespace(p: char**) {
    let ptr = *p;
    while (ptr[0] != 0 && isspace((int)ptr[0])) {
        ptr = &ptr[1];
    }
    *p = ptr;
}

fn parse_lisp_expr(p: char**, api: ZApi*, depth: int);

fn parse_atom(p: char**, api: ZApi*) {
    let out = api.out;
    let ptr = *p;
    let start = ptr;
    
    if (ptr[0] == ')') {
        api.error(api, "Unexpected ')' in Lisp expression");
        return;
    }
    
    // Number
    if (isdigit((int)ptr[0]) || (ptr[0] == '-' && isdigit((int)ptr[1]))) {
        if (ptr[0] == '-') ptr = &ptr[1];
        while (ptr[0] != 0 && isdigit((int)ptr[0])) {
            ptr = &ptr[1];
        }
        fputs("l_num(", out);
        fwrite(start, 1, (size_t)(ptr - start), out);
        fputs(")", out);
        *p = ptr;
        return;
    }
    
    if (ptr[0] == '"') {
        ptr = &ptr[1];
        while (ptr[0] != 0 && ptr[0] != '"') {
            if (ptr[0] == '\\') ptr = &ptr[1];
            ptr = &ptr[1];
        }
        if (ptr[0] == '"') ptr = &ptr[1];
        fputs("l_nil()", out);
        *p = ptr;
        return;
    }
    
    // Symbol
    while (ptr[0] != 0 && !isspace((int)ptr[0]) && ptr[0] != ')' && ptr[0] != '(') {
        ptr = &ptr[1];
    }
    fwrite(start, 1, (size_t)(ptr - start), out);
    *p = ptr;
}

fn parse_lisp_list(p: char**, api: ZApi*, depth: int) {
    let out = api.out;
    let ptr = *p;
    ptr = &ptr[1]; // consume (
    *p = ptr;
    skip_whitespace(p);
    ptr = *p;
    
    if (ptr[0] == 0) {
        api.error(api, "Unclosed parenthesis (unexpected EOF) in Lisp list");
        return;
    }
    
    let op_start = ptr;
    while (ptr[0] != 0 && !isspace((int)ptr[0]) && ptr[0] != ')' && ptr[0] != '(') {
        ptr = &ptr[1];
    }
    let op_len = (int)(ptr - op_start);
    *p = ptr;
    
    // Arithmetic
    if (op_len == 1 && strchr("+-*/", (int)op_start[0]) != (char*)0) {
        let op_char = op_start[0];
        let func = "l_add";
        match (int)op_char {
            45 => func = "l_sub",
            42 => func = "l_mul",
            47 => func = "l_div",
            _ => (void)0
        }
        fprintf(out, "%s(", func);
        skip_whitespace(p);
        parse_lisp_expr(p, api, depth + 1);
        fputs(", ", out);
        skip_whitespace(p);
        parse_lisp_expr(p, api, depth + 1);
        fputs(")", out);
        
        skip_whitespace(p);
        ptr = *p;
        while (ptr[0] != 0 && ptr[0] != ')') {
            if (ptr[0] == '(') {
                let d = 1;
                ptr = &ptr[1];
                while (d > 0 && ptr[0] != 0) {
                    if (ptr[0] == '(') d = d + 1;
                    if (ptr[0] == ')') d = d - 1;
                    ptr = &ptr[1];
                }
            } else {
                while (ptr[0] != 0 && !isspace((int)ptr[0]) && ptr[0] != ')') {
                    ptr = &ptr[1];
                }
            }
            *p = ptr;
            skip_whitespace(p);
            ptr = *p;
        }
        *p = ptr;
    } else if (strncmp(op_start, "print", 5) == 0) {
        skip_whitespace(p);
        ptr = *p;
        if (ptr[0] == '"') {
            fputs("printf(\x22", out);
            ptr = &ptr[1];
            while (ptr[0] != 0 && ptr[0] != '"') {
                if (ptr[0] == '\\') {
                    fputs("\\", out);
                    ptr = &ptr[1];
                    if (ptr[0] != 0) {
                        fprintf(out, "%c", (int)ptr[0]);
                        ptr = &ptr[1];
                    }
                } else {
                    fprintf(out, "%c", (int)ptr[0]);
                    ptr = &ptr[1];
                }
            }
            let ln_str: char*;
            if (op_start[5] == 'l') { ln_str = "\\n"; } else { ln_str = ""; }
            fprintf(out, "%s\x22)", ln_str);
            if (ptr[0] == '"') ptr = &ptr[1];
            *p = ptr;
        } else {
            fputs("l_print(", out);
            parse_lisp_expr(p, api, depth + 1);
            let print_stmt: char*;
            if (op_start[5] == 'l') { print_stmt = " printf(\x22\\n\x22);"; } else { print_stmt = ""; }
            fprintf(out, ")%s", print_stmt);
        }
    } else if (op_len == 5 && strncmp(op_start, "defun", 5) == 0) {
        skip_whitespace(p);
        ptr = *p;
        let nstart = ptr;
        while (ptr[0] != 0 && !isspace((int)ptr[0]) && ptr[0] != '(') {
            ptr = &ptr[1];
        }
        
        let h = api.hoist_out;
        if (h == (void*)0) h = out; // Fallback to inline if hoisting unavailable

        // Forward declaration in case of mutual recursion
        fputs("static LVal ", h);
        fwrite(nstart, 1, (size_t)(ptr - nstart), h);
        fputs("(LVal", h); // simplified check for args
        // (Actually adding full forward decl is complex without parsing params twice, 
        //  but the hoist out is usually at top level and the runtime is above it.)
        
        fputs("); \n", h);

        fputs("static LVal ", h);
        fwrite(nstart, 1, (size_t)(ptr - nstart), h);
        fputs("(", h);
        *p = ptr;
        skip_whitespace(p);
        ptr = *p;
        ptr = &ptr[1]; // eat (
        *p = ptr;
        skip_whitespace(p);
        ptr = *p;
        let first = 1;
        while (ptr[0] != 0 && ptr[0] != ')') {
            if (first == 0) fputs(", ", h);
            first = 0;
            fputs("LVal ", h);
            let astart = ptr;
            while (ptr[0] != 0 && !isspace((int)ptr[0]) && ptr[0] != ')') {
                ptr = &ptr[1];
            }
            fwrite(astart, 1, (size_t)(ptr - astart), h);
            *p = ptr;
            skip_whitespace(p);
            ptr = *p;
        }
        ptr = &ptr[1]; // eat )
        fputs(f") {{ \n return ({{ \n", h);
        *p = ptr;
        skip_whitespace(p);
        ptr = *p;
        let old_out = api.out;
        api.out = h;
        while (ptr[0] != 0 && ptr[0] != ')') {
            *p = ptr;
            parse_lisp_expr(p, api, depth + 1); 
            
            fputs(";\n", h); 
            skip_whitespace(p);
            ptr = *p;
        }
        api.out = old_out;
        fputs(f"}});\n}}\n", h);
        *p = ptr;
        
        // The defun expression itself in the current stream returns nil
        fputs("nil", out);
    } else {
        fwrite(op_start, 1, (size_t)op_len, out);
        fputs("(", out);
        skip_whitespace(p);
        let first = 1;
        ptr = *p;
        while (ptr[0] != 0 && ptr[0] != ')') {
            if (first == 0) fputs(", ", out);
            first = 0;
            *p = ptr;
            parse_lisp_expr(p, api, depth + 1);
            skip_whitespace(p);
            ptr = *p;
        }
        fputs(")", out);
        *p = ptr;
    }
    
    ptr = *p;
    while (ptr[0] != 0 && ptr[0] != ')') {
        ptr = &ptr[1];
    }
    if (ptr[0] == ')') ptr = &ptr[1];
    *p = ptr;
}

fn parse_lisp_expr(p: char**, api: ZApi*, depth: int) {
    skip_whitespace(p);
    let ptr = *p;
    if (ptr[0] == 0) return;
    if (ptr[0] == '(') {
        parse_lisp_list(p, api, depth);
    } else {
        parse_atom(p, api);
    }
}

fn lisp_transpile(input_body: char*, api: ZApi*) {
    let out = api.out;
    let p = input_body;

    static let runtime_emitted = 0;
    if (runtime_emitted == 0 && api.hoist_out != (void*)0) {
        let h = api.hoist_out;
        fputs(f"""/* Lisp Runtime */
typedef enum {{ L_NUM, L_PAIR, L_NIL }} LType;
typedef struct z_LVal {{ LType type; union {{ long num; struct {{ struct z_LVal *car; struct z_LVal *cdr; }} pair; }}; }} *LVal;
static struct z_LVal _nil = {{ L_NIL }}; static LVal LNIL = &_nil;
static LVal nil = &_nil;
static LVal l_num(long n) {{ LVal v = (LVal)malloc(sizeof(struct z_LVal)); v->type=L_NUM; v->num=n; return v; }}
static LVal l_nil() {{ return LNIL; }}
static LVal l_cons(LVal a, LVal b) {{ LVal v = (LVal)malloc(sizeof(struct z_LVal)); v->type=L_PAIR; v->pair.car=a; v->pair.cdr=b; return v; }}
static LVal l_car(LVal v) {{ return (v && v->type==L_PAIR) ? v->pair.car : LNIL; }}
static LVal l_cdr(LVal v) {{ return (v && v->type==L_PAIR) ? v->pair.cdr : LNIL; }}
static int l_truthy(LVal v) {{ return (v && v->type!=L_NIL); }}
static LVal l_add(LVal a, LVal b) {{ long x=(a&&a->type==L_NUM)?a->num:0; long y=(b&&b->type==L_NUM)?b->num:0; return l_num(x+y); }}
static LVal l_sub(LVal a, LVal b) {{ long x=(a&&a->type==L_NUM)?a->num:0; long y=(b&&b->type==L_NUM)?b->num:0; return l_num(x-y); }}
static LVal l_mul(LVal a, LVal b) {{ long x=(a&&a->type==L_NUM)?a->num:0; long y=(b&&b->type==L_NUM)?b->num:0; return l_num(x*y); }}
static LVal l_div(LVal a, LVal b) {{ long x=(a&&a->type==L_NUM)?a->num:0; long y=(b&&b->type==L_NUM)?b->num:0; return l_num(y?x/y:0); }}
static LVal l_lt(LVal a, LVal b) {{ long x=(a&&a->type==L_NUM)?a->num:0; long y=(b&&b->type==L_NUM)?b->num:0; return (x<y)?l_num(1):LNIL; }}
static void l_print(LVal v) {{
  if(!v || v->type==L_NIL) printf("nil");
  else if(v->type==L_NUM) printf("%ld", v->num);
  else if(v->type==L_PAIR) {{ printf("("); l_print(v->pair.car); printf(" . "); l_print(v->pair.cdr); printf(")"); }}
}}
""", h);
        runtime_emitted = 1;
    }

    fputs(f"({{ \n", out);

    while (p[0] != 0) {
        skip_whitespace(&p);
        if (p[0] == 0) break;
        parse_lisp_expr(&p, api, 0);
        fputs(";\n", out);
    }
    fputs(f"}});\n", out);
}

let lisp_plugin = ZPlugin {
    name: "lisp",
    handler: lisp_transpile
};

@export
fn z_plugin_init() -> ZPlugin* {
    return &lisp_plugin;
}