Skip to content

Commit

Permalink
Strings
Browse files Browse the repository at this point in the history
  • Loading branch information
dils2k committed Mar 12, 2023
1 parent e641678 commit c68693c
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 10 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ project(clox C)

set(CMAKE_C_STANDARD 11)

add_executable(clox main.c common.h chunk.h chunk.c memory.h debug.h debug.c memory.c value.h value.c vm.c compiler.h compiler.c scanner.h scanner.c)
add_executable(clox main.c common.h chunk.h chunk.c memory.h debug.h debug.c memory.c value.h value.c vm.c compiler.h compiler.c scanner.h scanner.c object.c object.h)
7 changes: 6 additions & 1 deletion compiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ static void number() {
emitConstant(NUMBER_VAL(value));
}

static void string() {
emitConstant(OBJ_VAL(copyString(parser.previous.start + 1,
parser.previous.length - 2)));
}

static void parsePrecedence(Precedence precedence) {
advance();
ParseFn prefixRule = getRule(parser.previous.type)->prefix;
Expand Down Expand Up @@ -222,7 +227,7 @@ ParseRule rules[] = {
[TOKEN_LESS] = {NULL, binary, PREC_COMPARISON},
[TOKEN_LESS_EQUAL] = {NULL, binary, PREC_COMPARISON},
[TOKEN_IDENTIFIER] = {NULL, NULL, PREC_NONE},
[TOKEN_STRING] = {NULL, NULL, PREC_NONE},
[TOKEN_STRING] = {string, NULL, PREC_NONE},
[TOKEN_NUMBER] = {number, NULL, PREC_NONE},
[TOKEN_AND] = {NULL, NULL, PREC_NONE},
[TOKEN_CLASS] = {NULL, NULL, PREC_NONE},
Expand Down
3 changes: 2 additions & 1 deletion compiler.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#ifndef clox_compiler_h
#define clox_compiler_h

#include "object.h"
#include "vm.h"

bool compile(const char* source, Chunk* chunk);

#endif
#endif
5 changes: 4 additions & 1 deletion memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

#include "common.h"

#define ALLOCATE(type, count) \
(type*)reallocate(NULL, 0, sizeof(type) * (count))

#define GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)

#define GROW_ARRAY(type, pointer, oldCount, newCount) \
Expand All @@ -13,4 +16,4 @@

void* reallocate(void* pointer, size_t oldSize, size_t newSize);

#endif
#endif
42 changes: 42 additions & 0 deletions object.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include <stdio.h>
#include <string.h>

#include "memory.h"
#include "object.h"
#include "value.h"
#include "vm.h"

#define ALLOCATE_OBJ(type, objectType) \
(type*)allocateObject(sizeof(type), objectType)

static Obj* allocateObject(size_t size, ObjType type) {
Obj* object = (Obj*)reallocate(NULL, 0, size);
object->type = type;
return object;
}

static ObjString* allocateString(char* chars, int length) {
ObjString* string = ALLOCATE_OBJ(ObjString, OBJ_STRING);
string->length = length;
string->chars = chars;
return string;
}

ObjString* copyString(const char* chars, int length) {
char* heapChars = ALLOCATE(char, length+1);
memcpy(heapChars, chars, length);
heapChars[length] = '\0';
return allocateString(heapChars, length);
}

ObjString* takeString(char* chars, int length) {
return allocateString(chars, length);
}

void printObject(Value value) {
switch (OBJ_TYPE(value)) {
case OBJ_STRING:
printf("%s", AS_CSTRING(value));
break;
}
}
36 changes: 36 additions & 0 deletions object.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef clox_object_h
#define clox_object_h

#include "common.h"
#include "value.h"

#define OBJ_TYPE(value) (AS_OBJ(value)->type)

#define IS_STRING(value) isObjType(value, OBJ_STRING)

#define AS_STRING(value) ((ObjString*)AS_OBJ(value))
#define AS_CSTRING(value) (((ObjString*)AS_OBJ(value))->chars)

typedef enum {
OBJ_STRING,
} ObjType;

struct Obj {
ObjType type;
};

struct ObjString {
Obj obj;
int length;
char* chars;
};

static inline bool isObjType(Value value, ObjType type) {
return IS_OBJ(value) && AS_OBJ(value)->type == type;
}

ObjString* takeString(char* chars, int length);
ObjString* copyString(const char* chars, int length);
void printObject(Value value);

#endif
2 changes: 1 addition & 1 deletion scanner.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ Token scanToken() {
return makeToken(match('=') ? TOKEN_LESS_EQUAL : TOKEN_LESS);
case '>':
return makeToken(match('=') ? TOKEN_GREATER_EQUAL : TOKEN_GREATER);
case '"': string();
case '"': return string();
}

return errorToken("unexpected character");
Expand Down
12 changes: 11 additions & 1 deletion value.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include <stdio.h>
#include <string.h>

#include "memory.h"
#include "value.h"
#include "object.h"

void initValueArray(ValueArray* array) {
array->values = NULL;
Expand Down Expand Up @@ -31,6 +33,7 @@ void printValue(Value value) {
break;
case VAL_NIL: printf("nil"); break;
case VAL_NUMBER: printf("%g", AS_NUMBER(value)); break;
case VAL_OBJ: printObject(value); break;
}
}

Expand All @@ -40,6 +43,13 @@ bool valuesEqual(Value a, Value b) {
case VAL_BOOL: return AS_BOOL(a) == AS_BOOL(b);
case VAL_NIL: return true;
case VAL_NUMBER: return AS_NUMBER(a) == AS_NUMBER(b);
case VAL_OBJ: {
ObjString* aString = AS_STRING(a);
ObjString* bString = AS_STRING(b);
return aString->length == bString->length &&
memcmp(aString->chars, bString->chars,
aString->length) == 0;
}
default: return false; // Unreachable.
}
}
}
16 changes: 13 additions & 3 deletions value.h
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
#ifndef clox_value_h
#define clox_value_h

