forked from solstice/solstice
Compare commits
58 Commits
5196f73e16
...
631b587d07
| Author | SHA1 | Date | |
|---|---|---|---|
| 631b587d07 | |||
| 445ba032f5 | |||
| 41a2fa53c6 | |||
| 00ef8a7d56 | |||
| f1eee4f6a8 | |||
| cfca3c1d7a | |||
| 40e0aec0d4 | |||
| 38473f0e01 | |||
| 139be30e2d | |||
| d576c7cdfc | |||
| 2e4dbce100 | |||
| ea6bf5925b | |||
| 142268c016 | |||
| 1c6b145d35 | |||
| 6002bd922b | |||
| 4b86fee7b5 | |||
| d12036fe70 | |||
| 59e273b26e | |||
| e285b2c59f | |||
| e3abe07f4b | |||
| 010d155f5d | |||
| 44785185f7 | |||
| c82f81f668 | |||
| 9397f410da | |||
| 6d5d29f05b | |||
| 48c130351a | |||
| 6bc483b1db | |||
| c5b67bff72 | |||
| ca8db171d9 | |||
| c266728ff0 | |||
| b46a66cea7 | |||
| 337b88c148 | |||
| d8812fa14e | |||
| 24ea348858 | |||
| 9a311c3cb8 | |||
| aa5ef0e664 | |||
| 7ff306b9e8 | |||
| ac7f22e1bc | |||
| 5ec2f86b70 | |||
| 7dd2b10603 | |||
| 7351604571 | |||
| 43310c70bf | |||
| 957e0fd95a | |||
| 525b2bc733 | |||
| d2f295f46a | |||
| 869f71466e | |||
| a3a9553189 | |||
| 3c66df5be0 | |||
| ce058c8f9c | |||
| 2aff15a317 | |||
| 72ec9c1fb6 | |||
| c04e631180 | |||
| 16a406b52f | |||
| 0488067ef2 | |||
| a8e5f6a0f1 | |||
| e8bf7b70f7 | |||
| b5c8b1b7ec | |||
| 99bc0dbdc2 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1 +1,3 @@
|
|||||||
hg
|
solstice
|
||||||
|
build
|
||||||
|
.*_solsbuild
|
||||||
|
|||||||
5
.project.fish
Normal file
5
.project.fish
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# source .project.fish
|
||||||
|
|
||||||
|
alias run "make && ./solstice"
|
||||||
|
alias clean "make clean"
|
||||||
|
alias cleanrun "make clean && make && ./solstice"
|
||||||
46
Makefile
Normal file
46
Makefile
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
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
|
||||||
42
README.md
42
README.md
@@ -1,49 +1,21 @@
|
|||||||
# High Ground
|

