25 Commits

Author SHA1 Message Date
7c0490668e Revert "started writing lexer after making string lib"
This reverts commit c0c35e4d17.
2026-04-13 20:00:55 +10:00
e5da1d255b started writing lexer after making string lib 2026-04-13 20:00:38 +10:00
c0c35e4d17 started writing lexer after making string lib 2026-04-13 19:59:48 +10:00
f67c045845 need to fix some solstice bugs before i can start on lexer 2026-04-13 13:00:51 +10:00
98c8775208 clear out c code 2026-04-13 09:30:17 +10:00
f384e19c06 private and protected fields 2026-04-12 21:16:59 +10:00
d24462f844 Don't evaluate left of '=' 2026-04-12 19:35:56 +10:00
4351821d30 Destructor example 2026-04-11 20:42:14 +10:00
78f974e189 Destructors 2026-04-11 20:41:54 +10:00
1dedb30a87 Constructors 2026-04-11 17:05:20 +10:00
2e7b5b7480 Call methods inside objects 2026-04-11 12:03:10 +10:00
1cf995f7ac Fixes for structs 2026-04-10 19:38:26 +10:00
605d0a87b1 Update highlighting 2026-04-10 19:37:41 +10:00
16569d7355 Struct member writing 2026-04-10 14:57:01 +10:00
fd08b7cdb7 Struct field access (slightly buggy) 2026-04-10 10:14:23 +10:00
5841a7a999 Parse object member access 2026-04-10 10:00:24 +10:00
a2fc138ba1 'new' keyword 2026-04-09 19:00:40 +10:00
9b55b509f5 Fix struct interaction with type system 2026-04-09 17:13:51 +10:00
f694f50d70 Fix struct parsing 2026-04-09 17:13:13 +10:00
5b61a11f00 Codegen for structs 2026-04-09 16:04:53 +10:00
00d6ed83fb Parse structs 2026-04-09 15:39:17 +10:00
6988f314b0 Parse exprs in parens 2026-04-09 11:43:00 +10:00
70dc5eb5a0 Parse expressions in parens (3 + 2) * 4 2026-04-09 10:38:58 +10:00
1e3bd6c601 Division no longer becomes subtraction 2026-04-09 10:17:37 +10:00
f66464a7cc Stuff 2026-04-09 10:01:30 +10:00
32 changed files with 154 additions and 6576 deletions

View File

