diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c709323 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Directories +build/ + +test.cpp \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6522110 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "stdio.h": "c", + "common.h": "c" + } +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a5f7e89 --- /dev/null +++ b/Makefile @@ -0,0 +1,60 @@ +CC = gcc +CFLAGS = -fPIC +DEBUG_CFLAGS = -D DEBUG -g3 -Og +RELEASE_CFLAGS = -g -O3 +LDFLAGS = -lm + +TARGET_EXEC = byte +BUILD_DIR = ./build/ + +SRC_DIRS = ./src/core/ +INC_DIRS = ./src/include/ + +BIN_DIR = bin/ +OBJ_DIR = obj/ + +SRCS := $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)*.c)) +OBJS := $(SRCS:.c=.o) +DEPS := $(OBJS:.o=.d) + +INC_FLAGS := $(addprefix -I,$(INC_DIRS)) +DEP_FLAGS = -MMD -MP +CC_FLAGS = $(INC_FLAGS) $(DEP_FLAGS) $(CFLAGS) + +DEBUG_DIR = $(BUILD_DIR)Debug/ +DEBUG_TARGET = $(DEBUG_DIR)$(BIN_DIR)$(TARGET_EXEC) +DEBUG_OBJS := $(addprefix $(DEBUG_DIR)$(OBJ_DIR), $(OBJS)) + +RELEASE_DIR = $(BUILD_DIR)Release/ +RELEASE_TARGET = $(RELEASE_DIR)$(BIN_DIR)$(TARGET_EXEC) +RELEASE_OBJS := $(addprefix $(RELEASE_DIR)$(OBJ_DIR), $(OBJS)) + +.PHONY: debug release all clean + +# default; target if run as `make` +debug: $(DEBUG_TARGET) + +$(DEBUG_TARGET): $(DEBUG_OBJS) + @mkdir -p $(dir $@) + $(CC) $^ -o $@ $(LDFLAGS) + +$(DEBUG_DIR)$(OBJ_DIR)%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CC_FLAGS) $(DEBUG_CFLAGS) -c $< -o $@ + +release: $(RELEASE_TARGET) + +$(RELEASE_TARGET): $(RELEASE_OBJS) + @mkdir -p $(dir $@) + $(CC) $^ -o $@ $(LDFLAGS) + +$(RELEASE_DIR)$(OBJ_DIR)%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CC_FLAGS) $(RELEASE_CFLAGS) -c $< -o $@ + +all: debug release + +clean: + rm -rf $(BUILD_DIR) + +-include $(DEPS) \ No newline at end of file diff --git a/src/core/chunk.c b/src/core/chunk.c new file mode 100644 index 0000000..37ec34c --- /dev/null +++ b/src/core/chunk.c @@ -0,0 +1,37 @@ +#include "chunk.h" +#include "common.h" +#include "memory.h" + +void initChunk(Chunk* chunk) { + chunk->count = 0; + chunk->capacity = 0; + chunk->code = NULL; + chunk->lines = NULL; + initValueArray(&chunk->constants); +} + +void writeChunk(Chunk* chunk, uint8_t byte, int line) { + if (chunk->capacity < chunk->count + 1) { + int oldCapacity = chunk->capacity; + chunk->capacity = GROW_CAPACITY(oldCapacity); + chunk->code = + GROW_ARRAY(uint8_t, chunk->code, oldCapacity, chunk->capacity); + chunk->lines = GROW_ARRAY(int, chunk->lines, oldCapacity, chunk->capacity); + } + + chunk->code[chunk->count] = byte; + chunk->lines[chunk->count] = line; + chunk->count++; +} + +void freeChunk(Chunk* chunk) { + FREE_ARRAY(uint8_t, chunk->code, chunk->capacity); + FREE_ARRAY(int, chunk->lines, chunk->capacity); + freeValueArray(&chunk->constants); + initChunk(chunk); +} + +int addConstant(Chunk* chunk, Value value) { + writeValueArray(&chunk->constants, value); + return chunk->constants.count - 1; +} \ No newline at end of file diff --git a/src/core/chunk.h b/src/core/chunk.h new file mode 100644 index 0000000..e14af32 --- /dev/null +++ b/src/core/chunk.h @@ -0,0 +1,22 @@ +#ifndef BYTE_CHUNK_H +#define BYTE_CHUNK_H + +#include "common.h" +#include "value.h" + +typedef enum { OP_CONSTANT, OP_RETURN } OpCode; + +typedef struct { + int count; + int capacity; + uint8_t* code; + int* lines; + ValueArray constants; +} Chunk; + +void initChunk(Chunk* chunk); +void freeChunk(Chunk* chunk); +void writeChunk(Chunk* chunk, uint8_t byte, int line); +int addConstant(Chunk* chunk, Value value); + +#endif \ No newline at end of file diff --git a/src/core/common.h b/src/core/common.h new file mode 100644 index 0000000..f79aa9d --- /dev/null +++ b/src/core/common.h @@ -0,0 +1,19 @@ +#ifndef BYTE_COMMON_H +#define BYTE_COMMON_H + +#include +#include +#include + + +#define MAX_INTERPOLATION_NESTING 8 + +// Fixes vasprintf usage +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif + + +#define BYTE_COPYRIGHT "Copyright (c) 2023 Saheb Giri" + +#endif \ No newline at end of file diff --git a/src/core/compiler.c b/src/core/compiler.c new file mode 100644 index 0000000..e69de29 diff --git a/src/core/debug.c b/src/core/debug.c new file mode 100644 index 0000000..4c4229f --- /dev/null +++ b/src/core/debug.c @@ -0,0 +1,43 @@ +#include + +#include "debug.h" + +static int simpleInstruction(const char* name, int offset) { + printf("%s\n", name); + return offset + 1; +} + +static int constantInstruction(const char* name, Chunk* chunk, int offset) { + uint8_t constant = chunk->code[offset + 1]; + printf("%-16s %4d '", name, constant); + printValue(chunk->constants.values[constant]); + printf("'\n"); + return offset + 2; +} + +int disassembleInstruction(Chunk* chunk, int offset) { + printf("%04d ", offset); + if (offset > 0 && chunk->lines[offset] == chunk->lines[offset - 1]) { + printf(" | "); + } else { + printf("%4d ", chunk->lines[offset]); + } + uint8_t instruction = chunk->code[offset]; + switch (instruction) { + case OP_CONSTANT: + return constantInstruction("OP_CONSTANT", chunk, offset); + case OP_RETURN: + return simpleInstruction("OP_RETURN", offset); + default: + printf("Unknown opcode %d\n", instruction); + return offset + 1; + } +} + +void disassembleChunk(Chunk* chunk, const char* name) { + printf("== %s ==\n", name); + + for (int offset = 0; offset < chunk->count;) { + offset = disassembleInstruction(chunk, offset); + } +} diff --git a/src/core/debug.h b/src/core/debug.h new file mode 100644 index 0000000..6fe070e --- /dev/null +++ b/src/core/debug.h @@ -0,0 +1,9 @@ +#ifndef BYTE_DEBUG_H +#define BYTE_DEBUG_H + +#include "chunk.h" + +void disassembleChunk(Chunk* chunk, const char* name); +int disassembleInstruction(Chunk* chunk, int offset); + +#endif \ No newline at end of file diff --git a/src/core/main.c b/src/core/main.c new file mode 100644 index 0000000..656c36b --- /dev/null +++ b/src/core/main.c @@ -0,0 +1,98 @@ +#include "chunk.h" +#include "common.h" +#include "debug.h" +#include "scanner.h" + +#include +#include +#include + +static void repl() { + printf("Byte v.0.1 \n"); + + char line[1024]; + for (;;) { + printf(">> "); + + if (!fgets(line, sizeof(line), stdin)) { + printf("\n"); + break; + } + + if (memcmp(line, "exit", 4) == 0) { + break; + } + + if (memcmp(line, "help", 4) == 0) { + printf("exit - Exit the program\n"); + } + + interpret(line); + } +} + +static char* readFile(const char* path) { + FILE* file = fopen(path, "rb"); + + if (file == NULL) { + fprintf(stderr, "Could not open file \"%s\".\n", path); + exit(74); + } + + fseek(file, 0L, SEEK_END); + size_t fileSize = ftell(file); + rewind(file); + + char* buffer = (char*)malloc(fileSize + 1); + + if (buffer == NULL) { + fprintf(stderr, "Not enough memory to read \"%s\".\n", path); + exit(74); + } + + size_t bytesRead = fread(buffer, sizeof(char), fileSize, file); + if (bytesRead < fileSize) { + fprintf(stderr, "Could not read file \"%s\".\n", path); + exit(74); + } + + buffer[bytesRead] = '\0'; + + fclose(file); + return buffer; +} + +// static void runFile(const char* path) { +// char* source = readFile(path); +// InterpretResult result = interpret(source); +// free(source); // [owner] + +// if (result == INTERPRET_COMPILE_ERROR) +// exit(65); +// if (result == INTERPRET_RUNTIME_ERROR) +// exit(70); +// } + +int main(int argc, const char* argv[]) { + Chunk chunk; + initChunk(&chunk); + writeChunk(&chunk, OP_RETURN, 12); + writeChunk(&chunk, OP_RETURN, 12); + writeChunk(&chunk, OP_RETURN, 12); + writeChunk(&chunk, OP_RETURN, 12); + int constant = addConstant(&chunk, 1.2); + writeChunk(&chunk, OP_CONSTANT, 123); + writeChunk(&chunk, constant, 123); + disassembleChunk(&chunk, "TEST"); + freeChunk(&chunk); + + // if (argc == 1) { + // repl(); + // } else if (argc == 2) { + // runFile(argv[1]); + // } else { + // fprintf(stderr, "Usage: byte \n"); + // exit(64); + // } + return 0; +} \ No newline at end of file diff --git a/src/core/memory.c b/src/core/memory.c new file mode 100644 index 0000000..a7f8e66 --- /dev/null +++ b/src/core/memory.c @@ -0,0 +1,12 @@ +#include "memory.h" +#include + +void* reallocate(void* pointer, size_t oldSize, size_t newSize) { + if (newSize == 0) { + free(pointer); + return NULL; + } + + void* result = realloc(pointer, newSize); + return result; +} \ No newline at end of file diff --git a/src/core/memory.h b/src/core/memory.h new file mode 100644 index 0000000..8e28cec --- /dev/null +++ b/src/core/memory.h @@ -0,0 +1,21 @@ +#ifndef BYTE_MEMORY_H +#define BYTE_MEMORY_H + +#include "common.h" + +#define ALLOCATE(type, count) (type*)reallocate(NULL, 0, sizeof(type) * (count)) + +#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0) + +#define GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity)*2) + +#define GROW_ARRAY(type, pointer, oldCount, newCount) \ + (type*)reallocate(pointer, sizeof(type) * (oldCount), \ + sizeof(type) * (newCount)) + +#define FREE_ARRAY(type, pointer, oldCount) \ + reallocate(pointer, sizeof(type) * (oldCount), 0) + +void* reallocate(void* pointer, size_t oldSize, size_t newSize); + +#endif \ No newline at end of file diff --git a/src/core/scanner.c b/src/core/scanner.c new file mode 100644 index 0000000..5d18238 --- /dev/null +++ b/src/core/scanner.c @@ -0,0 +1,288 @@ +#include "scanner.h" +#include +#include +#include + +void initScanner(Scanner* s, const char* source) { + s->current = source; + s->start = source; + s->line = 1; + s->interpolatingCount = -1; +} + +bool isAtEnd(Scanner* s) { + return *s->current == '\0'; +} + +static Token makeToken(Scanner* s, TokenType type) { + Token t; + t.type = type; + t.start = s->start; + t.length = (int)(s->current - s->start); + t.line = s->line; + return t; +} + +static Token errorToken(Scanner* s, const char* message, ...) { + va_list args; + va_start(args, message); + char* err = NULL; + int length = vasprintf(&err, message, args); + va_end(args); + + Token t; + t.type = TOKEN_ERROR; + t.start = err; + if (err != NULL) { + t.length = length; + } else { + t.length = 0; + } + t.line = s->line; + return t; +} + +static bool isDigit(char c) { + return c >= '0' && c <= '9'; +} + +static bool isBinary(char c) { + return c == '0' || c == '1'; +} + +static bool isAlpha(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; +} + +static bool isOctal(char c) { + return c >= '0' && c <= '7'; +} + +static bool isHex(char c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F'); +} + +static char advance(Scanner* s) { + s->current++; + if (s->current[-1] == '\n') + s->line++; + return s->current[-1]; +} + +static bool match(Scanner* s, char expected) { + if (isAtEnd(s)) + return false; + if (*s->current != expected) + return false; + + s->current++; + if (s->current[-1] == '\n') + s->line++; + return true; +} + +static char current(Scanner* s) { + return *s->current; +} + +static char previous(Scanner* s) { + return s->current[-1]; +} + +static char next(Scanner* s) { + if (isAtEnd(s)) + return '\0'; + return s->current[1]; +} + +static void skipWhitespace(Scanner* s) { + while (true) { + char c = current(s); + + switch (c) { + case ' ': + case '\r': + case '\t': + advance(s); + break; + + // skip line comment + case '#': { + while (current(s) != '\n' && !isAtEnd(s)) + advance(s); + break; + } + + default: + return; + } + } +} + +static Token string(Scanner* s, char quote) { + while (current(s) != quote && !isAtEnd(s)) { + if (current(s) == '$' && next(s) == '{' && previous(s) != '\\') { + if (s->interpolatingCount - 1 < MAX_INTERPOLATION_NESTING) { + s->interpolatingCount++; + s->interpolating[s->interpolatingCount] = (int)quote; + s->current++; + Token tkn = makeToken(s, TOKEN_INTERPOLATION); + s->current++; + return tkn; + } + + return errorToken(s, "maximum interpolation nesting of %d exceeded by %d", + MAX_INTERPOLATION_NESTING, + MAX_INTERPOLATION_NESTING - s->interpolatingCount + 1); + } + if (current(s) == '\\' && (next(s) == quote || next(s) == '\\')) { + advance(s); + } + advance(s); + } + + if (isAtEnd(s)) + return errorToken(s, "unterminated string (opening quote not matched)"); + + match(s, quote); // the closing quote + return makeToken(s, TOKEN_STRING); +} + +Token number(Scanner* s) { + while (isDigit(current(s))) + advance(s); + + // See if it has a floating point. Make sure there is a digit after the "." + // so we don't get confused by method calls on number literals. + if (current(s) == '.' && isDigit(next(s))) { + advance(s); + while (isDigit(current(s))) + advance(s); + } + + // See if the number is in scientific notation. + if (match(s, 'e') || match(s, 'E')) { + // Allow a single positive/negative exponent symbol. + if (!match(s, '+')) { + match(s, '-'); + } + + if (!isDigit(current(s))) { + return errorToken(s, "unterminated scientific notation"); + } + + while (isDigit(current(s))) + advance(s); + } + + return makeToken(s, TOKEN_NUMBER); +} + +static Token identifier(Scanner* s) { + while (isAlpha(current(s)) || isDigit(current(s))) + advance(s); + + TokenType type = TOKEN_IDENTIFIER; + size_t length = s->current - s->start; + + for (int i = 0; keywords[i].identifier != NULL; i++) { + if (length == keywords[i].length && + memcmp(s->start, keywords[i].identifier, length) == 0) { + type = keywords[i].tokenType; + break; + } + } + + return makeToken(s, type); +} + +Token scanToken(Scanner* s) { + skipWhitespace(s); + + s->start = s->current; + + if (isAtEnd(s)) + return makeToken(s, TOKEN_EOF); + + char c = advance(s); + + if (isDigit(c)) + return number(s); + else if (isAlpha(c)) + return identifier(s); + + switch (c) { + case '(': + return makeToken(s, TOKEN_LEFT_PAREN); + case ')': + return makeToken(s, TOKEN_RIGHT_PAREN); + case '[': + return makeToken(s, TOKEN_LEFT_BRACKET); + case ']': + return makeToken(s, TOKEN_RIGHT_BRACKET); + case '{': + return makeToken(s, TOKEN_LEFT_BRACE); + case '}': + if (s->interpolatingCount > -1) { + Token token = string(s, (char)s->interpolating[s->interpolatingCount]); + s->interpolatingCount--; + return token; + } + return makeToken(s, TOKEN_RIGHT_BRACE); + case ',': + return makeToken(s, TOKEN_COMMA); + case ':': + return makeToken(s, TOKEN_COLON); + case '.': + return makeToken(s, match(s, '.') ? TOKEN_DOT_DOT : TOKEN_DOT); + case '+': + return makeToken(s, match(s, '=') ? TOKEN_PLUS_EQUAL : TOKEN_PLUS); + case '-': + return makeToken(s, match(s, '=') ? TOKEN_MINUS_EQUAL : TOKEN_MINUS); + case '*': { + if (match(s, '*')) { + return makeToken( + s, match(s, '=') ? TOKEN_STAR_STAR_EQUAL : TOKEN_STAR_STAR); + } + return makeToken(s, match(s, '=') ? TOKEN_STAR_EQUAL : TOKEN_STAR); + } + case '/': { + if (match(s, '/')) { + return makeToken( + s, match(s, '=') ? TOKEN_SLASH_SLASH_EQUAL : TOKEN_SLASH_SLASH); + } + return makeToken(s, match(s, '=') ? TOKEN_SLASH_EQUAL : TOKEN_SLASH); + } + case '%': + return makeToken(s, match(s, '=') ? TOKEN_PERCENT_EQUAL : TOKEN_PERCENT); + case '=': + return makeToken(s, match(s, '=') ? TOKEN_EQUAL_EQUAL : TOKEN_EQUAL); + case '>': + return makeToken( + s, match(s, '=') ? TOKEN_GREATER_EQUAL : TOKEN_GREATER_THAN); + case '<': + return makeToken(s, match(s, '=') ? TOKEN_LESS_EQUAL : TOKEN_LESS_THAN); + case '!': + return makeToken(s, match(s, '=') ? TOKEN_BANG_EQUAL : TOKEN_BANG); + case '~': + return makeToken(s, match(s, '=') ? TOKEN_TILDE_EQUAL : TOKEN_TILDE); + case '|': + return makeToken(s, match(s, '=') ? TOKEN_PIPE_EQUAL : TOKEN_PIPE); + case '&': + return makeToken(s, match(s, '=') ? TOKEN_AMP_EQUAL : TOKEN_AMP); + case '^': + return makeToken(s, match(s, '=') ? TOKEN_CARET_EQUAL : TOKEN_CARET); + case '\n': + return makeToken(s, TOKEN_NEWLINE); + case '"': + return string(s, '"'); + case '\'': + return string(s, '\''); + + default: + break; + } + + return errorToken(s, "unexpected character %c", c); +} \ No newline at end of file diff --git a/src/core/scanner.h b/src/core/scanner.h new file mode 100644 index 0000000..34939b2 --- /dev/null +++ b/src/core/scanner.h @@ -0,0 +1,139 @@ +#ifndef BYTE_SCANNER_H +#define BYTE_SCANNER_H + +#include "common.h" + +typedef enum { + TOKEN_LEFT_PAREN, // ( + TOKEN_RIGHT_PAREN, // ) + TOKEN_LEFT_BRACKET, // [ + TOKEN_RIGHT_BRACKET, // ] + TOKEN_LEFT_BRACE, // { + TOKEN_RIGHT_BRACE, // } + TOKEN_COMMA, // , + TOKEN_COLON, // : + TOKEN_SEMICOLON, // ; + TOKEN_HASH, // # + TOKEN_DOT, // . + TOKEN_DOT_DOT, // .. + + TOKEN_PLUS, // + + TOKEN_MINUS, // - + TOKEN_STAR, // * + TOKEN_SLASH, // / + TOKEN_PERCENT, // % + TOKEN_STAR_STAR, // ** + TOKEN_SLASH_SLASH, // // + TOKEN_EQUAL, // = + TOKEN_GREATER_THAN, // > + TOKEN_LESS_THAN, // < + TOKEN_BANG, // ! + + TOKEN_TILDE, // ~ + TOKEN_PIPE, // | + TOKEN_AMP, // & + TOKEN_CARET, // ^ + + TOKEN_PLUS_EQUAL, // += + TOKEN_MINUS_EQUAL, // -= + TOKEN_STAR_EQUAL, // *= + TOKEN_SLASH_EQUAL, // /= + TOKEN_PERCENT_EQUAL, // %= + TOKEN_STAR_STAR_EQUAL, // **= + TOKEN_SLASH_SLASH_EQUAL, // //= + TOKEN_EQUAL_EQUAL, // == + TOKEN_GREATER_EQUAL, // >= + TOKEN_LESS_EQUAL, // <= + TOKEN_BANG_EQUAL, // != + + TOKEN_TILDE_EQUAL, // ~= + TOKEN_PIPE_EQUAL, // |= + TOKEN_AMP_EQUAL, // &= + TOKEN_CARET_EQUAL, // ^= + + TOKEN_IDENTIFIER, // identifier + TOKEN_STRING, // string + /* + A portion of a string literal preceding an interpolated + expression. This + string: + "a ${b} c ${d} e" + is tokenized to: + TOKEN_INTERPOLATION "a " + TOKEN_IDENTIFIER b + TOKEN_INTERPOLATION " c " + TOKEN_IDENTIFIER d + TOKEN_STRING " e" + */ + TOKEN_INTERPOLATION, // string interpolation + TOKEN_NUMBER, // number + + // Keywords. + TOKEN_AND, // and + TOKEN_OR, // or + TOKEN_NOT, // not + TOKEN_NIL, // nil + TOKEN_IN, // in + TOKEN_IMPORT, // import + TOKEN_CLASS, // class + TOKEN_IS, // is + TOKEN_SUPER, // super + TOKEN_IF, // if + TOKEN_ELSE, // else + TOKEN_TRUE, // true + TOKEN_FALSE, // false + TOKEN_FN, // fn + TOKEN_FOR, // for + TOKEN_PRINT, // print + TOKEN_RETURN, // return + TOKEN_THIS, // this + TOKEN_LET, // let + TOKEN_WHILE, // while + + TOKEN_NEWLINE, + + TOKEN_EOF, + TOKEN_ERROR, +} TokenType; + +typedef struct { + const char* identifier; + size_t length; + TokenType tokenType; +} Keyword; + +// The table of reserved words and their associated token types. +static Keyword keywords[] = { + {"and", 3, TOKEN_AND}, {"or", 2, TOKEN_OR}, + {"not", 3, TOKEN_NOT}, {"nil", 3, TOKEN_NIL}, + {"in", 2, TOKEN_IN}, {"is", 2, TOKEN_IS}, + {"import", 6, TOKEN_IMPORT}, {"class", 5, TOKEN_CLASS}, + {"if", 2, TOKEN_IF}, {"else", 4, TOKEN_ELSE}, + {"true", 4, TOKEN_TRUE}, {"false", 5, TOKEN_FALSE}, + {"fn", 2, TOKEN_FN}, {"for", 3, TOKEN_FOR}, + {"print", 5, TOKEN_PRINT}, {"return", 6, TOKEN_RETURN}, + {"super", 5, TOKEN_SUPER}, {"this", 4, TOKEN_THIS}, + {"let", 3, TOKEN_LET}, {"while", 5, TOKEN_WHILE}, + {NULL, 0, TOKEN_EOF} // Sentinel to mark the end of the array. +}; + +typedef struct { + TokenType type; + const char* start; + int length; + int line; +} Token; + +typedef struct { + const char* start; + const char* current; + int line; + int interpolatingCount; + int interpolating[MAX_INTERPOLATION_NESTING]; +} Scanner; + +void initScanner(Scanner* s, const char* source); +Token scanToken(Scanner* s); +bool isAtEnd(Scanner* s); + +#endif \ No newline at end of file diff --git a/src/core/value.c b/src/core/value.c new file mode 100644 index 0000000..3bcaf61 --- /dev/null +++ b/src/core/value.c @@ -0,0 +1,31 @@ +#include + +#include "memory.h" +#include "value.h" + +void initValueArray(ValueArray* array) { + array->values = NULL; + array->capacity = 0; + array->count = 0; +} + +void writeValueArray(ValueArray* array, Value value) { + if (array->capacity < array->count + 1) { + int oldCapacity = array->capacity; + array->capacity = GROW_CAPACITY(oldCapacity); + array->values = + GROW_ARRAY(Value, array->values, oldCapacity, array->capacity); + } + + array->values[array->count] = value; + array->count++; +} + +void freeValueArray(ValueArray* array) { + FREE_ARRAY(Value, array->values, array->capacity); + initValueArray(array); +} + +void printValue(Value value) { + printf("%g", value); +} \ No newline at end of file diff --git a/src/core/value.h b/src/core/value.h new file mode 100644 index 0000000..89e9bd7 --- /dev/null +++ b/src/core/value.h @@ -0,0 +1,19 @@ +#ifndef BYTE_VALUE_H +#define BYTE_VALUE_H + +#include "common.h" + +typedef double Value; + +typedef struct { + int capacity; + int count; + Value* values; +} ValueArray; + +void initValueArray(ValueArray* array); +void writeValueArray(ValueArray* array, Value value); +void freeValueArray(ValueArray* array); +void printValue(Value value); + +#endif \ No newline at end of file