83 Commits

Author SHA1 Message Date
7c0490668e Revert "started writing lexer after making string lib"
This reverts commit c0c35e4d17.
2026-04-13 20:00:55 +10:00
e5da1d255b started writing lexer after making string lib 2026-04-13 20:00:38 +10:00
c0c35e4d17 started writing lexer after making string lib 2026-04-13 19:59:48 +10:00
f67c045845 need to fix some solstice bugs before i can start on lexer 2026-04-13 13:00:51 +10:00
98c8775208 clear out c code 2026-04-13 09:30:17 +10:00
f384e19c06 private and protected fields 2026-04-12 21:16:59 +10:00
d24462f844 Don't evaluate left of '=' 2026-04-12 19:35:56 +10:00
4351821d30 Destructor example 2026-04-11 20:42:14 +10:00
78f974e189 Destructors 2026-04-11 20:41:54 +10:00
1dedb30a87 Constructors 2026-04-11 17:05:20 +10:00
2e7b5b7480 Call methods inside objects 2026-04-11 12:03:10 +10:00
1cf995f7ac Fixes for structs 2026-04-10 19:38:26 +10:00
605d0a87b1 Update highlighting 2026-04-10 19:37:41 +10:00
16569d7355 Struct member writing 2026-04-10 14:57:01 +10:00
fd08b7cdb7 Struct field access (slightly buggy) 2026-04-10 10:14:23 +10:00
5841a7a999 Parse object member access 2026-04-10 10:00:24 +10:00
a2fc138ba1 'new' keyword 2026-04-09 19:00:40 +10:00
9b55b509f5 Fix struct interaction with type system 2026-04-09 17:13:51 +10:00
f694f50d70 Fix struct parsing 2026-04-09 17:13:13 +10:00
5b61a11f00 Codegen for structs 2026-04-09 16:04:53 +10:00
00d6ed83fb Parse structs 2026-04-09 15:39:17 +10:00
6988f314b0 Parse exprs in parens 2026-04-09 11:43:00 +10:00
70dc5eb5a0 Parse expressions in parens (3 + 2) * 4 2026-04-09 10:38:58 +10:00
1e3bd6c601 Division no longer becomes subtraction 2026-04-09 10:17:37 +10:00
f66464a7cc Stuff 2026-04-09 10:01:30 +10:00
631b587d07 Fix a load of stuff 2026-03-07 16:26:54 +11:00
445ba032f5 Add unistd.sols to libraries 2026-03-07 16:26:20 +11:00
41a2fa53c6 Start work on WASM compatibility, lambda fix 2026-03-06 09:24:16 +11:00
00ef8a7d56 Type parser 2026-03-05 19:32:31 +11:00
f1eee4f6a8 Command line argument parsing 2026-03-04 12:25:59 +11:00
cfca3c1d7a Lambdas now capture state 2026-03-03 08:00:00 +11:00
40e0aec0d4 Package builder 2026-03-01 16:33:00 +11:00
38473f0e01 Friendship ended with C++, C is my new best friend 2026-03-01 16:00:03 +11:00
139be30e2d Fix spelling mistake 2026-01-29 09:07:43 +11:00
d576c7cdfc Wrap websockets in request library 2026-01-28 21:11:54 +11:00
2e4dbce100 Fix Makefile 2026-01-28 20:24:15 +11:00
ea6bf5925b Add file and request libs 2026-01-28 20:13:28 +11:00
142268c016 Output ground files as executable 2026-01-28 18:58:39 +11:00
1c6b145d35 Update Makefile 2026-01-28 17:44:38 +11:00
6002bd922b (BREAKING) "use" keyword, stdlib seperate 2026-01-28 17:03:40 +11:00
4b86fee7b5 Object field assignment 2026-01-28 14:56:36 +11:00
d12036fe70 Fix type issues with struct access 2026-01-27 21:30:50 +11:00
59e273b26e Member access (no writing yet) 2026-01-27 21:12:09 +11:00
e285b2c59f Use new to create an object from a struct 2026-01-27 14:10:57 +11:00
e3abe07f4b Add intToString() and stringToInt() functions 2026-01-27 12:19:26 +11:00
010d155f5d Start work on structs 2026-01-25 17:40:07 +11:00
44785185f7 Update syntax highlighting 2026-01-25 13:37:03 +11:00
c82f81f668 Compilation with ground->asm 2026-01-21 16:26:17 +11:00
9397f410da Use Chookspace Pages to redirect auto to sols.dev 2026-01-20 12:14:15 +11:00
6d5d29f05b Use inbuilt stdlib instead of hard coded functions 2026-01-12 16:18:59 +11:00
48c130351a Fix indentation 2026-01-12 15:43:18 +11:00
6bc483b1db Inline ground 2026-01-12 15:24:37 +11:00
c5b67bff72 Function calling fix 2026-01-12 14:45:07 +11:00
ca8db171d9 Type checking for functions 2025-12-29 10:30:07 +11:00
c266728ff0 make function calling better 2025-12-28 13:49:05 +11:00
b46a66cea7 Partially working function calling 2025-12-28 13:41:05 +11:00
337b88c148 Fix function calling bug 2025-12-26 13:50:14 +11:00
d8812fa14e Refactor type system, print all errors in file 2025-12-26 13:28:47 +11:00
24ea348858 Fix clang builds 2025-12-25 22:42:25 +11:00
9a311c3cb8 Better errors, comments 2025-12-25 22:37:40 +11:00
aa5ef0e664 Operator precedence I think 2025-12-24 22:01:16 +11:00
7ff306b9e8 Some math (no order of operations yet) 2025-12-24 16:22:51 +11:00
ac7f22e1bc Continue type checker work 2025-12-24 14:31:43 +11:00
5ec2f86b70 Start work on static type checking 2025-12-24 13:16:11 +11:00
7dd2b10603 Direct users to solstice website for docs 2025-12-23 23:47:01 +11:00
7351604571 Vim syntax highlighting 2025-12-22 20:38:02 +11:00
43310c70bf Clean up temporary variables properly 2025-12-22 19:38:06 +11:00
957e0fd95a Start work on dropping tmp vars after use 2025-12-22 14:26:12 +11:00
525b2bc733 Add >, >=, <, <= 2025-12-22 13:49:44 +11:00
d2f295f46a refactor 2025-12-21 12:06:55 +11:00
869f71466e Add argument parser, output files 2025-12-21 11:42:18 +11:00
a3a9553189 Function calling and input() builtin 2025-12-20 20:25:48 +11:00
3c66df5be0 Add fib test 2025-12-20 16:27:54 +11:00
ce058c8f9c Update README.md 2025-12-20 15:29:18 +11:00
2aff15a317 Add logo 2025-12-20 15:28:39 +11:00
72ec9c1fb6 Fixes, rebrand 2025-12-20 15:09:09 +11:00
c04e631180 Half working stuff 2025-12-20 14:47:24 +11:00
16a406b52f Variables 2025-12-20 14:28:39 +11:00
0488067ef2 While loop 2025-12-20 07:37:15 +11:00
a8e5f6a0f1 Proper string parsing 2025-12-20 06:58:41 +11:00
e8bf7b70f7 Fix lexer 2025-12-19 17:01:57 +11:00
b5c8b1b7ec WIP conditionals work 2025-12-19 16:17:57 +11:00
99bc0dbdc2 If statements 2025-12-15 11:37:27 +11:00
34 changed files with 527 additions and 604 deletions

