Initial commit, save progress so far
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
solstice
|
||||||
12
build.c
Normal file
12
build.c
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// -- LEXER --
|
||||||
|
#include "src/lexer/SolsType.c"
|
||||||
|
#include "src/lexer/lexer.c"
|
||||||
|
|
||||||
|
// -- PARSER --
|
||||||
|
|
||||||
|
|
||||||
|
// -- CODEGEN --
|
||||||
|
|
||||||
|
|
||||||
|
// -- MAIN --
|
||||||
|
#include "src/main.c"
|
||||||
83
src/include/error.h
Normal file
83
src/include/error.h
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifndef ERROR_H
|
||||||
|
#define ERROR_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* error.h - First class errors for C
|
||||||
|
* Have you ever wanted to have a Rust-like error experience in C?
|
||||||
|
* Look no further than this library! Using a couple simple macros,
|
||||||
|
* we can emulate their complicated enum system, and I'd argue that
|
||||||
|
* we do it better. Besides, it's in a better programming language.
|
||||||
|
*
|
||||||
|
* Enjoy!
|
||||||
|
*
|
||||||
|
* Licenced to you under the MIT license - see below.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Example usage:
|
||||||
|
*
|
||||||
|
* #include "error.h"
|
||||||
|
* #include <stdio.h>
|
||||||
|
*
|
||||||
|
* // You can't write char*, you have to define it with a typedef
|
||||||
|
* typedef char* charptr;
|
||||||
|
*
|
||||||
|
* Result(int, charptr) myFn(int x) {
|
||||||
|
* if (x > 5) {
|
||||||
|
* return Error(int, charptr, "Your number is too big");
|
||||||
|
* }
|
||||||
|
* return Success(int, charptr, x);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* int main() {
|
||||||
|
* ResultType(int, charptr) res = myFn(10);
|
||||||
|
* if (res.error) {
|
||||||
|
* printf("Uh oh, error is: %s\n", res.as.error);
|
||||||
|
* } else {
|
||||||
|
* printf("Got a result! It is %d\n", res.as.success);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2026 Maxwell Jeffress
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the “Software”),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Creates a new struct with the a (success) and b (error) types.
|
||||||
|
// If Result(a, b) has already been called with the same paramaters, please
|
||||||
|
// use ResultType(a, b) instead.
|
||||||
|
#define Result(a, b) struct __ResultType_##a##_##b { bool error; union {a success; b error;} as; }
|
||||||
|
|
||||||
|
// Uses an existing Result(a, b) struct.
|
||||||
|
#define ResultType(a, b) struct __ResultType_##a##_##b
|
||||||
|
|
||||||
|
|
||||||
|
// Creates a __ResultType_a_b struct, with .error as false and .as.success as res.
|
||||||
|
#define Success(a, b, res) (ResultType(a, b)) { .error = false, .as.success = res }
|
||||||
|
|
||||||
|
// Creates a __ResultType_a_b struct, with .error as true and .as.error as res.
|
||||||
|
#define Error(a, b, res) (ResultType(a, b)) { .error = true, .as.error = res }
|
||||||
|
|
||||||
|
#endif
|
||||||
52
src/include/estr.h
Normal file
52
src/include/estr.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef ESTR_H
|
||||||
|
#define ESTR_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
estr.h - Easy string manipulation
|
||||||
|
This library has macros to allow easier manipulation of strings. No longer shall
|
||||||
|
you have to malloc and realloc away to keep adding to your strings.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
Estr myString = CREATE_ESTR("my awesome string");
|
||||||
|
APPEND_ESTR(myString, " is so cool");
|
||||||
|
printf("%s\n", myString.str);
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CREATE_ESTR(instr) \
|
||||||
|
(Estr) { \
|
||||||
|
.str = instr,\
|
||||||
|
.size = strlen(instr),\
|
||||||
|
.shouldBeFreed = 0, \
|
||||||
|
.destroyed = 0 \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define APPEND_ESTR(estr, instr) { \
|
||||||
|
estr.size = estr.size + strlen(instr); \
|
||||||
|
char* tmp_ptr = malloc(estr.size + 1); \
|
||||||
|
if (tmp_ptr == NULL) printf("WARNING: Could not realloc estr " #estr "\n"); \
|
||||||
|
else { \
|
||||||
|
snprintf(tmp_ptr, estr.size + 1, "%s%s", estr.str, instr); \
|
||||||
|
if (estr.shouldBeFreed > 0) free(estr.str); \
|
||||||
|
estr.shouldBeFreed = 1; \
|
||||||
|
estr.str = tmp_ptr; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DESTROY_ESTR(estr) if (estr.shouldBeFreed > 0 && estr.destroyed < 1) free(estr.str);
|
||||||
|
|
||||||
|
typedef struct Estr {
|
||||||
|
char* str;
|
||||||
|
size_t size;
|
||||||
|
int8_t shouldBeFreed;
|
||||||
|
int8_t destroyed;
|
||||||
|
} Estr;
|
||||||
|
|
||||||
|
#endif // ESTR_H
|
||||||
93
src/lexer/SolsType.c
Normal file
93
src/lexer/SolsType.c
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#include "lexer.h"
|
||||||
|
#include "../include/error.h"
|
||||||
|
#include "../include/estr.h"
|
||||||
|
|
||||||
|
ResultType(SolsType, charptr) createSolsType(SolsTypeType in) {
|
||||||
|
SolsTypeField* ptr = malloc(sizeof(SolsTypeField) * 32);
|
||||||
|
if (ptr == NULL) {
|
||||||
|
return Error(SolsType, charptr, "Couldn't allocate memory (in createSolsType() function)");
|
||||||
|
}
|
||||||
|
SolsType type = { .type = in, .children.capacity = 32, .children.at = ptr };
|
||||||
|
return Success(SolsType, charptr, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultType(SolsType, charptr) copySolsType(SolsType* type) {
|
||||||
|
SolsType ret = { .type = type->type, .children.count = type->children.count, .children.capacity = type->children.capacity};
|
||||||
|
|
||||||
|
// Allocate memory
|
||||||
|
SolsTypeField* ptr = malloc(sizeof(SolsTypeField) * type->children.capacity);
|
||||||
|
if (ptr == NULL) {
|
||||||
|
return Error(SolsType, charptr, "Couldn't allocate memory (in copySolsType() function)");
|
||||||
|
}
|
||||||
|
ret.children.at = ptr;
|
||||||
|
|
||||||
|
// Deep copy values
|
||||||
|
for (size_t i = 0; i < type->children.count; i++) {
|
||||||
|
// Copy the SolsType value
|
||||||
|
ResultType(SolsType, charptr) copied = copySolsType(&type->children.at[i].type);
|
||||||
|
if (copied.error) {
|
||||||
|
Estr err = CREATE_ESTR(copied.as.error);
|
||||||
|
APPEND_ESTR(err, " (in addChildToSolsType() function)");
|
||||||
|
return Error(SolsType, charptr, err.str);
|
||||||
|
}
|
||||||
|
ret.children.at[i].type = copied.as.success;
|
||||||
|
|
||||||
|
// Copy the name
|
||||||
|
if (type->children.at[i].name == NULL) {
|
||||||
|
ret.children.at[i].name = NULL;
|
||||||
|
} else {
|
||||||
|
ret.children.at[i].name = malloc(strlen(type->children.at[i].name) + 1);
|
||||||
|
if (ret.children.at[i].name == NULL) {
|
||||||
|
return Error(SolsType, charptr, "Couldn't allocate memory (in copySolsType() function)");
|
||||||
|
}
|
||||||
|
strcpy(ret.children.at[i].name, type->children.at[i].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Success(SolsType, charptr, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultType(voidptr, charptr) addChildToSolsType(SolsType* type, SolsType child, const char* name) {
|
||||||
|
if (type->children.capacity < type->children.count + 1) {
|
||||||
|
type->children.capacity *= 2;
|
||||||
|
SolsTypeField* ptr = realloc(type->children.at, sizeof(SolsTypeField) * type->children.capacity);
|
||||||
|
if (ptr == NULL) {
|
||||||
|
return Error(voidptr, charptr, "Couldn't allocate memory (in addChildToSolsType() function)");
|
||||||
|
}
|
||||||
|
type->children.at = ptr;
|
||||||
|
}
|
||||||
|
ResultType(SolsType, charptr) copied = copySolsType(&child);
|
||||||
|
if (copied.error) {
|
||||||
|
Estr err = CREATE_ESTR(copied.as.error);
|
||||||
|
APPEND_ESTR(err, " (in addChildToSolsType() function)");
|
||||||
|
return Error(voidptr, charptr, err.str);
|
||||||
|
}
|
||||||
|
type->children.at[type->children.count].type = copied.as.success;
|
||||||
|
if (name == NULL) {
|
||||||
|
type->children.at[type->children.count].name = NULL;
|
||||||
|
} else {
|
||||||
|
type->children.at[type->children.count].name = malloc(strlen(name) + 1);
|
||||||
|
strcpy(type->children.at[type->children.count].name, name);
|
||||||
|
}
|
||||||
|
type->children.count++;
|
||||||
|
|
||||||
|
return Success(voidptr, charptr, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeSolsType(SolsType* type) {
|
||||||
|
for (size_t i = 0; i < type->children.count; i++) {
|
||||||
|
// Free the name
|
||||||
|
if (type->children.at[i].name != NULL) {
|
||||||
|
free(type->children.at[i].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the child SolsTypes
|
||||||
|
freeSolsType(&type->children.at[i].type);
|
||||||
|
}
|
||||||
|
// Free the field itself
|
||||||
|
free(type->children.at);
|
||||||
|
type->children.at = NULL;
|
||||||
|
|
||||||
|
// Set count and capacity to zero
|
||||||
|
type->children.count = 0;
|
||||||
|
type->children.capacity = 0;
|
||||||
|
}
|
||||||
80
src/lexer/SolsType.h
Normal file
80
src/lexer/SolsType.h
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#ifndef SOLSTYPE_H
|
||||||
|
#define SOLSTYPE_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "../include/error.h"
|
||||||
|
|
||||||
|
typedef enum SolsTypeType {
|
||||||
|
STT_INT, STT_STRING, STT_DOUBLE, STT_BOOL, STT_CHAR, STT_FUN, STT_TEMPLATE, STT_OBJECT
|
||||||
|
} SolsTypeType;
|
||||||
|
|
||||||
|
// Definition of charptr for Result() and ResultType() macros
|
||||||
|
typedef char* charptr;
|
||||||
|
// Definition of voidptr for Result() and ResultType() macros
|
||||||
|
typedef void* voidptr;
|
||||||
|
|
||||||
|
struct SolsTypeField;
|
||||||
|
|
||||||
|
// Holds type information for a struct, object or function.
|
||||||
|
// Say, for example, your type signature looks like this:
|
||||||
|
// object(string x, fun(int) y)
|
||||||
|
// This is stored like this:
|
||||||
|
// SolsType {
|
||||||
|
// type: STT_OBJECT
|
||||||
|
// children: [
|
||||||
|
// {
|
||||||
|
// type: {
|
||||||
|
// type: STT_STRING
|
||||||
|
// }
|
||||||
|
// name: "x"
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// type: {
|
||||||
|
// type: STT_FUN
|
||||||
|
// children: [
|
||||||
|
// {
|
||||||
|
// type: {
|
||||||
|
// type: STT_INT
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
// name: "y"
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// (Sorry for the long explaination, but it's worth it so you know how the type system works.)
|
||||||
|
//
|
||||||
|
typedef struct SolsType {
|
||||||
|
SolsTypeType type;
|
||||||
|
struct {
|
||||||
|
struct SolsTypeField* at;
|
||||||
|
size_t count;
|
||||||
|
size_t capacity;
|
||||||
|
} children;
|
||||||
|
} SolsType;
|
||||||
|
|
||||||
|
// Assists with holding child types in the SolsType struct.
|
||||||
|
typedef struct SolsTypeField {
|
||||||
|
SolsType type;
|
||||||
|
char* name;
|
||||||
|
} SolsTypeField;
|
||||||
|
|
||||||
|
|
||||||
|
// Creates a SolsType, with the provided type type.
|
||||||
|
// Use the "addChildToSolsType()" function to add children, in case this type has children.
|
||||||
|
Result(SolsType, charptr);
|
||||||
|
ResultType(SolsType, charptr) createSolsType(SolsTypeType in);
|
||||||
|
|
||||||
|
// Adds a child SolsType to a given SolsType.
|
||||||
|
Result(voidptr, charptr);
|
||||||
|
ResultType(voidptr, charptr) addChildToSolsType(SolsType* type, SolsType child, const char* name);
|
||||||
|
|
||||||
|
// Makes a deep copy of a SolsType.
|
||||||
|
ResultType(SolsType, charptr) copySolsType(SolsType* type);
|
||||||
|
|
||||||
|
// Frees a SolsType
|
||||||
|
void freeSolsType(SolsType* type);
|
||||||
|
|
||||||
|
#endif
|
||||||
0
src/lexer/lexer.c
Normal file
0
src/lexer/lexer.c
Normal file
77
src/lexer/lexer.h
Normal file
77
src/lexer/lexer.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#ifndef LEXER_H
|
||||||
|
#define LEXER_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../include/error.h"
|
||||||
|
|
||||||
|
#include "SolsType.h"
|
||||||
|
|
||||||
|
typedef enum SolsTokenType {
|
||||||
|
STT_IDENTIFIER, STT_LITERAL, STT_TYPE, STT_OPEN_CURLY, STT_CLOSE_CURLY, STT_OPEN_PAREN, STT_CLOSE_PAREN, STT_OP_ADD, STT_OP_SUB, STT_OP_MUL, STT_OP_DIV, STT_OP_SET, STT_OP_GREATER, STT_OP_LESSER, STT_OP_EQUAL, STT_OP_INEQUAL, STT_OP_EQGREATER, STT_OP_EQLESSER, STT_KW_DEF, STT_KW_STRUCT, STT_KW_PUTS, STT_KW_GROUND
|
||||||
|
} SolsTokenType;
|
||||||
|
|
||||||
|
typedef enum SolsLiteralType {
|
||||||
|
SLT_INT, SLT_STRING, SLT_DOUBLE, SLT_BOOL, SLT_CHAR
|
||||||
|
} SolsLiteralType;
|
||||||
|
|
||||||
|
// Stores literal values which will be added to the Ground code.
|
||||||
|
// Not much explaining needed here.
|
||||||
|
typedef struct SolsLiteral {
|
||||||
|
SolsLiteralType type;
|
||||||
|
union {
|
||||||
|
int64_t intv;
|
||||||
|
char* stringv;
|
||||||
|
double doublev;
|
||||||
|
bool boolv;
|
||||||
|
char charv;
|
||||||
|
} as;
|
||||||
|
} SolsLiteral;
|
||||||
|
|
||||||
|
// Represents a token lexed by the lex() function.
|
||||||
|
// Most token types exclusively use the .type field, however some tokens require storing
|
||||||
|
// more data, inside the .as union.
|
||||||
|
// Those tokens are:
|
||||||
|
// STT_LITERAL: A literal value. Uses field .as.literal
|
||||||
|
// STT_TYPE: A type descriptor. Uses field .as.type
|
||||||
|
// STT_IDENTIFIER: An identifier. Uses field .as.idName
|
||||||
|
// STT_KW_GROUND: Ground code embedded inside Solstice. Uses field .as.inlineGround
|
||||||
|
typedef struct SolsToken {
|
||||||
|
SolsTokenType type;
|
||||||
|
union {
|
||||||
|
SolsLiteral literal;
|
||||||
|
SolsType type;
|
||||||
|
char* idName;
|
||||||
|
char* inlineGround;
|
||||||
|
} as;
|
||||||
|
} SolsToken;
|
||||||
|
|
||||||
|
// Represents a Solstice program, seperated into tokens.
|
||||||
|
// .at is a pointer to the tokens
|
||||||
|
// .count is how many tokens are currently being stored
|
||||||
|
// .capacity is how many tokens worth of memory is allocated
|
||||||
|
typedef struct SolsTokens {
|
||||||
|
SolsToken* at;
|
||||||
|
size_t count;
|
||||||
|
size_t capacity;
|
||||||
|
} SolsTokens;
|
||||||
|
|
||||||
|
// Represents the current state of the lexer.
|
||||||
|
// .input is the Solstice program as written by the user.
|
||||||
|
// .output is the lexed Solstice program, which is constructed by the lex() function.
|
||||||
|
// .current represents the current character from .input being lexed.
|
||||||
|
typedef struct SolsLexer {
|
||||||
|
char* input;
|
||||||
|
SolsTokens output;
|
||||||
|
size_t current;
|
||||||
|
} SolsLexer;
|
||||||
|
|
||||||
|
// Creates a lexer for use by the lex() function.
|
||||||
|
SolsLexer createLexer(char* input);
|
||||||
|
|
||||||
|
// Uses the provided lexer to scan the code, and create tokens.
|
||||||
|
void lex(SolsLexer* lexer);
|
||||||
|
|
||||||
|
#endif
|
||||||
5
src/main.c
Normal file
5
src/main.c
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#include "lexer/lexer.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user