70 Commits

Author SHA1 Message Date
1c0c575494 I forgot to add makefile lol 2026-06-15 17:56:53 +10:00
e2c15b2f49 Repl (broken but it works) 2026-06-08 16:23:23 +10:00
dce308eb3f Start work on else (still buggy) 2026-05-31 13:52:05 +10:00
85d2a0d300 Hexadecimal number support 2026-05-30 19:51:47 +10:00
4ea28537e4 Fix stinky edge case bug 2026-05-30 19:06:00 +10:00
dd1ac9ae71 Fix constructor and field access issues 2026-05-30 17:13:51 +10:00
1da7f91313 Merge pull request 'generics-refactor' (#11) from generics-refactor into master
Reviewed-on: solstice/solstice#11
2026-05-24 16:12:04 +10:00
6a936bf282 Add box and socket wrappers for Solstice 2026-05-21 15:55:54 +10:00
8a8e4381bf Escape codes lmao 2026-05-21 13:49:30 +10:00
b78a5ee7bb Fixes again! I think it works now?? 2026-05-20 18:42:37 +10:00
cbc92b5b1d More fixes maybe? 2026-05-20 15:17:14 +10:00
45c9d9ee37 I think the bug is fixed? 2026-05-19 19:46:45 +10:00
3003605031 Some fixes maybe 2026-05-19 19:29:40 +10:00
c68bf1b662 start work on refactoring generics ig 2026-05-19 18:20:00 +10:00
3c36e92261 WEE WOO WEE WOO GENERICS ARE WORKING I REPEAT GENERICS ARE WORKING WEE
WOO WEE WOO
2026-05-17 21:19:08 +10:00
246a212cb9 Bug fixes 2026-05-17 19:46:18 +10:00
02ecef4ce2 Add updated Makefile 2026-05-10 14:56:18 +10:00
e0869d526e Start work on inbuilt compiler debugger 2026-05-09 14:07:35 +10:00
cba5ec1fa2 Calling functions in nested objects 2026-05-05 09:12:58 +10:00
908af13d8f I forgot to remove the fixme lmao 2026-05-04 19:29:09 +10:00
10046703ed Nested object setting 2026-05-04 19:28:03 +10:00
ebfd1d5045 Add sizeof test 2026-05-04 11:51:31 +10:00
1e0abcc0b4 sizeof operator 2026-05-04 11:51:07 +10:00
c7bd6e7766 Working generics 2026-04-20 18:43:20 +10:00
68f5868538 use files rel. to current file's directory 2026-04-20 15:16:36 +10:00
692dd6b0db Fix static builds 2026-04-17 13:28:02 +10:00
8087a8150e Static build 2026-04-17 13:25:21 +10:00
53908d29e2 woops, lemme delete that 2026-04-17 12:43:23 +10:00
effad2920f removed conversions lib, use the 'as' from now on 2026-04-17 12:42:24 +10:00
ac13b7a0ae Update highlighting 2026-04-17 12:37:11 +10:00
8b40dbd563 Fix generic constructor issue 2026-04-17 12:27:59 +10:00
0cf63b034f Fix some things, may break others 2026-04-16 17:13:55 +10:00
ba6ec79a10 x as y 2026-04-16 12:59:49 +10:00
f261a1cd0e More generics stuff 2026-04-15 16:57:41 +10:00
00bf654882 IT SORTA WORKS 2026-04-15 11:25:52 +10:00
591adf79a4 More work on generics, still no worky 2026-04-15 11:13:38 +10:00
c7d4a7700e Further work on generics 2026-04-14 17:08:44 +10:00
d0d1dc7465 Parse generic initializations 2026-04-14 12:00:20 +10:00
f8afa2f564 Start work on codegen for generics 2026-04-14 11:32:05 +10:00
7a81a47986 finish parsing generics 2026-04-14 09:35:23 +10:00
f127c2f5ab Add generics example 2026-04-14 09:21:30 +10:00
23041c041a merge 2026-04-14 08:52:30 +10:00
0bb3741c66 "as" in struct 2026-04-14 08:49:57 +10:00
af97f1b712 added strings solstice wrapper 2026-04-13 20:02:14 +10:00
dfe37de5c0 Fix duplicators 2026-04-13 18:27:27 +10:00
5d9cb02e7e Parse and codegen duplicator 2026-04-13 14:02:47 +10:00
547488964a Fix an issue with constructors 2026-04-13 13:12:58 +10:00
f0692eb940 added solstice wrappers for fileio and collections 2026-04-13 12:15:47 +10:00
67ea6cc5fc Enums as return types 2026-04-13 12:06:56 +10:00
36030f01a2 Basic enum 2026-04-13 11:53:20 +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
38 changed files with 8059 additions and 299 deletions

View File

@@ -1,5 +1,9 @@
CXX = gcc
CXXFLAGS = -Wall -Wextra -pedantic -O3 -ggdb
GROUND_STATIC = /usr/local/lib/libgroundvm.a
GROUND_INCLUDE = /usr/local/include/
CXXFLAGS = -I$(GROUND_INCLUDE) -Wall -Wextra -pedantic -ggdb
LDFLAGS = -lgroundvm
BUILD_DIR = build
@@ -10,7 +14,7 @@ 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
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 $(SRC_DIR)/interactive/interactive.c $(SRC_DIR)/repl/repl.c $(SRC_DIR)/linenoise/linenoise.c
OBJS = $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(SRCS))
TARGET = solstice
@@ -29,6 +33,9 @@ install: $(TARGET)
install -d $(LIBDIR)
cp -r libs/* $(LIBDIR)/solstice
static: $(OBJS)
$(CXX) $(OBJS) $(GROUND_STATIC) -o $(TARGET)
$(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
@@ -38,9 +45,12 @@ $(BUILD_DIR)/solstice.tar.gz: $(TARGET) $(LIBS_DIR)
package: $(BUILD_DIR)/solstice.tar.gz
$(BUILD_DIR):
mkdir -p $(BUILD_DIR) $(BUILD_DIR)/codegen $(BUILD_DIR)/lexer $(BUILD_DIR)/parser $(BUILD_DIR)/typeparser
mkdir -p $(BUILD_DIR) $(BUILD_DIR)/codegen $(BUILD_DIR)/lexer $(BUILD_DIR)/parser $(BUILD_DIR)/typeparser $(BUILD_DIR)/interactive $(BUILD_DIR)/repl $(BUILD_DIR)/linenoise
clean:
rm -rf $(BUILD_DIR) $(TARGET)
.PHONY: all clean
run: $(TARGET)
./$(TARGET)
.PHONY: all clean run

37
libs/box.sols Normal file
View File

@@ -0,0 +1,37 @@
struct _box {
private ptr = 0
def get() int { return 0 }
def set() int { return 0 }
def free() int { return 0 }
}
ground {
extern "_box"
}
/*
Shared mutable object. Use with caution - may cause impurities!
*/
struct Box[T] {
private box = new _box
def get() T {
retval = new T
ground { callmethod &self &box !get &retval }
return retval
}
def set(T in) int {
ground { callmethod &self &box !set $in &retval }
return 0
}
def free() int {
ground { callmethod &self &box !free &retval }
return 0
}
constructor() {}
}

37
libs/collections.sols Normal file
View File

@@ -0,0 +1,37 @@
struct List {
protected size = 0
protected capacity = 0
protected memSize = 0
private ptr = 0
constructor(int length) {}
def append(int value) int {}
def insert(int value, int index) int {}
def remove(int index) int {}
def at(int index) int {}
def clear() int {}
def set(int value, int index) int {}
def isEmpty() bool {}
def contains(int value) bool {}
def reverse() int {}
def find(int value) int {}
def reserve(int n) int {}
}
struct Hashmap {
private ptr = 0
constructor() {}
def set(string key, int value) int {}
def get(string key) int {}
def getOr(string key, int fallback) int {}
def remove(string key) int {}
def removeIfPresent(string key) bool {}
}
ground {
extern "collections"
}

View File

@@ -1,15 +0,0 @@
def stringToInt(string in) int {
result = 0
ground {
stoi $in &result
}
return result
}
def intToString(int in) string {
result = ""
ground {
tostring $in &result
}
return result
}

View File

