feat: add io.write primitive and test runner with number literal tests
Implement the io.write() primitive function that prints a value followed by a newline, enabling basic output for testing. Add a Python test runner script (runtests) that discovers and executes .wren test files, comparing output against // expect: comments. Include initial number literal tests covering positive, negative, and zero values. Remove debug print statements from the VM interpreter and refactor primitive registration macros for cleaner code.
This commit is contained in:
+2
-2
@@ -268,8 +268,8 @@ int defineName(Compiler* compiler)
|
||||
}
|
||||
|
||||
int symbol = addSymbol(symbols,
|
||||
compiler->parser->source + compiler->parser->previous.start,
|
||||
compiler->parser->previous.end - compiler->parser->previous.start);
|
||||
compiler->parser->source + compiler->parser->previous.start,
|
||||
compiler->parser->previous.end - compiler->parser->previous.start);
|
||||
|
||||
if (symbol == -1)
|
||||
{
|
||||
|
||||
+1
-3
@@ -30,9 +30,7 @@ int main(int argc, const char * argv[])
|
||||
|
||||
if (block)
|
||||
{
|
||||
Value value = interpret(vm, block);
|
||||
printValue(value);
|
||||
printf("\n");
|
||||
interpret(vm, block);
|
||||
}
|
||||
|
||||
freeVM(vm);
|
||||
|
||||
+29
-10
@@ -1,3 +1,4 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -6,14 +7,21 @@
|
||||
#define PRIMITIVE(cls, name, prim) \
|
||||
{ \
|
||||
int symbol = ensureSymbol(&vm->symbols, name, strlen(name)); \
|
||||
vm->cls##Class->methods[symbol].type = METHOD_PRIMITIVE; \
|
||||
vm->cls##Class->methods[symbol].primitive = primitive_##cls##_##prim; \
|
||||
cls->methods[symbol].type = METHOD_PRIMITIVE; \
|
||||
cls->methods[symbol].primitive = primitive_##prim; \
|
||||
}
|
||||
|
||||
#define DEF_PRIMITIVE(cls, prim) \
|
||||
static Value primitive_##cls##_##prim(Value* args, int numArgs)
|
||||
#define DEF_PRIMITIVE(prim) \
|
||||
static Value primitive_##prim(Value* args, int numArgs)
|
||||
|
||||
DEF_PRIMITIVE(num, abs)
|
||||
#define GLOBAL(cls, name) \
|
||||
{ \
|
||||
ObjInstance* obj = makeInstance(cls); \
|
||||
int symbol = addSymbol(&vm->globalSymbols, name, strlen(name)); \
|
||||
vm->globals[symbol] = (Value)obj; \
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(num_abs)
|
||||
{
|
||||
double value = ((ObjNum*)args[0])->value;
|
||||
if (value < 0) value = -value;
|
||||
@@ -21,7 +29,7 @@ DEF_PRIMITIVE(num, abs)
|
||||
return (Value)makeNum(value);
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(string, contains)
|
||||
DEF_PRIMITIVE(string_contains)
|
||||
{
|
||||
const char* string = ((ObjString*)args[0])->value;
|
||||
// TODO(bob): Check type of arg first!
|
||||
@@ -31,16 +39,27 @@ DEF_PRIMITIVE(string, contains)
|
||||
return (Value)makeNum(strstr(string, search) != NULL);
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(string, count)
|
||||
DEF_PRIMITIVE(string_count)
|
||||
{
|
||||
double count = strlen(((ObjString*)args[0])->value);
|
||||
|
||||
return (Value)makeNum(count);
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(io_write)
|
||||
{
|
||||
printValue(args[1]);
|
||||
printf("\n");
|
||||
return args[1];
|
||||
}
|
||||
|
||||
void registerPrimitives(VM* vm)
|
||||
{
|
||||
PRIMITIVE(num, "abs", abs);
|
||||
PRIMITIVE(string, "contains ", contains);
|
||||
PRIMITIVE(string, "count", count);
|
||||
PRIMITIVE(vm->numClass, "abs", num_abs);
|
||||
PRIMITIVE(vm->stringClass, "contains ", string_contains);
|
||||
PRIMITIVE(vm->stringClass, "count", string_count);
|
||||
|
||||
ObjClass* ioClass = makeClass();
|
||||
PRIMITIVE(ioClass, "write ", io_write);
|
||||
GLOBAL(ioClass, "io");
|
||||
}
|
||||
@@ -200,9 +200,6 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
int constant = frame->block->bytecode[frame->ip++];
|
||||
Value value = frame->block->constants[constant];
|
||||
fiber.stack[fiber.stackSize++] = value;
|
||||
printf("load constant ");
|
||||
printValue(value);
|
||||
printf(" to %d\n", fiber.stackSize - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -213,13 +210,11 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
// Define a "new" method on the metaclass.
|
||||
// TODO(bob): Can this be inherited?
|
||||
int newSymbol = ensureSymbol(&vm->symbols, "new", strlen("new"));
|
||||
printf("define new %d\n", newSymbol);
|
||||
classObj->metaclass->methods[newSymbol].type = METHOD_PRIMITIVE;
|
||||
classObj->metaclass->methods[newSymbol].primitive =
|
||||
primitive_metaclass_new;
|
||||
|
||||
push(&fiber, (Value)classObj);
|
||||
printf("push class at %d\n", fiber.stackSize - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -232,10 +227,6 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
ObjBlock* body = (ObjBlock*)frame->block->constants[constant];
|
||||
classObj->methods[symbol].type = METHOD_BLOCK;
|
||||
classObj->methods[symbol].block = body;
|
||||
|
||||
printf("define method %d using constant %d on ", symbol, constant);
|
||||
printValue((Value)classObj);
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -243,14 +234,12 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
{
|
||||
int local = frame->block->bytecode[frame->ip++];
|
||||
push(&fiber, fiber.stack[frame->locals + local]);
|
||||
printf("load local %d to %d\n", local, fiber.stackSize - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_STORE_LOCAL:
|
||||
{
|
||||
int local = frame->block->bytecode[frame->ip++];
|
||||
printf("store local %d from %d\n", local, fiber.stackSize - 1);
|
||||
fiber.stack[frame->locals + local] = fiber.stack[fiber.stackSize - 1];
|
||||
break;
|
||||
}
|
||||
@@ -259,25 +248,21 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
{
|
||||
int global = frame->block->bytecode[frame->ip++];
|
||||
push(&fiber, vm->globals[global]);
|
||||
printf("load global %d to %d\n", global, fiber.stackSize - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_STORE_GLOBAL:
|
||||
{
|
||||
int global = frame->block->bytecode[frame->ip++];
|
||||
printf("store global %d from %d\n", global, fiber.stackSize - 1);
|
||||
vm->globals[global] = fiber.stack[fiber.stackSize - 1];
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_DUP:
|
||||
push(&fiber, fiber.stack[fiber.stackSize - 1]);
|
||||
printf("dup %d\n", fiber.stackSize - 1);
|
||||
break;
|
||||
|
||||
case CODE_POP:
|
||||
printf("pop %d\n", fiber.stackSize - 1);
|
||||
pop(&fiber);
|
||||
break;
|
||||
|
||||
@@ -323,10 +308,6 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
break;
|
||||
}
|
||||
|
||||
printf("call %d on ", symbol);
|
||||
printValue(receiver);
|
||||
printf("\n");
|
||||
|
||||
Method* method = &classObj->methods[symbol];
|
||||
switch (method->type)
|
||||
{
|
||||
@@ -363,19 +344,10 @@ Value interpret(VM* vm, ObjBlock* block)
|
||||
fiber.numFrames--;
|
||||
|
||||
// If we are returning from the top-level block, just return the value.
|
||||
if (fiber.numFrames == 0)
|
||||
{
|
||||
printf("done with result ");
|
||||
printValue(result);
|
||||
printf("\n");
|
||||
return result;
|
||||
}
|
||||
if (fiber.numFrames == 0) return result;
|
||||
|
||||
// Store the result of the block in the first slot, which is where the
|
||||
// caller expects it.
|
||||
printf("return and store result ");
|
||||
printValue(result);
|
||||
printf(" in %d\n", frame->locals);
|
||||
fiber.stack[frame->locals] = result;
|
||||
|
||||
// Discard the stack slots for the locals.
|
||||
@@ -391,7 +363,7 @@ void printValue(Value value)
|
||||
switch (value->type)
|
||||
{
|
||||
case OBJ_NUM:
|
||||
printf("%f", ((ObjNum*)value)->value);
|
||||
printf("%g", ((ObjNum*)value)->value);
|
||||
break;
|
||||
|
||||
case OBJ_STRING:
|
||||
|
||||
Reference in New Issue
Block a user