1
0
forked from Mirror/wren

List literals.

This commit is contained in:
Bob Nystrom
2013-11-24 13:38:31 -08:00
parent f9b008e283
commit 7a343f2ecf
7 changed files with 156 additions and 18 deletions

View File

@ -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,

View File

@ -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"));

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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
View 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.