@@ -1,5 +1,18 @@
def file_Read(string file) string {}
def file_Write(string file, string content) {}
struct File {
private fileHandle = 0
protected filePath = ""
protected size = 0
protected tell = 0
constructor (string filePath) {}
def read() string {}
def write(string buffer) int {}
def append(string buffer) int {}
def flush() int {}
def seek(int offset) int {}
def seekEnd(int offset) int {}
}
ground {
extern "fileio"

9
libs/socket.sols Normal file
View File

@@ -0,0 +1,9 @@
/*
Function that blocks the main thread which listens to TCP connections.
All connection data will be pass
*/
def socket_Listen(fun(string) string handler, int port) int {}
ground {
extern "socket"
}

28
libs/strings.sols Normal file
View File

@@ -0,0 +1,28 @@
def string_CharAt(string str, int index) string {}
def string_Upper(string str) string {}
def string_Lower(string str) string {}
def string_Trim(string str) string {}
def string_TrimLeft(string str) string {}
def string_TrimRight(string str) string {}
def string_Substring(string str, int start, int end) string {}
def string_Repeat(string str, int times) string {}
def string_Replace(string haystack, string needle) string {}
def string_Reverse(string str) string {}
def string_StartsWith(string str, string prefix) bool {}
def string_EndsWith(string str, string suffix) bool {}
def string_Contains(string haystack, string needle) bool {}
def string_Find(string haystack, string needle) int {}
def string_FindLast(string haystack, string needle) int {}
def string_Count(string haystack, string needle) int {}
def string_isAlpha(string str) bool {}
def string_isAlnum(string str) bool {}
def string_isDigit(string str) bool {}
def string_isSpace(string str) bool {}
ground {
extern "string"
}

View File

@@ -9,7 +9,13 @@ void addVariableToScope(SolsScope* scope, const char* name, SolsType type) {
strncpy(s->id, name, sizeof(s->id) - 1);
s->id[sizeof(s->id) - 1] = '\0';
s->typeinfo = type;
s->typeinfo = ({
ResultType(SolsType, charptr) _result = copySolsType(&type);
if (_result.error) {
return;
}
_result.as.success;
});
HASH_ADD_STR(scope->variables, id, s);
}

File diff suppressed because it is too large Load Diff

71
src/include/list.h Normal file
View File

@@ -0,0 +1,71 @@
// list.h - create a list of any kind in C
// append, pop, safely retrieve
// licenced to you under the MIT license
// example:
//
// #include "list.h"
//
// UseList(int);
//
// int main() {
// List(int) myList = newList(int);
// append(myList, 32);
// printf("%d\n", *get(myList, 0));
// pop(myList);
// destroy(myList);
// }
//
#ifndef LIST_H
#define LIST_H
#include <stdlib.h>
#include <stdio.h>
#define LIST_H_INIT_CAPACITY 32
#define UseList(type) struct __List_##type { type* pointer; size_t count; size_t capacity; size_t itemsize; }
#define List(type) struct __List_##type
#define newList(type) ({ \
List(type) __list_tmp = { .pointer = malloc(sizeof(type) * LIST_H_INIT_CAPACITY), .count = 0, .capacity = LIST_H_INIT_CAPACITY, .itemsize = sizeof(type) };\
if (__list_tmp.pointer == NULL) { \
printf("list.h:32 (newList(type)) - failed to allocate memory"); exit(1);\
}\
__list_tmp;\
})
#define append(list, item) {\
if (list.pointer == NULL) {\
printf("list.h:39 (append(list, item)) - list pointer is null (perhaps you accidently destroyed the list?)"); exit(1);\
}\
if (list.capacity < list.count + 1) {\
list.capacity *= 2;\
void* __tmp_ptr = realloc(list.pointer, list.capacity * list.itemsize);\
if (__tmp_ptr == NULL) {\
printf("list.h:45 (append(list, item)) - failed to allocate memory"); exit(1);\
}\
list.pointer = __tmp_ptr;\
}\
list.pointer[list.count] = item;\
list.count++;\
}
#define get(list, idx) ({\
if (list.pointer == NULL) {\
printf("list.h:55 (get(list, idx)) - list pointer is null (perhaps you accidently destroyed the list?)"); exit(1);\
}\
(idx >= list.count) ? NULL : &list.pointer[idx];\
})
#define pop(list) {\
if (list.pointer == NULL) {\
printf("list.h:65 (pop(list)) - list pointer is null (perhaps you accidently destroyed the list?)"); exit(1);\
}\
if (list.count > 0) list.count--;\
}
#define destroy(list) if (list.pointer != NULL) {\
free(list.pointer); list.pointer = NULL; \
}
#endif

View File

@@ -0,0 +1,77 @@
#include "interactive.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
Interactive createInteractive() {
return (Interactive) {
.callbacks.at = malloc(sizeof(InteractiveCallback) * 16),
.callbacks.capacity = 16,
.callbacks.size = 0
};
}
void addCallback(Interactive* interactive, int (*function)(size_t, char**, void*), char* name) {
if (interactive->callbacks.size + 1 >= interactive->callbacks.capacity) {
interactive->callbacks.at = realloc(interactive->callbacks.at, (interactive->callbacks.capacity * 2) * sizeof(InteractiveCallback));
interactive->callbacks.capacity *= 2;
}
interactive->callbacks.at[interactive->callbacks.size].function = function;
interactive->callbacks.at[interactive->callbacks.size].name = name;
interactive->callbacks.size++;
}
typedef struct InteractiveCommand {
char* command;
size_t argc;
size_t capacity;
char** argv;
} InteractiveCommand;
static inline InteractiveCommand parseInteractiveCommand(char* input) {
InteractiveCommand command = {
.command = malloc(sizeof(char*) * 16),
.argc = 0,
.capacity = 16,
.argv = malloc(sizeof(char*) * 16)
};
for (size_t i = 0; i < command.capacity; i++) {
command.argv[i] = malloc(sizeof(char) * 64);
}
size_t len = strlen(input);
char** currentBuf = &command.command;
size_t currentLen = 0;
for (size_t i = 0; i < len; i++) {
if (input[i] == '\n') break;
if (input[i] == ' ' || currentLen == 63) {
if (command.argc + 1 >= command.capacity) {
command.capacity *= 2;
command.argv = realloc(command.argv, command.capacity * sizeof(char**));
}
currentBuf = &command.argv[command.argc];
command.argc++;
} else {
currentBuf[currentLen][i] = input[i];
}
}
return command;
}
void runInteractive(Interactive* interactive, void* ctx) {
for (;;) {
char buf[512];
printf("> ");
fgets(buf, sizeof(buf), stdin);
InteractiveCommand command = parseInteractiveCommand(buf);
if (strcmp(command.command, "exit") == 0) {
break;
}
for (size_t i = 0; i < interactive->callbacks.size; i++) {
if (strcmp(command.command, interactive->callbacks.at[i].name) == 0) {
interactive->callbacks.at[i].function(command.argc, command.argv, ctx);
}
}
}
}

View File

@@ -0,0 +1,26 @@
#ifndef INTERACTIVE_H
#define INTERACTIVE_H
#include <stdlib.h>
// Callback functions are of the form
// int callback(int argc, char** argv, void* ctx)
typedef struct InteractiveCallback {
int (*function)(size_t, char**, void*);
char* name;
} InteractiveCallback;
typedef struct Interactive {
struct {
InteractiveCallback* at;
size_t size;
size_t capacity;
} callbacks;
} Interactive;
Interactive createInteractive();
void addCallback(Interactive* interactive, int (*function)(size_t, char**, void*), char* name);
void runInteractive(Interactive* interactive, void* ctx);
#endif

View File

