8 Commits

Author SHA1 Message Date
f54f8df4d6 Remove Fn.new().
It's unneeded now that we have fn () { ... }.
2015-11-28 10:13:14 -08:00
22b9ed8f6f Merge branch 'master' into functional 2015-11-28 10:05:10 -08:00
a45a43606f Add an anonymous expression fn syntax. 2015-11-27 21:17:34 -08:00
edcacb830b Add syntax for named function declarations.
This is technically redundant since it's equivalent to:

var f = Fn.new { ... }

But it seems worth adding if we're going to make functions and a
functional/procedural style intuitive and natural in Wren. It's a bit
of sugar, but it doesn't hurt much to add and makes the code read a
lot better.
2015-11-27 12:20:15 -08:00
a25ea70e89 Update TODO comment. 2015-11-27 11:26:17 -08:00
6c0cbe36bb Allow lowercase names to be implicit top-level variables too. 2015-11-26 10:17:42 -08:00
f1fd944c6b Convert most ".call()" calls to "()". 2015-11-26 10:01:28 -08:00
3848286cf7 Allow "()" in infix position as sugar for ".call()". 2015-11-26 09:33:58 -08:00
133 changed files with 742 additions and 500 deletions

View File

@ -149,102 +149,108 @@ tightest to loosest, is:
</tr>
<tr>
<td>1</td>
<td><code>()</code> <code>[]</code> <code>.</code></td>
<td>Grouping, <a href="method-calls.html">Subscript, Method call</a></td>
<td><code>.</code></td>
<td><a href="method-calls.html">Method call</a></td>
<td>Left</td>
</tr>
<tr>
<td>2</td>
<td><code>()</code> <code>{}</code> <code>[]</code></td>
<td><a href="functions.html">Call, Block argument</a>, <a href="method-calls.html#subscripts">Subscript</a></td>
<td>Left</td>
</tr>
<tr>
<td>3</td>
<td><code>-</code> <code>!</code> <code>~</code></td>
<td><a href="method-calls.html#operators">Negate, Not, Complement</a></td>
<td>Right</td>
</tr>
<tr>
<td>3</td>
<td>4</td>
<td><code>*</code> <code>/</code> <code>%</code></td>
<td><a href="method-calls.html#operators">Multiply, Divide, Modulo</a></td>
<td>Left</td>
</tr>
<tr>
<td>4</td>
<td>5</td>
<td><code>+</code> <code>-</code></td>
<td><a href="method-calls.html#operators">Add, Subtract</a></td>
<td>Left</td>
</tr>
<tr>
<td>5</td>
<td>6</td>
<td><code>..</code> <code>...</code></td>
<td><a href="method-calls.html#operators">Inclusive range, Exclusive range</a></td>
<td>Left</td>
</tr>
<tr>
<td>6</td>
<td>7</td>
<td><code>&lt;&lt;</code> <code>&gt;&gt;</code></td>
<td><a href="method-calls.html#operators">Left shift, Right shift</a></td>
<td>Left</td>
</tr>
<tr>
<td>7</td>
<td>8</td>
<td><code>&lt;</code> <code>&lt;=</code> <code>&gt;</code> <code>&gt;=</code></td>
<td><a href="method-calls.html#operators">Comparison</a></td>
<td>Left</td>
</tr>
<tr>
<td>8</td>
<td>9</td>
<td><code>==</code></td>
<td><a href="method-calls.html#operators">Equals</a></td>
<td>Left</td>
</tr>
<tr>
<td>8</td>
<td>10</td>
<td><code>!=</code></td>
<td><a href="method-calls.html#operators">Not equal</a></td>
<td>Left</td>
</tr>
<tr>
<td>9</td>
<td>11</td>
<td><code>&amp;</code></td>
<td><a href="method-calls.html#operators">Bitwise and</a></td>
<td>Left</td>
</tr>
<tr>
<td>10</td>
<td>12</td>
<td><code>^</code></td>
<td><a href="method-calls.html#operators">Bitwise xor</a></td>
<td>Left</td>
</tr>
<tr>
<td>11</td>
<td>13</td>
<td><code>|</code></td>
<td><a href="method-calls.html#operators">Bitwise or</a></td>
<td>Left</td>
</tr>
<tr>
<td>12</td>
<td>14</td>
<td><code>is</code></td>
<td><a href="method-calls.html#operators">Type test</a></td>
<td>Left</td>
</tr>
<tr>
<td>13</td>
<td>15</td>
<td><code>&amp;&amp;</code></td>
<td><a href="control-flow.html#logical-operators">Logical and</a></td>
<td>Left</td>
</tr>
<tr>
<td>14</td>
<td>16</td>
<td><code>||</code></td>
<td><a href="control-flow.html#logical-operators">Logical or</a></td>
<td>Left</td>
</tr>
<tr>
<td>15</td>
<td>17</td>
<td><code>?:</code></td>
<td><a href="control-flow.html#the-conditional-operator-">Conditional</a></td>
<td>Right</td>
</tr>
<tr>
<td>16</td>
<td>18</td>
<td><code>=</code></td>
<td><a href="variables.html#assignment">Assignment</a>, <a href="method-calls.html#setters">Setter</a></td>
<td>Right</td>

View File

@ -71,5 +71,7 @@ then it isn't an assignment. Instead, it's calling a [setter method][].
[setter method]: method-calls.html#setters
**TODO: Implicit definition at top level.**
<a class="right" href="functions.html">Functions &rarr;</a>
<a href="control-flow.html">&larr; Control Flow</a>

View File

