[Chapter19&Strings] - Adding strings
This commit is contained in:
parent
8d0feaf157
commit
e218782499
@ -173,6 +173,11 @@ static void number() {
|
|||||||
emitConstant(NUMBER_VAL(value));
|
emitConstant(NUMBER_VAL(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void string() {
|
||||||
|
emitConstant(OBJ_VAL(copyString(parser.previous.start + 1,
|
||||||
|
parser.previous.length - 2)));
|
||||||
|
}
|
||||||
|
|
||||||
static void unary() {
|
static void unary() {
|
||||||
TokenType operatorType = parser.previous.type;
|
TokenType operatorType = parser.previous.type;
|
||||||
|
|
||||||
@ -226,7 +231,7 @@ ParseRule rules[] = {
|
|||||||
[TOKEN_LESS] = {NULL, binary, PREC_COMPARISON},
|
[TOKEN_LESS] = {NULL, binary, PREC_COMPARISON},
|
||||||
[TOKEN_LESS_EQUAL] = {NULL, binary, PREC_COMPARISON},
|
[TOKEN_LESS_EQUAL] = {NULL, binary, PREC_COMPARISON},
|
||||||
[TOKEN_IDENTIFIER] = {NULL, NULL, PREC_NONE},
|
[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_NUMBER] = {number, NULL, PREC_NONE},
|
||||||
[TOKEN_AND] = {NULL, NULL, PREC_NONE},
|
[TOKEN_AND] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_CLASS] = {NULL, NULL, PREC_NONE},
|
[TOKEN_CLASS] = {NULL, NULL, PREC_NONE},
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#ifndef clox_compiler_h
|
#ifndef clox_compiler_h
|
||||||
#define clox_compiler_h
|
#define clox_compiler_h
|
||||||
|
|
||||||
|
#include "object.h"
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
|
|
||||||
bool compile(const char* source, Chunk* chunk);
|
bool compile(const char* source, Chunk* chunk);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "vm.h"
|
||||||
|
|
||||||
void* reallocate(void* pointer, size_t oldSize, size_t newSize) {
|
void* reallocate(void* pointer, size_t oldSize, size_t newSize) {
|
||||||
if (newSize == 0) {
|
if (newSize == 0) {
|
||||||
@ -12,3 +13,23 @@ void* reallocate(void* pointer, size_t oldSize, size_t newSize) {
|
|||||||
if (result == NULL) exit(1);
|
if (result == NULL) exit(1);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
static void freeObject(Obj* object) {
|
||||||
|
switch (object->type) {
|
||||||
|
case OBJ_STRING: {
|
||||||
|
ObjString* string = (ObjString*)object;
|
||||||
|
FREE_ARRAY(char, string->chars, string->length + 1);
|
||||||
|
FREE(ObjString, object);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void freeObjects() {
|
||||||
|
Obj* object = vm.objects;
|
||||||
|
while (object != NULL) {
|
||||||
|
Obj* next = object->next;
|
||||||
|
freeObject(object);
|
||||||
|
object = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,6 +2,12 @@
|
|||||||
#define clox_memory_h
|
#define clox_memory_h
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "object.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) \
|
#define GROW_CAPACITY(capacity) \
|
||||||
((capacity) < 8 ? 8 : (capacity) * 2)
|
((capacity) < 8 ? 8 : (capacity) * 2)
|
||||||
@ -14,5 +20,6 @@
|
|||||||
reallocate(pointer, sizeof(type) * (oldCount), 0)
|
reallocate(pointer, sizeof(type) * (oldCount), 0)
|
||||||
|
|
||||||
void* reallocate(void* pointer, size_t oldSize, size_t newSize);
|
void* reallocate(void* pointer, size_t oldSize, size_t newSize);
|
||||||
|
void freeObjects();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
43
clox/object.c
Normal file
43
clox/object.c
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#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;
|
||||||
|
|
||||||
|
object->next = vm.objects;
|
||||||
|
vm.objects = object;
|
||||||
|
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* takeString(char* chars, int length) {
|
||||||
|
return allocateString(chars, length);
|
||||||
|
}
|
||||||
|
ObjString* copyString(const char* chars, int length) {
|
||||||
|
char* heapChars = ALLOCATE(char, length + 1);
|
||||||
|
memcpy(heapChars, chars, length);
|
||||||
|
heapChars[length] = '\0';
|
||||||
|
return allocateString(heapChars, length);
|
||||||
|
}
|
||||||
|
void printObject(Value value) {
|
||||||
|
switch (OBJ_TYPE(value)) {
|
||||||
|
case OBJ_STRING:
|
||||||
|
printf("%s", AS_CSTRING(value));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
33
clox/object.h
Normal file
33
clox/object.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef clox_object_h
|
||||||
|
#define clox_object_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 Obj* next;
|
||||||
|
};
|
||||||
|
struct ObjString {
|
||||||
|
Obj obj;
|
||||||
|
int length;
|
||||||
|
char* chars;
|
||||||
|
};
|
||||||
|
ObjString* takeString(char* chars, int length);
|
||||||
|
ObjString* copyString(const char* chars, int length);
|
||||||
|
void printObject(Value value);
|
||||||
|
static inline bool isObjType(Value value, ObjType type) {
|
||||||
|
return IS_OBJ(value) && AS_OBJ(value)->type == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
1
clox/tests/test_string.lox
Normal file
1
clox/tests/test_string.lox
Normal file
@ -0,0 +1 @@
|
|||||||
|
"st" + "ri" + "ng"
|
10
clox/value.c
10
clox/value.c
@ -1,5 +1,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#include "object.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
|
|
||||||
@ -30,6 +32,7 @@ void printValue(Value value) {
|
|||||||
break;
|
break;
|
||||||
case VAL_NIL: printf("nil"); break;
|
case VAL_NIL: printf("nil"); break;
|
||||||
case VAL_NUMBER: printf("%g", AS_NUMBER(value)); break;
|
case VAL_NUMBER: printf("%g", AS_NUMBER(value)); break;
|
||||||
|
case VAL_OBJ: printObject(value); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,6 +42,13 @@ bool valuesEqual(Value a, Value b) {
|
|||||||
case VAL_BOOL: return AS_BOOL(a) == AS_BOOL(b);
|
case VAL_BOOL: return AS_BOOL(a) == AS_BOOL(b);
|
||||||
case VAL_NIL: return true;
|
case VAL_NIL: return true;
|
||||||
case VAL_NUMBER: return AS_NUMBER(a) == AS_NUMBER(b);
|
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.
|
default: return false; // Unreachable.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,15 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
typedef struct Obj Obj;
|
||||||
|
typedef struct ObjString ObjString;
|
||||||
//typedef double Value;
|
//typedef double Value;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
VAL_BOOL,
|
VAL_BOOL,
|
||||||
VAL_NIL,
|
VAL_NIL,
|
||||||
VAL_NUMBER,
|
VAL_NUMBER,
|
||||||
|
VAL_OBJ
|
||||||
} ValueType;
|
} ValueType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -17,19 +19,22 @@ typedef struct {
|
|||||||
union {
|
union {
|
||||||
bool boolean;
|
bool boolean;
|
||||||
double number;
|
double number;
|
||||||
|
Obj* obj;
|
||||||
} as;
|
} as;
|
||||||
} Value;
|
} Value;
|
||||||
|
|
||||||
#define IS_BOOL(value) ((value).type == VAL_BOOL)
|
#define IS_BOOL(value) ((value).type == VAL_BOOL)
|
||||||
#define IS_NIL(value) ((value).type == VAL_NIL)
|
#define IS_NIL(value) ((value).type == VAL_NIL)
|
||||||
#define IS_NUMBER(value) ((value).type == VAL_NUMBER)
|
#define IS_NUMBER(value) ((value).type == VAL_NUMBER)
|
||||||
|
#define IS_OBJ(value) ((value).type == VAL_OBJ)
|
||||||
|
|
||||||
#define AS_BOOL(value) ((value).as.boolean)
|
#define AS_BOOL(value) ((value).as.boolean)
|
||||||
#define AS_NUMBER(value) ((value).as.number)
|
#define AS_NUMBER(value) ((value).as.number)
|
||||||
|
|
||||||
#define BOOL_VAL(value) ((Value){VAL_BOOL, {.boolean = value}})
|
#define BOOL_VAL(value) ((Value){VAL_BOOL, {.boolean = value}})
|
||||||
#define NIL_VAL ((Value){VAL_NIL, {.number = 0}})
|
#define NIL_VAL ((Value){VAL_NIL, {.number = 0}})
|
||||||
#define NUMBER_VAL(value) ((Value){VAL_NUMBER, {.number = value}})
|
#define NUMBER_VAL(value) ((Value){VAL_NUMBER, {.number = value}})
|
||||||
|
#define OBJ_VAL(object) ((Value){VAL_OBJ, {.obj = (Obj*)object}})
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int capacity;
|
int capacity;
|
||||||
|
37
clox/vm.c
37
clox/vm.c
@ -1,9 +1,12 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "compiler.h"
|
#include "compiler.h"
|
||||||
#include "vm.h"
|
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
#include "object.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "vm.h"
|
||||||
VM vm;
|
VM vm;
|
||||||
static void resetStack() {
|
static void resetStack() {
|
||||||
vm.stackTop = vm.stack;
|
vm.stackTop = vm.stack;
|
||||||
@ -24,10 +27,11 @@ static void runtimeError(const char* format, ...) {
|
|||||||
|
|
||||||
void initVM() {
|
void initVM() {
|
||||||
resetStack();
|
resetStack();
|
||||||
|
vm.objects = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeVM() {
|
void freeVM() {
|
||||||
|
freeObjects();
|
||||||
}
|
}
|
||||||
void push(Value value) {
|
void push(Value value) {
|
||||||
*vm.stackTop = value;
|
*vm.stackTop = value;
|
||||||
@ -45,6 +49,20 @@ static Value peek(int distance) {
|
|||||||
static bool isFalsey(Value value) {
|
static bool isFalsey(Value value) {
|
||||||
return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(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() {
|
static InterpretResult run() {
|
||||||
#define READ_BYTE() (*vm.ip++)
|
#define READ_BYTE() (*vm.ip++)
|
||||||
@ -91,7 +109,20 @@ static InterpretResult run() {
|
|||||||
}
|
}
|
||||||
case OP_GREATER: BINARY_OP(BOOL_VAL, >); break;
|
case OP_GREATER: BINARY_OP(BOOL_VAL, >); break;
|
||||||
case OP_LESS: 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_SUBTRACT: BINARY_OP(NUMBER_VAL, -); break;
|
||||||
case OP_MULTIPLY: BINARY_OP(NUMBER_VAL, *); break;
|
case OP_MULTIPLY: BINARY_OP(NUMBER_VAL, *); break;
|
||||||
case OP_DIVIDE: BINARY_OP(NUMBER_VAL, /); break;
|
case OP_DIVIDE: BINARY_OP(NUMBER_VAL, /); break;
|
||||||
|
@ -11,6 +11,7 @@ typedef struct {
|
|||||||
uint8_t* ip;
|
uint8_t* ip;
|
||||||
Value stack[STACK_MAX];
|
Value stack[STACK_MAX];
|
||||||
Value* stackTop;
|
Value* stackTop;
|
||||||
|
Obj* objects;
|
||||||
} VM;
|
} VM;
|
||||||
typedef enum {
|
typedef enum {
|
||||||
INTERPRET_OK,
|
INTERPRET_OK,
|
||||||
@ -20,7 +21,8 @@ typedef enum {
|
|||||||
|
|
||||||
void initVM();
|
void initVM();
|
||||||
void freeVM();
|
void freeVM();
|
||||||
InterpretResult interpret(const char* source);
|
InterpretResult interpret(const char* chunk);
|
||||||
|
extern VM vm;
|
||||||
void push(Value value);
|
void push(Value value);
|
||||||
Value pop();
|
Value pop();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user