feat: replace block syntax with fn keyword for function literals across compiler and tests
Rename `block` parsing rule to `fn` by adding TOKEN_FN token and function prefix rule, removing the old block prefix from TOKEN_LEFT_BRACE. Update compiler.c to replace `block()` with `function()` and add fn equality primitives (fn_eqeq, fn_bangeq) in primitives.c. Migrate test files: rename block_call.wren to fn_call.wren, block_syntax.wren to fn_syntax.wren, and replace `{ ... }` with `fn { ... }` or `fn expr` syntax. Remove obsolete bool_equality.wren test.
This commit is contained in:
+50
-20
@@ -37,6 +37,7 @@ typedef enum
|
||||
TOKEN_CLASS,
|
||||
TOKEN_ELSE,
|
||||
TOKEN_FALSE,
|
||||
TOKEN_FN,
|
||||
TOKEN_IF,
|
||||
TOKEN_META,
|
||||
TOKEN_NULL,
|
||||
@@ -164,8 +165,8 @@ static void expression(Compiler* compiler);
|
||||
static void parsePrecedence(Compiler* compiler, int precedence);
|
||||
|
||||
static void grouping(Compiler* compiler);
|
||||
static void block(Compiler* compiler);
|
||||
static void boolean(Compiler* compiler);
|
||||
static void function(Compiler* compiler);
|
||||
static void name(Compiler* compiler);
|
||||
static void null(Compiler* compiler);
|
||||
static void number(Compiler* compiler);
|
||||
@@ -235,7 +236,7 @@ ParseRule rules[] =
|
||||
/* TOKEN_RIGHT_PAREN */ UNUSED,
|
||||
/* TOKEN_LEFT_BRACKET */ UNUSED,
|
||||
/* TOKEN_RIGHT_BRACKET */ UNUSED,
|
||||
/* TOKEN_LEFT_BRACE */ PREFIX(block),
|
||||
/* TOKEN_LEFT_BRACE */ UNUSED,
|
||||
/* TOKEN_RIGHT_BRACE */ UNUSED,
|
||||
/* TOKEN_COLON */ UNUSED,
|
||||
/* TOKEN_DOT */ INFIX(PREC_CALL, call),
|
||||
@@ -258,6 +259,7 @@ ParseRule rules[] =
|
||||
/* TOKEN_CLASS */ UNUSED,
|
||||
/* TOKEN_ELSE */ UNUSED,
|
||||
/* TOKEN_FALSE */ PREFIX(boolean),
|
||||
/* TOKEN_FN */ PREFIX(function),
|
||||
/* TOKEN_IF */ UNUSED,
|
||||
/* TOKEN_META */ UNUSED,
|
||||
/* TOKEN_NULL */ PREFIX(null),
|
||||
@@ -549,20 +551,6 @@ void grouping(Compiler* compiler)
|
||||
consume(compiler, TOKEN_RIGHT_PAREN);
|
||||
}
|
||||
|
||||
void block(Compiler* compiler)
|
||||
{
|
||||
// TODO(bob): Make this just a local scope, not a block object.
|
||||
ObjFn* fn = compileFunction(
|
||||
compiler->parser, compiler, TOKEN_RIGHT_BRACE);
|
||||
|
||||
// Add the function to the constant table.
|
||||
compiler->fn->constants[compiler->fn->numConstants++] = (Value)fn;
|
||||
|
||||
// Compile the code to load it.
|
||||
emit(compiler, CODE_CONSTANT);
|
||||
emit(compiler, compiler->fn->numConstants - 1);
|
||||
}
|
||||
|
||||
void boolean(Compiler* compiler)
|
||||
{
|
||||
if (compiler->parser->previous.type == TOKEN_FALSE)
|
||||
@@ -575,6 +563,49 @@ void boolean(Compiler* compiler)
|
||||
}
|
||||
}
|
||||
|
||||
void function(Compiler* compiler)
|
||||
{
|
||||
// TODO(bob): Copied from compileFunction(). Unify?
|
||||
Compiler fnCompiler;
|
||||
initCompiler(&fnCompiler, compiler->parser, compiler);
|
||||
|
||||
if (match(&fnCompiler, TOKEN_LEFT_BRACE))
|
||||
{
|
||||
// Block body.
|
||||
for (;;)
|
||||
{
|
||||
statement(&fnCompiler);
|
||||
|
||||
// If there is no newline, it must be the end of the block on the same line.
|
||||
if (!match(&fnCompiler, TOKEN_LINE))
|
||||
{
|
||||
consume(&fnCompiler, TOKEN_RIGHT_BRACE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (match(&fnCompiler, TOKEN_RIGHT_BRACE)) break;
|
||||
|
||||
// Discard the result of the previous expression.
|
||||
emit(&fnCompiler, CODE_POP);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Single expression body.
|
||||
expression(&fnCompiler);
|
||||
}
|
||||
|
||||
emit(&fnCompiler, CODE_END);
|
||||
fnCompiler.fn->numLocals = fnCompiler.locals.count;
|
||||
|
||||
// Add the function to the constant table.
|
||||
compiler->fn->constants[compiler->fn->numConstants++] = (Value)fnCompiler.fn;
|
||||
|
||||
// Compile the code to load it.
|
||||
emit(compiler, CODE_CONSTANT);
|
||||
emit(compiler, compiler->fn->numConstants - 1);
|
||||
}
|
||||
|
||||
void name(Compiler* compiler)
|
||||
{
|
||||
// See if it's a local in this scope.
|
||||
@@ -686,8 +717,6 @@ void call(Compiler* compiler)
|
||||
length += partLength;
|
||||
// TODO(bob): Check for length overflow.
|
||||
|
||||
// TODO(bob): Allow block arguments.
|
||||
|
||||
// Parse the argument list, if any.
|
||||
if (match(compiler, TOKEN_LEFT_PAREN))
|
||||
{
|
||||
@@ -956,9 +985,10 @@ void readName(Parser* parser)
|
||||
TokenType type = TOKEN_NAME;
|
||||
|
||||
if (isKeyword(parser, "class")) type = TOKEN_CLASS;
|
||||
if (isKeyword(parser, "if")) type = TOKEN_IF;
|
||||
if (isKeyword(parser, "false")) type = TOKEN_FALSE;
|
||||
if (isKeyword(parser, "else")) type = TOKEN_ELSE;
|
||||
if (isKeyword(parser, "false")) type = TOKEN_FALSE;
|
||||
if (isKeyword(parser, "fn")) type = TOKEN_FN;
|
||||
if (isKeyword(parser, "if")) type = TOKEN_IF;
|
||||
if (isKeyword(parser, "meta")) type = TOKEN_META;
|
||||
if (isKeyword(parser, "null")) type = TOKEN_NULL;
|
||||
if (isKeyword(parser, "true")) type = TOKEN_TRUE;
|
||||
|
||||
@@ -69,6 +69,18 @@ DEF_PRIMITIVE(fn_call)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fn_eqeq)
|
||||
{
|
||||
if (args[1]->type != OBJ_FN) return makeBool(0);
|
||||
return makeBool(AS_FN(args[0]) == AS_FN(args[1]));
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fn_bangeq)
|
||||
{
|
||||
if (args[1]->type != OBJ_FN) return makeBool(1);
|
||||
return makeBool(AS_FN(args[0]) != AS_FN(args[1]));
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(num_abs)
|
||||
{
|
||||
double value = AS_NUM(args[0]);
|
||||
@@ -226,6 +238,8 @@ void registerPrimitives(VM* vm)
|
||||
|
||||
vm->fnClass = makeClass();
|
||||
PRIMITIVE(vm->fnClass, "call", fn_call);
|
||||
PRIMITIVE(vm->fnClass, "== ", fn_eqeq);
|
||||
PRIMITIVE(vm->fnClass, "!= ", fn_bangeq);
|
||||
|
||||
vm->nullClass = makeClass();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user