/* * The fLisp parser and interpreter * Copyright (C) 2025 Fcalva * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see * . */ #include #include #include #include #include "fixed.h" #include "types.h" #include "config.h" #include "code_defs.h" #include "byte_defs.h" #include "exec_common.h" typedef struct { u32 pos; u32 size; } AllocBlock; #define EXT_BLOCK 4096 void *internal_buf; AllocBlock *blocks; u32 *usage; // bitmap for usage, by 8byte chunks size_t internal_size; u32 block_count; // Utils void runtime_err(char *s){ fprintf(stderr,"Runtime Error : %s\n", s); exit(1); } int evaluate(Value val){ if(val.tag.is_array) return 0; switch(val.tag.type){ case T_null: return 0; case T_int: return val.v4B.value; case T_fix: return val.v4B.value; case T_float: return FL_EPSILON > fabs((float)val.v4B.value); case T_str: return 0; case T_fn: return 0; default: return 0; } } int alloc_extend(u32 size){ size = (size/EXT_BLOCK+1)*EXT_BLOCK; internal_buf = realloc(internal_buf, internal_size+size); usage = realloc(usage, sizeof(u32)*(internal_size+size)/32/8); if(!internal_buf || !usage) return 1; memset(usage+(internal_size/32/8), 0, sizeof(u32)*size/32/8); internal_size += size; return 0; } int block_cmpfunc(const void *_a, const void *_b){ const AllocBlock *a = _a; const AllocBlock *b = _b; return b->pos - a->pos; } AllocBlock *get_block(u32 pos){ AllocBlock tmp = {.pos = pos}; AllocBlock *ret = bsearch(&tmp, blocks, block_count, sizeof(AllocBlock), block_cmpfunc); return ret; } int alloc_init(){ blocks = malloc(sizeof(AllocBlock)); block_count = 0; internal_buf = NULL; internal_size = 0; usage = NULL; if(!blocks || alloc_extend(EXT_BLOCK)) return 1; // First 32bytes are reserved for null + alignement usage[0] = 0xF; return 0; } void alloc_clean(){ free(internal_buf); free(blocks); free(usage); } u32 flisp_alloc(u32 size){ u32 pos = 32; u32 curr_free = 0; while(pos < internal_size){ if(usage[pos/32/8]){ for(u32 opos = pos; pos < opos+32;){ if((usage[pos/32/8]>>((pos/8)%32)) & 1) curr_free = 0; else curr_free+=8; pos+=8; if(curr_free >= size) break; } } else { curr_free += 32*8; pos+=32*8; } if(curr_free >= size){ blocks = realloc(blocks, sizeof(AllocBlock)*(block_count+1)); if(!blocks) return 0; AllocBlock *block = &blocks[block_count++]; block->pos = pos-curr_free; block->size = size; for(u32 i = block->pos; i < block->pos+size; i+=8){ usage[i/32/8] |= 1<<((i/8)%32); } // Unvirtualize the memory memset(get_addr(block->pos),0xB00B5069,size); return block->pos; } } if(alloc_extend(size)) return 0; // Try again after extending the buffer return flisp_alloc(size); } // TODO : make it efficient u32 flisp_realloc(u32 pos, u32 size){ if(!pos) return flisp_alloc(size); u32 new = flisp_alloc(size); if(!new) return 0; AllocBlock *block = get_block(pos); memcpy(get_addr(new),get_addr(pos),block->size); flisp_free(pos); return new; } void flisp_free(u32 pos){ AllocBlock *ret = get_block(pos); if(!ret) return; i32 bpos = (size_t)(ret-blocks)/sizeof(AllocBlock); memset(get_addr(ret->pos), 0, ret->size); memmove(ret, ret+1, sizeof(AllocBlock) * (block_count-bpos-1)); block_count--; } void *get_addr(u32 pos){ return internal_buf + pos; } Value write(Value a); void print_ast1(Statement *base, i32 indent){ i32 close_parent = 1; printf("# "); for(int i = 0; i < indent; i++) printf(" "); switch(base->type){ case ST_None: printf("(none\n"); break; case ST_Call: printf("(call<%lx>\n", (u64)base->func); break; case ST_Const: write(base->cons); printf("\n"); close_parent = 0; break; case ST_Var: printf("<%d>\n", base->var_id); close_parent = 0; break; case ST_Block: printf("(block\n"); break; case ST_Varuse: printf("(use<%d>\n", base->var_id); break; case BI_var: printf("(var<%d>:<%d>\n", base->var_id, base->var_type.type); break; case BI_if: printf("(if\n"); break; case BI_else: printf("(else\n"); break; case BI_while: printf("(while\n"); break; case BI_is: printf("(is<%d>\n", base->var_type.type); break; case BI_cast: printf("(cast<%d>\n", base->var_type.type); break; default: printf("(other<%d>\n", base->type); break; } for(int i = 0; i < base->child_n; i++) print_ast1(base->children[i], indent+1); if(close_parent){ printf("# "); for(int i = 0; i < indent; i++) printf(" "); printf(")\n"); } } void print_ast(Statement *base){ #if LOG printf("# AST :\n"); print_ast1(base, 0); #endif } Value str_to_val(char *str, i32 len){ if(len < 0) len = strlen(str); Value retv; retv.tag = (Tag){.is_array = 0, .type = T_str}; #if LOG printf("str (%d) : %s\n", len, str); #endif if(len > 4){ u32 nstr = flisp_alloc(len); if(!nstr){ retv.tag = ntag; return retv; } retv.vstr.pos = nstr; memcpy(get_addr(nstr),str,len); } else { for(i32 i = 0; i < len; i++) retv.vstr.str[i] = str[i]; } retv.vstr.len = len; return retv; } void clean_strval(Value str){ if(str.vstr.len < 5) return; flisp_free(str.vstr.pos); } // Lesser special functions Value is(Tag tag, Value b){ Value ret = {.tag = {0,T_int}}; ret.v4B.value = tag.is_array == b.tag.is_array && tag.type == b.tag.type; return ret; } typedef Value (cast_fn_t)(Value); Value int_to_fix(Value x){ x.tag.type = T_fix; x.v4B.value = fix(x.v4B.value); return x; } Value int_to_flt(Value x){ x.tag.type = T_float; x.v4B.vfl = (float)x.v4B.value; return x; } Value fix_to_int(Value x){ x.tag.type = T_int; x.v4B.value = ffloor(x.v4B.value); return x; } Value fix_to_flt(Value x){ x.tag.type = T_float; x.v4B.vfl = f2float(x.v4B.value); return x; } Value flt_to_int(Value x){ x.tag.type = T_int; x.v4B.value = (i32)(x.v4B.vfl); return x; } Value flt_to_fix(Value x){ x.tag.type = T_fix; x.v4B.value = fixfloat(x.v4B.vfl); return x; } char tmpbuf[256]; Value int_to_str(Value x){ i32 len = snprintf(tmpbuf, 256, "%d", x.v4B.value); len = len > 256 ? 256:len; Value retv = str_to_val(tmpbuf, len); return retv; } Value fix_to_str(Value x){ i32 len = snprintf(tmpbuf, 256, "%f", f2float(x.v4B.value)); len = len > 256 ? 256:len; Value retv = str_to_val(tmpbuf, len); return retv; } Value flt_to_str(Value x){ i32 len = snprintf(tmpbuf, 256, "%f", x.v4B.vfl); len = len > 256 ? 256:len; Value retv = str_to_val(tmpbuf, len); return retv; } Value vstr_to(Value x, i32 type){ char *str; Value retv; if(x.vstr.len > 4) str = get_addr(x.vstr.pos); else str = x.vstr.str; char *nstr = malloc(x.vstr.len+1); if(!nstr){ retv.tag = ntag; clean_strval(x); return retv; } memcpy(nstr, str, x.vstr.len); clean_strval(x); nstr[x.vstr.len] = '\0'; i32 ret = 0; switch(type){ case T_int: ret = sscanf(nstr, "%d", &retv.v4B.value); break; case T_fix: ret = sscanf(nstr, "%f", &retv.v4B.vfl); x.v4B.value = fix(x.v4B.vfl); break; case T_float: ret = sscanf(nstr, "%f", &retv.v4B.vfl); break; default: break; } free(nstr); if(!ret){ retv.tag = ntag; return retv; } retv.tag = (Tag){.is_array = 0, .type = type}; return retv; } Value str_to_int(Value x){ return vstr_to(x,T_int); } Value str_to_fix(Value x){ return vstr_to(x,T_fix); } Value str_to_flt(Value x){ return vstr_to(x,T_float); } // [from][to] cast_fn_t *cast_table[4][4] = { {NULL , int_to_fix, int_to_flt, int_to_str}, {fix_to_int, NULL , fix_to_flt, fix_to_str}, {flt_to_int, flt_to_fix, NULL , flt_to_str}, {str_to_int, str_to_fix, str_to_flt, NULL } }; Value cast(Tag type, Value b){ if(type.type == b.tag.type) return b; if(type.type == T_null || b.tag.type == T_null || type.type == T_fn || b.tag.type == T_fn || type.is_array || b.tag.is_array){ b.tag.type = T_null; return b; } return cast_table[b.tag.type-1][type.type-1](b); } // Generic builtins Value add(Value a, Value b){ if(a.tag.is_array || b.tag.is_array) runtime_err("Add : Invalid types"); if(a.tag.type != b.tag.type) runtime_err("Add : Types do not match"); switch(a.tag.type){ case T_int: case T_fix: a.v4B.value += b.v4B.value; break; case T_float: a.v4B.vfl += b.v4B.vfl; break; case T_str: if(a.vstr.len + b.vstr.len > 4){ runtime_err("not implem"); } else { for(int i = 0; i < b.vstr.len; i++) a.vstr.str[a.vstr.len + i] = b.vstr.str[i]; a.vstr.len += b.vstr.len; } break; default: runtime_err("Add : Invalid types"); } return a; } Value sub(Value a, Value b){ if(a.tag.is_array || b.tag.is_array) runtime_err("Sub : Invalid types"); if(a.tag.type != b.tag.type) runtime_err("Sub : Types do not match"); switch(a.tag.type){ case T_int: case T_fix: a.v4B.value -= b.v4B.value; break; case T_float: a.v4B.vfl -= b.v4B.vfl; break; default: runtime_err("Sub : Invalid types"); } return a; } Value mul(Value a, Value b){ if(a.tag.is_array || b.tag.is_array) runtime_err("Mul : Invalid types"); if(a.tag.type != b.tag.type) runtime_err("Mul : Types do not match"); switch(a.tag.type){ case T_int: a.v4B.value *= b.v4B.value; break; case T_fix: a.v4B.value = fmul(a.v4B.value, b.v4B.value); break; case T_float: a.v4B.vfl *= b.v4B.vfl; break; default: runtime_err("Mul : Invalid types"); } return a; } Value divv(Value a, Value b){ if(a.tag.is_array || b.tag.is_array) runtime_err("Div : Invalid types"); if(a.tag.type != b.tag.type) runtime_err("Div : Types do not match"); switch(a.tag.type){ case T_int: a.v4B.value /= b.v4B.value; break; case T_fix: a.v4B.value = fdiv(a.v4B.value, b.v4B.value); break; case T_float: a.v4B.vfl /= b.v4B.vfl; break; default: runtime_err("Div : Invalid types"); } return a; } Value mod(Value a, Value b){ if(a.tag.is_array || b.tag.is_array) runtime_err("Mod : Invalid types"); if(a.tag.type != b.tag.type) runtime_err("Mod : Types do not match"); switch(a.tag.type){ case T_int: a.v4B.value /= b.v4B.value; break; case T_fix: a.v4B.value = fdiv(a.v4B.value, b.v4B.value); break; case T_float: a.v4B.vfl = (int)a.v4B.vfl % (int)b.v4B.vfl; break; default: runtime_err("Mod : Invalid types"); } return a; } Value sml(Value a, Value b){ Value returnv; returnv.tag = (Tag){.is_array=0,.type=T_int}; if(a.tag.is_array || b.tag.is_array) runtime_err("Sml : Invalid types"); if(a.tag.type != b.tag.type) runtime_err("Sml : Types do not match"); switch(a.tag.type){ case T_int: case T_fix: returnv.v4B.value = a.v4B.value < b.v4B.value; break; case T_float: returnv.v4B.value = a.v4B.vfl < b.v4B.vfl; break; default: runtime_err("Sml : Invalid types"); } return returnv; } Value sml_eq(Value a, Value b){ } Value eq(Value a, Value b){ } Value gt_eq(Value a, Value b){ } Value gt(Value a, Value b){ } Value not(Value a){ } Value and(Value a, Value b){ } Value or(Value a, Value b){ } Value len(Value a){ } Value push(Value a, Value b){ } Value pop(Value a, Value b){ } Value write(Value a){ switch(a.tag.type){ case T_null: case T_any: printf("null"); break; case T_int: printf("%d",a.v4B.value); break; case T_fix: printf("%f",f2float(a.v4B.value)); break; case T_float: printf("%f",a.v4B.vfl); break; case T_str: char *nstr; nstr = malloc(a.vstr.len+1); if(a.vstr.len > 4){ char *str = get_addr(a.vstr.pos); memcpy(nstr, str, a.vstr.len); } else{ memcpy(nstr, a.vstr.str, a.vstr.len); } nstr[a.vstr.len] = '\0'; printf("%s",nstr); free(nstr); break; case T_fn: printf("", (u64)get_fnsig(&a)); break; default: break; } return (Value){.tag={0,T_null}}; } Value nwline(){ printf("\n"); return (Value){.tag={0,T_null}}; } const Tag tany = {0,T_null}; const Tag tint = {0,T_int}; const Tag tfix = {0,T_fix}; const Tag tfloat = {0,T_float}; const Tag tstr = {0,T_str}; const Tag tfn = {0,T_fn}; const Tag tvec = {1,T_null}; #define BICAST(x) ((bi_fn_t*)(x)) FnSig builtins[] = { {{tany,tany},2,1,{.bi = BICAST(add)}}, {{tany,tany},2,1,{.bi = BICAST(sub)}}, {{tany,tany},2,1,{.bi = BICAST(mul)}}, {{tany,tany},2,1,{.bi = BICAST(divv)}}, {{tany,tany},2,1,{.bi = BICAST(mod)}}, {{tany,tany},2,1,{.bi = BICAST(sml)}}, {{tany,tany},2,1,{.bi = BICAST(sml_eq)}}, {{tany,tany},2,1,{.bi = BICAST(eq)}}, {{tany,tany},2,1,{.bi = BICAST(gt_eq)}}, {{tany,tany},2,1,{.bi = BICAST(gt)}}, {{tany} ,1,1,{.bi = BICAST(not)}}, {{tany,tany},2,1,{.bi = BICAST(and)}}, {{tany,tany},2,1,{.bi = BICAST(or)}}, {{tvec} ,1,1,{.bi = BICAST(len)}}, {{tvec,tany},2,1,{.bi = BICAST(push)}}, {{tvec,tint},2,1,{.bi = BICAST(pop)}}, {{tany} ,1,1,{.bi = BICAST(write)}}, {{0} ,0,1,{.bi = BICAST(nwline)}} }; char *bi_names[] = { "+", "-", "*", "/", "%", "<", "<=", "=", ">=", ">", "!", "and", "or", "len", "push", "pop", "write", "newline" }; void symbol_map_add_bi(HashMap *map){ Tag fntag = {0,T_fn}; for(int i = BI_add; i < BI__end; i++){ MapItem *ret = hashmap_insert(map, bi_names[i-BI_add]); ret->type = *((i16*)&fntag); ret->fnsig = &builtins[i-BI_add]; ret->is_const = 1; } }