@@ -1,6 +1,8 @@
#include "SolsLiteral.h"
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <stdio.h>
ResultType(SolsLiteral, charptr) createSolsLiteral(SolsLiteralType type, ...) {
va_list args;
@@ -47,5 +49,30 @@ ResultType(SolsLiteral, charptr) createSolsLiteral(SolsLiteralType type, ...) {
void freeSolsLiteral(SolsLiteral* lit) {
if (lit->type == SLT_STRING && lit->as.stringv != NULL) {
free(lit->as.stringv);
}
}
}
void printSolsLiteral(SolsLiteral* lit) {
switch (lit->type) {
case SLT_INT: {
printf("%" PRId64, lit->as.intv);
break;
}
case SLT_DOUBLE: {
printf("%f", lit->as.doublev);
break;
}
case SLT_STRING: {
printf("%s", lit->as.stringv);
break;
}
case SLT_BOOL: {
printf(lit->as.boolv ? "true" : "false");
break;
}
case SLT_CHAR: {
printf("%c", lit->as.charv);
break;
}
}
}

View File

@@ -43,4 +43,7 @@ ResultType(SolsLiteral, charptr) createSolsLiteral(SolsLiteralType type, ...);
// Frees a SolsLiteral. Primarily concerned with freeing .as.stringv
void freeSolsLiteral(SolsLiteral* lit);
// Prints a SolsLiteral
void printSolsLiteral(SolsLiteral* lit);
#endif

View File

@@ -10,7 +10,20 @@
#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
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_ENUM, STT_KW_CONSTRUCTOR, STT_KW_DESTRUCTOR, STT_KW_DUPLICATOR,
STT_KW_AS, STT_KW_SIZEOF,
STT_KW_PRIVATE, STT_KW_PROTECTED,
STT_KW_PUTS, STT_KW_IF, STT_KW_ELSE, STT_KW_WHILE, STT_KW_BREAK, STT_KW_CONTINUE,
STT_KW_NEW, STT_KW_GROUND, STT_LINE_END, STT_COMMA,
STT_OPEN_SQUARE, STT_CLOSE_SQUARE,
STT_KW_PRAGMA,
} SolsTokenType;
typedef char* charptr;
@@ -57,7 +70,7 @@ 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
// .count is how many tokens are currently being stored
// .capacity is how many tokens worth of memory is allocated
typedef struct SolsTokens {
SolsToken* at;

View File

@@ -1,5 +1,4 @@
#include "SolsType.h"
#include "lexer.h"
#include "../include/error.h"
#include "../include/estr.h"
#include <groundvm.h>
@@ -10,10 +9,143 @@ ResultType(SolsType, charptr) createSolsType(SolsTypeType in) {
if (ptr == NULL) {
return Error(SolsType, charptr, "Couldn't allocate memory (in createSolsType() function)");
}
SolsType type = { .type = in, .children.capacity = 32, .children.at = ptr };
SolsType type = {
.type = in == STT_GENERIC ? STT_TEMPLATE : in,
.identifierType = NULL,
.returnType = NULL,
.children.capacity = 32,
.children.count = 0,
.children.at = ptr,
.genericChildren.at = NULL,
.genericChildren.capacity = 0,
.genericChildren.count = 0,
.typeIsKnown = true,
.needsGroundStruct = false,
.metadata.isPrivate = false,
.metadata.isProtected = false,
.metadata.isEnum = false,
.metadata.isGenericField = in == STT_GENERIC,
.metadata.isGenericStruct = false,
.metadata.genericStructNode = NULL
};
return Success(SolsType, charptr, type);
}
ResultType(SolsType, charptr) createIdentifiedSolsType(char* in) {
char* copy = malloc(strlen(in) + 1);
if (copy == NULL) {
return Error(SolsType, charptr, "Couldn't allocate memory (in createIdentifiedSolsType() function)");
}
strcpy(copy, in);
return Success(SolsType, charptr, ((SolsType) {
.type = STT_UNKNOWN,
.identifierType = copy,
.returnType = NULL,
.children.capacity = 0,
.children.count = 0,
.children.at = NULL,
.genericChildren.at = NULL,
.genericChildren.capacity = 0,
.genericChildren.count = 0,
.typeIsKnown = false,
.needsGroundStruct = false,
.metadata.isPrivate = false,
.metadata.isProtected = false,
.metadata.isEnum = false,
.metadata.isGenericField = false,
.metadata.isGenericStruct = false,
.metadata.genericStructNode = NULL
}));
}
ResultType(SolsType, charptr) copySolsType(SolsType* type) {
SolsType ret = {
.type = type->type,
.identifierType = NULL,
.typeIsKnown = type->typeIsKnown,
.needsGroundStruct = type->needsGroundStruct,
.returnType = NULL,
.children.count = type->children.count,
.children.capacity = type->children.capacity,
.children.at = NULL,
.genericChildren.count = type->genericChildren.count,
.genericChildren.capacity = type->genericChildren.capacity,
.genericChildren.at = NULL,
.metadata = type->metadata
};
if (type->identifierType != NULL) {
ret.identifierType = malloc(strlen(type->identifierType) + 1);
if (ret.identifierType == NULL) {
return Error(SolsType, charptr, "Couldn't allocate memory (in copySolsType() function)");
}
strcpy(ret.identifierType, type->identifierType);
}
if (type->returnType != NULL && type->returnType->type != STT_NONE) {
ResultType(SolsType, charptr) copiedReturn = copySolsType(type->returnType);
if (copiedReturn.error) {
Estr err = CREATE_ESTR(copiedReturn.as.error);
APPEND_ESTR(err, " (in copySolsType() function)");
return Error(SolsType, charptr, err.str);
}
ret.returnType = malloc(sizeof(SolsType));
if (ret.returnType == NULL) {
return Error(SolsType, charptr, "Couldn't allocate memory (in copySolsType() function)");
}
*ret.returnType = copiedReturn.as.success;
}
if (type->children.capacity > 0) {
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;
}
for (size_t i = 0; i < type->children.count; i++) {
ResultType(SolsType, charptr) copied = copySolsType(&type->children.at[i].type);
if (copied.error) {
Estr err = CREATE_ESTR(copied.as.error);
APPEND_ESTR(err, " (in copySolsType() function)");
return Error(SolsType, charptr, err.str);
}
ret.children.at[i].type = copied.as.success;
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);
}
}
if (type->genericChildren.capacity > 0) {
SolsType* ptr = malloc(sizeof(SolsType) * type->genericChildren.capacity);
if (ptr == NULL) {
return Error(SolsType, charptr, "Couldn't allocate memory (in copySolsType() function)");
}
ret.genericChildren.at = ptr;
}
for (size_t i = 0; i < type->genericChildren.count; i++) {
ResultType(SolsType, charptr) copied = copySolsType(&type->genericChildren.at[i]);
if (copied.error) {
Estr err = CREATE_ESTR(copied.as.error);
APPEND_ESTR(err, " (in copySolsType() function)");
return Error(SolsType, charptr, err.str);
}
ret.genericChildren.at[i] = copied.as.success;
}
return Success(SolsType, charptr, ret);
}
/*
ResultType(SolsType, charptr) copySolsType(SolsType* type) {
SolsType ret = { .type = type->type, .children.count = type->children.count, .children.capacity = type->children.capacity};
@@ -48,6 +180,7 @@ ResultType(SolsType, charptr) copySolsType(SolsType* type) {
}
return Success(SolsType, charptr, ret);
}
*/
ResultType(Nothing, charptr) addChildToSolsType(SolsType* type, SolsType child, const char* name) {
if (type->children.capacity < type->children.count + 1) {
@@ -144,7 +277,7 @@ bool compareTypes(SolsType* left, SolsType* right) {
}
}
ResultType(GroundArg, charptr) createGroundArgFromSolsType(SolsType* type) {
ResultType(GroundArg, charptr) createGroundArgFromSolsType(SolsType* type, struct SolsScope* scope) {
switch (type->type) {
case STT_INT: {
return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "int"));
@@ -168,9 +301,133 @@ ResultType(GroundArg, charptr) createGroundArgFromSolsType(SolsType* type) {
return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "struct"));
}
case STT_OBJECT: {
// FIXME Do this later
return Error(GroundArg, charptr, "FIXME");
if (!type->needsGroundStruct) {
return Success(GroundArg, charptr, groundCreateReference(TYPEREF, type->identifierType));
} else {
// FIXME do this later
return Error(GroundArg, charptr, "Anonymous structs are not supported yet");
}
}
case STT_UNKNOWN: {
if (!type->needsGroundStruct) {
return Success(GroundArg, charptr, groundCreateReference(TYPEREF, type->identifierType));
} else {
// FIXME do this later
return Error(GroundArg, charptr, "Anonymous structs are not supported yet");
}
}
case STT_NONE: {
return Success(GroundArg, charptr, groundCreateReference(TYPEREF, "none"));
}
case STT_GENERIC: {
Estr typeName = CREATE_ESTR(type->identifierType);
APPEND_ESTR(typeName, "_SOLS_GENERIC_");
for (size_t i = 0; i < type->genericChildren.count; i++) {
ResultType(GroundArg, charptr) arg = createGroundArgFromSolsType(&type->genericChildren.at[i], scope);
if (arg.error) {
return Error(GroundArg, charptr, arg.as.error);
}
APPEND_ESTR(typeName, arg.as.success.value.refName);
APPEND_ESTR(typeName, "_");
}
return Success(GroundArg, charptr, groundCreateReference(TYPEREF, typeName.str));
}
}
return Error(GroundArg, charptr, "How did we get here?");
}
ResultType(SolsType, charptr) findStructMemberType(SolsType* type, char* member) {
for (size_t i = 0; i < type->children.count; i++) {
if (strcmp(type->children.at[i].name, member) == 0) {
return Success(SolsType, charptr, type->children.at[i].type);
}
}
return Error(SolsType, charptr, "Could not find member");
}
void printSolsType(SolsType* type) {
printf("Type: ");
switch (type->type) {
case STT_INT:
printf("int");
break;
case STT_DOUBLE:
printf("double");
break;
case STT_STRING:
printf("string");
break;
case STT_BOOL:
printf("bool");
break;
case STT_CHAR:
printf("char");
break;
case STT_FUN:
printf("fun");
break;
case STT_TEMPLATE:
printf("template");
break;
case STT_OBJECT:
printf("object");
break;
case STT_UNKNOWN:
printf("(unknown)");
break;
case STT_NONE:
printf("(none)");
break;
case STT_GENERIC:
printf("(generic");
break;
}
if (type->returnType != NULL) {
printf("\nReturn type: ");
printSolsType(type->returnType);
}
if (type->children.count > 0) {
printf("\nChildren {");
for (size_t i = 0; i < type->children.count; i++) {
if (type->children.at[i].name != NULL) {
printf("\nName: %s", type->children.at[i].name);
}
printf("\nType: ");
printSolsType(&type->children.at[i].type);
}
printf("}");
}
}
ResultType(Nothing, charptr) addGenericFieldToType(SolsType* type, SolsType field) {
if (type->genericChildren.at == NULL) {
type->genericChildren.at = malloc(sizeof(SolsType) * 4);
if (type->genericChildren.at == NULL) {
return Error(Nothing, charptr, "Failed to allocate memory");
}
type->genericChildren.capacity = 4;
type->genericChildren.count = 0;
}
if (type->genericChildren.count >= type->genericChildren.capacity) {
SolsType* tmp = realloc(type->genericChildren.at, type->genericChildren.capacity * 2 * sizeof(SolsType));
if (tmp == NULL) {
return Error(Nothing, charptr, "Failed to allocate memory");
}
type->genericChildren.capacity *= 2;
}
type->genericChildren.at[type->genericChildren.count] = ({
ResultType(SolsType, charptr) _res = copySolsType(&field);
if (_res.error) {
return Error(Nothing, charptr, _res.as.error);
}
_res.as.success;
});
type->genericChildren.count++;
return Success(Nothing, charptr, {});
}

