├── examples └── hello_world.jc ├── include ├── compiler.h ├── lexer.h ├── parser.h └── token.h ├── src ├── compiler.c ├── main.c ├── lexer.c └── parser.c ├── .gitignore ├── Makefile └── README /examples/hello_world.jc: -------------------------------------------------------------------------------- 1 | santa main 2 | presents counter 3 | sleigh 5 4 | star "Ho Ho Ho!" 5 | bell 6 | rudolph 7 | -------------------------------------------------------------------------------- /include/compiler.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPILER_H 2 | #define COMPILER_H 3 | 4 | int compile_output(const char* c_file, const char* exe_file); 5 | 6 | #endif -------------------------------------------------------------------------------- /include/lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef LEXER_H 2 | #define LEXER_H 3 | 4 | #include "token.h" 5 | 6 | typedef struct { 7 | char* source; 8 | int position; 9 | int length; 10 | } Lexer; 11 | 12 | Lexer* lexer_create(const char* source); 13 | void lexer_destroy(Lexer* lexer); 14 | Token lexer_next_token(Lexer* lexer); 15 | void lexer_skip_whitespace(Lexer* lexer); 16 | 17 | #endif -------------------------------------------------------------------------------- /include/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef PARSER_H 2 | #define PARSER_H 3 | 4 | #include 5 | #include "token.h" 6 | #include "lexer.h" 7 | 8 | #define MAX_VARIABLES 100 9 | 10 | typedef struct { 11 | FILE* output_file; 12 | Token current_token; 13 | int indent_level; 14 | char variables[MAX_VARIABLES][MAX_TOKEN_LEN]; 15 | int variable_count; 16 | } Parser; 17 | 18 | Parser* parser_create(FILE* output_file); 19 | void parser_destroy(Parser* parser); 20 | void parser_generate_code(Parser* parser, Lexer* lexer); 21 | 22 | #endif -------------------------------------------------------------------------------- /src/compiler.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "compiler.h" 5 | 6 | #define MAX_CMD_LEN 1024 7 | 8 | int compile_output(const char* c_file, const char* exe_file) { 9 | char compile_cmd[MAX_CMD_LEN]; 10 | 11 | snprintf(compile_cmd, MAX_CMD_LEN, "gcc -Wall -Wextra -o %s %s", exe_file, c_file); 12 | 13 | int result = system(compile_cmd); 14 | if (result != 0) { 15 | fprintf(stderr, "Compilation failed with error code: %d\n", result); 16 | return 0; 17 | } 18 | 19 | return 1; 20 | } -------------------------------------------------------------------------------- /include/token.h: -------------------------------------------------------------------------------- 1 | #ifndef TOKEN_H 2 | #define TOKEN_H 3 | 4 | #define MAX_TOKEN_LEN 256 5 | 6 | typedef enum { 7 | SANTA, // Function definition 8 | RUDOLPH, // Return 9 | PRESENTS, // Variables 10 | SLEIGH, // Loop 11 | REINDEER, // If condition 12 | MISTLETOE, // Else 13 | STAR, // Print 14 | HOLLY, // Number literal 15 | TINSEL, // String literal 16 | BELL, // End block 17 | WREATH, // Assignment 18 | QUOTE, // String delimiter 19 | EOF_TOKEN 20 | } TokenType; 21 | 22 | typedef struct { 23 | TokenType type; 24 | char value[MAX_TOKEN_LEN]; 25 | } Token; 26 | 27 | #endif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build directories (from your Makefile) 2 | obj/ 3 | bin/ 4 | 5 | # Prerequisites 6 | *.d 7 | 8 | # Object files 9 | *.o 10 | *.ko 11 | *.obj 12 | *.elf 13 | 14 | # Linker output 15 | *.ilk 16 | *.map 17 | *.exp 18 | 19 | # Precompiled Headers 20 | *.gch 21 | *.pch 22 | 23 | # Libraries 24 | *.lib 25 | *.a 26 | *.la 27 | *.lo 28 | 29 | # Shared objects (inc. Windows DLLs) 30 | *.dll 31 | *.so 32 | *.so.* 33 | *.dylib 34 | 35 | # Executables 36 | *.exe 37 | *.out 38 | *.app 39 | *.i*86 40 | *.x86_64 41 | *.hex 42 | 43 | # Debug files 44 | *.dSYM/ 45 | *.su 46 | *.idb 47 | *.pdb 48 | 49 | # Kernel Module Compile Results 50 | *.mod* 51 | *.cmd 52 | .tmp_versions/ 53 | modules.order 54 | Module.symvers 55 | Mkfile.old 56 | dkms.conf 57 | 58 | # IDE specific files 59 | .vscode/ 60 | .idea/ 61 | *.swp 62 | *.swo 63 | 64 | # JollyC 65 | jollyc 66 | 67 | # OS specific 68 | .DS_Store 69 | .DS_Store? 70 | ._* 71 | .Spotlight-V100 72 | .Trashes 73 | ehthumbs.db 74 | Thumbs.db -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall -Wextra -I./include 3 | SRCDIR = src 4 | OBJDIR = obj 5 | BINDIR = bin 6 | EXAMPLES = examples 7 | 8 | SOURCES = $(wildcard $(SRCDIR)/*.c) 9 | OBJECTS = $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o) 10 | TARGET = $(BINDIR)/jollyc 11 | 12 | .PHONY: all clean run-examples 13 | 14 | all: $(TARGET) 15 | 16 | $(TARGET): $(OBJECTS) 17 | @mkdir -p $(BINDIR) 18 | $(CC) $(OBJECTS) -o $(TARGET) 19 | 20 | $(OBJDIR)/%.o: $(SRCDIR)/%.c 21 | @mkdir -p $(OBJDIR) 22 | $(CC) $(CFLAGS) -c $< -o $@ 23 | 24 | # Run all examples in the examples directory 25 | run-examples: $(TARGET) 26 | @echo "Running examples..." 27 | @for example in $(EXAMPLES)/*.jc; do \ 28 | if [ -f "$$example" ]; then \ 29 | echo "\nCompiling and running $$example:"; \ 30 | filename=$$(basename "$$example" .jc); \ 31 | $(TARGET) "$$example" "$(OBJDIR)/$$filename.c" "$(BINDIR)/$$filename" && \ 32 | "$(BINDIR)/$$filename"; \ 33 | fi \ 34 | done 35 | 36 | clean: 37 | rm -rf $(OBJDIR) $(BINDIR) -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "lexer.h" 4 | #include "parser.h" 5 | #include "compiler.h" 6 | 7 | #define MAX_PROGRAM_LEN 10000 8 | 9 | void error(const char* msg) { 10 | fprintf(stderr, "Error: %s\n", msg); 11 | exit(1); 12 | } 13 | 14 | int main(int argc, char* argv[]) { 15 | if (argc != 4) { 16 | printf("Usage: %s \n", argv[0]); 17 | return 1; 18 | } 19 | 20 | // Read input file 21 | FILE* input_file = fopen(argv[1], "r"); 22 | if (!input_file) { 23 | error("Cannot open input file"); 24 | } 25 | 26 | char* program = malloc(MAX_PROGRAM_LEN); 27 | int program_len = fread(program, 1, MAX_PROGRAM_LEN - 1, input_file); 28 | program[program_len] = '\0'; 29 | fclose(input_file); 30 | 31 | // Create lexer 32 | Lexer* lexer = lexer_create(program); 33 | 34 | // Open output file 35 | FILE* output_file = fopen(argv[2], "w"); 36 | if (!output_file) { 37 | error("Cannot open output file"); 38 | } 39 | 40 | // Create parser and generate code 41 | Parser* parser = parser_create(output_file); 42 | parser_generate_code(parser, lexer); 43 | 44 | // Clean up 45 | parser_destroy(parser); 46 | lexer_destroy(lexer); 47 | free(program); 48 | fclose(output_file); 49 | 50 | // Compile the generated C code 51 | if (!compile_output(argv[2], argv[3])) { 52 | error("Compilation failed"); 53 | } 54 | 55 | printf("Program successfully compiled. Run with: ./%s\n", argv[3]); 56 | return 0; 57 | } -------------------------------------------------------------------------------- /src/lexer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "lexer.h" 5 | 6 | static int is_whitespace(char c) { 7 | return c == ' ' || c == '\t' || c == '\n' || c == '\r'; 8 | } 9 | 10 | Lexer* lexer_create(const char* source) { 11 | Lexer* lexer = malloc(sizeof(Lexer)); 12 | lexer->source = strdup(source); 13 | lexer->position = 0; 14 | lexer->length = strlen(source); 15 | return lexer; 16 | } 17 | 18 | void lexer_destroy(Lexer* lexer) { 19 | free(lexer->source); 20 | free(lexer); 21 | } 22 | 23 | void lexer_skip_whitespace(Lexer* lexer) { 24 | while (lexer->position < lexer->length && 25 | is_whitespace(lexer->source[lexer->position])) { 26 | lexer->position++; 27 | } 28 | } 29 | 30 | Token lexer_next_token(Lexer* lexer) { 31 | Token token; 32 | char buffer[MAX_TOKEN_LEN]; 33 | int buf_pos = 0; 34 | 35 | lexer_skip_whitespace(lexer); 36 | 37 | if (lexer->position >= lexer->length) { 38 | token.type = EOF_TOKEN; 39 | return token; 40 | } 41 | 42 | // Handle string literals 43 | if (lexer->source[lexer->position] == '"') { 44 | lexer->position++; // Skip opening quote 45 | while (lexer->position < lexer->length && 46 | lexer->source[lexer->position] != '"' && 47 | buf_pos < MAX_TOKEN_LEN - 1) { 48 | buffer[buf_pos++] = lexer->source[lexer->position++]; 49 | } 50 | if (lexer->source[lexer->position] == '"') { 51 | lexer->position++; // Skip closing quote 52 | } 53 | buffer[buf_pos] = '\0'; 54 | token.type = TINSEL; 55 | strcpy(token.value, buffer); 56 | return token; 57 | } 58 | 59 | // Read word 60 | while (lexer->position < lexer->length && 61 | !is_whitespace(lexer->source[lexer->position]) && 62 | buf_pos < MAX_TOKEN_LEN - 1) { 63 | buffer[buf_pos++] = lexer->source[lexer->position++]; 64 | } 65 | buffer[buf_pos] = '\0'; 66 | 67 | // Match keywords 68 | if (strcmp(buffer, "santa") == 0) token.type = SANTA; 69 | else if (strcmp(buffer, "rudolph") == 0) token.type = RUDOLPH; 70 | else if (strcmp(buffer, "presents") == 0) token.type = PRESENTS; 71 | else if (strcmp(buffer, "sleigh") == 0) token.type = SLEIGH; 72 | else if (strcmp(buffer, "reindeer") == 0) token.type = REINDEER; 73 | else if (strcmp(buffer, "mistletoe") == 0) token.type = MISTLETOE; 74 | else if (strcmp(buffer, "star") == 0) token.type = STAR; 75 | else if (strcmp(buffer, "bell") == 0) token.type = BELL; 76 | else if (strcmp(buffer, "wreath") == 0) token.type = WREATH; 77 | else { 78 | // Check if number 79 | int is_number = 1; 80 | for (int i = 0; buffer[i]; i++) { 81 | if (!isdigit(buffer[i])) { 82 | is_number = 0; 83 | break; 84 | } 85 | } 86 | token.type = is_number ? HOLLY : TINSEL; 87 | } 88 | 89 | strcpy(token.value, buffer); 90 | return token; 91 | } -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ╔══════════════════════════════════════════════════════════════════════════════╗ 2 | ║ JollyC ║ 3 | ╚══════════════════════════════════════════════════════════════════════════════╝ 4 | 5 | ▐ OVERVIEW ▐═══════════════════════════════════════════════════════════════════╗ 6 | │ │ 7 | │ JollyC is a C-based metalang for a christmas-themed │ 8 | │ programming language. It features festive syntax tokens and holiday- │ 9 | │ inspired programming constructs. │ 10 | │ │ 11 | ╠──────────────────────────────────────────────────────────────────────────────╣ 12 | 13 | ▐ TECHNICAL SPECIFICATIONS ▐═══════════════════════════════════════════════════╗ 14 | │ │ 15 | │ LEXICAL TOKENS: │ 16 | │ ├─ SANTA : Program entry point │ 17 | │ ├─ RUDOLPH : Control flow │ 18 | │ ├─ PRESENTS : Variable declaration │ 19 | │ ├─ SLEIGH : Function definition │ 20 | │ ├─ REINDEER : Loop construct │ 21 | │ ├─ MISTLETOE : Conditional branching │ 22 | │ ├─ STAR : Return statement │ 23 | │ ├─ BELL : I/O operations │ 24 | │ ├─ WREATH : Scope delimiter │ 25 | │ ├─ HOLLY : Numeric literal │ 26 | │ └─ TINSEL : String literal/Identifier │ 27 | │ │ 28 | ╠──────────────────────────────────────────────────────────────────────────────╣ 29 | 30 | ▐ BUILD INSTRUCTIONS ▐═══════════════════════════════════════════════════════════╗ 31 | │ │ 32 | │ 1. Clone the repository: │ 33 | │ git clone https://github.com/vmfunc/jollyc.git │ 34 | │ │ 35 | │ 2. Build the compiler: │ 36 | │ make │ 37 | │ │ 38 | │ 3. Compile a JollyC program: │ 39 | │ ./jollyc source.jc │ 40 | │ │ 41 | ╠────────────────────────────────────────────────────────────────────────────────╣ -------------------------------------------------------------------------------- /src/parser.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "parser.h" 4 | #include "lexer.h" 5 | 6 | Parser* parser_create(FILE* output_file) { 7 | Parser* parser = malloc(sizeof(Parser)); 8 | parser->output_file = output_file; 9 | parser->indent_level = 0; 10 | parser->variable_count = 0; 11 | return parser; 12 | } 13 | 14 | void parser_destroy(Parser* parser) { 15 | free(parser); 16 | } 17 | 18 | static int is_variable_declared(Parser* parser, const char* var_name) { 19 | for (int i = 0; i < parser->variable_count; i++) { 20 | if (strcmp(parser->variables[i], var_name) == 0) { 21 | return 1; 22 | } 23 | } 24 | return 0; 25 | } 26 | 27 | static void add_variable(Parser* parser, const char* var_name) { 28 | if (parser->variable_count < MAX_VARIABLES) { 29 | strcpy(parser->variables[parser->variable_count], var_name); 30 | parser->variable_count++; 31 | } 32 | } 33 | 34 | static void write_indentation(Parser* parser) { 35 | for (int i = 0; i < parser->indent_level; i++) { 36 | fprintf(parser->output_file, " "); 37 | } 38 | } 39 | 40 | void parser_generate_code(Parser* parser, Lexer* lexer) { 41 | // Write C file header 42 | fprintf(parser->output_file, "#include \n\n"); 43 | 44 | parser->current_token = lexer_next_token(lexer); 45 | 46 | while (parser->current_token.type != EOF_TOKEN) { 47 | write_indentation(parser); 48 | 49 | switch (parser->current_token.type) { 50 | case SANTA: 51 | parser->current_token = lexer_next_token(lexer); 52 | fprintf(parser->output_file, "int %s() {\n", parser->current_token.value); 53 | parser->indent_level++; 54 | break; 55 | 56 | case RUDOLPH: 57 | fprintf(parser->output_file, "return 0;\n"); 58 | break; 59 | 60 | case PRESENTS: 61 | parser->current_token = lexer_next_token(lexer); 62 | fprintf(parser->output_file, "int %s;\n", parser->current_token.value); 63 | add_variable(parser, parser->current_token.value); 64 | break; 65 | 66 | case SLEIGH: 67 | parser->current_token = lexer_next_token(lexer); 68 | fprintf(parser->output_file, "for(int i = 0; i < %s; i++) {\n", 69 | parser->current_token.value); 70 | parser->indent_level++; 71 | break; 72 | 73 | case REINDEER: 74 | parser->current_token = lexer_next_token(lexer); 75 | fprintf(parser->output_file, "if(%s) {\n", parser->current_token.value); 76 | parser->indent_level++; 77 | break; 78 | 79 | case MISTLETOE: 80 | parser->indent_level--; 81 | write_indentation(parser); 82 | fprintf(parser->output_file, "} else {\n"); 83 | parser->indent_level++; 84 | break; 85 | 86 | case STAR: 87 | parser->current_token = lexer_next_token(lexer); 88 | if (parser->current_token.type == TINSEL) { 89 | fprintf(parser->output_file, "printf(\"%%s\\n\", \"%s\");\n", 90 | parser->current_token.value); 91 | } else { 92 | fprintf(parser->output_file, "printf(\"%%d\\n\", %s);\n", 93 | parser->current_token.value); 94 | } 95 | break; 96 | 97 | case BELL: 98 | parser->indent_level--; 99 | fprintf(parser->output_file, "}\n"); 100 | break; 101 | 102 | case WREATH: 103 | { 104 | char var_name[MAX_TOKEN_LEN]; 105 | strcpy(var_name, parser->current_token.value); 106 | parser->current_token = lexer_next_token(lexer); 107 | if (!is_variable_declared(parser, var_name)) { 108 | fprintf(parser->output_file, "int %s;\n", var_name); 109 | add_variable(parser, var_name); 110 | } 111 | write_indentation(parser); 112 | fprintf(parser->output_file, "%s = %s;\n", var_name, parser->current_token.value); 113 | } 114 | break; 115 | 116 | default: 117 | break; 118 | } 119 | parser->current_token = lexer_next_token(lexer); 120 | } 121 | 122 | if (parser->indent_level > 0) { 123 | parser->indent_level--; 124 | write_indentation(parser); 125 | fprintf(parser->output_file, "}\n"); 126 | } 127 | } --------------------------------------------------------------------------------