test functions
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef BLISP_CONFIG_H_
|
||||||
|
#define BLISP_CONFIG_H_
|
||||||
|
|
||||||
|
#include "data.h"
|
||||||
|
|
||||||
|
ConfigValue *get_config_var(const char *name);
|
||||||
|
|
||||||
|
int get_config_int(const char *name, int default_val);
|
||||||
|
double get_config_float(const char *name, double default_val);
|
||||||
|
char *get_config_string(const char *name, const char *default_val);
|
||||||
|
bool get_config_bool(const char *name, bool default_val);
|
||||||
|
char **get_config_list(const char *name, int *count);
|
||||||
|
|
||||||
|
void free_config_value(ConfigValue *val);
|
||||||
|
|
||||||
|
bool config_var_exists(const char *name);
|
||||||
|
|
||||||
|
void set_config_int(const char *name, int value);
|
||||||
|
void set_config_float(const char *name, double value);
|
||||||
|
void set_config_string(const char *name, const char *value);
|
||||||
|
void set_config_bool(const char *name, bool value);
|
||||||
|
|
||||||
|
void print_all_config_vars(void);
|
||||||
|
#endif
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#ifndef CONFIG_TOOLS_H_
|
|
||||||
#define CONFIG_TOOLS_H_
|
|
||||||
|
|
||||||
#include "data.h"
|
|
||||||
#include "parser.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
void config_create(void);
|
|
||||||
void init_builtin_functions(void);
|
|
||||||
|
|
||||||
int handle_map_key(node_t **args, int arg_count);
|
|
||||||
int handle_define(node_t **args, int arg_count);
|
|
||||||
int handle_function(node_t **args, int arg_count);
|
|
||||||
|
|
||||||
int execute_function_call(node_t *call);
|
|
||||||
int config_parse_string(const char *input);
|
|
||||||
int config_parse_file(const char *filename);
|
|
||||||
|
|
||||||
const char *config_get_key_mapping(const char *key_combo);
|
|
||||||
node_t *find_variable(const char *name);
|
|
||||||
|
|
||||||
const char *config_get_string(const char *path, const char *default_value);
|
|
||||||
int config_get_int(const char *path, int default_value);
|
|
||||||
double config_get_double(const char *path, double default_value);
|
|
||||||
bool config_get_bool(const char *path, bool default_value);
|
|
||||||
void config_print_all(void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
+119
-125
@@ -1,151 +1,145 @@
|
|||||||
#ifndef DATA_H_PARSER
|
#ifndef DATA_H_PARSER
|
||||||
#define DATA_H_PARSER
|
#define DATA_H_PARSER
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
// Token types for lexical analysis
|
// Forward declarations
|
||||||
|
typedef struct Value Value;
|
||||||
|
typedef struct Env Env;
|
||||||
|
|
||||||
|
// Value types
|
||||||
|
typedef enum {
|
||||||
|
VAL_NIL,
|
||||||
|
VAL_NUMBER,
|
||||||
|
VAL_STRING,
|
||||||
|
VAL_SYMBOL,
|
||||||
|
VAL_LIST,
|
||||||
|
VAL_FUNCTION,
|
||||||
|
VAL_KEYMAP
|
||||||
|
} ValueType;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TOKEN_COMMA, // ,
|
CONFIG_INT,
|
||||||
TOKEN_PERCENT, // %
|
CONFIG_FLOAT,
|
||||||
TOKEN_LPAREN, // (
|
CONFIG_STRING,
|
||||||
TOKEN_RPAREN, // )
|
CONFIG_BOOL,
|
||||||
TOKEN_SYMBOL, // identifiers/function names
|
CONFIG_LIST,
|
||||||
TOKEN_STRING, // "quoted strings"
|
CONFIG_UNKNOWN
|
||||||
TOKEN_NUMBER, // integers and floats
|
} ConfigType;
|
||||||
TOKEN_BOOLEAN, // true false
|
|
||||||
TOKEN_NEWLINE, // \n (statement separator)
|
|
||||||
TOKEN_EOF,
|
|
||||||
TOKEN_ERROR
|
|
||||||
} token_type_t;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
token_type_t type;
|
ConfigType type;
|
||||||
char *value;
|
|
||||||
int line;
|
|
||||||
int column;
|
|
||||||
} token_t;
|
|
||||||
|
|
||||||
// AST node types
|
|
||||||
typedef enum {
|
|
||||||
NODE_SYMBOL,
|
|
||||||
NODE_STRING,
|
|
||||||
NODE_NUMBER,
|
|
||||||
NODE_BOOLEAN,
|
|
||||||
NODE_FUNCTION_REF, // %function-name
|
|
||||||
NODE_FUNCTION_CALL, // ,function-name()
|
|
||||||
NODE_LIST
|
|
||||||
} node_type_t;
|
|
||||||
|
|
||||||
typedef struct node {
|
|
||||||
node_type_t type;
|
|
||||||
union {
|
union {
|
||||||
char *symbol;
|
int int_val;
|
||||||
char *string;
|
double float_val;
|
||||||
double number;
|
char *string_val;
|
||||||
bool boolean;
|
bool bool_val;
|
||||||
char *function_ref;
|
|
||||||
struct {
|
struct {
|
||||||
char *function_name;
|
char **items;
|
||||||
struct node **args;
|
int count;
|
||||||
int arg_count;
|
} list_val;
|
||||||
} call;
|
} value;
|
||||||
struct {
|
} ConfigValue;
|
||||||
struct node **children;
|
|
||||||
int child_count;
|
/**
|
||||||
} list;
|
* @typedef Functions
|
||||||
} data;
|
*
|
||||||
} node_t;
|
*/
|
||||||
|
|
||||||
|
typedef Value *(*Function)(char **params, int param_count, Value *body,
|
||||||
|
Env *closure);
|
||||||
|
|
||||||
|
// Value structure
|
||||||
|
struct Value {
|
||||||
|
ValueType type;
|
||||||
|
union {
|
||||||
|
double number;
|
||||||
|
char *string;
|
||||||
|
char *symbol;
|
||||||
|
struct {
|
||||||
|
Value *car;
|
||||||
|
Value *cdr;
|
||||||
|
} list;
|
||||||
|
Function *function;
|
||||||
|
struct {
|
||||||
|
char **keys;
|
||||||
|
Value **values;
|
||||||
|
int count;
|
||||||
|
int capacity;
|
||||||
|
} keymap;
|
||||||
|
} data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Environment for variable bindings
|
||||||
|
struct Env {
|
||||||
|
char **names;
|
||||||
|
Value **values;
|
||||||
|
int count;
|
||||||
|
int capacity;
|
||||||
|
Env *parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TOK_LPAREN,
|
||||||
|
TOK_RPAREN,
|
||||||
|
TOK_QUOTE,
|
||||||
|
TOK_SYMBOL,
|
||||||
|
TOK_NUMBER,
|
||||||
|
TOK_STRING,
|
||||||
|
TOK_EOF
|
||||||
|
} TokenType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
TokenType type;
|
||||||
|
char *value;
|
||||||
|
} Token;
|
||||||
|
|
||||||
// Lexer state
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *input;
|
const char *input;
|
||||||
int pos;
|
int pos;
|
||||||
int line;
|
int length;
|
||||||
int column;
|
} Lexer;
|
||||||
char current_char;
|
|
||||||
} lexer_t;
|
|
||||||
|
|
||||||
// Configuration storage
|
/**
|
||||||
typedef struct key_mapping {
|
* @struct KeyBinding
|
||||||
char *key_combo;
|
* @brief Make the link between a Key Sequence and a command to execute
|
||||||
char *function_name;
|
*/
|
||||||
struct key_mapping *next;
|
|
||||||
} key_mapping_t;
|
|
||||||
|
|
||||||
typedef struct config_var {
|
typedef struct {
|
||||||
char *name;
|
char *key_sequence;
|
||||||
node_t *value;
|
char *command;
|
||||||
struct config_var *next;
|
} KeyBinding;
|
||||||
} config_var_t;
|
//@}
|
||||||
|
typedef struct {
|
||||||
typedef struct config {
|
KeyBinding *bindings;
|
||||||
key_mapping_t *key_mappings;
|
|
||||||
config_var_t *variables;
|
|
||||||
} config_t;
|
|
||||||
|
|
||||||
// Execution context for function calls
|
|
||||||
typedef struct exec_context {
|
|
||||||
struct {
|
|
||||||
char **names;
|
|
||||||
node_t **values;
|
|
||||||
int count;
|
int count;
|
||||||
} local_vars;
|
int capacity;
|
||||||
} exec_context_t;
|
} KeyBindingTable;
|
||||||
|
|
||||||
typedef void (*command_func_t)(void);
|
Value *make_value(ValueType type);
|
||||||
|
Value *make_nil(void);
|
||||||
|
Value *make_number(double n);
|
||||||
|
Value *make_string(const char *s);
|
||||||
|
Value *make_symbol(const char *s);
|
||||||
|
Value *make_list(Value *car, Value *cdr);
|
||||||
|
Value *make_builtin(BuiltinFunc func);
|
||||||
|
Env *make_env(Env *parent);
|
||||||
|
void env_set(Env *env, const char *name, Value *value);
|
||||||
|
Value *env_get(Env *env, const char *name);
|
||||||
|
|
||||||
typedef enum { FUNC_BUILTIN, FUNC_USER_DEFINED, FUNC_REGISTRY } function_type_t;
|
#ifdef GLOBAL_ENV_
|
||||||
|
|
||||||
typedef struct unified_function {
|
Env *global_env = NULL;
|
||||||
char *name;
|
KeyBindingTable *active_keybindings = NULL;
|
||||||
function_type_t type;
|
|
||||||
union {
|
|
||||||
// For built-in functions
|
|
||||||
struct {
|
|
||||||
int (*handler)(node_t **args, int arg_count);
|
|
||||||
bool eval_args; // Whether to evaluate arguments before calling
|
|
||||||
} builtin;
|
|
||||||
|
|
||||||
// For user-defined functions
|
|
||||||
struct {
|
|
||||||
char **parameters;
|
|
||||||
int param_count;
|
|
||||||
node_t *body;
|
|
||||||
} user_defined;
|
|
||||||
|
|
||||||
// For registry functions
|
|
||||||
struct {
|
|
||||||
command_func_t func;
|
|
||||||
} registry;
|
|
||||||
} data;
|
|
||||||
|
|
||||||
struct unified_function *next;
|
|
||||||
} unified_function_t;
|
|
||||||
|
|
||||||
void advance_char(lexer_t *lexer);
|
|
||||||
void skip_whitespace(lexer_t *lexer);
|
|
||||||
void skip_comment(lexer_t *lexer);
|
|
||||||
|
|
||||||
void add_arg_to_call(node_t *call, node_t *args);
|
|
||||||
void add_to_list(node_t *node, node_t *element);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef GLOBAL_CONFIG
|
|
||||||
|
|
||||||
unified_function_t *unified_functions = NULL;
|
|
||||||
int registry_count;
|
|
||||||
config_var_t variable_registry[256];
|
|
||||||
int var_registry_count;
|
|
||||||
|
|
||||||
config_t config;
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
extern unified_function_t *unified_functions;
|
extern Env *global_env;
|
||||||
extern int registry_count;
|
extern KeyBindingTable *active_keybindings;
|
||||||
extern config_var_t variable_registry[256];
|
|
||||||
extern int var_registry_count;
|
|
||||||
|
|
||||||
extern config_t config;
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
#ifndef BLISP_DEFINE_H_
|
|
||||||
#define BLISP_DEFINE_H_
|
|
||||||
|
|
||||||
#define MAX_TOKEN_LENGTH 256
|
|
||||||
#define MAX_SYMBOL_LENGTH 128
|
|
||||||
#define MAX_STRING_LENGTH 512
|
|
||||||
#define MAX_ARGS 16
|
|
||||||
|
|
||||||
#endif
|
|
||||||
+21
-12
@@ -3,22 +3,31 @@
|
|||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
|
||||||
int register_builtin_function(const char *name, int (*handler)(node_t **, int),
|
// Builtins
|
||||||
bool eval_args);
|
|
||||||
int register_user_function(const char *name, char **parameters, int param_count,
|
|
||||||
node_t *body);
|
|
||||||
int register_registry_function(const char *name, command_func_t func);
|
|
||||||
|
|
||||||
int register_function(const char *name, command_func_t func);
|
Value *builtin_add(Value *args);
|
||||||
|
Value *builtin_sub(Value *args);
|
||||||
|
Value *builtin_mul(Value *args);
|
||||||
|
Value *builtin_div(Value *args);
|
||||||
|
Value *builtin_eq(Value *args);
|
||||||
|
Value *builtin_list(Value *args);
|
||||||
|
Value *builtin_car(Value *args);
|
||||||
|
Value *builtin_cdr(Value *args);
|
||||||
|
|
||||||
unified_function_t *find_unified_function(const char *name);
|
// Evaluator
|
||||||
|
|
||||||
node_t *execute_unified_function(exec_context_t *ctx, const char *name,
|
Value *eval_list(Value *list, Env *env);
|
||||||
node_t **args, int arg_count);
|
Value *apply_function(Value *func, Value *args, Env *env);
|
||||||
|
Value *eval_expr(Value *expr, Env *env);
|
||||||
|
|
||||||
command_func_t find_function(const char *name);
|
// Key-mapping
|
||||||
int execute_command(const char *name);
|
|
||||||
|
|
||||||
node_t *execute_function_call_in_context(exec_context_t *ctx, node_t *call);
|
KeyBindingTable *extract_keybindings(Value *keymap);
|
||||||
|
char *normalize_key_sequence(const char *raw_input);
|
||||||
|
bool execute_keybinding(const char *key_sequence);
|
||||||
|
void set_active_keymap(const char *keymap_name);
|
||||||
|
void combine_keymaps(const char *global_keymap, const char *mode_keymap);
|
||||||
|
|
||||||
|
bool execute_editor_command(const char *command);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef BLISP_INIT_H_
|
||||||
|
#define BLISP_INIT_H_
|
||||||
|
|
||||||
|
#include "data.h"
|
||||||
|
|
||||||
|
void init_builtins(Env *env);
|
||||||
|
|
||||||
|
Value *lisp_eval(const char *input);
|
||||||
|
void lisp_init(void);
|
||||||
|
void repl(void);
|
||||||
|
void load_file(const char *filename);
|
||||||
|
|
||||||
|
#endif
|
||||||
+5
-3
@@ -2,9 +2,11 @@
|
|||||||
#define LEXER_H_
|
#define LEXER_H_
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
#include <ctype.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
token_t next_token(lexer_t *lexer);
|
void skip_whitespace(Lexer *lex);
|
||||||
|
|
||||||
|
Token peek_token(Lexer *lex);
|
||||||
|
|
||||||
|
Token next_token(Lexer *lex);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
#ifndef NODE_T_
|
|
||||||
#define NODE_T_
|
|
||||||
|
|
||||||
#include "data.h"
|
|
||||||
|
|
||||||
node_t *create_node(node_type_t type);
|
|
||||||
node_t *create_symbol_node(const char *symbol);
|
|
||||||
node_t *create_string_node(const char *string);
|
|
||||||
node_t *create_number_node(double number);
|
|
||||||
node_t *create_boolean_node(bool value);
|
|
||||||
node_t *create_list_node(void);
|
|
||||||
node_t *create_function_ref_node(const char *function_name);
|
|
||||||
node_t *create_function_call_node(const char *function_name);
|
|
||||||
|
|
||||||
void free_node(node_t *node);
|
|
||||||
node_t *copy_node(node_t *src);
|
|
||||||
|
|
||||||
node_t *evaluate_node(exec_context_t *ctx, node_t *node);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
+2
-4
@@ -3,9 +3,7 @@
|
|||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
|
||||||
node_t *parse_atom(lexer_t *lexer, token_t *token);
|
Value *parse_list(Lexer *lex);
|
||||||
node_t *parse_function_call(lexer_t *lexer, const char *function_name);
|
Value *parse_expr(Lexer *lex);
|
||||||
node_t *parse_statement(lexer_t *lexer);
|
|
||||||
node_t *parse_expression(lexer_t *lexer, token_t *token);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef BLISP_UTILS_H_
|
||||||
|
#define BLISP_UTILS_H_
|
||||||
|
|
||||||
|
#include "data.h"
|
||||||
|
|
||||||
|
bool is_nil(Value *v);
|
||||||
|
bool is_symbol(Value *v, const char *sym);
|
||||||
|
Value *car(Value *v);
|
||||||
|
Value *cdr(Value *v);
|
||||||
|
int list_length(Value *v);
|
||||||
|
|
||||||
|
void print_value(Value *v);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
// Configuration file
|
|
||||||
|
|
||||||
,map-key("CTRL-s" %editorSave)
|
|
||||||
// ,map-key("CTRL-q" %editorQuit)
|
|
||||||
,map-key("CTRL-a" %move-cursor-beg-line)
|
|
||||||
,map-key("CTRL-z" %move-cursor-end-line)
|
|
||||||
,map-key("CTRL-f o" %open-file)
|
|
||||||
,map-key("CTRL-w s h" %window-split-horizontal)
|
|
||||||
,map-key("CTRL-w s v" %window-split-vertical)
|
|
||||||
|
|
||||||
,fun(saveQuit () (
|
|
||||||
,editorSave
|
|
||||||
,editorQuit
|
|
||||||
))
|
|
||||||
|
|
||||||
,define(theme "dark")
|
|
||||||
,define(auto-save true)
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
|
|
||||||
#define GLOBAL_EDITOR
|
|
||||||
|
|
||||||
#include "include/config_tools.h"
|
|
||||||
#include "include/data.h"
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
// Parse the configuration
|
|
||||||
config_create();
|
|
||||||
char *config_file = "init.bl";
|
|
||||||
|
|
||||||
if (config_parse_file(config_file) != 0) {
|
|
||||||
fprintf(stderr, "Error: Failed to parse config file\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Successfully parsed Lisp config file: %s\n\n", config_file);
|
|
||||||
|
|
||||||
// Print all entries
|
|
||||||
config_print_all();
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
// Demonstrate usage
|
|
||||||
printf("Example usage:\n");
|
|
||||||
printf("CTRL-s maps to: %s\n", config_get_key_mapping("CTRL-s"));
|
|
||||||
printf("CTRL-f o maps to: %s\n", config_get_key_mapping("CTRL-f o"));
|
|
||||||
printf("Window width: %d\n", config_get_int("window-width", 1024));
|
|
||||||
printf("Theme: %s\n", config_get_string("theme", "light"));
|
|
||||||
printf("Auto-save: %s\n",
|
|
||||||
config_get_bool("auto-save", false) ? "enabled" : "disabled");
|
|
||||||
printf("Font size: %d\n", config_get_int("font-size", 12));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
+218
@@ -0,0 +1,218 @@
|
|||||||
|
#include "../include/config.h"
|
||||||
|
#include "../include/utils.h"
|
||||||
|
|
||||||
|
ConfigValue *get_config_var(const char *name) {
|
||||||
|
Value *val = env_get(global_env, name);
|
||||||
|
if (!val)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ConfigValue *config = malloc(sizeof(ConfigValue));
|
||||||
|
|
||||||
|
switch (val->type) {
|
||||||
|
case VAL_NUMBER:
|
||||||
|
if (val->data.number == (int)val->data.number) {
|
||||||
|
config->type = CONFIG_INT;
|
||||||
|
config->value.int_val = (int)val->data.number;
|
||||||
|
} else {
|
||||||
|
config->type = CONFIG_FLOAT;
|
||||||
|
config->value.float_val = val->data.number;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VAL_STRING:
|
||||||
|
config->type = CONFIG_STRING;
|
||||||
|
config->value.string_val = strdup(val->data.string);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VAL_SYMBOL:
|
||||||
|
if (strcmp(val->data.symbol, "true") == 0) {
|
||||||
|
config->type = CONFIG_BOOL;
|
||||||
|
config->value.bool_val = true;
|
||||||
|
} else if (strcmp(val->data.symbol, "false") == 0) {
|
||||||
|
config->type = CONFIG_BOOL;
|
||||||
|
config->value.bool_val = false;
|
||||||
|
} else {
|
||||||
|
config->type = CONFIG_STRING;
|
||||||
|
config->value.string_val = strdup(val->data.symbol);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VAL_LIST: {
|
||||||
|
config->type = CONFIG_LIST;
|
||||||
|
config->value.list_val.count = list_length(val);
|
||||||
|
config->value.list_val.items =
|
||||||
|
malloc(sizeof(char *) * config->value.list_val.count);
|
||||||
|
|
||||||
|
Value *curr = val;
|
||||||
|
int i = 0;
|
||||||
|
while (curr->type == VAL_LIST && i < config->value.list_val.count) {
|
||||||
|
Value *item = car(curr);
|
||||||
|
if (item->type == VAL_STRING) {
|
||||||
|
config->value.list_val.items[i] = strdup(item->data.string);
|
||||||
|
} else if (item->type == VAL_SYMBOL) {
|
||||||
|
config->value.list_val.items[i] = strdup(item->data.symbol);
|
||||||
|
} else if (item->type == VAL_NUMBER) {
|
||||||
|
char buffer[32];
|
||||||
|
snprintf(buffer, sizeof(buffer), "%.2f", item->data.number);
|
||||||
|
config->value.list_val.items[i] = strdup(buffer);
|
||||||
|
} else {
|
||||||
|
config->value.list_val.items[i] = strdup("unknown");
|
||||||
|
}
|
||||||
|
curr = cdr(curr);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
config->type = CONFIG_UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience functions for common types
|
||||||
|
int get_config_int(const char *name, int default_val) {
|
||||||
|
ConfigValue *val = get_config_var(name);
|
||||||
|
if (!val)
|
||||||
|
return default_val;
|
||||||
|
|
||||||
|
int result = default_val;
|
||||||
|
if (val->type == CONFIG_INT) {
|
||||||
|
result = val->value.int_val;
|
||||||
|
} else if (val->type == CONFIG_FLOAT) {
|
||||||
|
result = (int)val->value.float_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
free_config_value(val);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_config_float(const char *name, double default_val) {
|
||||||
|
ConfigValue *val = get_config_var(name);
|
||||||
|
if (!val)
|
||||||
|
return default_val;
|
||||||
|
|
||||||
|
double result = default_val;
|
||||||
|
if (val->type == CONFIG_FLOAT) {
|
||||||
|
result = val->value.float_val;
|
||||||
|
} else if (val->type == CONFIG_INT) {
|
||||||
|
result = (double)val->value.int_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
free_config_value(val);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *get_config_string(const char *name, const char *default_val) {
|
||||||
|
ConfigValue *val = get_config_var(name);
|
||||||
|
if (!val)
|
||||||
|
return default_val ? strdup(default_val) : NULL;
|
||||||
|
|
||||||
|
char *result = NULL;
|
||||||
|
if (val->type == CONFIG_STRING) {
|
||||||
|
result = strdup(val->value.string_val);
|
||||||
|
} else if (default_val) {
|
||||||
|
result = strdup(default_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
free_config_value(val);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_config_bool(const char *name, bool default_val) {
|
||||||
|
ConfigValue *val = get_config_var(name);
|
||||||
|
if (!val)
|
||||||
|
return default_val;
|
||||||
|
|
||||||
|
bool result = default_val;
|
||||||
|
if (val->type == CONFIG_BOOL) {
|
||||||
|
result = val->value.bool_val;
|
||||||
|
} else if (val->type == CONFIG_INT) {
|
||||||
|
result = val->value.int_val != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
free_config_value(val);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
char **get_config_list(const char *name, int *count) {
|
||||||
|
ConfigValue *val = get_config_var(name);
|
||||||
|
if (!val || val->type != CONFIG_LIST) {
|
||||||
|
if (count)
|
||||||
|
*count = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char **result = malloc(sizeof(char *) * val->value.list_val.count);
|
||||||
|
for (int i = 0; i < val->value.list_val.count; i++) {
|
||||||
|
result[i] = strdup(val->value.list_val.items[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count)
|
||||||
|
*count = val->value.list_val.count;
|
||||||
|
|
||||||
|
free_config_value(val);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free ConfigValue
|
||||||
|
void free_config_value(ConfigValue *val) {
|
||||||
|
if (!val)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (val->type) {
|
||||||
|
case CONFIG_STRING:
|
||||||
|
free(val->value.string_val);
|
||||||
|
break;
|
||||||
|
case CONFIG_LIST:
|
||||||
|
for (int i = 0; i < val->value.list_val.count; i++) {
|
||||||
|
free(val->value.list_val.items[i]);
|
||||||
|
}
|
||||||
|
free(val->value.list_val.items);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if variable exists
|
||||||
|
bool config_var_exists(const char *name) {
|
||||||
|
return env_get(global_env, name) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set variables from C code
|
||||||
|
void set_config_int(const char *name, int value) {
|
||||||
|
env_set(global_env, name, make_number(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_config_float(const char *name, double value) {
|
||||||
|
env_set(global_env, name, make_number(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_config_string(const char *name, const char *value) {
|
||||||
|
env_set(global_env, name, make_string(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_config_bool(const char *name, bool value) {
|
||||||
|
env_set(global_env, name, make_symbol(value ? "true" : "false"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print all variables (for debugging)
|
||||||
|
void print_all_config_vars(void) {
|
||||||
|
printf("\n=== Configuration Variables ===\n");
|
||||||
|
Env *env = global_env;
|
||||||
|
|
||||||
|
while (env) {
|
||||||
|
for (int i = 0; i < env->count; i++) {
|
||||||
|
printf("%-20s = ", env->names[i]);
|
||||||
|
print_value(env->values[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
env = env->parent;
|
||||||
|
}
|
||||||
|
printf("==============================\n\n");
|
||||||
|
}
|
||||||
@@ -1,333 +0,0 @@
|
|||||||
|
|
||||||
#include "../include/config_tools.h"
|
|
||||||
#include "../include/function.h"
|
|
||||||
#include "../include/node.h"
|
|
||||||
#define GLOBAL_CONFIG
|
|
||||||
#include "../include/data.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
void config_create(void) {
|
|
||||||
/*
|
|
||||||
* @brief Initialize config structure
|
|
||||||
*/
|
|
||||||
config.key_mappings = NULL;
|
|
||||||
config.variables = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_builtin_functions(void) {
|
|
||||||
register_builtin_function("map-key", handle_map_key, true);
|
|
||||||
register_builtin_function("define", handle_define, false); // Don't eval args
|
|
||||||
register_builtin_function("func", handle_function, false); // Don't eval args
|
|
||||||
}
|
|
||||||
|
|
||||||
// Built-in function handlers
|
|
||||||
int handle_map_key(node_t **args, int arg_count) {
|
|
||||||
if (arg_count != 2 || args[0]->type != NODE_STRING ||
|
|
||||||
args[1]->type != NODE_FUNCTION_REF) {
|
|
||||||
fprintf(stderr, "Error: map-key requires (key_combo, function_ref)\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
key_mapping_t *mapping = malloc(sizeof(key_mapping_t));
|
|
||||||
if (!mapping)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
mapping->key_combo = strdup(args[0]->data.string);
|
|
||||||
mapping->function_name = strdup(args[1]->data.function_ref);
|
|
||||||
mapping->next = config.key_mappings;
|
|
||||||
config.key_mappings = mapping;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int handle_define(node_t **args, int arg_count) {
|
|
||||||
|
|
||||||
if (arg_count != 2) {
|
|
||||||
fprintf(stderr, "Error: define requires exactly 2 arguments, got %d\n",
|
|
||||||
arg_count);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args[0]->type != NODE_SYMBOL) {
|
|
||||||
fprintf(
|
|
||||||
stderr,
|
|
||||||
"Error: define requires first argument to be a symbol, got type %d\n",
|
|
||||||
args[0]->type);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
config_var_t *var = malloc(sizeof(config_var_t));
|
|
||||||
if (!var) {
|
|
||||||
fprintf(stderr, "Error: Failed to allocate memory for variable\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var->name = strdup(args[0]->data.symbol);
|
|
||||||
if (!var->name) {
|
|
||||||
free(var);
|
|
||||||
fprintf(stderr, "Error: Failed to allocate memory for variable name\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var->value = copy_node(args[1]);
|
|
||||||
if (!var->value) {
|
|
||||||
free(var->name);
|
|
||||||
free(var);
|
|
||||||
fprintf(stderr, "Error: Failed to copy variable value\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var->next = config.variables;
|
|
||||||
config.variables = var;
|
|
||||||
|
|
||||||
// Verify it was stored
|
|
||||||
config_var_t *check = config.variables;
|
|
||||||
while (check) {
|
|
||||||
check = check->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int handle_function(node_t **args, int arg_count) {
|
|
||||||
if (arg_count != 3) {
|
|
||||||
fprintf(stderr, "Error: func requires exactly 3 arguments, got %d\n",
|
|
||||||
arg_count);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args[0]->type != NODE_SYMBOL) {
|
|
||||||
fprintf(
|
|
||||||
stderr,
|
|
||||||
"Error: func requires first argument to be a symbol (function name)\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args[1]->type != NODE_LIST) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Error: func requires second argument to be a list (parameters)\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare parameters
|
|
||||||
node_t *param_list = args[1];
|
|
||||||
int param_count = param_list->data.list.child_count;
|
|
||||||
char **parameters = NULL;
|
|
||||||
|
|
||||||
if (param_count > 0) {
|
|
||||||
parameters = malloc(sizeof(char *) * param_count);
|
|
||||||
if (!parameters)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < param_count; i++) {
|
|
||||||
if (param_list->data.list.children[i]->type != NODE_SYMBOL) {
|
|
||||||
fprintf(stderr, "Error: Function parameters must be symbols\n");
|
|
||||||
for (int j = 0; j < i; j++) {
|
|
||||||
free(parameters[j]);
|
|
||||||
}
|
|
||||||
free(parameters);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
parameters[i] = strdup(param_list->data.list.children[i]->data.symbol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare body
|
|
||||||
node_t *body = args[2];
|
|
||||||
if (body->type != NODE_LIST) {
|
|
||||||
node_t *wrapper = create_list_node();
|
|
||||||
add_to_list(wrapper, copy_node(body));
|
|
||||||
body = wrapper;
|
|
||||||
} else {
|
|
||||||
body = copy_node(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the function
|
|
||||||
return register_user_function(args[0]->data.symbol, parameters, param_count,
|
|
||||||
body);
|
|
||||||
}
|
|
||||||
|
|
||||||
int execute_function_call(node_t *call) {
|
|
||||||
exec_context_t ctx = {0};
|
|
||||||
ctx.local_vars.names = NULL;
|
|
||||||
ctx.local_vars.values = NULL;
|
|
||||||
ctx.local_vars.count = 0;
|
|
||||||
|
|
||||||
node_t *result = execute_function_call_in_context(&ctx, call);
|
|
||||||
if (result) {
|
|
||||||
free_node(result);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_parse_string(const char *input) {
|
|
||||||
lexer_t lexer = {0};
|
|
||||||
lexer.input = input;
|
|
||||||
lexer.pos = 0;
|
|
||||||
lexer.line = 1;
|
|
||||||
lexer.column = 1;
|
|
||||||
lexer.current_char = input[0];
|
|
||||||
|
|
||||||
while (lexer.current_char != '\0') {
|
|
||||||
skip_whitespace(&lexer);
|
|
||||||
skip_comment(&lexer);
|
|
||||||
skip_whitespace(&lexer);
|
|
||||||
|
|
||||||
if (lexer.current_char == '\0')
|
|
||||||
break;
|
|
||||||
if (lexer.current_char == '\n') {
|
|
||||||
advance_char(&lexer);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t *stmt = parse_statement(&lexer);
|
|
||||||
if (stmt) {
|
|
||||||
if (execute_function_call(stmt) != 0) {
|
|
||||||
fprintf(stderr, "Error executing statement at line %d\n", lexer.line);
|
|
||||||
}
|
|
||||||
if (stmt->type != NODE_FUNCTION_CALL ||
|
|
||||||
(strcmp(stmt->data.call.function_name, "define") != 0 &&
|
|
||||||
strcmp(stmt->data.call.function_name, "func") != 0)) {
|
|
||||||
free_node(stmt); // Don't free if ownership was transferred
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip to next line
|
|
||||||
while (lexer.current_char != '\n' && lexer.current_char != '\0') {
|
|
||||||
advance_char(&lexer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_parse_file(const char *filename) {
|
|
||||||
FILE *file = fopen(filename, "r");
|
|
||||||
long file_size = 0;
|
|
||||||
char *content = NULL;
|
|
||||||
int result = 0;
|
|
||||||
if (!file) {
|
|
||||||
perror("Error opening config file");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fseek(file, 0, SEEK_END);
|
|
||||||
file_size = ftell(file);
|
|
||||||
fseek(file, 0, SEEK_SET);
|
|
||||||
|
|
||||||
content = (char *)malloc(file_size + 1);
|
|
||||||
if (!content) {
|
|
||||||
fclose(file);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fread(content, 1, file_size, file);
|
|
||||||
content[file_size] = '\0';
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
result = config_parse_string(content);
|
|
||||||
free(content);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getter functions
|
|
||||||
|
|
||||||
const char *config_get_key_mapping(const char *key_combo) {
|
|
||||||
key_mapping_t *mapping = config.key_mappings;
|
|
||||||
while (mapping) {
|
|
||||||
if (strcmp(mapping->key_combo, key_combo) == 0) {
|
|
||||||
return mapping->function_name;
|
|
||||||
}
|
|
||||||
mapping = mapping->next;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t *find_variable(const char *name) {
|
|
||||||
config_var_t *var = config.variables;
|
|
||||||
while (var) {
|
|
||||||
if (strcmp(var->name, name) == 0) {
|
|
||||||
return var->value;
|
|
||||||
}
|
|
||||||
var = var->next;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *config_get_string(const char *path, const char *default_value) {
|
|
||||||
node_t *node = find_variable(path);
|
|
||||||
if (node && node->type == NODE_STRING) {
|
|
||||||
return node->data.string;
|
|
||||||
}
|
|
||||||
return default_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_get_int(const char *path, int default_value) {
|
|
||||||
node_t *node = find_variable(path);
|
|
||||||
if (node && node->type == NODE_NUMBER) {
|
|
||||||
return (int)node->data.number;
|
|
||||||
}
|
|
||||||
return default_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
double config_get_double(const char *path, double default_value) {
|
|
||||||
node_t *node = find_variable(path);
|
|
||||||
if (node && node->type == NODE_NUMBER) {
|
|
||||||
return node->data.number;
|
|
||||||
}
|
|
||||||
return default_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool config_get_bool(const char *path, bool default_value) {
|
|
||||||
node_t *node = find_variable(path);
|
|
||||||
if (node && node->type == NODE_BOOLEAN) {
|
|
||||||
return node->data.boolean;
|
|
||||||
}
|
|
||||||
return default_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void config_print_all(void) {
|
|
||||||
printf("Key Mappings:\n");
|
|
||||||
printf("%-20s -> %s\n", "Key Combination", "Function\n");
|
|
||||||
printf("%-20s %s\n", "---------------", "--------\n");
|
|
||||||
|
|
||||||
key_mapping_t *mapping = config.key_mappings;
|
|
||||||
while (mapping) {
|
|
||||||
printf("%-20s -> %s\n", mapping->key_combo, mapping->function_name);
|
|
||||||
mapping = mapping->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\nVariables:\n");
|
|
||||||
printf("%-20s = %s\n", "Name", "Value\n");
|
|
||||||
printf("%-20s %s\n", "----", "-----\n");
|
|
||||||
|
|
||||||
config_var_t *var = config.variables;
|
|
||||||
while (var) {
|
|
||||||
printf("%-20s = \n", var->name);
|
|
||||||
|
|
||||||
switch (var->value->type) {
|
|
||||||
case NODE_STRING:
|
|
||||||
printf("\"%s\"", var->value->data.string);
|
|
||||||
break;
|
|
||||||
case NODE_NUMBER:
|
|
||||||
printf("%g", var->value->data.number);
|
|
||||||
break;
|
|
||||||
case NODE_BOOLEAN:
|
|
||||||
printf("%s", var->value->data.boolean ? "true" : "false");
|
|
||||||
break;
|
|
||||||
case NODE_SYMBOL:
|
|
||||||
printf("%s", var->value->data.symbol);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("[unknown type]");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
var = var->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+91
-81
@@ -1,92 +1,102 @@
|
|||||||
#include "../include/data.h"
|
|
||||||
#include "../include/define.h"
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#define GLOBAL_ENV_
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
void advance_char(lexer_t *lexer) {
|
#include "../include/data.h"
|
||||||
if (lexer->current_char == '\n') {
|
|
||||||
lexer->line++;
|
|
||||||
lexer->column = 1;
|
|
||||||
} else {
|
|
||||||
lexer->column++;
|
|
||||||
}
|
|
||||||
lexer->pos++;
|
|
||||||
lexer->current_char = lexer->input[lexer->pos];
|
|
||||||
}
|
|
||||||
|
|
||||||
void skip_whitespace(lexer_t *lexer) {
|
// Memory management
|
||||||
while (isspace(lexer->current_char) && lexer->current_char != '\n') {
|
Value *make_value(ValueType type) {
|
||||||
advance_char(lexer);
|
/*
|
||||||
}
|
** Create a Value struct of type -type-
|
||||||
}
|
*/
|
||||||
|
Value *v = (Value *)malloc(sizeof(Value));
|
||||||
void skip_comment(lexer_t *lexer) {
|
if (!v) {
|
||||||
if (lexer->current_char == '/' && lexer->input[lexer->pos + 1] == '/') {
|
|
||||||
while (lexer->current_char != '\n' && lexer->current_char != '\0') {
|
|
||||||
advance_char(lexer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t *create_list_node(void) {
|
|
||||||
node_t *node = malloc(sizeof(node_t));
|
|
||||||
if (!node) {
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
v->type = type;
|
||||||
node->type = NODE_LIST;
|
return v;
|
||||||
node->data.list.child_count = 0;
|
|
||||||
node->data.list.children = NULL;
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_arg_to_call(node_t *call, node_t *arg) {
|
Value *make_nil(void) { return make_value(VAL_NIL); }
|
||||||
if (call->type == NODE_FUNCTION_CALL &&
|
|
||||||
call->data.call.arg_count < MAX_ARGS) {
|
Value *make_number(double n) {
|
||||||
call->data.call.args =
|
Value *v = make_value(VAL_NUMBER);
|
||||||
(node_t **)realloc(call->data.call.args,
|
if (!v) {
|
||||||
(call->data.call.arg_count + 1) * sizeof(node_t *));
|
fprintf(stderr, "ERROR : not assigned\n");
|
||||||
call->data.call.args[call->data.call.arg_count++] = arg;
|
|
||||||
}
|
}
|
||||||
|
v->data.number = n;
|
||||||
|
fprintf(stderr, "DEBUG : value %lf\n", n);
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_to_list(node_t *list, node_t *element) {
|
Value *make_string(const char *s) {
|
||||||
node_t **new_children = NULL;
|
Value *v = make_value(VAL_STRING);
|
||||||
if (!list || !element) {
|
v->data.string = strdup(s);
|
||||||
return;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure this is actually a list node
|
Value *make_symbol(const char *s) {
|
||||||
if (list->type != NODE_LIST) {
|
Value *v = make_value(VAL_SYMBOL);
|
||||||
return;
|
v->data.symbol = strdup(s);
|
||||||
}
|
return v;
|
||||||
|
}
|
||||||
// If this is the first element
|
|
||||||
if (list->data.list.children == NULL) {
|
Value *make_list(Value *car, Value *cdr) {
|
||||||
|
fprintf(stderr, "DEBUG : list done\n");
|
||||||
fprintf(stderr, "DEBUG : child number 0 added\n");
|
Value *v = make_value(VAL_LIST);
|
||||||
list->data.list.children = malloc(sizeof(node_t *));
|
if (!v) {
|
||||||
if (!list->data.list.children) {
|
fprintf(stderr, "ERROR : value\n");
|
||||||
return; // Memory allocation failed
|
}
|
||||||
}
|
v->data.list.cdr = car;
|
||||||
list->data.list.children[0] = element;
|
v->data.list.cdr = cdr;
|
||||||
list->data.list.child_count = 1;
|
return v;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
Value *make_builtin(BuiltinFunc func) {
|
||||||
// Reallocate to add new element
|
Value *v = make_value(VAL_BUILTIN);
|
||||||
new_children = realloc(list->data.list.children,
|
v->data.builtin = func;
|
||||||
sizeof(node_t *) * (list->data.list.child_count + 1));
|
return v;
|
||||||
if (!new_children) {
|
}
|
||||||
return; // Memory allocation failed
|
|
||||||
}
|
// Environment functions
|
||||||
|
Env *make_env(Env *parent) {
|
||||||
list->data.list.children = new_children;
|
Env *env = malloc(sizeof(Env));
|
||||||
list->data.list.children[list->data.list.child_count] = element;
|
env->names = malloc(sizeof(char *) * 16);
|
||||||
list->data.list.child_count++;
|
env->values = malloc(sizeof(Value *) * 16);
|
||||||
fprintf(stderr, "DEBUG : child number %d added\n",
|
env->count = 0;
|
||||||
list->data.list.child_count);
|
env->capacity = 16;
|
||||||
|
env->parent = parent;
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
void env_set(Env *env, const char *name, Value *value) {
|
||||||
|
// Check if variable already exists
|
||||||
|
for (int i = 0; i < env->count; i++) {
|
||||||
|
if (strcmp(env->names[i], name) == 0) {
|
||||||
|
env->values[i] = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new variable
|
||||||
|
if (env->count >= env->capacity) {
|
||||||
|
env->capacity *= 2;
|
||||||
|
env->names = realloc(env->names, sizeof(char *) * env->capacity);
|
||||||
|
env->values = realloc(env->values, sizeof(Value *) * env->capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
env->names[env->count] = strdup(name);
|
||||||
|
env->values[env->count] = value;
|
||||||
|
env->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value *env_get(Env *env, const char *name) {
|
||||||
|
while (env) {
|
||||||
|
for (int i = 0; i < env->count; i++) {
|
||||||
|
if (strcmp(env->names[i], name) == 0) {
|
||||||
|
return env->values[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
env = env->parent;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
+468
-165
@@ -1,215 +1,518 @@
|
|||||||
#include "../include/function.h"
|
#include "../include/function.h"
|
||||||
#include "../include/data.h"
|
#include "../include/utils.h"
|
||||||
#include "../include/node.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
int register_builtin_function(const char *name,
|
// Builtins
|
||||||
int (*handler)(node_t **args, int arg_count),
|
|
||||||
bool eval_args) {
|
|
||||||
unified_function_t *func = malloc(sizeof(unified_function_t));
|
|
||||||
if (!func)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
func->name = strdup(name);
|
Value *builtin_add(Value *args) {
|
||||||
func->type = FUNC_BUILTIN;
|
double sum = 0;
|
||||||
func->data.builtin.handler = handler;
|
while (args->type == VAL_LIST) {
|
||||||
func->data.builtin.eval_args = eval_args;
|
Value *arg = car(args);
|
||||||
func->next = unified_functions;
|
if (arg->type == VAL_NUMBER) {
|
||||||
unified_functions = func;
|
sum += arg->data.number;
|
||||||
fprintf(stderr, "%s succefully added\n", name);
|
}
|
||||||
|
args = cdr(args);
|
||||||
return 0;
|
}
|
||||||
|
return make_number(sum);
|
||||||
}
|
}
|
||||||
|
|
||||||
int register_user_function(const char *name, char **parameters, int param_count,
|
Value *builtin_sub(Value *args) {
|
||||||
node_t *body) {
|
if (args->type != VAL_LIST)
|
||||||
unified_function_t *func = malloc(sizeof(unified_function_t));
|
return make_number(0);
|
||||||
if (!func)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
func->name = strdup(name);
|
Value *first = car(args);
|
||||||
func->type = FUNC_USER_DEFINED;
|
if (first->type != VAL_NUMBER)
|
||||||
func->data.user_defined.parameters = parameters;
|
return make_number(0);
|
||||||
func->data.user_defined.param_count = param_count;
|
|
||||||
func->data.user_defined.body = body;
|
|
||||||
func->next = unified_functions;
|
|
||||||
unified_functions = func;
|
|
||||||
|
|
||||||
return 0;
|
double result = first->data.number;
|
||||||
|
args = cdr(args);
|
||||||
|
|
||||||
|
if (args->type == VAL_NIL) {
|
||||||
|
return make_number(-result);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (args->type == VAL_LIST) {
|
||||||
|
Value *arg = car(args);
|
||||||
|
if (arg->type == VAL_NUMBER) {
|
||||||
|
result -= arg->data.number;
|
||||||
|
}
|
||||||
|
args = cdr(args);
|
||||||
|
}
|
||||||
|
return make_number(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
int register_registry_function(const char *name, command_func_t c_func) {
|
Value *builtin_mul(Value *args) {
|
||||||
unified_function_t *func = malloc(sizeof(unified_function_t));
|
double product = 1;
|
||||||
if (!func)
|
while (args->type == VAL_LIST) {
|
||||||
return -1;
|
Value *arg = car(args);
|
||||||
|
if (arg->type == VAL_NUMBER) {
|
||||||
func->name = strdup(name);
|
product *= arg->data.number;
|
||||||
func->type = FUNC_REGISTRY;
|
}
|
||||||
func->data.registry.func = c_func;
|
args = cdr(args);
|
||||||
func->next = unified_functions;
|
}
|
||||||
unified_functions = func;
|
return make_number(product);
|
||||||
fprintf(stderr, "DEBIG : %s added\n", name);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int register_function(const char *name, command_func_t func) {
|
Value *builtin_div(Value *args) {
|
||||||
return register_registry_function(name, func);
|
if (args->type != VAL_LIST)
|
||||||
|
return make_number(0);
|
||||||
|
|
||||||
|
Value *first = car(args);
|
||||||
|
if (first->type != VAL_NUMBER)
|
||||||
|
return make_number(0);
|
||||||
|
|
||||||
|
double result = first->data.number;
|
||||||
|
args = cdr(args);
|
||||||
|
|
||||||
|
while (args->type == VAL_LIST) {
|
||||||
|
Value *arg = car(args);
|
||||||
|
if (arg->type == VAL_NUMBER && arg->data.number != 0) {
|
||||||
|
result /= arg->data.number;
|
||||||
|
}
|
||||||
|
args = cdr(args);
|
||||||
|
}
|
||||||
|
return make_number(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
unified_function_t *find_unified_function(const char *name) {
|
Value *builtin_eq(Value *args) {
|
||||||
unified_function_t *func = unified_functions;
|
if (list_length(args) < 2)
|
||||||
while (func) {
|
return make_nil();
|
||||||
// fprintf(stderr, "%s\n", func->name);
|
|
||||||
if (strcmp(func->name, name) == 0) {
|
Value *first = car(args);
|
||||||
|
args = cdr(args);
|
||||||
|
|
||||||
|
while (args->type == VAL_LIST) {
|
||||||
|
Value *arg = car(args);
|
||||||
|
|
||||||
|
if (first->type != arg->type)
|
||||||
|
return make_nil();
|
||||||
|
|
||||||
|
switch (first->type) {
|
||||||
|
case VAL_NUMBER:
|
||||||
|
if (first->data.number != arg->data.number)
|
||||||
|
return make_nil();
|
||||||
|
break;
|
||||||
|
case VAL_STRING:
|
||||||
|
if (strcmp(first->data.string, arg->data.string) != 0)
|
||||||
|
return make_nil();
|
||||||
|
break;
|
||||||
|
case VAL_SYMBOL:
|
||||||
|
if (strcmp(first->data.symbol, arg->data.symbol) != 0)
|
||||||
|
return make_nil();
|
||||||
|
break;
|
||||||
|
case VAL_NIL:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return make_nil();
|
||||||
|
}
|
||||||
|
|
||||||
|
args = cdr(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return make_symbol("true");
|
||||||
|
}
|
||||||
|
|
||||||
|
Value *builtin_list(Value *args) { return args; }
|
||||||
|
|
||||||
|
Value *builtin_car(Value *args) {
|
||||||
|
if (args->type != VAL_LIST)
|
||||||
|
return make_nil();
|
||||||
|
Value *arg = car(args);
|
||||||
|
return car(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value *builtin_cdr(Value *args) {
|
||||||
|
if (args->type != VAL_LIST)
|
||||||
|
return make_nil();
|
||||||
|
Value *arg = car(args);
|
||||||
|
return cdr(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluator
|
||||||
|
|
||||||
|
Value *eval_list(Value *list) {
|
||||||
|
if (list->type == VAL_NIL)
|
||||||
|
return make_nil();
|
||||||
|
Value *head = eval_expr(car(list));
|
||||||
|
Value *tail = eval_list(cdr(list));
|
||||||
|
return make_list(head, tail);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value *apply_function(Value *func, Value *args) {
|
||||||
|
if (func->type == VAL_FUNCTION) {
|
||||||
|
|
||||||
|
// Bind parameters
|
||||||
|
Value *arg_list = args;
|
||||||
|
for (int i = 0; i < func->data.function.param_count; i++) {
|
||||||
|
if (arg_list->type == VAL_LIST) {
|
||||||
|
env_set(local_env, func->data.function.params[i], car(arg_list));
|
||||||
|
arg_list = cdr(arg_list);
|
||||||
|
} else {
|
||||||
|
env_set(local_env, func->data.function.params[i], make_nil());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return eval_expr(func->data.function.body, local_env);
|
||||||
|
}
|
||||||
|
|
||||||
|
return make_nil();
|
||||||
|
}
|
||||||
|
|
||||||
|
Value *eval_expr(Value *expr) {
|
||||||
|
fprintf(stderr, "DEBUG : Starting evaluation %d\n", expr->type);
|
||||||
|
switch (expr->type) {
|
||||||
|
case VAL_NUMBER:
|
||||||
|
case VAL_STRING:
|
||||||
|
case VAL_NIL:
|
||||||
|
return expr;
|
||||||
|
|
||||||
|
case VAL_SYMBOL:
|
||||||
|
fprintf(stderr, "DEBUG : symbol %s\n", expr->data.symbol);
|
||||||
|
if (strcmp(expr->data.symbol, "true") == 0)
|
||||||
|
return expr;
|
||||||
|
if (strcmp(expr->data.symbol, "false") == 0)
|
||||||
|
return expr;
|
||||||
|
return env_get(env, expr->data.symbol) ?: make_nil();
|
||||||
|
|
||||||
|
case VAL_LIST: {
|
||||||
|
fprintf(stderr, "DEBUG : eval list\n");
|
||||||
|
if (is_nil(expr))
|
||||||
|
return expr;
|
||||||
|
|
||||||
|
Value *head = car(expr);
|
||||||
|
fprintf(stderr, "%s\n", head->data.symbol);
|
||||||
|
Value *args = cdr(expr);
|
||||||
|
|
||||||
|
// Special forms
|
||||||
|
if (is_symbol(head, "quote")) {
|
||||||
|
return car(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_symbol(head, "def")) {
|
||||||
|
fprintf(stderr, "DEBUG : def new var -> %s\n", car(args)->data.symbol);
|
||||||
|
Value *name = car(args);
|
||||||
|
Value *value = eval_expr(car(cdr(args)), env);
|
||||||
|
if (name->type == VAL_SYMBOL) {
|
||||||
|
env_set(env, name->data.symbol, value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_symbol(head, "defun")) {
|
||||||
|
Value *name = car(args);
|
||||||
|
Value *params = car(cdr(args));
|
||||||
|
Value *body = car(cdr(cdr(args)));
|
||||||
|
|
||||||
|
if (name->type == VAL_SYMBOL) {
|
||||||
|
Value *func = make_value(VAL_FUNCTION);
|
||||||
|
func->data.function.param_count = list_length(params);
|
||||||
|
func->data.function.params =
|
||||||
|
malloc(sizeof(char *) * func->data.function.param_count);
|
||||||
|
|
||||||
|
Value *param_list = params;
|
||||||
|
for (int i = 0; i < func->data.function.param_count; i++) {
|
||||||
|
Value *param = car(param_list);
|
||||||
|
if (param->type == VAL_SYMBOL) {
|
||||||
|
func->data.function.params[i] = strdup(param->data.symbol);
|
||||||
|
}
|
||||||
|
param_list = cdr(param_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
func->data.function.body = body;
|
||||||
|
func->data.function.closure = env;
|
||||||
|
|
||||||
|
env_set(env, name->data.symbol, func);
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
func = func->next;
|
return make_nil();
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t *execute_unified_function(exec_context_t *ctx, const char *name,
|
|
||||||
node_t **args, int arg_count) {
|
|
||||||
unified_function_t *func = find_unified_function(name);
|
|
||||||
if (!func) {
|
|
||||||
fprintf(stderr, "Error: Unknown function '%s'\n", name);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (func->type) {
|
if (is_symbol(head, "if")) {
|
||||||
case FUNC_BUILTIN: {
|
Value *condition = eval_expr(car(args), env);
|
||||||
node_t **eval_args = args;
|
Value *then_expr = car(cdr(args));
|
||||||
|
Value *else_expr = car(cdr(cdr(args)));
|
||||||
|
|
||||||
// Evaluate arguments if needed
|
bool is_true = !(condition->type == VAL_NIL ||
|
||||||
if (func->data.builtin.eval_args) {
|
(condition->type == VAL_SYMBOL &&
|
||||||
eval_args = malloc(sizeof(node_t *) * arg_count);
|
strcmp(condition->data.symbol, "false") == 0));
|
||||||
if (!eval_args)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
for (int i = 0; i < arg_count; i++) {
|
if (is_true) {
|
||||||
eval_args[i] = evaluate_node(ctx, args[i]);
|
return eval_expr(then_expr, env);
|
||||||
if (!eval_args[i]) {
|
} else {
|
||||||
// Clean up on failure
|
return eval_expr(else_expr, env);
|
||||||
for (int j = 0; j < i; j++) {
|
|
||||||
free_node(eval_args[j]);
|
|
||||||
}
|
|
||||||
free(eval_args);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = func->data.builtin.handler(eval_args, arg_count);
|
if (is_symbol(head, "defkeymap")) {
|
||||||
|
Value *name = car(args);
|
||||||
|
if (name->type != VAL_SYMBOL)
|
||||||
|
return make_nil();
|
||||||
|
|
||||||
// Clean up evaluated arguments if we allocated them
|
Value *keymap = make_value(VAL_KEYMAP);
|
||||||
if (func->data.builtin.eval_args) {
|
keymap->data.keymap.capacity = 16;
|
||||||
for (int i = 0; i < arg_count; i++) {
|
keymap->data.keymap.count = 0;
|
||||||
free_node(eval_args[i]);
|
keymap->data.keymap.keys =
|
||||||
}
|
malloc(sizeof(char *) * keymap->data.keymap.capacity);
|
||||||
free(eval_args);
|
keymap->data.keymap.values =
|
||||||
|
malloc(sizeof(Value *) * keymap->data.keymap.capacity);
|
||||||
|
|
||||||
|
Value *bindings = cdr(args);
|
||||||
|
while (bindings->type == VAL_LIST && cdr(bindings)->type == VAL_LIST) {
|
||||||
|
Value *key = car(bindings);
|
||||||
|
Value *value = car(cdr(bindings));
|
||||||
|
|
||||||
|
if (key->type == VAL_STRING && value->type == VAL_SYMBOL) {
|
||||||
|
if (keymap->data.keymap.count >= keymap->data.keymap.capacity) {
|
||||||
|
keymap->data.keymap.capacity *= 2;
|
||||||
|
keymap->data.keymap.keys =
|
||||||
|
realloc(keymap->data.keymap.keys,
|
||||||
|
sizeof(char *) * keymap->data.keymap.capacity);
|
||||||
|
keymap->data.keymap.values =
|
||||||
|
realloc(keymap->data.keymap.values,
|
||||||
|
sizeof(Value *) * keymap->data.keymap.capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
return create_boolean_node(result == 0);
|
keymap->data.keymap.keys[keymap->data.keymap.count] =
|
||||||
|
strdup(key->data.string);
|
||||||
|
keymap->data.keymap.values[keymap->data.keymap.count] = value;
|
||||||
|
keymap->data.keymap.count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
case FUNC_USER_DEFINED: {
|
bindings = cdr(cdr(bindings));
|
||||||
fprintf(stderr, "DEBUG : BODY length %d\n",
|
|
||||||
func->data.user_defined.body->data.list.child_count);
|
|
||||||
if (func->data.user_defined.param_count != arg_count) {
|
|
||||||
fprintf(stderr, "Error: Function '%s' expects %d arguments, got %d\n",
|
|
||||||
name, func->data.user_defined.param_count, arg_count);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new execution context
|
env_set(env, name->data.symbol, keymap);
|
||||||
exec_context_t new_ctx = *ctx;
|
return keymap;
|
||||||
|
|
||||||
if (func->data.user_defined.param_count > 0) {
|
|
||||||
new_ctx.local_vars.count = func->data.user_defined.param_count;
|
|
||||||
new_ctx.local_vars.names =
|
|
||||||
malloc(sizeof(char *) * func->data.user_defined.param_count);
|
|
||||||
new_ctx.local_vars.values =
|
|
||||||
malloc(sizeof(node_t *) * func->data.user_defined.param_count);
|
|
||||||
|
|
||||||
for (int i = 0; i < func->data.user_defined.param_count; i++) {
|
|
||||||
new_ctx.local_vars.names[i] = func->data.user_defined.parameters[i];
|
|
||||||
new_ctx.local_vars.values[i] = evaluate_node(ctx, args[i]);
|
|
||||||
if (!new_ctx.local_vars.values[i]) {
|
|
||||||
// Clean up on failure
|
|
||||||
for (int j = 0; j < i; j++) {
|
|
||||||
free_node(new_ctx.local_vars.values[j]);
|
|
||||||
}
|
|
||||||
free(new_ctx.local_vars.names);
|
|
||||||
free(new_ctx.local_vars.values);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "DEBUG : body %s\n",
|
// Function application
|
||||||
func->data.user_defined.body->data.symbol);
|
Value *func = eval_expr(head, env);
|
||||||
node_t *result = evaluate_node(&new_ctx, func->data.user_defined.body);
|
Value *eval_args = eval_list(args, env);
|
||||||
|
return apply_function(func, eval_args, env);
|
||||||
// Clean up context
|
|
||||||
if (func->data.user_defined.param_count > 0) {
|
|
||||||
for (int i = 0; i < func->data.user_defined.param_count; i++) {
|
|
||||||
free_node(new_ctx.local_vars.values[i]);
|
|
||||||
}
|
|
||||||
free(new_ctx.local_vars.names);
|
|
||||||
free(new_ctx.local_vars.values);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
case FUNC_REGISTRY: {
|
|
||||||
// Registry functions don't take arguments and don't return values
|
|
||||||
func->data.registry.func();
|
|
||||||
return create_boolean_node(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Error: Unknown function type for '%s'\n", name);
|
fprintf(stderr, "DEBUG : default\n");
|
||||||
|
return make_nil();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key-mapping
|
||||||
|
|
||||||
|
KeyBindingTable *extract_keybindings(Value *keymap) {
|
||||||
|
if (keymap->type != VAL_KEYMAP)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
KeyBindingTable *table = malloc(sizeof(KeyBindingTable));
|
||||||
|
table->capacity = keymap->data.keymap.count;
|
||||||
|
table->count = keymap->data.keymap.count;
|
||||||
|
table->bindings = malloc(sizeof(KeyBinding) * table->capacity);
|
||||||
|
|
||||||
|
for (int i = 0; i < keymap->data.keymap.count; i++) {
|
||||||
|
table->bindings[i].key_sequence = strdup(keymap->data.keymap.keys[i]);
|
||||||
|
if (keymap->data.keymap.values[i]->type == VAL_SYMBOL) {
|
||||||
|
table->bindings[i].command =
|
||||||
|
strdup(keymap->data.keymap.values[i]->data.symbol);
|
||||||
|
} else {
|
||||||
|
table->bindings[i].command = strdup("unknown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key sequence normalization
|
||||||
|
char *normalize_key_sequence(const char *raw_input) {
|
||||||
|
// Convert raw key input to standard format
|
||||||
|
// Example: Ctrl+S -> "Ctrl+s", Alt+F4 -> "Alt+F4"
|
||||||
|
char *normalized = malloc(strlen(raw_input) + 1);
|
||||||
|
strcpy(normalized, raw_input);
|
||||||
|
|
||||||
|
// Simple normalization - make letters lowercase except function keys
|
||||||
|
for (int i = 0; normalized[i]; i++) {
|
||||||
|
if (normalized[i] >= 'A' && normalized[i] <= 'Z' &&
|
||||||
|
(i == 0 || normalized[i - 1] == '+')) {
|
||||||
|
// Only lowercase single letters after modifiers
|
||||||
|
if (strlen(normalized + i) == 1 || normalized[i + 1] == '\0' ||
|
||||||
|
normalized[i + 1] == ' ') {
|
||||||
|
normalized[i] = tolower(normalized[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute key binding
|
||||||
|
bool execute_keybinding(const char *key_sequence) {
|
||||||
|
if (!active_keybindings)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
char *normalized = normalize_key_sequence(key_sequence);
|
||||||
|
|
||||||
|
for (int i = 0; i < active_keybindings->count; i++) {
|
||||||
|
if (strcmp(active_keybindings->bindings[i].key_sequence, normalized) == 0) {
|
||||||
|
// Found matching key binding - execute the command
|
||||||
|
char *command = active_keybindings->bindings[i].command;
|
||||||
|
|
||||||
|
// Try to find function in environment
|
||||||
|
Value *func = env_get(global_env, command);
|
||||||
|
if (func) {
|
||||||
|
if (func->type == VAL_FUNCTION || func->type == VAL_BUILTIN) {
|
||||||
|
// Execute function with no arguments
|
||||||
|
Value *result = apply_function(func, make_nil(), global_env);
|
||||||
|
free(normalized);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// It's a variable or other value - could be a command string
|
||||||
|
printf("Key bound to value: ");
|
||||||
|
print_value(func);
|
||||||
|
printf("\n");
|
||||||
|
free(normalized);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Command not found in Lisp environment
|
||||||
|
// This is where you'd call your C editor functions
|
||||||
|
printf("Executing C command: %s\n", command);
|
||||||
|
|
||||||
|
// Example: dispatch to your editor's command system
|
||||||
|
if (execute_editor_command(command)) {
|
||||||
|
free(normalized);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(normalized);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set active keymap
|
||||||
|
void set_active_keymap(const char *keymap_name) {
|
||||||
|
Value *keymap = env_get(global_env, keymap_name);
|
||||||
|
if (keymap && keymap->type == VAL_KEYMAP) {
|
||||||
|
if (active_keybindings) {
|
||||||
|
// Free old bindings
|
||||||
|
for (int i = 0; i < active_keybindings->count; i++) {
|
||||||
|
free(active_keybindings->bindings[i].key_sequence);
|
||||||
|
free(active_keybindings->bindings[i].command);
|
||||||
|
}
|
||||||
|
free(active_keybindings->bindings);
|
||||||
|
free(active_keybindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
active_keybindings = extract_keybindings(keymap);
|
||||||
|
printf("Activated keymap: %s (%d bindings)\n", keymap_name,
|
||||||
|
active_keybindings->count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int execute_command(const char *name) {
|
// Combine multiple keymaps (for mode-specific + global bindings)
|
||||||
fprintf(stderr, "Looking for command %s\n", name);
|
void combine_keymaps(const char *global_keymap, const char *mode_keymap) {
|
||||||
unified_function_t *func = find_unified_function(name);
|
Value *global = env_get(global_env, global_keymap);
|
||||||
if (!func) {
|
Value *mode = env_get(global_env, mode_keymap);
|
||||||
return -1;
|
|
||||||
|
int total_bindings = 0;
|
||||||
|
if (global && global->type == VAL_KEYMAP)
|
||||||
|
total_bindings += global->data.keymap.count;
|
||||||
|
if (mode && mode->type == VAL_KEYMAP)
|
||||||
|
total_bindings += mode->data.keymap.count;
|
||||||
|
|
||||||
|
if (active_keybindings) {
|
||||||
|
for (int i = 0; i < active_keybindings->count; i++) {
|
||||||
|
free(active_keybindings->bindings[i].key_sequence);
|
||||||
|
free(active_keybindings->bindings[i].command);
|
||||||
|
}
|
||||||
|
free(active_keybindings->bindings);
|
||||||
|
free(active_keybindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (func->type == FUNC_REGISTRY) {
|
active_keybindings = malloc(sizeof(KeyBindingTable));
|
||||||
func->data.registry.func();
|
active_keybindings->capacity = total_bindings;
|
||||||
return 0;
|
active_keybindings->count = 0;
|
||||||
} else if (func->type == FUNC_USER_DEFINED) {
|
active_keybindings->bindings = malloc(sizeof(KeyBinding) * total_bindings);
|
||||||
fprintf(stderr, "DEBUG ; user definned %s \n", name);
|
|
||||||
exec_context_t ctx = {0};
|
// Add global bindings first
|
||||||
node_t *result = evaluate_node(&ctx, func->data.user_defined.body);
|
if (global && global->type == VAL_KEYMAP) {
|
||||||
if (result) {
|
for (int i = 0; i < global->data.keymap.count; i++) {
|
||||||
free_node(result);
|
active_keybindings->bindings[active_keybindings->count].key_sequence =
|
||||||
return 0;
|
strdup(global->data.keymap.keys[i]);
|
||||||
|
if (global->data.keymap.values[i]->type == VAL_SYMBOL) {
|
||||||
|
active_keybindings->bindings[active_keybindings->count].command =
|
||||||
|
strdup(global->data.keymap.values[i]->data.symbol);
|
||||||
|
}
|
||||||
|
active_keybindings->count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
// Add mode-specific bindings (these override global)
|
||||||
|
if (mode && mode->type == VAL_KEYMAP) {
|
||||||
|
for (int i = 0; i < mode->data.keymap.count; i++) {
|
||||||
|
// Check if this key already exists (override)
|
||||||
|
bool found = false;
|
||||||
|
for (int j = 0; j < active_keybindings->count; j++) {
|
||||||
|
if (strcmp(active_keybindings->bindings[j].key_sequence,
|
||||||
|
mode->data.keymap.keys[i]) == 0) {
|
||||||
|
// Override existing binding
|
||||||
|
free(active_keybindings->bindings[j].command);
|
||||||
|
if (mode->data.keymap.values[i]->type == VAL_SYMBOL) {
|
||||||
|
active_keybindings->bindings[j].command =
|
||||||
|
strdup(mode->data.keymap.values[i]->data.symbol);
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
// Add new binding
|
||||||
|
active_keybindings->bindings[active_keybindings->count].key_sequence =
|
||||||
|
strdup(mode->data.keymap.keys[i]);
|
||||||
|
if (mode->data.keymap.values[i]->type == VAL_SYMBOL) {
|
||||||
|
active_keybindings->bindings[active_keybindings->count].command =
|
||||||
|
strdup(mode->data.keymap.values[i]->data.symbol);
|
||||||
|
}
|
||||||
|
active_keybindings->count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute function call with context support
|
bool execute_editor_command(const char *command) {
|
||||||
node_t *execute_function_call_in_context(exec_context_t *ctx, node_t *call) {
|
// This is where you implement your editor's built-in commands
|
||||||
|
|
||||||
if (call->type != NODE_FUNCTION_CALL) {
|
if (strcmp(command, "save-file") == 0) {
|
||||||
return NULL;
|
printf("Saving current file...\n");
|
||||||
|
// Call your editor's save function
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
fprintf(stderr, "DEBUG : Call in context %s\n",
|
|
||||||
call->data.call.function_name);
|
|
||||||
|
|
||||||
return execute_unified_function(ctx, call->data.call.function_name,
|
if (strcmp(command, "open-file") == 0) {
|
||||||
call->data.call.args,
|
printf("Opening file dialog...\n");
|
||||||
call->data.call.arg_count);
|
// Call your editor's open function
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(command, "quit-editor") == 0) {
|
||||||
|
printf("Quitting editor...\n");
|
||||||
|
// Call your editor's quit function
|
||||||
|
exit(0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(command, "new-file") == 0) {
|
||||||
|
printf("Creating new file...\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(command, "compile-current") == 0) {
|
||||||
|
printf("Compiling current file...\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add more commands as needed
|
||||||
|
printf("Unknown command: %s\n", command);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+85
@@ -0,0 +1,85 @@
|
|||||||
|
#include "../include/init.h"
|
||||||
|
#include "../include/function.h"
|
||||||
|
#include "../include/parser.h"
|
||||||
|
#include "../include/utils.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void init_builtins(Env *env) {
|
||||||
|
env_set(env, "+", make_builtin(builtin_add));
|
||||||
|
env_set(env, "-", make_builtin(builtin_sub));
|
||||||
|
env_set(env, "*", make_builtin(builtin_mul));
|
||||||
|
env_set(env, "/", make_builtin(builtin_div));
|
||||||
|
env_set(env, "=", make_builtin(builtin_eq));
|
||||||
|
env_set(env, "list", make_builtin(builtin_list));
|
||||||
|
env_set(env, "car", make_builtin(builtin_car));
|
||||||
|
env_set(env, "cdr", make_builtin(builtin_cdr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main interpreter functions
|
||||||
|
Value *lisp_eval(const char *input) {
|
||||||
|
Lexer lex = {input, 0, strlen(input)};
|
||||||
|
Value *expr = parse_expr(&lex);
|
||||||
|
return eval_expr(expr, global_env);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lisp_init(void) {
|
||||||
|
global_env = make_env(NULL);
|
||||||
|
init_builtins(global_env);
|
||||||
|
}
|
||||||
|
|
||||||
|
// REPL and file loading
|
||||||
|
void repl(void) {
|
||||||
|
char input[1024];
|
||||||
|
printf("Lisp Config Interpreter\n");
|
||||||
|
printf("Type 'quit' to exit\n\n");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
printf("> ");
|
||||||
|
if (!fgets(input, sizeof(input), stdin))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Remove newline
|
||||||
|
input[strcspn(input, "\n")] = 0;
|
||||||
|
|
||||||
|
if (strcmp(input, "quit") == 0)
|
||||||
|
break;
|
||||||
|
if (strlen(input) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Value *result = lisp_eval(input);
|
||||||
|
print_value(result);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_file(const char *filename) {
|
||||||
|
FILE *file = fopen(filename, "r");
|
||||||
|
if (!file) {
|
||||||
|
printf("Error: Cannot open file %s\n", filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "DEBUG : Reading file -> %s\n", filename);
|
||||||
|
|
||||||
|
fseek(file, 0, SEEK_END);
|
||||||
|
long length = ftell(file);
|
||||||
|
fseek(file, 0, SEEK_SET);
|
||||||
|
|
||||||
|
char *content = malloc(length + 1);
|
||||||
|
fread(content, 1, length, file);
|
||||||
|
content[length] = '\0';
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
Lexer lex = {content, 0, length};
|
||||||
|
|
||||||
|
while (lex.pos < lex.length) {
|
||||||
|
Value *expr = parse_expr(&lex);
|
||||||
|
if (expr->type != VAL_NIL) {
|
||||||
|
fprintf(stderr, "DEBUG : Expr parsed\n");
|
||||||
|
Value *result = eval_expr(expr, global_env);
|
||||||
|
// Optionally print results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(content);
|
||||||
|
}
|
||||||
+80
-146
@@ -1,152 +1,86 @@
|
|||||||
#include "../include/lexer.h"
|
#include "../include/lexer.h"
|
||||||
#include "../include/define.h"
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/ucontext.h>
|
|
||||||
|
|
||||||
token_t next_token(lexer_t *lexer) {
|
void skip_whitespace(Lexer *lex) {
|
||||||
token_t token = {0};
|
while (lex->pos < lex->length &&
|
||||||
token.line = lexer->line;
|
(isspace(lex->input[lex->pos]) || lex->input[lex->pos] == ';')) {
|
||||||
token.column = lexer->column;
|
if (lex->input[lex->pos] == ';') {
|
||||||
|
// Skip comment to end of line
|
||||||
skip_whitespace(lexer);
|
while (lex->pos < lex->length && lex->input[lex->pos] != '\n') {
|
||||||
skip_comment(lexer);
|
lex->pos++;
|
||||||
skip_whitespace(lexer);
|
|
||||||
|
|
||||||
if (lexer->current_char == '\0') {
|
|
||||||
token.type = TOKEN_EOF;
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Newline (statement separator)
|
|
||||||
if (lexer->current_char == '\n') {
|
|
||||||
token.type = TOKEN_NEWLINE;
|
|
||||||
advance_char(lexer);
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comma (function call indicator)
|
|
||||||
if (lexer->current_char == ',') {
|
|
||||||
token.type = TOKEN_COMMA;
|
|
||||||
token.value = (char *)malloc(2 * sizeof(char));
|
|
||||||
token.value[0] = ',';
|
|
||||||
token.value[1] = '\0';
|
|
||||||
advance_char(lexer);
|
|
||||||
fprintf(stderr, "DEBUG : next token = ,\n");
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Percent (function reference indicator)
|
|
||||||
if (lexer->current_char == '%') {
|
|
||||||
token.type = TOKEN_PERCENT;
|
|
||||||
token.value = (char *)malloc(2 * sizeof(char));
|
|
||||||
token.value[0] = '%';
|
|
||||||
token.value[1] = '\0';
|
|
||||||
advance_char(lexer);
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parentheses
|
|
||||||
if (lexer->current_char == '(') {
|
|
||||||
token.type = TOKEN_LPAREN;
|
|
||||||
token.value = (char *)malloc(2 * sizeof(char));
|
|
||||||
token.value[0] = '(';
|
|
||||||
token.value[1] = '\0';
|
|
||||||
advance_char(lexer);
|
|
||||||
fprintf(stderr, "DEBUG : next token = (\n");
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lexer->current_char == ')') {
|
|
||||||
token.type = TOKEN_RPAREN;
|
|
||||||
token.value = (char *)malloc(2 * sizeof(char));
|
|
||||||
token.value[0] = ')';
|
|
||||||
token.value[1] = '\0';
|
|
||||||
fprintf(stderr, "DEBUG : next token = )\n");
|
|
||||||
advance_char(lexer);
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strings
|
|
||||||
if (lexer->current_char == '"') {
|
|
||||||
token.type = TOKEN_STRING;
|
|
||||||
advance_char(lexer); // Skip opening quote
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
while (lexer->current_char != '"' && lexer->current_char != '\0') {
|
|
||||||
token.value = (char *)realloc(token.value, (i + 1) * sizeof(char));
|
|
||||||
if (lexer->current_char == '\\' && lexer->input[lexer->pos + 1] != '\0') {
|
|
||||||
advance_char(lexer);
|
|
||||||
switch (lexer->current_char) {
|
|
||||||
case 'n':
|
|
||||||
token.value[i++] = '\n';
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
token.value[i++] = '\t';
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
token.value[i++] = '\r';
|
|
||||||
break;
|
|
||||||
case '\\':
|
|
||||||
token.value[i++] = '\\';
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
token.value[i++] = '"';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
token.value[i++] = lexer->current_char;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
token.value[i++] = lexer->current_char;
|
|
||||||
}
|
|
||||||
advance_char(lexer);
|
|
||||||
}
|
|
||||||
token.value[i] = '\0';
|
|
||||||
|
|
||||||
if (lexer->current_char == '"') {
|
|
||||||
advance_char(lexer); // Skip closing quote
|
|
||||||
}
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Numbers, symbols, and booleans
|
|
||||||
if (isalnum(lexer->current_char) || lexer->current_char == '-' ||
|
|
||||||
lexer->current_char == '+' || lexer->current_char == '.' ||
|
|
||||||
lexer->current_char == '_') {
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
while (
|
|
||||||
(isalnum(lexer->current_char) || strchr("-+._", lexer->current_char))) {
|
|
||||||
token.value = (char *)realloc(token.value, (i + 1) * sizeof(char));
|
|
||||||
token.value[i++] = lexer->current_char;
|
|
||||||
advance_char(lexer);
|
|
||||||
}
|
|
||||||
token.value[i] = '\0';
|
|
||||||
|
|
||||||
// Check for booleans
|
|
||||||
if (strcmp(token.value, "true") == 0 || strcmp(token.value, "false") == 0) {
|
|
||||||
token.type = TOKEN_BOOLEAN;
|
|
||||||
} else {
|
|
||||||
// Check for numbers
|
|
||||||
char *endptr;
|
|
||||||
strtod(token.value, &endptr);
|
|
||||||
if (*endptr == '\0' && strlen(token.value) > 0 &&
|
|
||||||
(isdigit(token.value[0]) || token.value[0] == '-' ||
|
|
||||||
token.value[0] == '+')) {
|
|
||||||
token.type = TOKEN_NUMBER;
|
|
||||||
} else {
|
|
||||||
token.type = TOKEN_SYMBOL;
|
|
||||||
fprintf(stderr, "DEBUG : next token = %s\n", token.value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return token;
|
lex->pos++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Error case
|
|
||||||
token.type = TOKEN_ERROR;
|
Token peek_token(Lexer *lex) {
|
||||||
snprintf(token.value, MAX_TOKEN_LENGTH - 1, "Unexpected character '%c'",
|
int saved_pos = lex->pos;
|
||||||
lexer->current_char);
|
Token tok = next_token(lex);
|
||||||
return token;
|
lex->pos = saved_pos; // Restore position
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token next_token(Lexer *lex) {
|
||||||
|
Token tok = {0};
|
||||||
|
skip_whitespace(lex);
|
||||||
|
|
||||||
|
if (lex->pos >= lex->length) {
|
||||||
|
tok.type = TOK_EOF;
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
char c = lex->input[lex->pos];
|
||||||
|
|
||||||
|
if (c == '(') {
|
||||||
|
tok.type = TOK_LPAREN;
|
||||||
|
lex->pos++;
|
||||||
|
} else if (c == ')') {
|
||||||
|
tok.type = TOK_RPAREN;
|
||||||
|
lex->pos++;
|
||||||
|
} else if (c == '\'') {
|
||||||
|
tok.type = TOK_QUOTE;
|
||||||
|
lex->pos++;
|
||||||
|
} else if (c == '"') {
|
||||||
|
// String literal
|
||||||
|
tok.type = TOK_STRING;
|
||||||
|
lex->pos++; // Skip opening quote
|
||||||
|
int start = lex->pos;
|
||||||
|
while (lex->pos < lex->length && lex->input[lex->pos] != '"') {
|
||||||
|
lex->pos++;
|
||||||
|
}
|
||||||
|
int len = lex->pos - start;
|
||||||
|
tok.value = malloc(len + 1);
|
||||||
|
strncpy(tok.value, lex->input + start, len);
|
||||||
|
tok.value[len] = '\0';
|
||||||
|
lex->pos++; // Skip closing quote
|
||||||
|
} else if (isdigit(c) || (c == '-' && isdigit(lex->input[lex->pos + 1]))) {
|
||||||
|
// Number
|
||||||
|
tok.type = TOK_NUMBER;
|
||||||
|
int start = lex->pos;
|
||||||
|
if (c == '-')
|
||||||
|
lex->pos++;
|
||||||
|
while (lex->pos < lex->length &&
|
||||||
|
(isdigit(lex->input[lex->pos]) || lex->input[lex->pos] == '.')) {
|
||||||
|
lex->pos++;
|
||||||
|
}
|
||||||
|
int len = lex->pos - start;
|
||||||
|
tok.value = malloc(len + 1);
|
||||||
|
strncpy(tok.value, lex->input + start, len);
|
||||||
|
tok.value[len] = '\0';
|
||||||
|
} else {
|
||||||
|
// Symbol
|
||||||
|
tok.type = TOK_SYMBOL;
|
||||||
|
int start = lex->pos;
|
||||||
|
while (lex->pos < lex->length && !isspace(lex->input[lex->pos]) &&
|
||||||
|
lex->input[lex->pos] != '(' && lex->input[lex->pos] != ')' &&
|
||||||
|
lex->input[lex->pos] != ';') {
|
||||||
|
lex->pos++;
|
||||||
|
}
|
||||||
|
int len = lex->pos - start;
|
||||||
|
tok.value = malloc(len + 1);
|
||||||
|
strncpy(tok.value, lex->input + start, len);
|
||||||
|
tok.value[len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok;
|
||||||
}
|
}
|
||||||
|
|||||||
-223
@@ -1,223 +0,0 @@
|
|||||||
#include "../include/node.h"
|
|
||||||
#include "../include/config_tools.h"
|
|
||||||
#include "../include/define.h"
|
|
||||||
#include "../include/function.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/ucontext.h>
|
|
||||||
|
|
||||||
// Node creation functions
|
|
||||||
node_t *create_node(node_type_t type) {
|
|
||||||
node_t *node = malloc(sizeof(node_t));
|
|
||||||
if (!node)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
memset(node, 0, sizeof(node_t));
|
|
||||||
node->type = type;
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t *create_symbol_node(const char *symbol) {
|
|
||||||
node_t *node = create_node(NODE_SYMBOL);
|
|
||||||
if (node) {
|
|
||||||
node->data.symbol = (char *)malloc((MAX_SYMBOL_LENGTH - 1) * sizeof(char));
|
|
||||||
strncpy(node->data.symbol, symbol, MAX_SYMBOL_LENGTH - 1);
|
|
||||||
node->data.symbol[MAX_SYMBOL_LENGTH - 1] = '\0';
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t *create_string_node(const char *string) {
|
|
||||||
node_t *node = create_node(NODE_STRING);
|
|
||||||
if (node) {
|
|
||||||
node->data.string = (char *)malloc((MAX_STRING_LENGTH - 1) * sizeof(char));
|
|
||||||
strncpy(node->data.string, string, MAX_STRING_LENGTH - 1);
|
|
||||||
node->data.string[MAX_STRING_LENGTH - 1] = '\0';
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t *create_number_node(double number) {
|
|
||||||
node_t *node = create_node(NODE_NUMBER);
|
|
||||||
if (node) {
|
|
||||||
node->data.number = number;
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t *create_boolean_node(bool value) {
|
|
||||||
node_t *node = create_node(NODE_BOOLEAN);
|
|
||||||
if (node) {
|
|
||||||
node->data.boolean = value;
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t *create_function_ref_node(const char *function_name) {
|
|
||||||
node_t *node = create_node(NODE_FUNCTION_REF);
|
|
||||||
if (node) {
|
|
||||||
node->data.function_ref =
|
|
||||||
(char *)malloc((MAX_SYMBOL_LENGTH - 1) * sizeof(char));
|
|
||||||
strncpy(node->data.function_ref, function_name, MAX_SYMBOL_LENGTH - 1);
|
|
||||||
node->data.function_ref[MAX_SYMBOL_LENGTH - 1] = '\0';
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t *create_function_call_node(const char *function_name) {
|
|
||||||
node_t *node = create_node(NODE_FUNCTION_CALL);
|
|
||||||
if (node) {
|
|
||||||
node->data.call.function_name =
|
|
||||||
(char *)malloc((MAX_SYMBOL_LENGTH - 1) * sizeof(char));
|
|
||||||
strncpy(node->data.call.function_name, function_name,
|
|
||||||
MAX_SYMBOL_LENGTH - 1);
|
|
||||||
node->data.call.function_name[MAX_SYMBOL_LENGTH - 1] = '\0';
|
|
||||||
node->data.call.arg_count = 0;
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t *copy_node(node_t *src) {
|
|
||||||
if (!src)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
node_t *copy = malloc(sizeof(node_t));
|
|
||||||
if (!copy)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
copy->type = src->type;
|
|
||||||
|
|
||||||
switch (src->type) {
|
|
||||||
case NODE_STRING:
|
|
||||||
copy->data.string = strdup(src->data.string);
|
|
||||||
break;
|
|
||||||
case NODE_NUMBER:
|
|
||||||
copy->data.number = src->data.number;
|
|
||||||
break;
|
|
||||||
case NODE_BOOLEAN:
|
|
||||||
copy->data.boolean = src->data.boolean;
|
|
||||||
break;
|
|
||||||
case NODE_SYMBOL:
|
|
||||||
copy->data.symbol = strdup(src->data.symbol);
|
|
||||||
break;
|
|
||||||
case NODE_FUNCTION_REF:
|
|
||||||
copy->data.function_ref = strdup(src->data.function_ref);
|
|
||||||
break;
|
|
||||||
case NODE_LIST:
|
|
||||||
copy->data.list.child_count = src->data.list.child_count;
|
|
||||||
copy->data.list.children =
|
|
||||||
malloc(sizeof(node_t *) * src->data.list.child_count);
|
|
||||||
for (int i = 0; i < src->data.list.child_count; i++) {
|
|
||||||
copy->data.list.children[i] = copy_node(src->data.list.children[i]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NODE_FUNCTION_CALL:
|
|
||||||
copy->data.call.function_name = strdup(src->data.call.function_name);
|
|
||||||
copy->data.call.arg_count = src->data.call.arg_count;
|
|
||||||
copy->data.call.args = malloc(sizeof(node_t *) * src->data.call.arg_count);
|
|
||||||
for (int i = 0; i < src->data.call.arg_count; i++) {
|
|
||||||
copy->data.call.args[i] = copy_node(src->data.call.args[i]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
free(copy);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_node(node_t *node) {
|
|
||||||
int i;
|
|
||||||
if (!node)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (node->type == NODE_FUNCTION_CALL) {
|
|
||||||
for (i = 0; i < node->data.call.arg_count; i++) {
|
|
||||||
free_node(node->data.call.args[i]);
|
|
||||||
}
|
|
||||||
free(node->data.call.args);
|
|
||||||
} else if (node->type == NODE_LIST) {
|
|
||||||
for (i = 0; i < node->data.list.child_count; i++) {
|
|
||||||
free_node(node->data.list.children[i]);
|
|
||||||
}
|
|
||||||
free(node->data.list.children);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free string data if present
|
|
||||||
if (node->type == NODE_STRING && node->data.string) {
|
|
||||||
free(node->data.string);
|
|
||||||
} else if (node->type == NODE_SYMBOL && node->data.symbol) {
|
|
||||||
free(node->data.symbol);
|
|
||||||
} else if (node->type == NODE_FUNCTION_REF && node->data.function_ref) {
|
|
||||||
free(node->data.function_ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluate a node in the given context
|
|
||||||
node_t *evaluate_node(exec_context_t *ctx, node_t *node) {
|
|
||||||
fprintf(stderr, "DEBUG : evalute node type %d\n", node->type);
|
|
||||||
if (!node)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
switch (node->type) {
|
|
||||||
case NODE_STRING:
|
|
||||||
case NODE_NUMBER:
|
|
||||||
case NODE_BOOLEAN:
|
|
||||||
case NODE_FUNCTION_REF:
|
|
||||||
return copy_node(node);
|
|
||||||
|
|
||||||
case NODE_SYMBOL: {
|
|
||||||
// Look up variable
|
|
||||||
for (int i = 0; i < ctx->local_vars.count; i++) {
|
|
||||||
if (strcmp(ctx->local_vars.names[i], node->data.symbol) == 0) {
|
|
||||||
return copy_node(ctx->local_vars.values[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t *var = find_variable(node->data.symbol);
|
|
||||||
if (var) {
|
|
||||||
return copy_node(var);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Error: Undefined variable '%s'\n", node->data.symbol);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
case NODE_FUNCTION_CALL:
|
|
||||||
fprintf(stderr, "DEBUG: function call : %s\n",
|
|
||||||
node->data.call.function_name);
|
|
||||||
return execute_function_call_in_context(ctx, node);
|
|
||||||
|
|
||||||
case NODE_LIST: {
|
|
||||||
// Execute all expressions in list and return last result
|
|
||||||
node_t *result = NULL;
|
|
||||||
fprintf(stderr, "DEBUG : child count %d\n", node->data.list.child_count);
|
|
||||||
for (int i = 0; i < node->data.list.child_count; i++) {
|
|
||||||
fprintf(stderr, "DEBUG : child number %d\n", i);
|
|
||||||
node_t *child = node->data.list.children[i];
|
|
||||||
if (result) {
|
|
||||||
free_node(result);
|
|
||||||
}
|
|
||||||
// Evaluate child and handle function calls
|
|
||||||
if (child->type == NODE_FUNCTION_CALL) {
|
|
||||||
result = execute_function_call_in_context(ctx, child);
|
|
||||||
} else {
|
|
||||||
result = evaluate_node(ctx, child);
|
|
||||||
}
|
|
||||||
if (!result) {
|
|
||||||
fprintf(stderr, "Error evaluating list element %d\n", i);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "Error: Cannot evaluate node type %d\n", node->type);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+28
-232
@@ -1,245 +1,41 @@
|
|||||||
#include "../include/parser.h"
|
#include "../include/parser.h"
|
||||||
#include "../include/lexer.h"
|
#include "../include/lexer.h"
|
||||||
#include "../include/node.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
node_t *parse_atom(lexer_t *lexer, token_t *token) {
|
Value *parse_list(Lexer *lex) {
|
||||||
// Variable
|
|
||||||
node_t *list = NULL;
|
|
||||||
size_t saved_pos = 0;
|
|
||||||
int saved_line = 0;
|
|
||||||
int saved_column = 0;
|
|
||||||
char saved_char = '\000';
|
|
||||||
|
|
||||||
token_t peek;
|
fprintf(stderr, "DEBUG : parsing list\n");
|
||||||
token_t func_name;
|
Token tok = peek_token(lex);
|
||||||
token_t next;
|
if (tok.type == TOK_RPAREN) {
|
||||||
token_t func_token;
|
|
||||||
|
|
||||||
node_t *call = NULL;
|
|
||||||
node_t *arg = NULL;
|
|
||||||
node_t *element = NULL;
|
|
||||||
|
|
||||||
// Code
|
|
||||||
switch (token->type) {
|
|
||||||
case TOKEN_SYMBOL:
|
|
||||||
return create_symbol_node(token->value);
|
|
||||||
|
|
||||||
case TOKEN_STRING:
|
|
||||||
return create_string_node(token->value);
|
|
||||||
|
|
||||||
case TOKEN_NUMBER:
|
|
||||||
return create_number_node(atof(token->value));
|
|
||||||
|
|
||||||
case TOKEN_BOOLEAN:
|
|
||||||
return create_boolean_node(strcmp(token->value, "true") == 0);
|
|
||||||
|
|
||||||
case TOKEN_LPAREN: {
|
|
||||||
fprintf(stderr, "DEBUG : list parsing\n");
|
|
||||||
list = create_list_node();
|
|
||||||
|
|
||||||
// Look ahead to see if this is a function call or just a list
|
|
||||||
saved_pos = lexer->pos;
|
|
||||||
saved_line = lexer->line;
|
|
||||||
saved_column = lexer->column;
|
|
||||||
saved_char = lexer->current_char;
|
|
||||||
|
|
||||||
peek = next_token(lexer);
|
|
||||||
|
|
||||||
// If first token is a comma, this is a function call
|
|
||||||
if (peek.type == TOKEN_COMMA) {
|
|
||||||
func_name = next_token(lexer);
|
|
||||||
|
|
||||||
if (func_name.type == TOKEN_SYMBOL) {
|
|
||||||
call = create_function_call_node(func_name.value);
|
|
||||||
|
|
||||||
// Parse arguments until closing paren
|
|
||||||
next = next_token(lexer);
|
|
||||||
while (next.type != TOKEN_RPAREN && next.type != TOKEN_EOF) {
|
|
||||||
arg = NULL;
|
|
||||||
|
|
||||||
if (next.type == TOKEN_PERCENT) {
|
|
||||||
// Function reference argument
|
|
||||||
func_token = next_token(lexer);
|
|
||||||
if (func_token.type == TOKEN_SYMBOL) {
|
|
||||||
arg = create_function_ref_node(func_token.value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Parse the argument expression recursively
|
|
||||||
arg = parse_expression(lexer, &next);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg) {
|
|
||||||
add_arg_to_call(call, arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
next = next_token(lexer);
|
|
||||||
|
|
||||||
// Skip commas between arguments
|
|
||||||
while (next.type == TOKEN_COMMA) {
|
|
||||||
next = next_token(lexer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return call;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, parse as a regular list
|
|
||||||
// Restore lexer state
|
|
||||||
lexer->pos = saved_pos;
|
|
||||||
lexer->line = saved_line;
|
|
||||||
lexer->column = saved_column;
|
|
||||||
lexer->current_char = saved_char;
|
|
||||||
|
|
||||||
// Parse elements in the list
|
|
||||||
while (1) {
|
|
||||||
// Skip whitespace and comments
|
|
||||||
skip_whitespace(lexer);
|
|
||||||
skip_comment(lexer);
|
|
||||||
skip_whitespace(lexer);
|
|
||||||
|
|
||||||
// Check if we're at the end of the list
|
|
||||||
if (lexer->current_char == ')') {
|
|
||||||
// Consume the closing paren
|
// Consume the closing paren
|
||||||
lexer->pos++;
|
next_token(lex);
|
||||||
lexer->column++;
|
return make_nil();
|
||||||
if (lexer->pos < strlen(lexer->input)) {
|
|
||||||
lexer->current_char = lexer->input[lexer->pos];
|
|
||||||
} else {
|
|
||||||
lexer->current_char = '\0';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lexer->current_char == '\0') {
|
Value *car = parse_expr(lex);
|
||||||
break;
|
fprintf(stderr, "DEBUG : head %s\n", car->data.symbol);
|
||||||
}
|
Value *cdr = parse_list(lex);
|
||||||
|
return make_list(car, cdr);
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the next element
|
Value *parse_expr(Lexer *lex) {
|
||||||
element = parse_statement(lexer);
|
fprintf(stderr, "DEBUG : Parsing new expression\n");
|
||||||
if (element) {
|
Token tok = next_token(lex);
|
||||||
add_to_list(list, element);
|
|
||||||
} else {
|
|
||||||
// If we couldn't parse anything, advance one character to avoid
|
|
||||||
// infinite loop
|
|
||||||
lexer->pos++;
|
|
||||||
lexer->column++;
|
|
||||||
if (lexer->pos < strlen(lexer->input)) {
|
|
||||||
lexer->current_char = lexer->input[lexer->pos];
|
|
||||||
} else {
|
|
||||||
lexer->current_char = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
case TOKEN_PERCENT: {
|
|
||||||
// Parse function reference: %function-name
|
|
||||||
next = next_token(lexer);
|
|
||||||
if (next.type == TOKEN_SYMBOL) {
|
|
||||||
return create_function_ref_node(next.value);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
case TOKEN_COMMA: {
|
|
||||||
// Function call at top level: ,function-name
|
|
||||||
func_token = next_token(lexer);
|
|
||||||
if (func_token.type == TOKEN_SYMBOL) {
|
|
||||||
return create_function_call_node(func_token.value);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
switch (tok.type) {
|
||||||
|
case TOK_NUMBER:
|
||||||
|
fprintf(stderr, "DEBUG : new number\n");
|
||||||
|
return make_number(atof(tok.value));
|
||||||
|
case TOK_STRING:
|
||||||
|
return make_string(tok.value);
|
||||||
|
case TOK_SYMBOL:
|
||||||
|
return make_symbol(tok.value);
|
||||||
|
case TOK_QUOTE:
|
||||||
|
return make_list(make_symbol("quote"),
|
||||||
|
make_list(parse_expr(lex), make_nil()));
|
||||||
|
case TOK_LPAREN:
|
||||||
|
return parse_list(lex);
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return make_nil();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node_t *parse_expression(lexer_t *lexer, token_t *token) {
|
|
||||||
if (!token) {
|
|
||||||
token_t current = next_token(lexer);
|
|
||||||
return parse_expression(lexer, ¤t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this is a function call
|
|
||||||
if (token->type == TOKEN_COMMA) {
|
|
||||||
token_t func_token = next_token(lexer);
|
|
||||||
if (func_token.type == TOKEN_SYMBOL) {
|
|
||||||
return parse_function_call(lexer, func_token.value);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise parse as atom
|
|
||||||
return parse_atom(lexer, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t *parse_function_call(lexer_t *lexer, const char *function_name) {
|
|
||||||
node_t *call = create_function_call_node(function_name);
|
|
||||||
if (!call)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// Look for opening parenthesis (optional)
|
|
||||||
size_t saved_pos = lexer->pos;
|
|
||||||
int saved_line = lexer->line;
|
|
||||||
int saved_column = lexer->column;
|
|
||||||
char saved_char = lexer->current_char;
|
|
||||||
|
|
||||||
token_t token = next_token(lexer);
|
|
||||||
|
|
||||||
if (token.type != TOKEN_LPAREN) {
|
|
||||||
// No parentheses, restore position and return call with no args
|
|
||||||
lexer->pos = saved_pos;
|
|
||||||
lexer->line = saved_line;
|
|
||||||
lexer->column = saved_column;
|
|
||||||
lexer->current_char = saved_char;
|
|
||||||
return call;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse arguments
|
|
||||||
token = next_token(lexer);
|
|
||||||
while (token.type != TOKEN_RPAREN && token.type != TOKEN_EOF) {
|
|
||||||
node_t *arg = NULL;
|
|
||||||
|
|
||||||
if (token.type == TOKEN_PERCENT) {
|
|
||||||
// Function reference argument
|
|
||||||
token_t func_token = next_token(lexer);
|
|
||||||
if (func_token.type == TOKEN_SYMBOL) {
|
|
||||||
arg = create_function_ref_node(func_token.value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Parse argument expression
|
|
||||||
arg = parse_expression(lexer, &token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg) {
|
|
||||||
add_arg_to_call(call, arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get next token
|
|
||||||
token = next_token(lexer);
|
|
||||||
|
|
||||||
// Skip commas between arguments
|
|
||||||
while (token.type == TOKEN_COMMA) {
|
|
||||||
token = next_token(lexer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return call;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t *parse_statement(lexer_t *lexer) {
|
|
||||||
skip_whitespace(lexer);
|
|
||||||
skip_comment(lexer);
|
|
||||||
skip_whitespace(lexer);
|
|
||||||
|
|
||||||
token_t token = next_token(lexer);
|
|
||||||
|
|
||||||
return parse_expression(lexer, &token);
|
|
||||||
}
|
|
||||||
|
|||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
#include "../include/utils.h"
|
||||||
|
|
||||||
|
bool is_nil(Value *v) { return v->type == VAL_NIL; }
|
||||||
|
|
||||||
|
bool is_symbol(Value *v, const char *sym) {
|
||||||
|
return v->type == VAL_SYMBOL && strcmp(v->data.symbol, sym) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value *car(Value *v) {
|
||||||
|
if (v->type == VAL_LIST)
|
||||||
|
return v->data.list.car;
|
||||||
|
return make_nil();
|
||||||
|
}
|
||||||
|
|
||||||
|
Value *cdr(Value *v) {
|
||||||
|
if (v->type == VAL_LIST)
|
||||||
|
return v->data.list.cdr;
|
||||||
|
return make_nil();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_length(Value *v)
|
||||||
|
*
|
||||||
|
* @param v
|
||||||
|
*
|
||||||
|
* @return Length of the list of the argument value
|
||||||
|
*/
|
||||||
|
int list_length(Value *v) {
|
||||||
|
int len = 0;
|
||||||
|
while (v->type == VAL_LIST) {
|
||||||
|
len++;
|
||||||
|
v = v->data.list.cdr;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_value(Value *v) {
|
||||||
|
switch (v->type) {
|
||||||
|
case VAL_NIL:
|
||||||
|
printf("nil");
|
||||||
|
break;
|
||||||
|
case VAL_NUMBER:
|
||||||
|
if (v->data.number == (int)v->data.number) {
|
||||||
|
printf("%d", (int)v->data.number);
|
||||||
|
} else {
|
||||||
|
printf("%.2f", v->data.number);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case VAL_STRING:
|
||||||
|
printf("\"%s\"", v->data.string);
|
||||||
|
break;
|
||||||
|
case VAL_SYMBOL:
|
||||||
|
printf("%s", v->data.symbol);
|
||||||
|
break;
|
||||||
|
case VAL_LIST:
|
||||||
|
printf("(");
|
||||||
|
Value *curr = v;
|
||||||
|
bool first = true;
|
||||||
|
while (curr->type == VAL_LIST) {
|
||||||
|
if (!first)
|
||||||
|
printf(" ");
|
||||||
|
print_value(curr->data.list.car);
|
||||||
|
curr = curr->data.list.cdr;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
if (curr->type != VAL_NIL) {
|
||||||
|
printf(" . ");
|
||||||
|
print_value(curr);
|
||||||
|
}
|
||||||
|
printf(")");
|
||||||
|
break;
|
||||||
|
case VAL_FUNCTION:
|
||||||
|
printf("<function>");
|
||||||
|
break;
|
||||||
|
case VAL_BUILTIN:
|
||||||
|
printf("<builtin>");
|
||||||
|
break;
|
||||||
|
case VAL_KEYMAP:
|
||||||
|
printf("<keymap:%d>", v->data.keymap.count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user