commit d1711accded55a67dd9a69240e935014939a3419 Author: Maxwell Jeffress Date: Sun Nov 23 13:37:08 2025 +1100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bb7b0c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +cmake-build-debug +build +ground +groundc +.idea diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f9b734f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 4.0) +project(groundc C) + +set(CMAKE_C_STANDARD 11) + +include_directories(src) + +add_executable(groundc + src/main.c + src/parser.c + src/parser.h + src/types.c + src/types.h) diff --git a/src/lexer.c b/src/lexer.c new file mode 100644 index 0000000..8cb9fc1 --- /dev/null +++ b/src/lexer.c @@ -0,0 +1,144 @@ +#include "lexer.h" +#include +#include +#include +#include + +void addTokenToLine(TokenLine* line, Token tok) { + line->count++; + Token* newTokens = realloc(line->tokens, line->count * sizeof(Token)); + if (!newTokens) { + perror("Failed to allocate token"); + exit(EXIT_FAILURE); + } + line->tokens = newTokens; + line->tokens[line->count - 1] = tok; +} + +void addLineToLexed(LexedFile* lf, TokenLine line) { + lf->lineCount++; + TokenLine* newLines = realloc(lf->lines, lf->lineCount * sizeof(TokenLine)); + if (!newLines) { + perror("Failed to allocate line"); + exit(EXIT_FAILURE); + } + lf->lines = newLines; + lf->lines[lf->lineCount - 1] = line; +} + +LexedFile lexFile(const char* fileContents) { + LexedFile result = {0}; + result.lines = NULL; + result.lineCount = 0; + + TokenLine currentLine = {0}; + currentLine.tokens = NULL; + currentLine.count = 0; + + char buf[1024] = {0}; + size_t bufLen = 0; + bool inString = false; + bool inChar = false; + bool isComment = false; + + for (size_t i = 0; fileContents[i] != '\0'; i++) { + char c = fileContents[i]; + + switch (c) { + case '"': + if (!isComment) { + if (inChar) { + buf[bufLen++] = c; + } else { + inString = !inString; + buf[bufLen++] = c; + } + } + break; + + case '\'': + if (!isComment) { + if (inString) { + buf[bufLen++] = c; + } else { + inChar = !inChar; + buf[bufLen++] = c; + } + } + break; + + case '\n': + if (!inString && !inChar) { + // Add current token to line if exists + if (bufLen > 0) { + buf[bufLen] = '\0'; + Token tok; + tok.text = strdup(buf); + // Add tok to currentLine (need helper function) + addTokenToLine(¤tLine, tok); + bufLen = 0; + } + // Add line to result (need helper function) + addLineToLexed(&result, currentLine); + // Reset for next line + currentLine.tokens = NULL; + currentLine.count = 0; + isComment = false; + } else if (!isComment) { + buf[bufLen++] = c; + } + break; + + case '#': + if (!inString && !inChar) { + isComment = true; + if (bufLen > 0) { + buf[bufLen] = '\0'; + Token tok; + tok.text = strdup(buf); + addTokenToLine(¤tLine, tok); + bufLen = 0; + } + addLineToLexed(&result, currentLine); + currentLine.tokens = NULL; + currentLine.count = 0; + } else { + buf[bufLen++] = c; + } + break; + + case ' ': + if (!inString && !inChar) { + if (bufLen > 0 && !isComment) { + buf[bufLen] = '\0'; + Token tok; + tok.text = strdup(buf); + addTokenToLine(¤tLine, tok); + bufLen = 0; + } + } else { + buf[bufLen++] = c; + } + break; + + default: + if (!isComment) { + buf[bufLen++] = c; + } + break; + } + } + + // Handle any remaining content + if (bufLen > 0) { + buf[bufLen] = '\0'; + Token tok; + tok.text = strdup(buf); + addTokenToLine(¤tLine, tok); + } + if (currentLine.count > 0) { + addLineToLexed(&result, currentLine); + } + + return result; +} diff --git a/src/lexer.h b/src/lexer.h new file mode 100644 index 0000000..93bdc45 --- /dev/null +++ b/src/lexer.h @@ -0,0 +1,23 @@ +#ifndef LEXER_H +#define LEXER_H + +#include + +typedef struct Token { + char* text; +} Token; + +typedef struct TokenLine { + Token* tokens; + size_t count; +} TokenLine; + +typedef struct LexedFile { + TokenLine* lines; + size_t lineCount; +} LexedFile; + +LexedFile lexFile(const char* fileContents); +void freeLexedFile(LexedFile* lf); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..51185d7 --- /dev/null +++ b/src/main.c @@ -0,0 +1,48 @@ +#include "types.h" +#include "parser.h" +#include + +char* getFileContents(const char* filename) { + // https://stackoverflow.com/questions/3747086/reading-the-whole-text-file-into-a-char-array-in-c + FILE* fp; + long lSize; + char* file; + + fp = fopen(filename, "rb"); + if (!fp) { + perror(filename); + exit(1); + } + + fseek(fp, 0L, SEEK_END); + lSize = ftell(fp); + rewind(fp); + + file = calloc(1, lSize + 1); + if (!file) { + fclose(fp); + fputs("memory allocation fail when reading file", stderr); + exit(1); + } + + if (1!=fread(file, lSize, 1, fp)) { + fclose(fp); + free(file); + fputs("couldn't read entire file", stderr); + exit(1); + } + + // we done + fclose(fp); + + return file; +} + +int main(int argc, char** argv) { + if (argc < 2) { + printf("Usage: ground [file]\n"); + exit(1); + } + char* file = getFileContents(argv[1]); + GroundProgram program = parseFile(file); +} diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 0000000..7a4d9e8 --- /dev/null +++ b/src/parser.c @@ -0,0 +1,32 @@ +#include "parser.h" +#include "types.h" +#include "lexer.h" +#include + +GroundProgram createGroundProgram() { + GroundProgram gp; + gp.size = 0; + return gp; +} + +void addInstructionToProgram(GroundProgram* gp, GroundInstruction instruction) { + gp->size++; + GroundInstruction* ptr = realloc(gp->instructions, gp->size * sizeof(GroundInstruction)); + if (ptr == NULL) { + perror("Couldn't allocate memory for instruction"); + exit(1); + } + gp->instructions = ptr; + gp->instructions[gp->size - 1] = instruction; +} + +void freeGroundProgram(GroundProgram* gp) { + for (int i = 0; i < gp->size; i++) { + freeGroundInstruction(&gp->instructions[i]); + } +} + +GroundProgram parseFile(LexedFile file) { + GroundProgram gp; + return gp; +} diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 0000000..f696062 --- /dev/null +++ b/src/parser.h @@ -0,0 +1,27 @@ +#ifndef PARSER_H +#define PARSER_H + +#include +#include +#include +#include "types.h" +#include "lexer.h" + +typedef struct GroundProgram { + GroundInstruction* instructions; + size_t size; +} GroundProgram; + +// Creates a new GroundProgram +GroundProgram createGroundProgram(); + +// Adds instruction (instruction) to GroundProgram (gp) +void addInstructionToProgram(GroundProgram* gp, GroundInstruction instruction); + +// Frees all GroundInstructions in GroundProgram (gp) +void freeGroundProgram(GroundProgram* gp); + +// Parses the file into a GroundProgram +GroundProgram parseFile(LexedFile file); + +#endif diff --git a/src/types.c b/src/types.c new file mode 100644 index 0000000..a460b3e --- /dev/null +++ b/src/types.c @@ -0,0 +1,90 @@ +#include "types.h" + +GroundValue createIntGroundValue(int64_t in) { + GroundValue gv; + gv.data.intVal = in; + gv.type = INT; + return gv; +} + +GroundValue createDoubleGroundValue(double in) { + GroundValue gv; + gv.data.doubleVal = in; + gv.type = DOUBLE; + return gv; +} + +GroundValue createStringGroundValue(const char* in) { + GroundValue gv; + gv.data.stringVal = strdup(in); + gv.type = STRING; + return gv; +} + +GroundValue createCharGroundValue(char in) { + GroundValue gv; + gv.data.charVal = in; + gv.type = CHAR; + return gv; +} + +GroundValue createBoolGroundValue(bool in) { + GroundValue gv; + gv.data.boolVal = in; + gv.type = BOOL; + return gv; +} + +void freeGroundValue(GroundValue* gv) { + if (gv->type == STRING && gv->data.stringVal != NULL) { + free(gv->data.stringVal); + } +} + +GroundArg createValueGroundArg(GroundValue value) { + GroundArg ga; + ga.value.value = value; + ga.type = VALUE; + return ga; +} + +GroundArg createRefGroundArg(GroundArgType type, const char* refname) { + GroundArg ga; + ga.value.refName = strdup(refname); + ga.type = type; + return ga; +} + +void freeGroundArg(GroundArg* ga) { + if (ga->type == VALUE) { + freeGroundValue(&ga->value.value); + } else { + free(ga->value.refName); + } +} + +GroundInstruction createGroundInstruction(GroundInstType type) { + GroundInstruction gi; + gi.type = type; + gi.args.args = NULL; + gi.args.length = 0; + return gi; +} + +void freeGroundInstruction(GroundInstruction* gi) { + for (size_t i = 0; i < gi-> args.length; i++) { + freeGroundArg(&gi->args.args[i]); + } +} + +void addArgToInstruction(GroundInstruction* gi, GroundArg arg) { + gi->args.length ++; + GroundArg* newArgs = realloc(gi->args.args, gi->args.length * sizeof(GroundArg)); + if (newArgs == NULL) { + perror("Failed to allocate memory for instruction argument"); + exit(EXIT_FAILURE); + } + gi->args.args = newArgs; + gi->args.args[gi->args.length - 1] = arg; +} + diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..b98077d --- /dev/null +++ b/src/types.h @@ -0,0 +1,116 @@ +#ifndef TYPES_H +#define TYPES_H + +#include +#include +#include +#include +#include + +typedef enum GroundInstType { + IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, STOI, STOD, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, USE, EXTERN +} GroundInstType; + +typedef enum GroundValueType { + INT, DOUBLE, STRING, CHAR, BOOL, LIST, CUSTOM +} GroundValueType; + +typedef enum GroundArgType { + VALUE, VALREF, DIRREF, LINEREF, LABEL, FNREF +} GroundArgType; + +struct GroundValue; + +struct List; + +/* + * Stores literal values created in a Ground program. + * Associated functions: + * createIntGroundValue(), createDoubleGroundValue(), createStringGroundvalue(), + * createCharGroundValue(), createBoolGroundValue(), freeGroundValue() + */ +typedef struct GroundValue { + GroundValueType type; + union { + int64_t intVal; + double doubleVal; + char* stringVal; + char charVal; + bool boolVal; + struct List* listVal; + void* customVal; + } data; +} GroundValue; + +/* + * Currently unused. Max, implement this sometime soon. + */ +typedef struct List { + size_t size; + size_t capacity; + GroundValue* values; +} List; + +/* + * Stores arguments for the GroundInstruction struct. + * Associated functions: + * createValueGroundArg(), createRefGroundArg(), freeGroundArg() + */ +typedef struct GroundArg { + GroundArgType type; + union { + GroundValue value; + char* refName; + } value; +} GroundArg; + +/* + * Represents a Ground instruction. + * Associated functions: + * createGroundInstruction(), freeGroundInstruction(), addArgToInstruction() + */ +typedef struct GroundInstruction { + GroundInstType type; + struct { + GroundArg* args; + size_t length; + } args; +} GroundInstruction; + +// Creates a GroundValue containing (in), with type INT. +GroundValue createIntGroundValue(int64_t in); + +// Creates a GroundValue containing (in), with type DOUBLE. +GroundValue createDoubleGroundValue(double in); +; +// Creates a GroundValue containing (in), with type STRING. +GroundValue createStringGroundValue(const char* in); + +// Creates a GroundValue containing (in), with type CHAR. +GroundValue createCharGroundValue(char in); + +// Creates a GroundValue containing (in), with type BOOl. +GroundValue createBoolGroundValue(bool in); + +// If (gv) contains any data stored on the heap, frees it. +void freeGroundValue(GroundValue* gv); + +// Initializes a GroundArg with type VALUE +GroundArg createValueGroundArg(GroundValue value); + +// Initializes a GroundArg with type (type), and refname (refname). +GroundArg createRefGroundArg(GroundArgType type, const char* refname); + +// Frees all data stored on the heap in a GroundArg. +void freeGroundArg(GroundArg* ga); + +// Initializes a GroundInstruction, with inst type (type). +GroundInstruction createGroundInstruction(GroundInstType type); + +// Frees all data stored on the heap in a GroundInstruction. +void freeGroundInstruction(GroundInstruction* gi); + +// Adds arg (arg) to the GroundInstruction (gi). +void addArgToInstruction(GroundInstruction* gi, GroundArg arg); + +#endif diff --git a/tests/test.grnd b/tests/test.grnd new file mode 100644 index 0000000..170d049 --- /dev/null +++ b/tests/test.grnd @@ -0,0 +1 @@ +println "dingus"