5 Commits

Author SHA1 Message Date
0796bee066 Update compiler 2026-01-21 11:38:06 +11:00
e5a39f07fb Update .gitignore 2025-10-13 09:17:54 +11:00
fa5d805eef Refactoring (MAY BE BUGGY) 2025-10-13 09:16:28 +11:00
81d6e21a00 Deprecate *list syntax, use &direct refs instead 2025-09-25 08:09:27 +10:00
7066b4300d Fix "init &var -list" 2025-09-22 20:03:35 +10:00
25 changed files with 3189 additions and 2938 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
ground ground
Bobfile Bobfile
build

View File

@@ -1,5 +0,0 @@
compiler "g++";
binary "ground";
source "src/main.cpp";
flag "Ofast";
compile;

22
Makefile Normal file
View File

@@ -0,0 +1,22 @@
CXX = g++
CXXFLAGS = -O3 -Isrc -Wall -Wextra -std=c++17
BUILD_DIR = build
SRCS = $(shell find src -name '*.cpp')
VPATH = $(sort $(dir $(SRCS)))
OBJS = $(addprefix $(BUILD_DIR)/, $(notdir $(SRCS:.cpp=.o)))
TARGET = ground
all: $(TARGET)
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS)
$(BUILD_DIR)/%.o: %.cpp
@mkdir -p $(BUILD_DIR)
$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
rm -rf $(BUILD_DIR) $(TARGET)
.PHONY: all clean

View File