4
.gitignore vendored
View File

@@ -1 +1,3 @@
hg
solstice
build
.*_solsbuild

5
.project.fish Normal file
View File

@@ -0,0 +1,5 @@
# source .project.fish
alias run "make && ./solstice"
alias clean "make clean"
alias cleanrun "make clean && make && ./solstice"

View File

@@ -1,49 +1,21 @@
# High Ground
![](https://chookspace.com/max/solstice/raw/branch/master/logo/solstice.png)
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
First, ensure CGround is installed on your system with `sudo make install`. Then, compile with
```
g++ src/main.cpp -o hg -lgroundvm
make
```
## 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
* `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
```
Docs are avaliable at https://sols.dev/docs/

14
chookspace/index.html Normal file
View 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
View 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
View File

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

23
libs/io.sols Normal file
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

78
logo/solstice.svg Normal file
View 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

BIN
src/lexer/.DS_Store vendored Normal file

Binary file not shown.

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

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

View File

@@ -1,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);
}

0
src/main.sols Normal file
View File

9
tests/closure.sols Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,6 @@
number = 0
while number != 10 {
number = number + 1
puts number
}

View File

@@ -1,5 +0,0 @@
puts "dingus"
puts 432
puts 3.141
puts 'c'
puts true

7
tests/data.sols Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,7 @@
if 1 == 1 {
puts "huzzah"
}
if 5 == 10 {
puts "aww"
}

7
tests/inlineground.sols Normal file
View File

@@ -0,0 +1,7 @@
result = 0
ground {
println "dingus"
add 3 2 &result
}
puts result

15
tests/input.sols Normal file
View 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!"

View File

@@ -1 +1,3 @@
use io
println("dingus")

3
tests/set.sols Normal file
View File

@@ -0,0 +1,3 @@
x = 5
y = 10
puts x + y

32
tests/struct.sols Normal file
View File

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

3
tests/while.sols Normal file
View File

@@ -0,0 +1,3 @@
while 1 == 1 {
puts "yay infinite loop"
}

View File

@@ -0,0 +1 @@
au BufRead,BufNewFile *.sols setfiletype solstice

45
vim/syntax/solstice.vim Normal file
View File

@@ -0,0 +1,45 @@
" Vim highlight file for Solstice (.sols)
if exists("b:current_syntax")
finish
endif
" Keywords
syn keyword solsKeyword puts if while def lambda return use struct ground
syn keyword solsBool true false
" Types
syn keyword solsType int double string char bool fun template object
" Strings and chars
syn region solsString start=/"/ skip=/\\"/ end=/"/
syn region solsChar start=/'/ skip=/\\'/ end=/'/
" Numbers
syn match solsFloat /\<[0-9]\+\.[0-9]*\>/
syn match solsInt /\<[0-9]\+\>/
" Operators
syn match solsOperator /+\|-\|\*\|\/\|=\|!\|>\|<\|+=\|-=\|\*=\|\/=\|++\|--\|==\|!=\|>=\|<=/
" Delimiters
syn match solsDelimiter /[{}(),;]/
" Comments
syn match solsComment /\/\/.*$/
syn match solsComment /#.*$/
" Highlight links
hi def link solsKeyword Keyword
hi def link solsBool Boolean
hi def link solsType Type
hi def link solsString String
hi def link solsChar Character
hi def link solsFloat Float
hi def link solsInt Number
hi def link solsOperator Operator
hi def link solsDelimiter Delimiter
hi def link solsComment Comment
let b:current_syntax = "solstice"