View File

@@ -8,13 +8,14 @@
#include "../include/nothing.h"
typedef enum SolsTypeType {
STT_INT, STT_STRING, STT_DOUBLE, STT_BOOL, STT_CHAR, STT_FUN, STT_TEMPLATE, STT_OBJECT
STT_INT, STT_STRING, STT_DOUBLE, STT_BOOL, STT_CHAR, STT_FUN, STT_TEMPLATE, STT_OBJECT, STT_UNKNOWN, STT_GENERIC, STT_NONE
} SolsTypeType;
// Definition of charptr for Result() and ResultType() macros
typedef char* charptr;
struct SolsTypeField;
struct SolsNode; // for generic struct
// Holds type information for a struct, object or function.
// Say, for example, your type signature looks like this:
@@ -53,6 +54,12 @@ typedef struct SolsType {
// For use when type is identified with a name
char* identifierType;
// If type is identified with a name, record whether we have found the actual type
bool typeIsKnown;
// If using anonymous struct, record whether we need to generate a Ground struct
bool needsGroundStruct;
// For use in functions
struct SolsType* returnType;
@@ -62,6 +69,25 @@ typedef struct SolsType {
size_t count;
size_t capacity;
} children;
// For use in generics
struct {
struct SolsType* at;
size_t count;
size_t capacity;
} genericChildren;
struct {
bool isPrivate;
bool isProtected;
bool isEnum;
bool isGenericField;
bool isGenericStruct;
// For use by generic struct
// We will codegen this where required
struct SolsNode* genericStructNode;
} metadata;
} SolsType;
// Assists with holding child types in the SolsType struct.
@@ -73,16 +99,20 @@ typedef struct SolsTypeField {
Result(SolsType, charptr);
// Creates a SolsType, with the provided type type.
// 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);
// Creates a SolsType which is identified by a name.
// The type details are not known yet, so the type is marked as unknown.
ResultType(SolsType, charptr) createIdentifiedSolsType(char* in);
Result(Nothing, charptr);
// Adds a child SolsType to a given SolsType.
// Adds a child SolsType to a given SolsType.
// Returns:
// Success: Nothing
// Failure: char* detailing what went wrong (usually memory failure)
@@ -93,8 +123,10 @@ ResultType(SolsType, charptr) copySolsType(SolsType* type);
Result(GroundArg, charptr);
struct SolsScope;
// Represents a SolsType as a GroundArg (in typeref form)
ResultType(GroundArg, charptr) createGroundArgFromSolsType(SolsType* type);
ResultType(GroundArg, charptr) createGroundArgFromSolsType(SolsType* type, struct SolsScope* scope);
// Frees a SolsType
void freeSolsType(SolsType* type);
@@ -102,4 +134,16 @@ void freeSolsType(SolsType* type);
// Compares two SolsTypes
bool compareTypes(SolsType* left, SolsType* right);
// Finds the type of a struct member. Errors if the member is not found.
ResultType(SolsType, charptr) findStructMemberType(SolsType* type, char* member);
// Adds a generic argument to a SolsType.
// If you have
// MyStruct[int, string]
// you would add SolsType(STT_INT) and SolsType(STT_STRING) to the
// SolsType for `MyStruct` using this function.
ResultType(Nothing, charptr) addGenericFieldToType(SolsType* type, SolsType field);
void printSolsType(SolsType* type);
#endif

View File

@@ -9,17 +9,32 @@
struct _SolsTokenTypeMap SolsTokenTypeMap[] = {
{"puts", STT_KW_PUTS},
{"if", STT_KW_IF},
{"else", STT_KW_ELSE},
{"while", STT_KW_WHILE},
{"break", STT_KW_BREAK},
{"continue", STT_KW_CONTINUE},
{"def", STT_KW_DEF},
{"lambda", STT_KW_LAMBDA},
{"return", STT_KW_RETURN},
{"use", STT_KW_USE},
{"struct", STT_KW_STRUCT},
{"enum", STT_KW_ENUM},
{"constructor", STT_KW_CONSTRUCTOR},
{"destructor", STT_KW_DESTRUCTOR},
{"duplicator", STT_KW_DUPLICATOR},
{"private", STT_KW_PRIVATE},
{"protected", STT_KW_PROTECTED},
{"ground", STT_KW_GROUND},
{"new", STT_KW_NEW},
{"as", STT_KW_AS},
{"sizeof", STT_KW_SIZEOF},
{"pragma", STT_KW_PRAGMA},
{"{", STT_OPEN_CURLY},
{"}", STT_CLOSE_CURLY},
{"(", STT_OPEN_PAREN},
{")", STT_CLOSE_PAREN},
{"[", STT_OPEN_SQUARE},
{"]", STT_CLOSE_SQUARE},
{"+", STT_OP_ADD},
{"-", STT_OP_SUB},
{"*", STT_OP_MUL},
@@ -42,7 +57,7 @@ struct _SolsTokenTypeMap SolsTokenTypeMap[] = {
{",", 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
// Enable this by adding -DSUPER_SILLY_MODE to your
// compile flags (not recommended for production)
#ifdef SUPER_SILLY_MODE
{"plus", STT_OP_ADD},
@@ -62,6 +77,8 @@ struct _SolsTokenTypeMap SolsTokenTypeMap[] = {
{"subtracts", STT_OP_SUBTO},
{"multiplies", STT_OP_MULTO},
{"divides", STT_OP_DIVTO},
{"class", STT_KW_STRUCT},
{"compilerpleasedothisforme", STT_KW_PRAGMA}
#endif
};
@@ -292,7 +309,20 @@ ResultType(SolsToken, charptr) identifyToken(const char* token) {
size_t len = strlen(token);
bool isInt = true;
bool isDouble = false;
bool isHex = false;
if (token[0] == '0' && (token[1] == 'x' || token[1] == 'X')) {
isHex = true;
isInt = false;
}
for (size_t i = 1; i < len; i++) {
if (isHex) {
if (i == 1) continue;
if (!isdigit(token[i]) && !('A' <= token[i] && token[i] <= 'F') && !('a' <= token[i] && token[i] <= 'f')) {
isHex = false;
break;
}
continue;
}
if (isInt && token[i] == '.') {
isInt = false;
isDouble = true;
@@ -301,6 +331,7 @@ ResultType(SolsToken, charptr) identifyToken(const char* token) {
if (!isdigit(token[i])) {
isInt = false;
isDouble = false;
break;
}
}
if (isInt) {
@@ -332,6 +363,21 @@ ResultType(SolsToken, charptr) identifyToken(const char* token) {
};
return Success(SolsToken, charptr, tok);
}
if (isHex) {
int64_t newInt = strtoll(token, NULL, 0);
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);
}
}
// Handle boolean (true/false)
@@ -577,13 +623,15 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) {
}
// These characters require themselves added seperately from the previous token.
case '{':
case '{':
case '}':
case '(':
case ')':
case ',':
case ':':
case ';':
case '[':
case ']':
case '\n':
{
ResultType(Nothing, charptr) res = identifyAndAdd(lexer, &buf, &lineNum, &currentLine, chr.as.success, &skipDelimiter);
@@ -593,7 +641,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) {
DESTROY_ESTR(currentLine);
return Error(Nothing, charptr, err);
}
if (skipDelimiter) break;
char tmp[] = {chr.as.success, '\0'};
@@ -613,7 +661,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) {
return Error(Nothing, charptr, err);
}
strcpy(result.as.success.line.content, currentLine.str);
addTokenToSolsTokens(&lexer->output, result.as.success);
break;
}
@@ -694,7 +742,7 @@ ResultType(Nothing, charptr) lex(SolsLexer* lexer) {
addTokenToSolsTokens(&lexer->output, result.as.success);
lexerConsume(lexer);
}
break;
break;
}
// These characters may be followed by an equals sign, or nothing else.

4
src/linenoise/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
linenoise-example
linenoise-test
*.dSYM
history.txt

25
src/linenoise/LICENSE Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

13
src/linenoise/Makefile Normal file
View File

@@ -0,0 +1,13 @@
all: linenoise-example linenoise-test
linenoise-example: linenoise.h linenoise.c example.c
$(CC) -Wall -W -Os -g -o linenoise-example linenoise.c example.c
linenoise-test: linenoise-test.c linenoise-example
$(CC) -Wall -W -Os -g -o linenoise-test linenoise-test.c
test: linenoise-test linenoise-example
./linenoise-test
clean:
rm -f linenoise-example linenoise-test

View File

@@ -0,0 +1,380 @@
# Linenoise
A minimal, zero-config, BSD licensed, readline replacement used in Redis,
MongoDB, Android and many other projects.
* Single and multi line editing mode with the usual key bindings implemented.
* History handling.
* Completion.
* Hints (suggestions at the right of the prompt as you type).
* Multiplexing mode, with prompt hiding/restoring for asynchronous output.
* UTF-8 support for multi-byte characters and emoji.
* About ~1100 lines (comments and spaces excluded) of BSD license source code.
* Only uses a subset of VT100 escapes (ANSI.SYS compatible).
## Can a line editing library be 20k lines of code?
Line editing with some support for history is a really important feature for command line utilities. Instead of retyping almost the same stuff again and again it's just much better to hit the up arrow and edit on syntax errors, or in order to try a slightly different command. But apparently code dealing with terminals is some sort of Black Magic: readline is 30k lines of code, libedit 20k. Is it reasonable to link small utilities to huge libraries just to get a minimal support for line editing?
So what usually happens is either:
* Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (real world example of this problem: Tclsh).
* Smaller programs not using a configure script not supporting line editing at all (A problem we had with `redis-cli`, for instance).
The result is a pollution of binaries without line editing support.
So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporting line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to Linenoise if not.
## Terminals, in 2010.
Apparently almost every terminal you can happen to use today has some kind of support for basic VT100 escape sequences. So I tried to write a lib using just very basic VT100 features. The resulting library appears to work everywhere I tried to use it, and now can work even on ANSI.SYS compatible terminals, since no
VT220 specific sequences are used anymore.
The library is currently about 850 lines of code. In order to use it in your project just look at the *example.c* file in the source distribution, it is pretty straightforward. The library supports both a blocking mode and a multiplexing mode, see the API documentation later in this file for more information.
Linenoise is BSD-licensed code, so you can use both in free software and commercial software.
## Tested with...
* Linux text only console ($TERM = linux)
* Linux KDE terminal application ($TERM = xterm)
* Linux xterm ($TERM = xterm)
* Linux Buildroot ($TERM = vt100)
* Mac OS X iTerm ($TERM = xterm)
* Mac OS X default Terminal.app ($TERM = xterm)
* OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen)
* IBM AIX 6.1
* FreeBSD xterm ($TERM = xterm)
* ANSI.SYS
* Emacs comint mode ($TERM = dumb)
Please test it everywhere you can and report back!
## Let's push this forward!
Patches should be provided in the respect of Linenoise sensibility for small
easy to understand code.
Send feedbacks to antirez at gmail
# The API
Linenoise is very easy to use, and reading the example shipped with the
library should get you up to speed ASAP. Here is a list of API calls
and how to use them. Let's start with the simple blocking mode:
char *linenoise(const char *prompt);
This is the main Linenoise call: it shows the user a prompt with line editing
and history capabilities. The prompt you specify is used as a prompt, that is,
it will be printed to the left of the cursor. The library returns a buffer
with the line composed by the user, or NULL on end of file or when there
is an out of memory condition.
When a tty is detected (the user is actually typing into a terminal session)
the maximum editable line length is `LINENOISE_MAX_LINE`. When instead the
standard input is not a tty, which happens every time you redirect a file
to a program, or use it in an Unix pipeline, there are no limits to the
length of the line that can be returned.
The returned line should be freed with the `free()` standard system call.
However sometimes it could happen that your program uses a different dynamic
allocation library, so you may also used `linenoiseFree` to make sure the
line is freed with the same allocator it was created.
The canonical loop used by a program using Linenoise will be something like
this:
while((line = linenoise("hello> ")) != NULL) {
printf("You wrote: %s\n", line);
linenoiseFree(line); /* Or just free(line) if you use libc malloc. */
}
## Single line VS multi line editing
By default, Linenoise uses single line editing, that is, a single row on the
screen will be used, and as the user types more, the text will scroll towards
left to make room. This works if your program is one where the user is
unlikely to write a lot of text, otherwise multi line editing, where multiple
screens rows are used, can be a lot more comfortable.
In order to enable multi line editing use the following API call:
linenoiseSetMultiLine(1);
You can disable it using `0` as argument.
## History
Linenoise supporst history, so that the user does not have to retype
again and again the same things, but can use the down and up arrows in order
to search and re-edit already inserted lines of text.
The followings are the history API calls:
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(const char *filename);
Use `linenoiseHistoryAdd` every time you want to add a new element
to the top of the history (it will be the first the user will see when
using the up arrow).
Note that for history to work, you have to set a length for the history
(which is zero by default, so history will be disabled if you don't set
a proper one). This is accomplished using the `linenoiseHistorySetMaxLen`
function.
Linenoise has direct support for persisting the history into an history
file. The functions `linenoiseHistorySave` and `linenoiseHistoryLoad` do
just that. Both functions return -1 on error and 0 on success.
## Mask mode
Sometimes it is useful to allow the user to type passwords or other
secrets that should not be displayed. For such situations linenoise supports
a "mask mode" that will just replace the characters the user is typing
with `*` characters, like in the following example:
$ ./linenoise_example
hello> get mykey
echo: 'get mykey'
hello> /mask
hello> *********
You can enable and disable mask mode using the following two functions:
void linenoiseMaskModeEnable(void);
void linenoiseMaskModeDisable(void);
## Completion
Linenoise supports completion, which is the ability to complete the user
input when she or he presses the `<TAB>` key.
In order to use completion, you need to register a completion callback, which
is called every time the user presses `<TAB>`. Your callback will return a
list of items that are completions for the current string.
The following is an example of registering a completion callback:
linenoiseSetCompletionCallback(completion);
The completion must be a function returning `void` and getting as input
a `const char` pointer, which is the line the user has typed so far, and
a `linenoiseCompletions` object pointer, which is used as argument of
`linenoiseAddCompletion` in order to add completions inside the callback.
An example will make it more clear:
void completion(const char *buf, linenoiseCompletions *lc) {
if (buf[0] == 'h') {
linenoiseAddCompletion(lc,"hello");
linenoiseAddCompletion(lc,"hello there");
}
}
Basically in your completion callback, you inspect the input, and return
a list of items that are good completions by using `linenoiseAddCompletion`.
If you want to test the completion feature, compile the example program
with `make`, run it, type `h` and press `<TAB>`.
## Hints
Linenoise has a feature called *hints* which is very useful when you
use Linenoise in order to implement a REPL (Read Eval Print Loop) for
a program that accepts commands and arguments, but may also be useful in
other conditions.
The feature shows, on the right of the cursor, as the user types, hints that
may be useful. The hints can be displayed using a different color compared
to the color the user is typing, and can also be bold.
For example as the user starts to type `"git remote add"`, with hints it's
possible to show on the right of the prompt a string `<name> <url>`.
The feature works similarly to the history feature, using a callback.
To register the callback we use:
linenoiseSetHintsCallback(hints);
The callback itself is implemented like this:
char *hints(const char *buf, int *color, int *bold) {
if (!strcasecmp(buf,"git remote add")) {
*color = 35;
*bold = 0;
return " <name> <url>";
}
return NULL;
}
The callback function returns the string that should be displayed or NULL
if no hint is available for the text the user currently typed. The returned
string will be trimmed as needed depending on the number of columns available
on the screen.
It is possible to return a string allocated in dynamic way, by also registering
a function to deallocate the hint string once used:
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
The free hint callback will just receive the pointer and free the string
as needed (depending on how the hits callback allocated it).
As you can see in the example above, a `color` (in xterm color terminal codes)
can be provided together with a `bold` attribute. If no color is set, the
current terminal foreground color is used. If no bold attribute is set,
non-bold text is printed.
Color codes are:
red = 31
green = 32
yellow = 33
blue = 34
magenta = 35
cyan = 36
white = 37;
## Screen handling
Sometimes you may want to clear the screen as a result of something the
user typed. You can do this by calling the following function:
void linenoiseClearScreen(void);
## Asyncrhronous API
Sometimes you want to read from the keyboard but also from sockets or other
external events, and at the same time there could be input to display to the
user *while* the user is typing something. Let's call this the "IRC problem",
since if you want to write an IRC client with linenoise, without using
some fully featured libcurses approach, you will surely end having such an
issue.
Fortunately now a multiplexing friendly API exists, and it is just what the
blocking calls internally use. To start, we need to initialize a linenoise
context like this:
struct linenoiseState ls;
char buf[1024];
linenoiseEditStart(&ls,-1,-1,buf,sizeof(buf),"some prompt> ");
The two -1 and -1 arguments are the stdin/out descriptors. If they are
set to -1, linenoise will just use the default stdin/out file descriptors.
Now as soon as we have data from stdin (and we know it via select(2) or
some other way), we can ask linenoise to read the next character with:
linenoiseEditFeed(&ls);
The function returns a `char` pointer: if the user didn't yet press enter
to provide a line to the program, it will return `linenoiseEditMore`, that
means we need to call `linenoiseEditFeed()` again when more data is
available. If the function returns non NULL, then this is a heap allocated
data (to be freed with `linenoiseFree()`) representing the user input.
When the function returns NULL, than the user pressed CTRL-C or CTRL-D
with an empty line, to quit the program, or there was some I/O error.
After each line is received (or if you want to quit the program, and exit raw mode), the following function needs to be called:
linenoiseEditStop(&ls);
To start reading the next line, a new linenoiseEditStart() must
be called, in order to reset the state, and so forth, so a typical event
handler called when the standard input is readable, will work similarly
to the example below:
``` c
void stdinHasSomeData(void) {
char *line = linenoiseEditFeed(&LineNoiseState);
if (line == linenoiseEditMore) return;
linenoiseEditStop(&LineNoiseState);
if (line == NULL) exit(0);
printf("line: %s\n", line);
linenoiseFree(line);
linenoiseEditStart(&LineNoiseState,-1,-1,LineNoiseBuffer,sizeof(LineNoiseBuffer),"serial> ");
}
```
Now that we have a way to avoid blocking in the user input, we can use
two calls to hide/show the edited line, so that it is possible to also
show some input that we received (from socekts, bluetooth, whatever) on
screen:
linenoiseHide(&ls);
printf("some data...\n");
linenoiseShow(&ls);
To the API calls, the linenoise example C file implements a multiplexing
example using select(2) and the asynchronous API:
```c
struct linenoiseState ls;
char buf[1024];
linenoiseEditStart(&ls,-1,-1,buf,sizeof(buf),"hello> ");
while(1) {
// Select(2) setup code removed...
retval = select(ls.ifd+1, &readfds, NULL, NULL, &tv);
if (retval == -1) {
perror("select()");
exit(1);
} else if (retval) {
line = linenoiseEditFeed(&ls);
/* A NULL return means: line editing is continuing.
* Otherwise the user hit enter or stopped editing
* (CTRL+C/D). */
if (line != linenoiseEditMore) break;
} else {
// Timeout occurred
static int counter = 0;
linenoiseHide(&ls);
printf("Async output %d.\n", counter++);
linenoiseShow(&ls);
}
}
linenoiseEditStop(&ls);
if (line == NULL) exit(0); /* Ctrl+D/C. */
```
You can test the example by running the example program with the `--async` option.
## Running the tests
To run the test suite:
make test
The tests will display a virtual terminal showing linenoise output in real-time, making it easy to see what's being tested and debug any failures.
### What the tests cover
The test suite verifies:
* Basic typing and cursor movement (left, right, home, end)
* Backspace and delete operations
* UTF-8 multi-byte characters (accented letters, CJK)
* Emoji and grapheme clusters (skin tones, ZWJ sequences like flags)
* Horizontal scrolling for long lines
* Multiline mode editing and navigation
* History navigation in multiline mode
* Word and line deletion (Ctrl-W, Ctrl-U)
### How the test harness works
The test program (`linenoise-test.c`) implements a VT100 terminal emulator that captures and verifies linenoise output:
1. **Fork and pipes**: The test harness forks `linenoise-example`, connecting to it via pipes. The child process sees `LINENOISE_ASSUME_TTY=1` to enable terminal mode despite not having a real TTY.
2. **VT100 emulator**: A minimal VT100 emulator parses escape sequences (cursor movement, screen clearing, etc.) and maintains a virtual screen buffer. Each cell stores a complete UTF-8 grapheme cluster and its display width.
3. **Visual rendering**: After each operation, the virtual screen is rendered to your real terminal with a border, so you can watch the test execute and see exactly what linenoise is displaying.
4. **Assertions**: Tests verify screen contents and cursor position against expected values.
This approach tests linenoise as users actually experience it, catching rendering bugs that unit tests would miss.
## Related projects
* [Linenoise NG](https://github.com/arangodb/linenoise-ng) is a fork of Linenoise that aims to add more advanced features like Windows support and other features. Uses C++ instead of C as development language.
* [Linenoise-swift](https://github.com/andybest/linenoise-swift) is a reimplementation of Linenoise written in Swift.

124
src/linenoise/example.c Normal file
View File

@@ -0,0 +1,124 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include "linenoise.h"
void completion(const char *buf, linenoiseCompletions *lc) {
if (buf[0] == 'h') {
linenoiseAddCompletion(lc,"hello");
linenoiseAddCompletion(lc,"hello there");
}
}
char *hints(const char *buf, int *color, int *bold) {
if (!strcasecmp(buf,"hello")) {
*color = 35;
*bold = 0;
return " World";
}
return NULL;
}
int main(int argc, char **argv) {
char *line;
char *prgname = argv[0];
int async = 0;
/* Parse options, with --multiline we enable multi line editing. */
while(argc > 1) {
argc--;
argv++;
if (!strcmp(*argv,"--multiline")) {
linenoiseSetMultiLine(1);
printf("Multi-line mode enabled.\n");
} else if (!strcmp(*argv,"--keycodes")) {
linenoisePrintKeyCodes();
exit(0);
} else if (!strcmp(*argv,"--async")) {
async = 1;
} else {
fprintf(stderr, "Usage: %s [--multiline] [--keycodes] [--async]\n", prgname);
exit(1);
}
}
/* Set the completion callback. This will be called every time the
* user uses the <tab> key. */
linenoiseSetCompletionCallback(completion);
linenoiseSetHintsCallback(hints);
/* Load history from file. The history file is just a plain text file
* where entries are separated by newlines. */
linenoiseHistoryLoad("history.txt"); /* Load the history at startup */
/* Now this is the main loop of the typical linenoise-based application.
* The call to linenoise() will block as long as the user types something
* and presses enter.
*
* The typed string is returned as a malloc() allocated string by
* linenoise, so the user needs to free() it. */
while(1) {
if (!async) {
line = linenoise("hello> ");
if (line == NULL) break;
} else {
/* Asynchronous mode using the multiplexing API: wait for
* data on stdin, and simulate async data coming from some source
* using the select(2) timeout. */
struct linenoiseState ls;
char buf[1024];
linenoiseEditStart(&ls,-1,-1,buf,sizeof(buf),"hello> ");
while(1) {
fd_set readfds;
struct timeval tv;
int retval;
FD_ZERO(&readfds);
FD_SET(ls.ifd, &readfds);
tv.tv_sec = 1; // 1 sec timeout
tv.tv_usec = 0;
retval = select(ls.ifd+1, &readfds, NULL, NULL, &tv);
if (retval == -1) {
perror("select()");
exit(1);
} else if (retval) {
line = linenoiseEditFeed(&ls);
/* A NULL return means: line editing is continuing.
* Otherwise the user hit enter or stopped editing
* (CTRL+C/D). */
if (line != linenoiseEditMore) break;
} else {
// Timeout occurred
static int counter = 0;
linenoiseHide(&ls);
printf("Async output %d.\n", counter++);
linenoiseShow(&ls);
}
}
linenoiseEditStop(&ls);
if (line == NULL) exit(0); /* Ctrl+D/C. */
}
/* Do something with the string. */
if (line[0] != '\0' && line[0] != '/') {
printf("echo: '%s'\n", line);
linenoiseHistoryAdd(line); /* Add to the history. */
linenoiseHistorySave("history.txt"); /* Save the history on disk. */
} else if (!strncmp(line,"/historylen",11)) {
/* The "/historylen" command will change the history len. */
int len = atoi(line+11);
linenoiseHistorySetMaxLen(len);
} else if (!strncmp(line, "/mask", 5)) {
linenoiseMaskModeEnable();
} else if (!strncmp(line, "/unmask", 7)) {
linenoiseMaskModeDisable();
} else if (line[0] == '/') {
printf("Unreconized command: %s\n", line);
}
free(line);
}
return 0;
}

File diff suppressed because it is too large Load Diff

1762
src/linenoise/linenoise.c Normal file

File diff suppressed because it is too large Load Diff

114
src/linenoise/linenoise.h Normal file
View File

@@ -0,0 +1,114 @@
/* linenoise.h -- VERSION 1.0
*
* Guerrilla line editing library against the idea that a line editing lib
* needs to be 20,000 lines of C code.
*
* See linenoise.c for more information.
*
* ------------------------------------------------------------------------
*
* Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __LINENOISE_H
#define __LINENOISE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h> /* For size_t. */
extern char *linenoiseEditMore;
/* The linenoiseState structure represents the state during line editing.
* We pass this state to functions implementing specific editing
* functionalities. */
struct linenoiseState {
int in_completion; /* The user pressed TAB and we are now in completion
* mode, so input is handled by completeLine(). */
size_t completion_idx; /* Index of next completion to propose. */
int ifd; /* Terminal stdin file descriptor. */
int ofd; /* Terminal stdout file descriptor. */
char *buf; /* Edited line buffer. */
size_t buflen; /* Edited line buffer size. */
const char *prompt; /* Prompt to display. */
size_t plen; /* Prompt length. */
size_t pos; /* Current cursor position. */
size_t oldpos; /* Previous refresh cursor position. */
size_t len; /* Current edited line length. */
size_t cols; /* Number of columns in terminal. */
size_t oldrows; /* Rows used by last refrehsed line (multiline mode) */
int oldrpos; /* Cursor row from last refresh (for multiline clearing). */
int history_index; /* The history index we are currently editing. */
};
typedef struct linenoiseCompletions {
size_t len;
char **cvec;
} linenoiseCompletions;
/* Non blocking API. */
int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt);
char *linenoiseEditFeed(struct linenoiseState *l);
void linenoiseEditStop(struct linenoiseState *l);
void linenoiseHide(struct linenoiseState *l);
void linenoiseShow(struct linenoiseState *l);
/* Blocking API. */
char *linenoise(const char *prompt);
void linenoiseFree(void *ptr);
/* Completion API. */
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
typedef void(linenoiseFreeHintsCallback)(void *);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
/* History API. */
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(const char *filename);
/* Other utilities. */
void linenoiseClearScreen(void);
void linenoiseSetMultiLine(int ml);
void linenoisePrintKeyCodes(void);
void linenoiseMaskModeEnable(void);
void linenoiseMaskModeDisable(void);
#ifdef __cplusplus
}
#endif
#endif /* __LINENOISE_H */

