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
|
||||
|
||||
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] 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;
|
||||
} else if (strcmp(instruction, "exit") == 0) {
|
||||
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) {
|
||||
gdi.type = HELP;
|
||||
} else {
|
||||
@@ -209,6 +213,7 @@ GroundDebugInstruction parseDebugInstruction(char* in) {
|
||||
GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
||||
GroundLabel* labels = NULL;
|
||||
GroundVariable* variables = NULL;
|
||||
int instructionsToPause = -1;
|
||||
|
||||
GroundScope scope;
|
||||
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("Previous instruction: ");
|
||||
if (i > 0) {
|
||||
@@ -341,6 +346,19 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
||||
freeGroundProgram(&program);
|
||||
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: {
|
||||
printf("Ground Debugger Help\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(" eval (code): Runs Ground code in the current scope\n");
|
||||
printf(" help: Shows this help message");
|
||||
break;
|
||||
}
|
||||
case UNKNOWN: {
|
||||
printf("Unknown instruction (type \"help\" for help)");
|
||||
@@ -361,8 +380,11 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
if (in->instructions[i].type == PAUSE) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
instructionsToPause --;
|
||||
int ci = currentInstruction;
|
||||
GroundValue gv = interpretGroundInstruction(in->instructions[i], &scope);
|
||||
if (gv.type != NONE) {
|
||||
|
||||
@@ -11,7 +11,7 @@ typedef enum GroundRuntimeError {
|
||||
} GroundRuntimeError;
|
||||
|
||||
typedef enum GroundDebugInstructionType {
|
||||
DUMP, INSPECT, EVAL, CONTINUE, EXIT, HELP, UNKNOWN
|
||||
DUMP, INSPECT, EVAL, CONTINUE, EXIT, STEP, VIEW, HELP, UNKNOWN
|
||||
} GroundDebugInstructionType;
|
||||
|
||||
typedef struct GroundLabel {
|
||||
|
||||
@@ -6,3 +6,6 @@ set &x 5
|
||||
set &y "dingle"
|
||||
PAUSE
|
||||
println "continuing"
|
||||
println "step through here"
|
||||
println "step again"
|
||||
println "and again"
|
||||
|
||||
Reference in New Issue
Block a user