@@ -1,46 +0,0 @@
CXX = gcc
CXXFLAGS = -Wall -Wextra -pedantic -O3 -ggdb
LDFLAGS = -lgroundvm
BUILD_DIR = build
SRC_DIR = src
LIBS_DIR = libs
PREFIX ?= /usr/local
BINDIR = $(PREFIX)/bin
LIBDIR = /usr/lib
SRCS = $(SRC_DIR)/main.c $(SRC_DIR)/codegen/SolsScope.c $(SRC_DIR)/codegen/codegen.c $(SRC_DIR)/lexer/SolsLiteral.c $(SRC_DIR)/lexer/SolsToken.c $(SRC_DIR)/lexer/SolsType.c $(SRC_DIR)/lexer/lexer.c $(SRC_DIR)/parser/SolsNode.c $(SRC_DIR)/parser/parser.c $(SRC_DIR)/typeparser/typeparser.c
OBJS = $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(SRCS))
TARGET = solstice
all: $(TARGET)
$(TARGET): $(OBJS)
$(CXX) $(OBJS) -o $(TARGET) $(LDFLAGS)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR)
$(CXX) $(CXXFLAGS) -c $< -o $@
install: $(TARGET)
mkdir -p /usr/lib/solstice
install -d $(BINDIR)
install -m 755 $(TARGET) $(BINDIR)/$(TARGET)
install -d $(LIBDIR)
cp -r libs/* $(LIBDIR)/solstice
$(BUILD_DIR)/solstice.tar.gz: $(TARGET) $(LIBS_DIR)
mkdir -p $(BUILD_DIR)/pkg/bin $(BUILD_DIR)/pkg/lib/
cp $(TARGET) $(BUILD_DIR)/pkg/bin/solstice
cp -r $(LIBS_DIR) $(BUILD_DIR)/pkg/lib/solstice
tar -czvf $(BUILD_DIR)/solstice.tar.gz $(BUILD_DIR)/pkg
package: $(BUILD_DIR)/solstice.tar.gz
$(BUILD_DIR):
mkdir -p $(BUILD_DIR) $(BUILD_DIR)/codegen $(BUILD_DIR)/lexer $(BUILD_DIR)/parser $(BUILD_DIR)/typeparser
clean:
rm -rf $(BUILD_DIR) $(TARGET)
.PHONY: all clean

View File

@@ -1,5 +1,5 @@
def file_Read(string file) string {} def file_Read(string file) string {}
def file_Write(string file, string content) {} def file_Write(string file, string content) bool {}
ground { ground {
extern "fileio" extern "fileio"

View File

@@ -1,49 +0,0 @@
#include "SolsScope.h"
#include "../include/uthash.h"
#include "../lexer/SolsType.h"
void addVariableToScope(SolsScope* scope, const char* name, SolsType type) {
SolsVariable* s = malloc(sizeof(SolsVariable));
strncpy(s->id, name, sizeof(s->id) - 1);
s->id[sizeof(s->id) - 1] = '\0';
s->typeinfo = type;
HASH_ADD_STR(scope->variables, id, s);
}
SolsVariable* findSolsVariable(SolsScope* scope, const char* name) {
if (scope == NULL || scope->variables == NULL || name == NULL) {
return NULL;
}
SolsVariable* s;
HASH_FIND_STR(scope->variables, name, s);
return s;
}
SolsScope copySolsScope(SolsScope* scope) {
SolsScope newScope = {
.variables = NULL,
.tmpCounter = scope->tmpCounter,
.returnType = scope->returnType
};
SolsVariable *var, *tmp;
HASH_ITER(hh, scope->variables, var, tmp) {
addVariableToScope(&newScope, var->id, var->typeinfo);
}
return newScope;
}
void destroySolsScope(SolsScope* scope) {
SolsVariable *var, *tmp;
HASH_ITER(hh, scope->variables, var, tmp) {
HASH_DEL(scope->variables, var);
free(var);
}
}

View File

@@ -1,32 +0,0 @@
#ifndef SOLSSCOPE_H
#define SOLSSCOPE_H
#include "../include/uthash.h"
#include "../lexer/SolsType.h"
// Stores type information for variables in a UTHash table.
typedef struct SolsVariable {
char id[256];
UT_hash_handle hh;
SolsType typeinfo;
} SolsVariable;
typedef struct SolsScope {
SolsVariable* variables;
size_t tmpCounter;
SolsType returnType;
} SolsScope;
// Adds a variable to the SolsScope.
void addVariableToScope(SolsScope* scope, const char* name, SolsType type);
// Finds a variable in the SolsScope.
SolsVariable* findSolsVariable(SolsScope* scope, const char* name);
// Deep copies a SolsScope, usually for being inside a code block
SolsScope copySolsScope(SolsScope* scope);
// Destroys everything in the SolsScope
void destroySolsScope(SolsScope* scope);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,34 +0,0 @@
#ifndef CODEGEN_H
#define CODEGEN_H
#include <groundvm.h>
#include "SolsScope.h"
#include "../parser/SolsNode.h"
Result(GroundProgram, charptr);
// Generates a GroundProgram (from the Ground VM header) from
// a provided SolsNode.
// Returns:
// Success: Generated GroundProgram
// Failure: charptr detailing what happened
ResultType(GroundProgram, charptr) generateCode(SolsNode* node, SolsScope* scope);
// Gets the type of a node generated by the parser for the type checker.
ResultType(SolsType, charptr) getNodeType(SolsNode* node, SolsScope* scope);
// Macro to help with code generation (and soon error handling)
#define generate(nodetype) {\
ResultType(GroundProgram, charptr) __result = generate##nodetype##Node(node, scope);\
if (__result.error) {\
return Error(GroundProgram, charptr, __result.as.error);\
}\
for (size_t i = 0; i < __result.as.success.size; i++) {\
groundAddInstructionToProgram(&program, __result.as.success.instructions[i]);\
}\
break;\
}
#endif

View File

@@ -1,67 +0,0 @@
// ansii.h - made by SpookyDervish
// version 1.0.0
// do with this whatever you want
//
// example usage with printf: printf(ESC_BOLD ESC_RED_FG "hi\n");
#ifndef ANSII_H
#define ANSII_H
#define ESC_RESET "\x1b[0m"
#define ESC_BOLD "\x1b[1m"
#define ESC_DIM "\x1b[2m"
#define ESC_ITALIC "\x1b[3m"
#define ESC_UNDERLINE "\x1b[4m"
#define ESC_BLINKING "\x1b[5m"
#define ESC_REVERSE "\x1b[7m"
#define ESC_HIDDEN "\x1b[8m"
#define ESC_STRIKETHROUGH "\x1b[8m"
#define ESC_TERMINAL_BELL "\a"
#define ESC_BLACK_FG "\x1b[30m"
#define ESC_RED_FG "\x1b[31m"
#define ESC_GREEN_FG "\x1b[32m"
#define ESC_YELLOW_FG "\x1b[33m"
#define ESC_BLUE_FG "\x1b[34m"
#define ESC_MAGENTA_FG "\x1b[35m"
#define ESC_CYAN_FG "\x1b[36m"
#define ESC_WHITE_FG "\x1b[37m"
#define ESC_BLACK_FG "\x1b[30m"
#define ESC_RED_FG "\x1b[31m"
#define ESC_GREEN_FG "\x1b[32m"
#define ESC_YELLOW_FG "\x1b[33m"
#define ESC_BLUE_FG "\x1b[34m"
#define ESC_MAGENTA_FG "\x1b[35m"
#define ESC_CYAN_FG "\x1b[36m"
#define ESC_WHITE_FG "\x1b[37m"
#define ESC_BRIGHT_BLACK_FG "\x1b[90m"
#define ESC_BRIGHT_RED_FG "\x1b[91m"
#define ESC_BRIGHT_GREEN_FG "\x1b[92m"
#define ESC_BRIGHT_YELLOW_FG "\x1b[93m"
#define ESC_BRIGHT_BLUE_FG "\x1b[94m"
#define ESC_BRIGHT_MAGENTA_FG "\x1b[95m"
#define ESC_BRIGHT_CYAN_FG "\x1b[96m"
#define ESC_BRIGHT_WHITE_FG "\x1b[97m"
#define ESC_BLACK_BG "\x1b[40m"
#define ESC_RED_BG "\x1b[41m"
#define ESC_GREEN_BG "\x1b[42m"
#define ESC_YELLOW_BG "\x1b[43m"
#define ESC_BLUE_BG "\x1b[44m"
#define ESC_MAGENTA_BG "\x1b[45m"
#define ESC_CYAN_BG "\x1b[46m"
#define ESC_WHITE_BG "\x1b[47m"
#define ESC_BRIGHT_BLACK_BG "\x1b[100m"
#define ESC_BRIGHT_RED_BG "\x1b[101m"
#define ESC_BRIGHT_GREEN_BG "\x1b[102m"
#define ESC_BRIGHT_YELLOW_BG "\x1b[103m"
#define ESC_BRIGHT_BLUE_BG "\x1b[104m"
#define ESC_BRIGHT_MAGENTA_BG "\x1b[105m"
#define ESC_BRIGHT_CYAN_BG "\x1b[106m"
#define ESC_BRIGHT_WHITE_BG "\x1b[107m"
#define ESC_DEFAULT_FG "\x1b[39m"
#endif // !ANSII_H

View File

@@ -1,83 +0,0 @@
#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

View File

@@ -1,52 +0,0 @@
#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

View File

@@ -1,10 +0,0 @@
// nothing.h - ever needed to return nothing (but not void)?
// boy do I have the solution for you
#ifndef NOTHING_H
#define NOTHING_H
// Behold, it is nothing!
typedef struct Nothing {} Nothing;
#endif

File diff suppressed because it is too large Load Diff

BIN
src/lexer/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -1,51 +0,0 @@
#include "SolsLiteral.h"
#include <stdarg.h>
#include <string.h>
ResultType(SolsLiteral, charptr) createSolsLiteral(SolsLiteralType type, ...) {
va_list args;
va_start(args, type);
SolsLiteral literal = {
.type = type
};
switch (type) {
case SLT_INT: {
literal.as.intv = va_arg(args, int64_t);
break;
}
case SLT_DOUBLE: {
literal.as.doublev = va_arg(args, double);
break;
}
case SLT_BOOL: {
literal.as.boolv = (bool) va_arg(args, int);
break;
}
case SLT_CHAR: {
literal.as.charv = (char) va_arg(args, int);
break;
}
case SLT_STRING: {
char* input = va_arg(args, char*);
if (input == NULL) {
va_end(args);
return Error(SolsLiteral, charptr, "Unexpected NULL value (in createSolsLiteral() function)");
}
literal.as.stringv = malloc(strlen(input) + 1);
if (literal.as.stringv == NULL) {
va_end(args);
return Error(SolsLiteral, charptr, "Couldn't allocate memory (in createSolsLiteral() function)");
}
strcpy(literal.as.stringv, input);
break;
}
}
va_end(args);
return Success(SolsLiteral, charptr, literal);
}
void freeSolsLiteral(SolsLiteral* lit) {
if (lit->type == SLT_STRING && lit->as.stringv != NULL) {
free(lit->as.stringv);
}
}

View File

@@ -1,46 +0,0 @@
#ifndef SOLSLITERAL_H
#define SOLSLITERAL_H
#include <inttypes.h>
#include <stdarg.h>
#include "../include/error.h"
#include "../include/nothing.h"
typedef char* charptr;
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;
Result(SolsLiteral, charptr);
// Creates a SolsLiteral, based on the type provided.
// SolsLiteralType -> C type:
// SLT_INT -> int64_t
// SLT_STRING -> char*
// SLT_DOUBLE -> double
// SLT_BOOL -> bool
// SL_CHAR -> char
// An error will only be returned if there is an issue copying a provided char*.
// There is no way to detect incorrectly provided types, so ensure that the right type
// is provided!!!!
ResultType(SolsLiteral, charptr) createSolsLiteral(SolsLiteralType type, ...);
// Frees a SolsLiteral. Primarily concerned with freeing .as.stringv
void freeSolsLiteral(SolsLiteral* lit);
#endif

View File

@@ -1,93 +0,0 @@
#include "SolsToken.h"
#include "SolsLiteral.h"
#include "../include/error.h"
#include <stdarg.h>
#include <string.h>
ResultType(SolsToken, charptr) createSolsToken(SolsTokenType type, ...) {
va_list args;
va_start(args, type);
SolsToken token = {
.type = type
};
if (type == STT_IDENTIFIER) {
char* name = va_arg(args, char*);
if (name == NULL) {
va_end(args);
return Error(SolsToken, charptr, "String passed is NULL (in createSolsToken() function)");
}
token.as.idName = malloc(strlen(name) + 1);
if (token.as.idName == NULL) {
va_end(args);
return Error(SolsToken, charptr, "Couldn't allocate memory (in createSolsToken() function)");
}
strcpy(token.as.idName, name);
}
if (type == STT_KW_GROUND) {
char* ground = va_arg(args, char*);
if (ground == NULL) {
va_end(args);
return Error(SolsToken, charptr, "String passed is NULL (in createSolsToken() function)");
}
token.as.inlineGround = malloc(strlen(ground) + 1);
if (token.as.inlineGround == NULL) {
va_end(args);
return Error(SolsToken, charptr, "Couldn't allocate memory (in createSolsToken() function)");
}
strcpy(token.as.inlineGround, ground);
}
if (type == STT_LITERAL) {
token.as.literal = va_arg(args, SolsLiteral);
}
if (type == STT_TYPE) {
token.as.type = va_arg(args, SolsType);
}
va_end(args);
return Success(SolsToken, charptr, token);
}
void freeSolsToken(SolsToken* token) {
if (token->type == STT_IDENTIFIER && token->as.idName != NULL) {
free(token->as.idName);
}
if (token->type == STT_KW_GROUND && token->as.inlineGround != NULL) {
free(token->as.inlineGround);
}
if (token->type == STT_LITERAL) {
freeSolsLiteral(&token->as.literal);
}
if (token->type == STT_TYPE) {
freeSolsType(&token->as.type);
}
}
ResultType(SolsTokens, charptr) createSolsTokens() {
SolsTokens tokens = {
.at = malloc(sizeof(SolsToken) * 32),
.capacity = 32,
.count = 0
};
if (tokens.at == NULL) {
return Error(SolsTokens, charptr, "Failed to allocate memory (in createSolsTokens() function)");
}
return Success(SolsTokens, charptr, tokens);
}
ResultType(Nothing, charptr) addTokenToSolsTokens(SolsTokens* tokens, SolsToken token) {
if (tokens->capacity < tokens->count + 1) {
tokens->capacity *= 2;
SolsToken* tmp = realloc(tokens->at, sizeof(SolsToken) * tokens->capacity);
if (tmp == NULL) {
return Error(Nothing, charptr, "Failed to allocate memory (in addTokenToSolsTokens() function)");
}
tokens->at = tmp;
}
tokens->at[tokens->count] = token;
tokens->count++;
return Success(Nothing, charptr, {});
}

View File

@@ -1,83 +0,0 @@
#ifndef SOLSTOKEN_H
#define SOLSTOKEN_H
#include <stdarg.h>
#include "../include/error.h"
#include "../include/nothing.h"
#include "SolsType.h"
#include "SolsLiteral.h"
typedef enum SolsTokenType {
STT_IDENTIFIER, STT_LITERAL, STT_TYPE, STT_DOT, 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_ADDTO, STT_OP_SUBTO, STT_OP_MULTO, STT_OP_DIVTO, STT_OP_INCREMENT, STT_OP_DECREMENT, 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_LAMBDA, STT_KW_RETURN, STT_KW_USE, STT_KW_STRUCT, STT_KW_PUTS, STT_KW_IF, STT_KW_WHILE, STT_KW_NEW, STT_KW_GROUND, STT_LINE_END, STT_COMMA
} SolsTokenType;
typedef char* charptr;
// Stores information about the line that the token/node is on, for printing if an error
// occurs.
// .num is the line number, .content is the line's contents.
typedef struct LineInfo {
size_t num;
char* content;
} LineInfo;
// 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;
LineInfo line;
} SolsToken;
Result(SolsToken, charptr);
// Creates a SolsToken. If the type passed in is STT_LITERAL, STT_TYPE, STT_IDENTIFIER or
// STT_KW_GROUND, the function expects another argument, corresponding to the data type
// the token holds. See the SolsToken struct for more information.
// Returns:
// Success: The created SolsToken
// Failure: char* detailing what went wrong (usually memory failure)
ResultType(SolsToken, charptr) createSolsToken(SolsTokenType type, ...);
// Frees a SolsToken, specifically the .as field elements.
void freeSolsToken(SolsToken* token);
// 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;
Result(SolsTokens, charptr);
// Creates a SolsTokens holder.
// Returns:
// Success: Constructed SolsTokens
// Failure: char* detailing what went wrong (usually memory failure)
ResultType(SolsTokens, charptr) createSolsTokens();
// Adds a token to SolsTokens. Used by the lex() function.
// Returns:
// Success: Nothing
// Failure: char* detailing what went wrong (usually memory failure)
ResultType(Nothing, charptr) addTokenToSolsTokens(SolsTokens* tokens, SolsToken token);
#endif

View File

@@ -1,176 +0,0 @@
#include "SolsType.h"
#include "lexer.h"
#include "../include/error.h"
#include "../include/estr.h"
#include <groundvm.h>
#include <string.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(Nothing, 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(Nothing, 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(Nothing, 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(Nothing, charptr, {});
}
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;
}
bool compareTypes(SolsType* left, SolsType* right) {
if (left->type != right->type) {
return false;
}
switch (left->type) {
case STT_OBJECT: {
if (left->children.count != right->children.count) {
return false;
}
for (size_t i = 0; i < left->children.count; i++) {
if (strcmp(left->children.at[i].name, right->children.at[i].name) != 0) {
return false;
}
if (compareTypes(&left->children.at[i].type, &right->children.at[i].type) == false) {
return false;
}
}
return true;
}
case STT_TEMPLATE: {
if (left->children.count != right->children.count) {
return false;
}
for (size_t i = 0; i < left->children.count; i++) {
if (strcmp(left->children.at[i].name, right->children.at[i].name) != 0) {
return false;
}
if (compareTypes(&left->children.at[i].type, &right->children.at[i].type) == false) {
return false;
}
}
return true;
}
case STT_FUN: {
if (left->children.count != right->children.count) {
return false;
}
for (size_t i = 0; i < left->children.count; i++) {
if (compareTypes(&left->children.at[i].type, &right->children.at[i].type) == false) {
return false;
}
}
return true;
}
default: return true;
}
}
ResultType(GroundArg, charptr) createGroundArgFromSolsType(SolsType* type) {
switch (type->type) {
case STT_INT: {
return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "int"));
}
case STT_DOUBLE: {
return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "double"));
}
case STT_STRING: {
return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "string"));
}
case STT_BOOL: {
return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "bool"));
}
case STT_CHAR: {
return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "char"));
}
case STT_FUN: {
return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "function"));
}
case STT_TEMPLATE: {
return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "struct"));
}
case STT_OBJECT: {
// FIXME Do this later
return Error(GroundArg, charptr, "FIXME");
}
}
return Error(GroundArg, charptr, "How did we get here?");
}

View File

@@ -1,105 +0,0 @@
#ifndef SOLSTYPE_H
#define SOLSTYPE_H
#include <stdlib.h>
#include <groundvm.h>
#include "../include/error.h"
#include "../include/nothing.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;
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;
// For use when type is identified with a name
char* identifierType;
// For use in functions
struct SolsType* returnType;
// For use by fun, template, object
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;
Result(SolsType, charptr);
// Creates a SolsType, with the provided type type.
// Use the "addChildToSolsType()" function to add children, in case this type has children.
// Returns:
// Success: The constructed SolsType
// Failure: char* detailing what went wrong (usually memory failure)
ResultType(SolsType, charptr) createSolsType(SolsTypeType in);
Result(Nothing, charptr);
// Adds a child SolsType to a given SolsType.
// Returns:
// Success: Nothing
// Failure: char* detailing what went wrong (usually memory failure)
ResultType(Nothing, charptr) addChildToSolsType(SolsType* type, SolsType child, const char* name);
// Makes a deep copy of a SolsType.
ResultType(SolsType, charptr) copySolsType(SolsType* type);
Result(GroundArg, charptr);
// Represents a SolsType as a GroundArg (in typeref form)
ResultType(GroundArg, charptr) createGroundArgFromSolsType(SolsType* type);
// Frees a SolsType
void freeSolsType(SolsType* type);
// Compares two SolsTypes
bool compareTypes(SolsType* left, SolsType* right);
#endif

View File

@@ -1,844 +0,0 @@
#include "lexer.h"
#include "SolsLiteral.h"
#include "SolsToken.h"
#include "../include/error.h"
#include "../include/estr.h"
#include "../include/ansii.h"
#include <ctype.h>
struct _SolsTokenTypeMap SolsTokenTypeMap[] = {
{"puts", STT_KW_PUTS},
{"if", STT_KW_IF},
{"while", STT_KW_WHILE},
{"def", STT_KW_DEF},
{"lambda", STT_KW_LAMBDA},
{"return", STT_KW_RETURN},
{"use", STT_KW_USE},
{"struct", STT_KW_STRUCT},
{"ground", STT_KW_GROUND},
{"{", 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_ADDTO},
{"-=", STT_OP_SUBTO},
{"*=", STT_OP_MULTO},
{"/=", STT_OP_DIVTO},
{"++", STT_OP_INCREMENT},
{"--", STT_OP_DECREMENT},
{"==", STT_OP_EQUAL},
{"!=", STT_OP_INEQUAL},
{">", STT_OP_GREATER},
{"<", STT_OP_LESSER},
{">=", STT_OP_EQGREATER},
{"<=", STT_OP_EQLESSER},
{"\n", STT_LINE_END},
{";", STT_LINE_END},
{",", STT_COMMA},
// Shh, this is our little secret
// Your reward for actually reading the source code
// Enable this by adding -DSUPER_SILLY_MODE to your
// compile flags (not recommended for production)
#ifdef SUPER_SILLY_MODE
{"plus", STT_OP_ADD},
{"minus", STT_OP_SUB},
{"times", STT_OP_MUL},
{"dividedby", STT_OP_DIV},
{"then", STT_OPEN_CURLY},
{"do", STT_OPEN_CURLY},
{"end", STT_CLOSE_CURLY},
{"is", STT_OP_SET},
{"equals", STT_OP_EQUAL},
{"greaterthan", STT_OP_GREATER},
{"lesserthan", STT_OP_LESSER},
{"increment", STT_OP_INCREMENT},
{"decrement", STT_OP_DECREMENT},
{"adds", STT_OP_ADDTO},
{"subtracts", STT_OP_SUBTO},
{"multiplies", STT_OP_MULTO},
{"divides", STT_OP_DIVTO},
#endif
};
ResultType(SolsTokenType, Nothing) getTokenType(const char* input) {
size_t mapsize = sizeof(SolsTokenTypeMap) / sizeof(struct _SolsTokenTypeMap);
for (size_t i = 0; i < mapsize; i++) {
if (strcmp(input, SolsTokenTypeMap[i].str) == 0) {
return Success(SolsTokenType, Nothing, SolsTokenTypeMap[i].type);
}
}
return Error(SolsTokenType, Nothing, {});
}
static ResultType(Nothing, charptr) handleGround(SolsLexer* lexer, SolsToken* token, size_t* lineNum, Estr* currentLine, char currentChr, bool* skipDelimiter) {
bool foundBrace = false;
if (currentChr == '{') {
foundBrace = true;
*skipDelimiter = true;
} else {
while (true) {
ResultType(char, Nothing) peek = lexerPeek(lexer, 1);
if (peek.error) break;
if (isspace(peek.as.success)) {
char c = lexerConsume(lexer).as.success;
if (c == '\n') {
(*lineNum)++;
DESTROY_ESTR((*currentLine));
*currentLine = CREATE_ESTR("");
size_t lineStart = lexer->current;
for (size_t i = lineStart; i < lexer->inputsize; i++) {
if (lexer->input[i] == '\n') break;
char buf_tmp[] = {lexer->input[i], '\0'};
APPEND_ESTR((*currentLine), buf_tmp);
}
}
} else if (peek.as.success == '{') {
lexerConsume(lexer);
foundBrace = true;
break;
} else {
break;
}
}
}
if (!foundBrace) {
return Error(Nothing, charptr, "Expected '{' after 'ground'");
}
Estr groundBuf = CREATE_ESTR("");
int depth = 1;
while (depth > 0) {
ResultType(char, Nothing) next = lexerConsume(lexer);
if (next.error) {
DESTROY_ESTR(groundBuf);
return Error(Nothing, charptr, "Unterminated 'ground' block");
}
if (next.as.success == '{') depth++;
if (next.as.success == '}') {
depth--;
if (depth == 0) break;
}
char tmp[] = {next.as.success, '\0'};
APPEND_ESTR(groundBuf, tmp);
if (next.as.success == '\n') {
(*lineNum)++;
DESTROY_ESTR((*currentLine));
*currentLine = CREATE_ESTR("");
size_t lineStart = lexer->current;
for (size_t i = lineStart; i < lexer->inputsize; i++) {
if (lexer->input[i] == '\n') break;
char buf_tmp[] = {lexer->input[i], '\0'};
APPEND_ESTR((*currentLine), buf_tmp);
}
}
}
token->as.inlineGround = malloc(strlen(groundBuf.str) + 1);
if (token->as.inlineGround == NULL) {
DESTROY_ESTR(groundBuf);
return Error(Nothing, charptr, "Memory allocation failed (in handleGround() function)");
}
strcpy(token->as.inlineGround, groundBuf.str);
DESTROY_ESTR(groundBuf);
return Success(Nothing, charptr, {});
}
static ResultType(Nothing, charptr) identifyAndAdd(SolsLexer* lexer, Estr* buf, size_t* lineNum, Estr* currentLine, char currentChr, bool* skipDelimiter) {
if (strcmp(buf->str, "") == 0) return Success(Nothing, charptr, {});
ResultType(SolsToken, charptr) result = identifyToken(buf->str);
if (result.error) {
return Error(Nothing, charptr, result.as.error);
}
result.as.success.line.num = *lineNum;
result.as.success.line.content = malloc(strlen(currentLine->str) + 1);
if (result.as.success.line.content == NULL) {
return Error(Nothing, charptr, "Couldn't allocate memory to store line information in token (in identifyAndAdd() function)");
}
strcpy(result.as.success.line.content, currentLine->str);
if (result.as.success.type == STT_KW_GROUND) {
ResultType(Nothing, charptr) res = handleGround(lexer, &result.as.success, lineNum, currentLine, currentChr, skipDelimiter);
if (res.error) return res;
}
addTokenToSolsTokens(&lexer->output, result.as.success);
DESTROY_ESTR((*buf));
*buf = CREATE_ESTR("");
return Success(Nothing, charptr, {});
}
ResultType(SolsLexer, charptr) createLexer(char* input) {
// Copy input into the new lexer struct
char* inputcopy = malloc(strlen(input) + 1);
if (inputcopy == NULL) {
return Error(SolsLexer, charptr, "Couldn't copy string into lexer (in createLexer() function)");
}
strcpy(inputcopy, input);
// Create SolsTokens
ResultType(SolsTokens, charptr) tokens = createSolsTokens();
if (tokens.error) {
Estr e = CREATE_ESTR(tokens.as.error);
APPEND_ESTR(e, " (in createLexer() function)");
return Error(SolsLexer, charptr, e.str);
}
// Construct and return lexer
SolsLexer lexer = {
.input = inputcopy,
.inputsize = strlen(inputcopy),
.output = tokens.as.success,
.current = 0,
};
return Success(SolsLexer, charptr, lexer);
}
ResultType(char, Nothing) lexerPeek(SolsLexer* lexer, size_t ahead) {
// Reduce by 1 so peeking at the next token with 1 works
ahead--;
// Bounds and null checking
if (lexer->input == NULL) {
return Error(char, Nothing, {});
}
if (lexer->current + ahead >= lexer->inputsize) {
return Error(char, Nothing, {});
}
// Char is within bounds, return it
return Success(char, Nothing, lexer->input[lexer->current + ahead]);
}
ResultType(char, Nothing) lexerConsume(SolsLexer* lexer) {
// Bounds and null checking
if (lexer->input == NULL) {
return Error(char, Nothing, {});
}
if (lexer->current + 1 > lexer->inputsize) {
return Error(char, Nothing, {});
}
// Char is within bounds, return and increment
return Success(char, Nothing, lexer->input[lexer->current++]);
}
ResultType(SolsToken, charptr) identifyToken(const char* token) {
// Process strings
if (token[0] == '"') {
if (token[strlen(token) - 1] == '"') {
// Cut out the quotes
char* tokencopy = malloc(strlen(token) + 1);
strncpy(tokencopy, token + 1, strlen(token) - 2);
tokencopy[strlen(token) - 2] = '\0';
// Create a literal
ResultType(SolsLiteral, charptr) literal = createSolsLiteral(SLT_STRING, tokencopy);
// Free our copy of the string, createSolsLiteral creates a copy
free(tokencopy);
if (literal.error) {
Estr str = CREATE_ESTR(literal.as.error);
APPEND_ESTR(str, " (in identifyToken() function)");
return Error(SolsToken, charptr, str.str);
}
// Construct and return the token
SolsToken tok = {
.type = STT_LITERAL,
.as.literal = literal.as.success
};
return Success(SolsToken, charptr, tok);
}
return Error(SolsToken, charptr, "Unterminated string (in identifyToken() function)");
}
// Process characters
if (token[0] == '\'') {
if (strlen(token) != 3) {
return Error(SolsToken, charptr, "Characters can only hold one character at a time (try using \"this\" for strings?)");
}
if (token[2] == '\'') {
ResultType(SolsLiteral, charptr) literal = createSolsLiteral(SLT_CHAR, token[1]);
if (literal.error) {
Estr str = CREATE_ESTR(literal.as.error);
APPEND_ESTR(str, " (in identifyToken() function)");
return Error(SolsToken, charptr, str.str);
}
SolsToken tok = {
.type = STT_LITERAL,
.as.literal = literal.as.success
};
return Success(SolsToken, charptr, tok);
} else {
return Error(SolsToken, charptr, "Unterminated character (in identifyToken() function)");
}
}
// Process integers and floats
if (isdigit(token[0]) || (token[0] == '-' && strlen(token) > 1 && (isdigit(token[1]) || token[1] == '.'))) {
size_t len = strlen(token);
bool isInt = true;
bool isDouble = false;
for (size_t i = 1; i < len; i++) {
if (isInt && token[i] == '.') {
isInt = false;
isDouble = true;
continue;
}
if (!isdigit(token[i])) {
isInt = false;
isDouble = false;
}
}
if (isInt) {
int64_t newInt = atoll(token);
ResultType(SolsLiteral, charptr) literal = createSolsLiteral(SLT_INT, newInt);
if (literal.error) {
Estr str = CREATE_ESTR(literal.as.error);
APPEND_ESTR(str, " (in identifyToken() function)");
return Error(SolsToken, charptr, str.str);
}
SolsToken tok = {
.type = STT_LITERAL,
.as.literal = literal.as.success
};
return Success(SolsToken, charptr, tok);
}
if (isDouble) {
double newDouble = atof(token);
ResultType(SolsLiteral, charptr) literal = createSolsLiteral(SLT_DOUBLE, newDouble);
if (literal.error) {
Estr str = CREATE_ESTR(literal.as.error);
APPEND_ESTR(str, " (in identifyToken() function)");
return Error(SolsToken, charptr, str.str);
}
SolsToken tok = {
.type = STT_LITERAL,
.as.literal = literal.as.success
};
return Success(SolsToken, charptr, tok);
}
}
// Handle boolean (true/false)
if (strcmp(token, "true") == 0) {
ResultType(SolsLiteral, charptr) literal = createSolsLiteral(SLT_BOOL, true);
if (literal.error) {
Estr str = CREATE_ESTR(literal.as.error);
APPEND_ESTR(str, " (in identifyToken() function)");
return Error(SolsToken, charptr, str.str);
}
SolsToken tok = {
.type = STT_LITERAL,
.as.literal = literal.as.success
};
return Success(SolsToken, charptr, tok);
}
if (strcmp(token, "false") == 0) {
ResultType(SolsLiteral, charptr) literal = createSolsLiteral(SLT_BOOL, false);
if (literal.error) {
Estr str = CREATE_ESTR(literal.as.error);
APPEND_ESTR(str, " (in identifyToken() function)");
return Error(SolsToken, charptr, str.str);
}
SolsToken tok = {
.type = STT_LITERAL,
.as.literal = literal.as.success
};
return Success(SolsToken, charptr, tok);
}
// Process base types
if (strcmp(token, "int") == 0) {
ResultType(SolsType, charptr) type = createSolsType(STT_INT);
if (type.error) {
Estr e = CREATE_ESTR(type.as.error);
APPEND_ESTR(e, " (in identifyToken() function)");
return Error(SolsToken, charptr, e.str);
}
SolsToken tok = {
.type = STT_TYPE,
.as.type = type.as.success
};
return Success(SolsToken, charptr, tok);
}
if (strcmp(token, "double") == 0) {
ResultType(SolsType, charptr) type = createSolsType(STT_DOUBLE);
if (type.error) {
Estr e = CREATE_ESTR(type.as.error);
APPEND_ESTR(e, " (in identifyToken() function)");
return Error(SolsToken, charptr, e.str);
}
SolsToken tok = {
.type = STT_TYPE,
.as.type = type.as.success
};
return Success(SolsToken, charptr, tok);
}
if (strcmp(token, "string") == 0) {
ResultType(SolsType, charptr) type = createSolsType(STT_STRING);
if (type.error) {
Estr e = CREATE_ESTR(type.as.error);
APPEND_ESTR(e, " (in identifyToken() function)");
return Error(SolsToken, charptr, e.str);
}
SolsToken tok = {
.type = STT_TYPE,
.as.type = type.as.success
};
return Success(SolsToken, charptr, tok);
}
if (strcmp(token, "char") == 0) {
ResultType(SolsType, charptr) type = createSolsType(STT_CHAR);
if (type.error) {
Estr e = CREATE_ESTR(type.as.error);
APPEND_ESTR(e, " (in identifyToken() function)");
return Error(SolsToken, charptr, e.str);
}
SolsToken tok = {
.type = STT_TYPE,
.as.type = type.as.success
};
return Success(SolsToken, charptr, tok);
}
if (strcmp(token, "bool") == 0) {
ResultType(SolsType, charptr) type = createSolsType(STT_BOOL);
if (type.error) {
Estr e = CREATE_ESTR(type.as.error);
APPEND_ESTR(e, " (in identifyToken() function)");
return Error(SolsToken, charptr, e.str);
}
SolsToken tok = {
.type = STT_TYPE,
.as.type = type.as.success
};
return Success(SolsToken, charptr, tok);
}
// Find if it's a reserved keyword/operator
ResultType(SolsTokenType, Nothing) result = getTokenType(token);
if (!result.error) {
return Success(SolsToken, charptr, {result.as.success});
}
// No appropriate token found, it's an identifier (I hope)
SolsToken id = {
.type = STT_IDENTIFIER,
.as.idName = malloc(strlen(token) + 1)
};
if (id.as.idName == NULL) {
return Error(SolsToken, charptr, "Couldn't allocate memory to copy string (in identifyToken() function)");
}
strcpy(id.as.idName, token);
return Success(SolsToken, charptr, id);
}
char* createLexingError(size_t lineNum, char* line, char* why) {
Estr error = CREATE_ESTR(ESC_RESET ESC_BOLD ESC_RED_FG "Lexing Error " ESC_RESET ESC_YELLOW_FG "on line ");
char buf[256];
snprintf(buf, sizeof(buf), "%zu", lineNum);
APPEND_ESTR(error, buf);
APPEND_ESTR(error, ":\n\n" ESC_RESET ESC_BLUE_FG " ");
APPEND_ESTR(error, line);
APPEND_ESTR(error, "\n\n");
APPEND_ESTR(error, ESC_RESET ESC_MAGENTA_FG "-> ");
APPEND_ESTR(error, why);
APPEND_ESTR(error, "\n");
return error.str;
}
ResultType(Nothing, charptr) lex(SolsLexer* lexer) {
if (lexer->input == NULL) {
return Error(Nothing, charptr, "Lexer is not initialised");
}
Estr buf = CREATE_ESTR("");
bool inString = false;
size_t lineNum = 1;
size_t lineStart = 0;
Estr currentLine = CREATE_ESTR("");
for (; lineStart < lexer->inputsize; lineStart++) {
if (lexer->input[lineStart] == '\n') {
break;
}
char tmp[] = {lexer->input[lineStart], '\0'};
APPEND_ESTR(currentLine, tmp);
}
bool skipDelimiter = false;
for (;;) {
ResultType(char, Nothing) chr = lexerConsume(lexer);
if (chr.error) {
break;
}
skipDelimiter = false;
if (chr.as.success == '/' && !inString) {
ResultType(char, Nothing) peek = lexerPeek(lexer, 1);
if (!peek.error && peek.as.success == '/') {
// Consume characters until \n or EOF
while (true) {
ResultType(char, Nothing) next = lexerPeek(lexer, 1);
if (next.error || next.as.success == '\n') break;
lexerConsume(lexer);
}
continue;
} else if (!peek.error && peek.as.success == '*') {
// Skip the *
lexerConsume(lexer);
// Consume characters until */ or EOF
while (true) {
ResultType(char, Nothing) next = lexerConsume(lexer);
if (next.error) break;
if (next.as.success == '\n') {
lineNum++;
DESTROY_ESTR(currentLine);
currentLine = CREATE_ESTR("");
lineStart = lexer->current;
for (size_t i = lineStart; i < lexer->inputsize; i++) {
if (lexer->input[i] == '\n') break;
char tmp[] = {lexer->input[i], '\0'};
APPEND_ESTR(currentLine, tmp);
}
}
if (next.as.success == '*') {
ResultType(char, Nothing) peek2 = lexerPeek(lexer, 1);
if (!peek2.error && peek2.as.success == '/') {
lexerConsume(lexer); // skip /
break;
}
}
}
continue;
}
}
if (chr.as.success == '#' && !inString) {
while (true) {
ResultType(char, Nothing) next = lexerPeek(lexer, 1);
if (next.error || next.as.success == '\n') break;
lexerConsume(lexer);
}
continue;
}
if (chr.as.success == '\n') {
lineNum++;
DESTROY_ESTR(currentLine);
currentLine = CREATE_ESTR("");
lineStart = lexer->current;
for (size_t i = lineStart; i < lexer->inputsize; i++) {
if (lexer->input[i] == '\n') {
break;
}
char buf_tmp[] = {lexer->input[i], '\0'};
APPEND_ESTR(currentLine, buf_tmp);
}
}
if (inString) {
char str[2] = { chr.as.success, '\0' };
APPEND_ESTR(buf, str);
if (chr.as.success == '"') {
inString = false;
}
continue;
}
switch (chr.as.success) {
case '"': {
inString = true;
APPEND_ESTR(buf, "\"");
break;
}
// These characters require themselves added seperately from the previous token.
case '{':
case '}':
case '(':
case ')':
case ',':
case ':':
case ';':
case '\n':
{
ResultType(Nothing, charptr) res = identifyAndAdd(lexer, &buf, &lineNum, &currentLine, chr.as.success, &skipDelimiter);
if (res.error) {
char* err = createLexingError(lineNum, currentLine.str, res.as.error);
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
if (skipDelimiter) break;
char tmp[] = {chr.as.success, '\0'};
ResultType(SolsToken, charptr) result = identifyToken(tmp);
if (result.error) {
char* err = createLexingError(lineNum, currentLine.str, result.as.error);
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
result.as.success.line.num = lineNum;
result.as.success.line.content = malloc(strlen(currentLine.str) + 1);
if (result.as.success.line.content == NULL) {
char* err = createLexingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)");
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
strcpy(result.as.success.line.content, currentLine.str);
addTokenToSolsTokens(&lexer->output, result.as.success);
break;
}
// These characters may be repeated, or followed by an equals sign.
case '+':
case '-': {
ResultType(Nothing, charptr) res = identifyAndAdd(lexer, &buf, &lineNum, &currentLine, chr.as.success, &skipDelimiter);
if (res.error) {
char* err = createLexingError(lineNum, currentLine.str, res.as.error);
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
// skipDelimiter is unlikely here but handled just in case
if (skipDelimiter) break;
ResultType(char, Nothing) next = lexerPeek(lexer, 1);
if (next.error || (next.as.success != chr.as.success && next.as.success != '=')) {
char tmp[] = {chr.as.success, '\0'};
ResultType(SolsToken, charptr) result = identifyToken(tmp);
if (result.error) {
char* err = createLexingError(lineNum, currentLine.str, result.as.error);
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
result.as.success.line.num = lineNum;
result.as.success.line.content = malloc(strlen(currentLine.str) + 1);
if (result.as.success.line.content == NULL) {
char* err = createLexingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)");
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
strcpy(result.as.success.line.content, currentLine.str);
addTokenToSolsTokens(&lexer->output, result.as.success);
}
if (next.as.success == '=') {
char tmp[] = {chr.as.success, '=', '\0'};
ResultType(SolsToken, charptr) result = identifyToken(tmp);
if (result.error) {
char* err = createLexingError(lineNum, currentLine.str, result.as.error);
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
result.as.success.line.num = lineNum;
result.as.success.line.content = malloc(strlen(currentLine.str) + 1);
if (result.as.success.line.content == NULL) {
char* err = createLexingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)");
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
strcpy(result.as.success.line.content, currentLine.str);
addTokenToSolsTokens(&lexer->output, result.as.success);
lexerConsume(lexer);
}
if (next.as.success == chr.as.success) {
char tmp[] = {chr.as.success, chr.as.success, '\0'};
ResultType(SolsToken, charptr) result = identifyToken(tmp);
if (result.error) {
char* err = createLexingError(lineNum, currentLine.str, result.as.error);
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
result.as.success.line.num = lineNum;
result.as.success.line.content = malloc(strlen(currentLine.str) + 1);
if (result.as.success.line.content == NULL) {
char* err = createLexingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)");
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
strcpy(result.as.success.line.content, currentLine.str);
addTokenToSolsTokens(&lexer->output, result.as.success);
lexerConsume(lexer);
}
break;
}
// These characters may be followed by an equals sign, or nothing else.
case '=':
case '!':
case '>':
case '<':
case '*':
case '/': {
ResultType(Nothing, charptr) res = identifyAndAdd(lexer, &buf, &lineNum, &currentLine, chr.as.success, &skipDelimiter);
if (res.error) {
char* err = createLexingError(lineNum, currentLine.str, res.as.error);
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
if (skipDelimiter) break;
ResultType(char, Nothing) next = lexerPeek(lexer, 1);
if (next.error || next.as.success != '=') {
char tmp[] = {chr.as.success, '\0'};
ResultType(SolsToken, charptr) result = identifyToken(tmp);
if (result.error) {
char* err = createLexingError(lineNum, currentLine.str, result.as.error);
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
result.as.success.line.num = lineNum;
result.as.success.line.content = malloc(strlen(currentLine.str) + 1);
if (result.as.success.line.content == NULL) {
char* err = createLexingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)");
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
strcpy(result.as.success.line.content, currentLine.str);
addTokenToSolsTokens(&lexer->output, result.as.success);
}
if (next.as.success == '=') {
char tmp[] = {chr.as.success, '=', '\0'};
ResultType(SolsToken, charptr) result = identifyToken(tmp);
if (result.error) {
char* err = createLexingError(lineNum, currentLine.str, result.as.error);
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
result.as.success.line.num = lineNum;
result.as.success.line.content = malloc(strlen(currentLine.str) + 1);
if (result.as.success.line.content == NULL) {
char* err = createLexingError(lineNum, currentLine.str, "Couldn't allocate memory to store line information in token (in lex() function)");
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
strcpy(result.as.success.line.content, currentLine.str);
addTokenToSolsTokens(&lexer->output, result.as.success);
lexerConsume(lexer);
}
break;
}
// '.' requires checking whether it's a number or an identifier after
case '.': {
ResultType(char, Nothing) peek = lexerPeek(lexer, 1);
// If the next character is a digit, then this is a literal, not a member access dot.
if (!peek.error && isdigit(peek.as.success)) {
APPEND_ESTR(buf, ".");
} else {
ResultType(Nothing, charptr) res = identifyAndAdd(lexer, &buf, &lineNum, &currentLine, chr.as.success, &skipDelimiter);
if (res.error) {
char* err = createLexingError(lineNum, currentLine.str, res.as.error);
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
if (!skipDelimiter) {
addTokenToSolsTokens(&lexer->output, (SolsToken) {.type = STT_DOT});
}
}
break;
}
// This whitespace splits the program and does not get appended as it's own token.
case '\t':
case ' ': {
ResultType(Nothing, charptr) res = identifyAndAdd(lexer, &buf, &lineNum, &currentLine, chr.as.success, &skipDelimiter);
if (res.error) {
char* err = createLexingError(lineNum, currentLine.str, res.as.error);
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
break;
}
default: {
char newchar[] = {chr.as.success, '\0'};
APPEND_ESTR(buf, newchar);
break;
}
}
// Check whether we need to parse types
if (strcmp(buf.str, "fun") == 0) {
if (!lexerPeek(lexer, 1).error && lexerPeek(lexer, 1).as.success == '(') {
// do stuff
}
}
if (strcmp(buf.str, "template") == 0 ) {
if (!lexerPeek(lexer, 1).error && lexerPeek(lexer, 1).as.success == '(') {
}
}
if (strcmp(buf.str, "object") == 0 ) {
if (!lexerPeek(lexer, 1).error && lexerPeek(lexer, 1).as.success == '(') {
}
}
}
ResultType(Nothing, charptr) res = identifyAndAdd(lexer, &buf, &lineNum, &currentLine, '\0', &skipDelimiter);
if (res.error) {
char* err = createLexingError(lineNum, currentLine.str, res.as.error);
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
if (inString) {
char* err = createLexingError(lineNum, currentLine.str, "Unterminated string");
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
DESTROY_ESTR(buf);
DESTROY_ESTR(currentLine);
return Success(Nothing, charptr, (Nothing){});
}
ResultType(Nothing, charptr) processTypeSignature(SolsLexer* lexer) {
return Error(Nothing, charptr, "WIP (in processTypeSignature() function)");
}

View File

@@ -1,76 +0,0 @@
#ifndef LEXER_H
#define LEXER_H
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "../include/error.h"
#include "../include/nothing.h"
#include "SolsType.h"
#include "SolsToken.h"
#include "SolsLiteral.h"
// A map containing all corresponding strs and token types.
// Use the getTokenType() function to search this
extern struct _SolsTokenTypeMap {char* str; SolsTokenType type;} SolsTokenTypeMap[];
// 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;
size_t inputsize;
SolsTokens output;
size_t current;
} SolsLexer;
Result(SolsLexer, charptr);
// Creates a lexer for use by the lex() function.
// Returns:
// Success: Constructed SolsLexer
// Failure: char* detailing what went wrong (usually memory failure)
ResultType(SolsLexer, charptr) createLexer(char* input);
// Uses the provided lexer to scan the code, and create tokens.
// Returne:
// Success: Nothing
// Failure: char* detailing what went wrong (usually user failure or memory failure)
ResultType(Nothing, charptr) lex(SolsLexer* lexer);
Result(char, Nothing);
// Peeks at the next token in the lexer.
// Returns:
// Success: The token with offset ahead
// Failure: Nothing (requested character is out of bounds)
ResultType(char, Nothing) lexerPeek(SolsLexer* lexer, size_t ahead);
// Consumes the next token in the lexer.
// Success: The token that has just been consumed
// Failure: Nothing (requested character is out of bounds)
ResultType(char, Nothing) lexerConsume(SolsLexer* lexer);
// Helper function to classify tokens
// Returns:
// Success: A SolsToken which has all information needed from the token.
// Failure: char* detailing what went wrong (usually memory failure)
ResultType(SolsToken, charptr) identifyToken(const char* token);
Result(SolsTokenType, Nothing);
// Helper function to convert a char* into a SolsTokenType using the SolsTokenTypeMap.
// Returns:
// Success: The corresponding SolsTokenType
// Failure: Nothing (meaning the token is likely an identifier)
ResultType(SolsTokenType, Nothing) getTokenType(const char* input);
// Helper function to lex type signatures into tokens
// FIXME this function is a work in progress
ResultType(Nothing, charptr) processTypeSignature(SolsLexer* lexer);
#endif

97
src/lexer/lexer.sols Normal file
View File

@@ -0,0 +1,97 @@
use collections
use string
//
// Token type enum.
//
// Each token can be one of these types.
//
enum SolsToken {
Identifier, Literal, Type,
Dot, OpenCurly, CloseCurly, OpenParen, CloseParen, Comma,
OpAdd, OpSub, OpMul, OpDiv, OpAddTo, OpSubTo, OpMulTo, OpDivTo, OpIncrement, OpDecrement, OpSet,
OpGreater, OpLesser, OpEqual, OpInequal, OpEqGreater, OpEqLesser,
KwDef, KwLambda, KwReturn, KwUse, KwStruct, KwEnum, KwConstructor,
KwDestructor, KwDuplicator, KwPrivate, KwProtected, KwPuts, KwIf,
KwWhile, KwNew, KwGround,
LineEnd,
}
//
// Token type map.
//
// If a token matches one of the strings in this hashmap,
// its type gets set to the token type for that string.
//
KEYWORDS = Hashmap()
KEYWORDS.set("puts", SolsToken.KwPuts)
KEYWORDS.set("if", SolsToken.KwIf)
KEYWORDS.set("while", SolsToken.KwWhile)
KEYWORDS.set("def", SolsToken.KwDef)
KEYWORDS.set("lambda", SolsToken.KwLambda)
KEYWORDS.set("return", SolsToken.KwReturn)
KEYWORDS.set("use", SolsToken.KwUse)
KEYWORDS.set("struct", SolsToken.KwStruct)
KEYWORDS.set("enum", SolsToken.KwEnum)
KEYWORDS.set("constructor", SolsToken.KwConstructor)
KEYWORDS.set("destructor", SolsToken.KwDestructor)
KEYWORDS.set("duplicator", SolsToken.KwDuplicator)
KEYWORDS.set("private", SolsToken.KwPrivate)
KEYWORDS.set("protected", SolsToken.KwProtected)
KEYWORDS.set("ground", SolsToken.KwGround)
KEYWORDS.set("new", SolsToken.KwNew)
KEYWORDS.set("{", SolsToken.OpenCurly)
KEYWORDS.set("}", SolsToken.CloseCurly)
KEYWORDS.set("(", SolsToken.OpenParen)
KEYWORDS.set(")", SolsToken.CloseParen)
KEYWORDS.set("+", SolsToken.OpAdd)
KEYWORDS.set("-", SolsToken.OpSub)
KEYWORDS.set("*", SolsToken.OpMul)
KEYWORDS.set("/", SolsToken.OpDiv)
KEYWORDS.set("=", SolsToken.OpSet)
KEYWORDS.set("+=", SolsToken.OpAddTo)
KEYWORDS.set("-=", SolsToken.OpSubTo)
KEYWORDS.set("*=", SolsToken.OpMulTo)
KEYWORDS.set("/=", SolsToken.OpDivTo)
KEYWORDS.set("++", SolsToken.OpIncrement)
KEYWORDS.set("--", SolsToken.OpDecrement)
KEYWORDS.set("==", SolsToken.OpEqual)
KEYWORDS.set("!=", SolsToken.OpInequal)
KEYWORDS.set(">", SolsToken.OpGreater)
KEYWORDS.set("<", SolsToken.OpLesser)
KEYWORDS.set(">=", SolsToken.OpEqGreater)
KEYWORDS.set("<=", SolsToken.OpEqLesser)
KEYWORDS.set("\n", SolsToken.LineEnd)
KEYWORDS.set(";", SolsToken.LineEnd)
KEYWORDS.set(",", SolsToken.Comma)
struct SolsLexer {
sourceCode = "x = 123"
current = 0
private def getTokenType(string input) SolsToken {
return KEYWORDS.get(input)
}
def lex() {
inString = false
lineNum = 1
lineStart = 0
currentLine = ""
while lineStart < string_Length() {
}
}
}
lexer = new SolsLexer
puts lexer.getTokenType("if")

View File

@@ -1,266 +0,0 @@
#include "lexer/SolsType.h"
#include "lexer/lexer.h"
#include "typeparser/typeparser.h"
#include "parser/parser.h"
#include "codegen/codegen.h"
#include "include/error.h"
#include "include/estr.h"
#include <stdio.h>
#ifdef _WIN32
#include <direct.h>
#else
#include <sys/stat.h>
#endif
#include <groundvm.h>
typedef enum SolsAction {
SA_PRINT, SA_EXEC, SA_BYTECODE, SA_COMPILE, SA_EXIT, SA_EXITOK
} SolsAction;
typedef struct Args {
SolsAction action;
char* inputFile;
char* outputFile;
} Args;
Args parseArgs(int argc, char** argv) {
Args args = {
.action = SA_EXEC,
.inputFile = NULL,
.outputFile = NULL
};
for (int i = 0; i < argc; i++) {
if (strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
printf("Solstice programming language\n");
printf("Usage: %s <file> [-h] [--help] [-p] [--print] [-b <file>] [--bytecode <file>] [-c <file>] [--compile <file>]\n", argv[0]);
printf("Args:\n");
printf(" <file>: Solstice source file\n");
printf(" -h or --help: Prints this help message and exits\n");
printf(" -p or --print: Prints textual version of Ground bytecode to console\n");
printf(" -b <file> or --bytecode <file>: Generates Ground bytecode (.grbc) and saves it to the provided filename\n");
printf(" -c <file> or --compile <file>: Compiles Ground to Linux x86_64 assembly, outputs a binary to the provided filename (experimental)\n");
printf(" If no extra arguments are provided, the generated Ground bytecode will be executed.\n");
args.action = SA_EXIT;
return args;
}
else if (strcmp("-p", argv[i]) == 0 || strcmp("--print", argv[i]) == 0) {
args.action = SA_PRINT;
}
else if (strcmp("-b", argv[i]) == 0 || strcmp("--bytecode", argv[i]) == 0) {
if (args.action != SA_EXEC) {
printf("Expecting only one action\n");
args.action = SA_EXIT;
return args;
}
args.action = SA_BYTECODE;
if (i + 1 >= argc) {
printf("Expecting file name after %s\n", argv[i]);
args.action = SA_EXIT;
return args;
}
i++;
args.outputFile = argv[i];
}
else if (strcmp("-c", argv[i]) == 0 || strcmp("--compile", argv[i]) == 0) {
if (args.action != SA_EXEC) {
printf("Expecting only one action\n");
args.action = SA_EXIT;
return args;
}
args.action = SA_COMPILE;
if (i + 1 >= argc) {
printf("Expecting file name after %s\n", argv[i]);
args.action = SA_EXIT;
return args;
}
i++;
args.outputFile = argv[i];
}
else {
args.inputFile = argv[i];
}
}
if (args.inputFile == NULL) {
printf("Usage: %s <file> [-h] [--help] [-p] [--print] [-b <file>] [--bytecode <file>] [-c <file>] [--compile <file>]\n", argv[0]);
args.action = SA_EXIT;
return args;
}
return args;
}
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);
return NULL;
}
fseek(fp, 0L, SEEK_END);
lSize = ftell(fp);
rewind(fp);
file = calloc(1, lSize + 1);
if (!file) {
fclose(fp);
return NULL;
}
if (1!=fread(file, lSize, 1, fp)) {
fclose(fp);
free(file);
return NULL;
}
// we done
fclose(fp);
return file;
}
int main(int argc, char** argv) {
Args args = parseArgs(argc, argv);
if (args.action == SA_EXIT) {
return 1;
}
if (args.action == SA_EXITOK) {
return 0;
}
char* fileContents = getFileContents(args.inputFile);
if (fileContents == NULL) {
printf("Couldn't read that file :(\n");
return 1;
}
// Lex file
ResultType(SolsLexer, charptr) lexer = createLexer(fileContents);
if (lexer.error) {
printf("Error while creating lexer: %s", lexer.as.error);
return 1;
}
ResultType(Nothing, charptr) lexed = lex(&lexer.as.success);
if (lexed.error) {
printf("%s\n", lexed.as.error);
return 1;
}
// Detect and parse types
ResultType(SolsTokens, charptr) typed = addTypeInfo(&lexer.as.success.output);
if (typed.error) {
printf("%s\n", typed.as.error);
return 1;
}
// Parse file
ResultType(SolsParser, charptr) parser = createSolsParser(&typed.as.success);
if (parser.error) {
printf("Error while creating parser: %s\n", parser.as.error);
return 1;
}
ResultType(Nothing, charptr) parsed = parse(&parser.as.success);
if (parsed.error) {
printf("%s\n", parsed.as.error);
return 1;
}
SolsScope scope = {
.variables = NULL,
.tmpCounter = 0,
.returnType = createSolsType(STT_INT).as.success
};
// Do codegen on root node
ResultType(GroundProgram, charptr) codegen = generateCode(&parser.as.success.output, &scope);
if (codegen.error) {
printf("%s\n", codegen.as.error);
return 1;
}
switch (args.action) {
case SA_PRINT: {
groundPrintProgram(&codegen.as.success);
break;
}
case SA_EXEC: {
GroundValue retval = groundRunProgram(&codegen.as.success);
if (retval.type == INT) {
return retval.data.intVal;
} else {
return 0;
}
break;
}
case SA_BYTECODE: {
serializeProgramToFile(args.outputFile, &codegen.as.success);
break;
}
case SA_COMPILE: {
char* compiled = groundCompileProgram(&codegen.as.success);
// Make work directory
Estr dir = CREATE_ESTR(".solsbuild_");
APPEND_ESTR(dir, args.outputFile);
#ifdef _WIN32
int dirstatus = _mkdir(estr.str);
#else
int dirstatus = mkdir(dir.str, 0755);
#endif
if (dirstatus != 0) {
printf("Couldn't create temporary work directory\n");
return 1;
}
// Write generated asm to .s
Estr tmpasm = CREATE_ESTR(dir.str);
APPEND_ESTR(tmpasm, "/generated.s");
FILE* file = fopen(tmpasm.str, "w");
if (file == NULL) {
printf("Couldn't write to temporary assembly file");
}
fprintf(file, "%s", compiled);
fclose(file);
// Assemble with nasm
Estr nasmcmd = CREATE_ESTR("nasm -f elf64 -o ");
APPEND_ESTR(nasmcmd, dir.str);
APPEND_ESTR(nasmcmd, "/generated.o ");
APPEND_ESTR(nasmcmd, tmpasm.str);
int nasmstatus = system(nasmcmd.str);
if (nasmstatus != 0) {
printf("command \"%s\" exited with code %d\n", nasmcmd.str, nasmstatus);
return 1;
}
// Link with ld
Estr ldcmd = CREATE_ESTR("ld -o ");
APPEND_ESTR(ldcmd, args.outputFile);
APPEND_ESTR(ldcmd, " ");
APPEND_ESTR(ldcmd, dir.str);
APPEND_ESTR(ldcmd, "/generated.o");
int ldstatus = system(ldcmd.str);
if (ldstatus != 0) {
printf("command \"%s\" exited with code %d\n", ldcmd.str, ldstatus);
return 1;
}
// Yay we compiled it
break;
}
}
return 0;
}

0
src/main.sols Normal file
View File

View File

@@ -1,70 +0,0 @@
#include "SolsNode.h"
#include <stdarg.h>
#include <stdio.h>
#include "../include/error.h"
#include "../lexer/SolsLiteral.h"
#include "../lexer/SolsType.h"
ResultType(SolsNode, charptr) createSolsNode(SolsNodeType type, ...) {
va_list args;
va_start(args, type);
SolsNode node = {
.type = type,
.children.capacity = 32,
.children.count = 0,
.children.at = malloc(sizeof(SolsNode) * 32)
};
if (node.children.at == NULL) {
return Error(SolsNode, charptr, "Failed to allocate memory for children (in createSolsNode() function)");
}
switch (type) {
case SNT_LITERAL: {
node.as.literal = va_arg(args, SolsLiteral);
break;
}
case SNT_TYPE: {
node.as.type = va_arg(args, SolsType);
break;
}
case SNT_IDENTIFIER: {
node.as.idName = va_arg(args, char*);
break;
}
case SNT_GROUND: {
node.as.inlineGround = va_arg(args, char*);
break;
}
default: break;
}
va_end(args);
return Success(SolsNode, charptr, node);
}
ResultType(Nothing, charptr) addChildToSolsNode(SolsNode* parent, SolsNode child) {
if (parent->children.count + 1 >= parent->children.capacity) {
parent->children.capacity *= 2;
SolsNode* tmp = realloc(parent->children.at, sizeof(SolsNode) * parent->children.capacity);
if (tmp == NULL) {
return Error(Nothing, charptr, "Failed to increase memory for children (in addChildToSolsNode() function)");
}
parent->children.at = tmp;
}
parent->children.at[parent->children.count] = deepCopySolsNode(child);
parent->children.count++;
return Success(Nothing, charptr, {});
}
SolsNode deepCopySolsNode(SolsNode node) {
SolsNode copy = node;
copy.children.at = malloc(sizeof(SolsNode) * node.children.capacity);
for (size_t i = 0; i < node.children.count; i++) {
copy.children.at[i] = deepCopySolsNode(node.children.at[i]);
}
return copy;
}

View File

@@ -1,65 +0,0 @@
#ifndef SOLSNODE_H
#define SOLSNODE_H
#include <stdarg.h>
#include <groundvm.h>
#include "../include/error.h"
#include "../lexer/SolsType.h"
#include "../lexer/SolsLiteral.h"
#include "../lexer/SolsToken.h"
typedef enum SolsNodeType {
SNT_IDENTIFIER, SNT_LITERAL, SNT_TYPE, SNT_CODE_BLOCK, SNT_OP_ADD, SNT_OP_SUB, SNT_OP_MUL, SNT_OP_DIV, SNT_OP_ADDTO, SNT_OP_SUBTO, SNT_OP_MULTO, SNT_OP_DIVTO, SNT_OP_INCREMENT, SNT_OP_DECREMENT, SNT_OP_SET, SNT_OP_GREATER, SNT_OP_LESSER, SNT_OP_EQUAL, SNT_OP_INEQUAL, SNT_OP_EQGREATER, SNT_OP_EQLESSER, SNT_DEF, SNT_LAMBDA, SNT_FUNCTION_CALL, SNT_RETURN, SNT_USE, SNT_STRUCT, SNT_PUTS, SNT_IF, SNT_WHILE, SNT_NEW, SNT_GROUND, SNT_ROOT
} SolsNodeType;
struct SolsNode;
// Represents an AST node.
// Most node types use the .type and .children fields, however some nodes require storing
// more data, inside the .as union.
// Those tokens are:
// SNT_LITERAL: A literal value. Uses field .as.literal
// SNT_TYPE: A type descriptor. Uses field .as.type
// SNT_IDENTIFIER: An identifier. Uses field .as.idName
// SNT_GROUND: Ground code embedded inside Solstice. Uses field .as.inlineGround
typedef struct SolsNode {
SolsNodeType type;
union {
SolsLiteral literal;
SolsType type;
char* idName;
char* inlineGround;
} as;
struct {
size_t count;
size_t capacity;
struct SolsNode* at;
} children;
LineInfo line;
// Used by the code generator, unless value is a literal do not use in parser
GroundArg accessArg;
} SolsNode;
Result(SolsNode, charptr);
// Creates a SolsNode. If the type passed in is SNT_LITERAL, SNT_TYPE, SNT_IDENTIFIER or
// SNT_KW_GROUND, the function expects another argument, corresponding to the data type
// the token holds. See the SolsNode struct for more information.
// Returns:
// Success: The created SolsNode
// Failure: char* detailing what went wrong (usually memory failure)
ResultType(SolsNode, charptr) createSolsNode(SolsNodeType type, ...);
// Adds a child to a SolsNode.
// Returns:
// Success: Nothing
// Failure: char* detailing what went wrong (usually memory failure)
ResultType(Nothing, charptr) addChildToSolsNode(SolsNode* parent, SolsNode child);
// Deep copies a SolsNode
SolsNode deepCopySolsNode(SolsNode node);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,99 +0,0 @@
#ifndef PARSER_H
#define PARSER_H
#include "SolsNode.h"
#include "../lexer/SolsToken.h"
#include "../include/error.h"
#include "../include/nothing.h"
// Defines precedence for different tokens.
// Lower number means higher priority.
// Any operation involving same or higher precedence than
// the current token's precedence should be processed first
// (i.e. placed further down the tree)
typedef enum SolsTokenPrecedence {
STP_NEWLINE = 0,
STP_PUTS = 1,
STP_IF = 1,
STP_WHILE = 1,
STP_COMPARE = 2,
STP_SET = 3,
STP_FUNCTION = 4,
STP_ADD = 5,
STP_MUL = 6,
STP_OTHER = 7,
} SolsTokenPrecedence;
// Gets the precedence of the provided token.
SolsTokenPrecedence getPrecedence(SolsToken* token);
// Holds information about the parser.
// .input is lexed tokens, produced by a lexer.
// .current is the token currently being parsed.
// .output is the final product of the parser.
// .currentParent points to the current node being processed
// .errors holds any errors generated during parsing
typedef struct SolsParser {
SolsTokens* input;
size_t current;
SolsNode output;
SolsNode* currentParent;
struct {
size_t count;
size_t capacity;
char** at;
} errors;
} SolsParser;
Result(SolsParser, charptr);
// Creates a SolsParser.
// Returns:
// Success: Constructed SolsParser
// Failure: char* detailing what went wrong (usually memory failure)
ResultType(SolsParser, charptr) createSolsParser(SolsTokens* input);
// Parses the tokens in the SolsParser into an AST.
// Returns:
// Success: Nothing (output is stored in the passed SolsLexer)
// Failure: char* detailing what went wrong (usually user error)
ResultType(Nothing, charptr) parse(SolsParser* parser);
Result(SolsNode, Nothing);
// Parses one singular node and returns it.
// Returns:
// Success: The parsed SolsNode
// Failure: Nothing (out of bounds, or an error)
Result(SolsToken, Nothing);
// Peeks at a token at a specific index in the lexer, 0 being the first token.
// Returns:
// Success: The requested token
// Failure: Nothing (token is out of bounds)
ResultType(SolsToken, Nothing) parserLookAt(SolsParser* parser, size_t where);
// Peeks at future tokens in the parser, 0 meaning current token, 1 the next.
// Returns:
// Success: The requested token
// Failure: Nothing (token is out of bounds)
ResultType(SolsToken, Nothing) parserPeek(SolsParser* parser, int64_t ahead);
// Consumes the next token in the parser.
// Returns:
// Success: The requested token
// Failure: Nothing (we have reached the end of the tokens passed)
ResultType(SolsToken, Nothing) parserConsume(SolsParser* parser);
// Macro for cleaner handling of each token type in the parser.
// Calls functions and returns errors for you! Such amazing
#define PARSER_HANDLE(tokentype) {\
ResultType(Nothing, charptr) __result = parse##tokentype(parser);\
if (__result.error) {\
createParserError(parser, __result.as.error);\
}\
break;\
}
#endif

View File

@@ -1,230 +0,0 @@
#include "typeparser.h"
#include "../lexer/SolsToken.h"
#include "../include/error.h"
#include <string.h>
ResultType(SolsType, charptr) parseType(SolsTokens* in, size_t* index) {
if (*index >= in->count) {
return Error(SolsType, charptr, "Unexpected end of tokens while parsing type");
}
SolsToken* token = &in->at[*index];
if (token->type == STT_TYPE) {
// It's already a basic type token
ResultType(SolsType, charptr) res = copySolsType(&token->as.type);
(*index)++;
return res;
}
if (token->type == STT_IDENTIFIER) {
if (strcmp(token->as.idName, "fun") == 0) {
(*index)++; // skip fun
if (*index >= in->count || in->at[*index].type != STT_OPEN_PAREN) {
return Error(SolsType, charptr, "Expected '(' after 'fun' in type signature");
}
(*index)++; // skip (
ResultType(SolsType, charptr) funTypeRes = createSolsType(STT_FUN);
if (funTypeRes.error) return funTypeRes;
SolsType funType = funTypeRes.as.success;
if (*index < in->count && in->at[*index].type != STT_CLOSE_PAREN) {
for (;;) {
ResultType(SolsType, charptr) argType = parseType(in, index);
if (argType.error) {
freeSolsType(&funType);
return argType;
}
addChildToSolsType(&funType, argType.as.success, NULL);
freeSolsType(&argType.as.success); // addChildToSolsType copies it
if (*index < in->count && in->at[*index].type == STT_COMMA) {
(*index)++; // skip comma
} else {
break;
}
}
}
if (*index >= in->count || in->at[*index].type != STT_CLOSE_PAREN) {
freeSolsType(&funType);
return Error(SolsType, charptr, "Expected ')' after function arguments in type signature");
}
(*index)++; // skip )
// Return type
ResultType(SolsType, charptr) retType = parseType(in, index);
if (retType.error) {
freeSolsType(&funType);
return retType;
}
// Allocate memory for returnType pointer
funType.returnType = malloc(sizeof(SolsType));
if (funType.returnType == NULL) {
freeSolsType(&funType);
freeSolsType(&retType.as.success);
return Error(SolsType, charptr, "Couldn't allocate memory (in parseType() function)");
}
*funType.returnType = retType.as.success;
return Success(SolsType, charptr, funType);
}
if (strcmp(token->as.idName, "object") == 0 || strcmp(token->as.idName, "template") == 0) {
bool isTemplate = strcmp(token->as.idName, "template") == 0;
(*index)++; // skip object/template
if (*index >= in->count || in->at[*index].type != STT_OPEN_PAREN) {
return Error(SolsType, charptr, "Expected '(' after object/template in type signature");
}
(*index)++; // skip (
ResultType(SolsType, charptr) complexTypeRes = createSolsType(isTemplate ? STT_TEMPLATE : STT_OBJECT);
if (complexTypeRes.error) return complexTypeRes;
SolsType complexType = complexTypeRes.as.success;
if (*index < in->count && in->at[*index].type != STT_CLOSE_PAREN) {
for (;;) {
ResultType(SolsType, charptr) fieldType = parseType(in, index);
if (fieldType.error) {
freeSolsType(&complexType);
return fieldType;
}
char* fieldName = NULL;
if (*index < in->count && in->at[*index].type == STT_IDENTIFIER) {
fieldName = in->at[*index].as.idName;
(*index)++;
}
addChildToSolsType(&complexType, fieldType.as.success, fieldName);
freeSolsType(&fieldType.as.success);
if (*index < in->count && in->at[*index].type == STT_COMMA) {
(*index)++; // skip comma
} else {
break;
}
}
}
if (*index >= in->count || in->at[*index].type != STT_CLOSE_PAREN) {
freeSolsType(&complexType);
return Error(SolsType, charptr, "Expected ')' in type signature");
}
(*index)++; // skip )
return Success(SolsType, charptr, complexType);
}
// Handle user-defined types (as identifiers)
// For now, let's just create an OBJECT type with identifierType set
ResultType(SolsType, charptr) userTypeRes = createSolsType(STT_OBJECT);
if (userTypeRes.error) return userTypeRes;
SolsType userType = userTypeRes.as.success;
userType.identifierType = malloc(strlen(token->as.idName) + 1);
if (userType.identifierType == NULL) {
freeSolsType(&userType);
return Error(SolsType, charptr, "Couldn't allocate memory (in parseType() function)");
}
strcpy(userType.identifierType, token->as.idName);
(*index)++;
return Success(SolsType, charptr, userType);
}
return Error(SolsType, charptr, "Unexpected token while parsing type");
}
ResultType(SolsTokens, charptr) addTypeInfo(SolsTokens* in) {
ResultType(SolsTokens, charptr) tokensres = createSolsTokens();
if (tokensres.error) {
return tokensres;
}
SolsTokens tokens = tokensres.as.success;
for (size_t i = 0; i < in->count; ) {
if (in->at[i].type == STT_IDENTIFIER &&
(strcmp(in->at[i].as.idName, "fun") == 0 ||
strcmp(in->at[i].as.idName, "object") == 0 ||
strcmp(in->at[i].as.idName, "template") == 0)) {
size_t startIndex = i;
ResultType(SolsType, charptr) typeRes = parseType(in, &i);
if (typeRes.error) {
// For now, return error
return Error(SolsTokens, charptr, typeRes.as.error);
}
ResultType(SolsToken, charptr) tokenRes = createSolsToken(STT_TYPE, typeRes.as.success);
if (tokenRes.error) {
freeSolsType(&typeRes.as.success);
return Error(SolsTokens, charptr, tokenRes.as.error);
}
// Add line info from original token
tokenRes.as.success.line.num = in->at[startIndex].line.num;
if (in->at[startIndex].line.content) {
tokenRes.as.success.line.content = malloc(strlen(in->at[startIndex].line.content) + 1);
if (tokenRes.as.success.line.content) {
strcpy(tokenRes.as.success.line.content, in->at[startIndex].line.content);
}
} else {
tokenRes.as.success.line.content = NULL;
}
addTokenToSolsTokens(&tokens, tokenRes.as.success);
// SolsToken now owns typeRes.as.success and its buffers.
} else {
// Need to deep copy the token because addTokenToSolsTokens just copies the struct
// and freeSolsToken will free buffers.
// Wait, if we use the same tokens, it's fine.
// But 'in' tokens might be freed later.
// Actually, we are creating a *new* SolsTokens.
// So we should probably copy the token properly.
SolsToken original = in->at[i];
SolsToken copy = {0};
copy.type = original.type;
copy.line.num = original.line.num;
if (original.line.content) {
copy.line.content = malloc(strlen(original.line.content) + 1);
if (copy.line.content) strcpy(copy.line.content, original.line.content);
}
switch (original.type) {
case STT_IDENTIFIER:
copy.as.idName = malloc(strlen(original.as.idName) + 1);
if (copy.as.idName) strcpy(copy.as.idName, original.as.idName);
break;
case STT_KW_GROUND:
copy.as.inlineGround = malloc(strlen(original.as.inlineGround) + 1);
if (copy.as.inlineGround) strcpy(copy.as.inlineGround, original.as.inlineGround);
break;
case STT_LITERAL:
// Literals also need deep copy?
// SolsLiteral has stringv.
if (original.as.literal.type == SLT_STRING) {
copy.as.literal.type = SLT_STRING;
copy.as.literal.as.stringv = malloc(strlen(original.as.literal.as.stringv) + 1);
if (copy.as.literal.as.stringv) strcpy(copy.as.literal.as.stringv, original.as.literal.as.stringv);
} else {
copy.as.literal = original.as.literal;
}
break;
case STT_TYPE: {
ResultType(SolsType, charptr) copiedType = copySolsType(&original.as.type);
if (!copiedType.error) {
copy.as.type = copiedType.as.success;
}
break;
}
default:
copy.as = original.as;
break;
}
addTokenToSolsTokens(&tokens, copy);
i++;
}
}
return Success(SolsTokens, charptr, tokens);
}

View File

@@ -1,11 +0,0 @@
#ifndef TYPEPARSER_H
#define TYPEPARSER_H
#include "../lexer/SolsToken.h"
#include "../include/error.h"
// Scans a SolsTokens and identifies type signatures, like fun(int) string.
// These are then converted into a single STT_TYPE token.
ResultType(SolsTokens, charptr) addTypeInfo(SolsTokens* in);
#endif

View File

@@ -1,81 +0,0 @@
#include <emscripten.h>
#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#include "lexer/lexer.h"
#include "typeparser/typeparser.h"
#include "parser/parser.h"
#include "codegen/codegen.h"
#include <groundvm.h>
static char out_buf[65536];
static int out_pos = 0;
// Defined by Ground
void wasm_print(const char* str);
EMSCRIPTEN_KEEPALIVE
const char* solstice_run(char* source) {
out_pos = 0;
out_buf[0] = '\0';
// 1. Lex
ResultType(SolsLexer, charptr) lexer = createLexer(source);
if (lexer.error) {
snprintf(out_buf, sizeof(out_buf),
"[lex error] %s", lexer.as.error);
return out_buf;
}
ResultType(Nothing, charptr) lexed = lex(&lexer.as.success);
if (lexed.error) {
snprintf(out_buf, sizeof(out_buf), "%s", lexed.as.error);
return out_buf;
}
// 2. Type parse
ResultType(SolsTokens, charptr) typed = addTypeInfo(&lexer.as.success.output);
if (typed.error) {
snprintf(out_buf, sizeof(out_buf), "%s", typed.as.error);
return out_buf;
}
// 3. Parse
ResultType(SolsParser, charptr) parser = createSolsParser(&typed.as.success);
if (parser.error) {
snprintf(out_buf, sizeof(out_buf),
"[parse error] %s", parser.as.error);
return out_buf;
}
ResultType(Nothing, charptr) parsed = parse(&parser.as.success);
if (parsed.error) {
snprintf(out_buf, sizeof(out_buf), "%s", parsed.as.error);
return out_buf;
}
// 4. Codegen
SolsScope scope = {
.variables = NULL,
.tmpCounter = 0,
.returnType = createSolsType(STT_INT).as.success
};
ResultType(GroundProgram, charptr) codegen =
generateCode(&parser.as.success.output, &scope);
if (codegen.error) {
snprintf(out_buf, sizeof(out_buf), "%s", codegen.as.error);
return out_buf;
}
// 5. Run
GroundValue retval = groundRunProgram(&codegen.as.success);
if (out_pos == 0) {
// Program produced no output — report exit code
snprintf(out_buf, sizeof(out_buf),
"[exited with code %d]",
retval.type == INT ? retval.data.intVal : 0);
}
return out_buf;
}

View File

@@ -1,18 +1,32 @@
use io struct Person {
protected name = ""
private age = 0
struct dingus { def greet() string {
x = 5 return "Hello, " + self.name + "!"
y = "dingus"
} }
e = new dingus def dance() string {
puts e return "Dancing..."
puts dingus }
puts e.x constructor(string name, int age) {
println(e.y) self.name = name
self.age = age
}
e.x = 7 destructor {
e.y = "heheheha" // We don't need to do anything here.
puts e.x }
println(e.y)
}
max = Person("Max", 16)
puts max
puts max.greet()
puts max.name
// puts max.age (causes compile time error, age is private)
// max.name = "Maximilian" (causes compile time error, name is protected)
max.dance()

View File

@@ -5,37 +5,41 @@ if exists("b:current_syntax")
endif endif
" Keywords " Keywords
syn keyword solsticeConditional if syn keyword solsKeyword puts if while def lambda return use struct ground
syn keyword solsticeRepeat while syn keyword solsBool true false
syn keyword solsticeKeyword def struct return use
syn keyword solsticeType int string bool double char
syn keyword solsticeBoolean true false
" Built-in functions " Types
syn keyword solsticeBuiltin puts print println input syn keyword solsType int double string char bool fun template object
" Data Types " Strings and chars
syn match solsticeNumber "\d\+\(\.\d\+\)\=" syn region solsString start=/"/ skip=/\\"/ end=/"/
syn region solsticeString start=/"/ end=/"/ syn region solsChar start=/'/ skip=/\\'/ end=/'/
syn match solsticeCharacter /'[^']'/
" Numbers
syn match solsFloat /\<[0-9]\+\.[0-9]*\>/
syn match solsInt /\<[0-9]\+\>/
" Operators " Operators
syn match solsticeOperator "==\|!=\|>=\|<=\|++\|--\|+\=\|-\=\|\*=\|\/=" syn match solsOperator /+\|-\|\*\|\/\|=\|!\|>\|<\|+=\|-=\|\*=\|\/=\|++\|--\|==\|!=\|>=\|<=/
syn match solsticeOperator "[><=+\-*/]"
" Delimiters " Delimiters
syn match solsticeDelimiter "[{()}]" syn match solsDelimiter /[{}(),;]/
hi def link solsticeConditional Conditional " Comments
hi def link solsticeRepeat Repeat syn match solsComment /\/\/.*$/
hi def link solsticeKeyword Keyword syn match solsComment /#.*$/
hi def link solsticeType Type
hi def link solsticeBoolean Boolean " Highlight links
hi def link solsticeBuiltin Function hi def link solsKeyword Keyword
hi def link solsticeNumber Number hi def link solsBool Boolean
hi def link solsticeString String hi def link solsType Type
hi def link solsticeCharacter Character hi def link solsString String
hi def link solsticeOperator Operator hi def link solsChar Character
hi def link solsticeDelimiter Delimiter hi def link solsFloat Float
hi def link solsInt Number
hi def link solsOperator Operator
hi def link solsDelimiter Delimiter
hi def link solsComment Comment
let b:current_syntax = "solstice" let b:current_syntax = "solstice"