More debug features, update README, new makefile
This commit is contained in:
@@ -1,17 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 4.0)
|
|
||||||
project(groundc C)
|
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
|
||||||
|
|
||||||
include_directories(src)
|
|
||||||
|
|
||||||
add_executable(groundc
|
|
||||||
src/main.c
|
|
||||||
src/interpreter.c
|
|
||||||
src/interpreter.h
|
|
||||||
src/parser.c
|
|
||||||
src/parser.h
|
|
||||||
src/lexer.c
|
|
||||||
src/lexer.h
|
|
||||||
src/types.c
|
|
||||||
src/types.h)
|
|
||||||
77
Makefile
Normal file
77
Makefile
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
CC = gcc
|
||||||
|
CFLAGS = -Wall -Wextra -O3 -Isrc/include -Iinclude
|
||||||
|
LDFLAGS =
|
||||||
|
|
||||||
|
# Directories
|
||||||
|
SRC_DIR = src
|
||||||
|
BUILD_DIR = build
|
||||||
|
BIN_DIR = $(BUILD_DIR)/bin
|
||||||
|
LIB_DIR = $(BUILD_DIR)/lib
|
||||||
|
INC_DIR = $(BUILD_DIR)/include
|
||||||
|
OBJ_DIR = $(BUILD_DIR)/obj
|
||||||
|
|
||||||
|
# Output names
|
||||||
|
EXECUTABLE = $(BIN_DIR)/ground
|
||||||
|
SHARED_LIB = $(LIB_DIR)/libgroundvm.so
|
||||||
|
HEADER = $(INC_DIR)/groundvm.h
|
||||||
|
|
||||||
|
# Source files
|
||||||
|
LIB_SOURCES = $(filter-out $(SRC_DIR)/main.c, $(wildcard $(SRC_DIR)/*.c))
|
||||||
|
EXE_SOURCES = $(wildcard $(SRC_DIR)/*.c)
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
LIB_OBJECTS = $(LIB_SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/lib_%.o)
|
||||||
|
EXE_OBJECTS = $(EXE_SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/exe_%.o)
|
||||||
|
|
||||||
|
# Default target: build standalone executable
|
||||||
|
.PHONY: all
|
||||||
|
all: executable
|
||||||
|
|
||||||
|
# Build standalone executable
|
||||||
|
.PHONY: executable
|
||||||
|
executable: $(EXECUTABLE)
|
||||||
|
|
||||||
|
# Build shared library
|
||||||
|
.PHONY: library
|
||||||
|
library: $(SHARED_LIB) $(HEADER)
|
||||||
|
|
||||||
|
# Build both
|
||||||
|
.PHONY: both
|
||||||
|
both: executable library
|
||||||
|
|
||||||
|
# Link executable
|
||||||
|
$(EXECUTABLE): $(EXE_OBJECTS) | $(BIN_DIR)
|
||||||
|
$(CC) $(EXE_OBJECTS) -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
# Build shared library
|
||||||
|
$(SHARED_LIB): $(LIB_OBJECTS) | $(LIB_DIR)
|
||||||
|
$(CC) -shared $(LIB_OBJECTS) -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
# Copy header for library distribution
|
||||||
|
$(HEADER): include/groundvm.h | $(INC_DIR)
|
||||||
|
cp $< $@
|
||||||
|
|
||||||
|
# Compile object files for executable
|
||||||
|
$(OBJ_DIR)/exe_%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
# Compile object files for shared library (with -fPIC)
|
||||||
|
$(OBJ_DIR)/lib_%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
|
||||||
|
$(CC) $(CFLAGS) -fPIC -c $< -o $@
|
||||||
|
|
||||||
|
# Create directories
|
||||||
|
$(BUILD_DIR) $(BIN_DIR) $(LIB_DIR) $(INC_DIR) $(OBJ_DIR):
|
||||||
|
mkdir -p $@
|
||||||
|
|
||||||
|
# Clean build artifacts
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -rf $(BUILD_DIR)
|
||||||
|
|
||||||
|
# Debug: print variables
|
||||||
|
.PHONY: debug
|
||||||
|
debug:
|
||||||
|
@echo "LIB_SOURCES: $(LIB_SOURCES)"
|
||||||
|
@echo "EXE_SOURCES: $(EXE_SOURCES)"
|
||||||
|
@echo "LIB_OBJECTS: $(LIB_OBJECTS)"
|
||||||
|
@echo "EXE_OBJECTS: $(EXE_OBJECTS)"
|
||||||
39
README.md
39
README.md
@@ -17,7 +17,44 @@ Now that Ground's features have mostly been finalised, this interpreter can be b
|
|||||||
|
|
||||||
*so far, only tested on Linux, but hopefully should work on other platforms as well
|
*so far, only tested on Linux, but hopefully should work on other platforms as well
|
||||||
|
|
||||||
Progress marker:
|
## Building
|
||||||
|
|
||||||
|
To build, make sure Make is installed. Then, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
Ground can also be embedded as a library in other programs. Run this code to generate library files:
|
||||||
|
```bash
|
||||||
|
make library
|
||||||
|
```
|
||||||
|
|
||||||
|
File structure of the build directory:
|
||||||
|
```
|
||||||
|
build
|
||||||
|
├── bin
|
||||||
|
│ └── ground
|
||||||
|
├── include
|
||||||
|
│ └── groundvm.h
|
||||||
|
├── lib
|
||||||
|
│ └── libgroundvm.so
|
||||||
|
└── obj
|
||||||
|
├── exe_interface.o
|
||||||
|
├── exe_interpreter.o
|
||||||
|
├── exe_lexer.o
|
||||||
|
├── exe_main.o
|
||||||
|
├── exe_parser.o
|
||||||
|
├── exe_types.o
|
||||||
|
├── lib_interface.o
|
||||||
|
├── lib_interpreter.o
|
||||||
|
├── lib_lexer.o
|
||||||
|
├── lib_parser.o
|
||||||
|
└── lib_types.o
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Progress marker
|
||||||
|
|
||||||
- [x] Lexer
|
- [x] Lexer
|
||||||
- [x] Parser
|
- [x] Parser
|
||||||
|
|||||||
130
include/groundvm.h
Normal file
130
include/groundvm.h
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
#ifndef LIBGROUND_H
|
||||||
|
#define LIBGROUND_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* groundvm.h
|
||||||
|
* Provides an interface for external programs wanting to run Ground code.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
typedef enum GroundInstType {
|
||||||
|
IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, STOI, STOD, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, USE, EXTERN, CREATELABEL, PAUSE
|
||||||
|
} GroundInstType;
|
||||||
|
|
||||||
|
typedef enum GroundValueType {
|
||||||
|
INT, DOUBLE, STRING, CHAR, BOOL, LIST, FUNCTION, CUSTOM, NONE
|
||||||
|
} GroundValueType;
|
||||||
|
|
||||||
|
typedef enum GroundArgType {
|
||||||
|
VALUE, VALREF, DIRREF, LINEREF, LABEL, FNREF, TYPEREF
|
||||||
|
} GroundArgType;
|
||||||
|
|
||||||
|
typedef enum ListAccessStatus {
|
||||||
|
LIST_OKAY, LIST_OUT_OF_BOUNDS, LIST_FIXME
|
||||||
|
} ListAccessStatus;
|
||||||
|
|
||||||
|
struct GroundValue;
|
||||||
|
struct GroundFunction;
|
||||||
|
|
||||||
|
struct List;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Custom data type that stores Ground values.
|
||||||
|
*/
|
||||||
|
typedef struct List {
|
||||||
|
size_t size;
|
||||||
|
struct GroundValue* values;
|
||||||
|
} List;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stores literal values created in a Ground program.
|
||||||
|
*/
|
||||||
|
typedef struct GroundValue {
|
||||||
|
GroundValueType type;
|
||||||
|
union {
|
||||||
|
int64_t intVal;
|
||||||
|
double doubleVal;
|
||||||
|
char* stringVal;
|
||||||
|
char charVal;
|
||||||
|
bool boolVal;
|
||||||
|
List listVal;
|
||||||
|
struct GroundFunction* fnVal;
|
||||||
|
void* customVal;
|
||||||
|
} data;
|
||||||
|
} GroundValue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Indicates status when accessing a list.
|
||||||
|
*/
|
||||||
|
typedef struct ListAccess {
|
||||||
|
ListAccessStatus status;
|
||||||
|
GroundValue* value;
|
||||||
|
} ListAccess;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stores arguments for the GroundInstruction struct.
|
||||||
|
*/
|
||||||
|
typedef struct GroundArg {
|
||||||
|
GroundArgType type;
|
||||||
|
union {
|
||||||
|
GroundValue value;
|
||||||
|
char* refName;
|
||||||
|
} value;
|
||||||
|
} GroundArg;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Represents a Ground instruction.
|
||||||
|
*/
|
||||||
|
typedef struct GroundInstruction {
|
||||||
|
GroundInstType type;
|
||||||
|
struct {
|
||||||
|
GroundArg* args;
|
||||||
|
size_t length;
|
||||||
|
} args;
|
||||||
|
} GroundInstruction;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Represents a Ground program or function.
|
||||||
|
*/
|
||||||
|
typedef struct GroundProgram {
|
||||||
|
GroundInstruction* instructions;
|
||||||
|
size_t size;
|
||||||
|
} GroundProgram;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Represents the argument typing for a GroundFunction.
|
||||||
|
*/
|
||||||
|
typedef struct GroundFunctionArgs {
|
||||||
|
GroundValueType type;
|
||||||
|
char* name;
|
||||||
|
} GroundFunctionArgs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Represents a Ground function.
|
||||||
|
*/
|
||||||
|
typedef struct GroundFunction {
|
||||||
|
GroundFunctionArgs* args;
|
||||||
|
size_t argSize;
|
||||||
|
GroundValueType returnType;
|
||||||
|
GroundProgram program;
|
||||||
|
size_t startLine;
|
||||||
|
} GroundFunction;
|
||||||
|
|
||||||
|
GroundProgram groundCreateProgram();
|
||||||
|
void groundAddInstructionToProgram(GroundProgram* program, GroundInstruction instruction);
|
||||||
|
GroundValue groundRunProgram(GroundProgram* program);
|
||||||
|
|
||||||
|
GroundInstruction groundCreateInstruction(GroundInstType type);
|
||||||
|
void groundAddValueToInstruction(GroundInstruction* inst, GroundValue value);
|
||||||
|
void groundAddReferenceToInstruction(GroundInstruction* inst, GroundArg value);
|
||||||
|
GroundArg groundCreateReference(GroundArgType type, char* ref);
|
||||||
|
|
||||||
|
GroundValue groundCreateValue(GroundValueType type, ...);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
81
src/interface.c
Normal file
81
src/interface.c
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include "lexer.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
GroundProgram groundCreateProgram() {
|
||||||
|
return (GroundProgram) {
|
||||||
|
.instructions = malloc(sizeof(GroundInstruction)),
|
||||||
|
.size = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
void groundAddInstructionToProgram(GroundProgram* program, GroundInstruction instruction) {
|
||||||
|
addInstructionToProgram(program, instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
GroundInstruction groundCreateInstruction(GroundInstType type) {
|
||||||
|
return (GroundInstruction) {
|
||||||
|
.type = type,
|
||||||
|
.args.args = malloc(sizeof(GroundArg)),
|
||||||
|
.args.length = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void groundAddValueToInstruction(GroundInstruction* inst, GroundValue value) {
|
||||||
|
addArgToInstruction(inst, createValueGroundArg(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void groundAddReferenceToInstruction(GroundInstruction* inst, GroundArg value) {
|
||||||
|
addArgToInstruction(inst, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
GroundArg groundCreateReference(GroundArgType type, char* ref) {
|
||||||
|
return (GroundArg) {
|
||||||
|
.type = type,
|
||||||
|
.value.refName = ref
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
GroundValue groundCreateValue(GroundValueType type, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, type);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case INT: {
|
||||||
|
return createIntGroundValue(va_arg(args, int));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DOUBLE: {
|
||||||
|
return createDoubleGroundValue(va_arg(args, double));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case STRING: {
|
||||||
|
return createStringGroundValue(va_arg(args, char*));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CHAR: {
|
||||||
|
return createCharGroundValue((char)va_arg(args, int));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BOOL: {
|
||||||
|
return createBoolGroundValue((bool)va_arg(args, int));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LIST: {
|
||||||
|
return createListGroundValue(va_arg(args, List));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return createNoneGroundValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
GroundValue groundRunProgram(GroundProgram* program) {
|
||||||
|
return interpretGroundProgram(program, NULL);
|
||||||
|
}
|
||||||
@@ -190,6 +190,10 @@ GroundDebugInstruction parseDebugInstruction(char* in) {
|
|||||||
gdi.type = CONTINUE;
|
gdi.type = CONTINUE;
|
||||||
} else if (strcmp(instruction, "exit") == 0) {
|
} else if (strcmp(instruction, "exit") == 0) {
|
||||||
gdi.type = EXIT;
|
gdi.type = EXIT;
|
||||||
|
} else if (strcmp(instruction, "step") == 0) {
|
||||||
|
gdi.type = STEP;
|
||||||
|
} else if (strcmp(instruction, "view") == 0) {
|
||||||
|
gdi.type = VIEW;
|
||||||
} else if (strcmp(instruction, "help") == 0) {
|
} else if (strcmp(instruction, "help") == 0) {
|
||||||
gdi.type = HELP;
|
gdi.type = HELP;
|
||||||
} else {
|
} else {
|
||||||
@@ -209,6 +213,7 @@ GroundDebugInstruction parseDebugInstruction(char* in) {
|
|||||||
GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
||||||
GroundLabel* labels = NULL;
|
GroundLabel* labels = NULL;
|
||||||
GroundVariable* variables = NULL;
|
GroundVariable* variables = NULL;
|
||||||
|
int instructionsToPause = -1;
|
||||||
|
|
||||||
GroundScope scope;
|
GroundScope scope;
|
||||||
if (inScope != NULL) {
|
if (inScope != NULL) {
|
||||||
@@ -277,7 +282,7 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (in->instructions[i].type == PAUSE) {
|
if (in->instructions[i].type == PAUSE || instructionsToPause == 0) {
|
||||||
printf("Paused execution\n");
|
printf("Paused execution\n");
|
||||||
printf("Previous instruction: ");
|
printf("Previous instruction: ");
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
@@ -341,6 +346,19 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
|||||||
freeGroundProgram(&program);
|
freeGroundProgram(&program);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case STEP: {
|
||||||
|
if (gdi.arg != NULL && strlen(gdi.arg) > 0) {
|
||||||
|
instructionsToPause = atoi(gdi.arg);
|
||||||
|
} else {
|
||||||
|
instructionsToPause = 1;
|
||||||
|
}
|
||||||
|
instructionsToPause++;
|
||||||
|
shouldBreak = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VIEW: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
case HELP: {
|
case HELP: {
|
||||||
printf("Ground Debugger Help\n");
|
printf("Ground Debugger Help\n");
|
||||||
printf("Didn't mean to end up here? Type \"continue\" to escape this prompt.\n");
|
printf("Didn't mean to end up here? Type \"continue\" to escape this prompt.\n");
|
||||||
@@ -351,6 +369,7 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
|||||||
printf(" inspect (variable): Shows the contents of a variable\n");
|
printf(" inspect (variable): Shows the contents of a variable\n");
|
||||||
printf(" eval (code): Runs Ground code in the current scope\n");
|
printf(" eval (code): Runs Ground code in the current scope\n");
|
||||||
printf(" help: Shows this help message");
|
printf(" help: Shows this help message");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case UNKNOWN: {
|
case UNKNOWN: {
|
||||||
printf("Unknown instruction (type \"help\" for help)");
|
printf("Unknown instruction (type \"help\" for help)");
|
||||||
@@ -361,8 +380,11 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue;
|
if (in->instructions[i].type == PAUSE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
instructionsToPause --;
|
||||||
int ci = currentInstruction;
|
int ci = currentInstruction;
|
||||||
GroundValue gv = interpretGroundInstruction(in->instructions[i], &scope);
|
GroundValue gv = interpretGroundInstruction(in->instructions[i], &scope);
|
||||||
if (gv.type != NONE) {
|
if (gv.type != NONE) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ typedef enum GroundRuntimeError {
|
|||||||
} GroundRuntimeError;
|
} GroundRuntimeError;
|
||||||
|
|
||||||
typedef enum GroundDebugInstructionType {
|
typedef enum GroundDebugInstructionType {
|
||||||
DUMP, INSPECT, EVAL, CONTINUE, EXIT, HELP, UNKNOWN
|
DUMP, INSPECT, EVAL, CONTINUE, EXIT, STEP, VIEW, HELP, UNKNOWN
|
||||||
} GroundDebugInstructionType;
|
} GroundDebugInstructionType;
|
||||||
|
|
||||||
typedef struct GroundLabel {
|
typedef struct GroundLabel {
|
||||||
|
|||||||
@@ -6,3 +6,6 @@ set &x 5
|
|||||||
set &y "dingle"
|
set &y "dingle"
|
||||||
PAUSE
|
PAUSE
|
||||||
println "continuing"
|
println "continuing"
|
||||||
|
println "step through here"
|
||||||
|
println "step again"
|
||||||
|
println "and again"
|
||||||
|
|||||||
Reference in New Issue
Block a user