View File

@@ -1,5 +1,6 @@
#include "lexer/SolsType.h"
#include "lexer/lexer.h"
#include "repl/repl.h"
#include "typeparser/typeparser.h"
#include "parser/parser.h"
#include "codegen/codegen.h"
@@ -7,13 +8,19 @@
#include "include/estr.h"
#include <stdio.h>
#include <unistd.h>
#ifdef _WIN32
#include <direct.h>
#else
#include <sys/stat.h>
#include <libgen.h>
#endif
#include <groundvm.h>
extern bool groundDisableTypeChecking;
char* fileDir = NULL;
typedef enum SolsAction {
SA_PRINT, SA_EXEC, SA_BYTECODE, SA_COMPILE, SA_EXIT, SA_EXITOK
} SolsAction;
@@ -91,41 +98,17 @@ Args parseArgs(int argc, char** argv) {
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;
}
// Use ground's getFileContents
char* getFileContents(const char* filename);
int main(int argc, char** argv) {
groundDisableTypeChecking = true;
if (argc == 1) {
solsticeRepl();
exit(0);
}
Args args = parseArgs(argc, argv);
if (args.action == SA_EXIT) {
@@ -135,6 +118,17 @@ int main(int argc, char** argv) {
return 0;
}
#ifdef _WIN32
fileDir = NULL;
#else
{
char tmp[FILENAME_MAX];
char pwd[FILENAME_MAX];
getcwd(pwd, sizeof(pwd));
snprintf(tmp, FILENAME_MAX, "%s/%s", pwd, args.inputFile);
fileDir = dirname(tmp);
}
#endif
char* fileContents = getFileContents(args.inputFile);
if (fileContents == NULL) {

View File

@@ -15,7 +15,11 @@ ResultType(SolsNode, charptr) createSolsNode(SolsNodeType type, ...) {
.type = type,
.children.capacity = 32,
.children.count = 0,
.children.at = malloc(sizeof(SolsNode) * 32)
.children.at = malloc(sizeof(SolsNode) * 32),
.accessArg = {
.type = VALUE,
.value.value.type = NONE
}
};
if (node.children.at == NULL) {
@@ -68,3 +72,198 @@ SolsNode deepCopySolsNode(SolsNode node) {
}
return copy;
}
void printSolsNodeType(SolsNodeType type) {
switch (type) {
case SNT_IDENTIFIER:
printf("Identifier");
break;
case SNT_LITERAL:
printf("Literal");
break;
case SNT_TYPE:
printf("Type");
break;
case SNT_CODE_BLOCK:
printf("Code Block");
break;
case SNT_OP_ADD:
printf("+");
break;
case SNT_OP_SUB:
printf("-");
break;
case SNT_OP_MUL:
printf("*");
break;
case SNT_OP_DIV:
printf("/");
break;
case SNT_OP_ADDTO:
printf("+=");
break;
case SNT_OP_SUBTO:
printf("-=");
break;
case SNT_OP_MULTO:
printf("*=");
break;
case SNT_OP_DIVTO:
printf("/=");
break;
case SNT_OP_INCREMENT:
printf("++");
break;
case SNT_OP_DECREMENT:
printf("--");
break;
case SNT_OP_SET:
printf("=");
break;
case SNT_OP_GREATER:
printf(">");
break;
case SNT_OP_LESSER:
printf("<");
break;
case SNT_OP_EQUAL:
printf("==");
break;
case SNT_OP_INEQUAL:
printf("!=");
break;
case SNT_OP_EQGREATER:
printf(">=");
break;
case SNT_OP_EQLESSER:
printf("<=");
break;
case SNT_DEF:
printf("def");
break;
case SNT_LAMBDA:
printf("lambda");
break;
case SNT_FUNCTION_CALL:
printf("Function call");
break;
case SNT_RETURN:
printf("return");
break;
case SNT_SET_PRIVATE:
printf("Private =");
break;
case SNT_SET_PROTECTED:
printf("Protected =");
break;
case SNT_DEF_PRIVATE:
printf("Private def");
break;
case SNT_DEF_PROTECTED:
printf("Protected def");
break;
case SNT_USE:
printf("use");
break;
case SNT_LOCAL_USE:
printf("Local use");
break;
case SNT_STRUCT:
printf("struct");
break;
case SNT_ENUM:
printf("enum");
break;
case SNT_CONSTRUCTOR:
printf("constructor");
break;
case SNT_DESTRUCTOR:
printf("destructor");
break;
case SNT_DUPLICATOR:
printf("duplicator");
break;
case SNT_STRUCT_AS:
printf("struct as");
break;
case SNT_AS:
printf("as");
break;
case SNT_SIZE_OF:
printf("sizeof");
break;
case SNT_PUTS:
printf("puts");
break;
case SNT_IF:
printf("if");
break;
case SNT_WHILE:
printf("while");
break;
case SNT_NEW:
printf("new");
break;
case SNT_GROUND:
printf("ground");
break;
case SNT_ROOT:
printf("Root node");
break;
case SNT_EXPR_IN_PAREN:
printf("Parenthases");
break;
case SNT_DOT:
printf(".");
break;
case SNT_GENERIC:
printf("Generic");
break;
case SNT_GENERIC_INIT:
printf("Generic init");
break;
case SNT_PRAGMA:
printf("pragma");
break;
}
}
void printSolsNode(SolsNode* node) {
printf("Node (\nNodeType: ");
printSolsNodeType(node->type);
printf("\n");
printf("Line: num: %zu, content: %s",node->line.num, node->line.content);
switch (node->type) {
case SNT_IDENTIFIER: {
printf("\nIdentifier name: '%s'", node->as.idName);
break;
}
case SNT_GROUND: {
printf("\nInline Ground: '%s'", node->as.inlineGround);
break;
}
case SNT_LITERAL: {
printf("\nLiteral: ");
printSolsLiteral(&node->as.literal);
break;
}
case SNT_TYPE: {
printf("\nType: ");
printSolsType(&node->as.type);
break;
}
}
printf("\nChildren count: %zu", node->children.count);
if (node->children.count > 0) {
printf("\nChildren: (\n");
for (size_t i = 0; i < node->children.count; i++) {
printSolsNode(&node->children.at[i]);
}
printf("\n)");
}
printf("\n)\n");
}

View File

@@ -11,7 +11,21 @@
#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
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_SET_PRIVATE, SNT_SET_PROTECTED, SNT_DEF_PRIVATE, SNT_DEF_PROTECTED,
SNT_USE, SNT_LOCAL_USE, SNT_STRUCT, SNT_ENUM, SNT_CONSTRUCTOR, SNT_DESTRUCTOR, SNT_DUPLICATOR,
SNT_STRUCT_AS, SNT_AS, SNT_SIZE_OF,
SNT_PUTS, SNT_IF, SNT_ELSE, SNT_WHILE, SNT_BREAK, SNT_CONTINUE, SNT_NEW,
SNT_GROUND, SNT_ROOT, SNT_EXPR_IN_PAREN, SNT_DOT,
SNT_GENERIC, SNT_GENERIC_INIT,
SNT_PRAGMA
} SolsNodeType;
struct SolsNode;
@@ -59,6 +73,8 @@ ResultType(SolsNode, charptr) createSolsNode(SolsNodeType type, ...);
// Failure: char* detailing what went wrong (usually memory failure)
ResultType(Nothing, charptr) addChildToSolsNode(SolsNode* parent, SolsNode child);
void printSolsNode(SolsNode* node);
// Deep copies a SolsNode
SolsNode deepCopySolsNode(SolsNode node);

File diff suppressed because it is too large Load Diff

173
src/repl/repl.c Normal file
View File

@@ -0,0 +1,173 @@
#include "repl.h"
#include "../lexer/lexer.h"
#include "../typeparser/typeparser.h"
#include "../parser/parser.h"
#include "../codegen/codegen.h"
#include "../linenoise/linenoise.h"
#include "../include/estr.h"
#include <groundvm.h>
#include <stdio.h>
GroundValue interpretGroundProgram(GroundProgram* program, GroundScope* scope);
void printGroundValue(GroundValue* value);
void solsticeRepl() {
SolsScope scope = {
.variables = NULL,
.tmpCounter = 0,
.returnType = createSolsType(STT_INT).as.success
};
GroundVariable* variables = NULL;
GroundLabel* labels = NULL;
GroundCatch* catches = NULL;
GroundScope gscope = {
.variables = &variables,
.labels = &labels,
.catches = &catches
};
for (;;) {
Estr toRun = CREATE_ESTR("");
int curlyCount = 0;
int bracketCount = 0;
int squareCount = 0;
for (;;) {
char* line = linenoise("sols > ");
if (line == NULL) {
continue;
}
APPEND_ESTR(toRun, line);
size_t lineLen = strlen(line);
for (size_t i = 0; i < lineLen; i++) {
switch (line[i]) {
case '{': {
curlyCount++;
break;
}
case '}': {
curlyCount--;
break;
}
case '(': {
bracketCount++;
break;
}
case ')': {
bracketCount--;
break;
}
case '[': {
squareCount++;
break;
}
case ']': {
squareCount--;
break;
}
}
}
linenoiseFree(line);
if (curlyCount == 0 && bracketCount == 0 && squareCount == 0) {
break;
}
if (curlyCount < 0 || bracketCount < 0 || squareCount < 0) {
DESTROY_ESTR(toRun);
toRun = CREATE_ESTR("");
printf("You've got some extra closing brackets :(\n");
}
}
linenoiseHistoryAdd(toRun.str);
if (toRun.str[0] == '.') {
// In-built command
if (strcmp(toRun.str, ".exit") == 0) {
return;
}
continue;
}
ResultType(SolsLexer, charptr) lexer = createLexer(toRun.str);
if (lexer.error) {
printf("Error while creating lexer: %s", lexer.as.error);
continue;
}
ResultType(Nothing, charptr) lexed = lex(&lexer.as.success);
if (lexed.error) {
printf("%s\n", lexed.as.error);
continue;
}
// Detect and parse types
ResultType(SolsTokens, charptr) typed = addTypeInfo(&lexer.as.success.output);
if (typed.error) {
printf("%s\n", typed.as.error);
continue;
}
// Parse file
ResultType(SolsParser, charptr) parser = createSolsParser(&typed.as.success);
if (parser.error) {
printf("Error while creating parser: %s\n", parser.as.error);
continue;
}
ResultType(Nothing, charptr) parsed = parse(&parser.as.success);
if (parsed.error) {
printf("%s\n", parsed.as.error);
continue;
}
// Do codegen on root node
ResultType(GroundProgram, charptr) codegen = generateCode(&parser.as.success.output, &scope);
if (codegen.error) {
printf("%s\n", codegen.as.error);
continue;
}
GroundValue retval = interpretGroundProgram(&codegen.as.success, &gscope);
SolsNode* currentParent = parser.as.success.currentParent;
if (retval.type != NONE) {
printf("\n");
printGroundValue(&retval);
}
if (currentParent->children.count > 0) {
SolsNode* lastNode = &currentParent->children.at[currentParent->children.count - 1];
GroundArg arg = lastNode->accessArg;
if (arg.type == VALUE) {
if (arg.value.value.type != NONE) {
printf("\n");
printGroundValue(&arg.value.value);
}
} else {
GroundVariable* variable = groundFindVariable(&gscope, arg.value.refName);
if (variable != NULL) {
printf("\n");
printGroundValue(&variable->value);
}
}
}
DESTROY_ESTR(toRun);
}
}

8
src/repl/repl.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef REPL_H
#define REPL_H
#include <groundvm.h>
void solsticeRepl();
#endif

11
tests/enum.sols Normal file
View File

@@ -0,0 +1,11 @@
enum Food {
Apple, Banana, Orange,
}
def function(Food food) Food {
return food
}
puts Food
puts function(Food.Banana)

46
tests/generics.sols Normal file
View File

@@ -0,0 +1,46 @@
struct Hash {
protected hashstr = ""
}
struct Person {
age = 0
name = ""
constructor(int age, string name) {
self.age = age
self.name = name
}
as Hash {
// hash the Person here
}
}
struct HashTable<Key can Hash, Value> {
private ptr = 0
protected size = 0
private capacity = 0
def find(Key key) {
hash = key as Hash
// find in table using hash
}
def set(Key key, Value value) {
hash = key as Hash
// store Value in the table
}
constructor() {
// malloc the pointer and set up hash information
}
duplicator {
// copy all contents
}
destructor {
// free the ptr
}
}

23
tests/nested-object.sols Normal file
View File

@@ -0,0 +1,23 @@
struct x {
a = 5
def modifyA(int a) int {
self.a = a
return a
}
}
struct y {
b = new x
}
z = new y
puts z
puts z.b
puts z.b.a
z.b.a = 10
puts z
z.b.modifyA(15)
puts z

7
tests/sizeof.sols Normal file
View File

@@ -0,0 +1,7 @@
puts sizeof "dingleing"
struct MyStruct {
size = 32
}
puts sizeof new MyStruct

View File

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

View File

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