forked from Mirror/wren
List literals.
This commit is contained in:
@ -783,6 +783,27 @@ static void grouping(Compiler* compiler, int allowAssignment)
|
||||
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after expression.");
|
||||
}
|
||||
|
||||
static void list(Compiler* compiler, int allowAssignment)
|
||||
{
|
||||
// Compile the list elements.
|
||||
int numElements = 0;
|
||||
if (peek(compiler) != TOKEN_RIGHT_BRACKET)
|
||||
{
|
||||
do
|
||||
{
|
||||
numElements++;
|
||||
assignment(compiler);
|
||||
} while (match(compiler, TOKEN_COMMA));
|
||||
}
|
||||
|
||||
consume(compiler, TOKEN_RIGHT_BRACKET, "Expect ']' after list elements.");
|
||||
|
||||
// Create the list.
|
||||
emit(compiler, CODE_LIST);
|
||||
// TODO(bob): Handle lists >255 elements.
|
||||
emit(compiler, numElements);
|
||||
}
|
||||
|
||||
// Unary operators like `-foo`.
|
||||
static void unaryOp(Compiler* compiler, int allowAssignment)
|
||||
{
|
||||
@ -1145,14 +1166,13 @@ void mixedSignature(Compiler* compiler, char* name, int* length)
|
||||
#define PREFIX(fn) { fn, NULL, NULL, PREC_NONE, NULL }
|
||||
#define INFIX(prec, fn) { NULL, fn, NULL, prec, NULL }
|
||||
#define INFIX_OPERATOR(prec, name) { NULL, infixOp, infixSignature, prec, name }
|
||||
#define OPERATOR(prec, name) { unaryOp, infixOp, mixedSignature, prec, name }
|
||||
#define PREFIX_OPERATOR(name) { unaryOp, NULL, unarySignature, PREC_NONE, name }
|
||||
|
||||
GrammarRule rules[] =
|
||||
{
|
||||
/* TOKEN_LEFT_PAREN */ PREFIX(grouping),
|
||||
/* TOKEN_RIGHT_PAREN */ UNUSED,
|
||||
/* TOKEN_LEFT_BRACKET */ INFIX(PREC_CALL, subscript),
|
||||
/* TOKEN_LEFT_BRACKET */ { list, subscript, NULL, PREC_CALL, NULL },
|
||||
/* TOKEN_RIGHT_BRACKET */ UNUSED,
|
||||
/* TOKEN_LEFT_BRACE */ UNUSED,
|
||||
/* TOKEN_RIGHT_BRACE */ UNUSED,
|
||||
@ -1163,7 +1183,7 @@ GrammarRule rules[] =
|
||||
/* TOKEN_SLASH */ INFIX_OPERATOR(PREC_FACTOR, "/ "),
|
||||
/* TOKEN_PERCENT */ INFIX_OPERATOR(PREC_TERM, "% "),
|
||||
/* TOKEN_PLUS */ INFIX_OPERATOR(PREC_TERM, "+ "),
|
||||
/* TOKEN_MINUS */ OPERATOR(PREC_TERM, "- "),
|
||||
/* TOKEN_MINUS */ { unaryOp, infixOp, mixedSignature, PREC_TERM, "- " },
|
||||
/* TOKEN_PIPE */ UNUSED,
|
||||
/* TOKEN_PIPEPIPE */ INFIX(PREC_LOGIC, or),
|
||||
/* TOKEN_AMP */ UNUSED,
|
||||
|
||||
@ -85,6 +85,12 @@ DEF_PRIMITIVE(fn_bangeq)
|
||||
return BOOL_VAL(AS_FN(args[0]) != AS_FN(args[1]));
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(list_count)
|
||||
{
|
||||
ObjList* list = AS_LIST(args[0]);
|
||||
return NUM_VAL(list->count);
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(num_abs)
|
||||
{
|
||||
return NUM_VAL(fabs(AS_NUM(args[0])));
|
||||
@ -277,6 +283,7 @@ static const char* CORE_LIB =
|
||||
"class Bool {}\n"
|
||||
"class Class {}\n"
|
||||
"class Function {}\n"
|
||||
"class List {}\n"
|
||||
"class Num {}\n"
|
||||
"class Null {}\n"
|
||||
"class String {}\n"
|
||||
@ -310,6 +317,9 @@ void loadCore(VM* vm)
|
||||
PRIMITIVE(vm->fnClass, "== ", fn_eqeq);
|
||||
PRIMITIVE(vm->fnClass, "!= ", fn_bangeq);
|
||||
|
||||
vm->listClass = AS_CLASS(findGlobal(vm, "List"));
|
||||
PRIMITIVE(vm->listClass, "count", list_count);
|
||||
|
||||
vm->nullClass = AS_CLASS(findGlobal(vm, "Null"));
|
||||
|
||||
vm->numClass = AS_CLASS(findGlobal(vm, "Num"));
|
||||
|
||||
12
src/value.h
12
src/value.h
@ -22,6 +22,7 @@ typedef enum {
|
||||
OBJ_CLASS,
|
||||
OBJ_FN,
|
||||
OBJ_INSTANCE,
|
||||
OBJ_LIST,
|
||||
OBJ_STRING
|
||||
} ObjType;
|
||||
|
||||
@ -121,6 +122,14 @@ typedef struct
|
||||
Value fields[];
|
||||
} ObjInstance;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Obj obj;
|
||||
int count;
|
||||
// Pointer to a contiguous array of [length] elements.
|
||||
Value* elements;
|
||||
} ObjList;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Obj obj;
|
||||
@ -137,6 +146,9 @@ typedef struct
|
||||
// Value -> ObjInstance*.
|
||||
#define AS_INSTANCE(value) ((ObjInstance*)AS_OBJ(value))
|
||||
|
||||
// Value -> ObjList*.
|
||||
#define AS_LIST(value) ((ObjList*)AS_OBJ(value))
|
||||
|
||||
// Value -> double.
|
||||
#define AS_NUM(v) ((v).num)
|
||||
|
||||
|
||||
98
src/vm.c
98
src/vm.c
@ -93,6 +93,15 @@ static void markInstance(ObjInstance* instance)
|
||||
}
|
||||
}
|
||||
|
||||
static void markList(ObjList* list)
|
||||
{
|
||||
Value* elements = list->elements;
|
||||
for (int i = 0; i < list->count; i++)
|
||||
{
|
||||
markValue(elements[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void markObj(Obj* obj)
|
||||
{
|
||||
#ifdef TRACE_MEMORY
|
||||
@ -110,6 +119,7 @@ static void markObj(Obj* obj)
|
||||
case OBJ_CLASS: markClass((ObjClass*)obj); break;
|
||||
case OBJ_FN: markFn((ObjFn*)obj); break;
|
||||
case OBJ_INSTANCE: markInstance((ObjInstance*)obj); break;
|
||||
case OBJ_LIST: markList((ObjList*)obj); break;
|
||||
case OBJ_STRING:
|
||||
// Just mark the string itself.
|
||||
obj->flags |= FLAG_MARKED;
|
||||
@ -139,6 +149,10 @@ void freeObj(VM* vm, Obj* obj)
|
||||
|
||||
switch (obj->type)
|
||||
{
|
||||
case OBJ_CLASS:
|
||||
size = sizeof(ObjClass);
|
||||
break;
|
||||
|
||||
case OBJ_FN:
|
||||
{
|
||||
// TODO(bob): Don't hardcode array sizes.
|
||||
@ -149,6 +163,25 @@ void freeObj(VM* vm, Obj* obj)
|
||||
break;
|
||||
}
|
||||
|
||||
case OBJ_INSTANCE:
|
||||
{
|
||||
size = sizeof(ObjInstance);
|
||||
|
||||
// Include the size of the field array.
|
||||
ObjInstance* instance = (ObjInstance*)obj;
|
||||
size += sizeof(Value) * instance->classObj->numFields;
|
||||
break;
|
||||
}
|
||||
|
||||
case OBJ_LIST:
|
||||
{
|
||||
size = sizeof(ObjList);
|
||||
ObjList* list = (ObjList*)obj;
|
||||
size += sizeof(Value) * list->count;
|
||||
free(list->elements);
|
||||
break;
|
||||
}
|
||||
|
||||
case OBJ_STRING:
|
||||
{
|
||||
// TODO(bob): O(n) calculation here is lame!
|
||||
@ -157,16 +190,6 @@ void freeObj(VM* vm, Obj* obj)
|
||||
free(string->value);
|
||||
break;
|
||||
}
|
||||
|
||||
case OBJ_CLASS:
|
||||
size = sizeof(ObjClass);
|
||||
break;
|
||||
|
||||
case OBJ_INSTANCE:
|
||||
// Nothing to delete.
|
||||
size = sizeof(Obj);
|
||||
// TODO(bob): Include size of fields for OBJ_INSTANCE.
|
||||
break;
|
||||
}
|
||||
|
||||
vm->totalAllocated -= size;
|
||||
@ -233,6 +256,8 @@ void collectGarbage(VM* vm)
|
||||
|
||||
void* allocate(VM* vm, size_t size)
|
||||
{
|
||||
ASSERT(size > 0, "Should not allocate 0 bytes.");
|
||||
|
||||
vm->totalAllocated += size;
|
||||
|
||||
#ifdef DEBUG_GC_STRESS
|
||||
@ -345,6 +370,23 @@ Value newInstance(VM* vm, ObjClass* classObj)
|
||||
return OBJ_VAL(instance);
|
||||
}
|
||||
|
||||
ObjList* newList(VM* vm, int numElements)
|
||||
{
|
||||
// Allocate this before the list object in case it triggers a GC which would
|
||||
// free the list.
|
||||
Value* elements = NULL;
|
||||
if (numElements > 0)
|
||||
{
|
||||
elements = allocate(vm, sizeof(Value) * numElements);
|
||||
}
|
||||
|
||||
ObjList* list = allocate(vm, sizeof(ObjList));
|
||||
initObj(vm, &list->obj, OBJ_LIST);
|
||||
list->count = numElements;
|
||||
list->elements = elements;
|
||||
return list;
|
||||
}
|
||||
|
||||
Value newString(VM* vm, const char* text, size_t length)
|
||||
{
|
||||
// Allocate before the string object in case this triggers a GC which would
|
||||
@ -474,12 +516,20 @@ int dumpInstruction(VM* vm, ObjFn* fn, int i)
|
||||
break;
|
||||
|
||||
case CODE_CLASS:
|
||||
{
|
||||
int numFields = bytecode[i++];
|
||||
printf("CLASS\n");
|
||||
printf("%04d | num fields %d\n", i, numFields);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_SUBCLASS:
|
||||
{
|
||||
int numFields = bytecode[i++];
|
||||
printf("SUBCLASS\n");
|
||||
printf("%04d | num fields %d\n", i, numFields);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_METHOD_INSTANCE:
|
||||
{
|
||||
@ -511,6 +561,14 @@ int dumpInstruction(VM* vm, ObjFn* fn, int i)
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_LIST:
|
||||
{
|
||||
int count = bytecode[i++];
|
||||
printf("LIST\n");
|
||||
printf("%04d | count %d\n", i, count);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_LOAD_LOCAL:
|
||||
{
|
||||
int local = bytecode[i++];
|
||||
@ -673,6 +731,7 @@ static ObjClass* getClass(VM* vm, Value value)
|
||||
case OBJ_CLASS: return AS_CLASS(value)->metaclass;
|
||||
case OBJ_FN: return vm->fnClass;
|
||||
case OBJ_INSTANCE: return AS_INSTANCE(value)->classObj;
|
||||
case OBJ_LIST: return vm->listClass;
|
||||
case OBJ_STRING: return vm->stringClass;
|
||||
}
|
||||
}
|
||||
@ -699,6 +758,7 @@ static ObjClass* getClass(VM* vm, Value value)
|
||||
{
|
||||
case OBJ_CLASS: return AS_CLASS(value)->metaclass;
|
||||
case OBJ_FN: return vm->fnClass;
|
||||
case OBJ_LIST: return vm->listClass;
|
||||
case OBJ_STRING: return vm->stringClass;
|
||||
case OBJ_INSTANCE: return AS_INSTANCE(value)->classObj;
|
||||
}
|
||||
@ -797,6 +857,22 @@ Value interpret(VM* vm, ObjFn* fn)
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_LIST:
|
||||
{
|
||||
int numElements = READ_ARG();
|
||||
ObjList* list = newList(vm, numElements);
|
||||
for (int i = 0; i < numElements; i++)
|
||||
{
|
||||
list->elements[i] = fiber->stack[fiber->stackSize - numElements + i];
|
||||
}
|
||||
|
||||
// Discard the elements.
|
||||
fiber->stackSize -= numElements;
|
||||
|
||||
PUSH(OBJ_VAL(list));
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_METHOD_INSTANCE:
|
||||
case CODE_METHOD_STATIC:
|
||||
case CODE_METHOD_CTOR:
|
||||
@ -1098,6 +1174,7 @@ void printValue(Value value)
|
||||
case OBJ_CLASS: printf("[class %p]", obj); break;
|
||||
case OBJ_FN: printf("[fn %p]", obj); break;
|
||||
case OBJ_INSTANCE: printf("[instance %p]", obj); break;
|
||||
case OBJ_LIST: printf("[list %p]", obj); break;
|
||||
case OBJ_STRING: printf("%s", AS_CSTRING(value)); break;
|
||||
}
|
||||
}
|
||||
@ -1124,6 +1201,7 @@ void printValue(Value value)
|
||||
case OBJ_CLASS: printf("[class %p]", value.obj); break;
|
||||
case OBJ_FN: printf("[fn %p]", value.obj); break;
|
||||
case OBJ_INSTANCE: printf("[instance %p]", value.obj); break;
|
||||
case OBJ_LIST: printf("[list %p]", value.obj); break;
|
||||
case OBJ_STRING: printf("%s", AS_CSTRING(value)); break;
|
||||
}
|
||||
}
|
||||
|
||||
20
src/vm.h
20
src/vm.h
@ -42,11 +42,10 @@ typedef enum
|
||||
// the stack.
|
||||
CODE_METHOD_CTOR,
|
||||
|
||||
// Push a copy of the top of stack.
|
||||
CODE_DUP,
|
||||
|
||||
// Pop and discard the top of stack.
|
||||
CODE_POP,
|
||||
// Create a new list with [arg] elements. The top [arg] values on the stack
|
||||
// are the elements in forward order. Removes the elements and then pushes
|
||||
// the new list.
|
||||
CODE_LIST,
|
||||
|
||||
// Pushes the value in local slot [arg].
|
||||
CODE_LOAD_LOCAL,
|
||||
@ -66,6 +65,12 @@ typedef enum
|
||||
// Stores the top of stack in field slot [arg] in the current receiver.
|
||||
CODE_STORE_FIELD,
|
||||
|
||||
// Push a copy of the top of stack.
|
||||
CODE_DUP,
|
||||
|
||||
// Pop and discard the top of stack.
|
||||
CODE_POP,
|
||||
|
||||
// Invoke the method with symbol [arg]. The number indicates the number of
|
||||
// arguments (not including the receiver).
|
||||
CODE_CALL_0,
|
||||
@ -119,6 +124,7 @@ struct sVM
|
||||
ObjClass* boolClass;
|
||||
ObjClass* classClass;
|
||||
ObjClass* fnClass;
|
||||
ObjClass* listClass;
|
||||
ObjClass* nullClass;
|
||||
ObjClass* numClass;
|
||||
ObjClass* objectClass;
|
||||
@ -191,6 +197,10 @@ ObjClass* newClass(VM* vm, ObjClass* superclass, int numFields);
|
||||
// Creates a new instance of the given [classObj].
|
||||
Value newInstance(VM* vm, ObjClass* classObj);
|
||||
|
||||
// Creates a new list with [numElements] elements (which are left
|
||||
// uninitialized.)
|
||||
ObjList* newList(VM* vm, int numElements);
|
||||
|
||||
// Creates a new string object and copies [text] into it.
|
||||
Value newString(VM* vm, const char* text, size_t length);
|
||||
|
||||
|
||||
@ -24,3 +24,4 @@ bar.method(1, 2, 3, 4) // expect: bar
|
||||
// TODO(bob): Private fields.
|
||||
// TODO(bob): Super (or inner) calls.
|
||||
// TODO(bob): Grammar for what expressions can follow "is".
|
||||
// TODO(bob): Prevent extending built-in types.
|
||||
|
||||
7
test/list_count.wren
Normal file
7
test/list_count.wren
Normal file
@ -0,0 +1,7 @@
|
||||
io.write([].count) // expect: 0
|
||||
io.write([1].count) // expect: 1
|
||||
io.write([1, 2, 3, 4].count) // expect: 4
|
||||
|
||||
// TODO(bob): Literal syntax, including newline handling.
|
||||
// TODO(bob): Unterminated list literal.
|
||||
// TODO(bob): Subscript operator.
|
||||
Reference in New Issue
Block a user