|
||||||
|
|
||||||
High Ground is a programming language based on Ground.
|
# Solstice
|
||||||
|
|
||||||
It is the reference language designed to teach you how to build your own Ground-based language.
|
Solstice is a programming language based on Ground.
|
||||||
|
|
||||||
## Compiling
|
## Compiling
|
||||||
|
|
||||||
First, ensure CGround is installed on your system with `sudo make install`. Then, compile with
|
First, ensure CGround is installed on your system with `sudo make install`. Then, compile with
|
||||||
|
|
||||||
```
|
```
|
||||||
g++ src/main.cpp -o hg -lgroundvm
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
High Ground files use the `.hg` extension. Run files as you would with any other interpreted language.
|
Solstice files use the `.sols` extension. Run files as you would with any other interpreted language.
|
||||||
|
|
||||||
## Using High Ground
|
## Docs
|
||||||
|
|
||||||
### Types
|
Docs are avaliable at https://sols.dev/docs/
|
||||||
|
|
||||||
* `int`: Ground int (64 bit integer) eg 3, 7, 31432
|
|
||||||
* `double`: Ground double (Double precision floating point) eg 3.141, 2.7
|
|
||||||
* `string`: Ground string (char*) eg "Hello, World"
|
|
||||||
* `char`: Ground char (char) eg 'a'
|
|
||||||
* `bool`: Ground bool (either true or false)
|
|
||||||
|
|
||||||
### Printing
|
|
||||||
|
|
||||||
Prefix an expression with `puts` to print it to the console.
|
|
||||||
|
|
||||||
```
|
|
||||||
puts "Hello, world!"
|
|
||||||
puts 3.141
|
|
||||||
puts 7
|
|
||||||
puts 'a'
|
|
||||||
puts true
|
|
||||||
```
|
|
||||||
|
|
||||||
### Math
|
|
||||||
|
|
||||||
Add numbers with `+` (more operations coming soon)
|
|
||||||
|
|
||||||
```
|
|
||||||
puts 1 + 1
|
|
||||||
puts 3.14 + 2.7
|
|
||||||
puts 532 + 314 + 89432
|
|
||||||
```
|
|
||||||
14
chookspace/index.html
Normal file
14
chookspace/index.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Redirecting to sols.dev</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Redirecting to sols.dev, if that doesn't work <a href="https://sols.dev">click here</a>.</p>
|
||||||
|
<script>
|
||||||
|
window.location.href = "https://sols.dev";
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
15
libs/conversions.sols
Normal file
15
libs/conversions.sols
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
6
libs/file.sols
Normal file
6
libs/file.sols
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
def file_Read(string file) string {}
|
||||||
|
def file_Write(string file, string content) {}
|
||||||
|
|
||||||
|
ground {
|
||||||
|
extern "fileio"
|
||||||
|
}
|
||||||
23
libs/io.sols
Normal file
23
libs/io.sols
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
def input(string msg) string {
|
||||||
|
retval = ""
|
||||||
|
ground {
|
||||||
|
print $msg
|
||||||
|
input &retval
|
||||||
|
}
|
||||||
|
return retval
|
||||||
|
}
|
||||||
|
|
||||||
|
def print(string msg) string {
|
||||||
|
ground {
|
||||||
|
print $msg
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
def println(string msg) string {
|
||||||
|
ground {
|
||||||
|
println $msg
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
12
libs/request.sols
Normal file
12
libs/request.sols
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
def request_Get(string url) string {}
|
||||||
|
def request_Post(string url, string data) string {}
|
||||||
|
def ws_Connect(string url) int {}
|
||||||
|
def ws_Send(int connId, string data) int {}
|
||||||
|
def ws_SendBinary(int connId, string data) int {}
|
||||||
|
def ws_Receive(int connId) string {}
|
||||||
|
def ws_ReceiveTimeout(int connId, int timeout) string {}
|
||||||
|
def ws_Close(int connId) bool {}
|
||||||
|
|
||||||
|
ground {
|
||||||
|
extern "request"
|
||||||
|
}
|
||||||
43
libs/unistd.sols
Normal file
43
libs/unistd.sols
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
def unistd_Crypt(string key, string value) string {}
|
||||||
|
def unistd_GetHostId() string {}
|
||||||
|
def unistd_SetHotId(int hostid) int {}
|
||||||
|
def unistd_GetHostname() string {}
|
||||||
|
def unistd_SetHostname(string name) int {}
|
||||||
|
|
||||||
|
def unistd_Alarm(int seconds) int {}
|
||||||
|
def unistd_Pause() int {}
|
||||||
|
|
||||||
|
unistd_F_OK = 0
|
||||||
|
unistd_R_OK = 0
|
||||||
|
unistd_W_OK = 0
|
||||||
|
unistd_X_OK = 0
|
||||||
|
|
||||||
|
def unistd_Access(string path, int mode) int {}
|
||||||
|
def unistd_Chdir(string path) int {}
|
||||||
|
def unistd_Chown(string path, int owner, int group) int {}
|
||||||
|
def unistd_Link(string oldpath, string newpath) int {}
|
||||||
|
def unistd_Rmdir(string path) int {}
|
||||||
|
def unistd_Symlink(string target, string linkpath) int {}
|
||||||
|
|
||||||
|
def unistd_Exit(int status) int {}
|
||||||
|
def unistd_Fork() int {}
|
||||||
|
def unistd_GetPid() int {}
|
||||||
|
def unistd_GetPPid() int {}
|
||||||
|
def unistd_GetSid(int pid) int {}
|
||||||
|
def unistd_Nice(int inc) int {}
|
||||||
|
def unistd_SetSid() int {}
|
||||||
|
def unistd_Sleep(int seconds) int {}
|
||||||
|
|
||||||
|
def unistd_GetGid() int {}
|
||||||
|
def unistd_GetEGid() int {}
|
||||||
|
def unistd_GetUid() int {}
|
||||||
|
def unistd_GetEUid() int {}
|
||||||
|
def unistd_GetLogin() string {}
|
||||||
|
def unistd_SetEUid(int euid) int {}
|
||||||
|
def unistd_SetEGid(int egid) int {}
|
||||||
|
def unistd_SetREUid(int ruid, int euid) int {}
|
||||||
|
def unistd_SetREGid(int rgid, int egid) int {}
|
||||||
|
|
||||||
|
ground {
|
||||||
|
extern "unistd"
|
||||||
|
}
|
||||||
BIN
logo/solstice.png
Normal file
BIN
logo/solstice.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
78
logo/solstice.svg
Normal file
78
logo/solstice.svg
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="210mm"
|
||||||
|
height="297mm"
|
||||||
|
viewBox="0 0 210 297"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||||
|
sodipodi:docname="solstice.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:zoom="1.8101934"
|
||||||
|
inkscape:cx="341.39999"
|
||||||
|
inkscape:cy="633.35776"
|
||||||
|
inkscape:window-width="1908"
|
||||||
|
inkscape:window-height="1028"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient8"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#64ffe1;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop8" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#a3b9f5;stop-opacity:1;"
|
||||||
|
offset="0.5"
|
||||||
|
id="stop10" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#d1b8fb;stop-opacity:1;"
|
||||||
|
offset="0.75"
|
||||||
|
id="stop11" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#f3cdff;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop9" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient8"
|
||||||
|
id="linearGradient9"
|
||||||
|
x1="-42.253399"
|
||||||
|
y1="135.74071"
|
||||||
|
x2="-41.225731"
|
||||||
|
y2="239.96782"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.73239966,-0.77274564,0.76516532,0.73965537,-4.0932457,-8.6853091)" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<path
|
||||||
|
id="path1"
|
||||||
|
style="fill:url(#linearGradient9);fill-opacity:1;stroke:#ffffff;stroke-width:0.281629;stroke-opacity:0"
|
||||||
|
d="m 100.38267,124.7767 a 31.775753,32.090549 0 0 0 -22.257004,9.893 31.775753,32.090549 0 0 0 0.979215,45.36846 l -0.0038,0.004 45.917939,44.38672 43.95132,-46.37229 -45.91795,-44.38673 -0.004,0.004 a 31.775753,32.090549 0 0 0 -22.66587,-8.8969 z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
49
src/codegen/SolsScope.c
Normal file
49
src/codegen/SolsScope.c
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/codegen/SolsScope.h
Normal file
32
src/codegen/SolsScope.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#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
|
||||||
1033
src/codegen/codegen.c
Normal file
1033
src/codegen/codegen.c
Normal file
File diff suppressed because it is too large
Load Diff
34
src/codegen/codegen.h
Normal file
34
src/codegen/codegen.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#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
|
||||||
67
src/include/ansii.h
Normal file
67
src/include/ansii.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// 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
|
||||||
83
src/include/error.h
Normal file
83
src/include/error.h
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifndef ERROR_H
|
||||||
|
#define ERROR_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* error.h - First class errors for C
|
||||||
|
* Have you ever wanted to have a Rust-like error experience in C?
|
||||||
|
* Look no further than this library! Using a couple simple macros,
|
||||||
|
* we can emulate their complicated enum system, and I'd argue that
|
||||||
|
* we do it better. Besides, it's in a better programming language.
|
||||||
|
*
|
||||||
|
* Enjoy!
|
||||||
|
*
|
||||||
|
* Licenced to you under the MIT license - see below.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Example usage:
|
||||||
|
*
|
||||||
|
* #include "error.h"
|
||||||
|
* #include <stdio.h>
|
||||||
|
*
|
||||||
|
* // You can't write char*, you have to define it with a typedef
|
||||||
|
* typedef char* charptr;
|
||||||
|
*
|
||||||
|
* Result(int, charptr) myFn(int x) {
|
||||||
|
* if (x > 5) {
|
||||||
|
* return Error(int, charptr, "Your number is too big");
|
||||||
|
* }
|
||||||
|
* return Success(int, charptr, x);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* int main() {
|
||||||
|
* ResultType(int, charptr) res = myFn(10);
|
||||||
|
* if (res.error) {
|
||||||
|
* printf("Uh oh, error is: %s\n", res.as.error);
|
||||||
|
* } else {
|
||||||
|
* printf("Got a result! It is %d\n", res.as.success);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2026 Maxwell Jeffress
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the “Software”),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Creates a new struct with the a (success) and b (error) types.
|
||||||
|
// If Result(a, b) has already been called with the same paramaters, please
|
||||||
|
// use ResultType(a, b) instead.
|
||||||
|
#define Result(a, b) struct __ResultType_##a##_##b { bool error; union {a success; b error;} as; }
|
||||||
|
|
||||||
|
// Uses an existing Result(a, b) struct.
|
||||||
|
#define ResultType(a, b) struct __ResultType_##a##_##b
|
||||||
|
|
||||||
|
|
||||||
|
// Creates a __ResultType_a_b struct, with .error as false and .as.success as res.
|
||||||
|
#define Success(a, b, res) (ResultType(a, b)) { .error = false, .as.success = res }
|
||||||
|
|
||||||
|
// Creates a __ResultType_a_b struct, with .error as true and .as.error as res.
|
||||||
|
#define Error(a, b, res) (ResultType(a, b)) { .error = true, .as.error = res }
|
||||||
|
|
||||||
|
#endif
|
||||||
52
src/include/estr.h
Normal file
52
src/include/estr.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef ESTR_H
|
||||||
|
#define ESTR_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
estr.h - Easy string manipulation
|
||||||
|
This library has macros to allow easier manipulation of strings. No longer shall
|
||||||
|
you have to malloc and realloc away to keep adding to your strings.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
Estr myString = CREATE_ESTR("my awesome string");
|
||||||
|
APPEND_ESTR(myString, " is so cool");
|
||||||
|
printf("%s\n", myString.str);
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CREATE_ESTR(instr) \
|
||||||
|
(Estr) { \
|
||||||
|
.str = instr,\
|
||||||
|
.size = strlen(instr),\
|
||||||
|
.shouldBeFreed = 0, \
|
||||||
|
.destroyed = 0 \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define APPEND_ESTR(estr, instr) { \
|
||||||
|
estr.size = estr.size + strlen(instr); \
|
||||||
|
char* tmp_ptr = malloc(estr.size + 1); \
|
||||||
|
if (tmp_ptr == NULL) printf("WARNING: Could not realloc estr " #estr "\n"); \
|
||||||
|
else { \
|
||||||
|
snprintf(tmp_ptr, estr.size + 1, "%s%s", estr.str, instr); \
|
||||||
|
if (estr.shouldBeFreed > 0) free(estr.str); \
|
||||||
|
estr.shouldBeFreed = 1; \
|
||||||
|
estr.str = tmp_ptr; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DESTROY_ESTR(estr) if (estr.shouldBeFreed > 0 && estr.destroyed < 1) free(estr.str);
|
||||||
|
|
||||||
|
typedef struct Estr {
|
||||||
|
char* str;
|
||||||
|
size_t size;
|
||||||
|
int8_t shouldBeFreed;
|
||||||
|
int8_t destroyed;
|
||||||
|
} Estr;
|
||||||
|
|
||||||
|
#endif // ESTR_H
|
||||||
10
src/include/nothing.h
Normal file
10
src/include/nothing.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// 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
|
||||||
1137
src/include/uthash.h
Normal file
1137
src/include/uthash.h
Normal file
File diff suppressed because it is too large
Load Diff
51
src/lexer/SolsLiteral.c
Normal file
51
src/lexer/SolsLiteral.c
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/lexer/SolsLiteral.h
Normal file
46
src/lexer/SolsLiteral.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#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
|
||||||
93
src/lexer/SolsToken.c
Normal file
93
src/lexer/SolsToken.c
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#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, {});
|
||||||
|
}
|
||||||
83
src/lexer/SolsToken.h
Normal file
83
src/lexer/SolsToken.h
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#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
|
||||||
176
src/lexer/SolsType.c
Normal file
176
src/lexer/SolsType.c
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
#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?");
|
||||||
|
}
|
||||||
105
src/lexer/SolsType.h
Normal file
105
src/lexer/SolsType.h
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#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
|
||||||
844
src/lexer/lexer.c
Normal file
844
src/lexer/lexer.c
Normal file
@@ -0,0 +1,844 @@
|
|||||||
|
#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, ¤tLine, 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, ¤tLine, 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, ¤tLine, 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, ¤tLine, 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, ¤tLine, 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, ¤tLine, '\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)");
|
||||||
|
}
|
||||||
76
src/lexer/lexer.h
Normal file
76
src/lexer/lexer.h
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#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
|
||||||
266
src/main.c
Normal file
266
src/main.c
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
563
src/main.cpp
563
src/main.cpp
@@ -1,563 +0,0 @@
|
|||||||
#include <cctype>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <groundvm.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <optional>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
#define parseOneToken(token) Parser({token.value()}).parse().children[0]
|
|
||||||
|
|
||||||
namespace HighGround {
|
|
||||||
|
|
||||||
int tmpIdIterator = 0;
|
|
||||||
|
|
||||||
namespace Parser {
|
|
||||||
|
|
||||||
enum class HGNodeType {
|
|
||||||
Add, Subtract, Equal, Set, While, If, Value, Identifier, None, Root, CodeBlock, CodeBlockStart, CodeBlockEnd, Puts
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class HGDataType {
|
|
||||||
Int, String, Double, Bool, Char, None
|
|
||||||
};
|
|
||||||
|
|
||||||
class HGNode;
|
|
||||||
|
|
||||||
class HGGroundCodeBlock {
|
|
||||||
public:
|
|
||||||
std::vector<GroundInstruction> code;
|
|
||||||
HGGroundCodeBlock() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
class HGData {
|
|
||||||
typedef std::variant<int64_t, std::string, double, bool, char> varData;
|
|
||||||
varData data;
|
|
||||||
public:
|
|
||||||
HGDataType type = HGDataType::Int;
|
|
||||||
HGData() = default;
|
|
||||||
HGData(int64_t in) : data(in), type(HGDataType::Int) {}
|
|
||||||
HGData(double in) : data(in), type(HGDataType::Double) {}
|
|
||||||
HGData(std::string in) : data(in), type(HGDataType::String) {}
|
|
||||||
HGData(char in) : data(in), type(HGDataType::Char) {}
|
|
||||||
HGData(bool in) : data(in), type(HGDataType::Bool) {}
|
|
||||||
std::optional<int64_t> getInt() {
|
|
||||||
if (type == HGDataType::Int) {
|
|
||||||
return std::get<int64_t>(data);
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::optional<double> getDouble() {
|
|
||||||
if (type == HGDataType::Double) {
|
|
||||||
return std::get<double>(data);
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::optional<std::string> getString() {
|
|
||||||
if (type == HGDataType::String) {
|
|
||||||
return std::get<std::string>(data);
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::optional<char> getChar() {
|
|
||||||
if (type == HGDataType::Char) {
|
|
||||||
return std::get<char>(data);
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::optional<bool> getBool() {
|
|
||||||
if (type == HGDataType::Bool) {
|
|
||||||
return std::get<bool>(data);
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class HGNode {
|
|
||||||
HGNodeType nodeType = HGNodeType::None;
|
|
||||||
HGData data;
|
|
||||||
public:
|
|
||||||
std::vector<HGNode> children;
|
|
||||||
std::string outputId;
|
|
||||||
HGNode(HGNodeType nodeType) : nodeType(nodeType) {}
|
|
||||||
HGNode(HGNodeType nodeType, HGData data) : nodeType(nodeType), data(data) {}
|
|
||||||
HGNode() = default;
|
|
||||||
void addNode(HGNode in) {
|
|
||||||
children.push_back(in);
|
|
||||||
}
|
|
||||||
void setValue(HGData in) {
|
|
||||||
data = in;
|
|
||||||
}
|
|
||||||
const std::vector<HGGroundCodeBlock> generateCode() {
|
|
||||||
std::vector<HGGroundCodeBlock> code;
|
|
||||||
for (auto& child : children) {
|
|
||||||
auto childCode = child.generateCode();
|
|
||||||
code.insert(code.end(), childCode.begin(), childCode.end());
|
|
||||||
}
|
|
||||||
switch (nodeType) {
|
|
||||||
case HGNodeType::Value: {
|
|
||||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
|
||||||
HGGroundCodeBlock codeBlock;
|
|
||||||
GroundInstruction gi = groundCreateInstruction(SET);
|
|
||||||
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, outputId.data()));
|
|
||||||
switch (data.type) {
|
|
||||||
case HGDataType::Int: {
|
|
||||||
auto dataopt = data.getInt();
|
|
||||||
if (dataopt) {
|
|
||||||
groundAddValueToInstruction(&gi, groundCreateValue(INT, dataopt.value()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HGDataType::Double: {
|
|
||||||
auto dataopt = data.getDouble();
|
|
||||||
if (dataopt) {
|
|
||||||
groundAddValueToInstruction(&gi, groundCreateValue(DOUBLE, dataopt.value()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HGDataType::String: {
|
|
||||||
auto dataopt = data.getString();
|
|
||||||
if (dataopt) {
|
|
||||||
groundAddValueToInstruction(&gi, groundCreateValue(STRING, dataopt.value().c_str()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HGDataType::Char: {
|
|
||||||
auto dataopt = data.getChar();
|
|
||||||
if (dataopt) {
|
|
||||||
groundAddValueToInstruction(&gi, groundCreateValue(CHAR, dataopt.value()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HGDataType::Bool: {
|
|
||||||
auto dataopt = data.getBool();
|
|
||||||
if (dataopt) {
|
|
||||||
groundAddValueToInstruction(&gi, groundCreateValue(BOOL, dataopt.value()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
codeBlock.code.push_back(gi);
|
|
||||||
code.push_back(codeBlock);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HGNodeType::Add: {
|
|
||||||
HGGroundCodeBlock codeBlock;
|
|
||||||
outputId = "tmp_" + std::to_string(tmpIdIterator++);
|
|
||||||
GroundInstruction gi = groundCreateInstruction(ADD);
|
|
||||||
if (children.size() < 2) {
|
|
||||||
std::cout << "Need more stuff to add\n";
|
|
||||||
}
|
|
||||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data()));
|
|
||||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data()));
|
|
||||||
groundAddReferenceToInstruction(&gi, groundCreateReference(DIRREF, outputId.data()));
|
|
||||||
codeBlock.code.push_back(gi);
|
|
||||||
code.push_back(codeBlock);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HGNodeType::Puts: {
|
|
||||||
HGGroundCodeBlock codeBlock;
|
|
||||||
GroundInstruction gi = groundCreateInstruction(PRINTLN);
|
|
||||||
if (children.size() < 1) {
|
|
||||||
std::cout << "Need more stuff to puts\n";
|
|
||||||
}
|
|
||||||
groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data()));
|
|
||||||
codeBlock.code.push_back(gi);
|
|
||||||
code.push_back(codeBlock);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {}
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class Parser {
|
|
||||||
std::vector<std::string> tokensToParse;
|
|
||||||
size_t current;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
std::optional<std::string> peek(int ahead = 1) {
|
|
||||||
if (current + ahead < size) {
|
|
||||||
return tokensToParse[current + ahead];
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> consume() {
|
|
||||||
if (current < size) {
|
|
||||||
return tokensToParse[current++];
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isInt(std::string in) {
|
|
||||||
for (const char& c : in) {
|
|
||||||
if (!std::isdigit(c)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool isDouble(std::string in) {
|
|
||||||
bool foundDot = false;
|
|
||||||
for (const char& c : in) {
|
|
||||||
if (!std::isdigit(c)) {
|
|
||||||
if (!foundDot && c == '.') {
|
|
||||||
foundDot = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool isString(std::string in) {
|
|
||||||
if (in.size() > 1 && in[0] == '"' && in.back() == '"') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool isChar(std::string in) {
|
|
||||||
if (in.size() == 3 && in[0] == '\'' && in.back() == '\'') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool isBool(std::string in) {
|
|
||||||
if (in == "true" || in == "false") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
HGDataType getDataType(std::string in) {
|
|
||||||
if (isInt(in)) {
|
|
||||||
return HGDataType::Int;
|
|
||||||
}
|
|
||||||
if (isDouble(in)) {
|
|
||||||
return HGDataType::Double;
|
|
||||||
}
|
|
||||||
if (isString(in)) {
|
|
||||||
return HGDataType::String;
|
|
||||||
}
|
|
||||||
if (isChar(in)) {
|
|
||||||
return HGDataType::Char;
|
|
||||||
}
|
|
||||||
if (isBool(in)) {
|
|
||||||
return HGDataType::Bool;
|
|
||||||
}
|
|
||||||
return HGDataType::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
HGNodeType getNodeType(std::string in) {
|
|
||||||
if (getDataType(in) != HGDataType::None) {
|
|
||||||
return HGNodeType::Value;
|
|
||||||
}
|
|
||||||
if (in == "+") {
|
|
||||||
return HGNodeType::Add;
|
|
||||||
}
|
|
||||||
if (in == "puts") {
|
|
||||||
return HGNodeType::Puts;
|
|
||||||
}
|
|
||||||
if (in == "if") {
|
|
||||||
return HGNodeType::If;
|
|
||||||
}
|
|
||||||
if (in == "{") {
|
|
||||||
return HGNodeType::CodeBlockStart;
|
|
||||||
}
|
|
||||||
if (in == "}") {
|
|
||||||
return HGNodeType::CodeBlockEnd;
|
|
||||||
}
|
|
||||||
return HGNodeType::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
Parser(std::vector<std::string> in) : tokensToParse(in) {}
|
|
||||||
|
|
||||||
HGNode parse() {
|
|
||||||
current = 0;
|
|
||||||
size = tokensToParse.size();
|
|
||||||
HGNode rootNode(HGNodeType::Root);
|
|
||||||
while (auto tokenopt = consume()) {
|
|
||||||
std::string token = tokenopt.value();
|
|
||||||
switch (getNodeType(token)) {
|
|
||||||
case HGNodeType::Value: {
|
|
||||||
switch (getDataType(token)) {
|
|
||||||
case HGDataType::Int: {
|
|
||||||
HGNode intNode(HGNodeType::Value);
|
|
||||||
intNode.setValue((int64_t) std::stoll(token));
|
|
||||||
rootNode.addNode(intNode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HGDataType::Double: {
|
|
||||||
HGNode doubleNode(HGNodeType::Value);
|
|
||||||
doubleNode.setValue(std::stod(token));
|
|
||||||
rootNode.addNode(doubleNode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HGDataType::String: {
|
|
||||||
HGNode stringNode(HGNodeType::Value);
|
|
||||||
stringNode.setValue(token.substr(1, token.size() - 2));
|
|
||||||
rootNode.addNode(stringNode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HGDataType::Char: {
|
|
||||||
HGNode charNode(HGNodeType::Value);
|
|
||||||
charNode.setValue(token[1]);
|
|
||||||
rootNode.addNode(charNode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HGDataType::Bool: {
|
|
||||||
HGNode boolNode(HGNodeType::Value);
|
|
||||||
boolNode.setValue(token == "true");
|
|
||||||
rootNode.addNode(boolNode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HGNodeType::Add: {
|
|
||||||
HGNode addNode(HGNodeType::Add);
|
|
||||||
addNode.addNode(rootNode.children.back());
|
|
||||||
rootNode.children.pop_back();
|
|
||||||
auto tokenopt = consume();
|
|
||||||
if (tokenopt) {
|
|
||||||
addNode.addNode(parseOneToken(tokenopt));
|
|
||||||
} else {
|
|
||||||
std::cout << "FEED ME MORE TOKENS\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
rootNode.addNode(addNode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HGNodeType::Puts: {
|
|
||||||
HGNode putsNode(HGNodeType::Puts);
|
|
||||||
std::vector<std::string> tokens;
|
|
||||||
while (auto tokenopt = consume()) {
|
|
||||||
if (tokenopt.value() == "\n") {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tokens.push_back(tokenopt.value());
|
|
||||||
}
|
|
||||||
auto children = Parser(tokens).parse();
|
|
||||||
for (auto& child : children.children) {
|
|
||||||
putsNode.addNode(child);
|
|
||||||
}
|
|
||||||
rootNode.addNode(putsNode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HGNodeType::If: {
|
|
||||||
HGNode ifNode(HGNodeType::If);
|
|
||||||
std::vector<std::string> tokens;
|
|
||||||
while (auto tokenopt = consume()) {
|
|
||||||
if (tokenopt.value() == "\n") {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tokens.push_back(tokenopt.value());
|
|
||||||
}
|
|
||||||
auto children = Parser(tokens).parse();
|
|
||||||
if (children.children.size() != 1) {
|
|
||||||
std::cout << "Too many or too little conditions for if\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
ifNode.addNode(children.children[0]);
|
|
||||||
tokens.clear();
|
|
||||||
size_t brackets = 1;
|
|
||||||
auto tokenopt = consume();
|
|
||||||
if (tokenopt) {
|
|
||||||
if (tokenopt.value() == "{") {
|
|
||||||
tokens.push_back(tokenopt.value());
|
|
||||||
while (auto tokenopt = consume()) {
|
|
||||||
tokens.push_back(tokenopt.value());
|
|
||||||
if (tokenopt.value() == "{") {
|
|
||||||
brackets++;
|
|
||||||
}
|
|
||||||
if (tokenopt.value() == "}") {
|
|
||||||
brackets--;
|
|
||||||
}
|
|
||||||
if (brackets == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::cout << "I want a code block\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::cout << "FEED ME MORE TOKENSSSSS\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HGNodeType::CodeBlockStart: {
|
|
||||||
HGNode codeBlockNode(HGNodeType::CodeBlock);
|
|
||||||
// WIP
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rootNode;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
GroundProgram assembleProgram(HGNode& rootNode) {
|
|
||||||
GroundProgram gp = groundCreateProgram();
|
|
||||||
auto code = rootNode.generateCode();
|
|
||||||
for (int i = 0; i < code.size(); i++) {
|
|
||||||
for (const auto& inst : code[i].code) {
|
|
||||||
groundAddInstructionToProgram(&gp, inst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return gp;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Parser
|
|
||||||
|
|
||||||
class Lexer {
|
|
||||||
std::string input;
|
|
||||||
size_t size;
|
|
||||||
size_t current;
|
|
||||||
|
|
||||||
std::optional<char> peek(int ahead = 1) {
|
|
||||||
if (current + ahead < size) {
|
|
||||||
return input[current + ahead];
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<char> consume() {
|
|
||||||
if (current < size) {
|
|
||||||
return input[current++];
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
Lexer(std::string in) : input(in), size(in.size()) {};
|
|
||||||
std::vector<std::string> lex() {
|
|
||||||
current = 0;
|
|
||||||
std::vector<std::string> tokens;
|
|
||||||
std::string buf;
|
|
||||||
while (auto copt = consume()) {
|
|
||||||
char c = copt.value();
|
|
||||||
switch (c) {
|
|
||||||
// tokens which are not followed by anything
|
|
||||||
case '\n':
|
|
||||||
case '(':
|
|
||||||
case ')':
|
|
||||||
case '}':
|
|
||||||
{
|
|
||||||
if (!buf.empty()) {
|
|
||||||
tokens.push_back(buf);
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
tokens.push_back(std::string(1, c));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// tokens which may be followed by either themselves
|
|
||||||
// or an equals sign
|
|
||||||
case '+':
|
|
||||||
case '-':
|
|
||||||
{
|
|
||||||
std::string newToken(1, c);
|
|
||||||
auto tokenopt = peek();
|
|
||||||
if (tokenopt) {
|
|
||||||
char token = tokenopt.value();
|
|
||||||
if (token == c || token == '=') {
|
|
||||||
newToken += token;
|
|
||||||
consume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!buf.empty()) {
|
|
||||||
tokens.push_back(buf);
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
tokens.push_back(newToken);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// tokens which may be followed by an equals sign
|
|
||||||
case '*':
|
|
||||||
case '/':
|
|
||||||
case '=':
|
|
||||||
{
|
|
||||||
std::string newToken(1, c);
|
|
||||||
auto tokenopt = peek();
|
|
||||||
if (tokenopt) {
|
|
||||||
char token = tokenopt.value();
|
|
||||||
if (token == '=') {
|
|
||||||
newToken += token;
|
|
||||||
consume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!buf.empty()) {
|
|
||||||
tokens.push_back(buf);
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
tokens.push_back(newToken);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// tokens which need a newline inserted for them
|
|
||||||
case '{':
|
|
||||||
{
|
|
||||||
if (!buf.empty()) {
|
|
||||||
tokens.push_back(buf);
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
tokens.push_back(std::string(1, c));
|
|
||||||
tokens.push_back("\n");
|
|
||||||
}
|
|
||||||
// tokens which do not need to be included
|
|
||||||
case ' ':
|
|
||||||
{
|
|
||||||
if (!buf.empty()) {
|
|
||||||
tokens.push_back(buf);
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
buf += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!buf.empty()) {
|
|
||||||
tokens.push_back(buf);
|
|
||||||
}
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace HighGround
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
if (argc < 2) {
|
|
||||||
std::cout << "Usage: " << argv[0] << " (file)\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
std::ifstream file(argv[1]);
|
|
||||||
std::ostringstream ss;
|
|
||||||
ss << file.rdbuf();
|
|
||||||
auto lexed = HighGround::Lexer(ss.str()).lex();
|
|
||||||
auto parsed = HighGround::Parser::Parser(lexed).parse();
|
|
||||||
GroundProgram program = HighGround::Parser::assembleProgram(parsed);
|
|
||||||
groundRunProgram(&program);
|
|
||||||
}
|
|
||||||
70
src/parser/SolsNode.c
Normal file
70
src/parser/SolsNode.c
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
65
src/parser/SolsNode.h
Normal file
65
src/parser/SolsNode.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#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
|
||||||
1698
src/parser/parser.c
Normal file
1698
src/parser/parser.c
Normal file
File diff suppressed because it is too large
Load Diff
99
src/parser/parser.h
Normal file
99
src/parser/parser.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#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
|
||||||
230
src/typeparser/typeparser.c
Normal file
230
src/typeparser/typeparser.c
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
11
src/typeparser/typeparser.h
Normal file
11
src/typeparser/typeparser.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#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
|
||||||
81
src/wasm_main.c
Normal file
81
src/wasm_main.c
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
9
tests/closure.sols
Normal file
9
tests/closure.sols
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
def createClosure(int x) fun(int) int {
|
||||||
|
return lambda(int y) int {
|
||||||
|
return x + y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
myVar = createClosure(5)
|
||||||
|
|
||||||
|
puts myVar(3)
|
||||||
55
tests/compare.sols
Normal file
55
tests/compare.sols
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
if 1 == 1 {
|
||||||
|
puts "working"
|
||||||
|
}
|
||||||
|
|
||||||
|
if 1 == 2 {
|
||||||
|
puts "not working"
|
||||||
|
}
|
||||||
|
|
||||||
|
if 1 != 2 {
|
||||||
|
puts "working"
|
||||||
|
}
|
||||||
|
|
||||||
|
if 1 != 1 {
|
||||||
|
puts "not working"
|
||||||
|
}
|
||||||
|
|
||||||
|
if 2 > 1 {
|
||||||
|
puts "working"
|
||||||
|
}
|
||||||
|
|
||||||
|
if 1 > 2 {
|
||||||
|
puts "not working"
|
||||||
|
}
|
||||||
|
|
||||||
|
if 1 < 2 {
|
||||||
|
puts "working"
|
||||||
|
}
|
||||||
|
|
||||||
|
if 2 < 1 {
|
||||||
|
puts "not working"
|
||||||
|
}
|
||||||
|
|
||||||
|
if 2 >= 2 {
|
||||||
|
puts "working"
|
||||||
|
}
|
||||||
|
|
||||||
|
if 2 >= 1 {
|
||||||
|
puts "working"
|
||||||
|
}
|
||||||
|
|
||||||
|
if 2 >= 3 {
|
||||||
|
puts "not working"
|
||||||
|
}
|
||||||
|
|
||||||
|
if 2 <= 2 {
|
||||||
|
puts "working"
|
||||||
|
}
|
||||||
|
|
||||||
|
if 2 <= 3 {
|
||||||
|
puts "working"
|
||||||
|
}
|
||||||
|
|
||||||
|
if 3 <= 2 {
|
||||||
|
puts "not working"
|
||||||
|
}
|
||||||
11
tests/convert.sols
Normal file
11
tests/convert.sols
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
use conversions
|
||||||
|
use io
|
||||||
|
|
||||||
|
myString = "312"
|
||||||
|
myInt = 435
|
||||||
|
|
||||||
|
myNewString = intToString(myInt)
|
||||||
|
myNewInt = stringToInt(myString)
|
||||||
|
|
||||||
|
println(myNewString)
|
||||||
|
puts myNewInt
|
||||||
6
tests/count.sols
Normal file
6
tests/count.sols
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
number = 0
|
||||||
|
|
||||||
|
while number != 10 {
|
||||||
|
number = number + 1
|
||||||
|
puts number
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
puts "dingus"
|
|
||||||
puts 432
|
|
||||||
puts 3.141
|
|
||||||
puts 'c'
|
|
||||||
puts true
|
|
||||||
7
tests/data.sols
Normal file
7
tests/data.sols
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
puts "dingus"
|
||||||
|
puts 432
|
||||||
|
puts 3.141
|
||||||
|
puts 'c'
|
||||||
|
puts true
|
||||||
|
|
||||||
|
puts "now we have proper string lexing!!!!!11!!@!2!1@1@12!!!112!@"
|
||||||
15
tests/fib.sols
Normal file
15
tests/fib.sols
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
a = 0
|
||||||
|
b = 1
|
||||||
|
n = 92
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
while i != n {
|
||||||
|
temp = a + b
|
||||||
|
a = b
|
||||||
|
b = temp
|
||||||
|
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
puts a
|
||||||
6
tests/function.sols
Normal file
6
tests/function.sols
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
def add(int a, int b) int {
|
||||||
|
puts "testing"
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
puts add(1, 2)
|
||||||
7
tests/if.sols
Normal file
7
tests/if.sols
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
if 1 == 1 {
|
||||||
|
puts "huzzah"
|
||||||
|
}
|
||||||
|
|
||||||
|
if 5 == 10 {
|
||||||
|
puts "aww"
|
||||||
|
}
|
||||||
7
tests/inlineground.sols
Normal file
7
tests/inlineground.sols
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
result = 0
|
||||||
|
ground {
|
||||||
|
println "dingus"
|
||||||
|
add 3 2 &result
|
||||||
|
}
|
||||||
|
|
||||||
|
puts result
|
||||||
15
tests/input.sols
Normal file
15
tests/input.sols
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
use io
|
||||||
|
|
||||||
|
accessNotGranted = true
|
||||||
|
|
||||||
|
while accessNotGranted {
|
||||||
|
password = input("Password: ")
|
||||||
|
if password == "dingus" {
|
||||||
|
accessNotGranted = false
|
||||||
|
}
|
||||||
|
if password != "dingus" {
|
||||||
|
puts "Incorrect password!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
puts "Welcome!"
|
||||||
@@ -1 +1,3 @@
|
|||||||
|
use io
|
||||||
|
|
||||||
println("dingus")
|
println("dingus")
|
||||||
3
tests/set.sols
Normal file
3
tests/set.sols
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
x = 5
|
||||||
|
y = 10
|
||||||
|
puts x + y
|
||||||
18
tests/struct.sols
Normal file
18
tests/struct.sols
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
use io
|
||||||
|
|
||||||
|
struct dingus {
|
||||||
|
x = 5
|
||||||
|
y = "dingus"
|
||||||
|
}
|
||||||
|
|
||||||
|
e = new dingus
|
||||||
|
puts e
|
||||||
|
puts dingus
|
||||||
|
|
||||||
|
puts e.x
|
||||||
|
println(e.y)
|
||||||
|
|
||||||
|
e.x = 7
|
||||||
|
e.y = "heheheha"
|
||||||
|
puts e.x
|
||||||
|
println(e.y)
|
||||||
3
tests/while.sols
Normal file
3
tests/while.sols
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
while 1 == 1 {
|
||||||
|
puts "yay infinite loop"
|
||||||
|
}
|
||||||
1
vim/ftdetect/solstice.vim
Normal file
1
vim/ftdetect/solstice.vim
Normal file
@@ -0,0 +1 @@
|
|||||||
|
au BufRead,BufNewFile *.sols setfiletype solstice
|
||||||
41
vim/syntax/solstice.vim
Normal file
41
vim/syntax/solstice.vim
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
" Vim highlight file for Solstice (.sols)
|
||||||
|
|
||||||
|
if exists("b:current_syntax")
|
||||||
|
finish
|
||||||
|
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
|
||||||
|
|
||||||
|
" Built-in functions
|
||||||
|
syn keyword solsticeBuiltin puts print println input
|
||||||
|
|
||||||
|
" Data Types
|
||||||
|
syn match solsticeNumber "\d\+\(\.\d\+\)\="
|
||||||
|
syn region solsticeString start=/"/ end=/"/
|
||||||
|
syn match solsticeCharacter /'[^']'/
|
||||||
|
|
||||||
|
" Operators
|
||||||
|
syn match solsticeOperator "==\|!=\|>=\|<=\|++\|--\|+\=\|-\=\|\*=\|\/="
|
||||||
|
syn match solsticeOperator "[><=+\-*/]"
|
||||||
|
|
||||||
|
" Delimiters
|
||||||
|
syn match solsticeDelimiter "[{()}]"
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
let b:current_syntax = "solstice"
|
||||||
Reference in New Issue
Block a user