@@ -2,7 +2,7 @@
## What is Ground? ## What is Ground?
Ground is a sigma interpreter which processes and interprets Ground instructions. It is quite fast, and the syntax is simple. Ground is an interpreter which processes and interprets Ground instructions. It is quite fast, and the syntax is simple.
## What are the main features of Ground? ## What are the main features of Ground?
@@ -13,13 +13,7 @@ Ground is a sigma interpreter which processes and interprets Ground instructions
## How do I get started? ## How do I get started?
Clone the repo and compile with your favourite C++ compiler: Clone the repo and compile with `make`.
```
g++ src/main.cpp -std=c++17 -O3 -o ground
```
(You can omit the -std flag on systems which default to the latest standard, and the -O3 flag if you're fine with a slightly slower interpreter.)
Run a Ground program: Run a Ground program:

View File

@@ -44,12 +44,6 @@ and jump to that (setting labels will be discussed below):
jump %myLabel jump %myLabel
``` ```
Reference a list (a list reference) with an asterisk:
```
setlist *myList $value1 $value2 # and so on
```
Add comments with a `#`: Add comments with a `#`:
``` ```
@@ -128,31 +122,31 @@ Note: You can also replace &var1 with a list or line reference to check if it al
Allows you to initialize a list. Allows you to initialize a list.
Usage: `setlist *list $value1 $value2 $value3...` Usage: `setlist &list $value1 $value2 $value3...`
#### setlistat #### setlistat
Sets a list item at an index. The item at the index must already exist. Lists are index 0. Sets a list item at an index. The item at the index must already exist. Lists are index 0.
Usage: `setlistat *list $intvalue $value` Usage: `setlistat &list $intvalue $value`
#### getlistat #### getlistat
Gets a list item at an index, and puts it in the variable provided. The item at the index must already exist. Lists are index 0. Gets a list item at an index, and puts it in the variable provided. The item at the index must already exist. Lists are index 0.
Usage: `getlistat *list $intvalue &var` Usage: `getlistat &list $intvalue &var`
#### getlistsize #### getlistsize
Gets the size of a list and puts it in the variable provided. Gets the size of a list and puts it in the variable provided.
Usage: `getlistsize *list &var` Usage: `getlistsize &list &var`
#### listappend #### listappend
Appends an item to a list. Appends an item to a list.
Usage: `listappend *list $var` Usage: `listappend &list $var`
### String Operations ### String Operations

170
src/compiler/compiler.cpp Normal file
View File

@@ -0,0 +1,170 @@
#include "compiler.h"
#include "../main.h"
#include <vector>
#include <iostream>
#include <llvm-c/TargetMachine.h>
void error(std::string in) {
std::cout << "Compiler error:" << in << std::endl;
exit(1);
}
GroundCompiler::GroundCompiler(const std::string& moduleName) {
context = std::make_unique<llvm::LLVMContext>();
module = std::make_unique<llvm::Module>(moduleName, *context);
builder = std::make_unique<llvm::IRBuilder<>>(*context);
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetAsmParser();
}
void GroundCompiler::createMainFunction() {
llvm::FunctionType* mainType = llvm::FunctionType::get(
llvm::Type::getInt32Ty(*context), false
);
currentFunction = llvm::Function::Create(
mainType, llvm::Function::ExternalLinkage, "main", module.get()
);
llvm::BasicBlock* entry = llvm::BasicBlock::Create(
*context, "entry", currentFunction
);
builder -> SetInsertPoint(entry);
}
llvm::Function* GroundCompiler::getPrintfFunction() {
llvm::Function* printfFunc = module->getFunction("printf");
if (!printfFunc) {
llvm::FunctionType* printfType = llvm::FunctionType::get(
llvm::Type::getInt32Ty(*context),
//llvm::Type::getInt8PtrTy(*context),
true
);
printfFunc = llvm::Function::Create(
printfType,
llvm::Function::ExternalLinkage,
"printf",
module.get()
);
}
return printfFunc;
}
llvm::AllocaInst* GroundCompiler::createVariable(const std::string& name, llvm::Type* type) {
if (namedVals.find(name) != namedVals.end()) {
return namedVals[name];
}
llvm::IRBuilder<> tmpBuilder(
&currentFunction -> getEntryBlock(),
currentFunction -> getEntryBlock().begin()
);
llvm::AllocaInst* alloca = tmpBuilder.CreateAlloca(type, nullptr, name);
namedVals[name] = alloca;
return alloca;
}
void GroundCompiler::compileInstruction(const Instruction& inst) {
switch (inst.inst) {
case Instructions::Set:
{
if (inst.args.size() < 2) break;
const Direct& varRef = std::get<Direct>(inst.args[0]);
const Literal& value = std::get<Literal>(inst.args[1]);
llvm::Value* val = nullptr;
llvm::Type* type = nullptr;
if (std::holds_alternative<int>(value.val)) {
int intVal = std::get<int>(value.val);
val = llvm::ConstantInt::get(*context, llvm::APInt(32, intVal, true));
type = llvm::Type::getInt32Ty(*context);
} else {
error("Unsupported type at present");
}
llvm::AllocaInst* variable = createVariable(varRef.varName, type);
builder->CreateStore(val, variable);
break;
}
default:
error("For now, only set instruction is supported in compiler");
}
}
void GroundCompiler::createBasicBlocks(const std::vector<Instruction>& instructions) {
for (size_t i = 0; i < instructions.size(); i++) {
llvm::BasicBlock* bb = llvm::BasicBlock::Create(
*context, "line_" + std::to_string(i), currentFunction
);
lineBlocks[i] = bb;
}
}
void GroundCompiler::compile(const std::vector<Instruction>& instructions) {
createMainFunction();
createBasicBlocks(instructions);
for (const auto& inst : instructions) {
compileInstruction(inst);
}
std::string errorStr;
llvm::raw_string_ostream errorStream(errorStr);
if (llvm::verifyModule(*module, &errorStream)) {
error("Module verification error" + errorStr);
}
}
bool GroundCompiler::writeObjectFile(const std::string& filename) {
char* targetTripleCStr = LLVMGetDefaultTargetTriple();
std::string targetTriple(targetTripleCStr);
LLVMDisposeErrorMessage(targetTripleCStr);
module -> setTargetTriple(targetTriple);
std::string error;
const llvm::Target* target = llvm::TargetRegistry::lookupTarget(targetTriple, error);
if (!target) {
llvm::errs() << "Target Error: " << error;
return false;
}
llvm::TargetOptions opt;
llvm::TargetMachine* targetMachine = target -> createTargetMachine(
targetTriple, "generic", "", opt, llvm::Reloc::PIC_
);
module -> setDataLayout(targetMachine -> createDataLayout());
std::error_code ec;
llvm::raw_fd_ostream dest(filename, ec, llvm::sys::fs::OF_None);
if (ec) {
llvm::errs() << "Couldn't open file, error: " << ec.message();
return false;
}
llvm::legacy::PassManager pass;
if (targetMachine -> addPassesToEmitFile(pass, dest, nullptr, llvm::CodeGenFileType::ObjectFile)) {
llvm::errs() << "Target machine cannot emit object file";
return false;
}
pass.run(*module);
dest.flush();
return true;
}
void GroundCompiler::printIR() {
module->print(llvm::outs(), nullptr);
}
void compile(std::vector<Instruction> instructions) {
std::cout << "gonna compile some crap" << std::endl;
GroundCompiler compiler("cool_program");
compiler.compile(instructions);
compiler.printIR();
}

47
src/compiler/compiler.h Normal file
View File

@@ -0,0 +1,47 @@
#pragma once
#include <vector>
#include <memory>
#include <map>
#include <string>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Verifier.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Target/TargetOptions.h>
#include <llvm/MC/TargetRegistry.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/IR/LegacyPassManager.h>
//#include <llvm/Support/Host.h>
#include "../main.h"
void error(std::string in);
// class to make llvming around easier
class GroundCompiler {
private:
std::unique_ptr<llvm::LLVMContext> context;
std::unique_ptr<llvm::Module> module;
std::unique_ptr<llvm::IRBuilder<>> builder;
std::map<std::string, llvm::AllocaInst*> namedVals;
llvm::Function* currentFunction;
std::map<int, llvm::BasicBlock*> lineBlocks;
public:
GroundCompiler(const std::string& moduleName);
void createMainFunction();
llvm::Function* getPrintfFunction();
llvm::AllocaInst* createVariable(const std::string& name, llvm::Type* type);
void compileInstruction(const Instruction& inst);
void createBasicBlocks(const std::vector<Instruction>& instructions);
void compile(const std::vector<Instruction>& instructions);
bool writeObjectFile(const std::string& filename);
void printIR();
};
void compile(std::vector<Instruction> instructions);

244
src/data/data.cpp Normal file
View File

@@ -0,0 +1,244 @@
#include "data.h"
#include "../main.h"
#include "../error/error.h"
#include <string>
#include <vector>
#include <map>
#include <stack>
/*
labelStack stack
Allows each function to hold it's own set of labels
*/
std::stack<std::map<std::string, int>> labelStack;
/*
variables map
Contains all variables made while running the program. See also Literal struct.
*/
std::map<std::string, Literal> variables;
/*
functions map
Contains the code of functions and types of their values
*/
std::map<std::string, Function> functions;
/*
structs map
Contains structs (see the Struct struct for more info)
*/
std::map<std::string, Struct> structs;
/*
fnArgs vector
Containst the arguments to be passed to a function
*/
std::vector<Literal> fnArgs;
// External library functions and other things
// Handle to loaded libraries
std::map<std::string, void*> loadedLibraries;
// Map of function name to function pointer
std::map<std::string, void*> externalFunctions;
// Libraries currently imported
std::vector<std::string> libraries;
// Conversion functions
GroundValue literalToGroundValue(const Literal& lit) {
GroundValue gv;
if (std::holds_alternative<int>(lit.val)) {
gv.type = GROUND_INT;
gv.data.int_val = std::get<int>(lit.val);
} else if (std::holds_alternative<double>(lit.val)) {
gv.type = GROUND_DOUBLE;
gv.data.double_val = std::get<double>(lit.val);
} else if (std::holds_alternative<bool>(lit.val)) {
gv.type = GROUND_BOOL;
gv.data.bool_val = std::get<bool>(lit.val) ? 1 : 0;
} else if (std::holds_alternative<std::string>(lit.val)) {
gv.type = GROUND_STRING;
gv.data.string_val = const_cast<char*>(std::get<std::string>(lit.val).c_str());
} else if (std::holds_alternative<char>(lit.val)) {
gv.type = GROUND_CHAR;
gv.data.char_val = std::get<char>(lit.val);
}
return gv;
}
/*
catches stackmap
This stores all catches in a scope, to catch errors before they happen.
*/
std::stack<std::map<std::string, Direct>> catches;
Literal groundValueToLiteral(const GroundValue& gv) {
Literal lit;
switch (gv.type) {
case GROUND_INT:
lit.val = gv.data.int_val;
break;
case GROUND_DOUBLE:
lit.val = gv.data.double_val;
break;
case GROUND_BOOL:
lit.val = (gv.data.bool_val != 0);
break;
case GROUND_STRING:
lit.val = std::string(gv.data.string_val);
break;
case GROUND_CHAR:
lit.val = gv.data.char_val;
break;
}
return lit;
}
/*
is* functions
These functions determine if a string value can be converted into a different type.
*/
bool isInt(std::string in) {
try {
std::stoi(in);
if (std::stod(in) != std::stoi(in)) return false;
return true;
} catch (...) {
return false;
}
}
bool isDouble(std::string in) {
try {
std::stod(in);
return true;
} catch (...) {
return false;
}
}
bool isBool(std::string in) {
if (in == "true" || in == "false") return true;
else return false;
}
bool isString(std::string in) {
if (in.size() >= 2 && in[0] == '"' && in[in.size() - 1] == '"') return true;
else return false;
}
bool isChar(std::string in) {
if (in.size() == 3 && in[0] == '\'' && in[in.size() - 1] == '\'') return true;
else return false;
}
bool isValue(std::string in) {
if (in.size() >= 1 && in[0] == '$') return true;
else return false;
}
bool isDirect(std::string in) {
if (in.size() >= 1 && in[0] == '&') return true;
else return false;
}
bool isLine(std::string in) {
if (in.size() >= 1 && in[0] == '%') return true;
else return false;
}
bool isLabel(std::string in) {
if (in.size() >= 1 && in[0] == '@') return true;
else return false;
}
bool isType(std::string in) {
if (in.size() > 1 && in[0] == '-') return true;
else return false;
}
bool isFunction(std::string in) {
if (in.size() >= 1 && in[0] == '!') return true;
else return false;
}
bool isReferencingStruct(std::string in) {
return in.find('.') != std::string::npos;
}
/*
getType function
This function determines the type of a value inside a string based on the is*
functions above. Returns a type from the Types enum class.
*/
Types getType(std::string in) {
if (isInt(in)) return Types::Int;
if (isDouble(in)) return Types::Double;
if (isBool(in)) return Types::Bool;
if (isString(in)) return Types::String;
if (isChar(in)) return Types::Char;
if (isValue(in)) return Types::Value;
if (isDirect(in)) return Types::Direct;
if (isLine(in)) return Types::Line;
if (isLabel(in)) return Types::Label;
if (isType(in)) return Types::Type;
if (isFunction(in)) return Types::Function;
error("Could not determine type of \"" + in + "\"");
return Types::Int;
}
/*
getLitType function
This function determines the type of a value inside a Literal based on the
holds_alternative() function. Returns a type from the Types enum class.
*/
Types getLitType(Literal in) {
if (std::holds_alternative<int>(in.val)) return Types::Int;
if (std::holds_alternative<double>(in.val)) return Types::Double;
if (std::holds_alternative<bool>(in.val)) return Types::Bool;
if (std::holds_alternative<std::string>(in.val)) return Types::String;
if (std::holds_alternative<char>(in.val)) return Types::Char;
if (std::holds_alternative<List>(in.val)) return Types::List;
return Types::Other;
}
/*
setVal function
This function sets the value of a variable (whether in a struct or not).
*/
void setVal(std::string varName, Literal value) {
if (isReferencingStruct(varName)) {
std::string structName;
std::string varInStruct;
size_t dotPos = varName.find('.'); // Use size_t
if (dotPos != std::string::npos) {
structName = varName.substr(0, dotPos);
varInStruct = varName.substr(dotPos + 1);
if (variables.find(structName) != variables.end()) {
if (std::holds_alternative<Struct>(variables[structName].val)) {
Struct structVal = std::get<Struct>(variables[structName].val);
if (structVal.values.find(varInStruct) != structVal.values.end()) {
structVal.values[varInStruct] = value;
variables[structName].val = structVal; // Write back the modified struct
} else {
error("Could not find property '" + varInStruct + "' in struct '" + structName + "'");
}
} else {
error("Variable '" + structName + "' is not a struct");
}
} else {
error("Could not find struct '" + structName + "'");
}
} else {
error("Invalid struct member access syntax");
}
} else {
// Handle regular variables (both existing and new)
variables[varName] = value;
}
}

66
src/data/data.h Normal file
View File

@@ -0,0 +1,66 @@
#pragma once
#include "../main.h"
#include <string>
#include <vector>
#include <map>
#include <stack>
/*
functions map
Contains the code of functions and types of their values
*/
extern std::map<std::string, Function> functions;
/*
structs map
Contains structs (see the Struct struct for more info)
*/
extern std::map<std::string, Struct> structs;
/*
fnArgs vector
Containst the arguments to be passed to a function
*/
extern std::vector<Literal> fnArgs;
// External library functions and other things
// Handle to loaded libraries
extern std::map<std::string, void*> loadedLibraries;
// Map of function name to function pointer
extern std::map<std::string, void*> externalFunctions;
// Libraries currently imported
extern std::vector<std::string> libraries;
// catches stackmap
extern std::stack<std::map<std::string, Direct>> catches;
// labelStack stack
extern std::stack<std::map<std::string, int>> labelStack;
// variables map
extern std::map<std::string, Literal> variables;
GroundValue literalToGroundValue(const Literal& lit);
Literal groundValueToLiteral(const GroundValue& gv);
void setVal(std::string varName, Literal value);
bool isInt(std::string in);
bool isDouble(std::string in);
bool isBool(std::string in);
bool isString(std::string in);
bool isChar(std::string in);
bool isValue(std::string in);
bool isDirect(std::string in);
bool isLine(std::string in);
bool isLabel(std::string in);
bool isType(std::string in);
bool isFunction(std::string in);
bool isReferencingStruct(std::string in);
Types getType(std::string in);
Types getLitType(Literal in);
void setVal(std::string varName, Literal value);

34
src/error/error.cpp Normal file
View File

@@ -0,0 +1,34 @@
#include <iostream>
#include <cstdlib>
#include "error.h"
#include "../data/data.h"
#include "../main.h"
using namespace std;
/*
error function
Takes a string (which is a debug message) and prints it to the console, letting the
user know what went wrong with the program.
*/
Literal error(string in, string errCode, int exitc) {
Error retError;
retError.code = errCode;
retError.pops = 0;
while (catches.size() > 0) {
if (catches.top().find(errCode) != catches.top().end()) {
Literal tmpLit;
tmpLit.val = false;
setVal(catches.top()[errCode].varName, tmpLit);
retError.reporter = catches.top()[errCode].varName;
Literal tmpLit2;
tmpLit2.val = retError;
return tmpLit2;
} else {
catches.pop();
retError.pops ++;
}
}
cout << "\033[31m" + errCode + ": \033[39m" << in << endl;
exit(exitc);
}

6
src/error/error.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include "../main.h"
#include <string>
Literal error(std::string in, std::string errCode = "syntaxError", int exitc = 1);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
#pragma once
#include <vector>
#include "../main.h"
void preProcessLabels(std::vector<Instruction> instructions);
Literal exec(std::vector<Instruction> in, bool executingFunction = false);

81
src/lexer/lexer.cpp Normal file
View File

@@ -0,0 +1,81 @@
#include <vector>
#include <string>
#include "lexer.h"
using namespace std;
/*
lexer function
This function takes the raw text from the file and splits it into a format
that the parser can understand.
*/
vector<vector<string>> lexer(string in) {
vector<vector<string>> out;
vector<string> line;
string buf;
bool procString = false;
bool procChar = false;
bool isComment = false;
for (char i : in) {
switch (i) {
case '"':
if (!isComment) {
if (procChar) {
buf.push_back(i);
} else {
procString = !procString;
buf.push_back(i);
}
}
break;
case '\'':
if (!isComment) {
if (procString) {
buf.push_back(i);
} else {
procChar = !procChar;
buf.push_back(i);
}
}
break;
case '\n':
if (!procString && !procChar) {
if (!buf.empty()) line.push_back(buf);
out.push_back(line);
buf.clear();
line.clear();
isComment = false;
} else {
if (!isComment) buf.push_back(i);
}
break;
case '#':
if (!procString && !procChar) {
isComment = true;
if (!buf.empty()) line.push_back(buf);
out.push_back(line);
buf.clear();
line.clear();
} else {
buf.push_back(i);
}
break;
case ' ':
if (!procString && !procChar) {
if (!buf.empty() && !isComment) line.push_back(buf);
buf.clear();
} else {
buf.push_back(i);
}
break;
default:
if (!isComment) buf.push_back(i);
break;
}
}
if (!buf.empty()) line.push_back(buf);
out.push_back(line);
return out;
}

6
src/lexer/lexer.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include <vector>
#include <string>
std::vector<std::vector<std::string>> lexer(std::string in);

File diff suppressed because it is too large Load Diff

250
src/main.h Normal file
View File

@@ -0,0 +1,250 @@
#pragma once
#include <vector>
#include <string>
#include <map>
#include <variant>
// Headers for external libraries
#ifdef _WIN32
// Note: Windows support is experiemental. Maybe try using a superior
// operating system? (cough cough, Linux?)
#include <windows.h>
#define DLOPEN(path) LoadLibrary(path)
#define DLSYM(handle, name) GetProcAddress(handle, name)
#define DLCLOSE(handle) FreeLibrary(handle)
#define DLERROR() "Windows DLL Error"
#else
#include <dlfcn.h>
#define DLOPEN(path) dlopen(path, RTLD_LAZY)
#define DLSYM(handle, name) dlsym(handle, name)
#define DLCLOSE(handle) dlclose(handle)
#define DLERROR() dlerror()
#endif
/*
Instructions enum class
For each function keyword, an instruction is assigned. See also parser
function, interpreter function, Instruction struct
*/
enum class Instructions {
Jump, If,
Stdout, Stdin, Stdlnout,
Add, Subtract, Multiply, Divide,
Equal, Inequal, Greater, Lesser, Not,
End, Set, Empty, Gettype, Exists,
Setlist, Getlistat, Setlistat, Getlistsize, Listappend, Listprepend,
Getstrcharat, Getstrsize,
Stoi, Stod, Tostring,
Fun, Return, Endfun, Pusharg, Call, Local,
Use, Extern, Error, Catch, Try, Exception,
Struct, Endstruct, Init
};
/*
Types enum class
Assists in type checking in the parser function. For example, the
following values correspond to the following types:
1 Int
3.14 Double
"Hello!" String
'e' Char
true Bool
$value Value
&var Direct
%10 Line
See also parser function
*/
enum class Types {
Int, Double, String, Char, Bool, Value, Direct, Line, List, Label, Type, Function, Other
};
// Forward declaration of Literal for list
struct Literal;
/*
List struct
Contains literal values inside a vector. For example, if the following
program was written:
setlist #myNums 3 5 9 13
The List struct which would be created and stored should look like this:
{
val = {
Literal {
val = 3
},
Literal {
val = 5
},
Literal {
val = 9
},
Literal {
val = 13
},
}
}
All elements in the list must be of the same type. See also Literal struct.
*/
struct List {
std::vector<Literal> val;
};
/*
Direct struct
If the program being executed makes a direct reference, it is stored in a Direct
struct. For example, if the following line was written:
stdin &myVar
The Direct struct in the instruction should look like this:
{
varName = "myVar";
}
*/
struct Direct {
std::string varName;
};
struct TypeRef {
bool isCustomType = false;
std::string customType;
Types type;
};
struct FunctionRef {
std::string fnName;
};
/*
Label struct
Contains information needed to register labels
*/
struct Label {
std::string id;
int lineNum = -1;
};
/*
Line struct
Contains information needed to jump to lines
*/
struct Line {
int lineNum = -1;
bool isLabel = false;
std::string label;
};
/*
ValueRef struct
If the program being executed makes a value reference, it is stored in a ValueRef
struct. For example, if the following line was written:
stdin &myVar
The ValueRef struct in the instruction should look like this:
{
varName = "myVar";
}
*/
struct ValueRef {
std::string varName;
};
/*
Instruction struct
An instruction usually corresponds to a line in the program being interpreted.
For example, if the following line was written:
add 5 $myVar &outVar
The instruction should look like this:
{
inst = Instructions::Add;
args = {
Literal {
val = 5
},
ValueRef {
varName = "myVar"
},
Direct {
varName = "outVar"
}
};
}
inst starts as empty, so empty lines and commented out lines do not get in the
way of jump and if.
See also: Instructions enum class, Literal struct, ValueRef struct, Direct struct,
Line struct, exec function, parser function
*/
typedef std::variant<Literal, ValueRef, FunctionRef, TypeRef, Direct, Line> argument;
struct Instruction {
Instructions inst = Instructions::Empty;
std::vector<argument> args;
bool isLabel = false;
Label label;
};
struct FnArg {
Direct ref;
Types type;
};
/*
Function struct
Contains information needed to run a Ground function.
*/
struct Function {
Types returnType;
std::vector<FnArg> args;
std::vector<Instruction> instructions;
std::vector<Label> labels;
};
/*
Struct struct
This struct stores data for structures in Ground.
*/
struct Struct {
std::map<std::string, Literal> values;
std::map<std::string, Function> functions;
};
struct Error {
std::string code;
int pops;
std::string reporter;
};
/*
Literal struct
Contains literal values. For example, if the following line was written:
stdout "Hello world!"
The Literal struct in the instruction should look like this:
{
val = "Hello world!"; // I am ignoring the variant for simplicity
// of documenting the code
}
All value references are swapped out for their respective Literal they
point to. See also variables map, parser function, interpreter function
*/
struct Literal {
std::variant<int, double, bool, std::string, char, List, Struct, Error> val;
};
// C-compatible enum and types for developing libraries for Ground in C
typedef enum {
GROUND_INT,
GROUND_DOUBLE,
GROUND_BOOL,
GROUND_STRING,
GROUND_CHAR
} GroundType;
typedef struct {
GroundType type;
union {
int int_val;
double double_val;
int bool_val;
char* string_val;
char char_val;
} data;
} GroundValue;

184
src/parser/parser.cpp Normal file
View File

@@ -0,0 +1,184 @@
#include <vector>
#include <string>
#include <map>
#include "parser.h"
#include "../main.h"
#include "../data/data.h"
#include "../error/error.h"
using namespace std;
/*
parser function
This function takes a vector of a vector of strings from the lexer and
converts it into a list of instructions that the interpreter can understand.
*/
vector<Instruction> parser(vector<vector<string>> in) {
vector<Instruction> out;
for (vector<string> line : in) {
Instruction currentInstruction;
if (line.size() < 1) {
out.push_back(currentInstruction);
continue;
}
string inst = line[0];
if (isLabel(inst)) {
currentInstruction.isLabel = true;
currentInstruction.label.id = inst.substr(1);
line.erase(line.begin());
}
else if (isFunction(inst)) {
currentInstruction.inst = Instructions::Call;
FunctionRef newFnRef;
newFnRef.fnName = inst.substr(1);
currentInstruction.args.push_back(newFnRef);
}
else if (inst == "stdout") currentInstruction.inst = Instructions::Stdout;
else if (inst == "print") currentInstruction.inst = Instructions::Stdout;
else if (inst == "stdlnout") currentInstruction.inst = Instructions::Stdlnout;
else if (inst == "println") currentInstruction.inst = Instructions::Stdlnout;
else if (inst == "stdin") currentInstruction.inst = Instructions::Stdin;
else if (inst == "input") currentInstruction.inst = Instructions::Stdin;
else if (inst == "add") currentInstruction.inst = Instructions::Add;
else if (inst == "subtract") currentInstruction.inst = Instructions::Subtract;
else if (inst == "multiply") currentInstruction.inst = Instructions::Multiply;
else if (inst == "divide") currentInstruction.inst = Instructions::Divide;
else if (inst == "set") currentInstruction.inst = Instructions::Set;
else if (inst == "end") currentInstruction.inst = Instructions::End;
else if (inst == "jump") currentInstruction.inst = Instructions::Jump;
else if (inst == "if") currentInstruction.inst = Instructions::If;
else if (inst == "equal") currentInstruction.inst = Instructions::Equal;
else if (inst == "inequal") currentInstruction.inst = Instructions::Inequal;
else if (inst == "greater") currentInstruction.inst = Instructions::Greater;
else if (inst == "lesser") currentInstruction.inst = Instructions::Lesser;
else if (inst == "not") currentInstruction.inst = Instructions::Not;
else if (inst == "setlist") currentInstruction.inst = Instructions::Setlist;
else if (inst == "getlistat") currentInstruction.inst = Instructions::Getlistat;
else if (inst == "setlistat") currentInstruction.inst = Instructions::Setlistat;
else if (inst == "getlistsize") currentInstruction.inst = Instructions::Getlistsize;
else if (inst == "stoi") currentInstruction.inst = Instructions::Stoi;
else if (inst == "stod") currentInstruction.inst = Instructions::Stod;
else if (inst == "tostring") currentInstruction.inst = Instructions::Tostring;
else if (inst == "fun") currentInstruction.inst = Instructions::Fun;
else if (inst == "return") currentInstruction.inst = Instructions::Return;
else if (inst == "endfun") currentInstruction.inst = Instructions::Endfun;
else if (inst == "pusharg") currentInstruction.inst = Instructions::Pusharg;
else if (inst == "call") currentInstruction.inst = Instructions::Call;
else if (inst == "use") currentInstruction.inst = Instructions::Use;
else if (inst == "extern") currentInstruction.inst = Instructions::Extern;
else if (inst == "getstrcharat") currentInstruction.inst = Instructions::Getstrcharat;
else if (inst == "getstrsize") currentInstruction.inst = Instructions::Getstrsize;
else if (inst == "error") currentInstruction.inst = Instructions::Error;
else if (inst == "catch") currentInstruction.inst = Instructions::Catch;
else if (inst == "try") currentInstruction.inst = Instructions::Try;
else if (inst == "exception") currentInstruction.inst = Instructions::Exception;
else if (inst == "gettype") currentInstruction.inst = Instructions::Gettype;
else if (inst == "exists") currentInstruction.inst = Instructions::Exists;
else if (inst == "listappend") currentInstruction.inst = Instructions::Listappend;
else if (inst == "listprepend") currentInstruction.inst = Instructions::Listprepend;
else if (inst == "struct") currentInstruction.inst = Instructions::Struct;
else if (inst == "endstruct") currentInstruction.inst = Instructions::Endstruct;
else if (inst == "init") currentInstruction.inst = Instructions::Init;
else {
error("Unknown instruction " + inst);
}
for (int i = 1; i < line.size(); i++) {
string arg = line[i];
Types type = getType(arg);
switch (type) {
case Types::Int:
{
Literal newLit;
newLit.val = stoi(arg);
currentInstruction.args.push_back(newLit);
}
break;
case Types::Double:
{
Literal newLit;
newLit.val = stod(arg);
currentInstruction.args.push_back(newLit);
}
break;
case Types::String:
{
Literal newLit;
newLit.val = arg.substr(1, arg.size() - 2);
currentInstruction.args.push_back(newLit);
}
break;
case Types::Char:
{
Literal newLit;
newLit.val = arg[1];
currentInstruction.args.push_back(newLit);
}
break;
case Types::Bool:
{
Literal newLit;
if (arg == "true") newLit.val = true;
else newLit.val = false;
currentInstruction.args.push_back(newLit);
}
break;
case Types::Value:
{
ValueRef newVal;
newVal.varName = arg.substr(1);
currentInstruction.args.push_back(newVal);
}
break;
case Types::Direct:
{
Direct newDir;
newDir.varName = arg.substr(1);
currentInstruction.args.push_back(newDir);
}
break;
case Types::Line:
{
Line newLine;
string lineRef = arg.substr(1);
if (isInt(lineRef)) {
newLine.lineNum = stoi(lineRef);
} else {
newLine.isLabel = true;
newLine.label = lineRef;
}
currentInstruction.args.push_back(newLine);
}
break;
case Types::Type:
{
TypeRef newTypeRef;
string type = arg.substr(1);
if (type == "int") newTypeRef.type = Types::Int;
else if (type == "double") newTypeRef.type = Types::Double;
else if (type == "string") newTypeRef.type = Types::String;
else if (type == "char") newTypeRef.type = Types::Char;
else if (type == "bool") newTypeRef.type = Types::Bool;
else if (type == "list") newTypeRef.type = Types::List;
else {
newTypeRef.isCustomType = true;
newTypeRef.customType = type;
};
currentInstruction.args.push_back(newTypeRef);
}
break;
case Types::Function:
{
FunctionRef newFuncRef;
newFuncRef.fnName = arg.substr(1);
currentInstruction.args.push_back(newFuncRef);
}
break;
default:
error("Could not parse argument " + arg);
}
}
out.push_back(currentInstruction);
}
return out;
}

7
src/parser/parser.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#include <vector>
#include <string>
#include "../main.h"
std::vector<Instruction> parser(std::vector<std::vector<std::string>> in);

View File

@@ -1,11 +1,11 @@
#!/usr/bin/env ground #!/usr/bin/env ground
stdlnout "Program args: " stdlnout "Program args: "
getlistsize *args &argsSize getlistsize &args &argsSize
set &counter 0 set &counter 0
@loopstart @loopstart
equal $counter $argsSize &bool equal $counter $argsSize &bool
if $bool %end if $bool %end
getlistat *args $counter &item getlistat &args $counter &item
stdlnout $item stdlnout $item
add 1 $counter &counter add 1 $counter &counter
jump %loopstart jump %loopstart

View File

@@ -20,19 +20,19 @@ stdlnout $testVar
# Lists # Lists
setlist *testList "Item 1" "Another Item" "Item the Third" setlist &testList "Item 1" "Another Item" "Item the Third"
getlistat *testList 2 &tmp getlistat &testList 2 &tmp
stdlnout $tmp stdlnout $tmp
setlistat *testList 1 "I changed this item" setlistat &testList 1 "I changed this item"
getlistat *testList 1 &tmp getlistat &testList 1 &tmp
stdlnout $tmp stdlnout $tmp
listappend *testList "I appended this item" listappend &testList "I appended this item"
getlistat *testList 3 &tmp getlistat &testList 3 &tmp
stdlnout $tmp stdlnout $tmp
getlistsize *testList &tmp getlistsize &testList &tmp
stdlnout $tmp stdlnout $tmp
# String Operations # String Operations

View File

@@ -2,8 +2,8 @@ set &testVar "dingus"
exists &testVar &exist exists &testVar &exist
stdlnout $exist stdlnout $exist
setlist *myList "item" setlist &myList "item"
exists *myList &exist exists &myList &exist
stdlnout $exist stdlnout $exist
@dingus @dingus
@@ -13,8 +13,5 @@ stdlnout $exist
exists &doesNotExist &exist exists &doesNotExist &exist
stdlnout $exist stdlnout $exist
exists *doesNotExist &exist
stdlnout $exist
exists %doesNotExist &exist exists %doesNotExist &exist
stdlnout $exist stdlnout $exist

View File

@@ -22,18 +22,18 @@ stdlnout "I called a function"
fun -list !dingus fun -list !dingus
stdlnout "Testing lists in functions" stdlnout "Testing lists in functions"
setlist *dingle "heheheha" "hahahahe" "hmmm" setlist &dingle "heheheha" "hahahahe" "hmmm"
return *dingle return $dingle
endfun endfun
call !dingus *outlist call !dingus &outlist
getlistsize *outlist &size getlistsize &outlist &size
set &counter 0 set &counter 0
@loopstart @loopstart
equal $size $counter &cond equal $size $counter &cond
if $cond %loopend if $cond %loopend
getlistat *outlist $counter &tmp getlistat &outlist $counter &tmp
stdlnout $tmp stdlnout $tmp
add 1 $counter &counter add 1 $counter &counter
jump %loopstart jump %loopstart

View File

@@ -1,14 +1,14 @@
# A cool list # A cool list
setlist *favWords "hello" "there" "general" "kenobi" setlist &favWords "hello" "there" "general" "kenobi"
stdlnout *favWords stdlnout $favWords
set &count 0 set &count 0
set &passedThrough true set &passedThrough true
@jmpbck @jmpbck
getlistat *favWords $count &tmp getlistat &favWords $count &tmp
stdlnout $tmp stdlnout $tmp
add $count 1 &count add $count 1 &count
getlistsize *favWords &tmp2 getlistsize &favWords &tmp2
inequal $count $tmp2 &tmp3 inequal $count $tmp2 &tmp3
if $tmp3 %jmpbck if $tmp3 %jmpbck