@ -9,9 +9,9 @@ foreign class File {
return new_(fd)
}
static open(path, fn) {
static open(path, function) {
var file = open(path)
var fiber = Fiber.new { fn.call(file) }
var fiber = Fiber.new { function(file) }
// Poor man's finally. Can we make this more elegant?
var result = fiber.try()

View File

@ -11,9 +11,9 @@ static const char* ioModuleSource =
" return new_(fd)\n"
" }\n"
"\n"
" static open(path, fn) {\n"
" static open(path, function) {\n"
" var file = open(path)\n"
" var fiber = Fiber.new { fn.call(file) }\n"
" var fiber = Fiber.new { function(file) }\n"
"\n"
" // Poor man's finally. Can we make this more elegant?\n"
" var result = fiber.try()\n"

View File

@ -3,7 +3,7 @@ class Scheduler {
if (__scheduled == null) __scheduled = []
__scheduled.add(Fiber.new {
callable.call()
callable()
runNextScheduled_()
})
}

View File

@ -5,7 +5,7 @@ static const char* schedulerModuleSource =
" if (__scheduled == null) __scheduled = []\n"
"\n"
" __scheduled.add(Fiber.new {\n"
" callable.call()\n"
" callable()\n"
" runNextScheduled_()\n"
" })\n"
" }\n"

View File

@ -2,11 +2,11 @@ class Meta {
static eval(source) {
if (!(source is String)) Fiber.abort("Source code must be a string.")
var fn = compile_(source)
var function = compile_(source)
// TODO: Include compile errors.
if (fn == null) Fiber.abort("Could not compile source code.")
if (function == null) Fiber.abort("Could not compile source code.")
Fiber.new(fn).call()
Fiber.new(function)()
}
foreign static compile_(source)

View File

@ -4,11 +4,11 @@ static const char* metaModuleSource =
" static eval(source) {\n"
" if (!(source is String)) Fiber.abort(\"Source code must be a string.\")\n"
"\n"
" var fn = compile_(source)\n"
" var function = compile_(source)\n"
" // TODO: Include compile errors.\n"
" if (fn == null) Fiber.abort(\"Could not compile source code.\")\n"
" if (function == null) Fiber.abort(\"Could not compile source code.\")\n"
"\n"
" Fiber.new(fn).call()\n"
" Fiber.new(function)()\n"
" }\n"
"\n"
" foreign static compile_(source)\n"

View File

@ -82,6 +82,7 @@ typedef enum
TOKEN_CONSTRUCT,
TOKEN_ELSE,
TOKEN_FALSE,
TOKEN_FN,
TOKEN_FOR,
TOKEN_FOREIGN,
TOKEN_IF,
@ -491,6 +492,7 @@ static Keyword keywords[] =
{"construct", 9, TOKEN_CONSTRUCT},
{"else", 4, TOKEN_ELSE},
{"false", 5, TOKEN_FALSE},
{"fn", 2, TOKEN_FN},
{"for", 3, TOKEN_FOR},
{"foreign", 7, TOKEN_FOREIGN},
{"if", 2, TOKEN_IF},
@ -1493,7 +1495,8 @@ typedef enum
PREC_TERM, // + -
PREC_FACTOR, // * / %
PREC_UNARY, // unary - ! ~
PREC_CALL, // . () []
PREC_APPLY, // () {} []
PREC_CALL, // .
PREC_PRIMARY
} Precedence;
@ -1767,6 +1770,39 @@ static void callMethod(Compiler* compiler, int numArgs, const char* name,
emitShortArg(compiler, (Code)(CODE_CALL_0 + numArgs), symbol);
}
// Parses the rest of a block argument after the "{".
static void finishBlockArgument(Compiler* compiler, Signature* signature)
{
// Include the block argument in the arity.
signature->type = SIG_METHOD;
signature->arity++;
Compiler fnCompiler;
initCompiler(&fnCompiler, compiler->parser, compiler, true);
// Make a dummy signature to track the arity.
Signature fnSignature = { "", 0, SIG_METHOD, 0 };
// Parse the parameter list, if any.
if (match(compiler, TOKEN_PIPE))
{
finishParameterList(&fnCompiler, &fnSignature);
consume(compiler, TOKEN_PIPE, "Expect '|' after function parameters.");
}
fnCompiler.numParams = fnSignature.arity;
finishBody(&fnCompiler, false);
// Name the function based on the method its passed to.
char blockName[MAX_METHOD_SIGNATURE + 15];
int blockLength;
signatureToString(signature, blockName, &blockLength);
memmove(blockName + blockLength, " block argument", 16);
endCompiler(&fnCompiler, blockName, blockLength + 15);
}
// Compiles an (optional) argument list for a method call with [methodSignature]
// and then calls it.
static void methodCall(Compiler* compiler, Code instruction,
@ -1789,37 +1825,10 @@ static void methodCall(Compiler* compiler, Code instruction,
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after arguments.");
}
// Parse the block argument, if any.
// Parse an optional block argument.
if (match(compiler, TOKEN_LEFT_BRACE))
{
// Include the block argument in the arity.
called.type = SIG_METHOD;
called.arity++;
Compiler fnCompiler;
initCompiler(&fnCompiler, compiler->parser, compiler, true);
// Make a dummy signature to track the arity.
Signature fnSignature = { "", 0, SIG_METHOD, 0 };
// Parse the parameter list, if any.
if (match(compiler, TOKEN_PIPE))
{
finishParameterList(&fnCompiler, &fnSignature);
consume(compiler, TOKEN_PIPE, "Expect '|' after function parameters.");
}
fnCompiler.numParams = fnSignature.arity;
finishBody(&fnCompiler, false);
// Name the function based on the method its passed to.
char blockName[MAX_METHOD_SIGNATURE + 15];
int blockLength;
signatureToString(&called, blockName, &blockLength);
memmove(blockName + blockLength, " block argument", 16);
endCompiler(&fnCompiler, blockName, blockLength + 15);
finishBlockArgument(compiler, &called);
}
// TODO: Allow Grace-style mixfix methods?
@ -1892,6 +1901,21 @@ static void loadCoreVariable(Compiler* compiler, const char* name)
emitShortArg(compiler, CODE_LOAD_MODULE_VAR, symbol);
}
// Parses an optional parenthesized parameter list. Updates `type` and `arity`
// in [signature] to match what was parsed.
static void parameterList(Compiler* compiler, Signature* signature)
{
consume(compiler, TOKEN_LEFT_PAREN, "Expect '(' before parameters.");
signature->type = SIG_METHOD;
// Allow an empty parameter list.
if (match(compiler, TOKEN_RIGHT_PAREN)) return;
finishParameterList(compiler, signature);
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after parameters.");
}
// A parenthesized expression.
static void grouping(Compiler* compiler, bool allowAssignment)
{
@ -1899,6 +1923,30 @@ static void grouping(Compiler* compiler, bool allowAssignment)
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after expression.");
}
static void call(Compiler* compiler, bool allowAssignment)
{
// An infix parenthesized call is syntax sugar for invoking the "call" method
// on the left-hand side.
Signature signature = { "call", 4, SIG_METHOD, 0 };
// Allow empty an argument list.
if (peek(compiler) != TOKEN_RIGHT_PAREN)
{
// Parse the argument list.
finishArgumentList(compiler, &signature);
}
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after arguments.");
// Parse an optional block argument.
if (match(compiler, TOKEN_LEFT_BRACE))
{
finishBlockArgument(compiler, &signature);
}
callSignature(compiler, CODE_CALL_0, &signature);
}
// A list literal.
static void list(Compiler* compiler, bool allowAssignment)
{
@ -1955,6 +2003,16 @@ static void map(Compiler* compiler, bool allowAssignment)
consume(compiler, TOKEN_RIGHT_BRACE, "Expect '}' after map entries.");
}
// A block argument call on an expression receiver like `fn { block }`.
static void blockArgument(Compiler* compiler, bool allowAssignment)
{
// An infix block argument call is syntax sugar for invoking the "call"
// method on the left-hand side.
Signature signature = { "call", 4, SIG_METHOD, 0 };
finishBlockArgument(compiler, &signature);
callSignature(compiler, CODE_CALL_0, &signature);
}
// Unary operators like `-foo`.
static void unaryOp(Compiler* compiler, bool allowAssignment)
{
@ -1975,6 +2033,25 @@ static void boolean(Compiler* compiler, bool allowAssignment)
compiler->parser->previous.type == TOKEN_FALSE ? CODE_FALSE : CODE_TRUE);
}
// A function literal.
static void function(Compiler* compiler, bool allowAssignment)
{
Compiler fnCompiler;
initCompiler(&fnCompiler, compiler->parser, compiler, true);
// Make a dummy signature to track the arity.
Signature signature = { "", 0, SIG_METHOD, 0 };
parameterList(&fnCompiler, &signature);
fnCompiler.numParams = signature.arity;
consume(compiler, TOKEN_LEFT_BRACE, "Expect '{' to begin function body.");
finishBody(&fnCompiler, false);
endCompiler(&fnCompiler, "fn", 2);
}
// Walks the compiler chain to find the compiler for the nearest class
// enclosing this one. Returns NULL if not currently inside a class definition.
static Compiler* getEnclosingClassCompiler(Compiler* compiler)
@ -2155,13 +2232,13 @@ static void name(Compiler* compiler, bool allowAssignment)
return;
}
// TODO: The fact that we return above here if the variable is known and parse
// an optional argument list below if not means that the grammar is not
// context-free. A line of code in a method like "someName(foo)" is a parse
// error if "someName" is a defined variable in the surrounding scope and not
// if it isn't. Fix this. One option is to have "someName(foo)" always
// resolve to a self-call if there is an argument list, but that makes
// getters a little confusing.
// If a parameter list follows a resolved name, then it will be treated as an
// infix call operator on it. So "foo(arg)" gets parsed to "foo.call(arg)" if
// foo resolves to a variable and "this.foo(arg)" if we're in a class.
//
// This makes the *semantics* slightly context-sensitive but keeps the syntax
// context free: "foo(arg)" is always *meaningful* regardless of how "foo" is
// resolved, it just means one of two things.
// If we're inside a method and the name is lowercase, treat it as a method
// on this.
@ -2177,14 +2254,8 @@ static void name(Compiler* compiler, bool allowAssignment)
token->start, token->length);
if (module == -1)
{
if (isLocalName(token->start))
{
error(compiler, "Undefined variable.");
return;
}
// If it's a nonlocal name, implicitly define a module-level variable in
// the hopes that we get a real definition later.
// If it's not a self-send or a closure, implicitly define a module-level
// variable in the hopes that we get a real definition later.
module = wrenDeclareVariable(compiler->parser->vm, compiler->parser->module,
token->start, token->length);
@ -2312,7 +2383,7 @@ static void subscript(Compiler* compiler, bool allowAssignment)
callSignature(compiler, CODE_CALL_0, &signature);
}
static void call(Compiler* compiler, bool allowAssignment)
static void dot(Compiler* compiler, bool allowAssignment)
{
ignoreNewlines(compiler);
consume(compiler, TOKEN_NAME, "Expect method name after '.'.");
@ -2464,22 +2535,6 @@ void subscriptSignature(Compiler* compiler, Signature* signature)
maybeSetter(compiler, signature);
}
// Parses an optional parenthesized parameter list. Updates `type` and `arity`
// in [signature] to match what was parsed.
static void parameterList(Compiler* compiler, Signature* signature)
{
// The parameter list is optional.
if (!match(compiler, TOKEN_LEFT_PAREN)) return;
signature->type = SIG_METHOD;
// Allow an empty parameter list.
if (match(compiler, TOKEN_RIGHT_PAREN)) return;
finishParameterList(compiler, signature);
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after parameters.");
}
// Compiles a method signature for a named method or setter.
void namedSignature(Compiler* compiler, Signature* signature)
{
@ -2489,7 +2544,10 @@ void namedSignature(Compiler* compiler, Signature* signature)
if (maybeSetter(compiler, signature)) return;
// Regular named method with an optional parameter list.
parameterList(compiler, signature);
if (peek(compiler) == TOKEN_LEFT_PAREN)
{
parameterList(compiler, signature);
}
}
// Compiles a method signature for a constructor.
@ -2531,14 +2589,14 @@ void constructorSignature(Compiler* compiler, Signature* signature)
GrammarRule rules[] =
{
/* TOKEN_LEFT_PAREN */ PREFIX(grouping),
/* TOKEN_LEFT_PAREN */ { grouping, call, NULL, PREC_APPLY, NULL },
/* TOKEN_RIGHT_PAREN */ UNUSED,
/* TOKEN_LEFT_BRACKET */ { list, subscript, subscriptSignature, PREC_CALL, NULL },
/* TOKEN_LEFT_BRACKET */ { list, subscript, subscriptSignature, PREC_APPLY, NULL },
/* TOKEN_RIGHT_BRACKET */ UNUSED,
/* TOKEN_LEFT_BRACE */ PREFIX(map),
/* TOKEN_LEFT_BRACE */ { map, blockArgument, NULL, PREC_APPLY, NULL },
/* TOKEN_RIGHT_BRACE */ UNUSED,
/* TOKEN_COLON */ UNUSED,
/* TOKEN_DOT */ INFIX(PREC_CALL, call),
/* TOKEN_DOT */ INFIX(PREC_CALL, dot),
/* TOKEN_DOTDOT */ INFIX_OPERATOR(PREC_RANGE, ".."),
/* TOKEN_DOTDOTDOT */ INFIX_OPERATOR(PREC_RANGE, "..."),
/* TOKEN_COMMA */ UNUSED,
@ -2569,6 +2627,7 @@ GrammarRule rules[] =
/* TOKEN_CONSTRUCT */ { NULL, NULL, constructorSignature, PREC_NONE, NULL },
/* TOKEN_ELSE */ UNUSED,
/* TOKEN_FALSE */ PREFIX(boolean),
/* TOKEN_FN */ PREFIX(function),
/* TOKEN_FOR */ UNUSED,
/* TOKEN_FOREIGN */ UNUSED,
/* TOKEN_IF */ UNUSED,
@ -2600,6 +2659,19 @@ static GrammarRule* getRule(TokenType type)
return &rules[type];
}
// Completes parsing an expression after the prefix part has been parsed.
// Parses any trailing infix expressions.
static void parseInfix(Compiler* compiler, bool allowAssignment,
Precedence precedence)
{
while (precedence <= rules[compiler->parser->current.type].precedence)
{
nextToken(compiler->parser);
GrammarFn infix = rules[compiler->parser->previous.type].infix;
infix(compiler, allowAssignment);
}
}
// The main entrypoint for the top-down operator precedence parser.
void parsePrecedence(Compiler* compiler, bool allowAssignment,
Precedence precedence)
@ -2614,13 +2686,7 @@ void parsePrecedence(Compiler* compiler, bool allowAssignment,
}
prefix(compiler, allowAssignment);
while (precedence <= rules[compiler->parser->current.type].precedence)
{
nextToken(compiler->parser);
GrammarFn infix = rules[compiler->parser->previous.type].infix;
infix(compiler, allowAssignment);
}
parseInfix(compiler, allowAssignment, precedence);
}
// Parses an expression. Unlike statements, expressions leave a resulting value
@ -3234,6 +3300,45 @@ static void classDefinition(Compiler* compiler, bool isForeign)
popScope(compiler);
}
static void functionDefinition(Compiler* compiler)
{
// If we don't have a name after "fn", then this isn't a function declaration,
// it's an anonymous function expression appearing in an expressions
// statement. In that case, we need to parse it like an expression to allow
// other code to follow it, like:
//
// fn (param) { param }("call with argument")
if (peek(compiler) != TOKEN_NAME)
{
function(compiler, true);
parseInfix(compiler, true, PREC_LOWEST);
emit(compiler, CODE_POP);
return;
}
// Create a variable to store the function in.
int slot = declareNamedVariable(compiler);
Token name = compiler->parser->previous;
Compiler fnCompiler;
initCompiler(&fnCompiler, compiler->parser, compiler, true);
// Make a dummy signature to track the arity.
Signature signature = { "", 0, SIG_METHOD, 0 };
parameterList(&fnCompiler, &signature);
fnCompiler.numParams = signature.arity;
consume(compiler, TOKEN_LEFT_BRACE, "Expect '{' to begin function body.");
finishBody(&fnCompiler, false);
// Use the function's declared name.
endCompiler(&fnCompiler, name.start, name.length);
defineVariable(compiler, slot);
}
// Compiles an "import" statement.
static void import(Compiler* compiler)
{
@ -3302,6 +3407,10 @@ void definition(Compiler* compiler)
{
classDefinition(compiler, false);
}
else if (match(compiler, TOKEN_FN))
{
functionDefinition(compiler);
}
else if (match(compiler, TOKEN_FOREIGN))
{
consume(compiler, TOKEN_CLASS, "Expect 'class' after 'foreign'.");

View File

@ -235,14 +235,6 @@ DEF_PRIMITIVE(fiber_yield1)
return false;
}
DEF_PRIMITIVE(fn_new)
{
if (!validateFn(vm, args[1], "Argument")) return false;
// The block argument is already a function, so just return it.
RETURN_VAL(args[1]);
}
DEF_PRIMITIVE(fn_arity)
{
RETURN_NUM(AS_FN(args[0])->arity);
@ -1161,7 +1153,6 @@ void wrenInitializeCore(WrenVM* vm)
PRIMITIVE(vm->fiberClass, "try()", fiber_try);
vm->fnClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Fn"));
PRIMITIVE(vm->fnClass->obj.classObj, "new(_)", fn_new);
PRIMITIVE(vm->fnClass, "arity", fn_arity);
fnCall(vm, "call()");

View File

@ -8,7 +8,7 @@ class Sequence {
all(f) {
var result = true
for (element in this) {
result = f.call(element)
result = f(element)
if (!result) return result
}
return result
@ -17,7 +17,7 @@ class Sequence {
any(f) {
var result = false
for (element in this) {
result = f.call(element)
result = f(element)
if (result) return result
}
return result
@ -41,14 +41,14 @@ class Sequence {
count(f) {
var result = 0
for (element in this) {
if (f.call(element)) result = result + 1
if (f(element)) result = result + 1
}
return result
}
each(f) {
for (element in this) {
f.call(element)
f(element)
}
}
@ -60,7 +60,7 @@ class Sequence {
reduce(acc, f) {
for (element in this) {
acc = f.call(acc, element)
acc = f(acc, element)
}
return acc
}
@ -72,7 +72,7 @@ class Sequence {
// Seed with the first element.
var result = iteratorValue(iter)
while (iter = iterate(iter)) {
result = f.call(result, iteratorValue(iter))
result = f(result, iteratorValue(iter))
}
return result
@ -103,24 +103,24 @@ class Sequence {
}
class MapSequence is Sequence {
construct new(sequence, fn) {
construct new(sequence, function) {
_sequence = sequence
_fn = fn
_function = function
}
iterate(iterator) { _sequence.iterate(iterator) }
iteratorValue(iterator) { _fn.call(_sequence.iteratorValue(iterator)) }
iteratorValue(iterator) { _function.call(_sequence.iteratorValue(iterator)) }
}
class WhereSequence is Sequence {
construct new(sequence, fn) {
construct new(sequence, function) {
_sequence = sequence
_fn = fn
_function = function
}
iterate(iterator) {
while (iterator = _sequence.iterate(iterator)) {
if (_fn.call(_sequence.iteratorValue(iterator))) break
if (_function.call(_sequence.iteratorValue(iterator))) break
}
return iterator
}

View File

@ -10,7 +10,7 @@ static const char* coreModuleSource =
" all(f) {\n"
" var result = true\n"
" for (element in this) {\n"
" result = f.call(element)\n"
" result = f(element)\n"
" if (!result) return result\n"
" }\n"
" return result\n"
@ -19,7 +19,7 @@ static const char* coreModuleSource =
" any(f) {\n"
" var result = false\n"
" for (element in this) {\n"
" result = f.call(element)\n"
" result = f(element)\n"
" if (result) return result\n"
" }\n"
" return result\n"
@ -43,14 +43,14 @@ static const char* coreModuleSource =
" count(f) {\n"
" var result = 0\n"
" for (element in this) {\n"
" if (f.call(element)) result = result + 1\n"
" if (f(element)) result = result + 1\n"
" }\n"
" return result\n"
" }\n"
"\n"
" each(f) {\n"
" for (element in this) {\n"
" f.call(element)\n"
" f(element)\n"
" }\n"
" }\n"
"\n"
@ -62,7 +62,7 @@ static const char* coreModuleSource =
"\n"
" reduce(acc, f) {\n"
" for (element in this) {\n"
" acc = f.call(acc, element)\n"
" acc = f(acc, element)\n"
" }\n"
" return acc\n"
" }\n"
@ -74,7 +74,7 @@ static const char* coreModuleSource =
" // Seed with the first element.\n"
" var result = iteratorValue(iter)\n"
" while (iter = iterate(iter)) {\n"
" result = f.call(result, iteratorValue(iter))\n"
" result = f(result, iteratorValue(iter))\n"
" }\n"
"\n"
" return result\n"
@ -105,24 +105,24 @@ static const char* coreModuleSource =
"}\n"
"\n"
"class MapSequence is Sequence {\n"
" construct new(sequence, fn) {\n"
" construct new(sequence, function) {\n"
" _sequence = sequence\n"
" _fn = fn\n"
" _function = function\n"
" }\n"
"\n"
" iterate(iterator) { _sequence.iterate(iterator) }\n"
" iteratorValue(iterator) { _fn.call(_sequence.iteratorValue(iterator)) }\n"
" iteratorValue(iterator) { _function.call(_sequence.iteratorValue(iterator)) }\n"
"}\n"
"\n"
"class WhereSequence is Sequence {\n"
" construct new(sequence, fn) {\n"
" construct new(sequence, function) {\n"
" _sequence = sequence\n"
" _fn = fn\n"
" _function = function\n"
" }\n"
"\n"
" iterate(iterator) {\n"
" while (iterator = _sequence.iterate(iterator)) {\n"
" if (_fn.call(_sequence.iteratorValue(iterator))) break\n"
" if (_function.call(_sequence.iteratorValue(iterator))) break\n"
" }\n"
" return iterator\n"
" }\n"

View File

@ -68,8 +68,6 @@ var ORDERED = [
WEAKEST, WEAK_DEFAULT, NORMAL, STRONG_DEFAULT, PREFERRED, STRONG_REFERRED
]
var ThePlanner
class Constraint {
construct new(strength) {
_strength = strength
@ -410,7 +408,7 @@ class Variable {
// Removes all traces of c from this variable.
removeConstraint(constraint) {
_constraints = _constraints.where { |c| c != constraint }
_constraints = _constraints.where {|c| c != constraint }
if (_determinedBy == constraint) _determinedBy = null
}
}
@ -611,7 +609,7 @@ var total = 0
// constraint so it cannot be accomodated. The cost in this case is,
// of course, very low. Typical situations lie somewhere between these
// two extremes.
var chainTest = Fn.new {|n|
fn chainTest(n) {
ThePlanner = Planner.new()
var prev = null
var first = null
@ -636,7 +634,7 @@ var chainTest = Fn.new {|n|
}
}
var change = Fn.new {|v, newValue|
fn change(v, newValue) {
var edit = EditConstraint.new(v, PREFERRED)
var plan = ThePlanner.extractPlanFromConstraints([edit])
for (i in 0...10) {
@ -647,11 +645,13 @@ var change = Fn.new {|v, newValue|
edit.destroyConstraint
}
var ThePlanner
// This test constructs a two sets of variables related to each
// other by a simple linear transformation (scale and offset). The
// time is measured to change a variable on either side of the
// mapping and to change the scale and offset factors.
var projectionTest = Fn.new {|n|
fn projectionTest(n) {
ThePlanner = Planner.new()
var scale = Variable.new("scale", 10)
var offset = Variable.new("offset", 1000)
@ -667,22 +667,22 @@ var projectionTest = Fn.new {|n|
ScaleConstraint.new(src, scale, offset, dst, REQUIRED)
}
change.call(src, 17)
change(src, 17)
total = total + dst.value
if (dst.value != 1170) System.print("Projection 1 failed")
change.call(dst, 1050)
change(dst, 1050)
total = total + src.value
if (src.value != 5) System.print("Projection 2 failed")
change.call(scale, 5)
change(scale, 5)
for (i in 0...n - 1) {
total = total + dests[i].value
if (dests[i].value != i * 5 + 1000) System.print("Projection 3 failed")
}
change.call(offset, 2000)
change(offset, 2000)
for (i in 0...n - 1) {
total = total + dests[i].value
if (dests[i].value != i * 5 + 2000) System.print("Projection 4 failed")
@ -691,8 +691,8 @@ var projectionTest = Fn.new {|n|
var start = System.clock
for (i in 0...40) {
chainTest.call(100)
projectionTest.call(100)
chainTest(100)
projectionTest(100)
}
System.print(total)

View File

@ -7,10 +7,10 @@ var start = System.clock
for (i in 0...100000) {
fibers.add(Fiber.new {
sum = sum + i
if (i < 99999) fibers[i + 1].call()
if (i < 99999) fibers[i + 1]()
})
}
fibers[0].call()
fibers[0]()
System.print(sum)
System.print("elapsed: %(System.clock - start)")

View File

@ -3,5 +3,5 @@ var fiber = Fiber.new {
}
System.print("before") // expect: before
fiber.call() // expect: fiber
fiber() // expect: fiber
System.print("after") // expect: after

View File

@ -1,7 +1,7 @@
var fiber
fiber = Fiber.new {
fiber.call() // expect runtime error: Fiber has already been called.
fiber() // expect runtime error: Fiber has already been called.
}
fiber.call()
fiber()

View File

@ -2,5 +2,5 @@ var fiber = Fiber.new {
System.print("call")
}
fiber.call() // expect: call
fiber.call() // expect runtime error: Cannot call a finished fiber.
fiber() // expect: call
fiber() // expect runtime error: Cannot call a finished fiber.

View File

@ -4,4 +4,4 @@ var fiber = Fiber.new {
}
fiber.try()
fiber.call() // expect runtime error: Cannot call an aborted fiber.
fiber() // expect runtime error: Cannot call an aborted fiber.

View File

@ -2,11 +2,11 @@ var a
var b
a = Fiber.new {
b.call() // expect runtime error: Fiber has already been called.
b() // expect runtime error: Fiber has already been called.
}
b = Fiber.new {
a.call()
a()
}
b.call()
b()

View File

@ -2,4 +2,4 @@ var fiber = Fiber.new {
System.print("fiber") // expect: fiber
}
System.print(fiber.call()) // expect: null
System.print(fiber()) // expect: null

View File

@ -3,4 +3,4 @@ var fiber = Fiber.new {
return "result" // expect: fiber
}
System.print(fiber.call()) // expect: result
System.print(fiber()) // expect: result

View File

@ -6,7 +6,7 @@ var fiber = Fiber.new {
System.print("called")
}
fiber.transfer() // expect: transferred
System.print("main") // expect: main
fiber.call() // expect: null
// expect: called
fiber.transfer() // expect: transferred
System.print("main") // expect: main
fiber() // expect: null
// expect: called

View File

@ -5,5 +5,5 @@ var fiber = Fiber.new {
// The first value passed to the fiber is ignored, since there's no yield call
// to return it.
System.print("before") // expect: before
fiber.call("ignored") // expect: fiber
fiber("ignored") // expect: fiber
System.print("after") // expect: after

View File

@ -1,7 +1,7 @@
var fiber
fiber = Fiber.new {
fiber.call(2) // expect runtime error: Fiber has already been called.
fiber(2) // expect runtime error: Fiber has already been called.
}
fiber.call(1)
fiber(1)

View File

@ -2,5 +2,5 @@ var fiber = Fiber.new {
System.print("call")
}
fiber.call(1) // expect: call
fiber.call(2) // expect runtime error: Cannot call a finished fiber.
fiber(1) // expect: call
fiber(2) // expect runtime error: Cannot call a finished fiber.

View File

@ -4,4 +4,4 @@ var fiber = Fiber.new {
}
fiber.try()
fiber.call("value") // expect runtime error: Cannot call an aborted fiber.
fiber("value") // expect runtime error: Cannot call an aborted fiber.

View File

@ -1,9 +1,9 @@
var A = Fiber.new {
B.call(3) // expect runtime error: Fiber has already been called.
var a = Fiber.new {
b(3) // expect runtime error: Fiber has already been called.
}
var B = Fiber.new {
A.call(2)
var b = Fiber.new {
a(2)
}
B.call(1)
b(1)

View File

@ -6,7 +6,7 @@ var fiber = Fiber.new {
System.print("called")
}
fiber.transfer() // expect: transferred
System.print("main") // expect: main
fiber.call("value") // expect: value
// expect: called
fiber.transfer() // expect: transferred
System.print("main") // expect: main
fiber("value") // expect: value
// expect: called

View File

@ -5,7 +5,7 @@ var fiber = Fiber.new {
}
System.print(fiber.isDone) // expect: false
fiber.call() // expect: 1
fiber() // expect: 1
System.print(fiber.isDone) // expect: false
fiber.call() // expect: 2
fiber() // expect: 2
System.print(fiber.isDone) // expect: true

View File

@ -4,12 +4,12 @@ var b = Fiber.new {
var a = Fiber.new {
System.print("begin fiber a")
b.call()
b()
System.print("end fiber a")
}
System.print("begin main")
a.call()
a()
System.print("end main")
// expect: begin main

View File

@ -1,9 +1,9 @@
var F = Fiber.new {
var f = Fiber.new {
System.print(1) // expect: 1
System.print(F.transfer()) // expect: null
System.print(f.transfer()) // expect: null
System.print(2) // expect: 2
}
F.call()
// F remembers its original caller so transfers back to main.
f()
// f remembers its original caller so transfers back to main.
System.print(3) // expect: 3

View File

@ -1,15 +1,15 @@
var A = Fiber.new {
System.print("transferred to A")
B.transferError("error!")
var a = Fiber.new {
System.print("transferred to a")
b.transferError("error!")
}
var B = Fiber.new {
System.print("started B")
A.transfer()
var b = Fiber.new {
System.print("started b")
a.transfer()
System.print("should not get here")
}
B.try()
// expect: started B
// expect: transferred to A
System.print(B.error) // expect: error!
b.try()
// expect: started b
// expect: transferred to a
System.print(b.error) // expect: error!

View File

@ -1,10 +1,10 @@
var A = Fiber.new {
B.transferError(123)
var a = Fiber.new {
b.transferError(123)
}
var B = Fiber.new {
A.transfer()
var b = Fiber.new {
a.transfer()
}
B.try()
System.print(B.error) // expect: 123
b.try()
System.print(b.error) // expect: 123

View File

@ -1,18 +1,18 @@
var A = Fiber.new {
var a = Fiber.new {
System.print(2)
B.transfer()
b.transfer()
System.print("nope")
}
var B = Fiber.new {
var b = Fiber.new {
System.print(1)
A.transfer()
a.transfer()
System.print(3)
}
B.call()
b()
// expect: 1
// expect: 2
// expect: 3
// B remembers its original caller so returns to main.
// b remembers its original caller so returns to main.
System.print(4) // expect: 4

View File

@ -13,11 +13,11 @@ var fiber = Fiber.new {
fiber.transfer() // expect: fiber 1
System.print("main 1") // expect: main 1
fiber.call("call 1") // expect: call 1
fiber("call 1") // expect: call 1
System.print("main 2") // expect: main 2
// Transfer back into the fiber so it has a NULL caller.
fiber.transfer()
fiber.call() // expect: null
fiber() // expect: null
System.print("main 3") // expect: main 3

View File

@ -2,5 +2,5 @@ var a = Fiber.new {
System.print("run")
}
a.call() // expect: run
a() // expect: run
a.transfer() // expect runtime error: Cannot transfer to a finished fiber.

View File

@ -4,6 +4,6 @@ var fiber = Fiber.new {
System.print("transferred")
}
fiber.call() // expect: called
fiber() // expect: called
fiber.transfer() // expect: null
// expect: transferred

View File

@ -1,8 +1,8 @@
var F = Fiber.new {
var f = Fiber.new {
System.print(1) // expect: 1
System.print(F.transfer("value")) // expect: value
System.print(f.transfer("value")) // expect: value
System.print(2) // expect: 2
}
F.call()
f()
System.print(3) // expect: 3

View File

@ -1,16 +1,16 @@
var A = Fiber.new {
var a = Fiber.new {
System.print(2)
B.transfer("ignored")
b.transfer("ignored")
System.print("nope")
}
var B = Fiber.new {
var b = Fiber.new {
System.print(1)
A.transfer("ignored")
a.transfer("ignored")
System.print(3)
}
B.call()
b()
// expect: 1
// expect: 2
// expect: 3

View File

@ -2,5 +2,5 @@ var a = Fiber.new {
System.print("run")
}
a.call() // expect: run
a.transfer("blah") // expect runtime error: Cannot transfer to a finished fiber.
a() // expect: run
a.transfer("blah") // expect runtime error: Cannot transfer to a finished fiber.

View File

@ -4,6 +4,6 @@ var fiber = Fiber.new {
System.print("transferred")
}
fiber.call() // expect: called
fiber() // expect: called
fiber.transfer("value") // expect: value
// expect: transferred

View File

@ -4,4 +4,4 @@ fiber = Fiber.new {
fiber.try() // expect runtime error: Fiber has already been called.
}
fiber.call()
fiber()

View File

@ -6,7 +6,7 @@ a = Fiber.new {
}
b = Fiber.new {
a.call()
a()
}
b.call()
b()

View File

@ -6,9 +6,9 @@ var fiber = Fiber.new {
System.print("fiber 3")
}
fiber.call() // expect: fiber 1
fiber() // expect: fiber 1
System.print("main 1") // expect: main 1
fiber.call() // expect: fiber 2
fiber() // expect: fiber 2
System.print("main 2") // expect: main 2
fiber.call() // expect: fiber 3
fiber() // expect: fiber 3
System.print("main 3") // expect: main 3

View File

@ -4,9 +4,9 @@ var fiber = Fiber.new {
System.print(Fiber.yield())
}
fiber.call() // expect: fiber 1
System.print("main 1") // expect: main 1
fiber.call("call 1") // expect: call 1
System.print("main 2") // expect: main 2
fiber.call() // expect: null
System.print("main 3") // expect: main 3
fiber() // expect: fiber 1
System.print("main 1") // expect: main 1
fiber("call 1") // expect: call 1
System.print("main 2") // expect: main 2
fiber() // expect: null
System.print("main 3") // expect: main 3

View File

@ -9,11 +9,11 @@ var b = Fiber.new {
System.print("b")
System.print(Fiber.yield())
a.call()
a()
a.transfer("value")
}
b.call() // expect: b
b() // expect: b
b.transfer()
// expect: null
// expect: a

View File

@ -6,9 +6,9 @@ var fiber = Fiber.new {
System.print("fiber 3")
}
System.print(fiber.call()) // expect: fiber 1
System.print(fiber()) // expect: fiber 1
// expect: yield 1
System.print(fiber.call()) // expect: fiber 2
System.print(fiber()) // expect: fiber 2
// expect: yield 2
System.print(fiber.call()) // expect: fiber 3
System.print(fiber()) // expect: fiber 3
// expect: null

View File

@ -1,5 +1,5 @@
System.print(Fn.new {}.arity) // expect: 0
System.print(Fn.new {|a| a}.arity) // expect: 1
System.print(Fn.new {|a, b| a}.arity) // expect: 2
System.print(Fn.new {|a, b, c| a}.arity) // expect: 3
System.print(Fn.new {|a, b, c, d| a}.arity) // expect: 4
System.print(fn () {}.arity) // expect: 0
System.print(fn (a) {a}.arity) // expect: 1
System.print(fn (a, b) {a}.arity) // expect: 2
System.print(fn (a, b, c) {a}.arity) // expect: 3
System.print(fn (a, b, c, d) {a}.arity) // expect: 4

View File

@ -1,16 +1,16 @@
var f0 = Fn.new { System.print("zero") }
var f1 = Fn.new {|a| System.print("one %(a)") }
var f2 = Fn.new {|a, b| System.print("two %(a) %(b)") }
var f3 = Fn.new {|a, b, c| System.print("three %(a) %(b) %(c)") }
fn f0() { System.print("zero") }
fn f1(a) { System.print("one %(a)") }
fn f2(a, b) { System.print("two %(a) %(b)") }
fn f3(a, b, c) { System.print("three %(a) %(b) %(c)") }
f0.call("a") // expect: zero
f0.call("a", "b") // expect: zero
f0("a") // expect: zero
f0("a", "b") // expect: zero
f1.call("a", "b") // expect: one a
f1.call("a", "b", "c") // expect: one a
f1("a", "b") // expect: one a
f1("a", "b", "c") // expect: one a
f2.call("a", "b", "c") // expect: two a b
f2.call("a", "b", "c", "d") // expect: two a b
f2("a", "b", "c") // expect: two a b
f2("a", "b", "c", "d") // expect: two a b
f3.call("a", "b", "c", "d") // expect: three a b c
f3.call("a", "b", "c", "d", "e") // expect: three a b c
f3("a", "b", "c", "d") // expect: three a b c
f3("a", "b", "c", "d", "e") // expect: three a b c

View File

@ -1,2 +1,2 @@
var f2 = Fn.new {|a, b| System.print(a + b) }
f2.call("a") // expect runtime error: Function expects more arguments.
fn f2(a, b) { System.print(a + b) }
f2("a") // expect runtime error: Function expects more arguments.

View File

@ -1,16 +1,16 @@
// Not structurally equal.
System.print(Fn.new { 123 } == Fn.new { 123 }) // expect: false
System.print(Fn.new { 123 } != Fn.new { 123 }) // expect: true
System.print(fn () { 123 } == fn () { 123 }) // expect: false
System.print(fn () { 123 } != fn () { 123 }) // expect: true
// Not equal to other types.
System.print(Fn.new { 123 } == 1) // expect: false
System.print(Fn.new { 123 } == false) // expect: false
System.print(Fn.new { 123 } == "fn 123") // expect: false
System.print(Fn.new { 123 } != 1) // expect: true
System.print(Fn.new { 123 } != false) // expect: true
System.print(Fn.new { 123 } != "fn 123") // expect: true
System.print(fn () { 123 } == 1) // expect: false
System.print(fn () { 123 } == false) // expect: false
System.print(fn () { 123 } == "fn 123") // expect: false
System.print(fn () { 123 } != 1) // expect: true
System.print(fn () { 123 } != false) // expect: true
System.print(fn () { 123 } != "fn 123") // expect: true
// Equal by identity.
var f = Fn.new { 123 }
fn f() { 123 }
System.print(f == f) // expect: true
System.print(f != f) // expect: false

View File

@ -1 +0,0 @@
Fn.new(3) // expect runtime error: Argument must be a function.

View File

@ -1 +1 @@
System.print(Fn.new {}) // expect: <fn>
System.print(fn () {}) // expect: <fn>

View File

@ -1,4 +1,4 @@
System.print(Fn.new { 0 } is Fn) // expect: true
System.print(Fn.new { 0 } is Object) // expect: true
System.print(Fn.new { 0 } is String) // expect: false
System.print(Fn.new { 0 }.type == Fn) // expect: true
System.print(fn () { 0 } is Fn) // expect: true
System.print(fn () { 0 } is Object) // expect: true
System.print(fn () { 0 } is String) // expect: false
System.print(fn () { 0 }.type == Fn) // expect: true

View File

@ -1,14 +1,8 @@
var a = [1, 4, 2, 1, 5]
var b = ["W", "o", "r", "l", "d"]
var max = Fn.new {|a, b| a > b ? a : b }
var sum = Fn.new {|a, b| a + b }
System.print(a.reduce(max)) // expect: 5
System.print(a.reduce(10, max)) // expect: 10
System.print(a.reduce {|a, b| a > b ? a : b }) // expect: 5
System.print(a.reduce(10) {|a, b| a > b ? a : b }) // expect: 10
System.print(a.reduce(sum)) // expect: 13
System.print(a.reduce(-1, sum)) // expect: 12
// sum also concatenates strings
System.print(b.reduce("Hello ", sum)) // expect: Hello World
System.print(b.reduce(sum)) // expect: World
System.print(a.reduce {|a, b| a + b }) // expect: 13
System.print(a.reduce(-1) {|a, b| a + b }) // expect: 12

View File

@ -1,6 +1,6 @@
System.print(Num is Class) // expect: true
System.print(true is Bool) // expect: true
System.print(Fn.new { 1 } is Fn) // expect: true
System.print(fn () { 1 } is Fn) // expect: true
System.print(123 is Num) // expect: true
System.print(null is Null) // expect: true
System.print("s" is String) // expect: true
@ -8,7 +8,7 @@ System.print("s" is String) // expect: true
System.print(Num is Bool) // expect: false
System.print(null is Class) // expect: false
System.print(true is Fn) // expect: false
System.print(Fn.new { 1 } is Num) // expect: false
System.print(fn () { 1 } is Num) // expect: false
System.print("s" is Null) // expect: false
System.print(123 is String) // expect: false
@ -16,7 +16,7 @@ System.print(123 is String) // expect: false
System.print(Num is Object) // expect: true
System.print(null is Object) // expect: true
System.print(true is Object) // expect: true
System.print(Fn.new { 1 } is Object) // expect: true
System.print(fn () { 1 } is Object) // expect: true
System.print("s" is Object) // expect: true
System.print(123 is Object) // expect: true
@ -24,7 +24,7 @@ System.print(123 is Object) // expect: true
System.print(Num is Class) // expect: true
System.print(null is Class) // expect: false
System.print(true is Class) // expect: false
System.print(Fn.new { 1 } is Class) // expect: false
System.print(fn () { 1 } is Class) // expect: false
System.print("s" is Class) // expect: false
System.print(123 is Class) // expect: false

View File

@ -1,3 +0,0 @@
var a = "a"
var b = "b"
b is a = "value" // expect error

View File

@ -1,4 +1,4 @@
Fn.new {
fn () {
var a = "before"
System.print(a) // expect: before
@ -7,4 +7,4 @@ Fn.new {
System.print(a = "arg") // expect: arg
System.print(a) // expect: arg
}.call()
}()

View File

@ -1 +1,4 @@
unknown = "what" // expect error
unknown = "what"
// Error on last line since the variable is assumed to be implicitly defined
// until we hit the end and don't find it.
// expect error

View File

@ -0,0 +1,3 @@
fn block(arg) { arg }
var f = block {}
System.print(f()) // expect: null

View File

@ -0,0 +1,5 @@
fn block(arg) { arg }
var f = block {
// Hi.
}
System.print(f()) // expect: null

View File

@ -0,0 +1,3 @@
fn block(arg) { arg }
block { System.print("ok") // expect error
}()

View File

@ -0,0 +1,3 @@
fn block(arg) { arg }
block {
System.print("ok") } // expect error

View File

@ -0,0 +1,2 @@
fn block(arg) { arg }
block {|| null } // expect error

View File

@ -0,0 +1,52 @@
fn block(arg) { arg }
var f0 = block { 0 }
System.print(f0.call()) // expect: 0
var f1 = block {|a| a }
System.print(f1.call(1)) // expect: 1
var f2 = block {|a, b| a + b }
System.print(f2.call(1, 2)) // expect: 3
var f3 = block {|a, b, c| a + b + c }
System.print(f3.call(1, 2, 3)) // expect: 6
var f4 = block {|a, b, c, d| a + b + c + d }
System.print(f4.call(1, 2, 3, 4)) // expect: 10
var f5 = block {|a, b, c, d, e| a + b + c + d + e }
System.print(f5.call(1, 2, 3, 4, 5)) // expect: 15
var f6 = block {|a, b, c, d, e, f| a + b + c + d + e + f }
System.print(f6.call(1, 2, 3, 4, 5, 6)) // expect: 21
var f7 = block {|a, b, c, d, e, f, g| a + b + c + d + e + f + g }
System.print(f7.call(1, 2, 3, 4, 5, 6, 7)) // expect: 28
var f8 = block {|a, b, c, d, e, f, g, h| a + b + c + d + e + f + g + h }
System.print(f8.call(1, 2, 3, 4, 5, 6, 7, 8)) // expect: 36
var f9 = block {|a, b, c, d, e, f, g, h, i| a + b + c + d + e + f + g + h + i }
System.print(f9.call(1, 2, 3, 4, 5, 6, 7, 8, 9)) // expect: 45
var f10 = block {|a, b, c, d, e, f, g, h, i, j| a + b + c + d + e + f + g + h + i + j }
System.print(f10.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) // expect: 55
var f11 = block {|a, b, c, d, e, f, g, h, i, j, k| a + b + c + d + e + f + g + h + i + j + k }
System.print(f11.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)) // expect: 66
var f12 = block {|a, b, c, d, e, f, g, h, i, j, k, l| a + b + c + d + e + f + g + h + i + j + k + l }
System.print(f12.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)) // expect: 78
var f13 = block {|a, b, c, d, e, f, g, h, i, j, k, l, m| a + b + c + d + e + f + g + h + i + j + k + l + m }
System.print(f13.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)) // expect: 91
var f14 = block {|a, b, c, d, e, f, g, h, i, j, k, l, m, n| a + b + c + d + e + f + g + h + i + j + k + l + m + n }
System.print(f14.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)) // expect: 105
var f15 = block {|a, b, c, d, e, f, g, h, i, j, k, l, m, n, o| a + b + c + d + e + f + g + h + i + j + k + l + m + n + o }
System.print(f15.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)) // expect: 120
var f16 = block {|a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p| a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p }
System.print(f16.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)) // expect: 136

View File

@ -1,19 +1,21 @@
fn block(arg) { arg }
// Single expression body.
Fn.new { System.print("ok") }.call() // expect: ok
System.print(block { "ok" }()) // expect: ok
// Curly body.
Fn.new {
block {
System.print("ok") // expect: ok
}.call()
}()
// Multiple statements.
Fn.new {
block {
System.print("1") // expect: 1
System.print("2") // expect: 2
}.call()
}()
// Extra newlines.
Fn.new {
block {
System.print("1") // expect: 1
@ -22,4 +24,4 @@ Fn.new {
System.print("2") // expect: 2
}.call()
}()

View File

@ -1,9 +1,9 @@
var f
for (i in [1, 2, 3]) {
var j = 4
f = Fn.new { System.print(i + j) }
f = fn () { System.print(i + j) }
break
}
f.call()
f()
// expect: 5

View File

@ -1,9 +1,9 @@
var f
while (true) {
var i = "i"
f = Fn.new { System.print(i) }
f = fn () { System.print(i) }
break
}
f.call()
f()
// expect: i

View File

@ -1,6 +1,6 @@
var done = false
while (!done) {
Fn.new {
fn () {
break // expect error
}
done = true

View File

@ -3,23 +3,23 @@ var g = null
{
var local = "local"
f = Fn.new {
f = fn () {
System.print(local)
local = "after f"
System.print(local)
}
g = Fn.new {
g = fn () {
System.print(local)
local = "after g"
System.print(local)
}
}
f.call()
f()
// expect: local
// expect: after f
g.call()
g()
// expect: after f
// expect: after g

View File

@ -1,9 +1,9 @@
var f = null
Fn.new {|param|
f = Fn.new {
fn (param) {
f = fn () {
System.print(param)
}
}.call("param")
}("param")
f.call() // expect: param
f() // expect: param

View File

@ -3,11 +3,11 @@
// would crash because it walked to the end of the upvalue list (correct), but
// then didn't handle not finding the variable.
Fn.new {
fn () {
var a = "a"
var b = "b"
Fn.new {
fn () {
System.print(b) // expect: b
System.print(a) // expect: a
}.call()
}.call()
}()
}()

View File

@ -1,14 +1,14 @@
var F = null
var F
class Foo {
construct new() {}
method(param) {
F = Fn.new {
F = fn () {
System.print(param)
}
}
}
Foo.new().method("param")
F.call() // expect: param
F() // expect: param

View File

@ -2,9 +2,9 @@ var f = null
{
var local = "local"
f = Fn.new {
f = fn () {
System.print(local)
}
}
f.call() // expect: local
f() // expect: local

View File

@ -1,21 +1,21 @@
var f = null
Fn.new {
fn () {
var a = "a"
Fn.new {
fn () {
var b = "b"
Fn.new {
fn () {
var c = "c"
f = Fn.new {
f = fn () {
System.print(a)
System.print(b)
System.print(c)
}
}.call()
}.call()
}.call()
}()
}()
}()
f.call()
f()
// expect: a
// expect: b
// expect: c

View File

@ -1,6 +1,6 @@
{
var local = "local"
Fn.new {
fn () {
System.print(local) // expect: local
}.call()
}()
}

View File

@ -2,12 +2,12 @@ var f = null
{
var a = "a"
f = Fn.new {
f = fn () {
System.print(a)
System.print(a)
}
}
f.call()
f()
// expect: a
// expect: a

View File

@ -3,14 +3,14 @@
{
var a = "a"
f = Fn.new { System.print(a) }
f = fn () { System.print(a) }
}
{
// Since a is out of scope, the local slot will be reused by b. Make sure
// that f still closes over a.
var b = "b"
f.call() // expect: a
f() // expect: a
}
}

View File

@ -1,11 +1,11 @@
{
var foo = "closure"
Fn.new {
fn () {
{
System.print(foo) // expect: closure
var foo = "shadow"
System.print(foo) // expect: shadow
}
System.print(foo) // expect: closure
}.call()
}()
}

View File

@ -4,7 +4,7 @@
{
var a = "a"
if (false) Fn.new { a }
if (false) fn () { a }
}
// If we get here, we didn't segfault when a went out of scope.

View File

@ -11,9 +11,9 @@ var closure
{
var b = "b"
closure = Fn.new { a }
if (false) Fn.new { b }
closure = fn () { a }
if (false) fn () { b }
}
System.print(closure.call()) // expect: a
System.print(closure()) // expect: a
}

View File

@ -12,14 +12,14 @@ var closure
a = "final"
}
closure = Fn.new {
closure = fn () {
System.print(a)
}
}
fiber.call() // expect: before
closure.call() // expect: before
fiber.call()
closure.call() // expect: after
fiber.call() // expect: after
closure.call() // expect: final
fiber() // expect: before
closure() // expect: before
fiber()
closure() // expect: after
fiber() // expect: after
closure() // expect: final

View File

@ -1,16 +1,16 @@
class Foo {
construct new() { _field = "Foo field" }
closeOverGet {
return Fn.new { _field }
closeOverGet() {
return fn () { _field }
}
closeOverSet {
return Fn.new { _field = "new value" }
closeOverSet() {
return fn () { _field = "new value" }
}
}
var foo = Foo.new()
System.print(foo.closeOverGet.call()) // expect: Foo field
foo.closeOverSet.call()
System.print(foo.closeOverGet.call()) // expect: new value
System.print(foo.closeOverGet()()) // expect: Foo field
foo.closeOverSet()()
System.print(foo.closeOverGet()()) // expect: new value

View File

@ -1,5 +1,5 @@
class Foo {
static bar {
Fn.new { _field = "wat" } // expect error
fn () { _field = "wat" } // expect error
}
}

View File

@ -1,10 +1,10 @@
var list = []
for (i in [1, 2, 3]) {
list.add(Fn.new { System.print(i) })
list.add(fn () { System.print(i) })
}
for (f in list) f.call()
for (f in list) f()
// expect: 1
// expect: 2
// expect: 3

View File

@ -2,10 +2,10 @@ var list = []
for (i in [1, 2, 3]) {
var j = i + 1
list.add(Fn.new { System.print(j) })
list.add(fn () { System.print(j) })
}
for (f in list) f.call()
for (f in list) f()
// expect: 2
// expect: 3
// expect: 4

View File

@ -1,9 +1,9 @@
var f = Fn.new {
fn f() {
System.print("evaluate sequence")
return [1, 2, 3]
}
for (i in f.call()) System.print(i)
for (i in f()) System.print(i)
// expect: evaluate sequence
// expect: 1
// expect: 2

View File

@ -1,9 +1,9 @@
var f = Fn.new {
fn f() {
for (i in [1, 2, 3]) {
return Fn.new { System.print(i) }
return fn () { System.print(i) }
}
}
var g = f.call()
g.call()
var g = f()
g()
// expect: 1

View File

@ -1,8 +1,8 @@
var f = Fn.new {
fn f() {
for (i in [1, 2, 3]) {
return i
}
}
System.print(f.call())
System.print(f())
// expect: 1

View File

@ -0,0 +1,31 @@
// Single expression body.
fn a() { "ok" }
System.print(a()) // expect: ok
// Curly body.
fn b() {
System.print("ok") // expect: ok
}
b()
// Multiple statements.
fn c() {
System.print("1") // expect: 1
System.print("2") // expect: 2
}
c()
// Extra newlines.
fn d() {
System.print("1") // expect: 1
System.print("2") // expect: 2
}
d()
// TODO: Newlines before/after parameters.

View File

@ -1,2 +1,2 @@
var f = Fn.new {}
System.print(f.call()) // expect: null
fn f() {}
System.print(f()) // expect: null

View File

@ -0,0 +1,25 @@
// Single expression body.
System.print(fn () { "ok" }()) // expect: ok
// Curly body.
fn () {
System.print("ok") // expect: ok
}()
// Multiple statements.
fn () {
System.print("1") // expect: 1
System.print("2") // expect: 2
}()
// Extra newlines.
fn () {
System.print("1") // expect: 1
System.print("2") // expect: 2
}()

View File

@ -1,4 +1,4 @@
var f = Fn.new {
fn f() {
// Hi.
}
System.print(f.call()) // expect: null
System.print(f()) // expect: null

View File

@ -1,2 +1,2 @@
Fn.new { System.print("ok") // expect error
}.call()
fn () { System.print("ok") // expect error
}()

View File

@ -1,2 +1,2 @@
Fn.new {
fn () {
System.print("ok") } // expect error

View File

@ -1 +0,0 @@
Fn.new {|| null } // expect error

View File

@ -1,50 +1,50 @@
var f0 = Fn.new { 0 }
System.print(f0.call()) // expect: 0
fn f0() { 0 }
System.print(f0()) // expect: 0
var f1 = Fn.new {|a| a }
System.print(f1.call(1)) // expect: 1
fn f1(a) { a }
System.print(f1(1)) // expect: 1
var f2 = Fn.new {|a, b| a + b }
System.print(f2.call(1, 2)) // expect: 3
fn f2(a, b) { a + b }
System.print(f2(1, 2)) // expect: 3
var f3 = Fn.new {|a, b, c| a + b + c }
System.print(f3.call(1, 2, 3)) // expect: 6
fn f3(a, b, c) { a + b + c }
System.print(f3(1, 2, 3)) // expect: 6
var f4 = Fn.new {|a, b, c, d| a + b + c + d }
System.print(f4.call(1, 2, 3, 4)) // expect: 10
fn f4(a, b, c, d) { a + b + c + d }
System.print(f4(1, 2, 3, 4)) // expect: 10
var f5 = Fn.new {|a, b, c, d, e| a + b + c + d + e }
System.print(f5.call(1, 2, 3, 4, 5)) // expect: 15
fn f5(a, b, c, d, e) { a + b + c + d + e }
System.print(f5(1, 2, 3, 4, 5)) // expect: 15
var f6 = Fn.new {|a, b, c, d, e, f| a + b + c + d + e + f }
System.print(f6.call(1, 2, 3, 4, 5, 6)) // expect: 21
fn f6(a, b, c, d, e, f) { a + b + c + d + e + f }
System.print(f6(1, 2, 3, 4, 5, 6)) // expect: 21
var f7 = Fn.new {|a, b, c, d, e, f, g| a + b + c + d + e + f + g }
System.print(f7.call(1, 2, 3, 4, 5, 6, 7)) // expect: 28
fn f7(a, b, c, d, e, f, g) { a + b + c + d + e + f + g }
System.print(f7(1, 2, 3, 4, 5, 6, 7)) // expect: 28
var f8 = Fn.new {|a, b, c, d, e, f, g, h| a + b + c + d + e + f + g + h }
System.print(f8.call(1, 2, 3, 4, 5, 6, 7, 8)) // expect: 36
fn f8(a, b, c, d, e, f, g, h) { a + b + c + d + e + f + g + h }
System.print(f8(1, 2, 3, 4, 5, 6, 7, 8)) // expect: 36
var f9 = Fn.new {|a, b, c, d, e, f, g, h, i| a + b + c + d + e + f + g + h + i }
System.print(f9.call(1, 2, 3, 4, 5, 6, 7, 8, 9)) // expect: 45
fn f9(a, b, c, d, e, f, g, h, i) { a + b + c + d + e + f + g + h + i }
System.print(f9(1, 2, 3, 4, 5, 6, 7, 8, 9)) // expect: 45
var f10 = Fn.new {|a, b, c, d, e, f, g, h, i, j| a + b + c + d + e + f + g + h + i + j }
System.print(f10.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) // expect: 55
fn f10(a, b, c, d, e, f, g, h, i, j) { a + b + c + d + e + f + g + h + i + j }
System.print(f10(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) // expect: 55
var f11 = Fn.new {|a, b, c, d, e, f, g, h, i, j, k| a + b + c + d + e + f + g + h + i + j + k }
System.print(f11.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)) // expect: 66
fn f11(a, b, c, d, e, f, g, h, i, j, k) { a + b + c + d + e + f + g + h + i + j + k }
System.print(f11(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)) // expect: 66
var f12 = Fn.new {|a, b, c, d, e, f, g, h, i, j, k, l| a + b + c + d + e + f + g + h + i + j + k + l }
System.print(f12.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)) // expect: 78
fn f12(a, b, c, d, e, f, g, h, i, j, k, l) { a + b + c + d + e + f + g + h + i + j + k + l }
System.print(f12(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)) // expect: 78
var f13 = Fn.new {|a, b, c, d, e, f, g, h, i, j, k, l, m| a + b + c + d + e + f + g + h + i + j + k + l + m }
System.print(f13.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)) // expect: 91
fn f13(a, b, c, d, e, f, g, h, i, j, k, l, m) { a + b + c + d + e + f + g + h + i + j + k + l + m }
System.print(f13(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)) // expect: 91
var f14 = Fn.new {|a, b, c, d, e, f, g, h, i, j, k, l, m, n| a + b + c + d + e + f + g + h + i + j + k + l + m + n }
System.print(f14.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)) // expect: 105
fn f14(a, b, c, d, e, f, g, h, i, j, k, l, m, n) { a + b + c + d + e + f + g + h + i + j + k + l + m + n }
System.print(f14(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)) // expect: 105
var f15 = Fn.new {|a, b, c, d, e, f, g, h, i, j, k, l, m, n, o| a + b + c + d + e + f + g + h + i + j + k + l + m + n + o }
System.print(f15.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)) // expect: 120
fn f15(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) { a + b + c + d + e + f + g + h + i + j + k + l + m + n + o }
System.print(f15(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)) // expect: 120
var f16 = Fn.new {|a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p| a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p }
System.print(f16.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)) // expect: 136
fn f16(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) { a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p }
System.print(f16(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)) // expect: 136

View File

@ -0,0 +1,11 @@
class Foo {
construct new() {
method { "block" }
}
method(a) {
System.print("method %(a())")
}
}
Foo.new() // expect: method block

View File

@ -2,7 +2,7 @@ var ClosureType
{
var a = "a"
ClosureType = Fn.new { System.print(a) }.type
ClosureType = fn () { System.print(a) }.type
}
class Subclass is ClosureType {} // expect runtime error: Class 'Subclass' cannot inherit from built-in class 'Fn'.

View File

@ -1,12 +1,12 @@
class Foo {
construct new() { _field = "Foo field" }
closeOverFooGet {
return Fn.new { Fn.new { _field } }
closeOverFooGet() {
return fn () { fn () { _field } }
}
closeOverFooSet {
return Fn.new { Fn.new { _field = "new foo value" } }
closeOverFooSet() {
return fn () { fn () { _field = "new foo value" } }
}
}
@ -16,21 +16,21 @@ class Bar is Foo {
_field = "Bar field"
}
closeOverBarGet {
return Fn.new { Fn.new { _field } }
closeOverBarGet() {
return fn () { fn () { _field } }
}
closeOverBarSet {
return Fn.new { Fn.new { _field = "new bar value" } }
closeOverBarSet() {
return fn () { fn () { _field = "new bar value" } }
}
}
var bar = Bar.new()
System.print(bar.closeOverFooGet.call().call()) // expect: Foo field
System.print(bar.closeOverBarGet.call().call()) // expect: Bar field
bar.closeOverFooSet.call().call()
System.print(bar.closeOverFooGet.call().call()) // expect: new foo value
System.print(bar.closeOverBarGet.call().call()) // expect: Bar field
bar.closeOverBarSet.call().call()
System.print(bar.closeOverFooGet.call().call()) // expect: new foo value
System.print(bar.closeOverBarGet.call().call()) // expect: new bar value
System.print(bar.closeOverFooGet()()()) // expect: Foo field
System.print(bar.closeOverBarGet()()()) // expect: Bar field
bar.closeOverFooSet()()()
System.print(bar.closeOverFooGet()()()) // expect: new foo value
System.print(bar.closeOverBarGet()()()) // expect: Bar field
bar.closeOverBarSet()()()
System.print(bar.closeOverFooGet()()()) // expect: new foo value
System.print(bar.closeOverBarGet()()()) // expect: new bar value

Some files were not shown because too many files have changed in this diff Show More