first commit
This commit is contained in:
@@ -0,0 +1,34 @@
|
|||||||
|
#ifndef CONFIG_TOOLS_H_
|
||||||
|
#define CONFIG_TOOLS_H_
|
||||||
|
|
||||||
|
#include "data.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
config_t *config_create(void);
|
||||||
|
void free_node(node_t *node);
|
||||||
|
void config_destroy(config_t *config);
|
||||||
|
int handle_map_key(config_t *config, node_t **args, int arg_count);
|
||||||
|
int handle_define(config_t *config, node_t **args, int arg_count);
|
||||||
|
int execute_function_call(config_t *config, node_t *call);
|
||||||
|
int config_parse_string(config_t *config, const char *input);
|
||||||
|
int config_parse_file(config_t *config, const char *filename);
|
||||||
|
|
||||||
|
const char *config_get_key_mapping(config_t *config, const char *key_combo);
|
||||||
|
node_t *find_variable(config_t *config, const char *name);
|
||||||
|
|
||||||
|
const char *config_get_string(config_t *config, const char *path,
|
||||||
|
const char *default_value);
|
||||||
|
int config_get_int(config_t *config, const char *path, int default_value);
|
||||||
|
double config_get_double(config_t *config, const char *path,
|
||||||
|
double default_value);
|
||||||
|
bool config_get_bool(config_t *config, const char *path, bool default_value);
|
||||||
|
void config_print_all(config_t *config);
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
const char *name;
|
||||||
|
int (*handler)(config_t *config, node_t **args, int arg_count);
|
||||||
|
} builtin_functions[] = {
|
||||||
|
{"map-key", handle_map_key}, {"define", handle_define}, {NULL, NULL}};
|
||||||
|
|
||||||
|
#endif
|
||||||
+100
@@ -0,0 +1,100 @@
|
|||||||
|
#ifndef DATA_H_PARSER
|
||||||
|
#define DATA_H_PARSER
|
||||||
|
|
||||||
|
#include "define.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// Token types for lexical analysis
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TOKEN_COMMA, // ,
|
||||||
|
TOKEN_PERCENT, // %
|
||||||
|
TOKEN_LPAREN, // (
|
||||||
|
TOKEN_RPAREN, // )
|
||||||
|
TOKEN_SYMBOL, // identifiers/function names
|
||||||
|
TOKEN_STRING, // "quoted strings"
|
||||||
|
TOKEN_NUMBER, // integers and floats
|
||||||
|
TOKEN_BOOLEAN, // true false
|
||||||
|
TOKEN_NEWLINE, // \n (statement separator)
|
||||||
|
TOKEN_EOF,
|
||||||
|
TOKEN_ERROR
|
||||||
|
} token_type_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
token_type_t type;
|
||||||
|
char value[MAX_TOKEN_LENGTH];
|
||||||
|
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_type_t;
|
||||||
|
|
||||||
|
typedef struct node {
|
||||||
|
node_type_t type;
|
||||||
|
union {
|
||||||
|
char symbol[MAX_SYMBOL_LENGTH];
|
||||||
|
char string[MAX_STRING_LENGTH];
|
||||||
|
double number;
|
||||||
|
bool boolean;
|
||||||
|
char function_ref[MAX_SYMBOL_LENGTH];
|
||||||
|
struct {
|
||||||
|
char function_name[MAX_SYMBOL_LENGTH];
|
||||||
|
struct node *args[MAX_ARGS];
|
||||||
|
int arg_count;
|
||||||
|
} call;
|
||||||
|
} data;
|
||||||
|
} node_t;
|
||||||
|
|
||||||
|
// Lexer state
|
||||||
|
typedef struct {
|
||||||
|
const char *input;
|
||||||
|
int pos;
|
||||||
|
int line;
|
||||||
|
int column;
|
||||||
|
char current_char;
|
||||||
|
} lexer_t;
|
||||||
|
|
||||||
|
// Configuration storage
|
||||||
|
typedef struct key_mapping {
|
||||||
|
char *key_combo;
|
||||||
|
char *function_name;
|
||||||
|
struct key_mapping *next;
|
||||||
|
} key_mapping_t;
|
||||||
|
|
||||||
|
typedef struct config_var {
|
||||||
|
char *name;
|
||||||
|
node_t *value;
|
||||||
|
struct config_var *next;
|
||||||
|
} config_var_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
key_mapping_t *key_mappings;
|
||||||
|
config_var_t *variables;
|
||||||
|
} config_t;
|
||||||
|
|
||||||
|
void advance_char(lexer_t *lexer);
|
||||||
|
void skip_whitespace(lexer_t *lexer);
|
||||||
|
void skip_comment(lexer_t *lexer);
|
||||||
|
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_function_ref_node(const char *function_name);
|
||||||
|
node_t *create_function_call_node(const char *function_name);
|
||||||
|
|
||||||
|
void add_arg_to_call(node_t *call, node_t *args);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#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
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef LEXER_H_
|
||||||
|
#define LEXER_H_
|
||||||
|
|
||||||
|
#include "data.h"
|
||||||
|
|
||||||
|
token_t next_token(lexer_t *lexer);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef PARSER_H_
|
||||||
|
#define PARSER_H_
|
||||||
|
|
||||||
|
#include "data.h"
|
||||||
|
#include "lexer.h"
|
||||||
|
|
||||||
|
node_t *parse_atom(lexer_t *lexer, token_t *token);
|
||||||
|
node_t *parse_function_call(lexer_t *lexer, const char *function_name);
|
||||||
|
node_t *parse_statement(lexer_t *lexer);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// 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)
|
||||||
|
|
||||||
|
,define(theme "dark")
|
||||||
|
,define(auto-save true)
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
#include "src/data.h"
|
||||||
|
#include "src/config_tools.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Parse the configuration
|
||||||
|
config_t *config = config_create();
|
||||||
|
char *config_file = "init.bl";
|
||||||
|
if (!config) {
|
||||||
|
fprintf(stderr, "Error: Failed to create config structure\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_parse_file(config, config_file) != 0) {
|
||||||
|
fprintf(stderr, "Error: Failed to parse config file\n");
|
||||||
|
config_destroy(config);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Successfully parsed Lisp config file: %s\n\n", config_file);
|
||||||
|
|
||||||
|
// Print all entries
|
||||||
|
config_print_all(config);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// Demonstrate usage
|
||||||
|
printf("Example usage:\n");
|
||||||
|
printf("CTRL-s maps to: %s\n", config_get_key_mapping(config, "CTRL-s"));
|
||||||
|
printf("CTRL-f o maps to: %s\n", config_get_key_mapping(config, "CTRL-f o"));
|
||||||
|
printf("Window width: %d\n", config_get_int(config, "window-width", 1024));
|
||||||
|
printf("Theme: %s\n", config_get_string(config, "theme", "light"));
|
||||||
|
printf("Auto-save: %s\n",
|
||||||
|
config_get_bool(config, "auto-save", false) ? "enabled" : "disabled");
|
||||||
|
printf("Font size: %d\n", config_get_int(config, "font-size", 12));
|
||||||
|
|
||||||
|
config_destroy(config);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,282 @@
|
|||||||
|
#include "../include/config_tools.h"
|
||||||
|
#include "../include/data.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
config_t *config_create(void) {
|
||||||
|
/*
|
||||||
|
* @brief Initialize config structure
|
||||||
|
*/
|
||||||
|
config_t *config = malloc(sizeof(config_t));
|
||||||
|
if (config) {
|
||||||
|
config->key_mappings = NULL;
|
||||||
|
config->variables = NULL;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_destroy(config_t *config) {
|
||||||
|
if (!config)
|
||||||
|
return;
|
||||||
|
|
||||||
|
key_mapping_t *mapping = config->key_mappings;
|
||||||
|
while (mapping) {
|
||||||
|
key_mapping_t *next = mapping->next;
|
||||||
|
free(mapping->key_combo);
|
||||||
|
free(mapping->function_name);
|
||||||
|
free(mapping);
|
||||||
|
mapping = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free variables
|
||||||
|
config_var_t *var = config->variables;
|
||||||
|
while (var) {
|
||||||
|
config_var_t *next = var->next;
|
||||||
|
free(var->name);
|
||||||
|
free_node(var->value);
|
||||||
|
free(var);
|
||||||
|
var = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Built-in function handlers
|
||||||
|
int handle_map_key(config_t *config, 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(config_t *config, node_t **args, int arg_count) {
|
||||||
|
if (arg_count != 2 || args[0]->type != NODE_SYMBOL) {
|
||||||
|
fprintf(stderr, "Error: define requires (variable_name, value)\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
config_var_t *var = malloc(sizeof(config_var_t));
|
||||||
|
if (!var)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
var->name = strdup(args[0]->data.symbol);
|
||||||
|
var->value = args[1]; // Transfer ownership
|
||||||
|
var->next = config->variables;
|
||||||
|
config->variables = var;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int execute_function_call(config_t *config, node_t *call) {
|
||||||
|
if (call->type != NODE_FUNCTION_CALL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// Look for built-in function
|
||||||
|
for (int i = 0; builtin_functions[i].name; i++) {
|
||||||
|
if (strcmp(call->data.call.function_name, builtin_functions[i].name) == 0) {
|
||||||
|
return builtin_functions[i].handler(config, call->data.call.args,
|
||||||
|
call->data.call.arg_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Error: Unknown function '%s'\n",
|
||||||
|
call->data.call.function_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_parse_string(config_t *config, 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') {
|
||||||
|
printf("%c", lexer.current_char);
|
||||||
|
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(config, 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) {
|
||||||
|
free_node(stmt); // Don't free if ownership was transferred to define
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip to next line
|
||||||
|
while (lexer.current_char != '\n' && lexer.current_char != '\0') {
|
||||||
|
advance_char(&lexer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_parse_file(config_t *config, 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(config, content);
|
||||||
|
free(content);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter functions
|
||||||
|
|
||||||
|
const char *config_get_key_mapping(config_t *config, 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(config_t *config, 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(config_t *config, const char *path,
|
||||||
|
const char *default_value) {
|
||||||
|
node_t *node = find_variable(config, path);
|
||||||
|
if (node && node->type == NODE_STRING) {
|
||||||
|
return node->data.string;
|
||||||
|
}
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_get_int(config_t *config, const char *path, int default_value) {
|
||||||
|
node_t *node = find_variable(config, path);
|
||||||
|
if (node && node->type == NODE_NUMBER) {
|
||||||
|
return (int)node->data.number;
|
||||||
|
}
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
double config_get_double(config_t *config, const char *path,
|
||||||
|
double default_value) {
|
||||||
|
node_t *node = find_variable(config, path);
|
||||||
|
if (node && node->type == NODE_NUMBER) {
|
||||||
|
return node->data.number;
|
||||||
|
}
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool config_get_bool(config_t *config, const char *path, bool default_value) {
|
||||||
|
node_t *node = find_variable(config, path);
|
||||||
|
if (node && node->type == NODE_BOOLEAN) {
|
||||||
|
return node->data.boolean;
|
||||||
|
}
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_print_all(config_t *config) {
|
||||||
|
printf("Key Mappings:\n");
|
||||||
|
printf("%-20s -> %s\n", "Key Combination", "Function");
|
||||||
|
printf("%-20s %s\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");
|
||||||
|
printf("%-20s %s\n", "----", "-----");
|
||||||
|
|
||||||
|
config_var_t *var = config->variables;
|
||||||
|
while (var) {
|
||||||
|
printf("%-20s = ", 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+100
@@ -0,0 +1,100 @@
|
|||||||
|
#include "../include/data.h"
|
||||||
|
#include "../include/define.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void advance_char(lexer_t *lexer) {
|
||||||
|
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) {
|
||||||
|
while (isspace(lexer->current_char) && lexer->current_char != '\n') {
|
||||||
|
advance_char(lexer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void skip_comment(lexer_t *lexer) {
|
||||||
|
if (lexer->current_char == '/' && lexer->input[lexer->pos + 1] == '/') {
|
||||||
|
while (lexer->current_char != '\n' && lexer->current_char != '\0') {
|
||||||
|
advance_char(lexer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_arg_to_call(node_t *call, node_t *arg) {
|
||||||
|
if (call->type == NODE_FUNCTION_CALL &&
|
||||||
|
call->data.call.arg_count < MAX_ARGS) {
|
||||||
|
call->data.call.args[call->data.call.arg_count++] = arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
+140
@@ -0,0 +1,140 @@
|
|||||||
|
#include "../include/lexer.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
token_t next_token(lexer_t *lexer) {
|
||||||
|
token_t token = {0};
|
||||||
|
token.line = lexer->line;
|
||||||
|
token.column = lexer->column;
|
||||||
|
|
||||||
|
skip_whitespace(lexer);
|
||||||
|
skip_comment(lexer);
|
||||||
|
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[0] = ',';
|
||||||
|
token.value[1] = '\0';
|
||||||
|
advance_char(lexer);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Percent (function reference indicator)
|
||||||
|
if (lexer->current_char == '%') {
|
||||||
|
token.type = TOKEN_PERCENT;
|
||||||
|
token.value[0] = '%';
|
||||||
|
token.value[1] = '\0';
|
||||||
|
advance_char(lexer);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parentheses
|
||||||
|
if (lexer->current_char == '(') {
|
||||||
|
token.type = TOKEN_LPAREN;
|
||||||
|
token.value[0] = '(';
|
||||||
|
token.value[1] = '\0';
|
||||||
|
advance_char(lexer);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lexer->current_char == ')') {
|
||||||
|
token.type = TOKEN_RPAREN;
|
||||||
|
token.value[0] = ')';
|
||||||
|
token.value[1] = '\0';
|
||||||
|
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' &&
|
||||||
|
i < MAX_TOKEN_LENGTH - 1) {
|
||||||
|
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)) &&
|
||||||
|
i < MAX_TOKEN_LENGTH - 1) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error case
|
||||||
|
token.type = TOKEN_ERROR;
|
||||||
|
snprintf(token.value, MAX_TOKEN_LENGTH, "Unexpected character '%c'",
|
||||||
|
lexer->current_char);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
#include "../include/parser.h"
|
||||||
|
|
||||||
|
node_t *parse_atom(lexer_t *lexer, token_t *token) {
|
||||||
|
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_PERCENT: {
|
||||||
|
// Parse function reference: %function-name
|
||||||
|
token_t next = next_token(lexer);
|
||||||
|
if (next.type == TOKEN_SYMBOL) {
|
||||||
|
return create_function_ref_node(next.value);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
token_t token = next_token(lexer);
|
||||||
|
if (token.type != TOKEN_LPAREN) {
|
||||||
|
// Function call without parentheses - no arguments
|
||||||
|
printf("no paren\n");
|
||||||
|
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 {
|
||||||
|
// Regular argument
|
||||||
|
arg = parse_atom(lexer, &token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg) {
|
||||||
|
add_arg_to_call(call, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (token.type == TOKEN_COMMA) {
|
||||||
|
// Function call: ,function-name(args)
|
||||||
|
token_t func_token = next_token(lexer);
|
||||||
|
if (func_token.type == TOKEN_SYMBOL) {
|
||||||
|
return parse_function_call(lexer, func_token.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user