#include "common.h"

typedef struct Obj Obj;
typedef struct ObjString ObjString;

typedef enum {
VAL_BOOL,
VAL_NIL,
VAL_NUMBER,
VAL_OBJ,
} ValueType;

typedef struct {
ValueType type;
union {
bool boolean;
double number;
Obj* obj;
} as;
} Value;

#define BOOL_VAL(value) ((Value){VAL_BOOL, {.boolean = value}})
#define NIL_VAL ((Value){VAL_NIL, {.number = 0}})
#define BOOL_VAL(value) ((Value){VAL_BOOL, {.boolean = value}})
#define NIL_VAL ((Value){VAL_NIL, {.number = 0}})
#define NUMBER_VAL(value) ((Value){VAL_NUMBER, {.number = value}})
#define OBJ_VAL(value) ((Value){VAL_OBJ, {.obj = value}})

#define AS_BOOL(value) ((value).as.boolean)
#define AS_BOOL(value) ((value).as.boolean)
#define AS_NUMBER(value) ((value).as.number)
#define AS_OBJ(value) ((value).as.obj)

#define IS_BOOL(value) ((value).type == VAL_BOOL)
#define IS_NIL(value) ((value).type == VAL_NIL)
#define IS_NUMBER(value) ((value).type == VAL_NUMBER)
#define IS_OBJ(value) ((value).type == VAL_OBJ)

typedef struct {
int capacity;
Expand Down
31 changes: 30 additions & 1 deletion vm.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

#include "common.h"
#include "compiler.h"
#include "object.h"
#include "memory.h"
#include "vm.h"
#include "debug.h"
#include "value.h"
Expand Down Expand Up @@ -51,6 +54,20 @@ static bool isFalsey(Value value) {
return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
}

static void concatenate() {
ObjString* b = AS_STRING(pop());
ObjString* a = AS_STRING(pop());

int length = a->length + b->length;
char* chars = ALLOCATE(char, length + 1);
memcpy(chars, a->chars, a->length);
memcpy(chars + a->length, b->chars, b->length);
chars[length] = '\0';

ObjString* result = takeString(chars, length);
push(OBJ_VAL(result));
}

static InterpretResult run() {
#define READ_BYTE() (*vm.ip++)
#define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()])
Expand Down Expand Up @@ -90,7 +107,19 @@ static InterpretResult run() {
case OP_FALSE: push(BOOL_VAL(false)); break;
case OP_GREATER: BINARY_OP(BOOL_VAL, >); break;
case OP_LESS: BINARY_OP(BOOL_VAL, <); break;
case OP_ADD: BINARY_OP(NUMBER_VAL, +); break;
case OP_ADD: {
if (IS_STRING(peek(0)) && IS_STRING(peek(1))) {
concatenate();
} else if (IS_NUMBER(peek(0)) && IS_NUMBER(peek(1))) {
double b = AS_NUMBER(pop());
double a = AS_NUMBER(pop());
push(NUMBER_VAL(a + b));
} else {
runtimeError("Operands must be two numbers or two strings.");
return INTERPRET_RUNTIME_ERROR;
}
break;
}
case OP_SUBTRACT: BINARY_OP(NUMBER_VAL, -); break;
case OP_MULTIPLY: BINARY_OP(NUMBER_VAL, *); break;
case OP_DIVIDE: BINARY_OP(NUMBER_VAL, /); break;
Expand Down

0 comments on commit c68693c

Please sign in to comment.