4 Commits

Author SHA1 Message Date
10f149f359 Add "def" syntax sugar for named function definitions.
This is technically redundant but it makes functional/procedural code
look a *lot* more natural.
2015-12-01 08:05:46 -08:00
c3e2f17758 Allow infix () and {} to call "call" on the left-hand side.
- Allow this for both argument lists and block arguments.

- Tweak precedence to make "." higher than infix "{}" to avoid a
  class body being parsed as the superclass clause's block argument.

- Convert all uses of ".call" to use this. (There was not a single
  case in the repo that ran into the getter ambiguity.)
2015-12-01 07:49:04 -08:00
9df6aa2f8a Remove support for implicit self sends and convert everything to '@'.
This fixes the nasty case where "foo(bar)" is context-sensitive, and
generally simplifies the compiler a lot since there is a clear
distinction between lexical and dynamic scope.

Also:

- Remove the special handling of capitalized names since all names are
  lexical now.
- Allow methods to close over local variables in enclosing functions.
- Allow implicit definition of all lexical names.
2015-11-30 22:46:11 -08:00
2e425ae840 Allow "@method()" syntax for explicit self sends, like CoffeeScript. 2015-11-30 21:44:12 -08:00
155 changed files with 1000 additions and 863 deletions

View File

@ -9,29 +9,27 @@ import "io" for Stdin
// animals. Internal nodes are yes/no questions that choose which branch to
// explore.
class Node {
// Reads a "yes" or "no" (or something approximating) those and returns true
// if yes was entered.
promptYesNo(prompt) {
while (true) {
var line = promptString(prompt)
// Reads a "yes" or "no" (or something approximating) those and returns true
// if yes was entered.
def promptYesNo(prompt) {
while (true) {
var line = promptString(prompt)
if (line.startsWith("y") || line.startsWith("Y")) return true
if (line.startsWith("n") || line.startsWith("N")) return false
if (line.startsWith("y") || line.startsWith("Y")) return true
if (line.startsWith("n") || line.startsWith("N")) return false
// Quit.
if (line.startsWith("q") || line.startsWith("Q")) Fiber.yield()
}
}
// Writes a prompt and reads a string of input.
promptString(prompt) {
System.write("%(prompt) ")
return Stdin.readLine()
// Quit.
if (line.startsWith("q") || line.startsWith("Q")) Fiber.yield()
}
}
class Animal is Node {
// Writes a prompt and reads a string of input.
def promptString(prompt) {
System.write("%(prompt) ")
return Stdin.readLine()
}
class Animal {
construct new(name) {
_name = name
}
@ -56,7 +54,7 @@ class Animal is Node {
}
}
class Question is Node {
class Question {
construct new(question, ifYes, ifNo) {
_question = question
_ifYes = ifYes
@ -83,4 +81,4 @@ var root = Question.new("Does it live in the water?",
// Play games until the user quits.
Fiber.new {
while (true) root.ask()
}.call()
}()

View File

@ -11,15 +11,15 @@ class SyntaxExample {
// Top-level name `IO`
System.print("I am a constructor!")
// Method calls
variables
fields()
// This calls
@variables
@fields()
// Block arguments
fields { block }
fields {|a, b| block }
fields(argument) { block }
fields(argument) {|a, b| block }
@fields { "block" }
@fields {|a, b| "block" }
@fields("argument") { "block" }
@fields("argument") {|a, b| "block" }
// Static method call
SyntaxExample.fields(1)
@ -27,8 +27,8 @@ class SyntaxExample {
// Constructor with arguments
construct constructor(a, b) {
print(a, b)
field = a
@print(a, b)
@field = a
}
// Method without arguments

View File

@ -4,14 +4,14 @@ foreign class File {
static open(path) {
if (!(path is String)) Fiber.abort("Path must be a string.")
open_(path, Fiber.current)
@open_(path, Fiber.current)
var fd = Scheduler.runNextScheduled_()
return new_(fd)
return @new_(fd)
}
static open(path, fn) {
var file = open(path)
var fiber = Fiber.new { fn.call(file) }
var file = @open(path)
var fiber = Fiber.new { fn(file) }
// Poor man's finally. Can we make this more elegant?
var result = fiber.try()
@ -29,33 +29,33 @@ foreign class File {
static size(path) {
if (!(path is String)) Fiber.abort("Path must be a string.")
sizePath_(path, Fiber.current)
@sizePath_(path, Fiber.current)
return Scheduler.runNextScheduled_()
}
construct new_(fd) {}
close() {
if (close_(Fiber.current)) return
if (@close_(Fiber.current)) return
Scheduler.runNextScheduled_()
}
isOpen { descriptor != -1 }
isOpen { @descriptor != -1 }
size {
if (!isOpen) Fiber.abort("File is not open.")
if (!@isOpen) Fiber.abort("File is not open.")
size_(Fiber.current)
@size_(Fiber.current)
return Scheduler.runNextScheduled_()
}
readBytes(count) {
if (!isOpen) Fiber.abort("File is not open.")
if (!@isOpen) Fiber.abort("File is not open.")
if (!(count is Num)) Fiber.abort("Count must be an integer.")
if (!count.isInteger) Fiber.abort("Count must be an integer.")
if (count < 0) Fiber.abort("Count cannot be negative.")
readBytes_(count, Fiber.current)
@readBytes_(count, Fiber.current)
return Scheduler.runNextScheduled_()
}
@ -75,19 +75,19 @@ class Stdin {
}
// TODO: Error if other fiber is already waiting.
readStart_()
@readStart_()
__waitingFiber = Fiber.current
var line = Scheduler.runNextScheduled_()
readStop_()
@readStop_()
return line
}
static onData_(data) {
if (data == null) {
__isClosed = true
readStop_()
@readStop_()
if (__line != null) {
var line = __line

View File

@ -6,14 +6,14 @@ static const char* ioModuleSource =
" static open(path) {\n"
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
"\n"
" open_(path, Fiber.current)\n"
" @open_(path, Fiber.current)\n"
" var fd = Scheduler.runNextScheduled_()\n"
" return new_(fd)\n"
" return @new_(fd)\n"
" }\n"
"\n"
" static open(path, fn) {\n"
" var file = open(path)\n"
" var fiber = Fiber.new { fn.call(file) }\n"
" var file = @open(path)\n"
" var fiber = Fiber.new { fn(file) }\n"
"\n"
" // Poor man's finally. Can we make this more elegant?\n"
" var result = fiber.try()\n"
@ -31,33 +31,33 @@ static const char* ioModuleSource =
" static size(path) {\n"
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
"\n"
" sizePath_(path, Fiber.current)\n"
" @sizePath_(path, Fiber.current)\n"
" return Scheduler.runNextScheduled_()\n"
" }\n"
"\n"
" construct new_(fd) {}\n"
"\n"
" close() {\n"
" if (close_(Fiber.current)) return\n"
" if (@close_(Fiber.current)) return\n"
" Scheduler.runNextScheduled_()\n"
" }\n"
"\n"
" isOpen { descriptor != -1 }\n"
" isOpen { @descriptor != -1 }\n"
"\n"
" size {\n"
" if (!isOpen) Fiber.abort(\"File is not open.\")\n"
" if (!@isOpen) Fiber.abort(\"File is not open.\")\n"
"\n"
" size_(Fiber.current)\n"
" @size_(Fiber.current)\n"
" return Scheduler.runNextScheduled_()\n"
" }\n"
"\n"
" readBytes(count) {\n"
" if (!isOpen) Fiber.abort(\"File is not open.\")\n"
" if (!@isOpen) Fiber.abort(\"File is not open.\")\n"
" if (!(count is Num)) Fiber.abort(\"Count must be an integer.\")\n"
" if (!count.isInteger) Fiber.abort(\"Count must be an integer.\")\n"
" if (count < 0) Fiber.abort(\"Count cannot be negative.\")\n"
"\n"
" readBytes_(count, Fiber.current)\n"
" @readBytes_(count, Fiber.current)\n"
" return Scheduler.runNextScheduled_()\n"
" }\n"
"\n"
@ -77,19 +77,19 @@ static const char* ioModuleSource =
" }\n"
"\n"
" // TODO: Error if other fiber is already waiting.\n"
" readStart_()\n"
" @readStart_()\n"
"\n"
" __waitingFiber = Fiber.current\n"
" var line = Scheduler.runNextScheduled_()\n"
"\n"
" readStop_()\n"
" @readStop_()\n"
" return line\n"
" }\n"
"\n"
" static onData_(data) {\n"
" if (data == null) {\n"
" __isClosed = true\n"
" readStop_()\n"
" @readStop_()\n"
"\n"
" if (__line != null) {\n"
" var line = __line\n"

View File

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

View File

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

View File

@ -5,7 +5,7 @@ class Timer {
if (!(milliseconds is Num)) Fiber.abort("Milliseconds must be a number.")
if (milliseconds < 0) Fiber.abort("Milliseconds cannot be negative.")
startTimer_(milliseconds, Fiber.current)
@startTimer_(milliseconds, Fiber.current)
Scheduler.runNextScheduled_()
}

View File

@ -7,7 +7,7 @@ static const char* timerModuleSource =
" if (!(milliseconds is Num)) Fiber.abort(\"Milliseconds must be a number.\")\n"
" if (milliseconds < 0) Fiber.abort(\"Milliseconds cannot be negative.\")\n"
"\n"
" startTimer_(milliseconds, Fiber.current)\n"
" @startTimer_(milliseconds, Fiber.current)\n"
" Scheduler.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 fn = @compile_(source)
// TODO: Include compile errors.
if (fn == null) Fiber.abort("Could not compile source code.")
Fiber.new(fn).call()
Fiber.new(fn)()
}
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 fn = @compile_(source)\n"
" // TODO: Include compile errors.\n"
" if (fn == null) Fiber.abort(\"Could not compile source code.\")\n"
"\n"
" Fiber.new(fn).call()\n"
" Fiber.new(fn)()\n"
" }\n"
"\n"
" foreign static compile_(source)\n"

View File

@ -1,11 +1,11 @@
foreign class Random {
construct new() {
seed_()
@seed_()
}
construct new(seed) {
if (seed is Num) {
seed_(seed)
@seed_(seed)
} else if (seed is Sequence) {
if (seed.isEmpty) Fiber.abort("Sequence cannot be empty.")
@ -25,7 +25,7 @@ foreign class Random {
i = i + 1
}
seed_(
@seed_(
seeds[0], seeds[1], seeds[2], seeds[3],
seeds[4], seeds[5], seeds[6], seeds[7],
seeds[8], seeds[9], seeds[10], seeds[11],
@ -40,10 +40,10 @@ foreign class Random {
foreign seed_(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16)
foreign float()
float(end) { float() * end }
float(start, end) { float() * (end - start) + start }
float(end) { @float() * end }
float(start, end) { @float() * (end - start) + start }
foreign int()
int(end) { (float() * end).floor }
int(start, end) { (float() * (end - start)).floor + start }
int(end) { (@float() * end).floor }
int(start, end) { (@float() * (end - start)).floor + start }
}

View File

@ -2,12 +2,12 @@
static const char* randomModuleSource =
"foreign class Random {\n"
" construct new() {\n"
" seed_()\n"
" @seed_()\n"
" }\n"
"\n"
" construct new(seed) {\n"
" if (seed is Num) {\n"
" seed_(seed)\n"
" @seed_(seed)\n"
" } else if (seed is Sequence) {\n"
" if (seed.isEmpty) Fiber.abort(\"Sequence cannot be empty.\")\n"
"\n"
@ -27,7 +27,7 @@ static const char* randomModuleSource =
" i = i + 1\n"
" }\n"
"\n"
" seed_(\n"
" @seed_(\n"
" seeds[0], seeds[1], seeds[2], seeds[3],\n"
" seeds[4], seeds[5], seeds[6], seeds[7],\n"
" seeds[8], seeds[9], seeds[10], seeds[11],\n"
@ -42,10 +42,10 @@ static const char* randomModuleSource =
" foreign seed_(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16)\n"
"\n"
" foreign float()\n"
" float(end) { float() * end }\n"
" float(start, end) { float() * (end - start) + start }\n"
" float(end) { @float() * end }\n"
" float(start, end) { @float() * (end - start) + start }\n"
"\n"
" foreign int()\n"
" int(end) { (float() * end).floor }\n"
" int(start, end) { (float() * (end - start)).floor + start }\n"
" int(end) { (@float() * end).floor }\n"
" int(start, end) { (@float() * (end - start)).floor + start }\n"
"}\n";

View File

@ -69,6 +69,7 @@ typedef enum
TOKEN_BANG,
TOKEN_TILDE,
TOKEN_QUESTION,
TOKEN_AT,
TOKEN_EQ,
TOKEN_LT,
TOKEN_GT,
@ -80,6 +81,7 @@ typedef enum
TOKEN_BREAK,
TOKEN_CLASS,
TOKEN_CONSTRUCT,
TOKEN_DEF,
TOKEN_ELSE,
TOKEN_FALSE,
TOKEN_FOR,
@ -489,6 +491,7 @@ static Keyword keywords[] =
{"break", 5, TOKEN_BREAK},
{"class", 5, TOKEN_CLASS},
{"construct", 9, TOKEN_CONSTRUCT},
{"def", 3, TOKEN_DEF},
{"else", 4, TOKEN_ELSE},
{"false", 5, TOKEN_FALSE},
{"for", 3, TOKEN_FOR},
@ -886,6 +889,7 @@ static void nextToken(Parser* parser)
case '-': makeToken(parser, TOKEN_MINUS); return;
case '~': makeToken(parser, TOKEN_TILDE); return;
case '?': makeToken(parser, TOKEN_QUESTION); return;
case '@': makeToken(parser, TOKEN_AT); return;
case '|': twoCharToken(parser, '|', TOKEN_PIPEPIPE, TOKEN_PIPE); return;
case '&': twoCharToken(parser, '&', TOKEN_AMPAMP, TOKEN_AMP); return;
@ -1292,26 +1296,19 @@ static int addUpvalue(Compiler* compiler, bool isLocal, int index)
return compiler->numUpvalues++;
}
// Attempts to look up [name] in the functions enclosing the one being compiled
// by [compiler]. If found, it adds an upvalue for it to this compiler's list
// of upvalues (unless it's already in there) and returns its index. If not
// found, returns -1.
// Attempts to look up [name] in the local scopes enclosing the current one.
// If found, it adds an upvalue for it to this compiler's list of upvalues
// (unless it's already in there) and returns its index. If not found, returns
// -1.
//
// If the name is found outside of the immediately enclosing function, this
// will flatten the closure and add upvalues to all of the intermediate
// functions so that it gets walked down to this one.
//
// If it reaches a method boundary, this stops and returns -1 since methods do
// not close over local variables.
static int findUpvalue(Compiler* compiler, const char* name, int length)
{
// If we are at the top level, we didn't find it.
if (compiler->parent == NULL) return -1;
// If we hit the method boundary (and the name isn't a static field), then
// stop looking for it. We'll instead treat it as a self send.
if (name[0] != '_' && compiler->parent->enclosingClass != NULL) return -1;
// See if it's a local variable in the immediately enclosing function.
int local = resolveLocal(compiler->parent, name, length);
if (local != -1)
@ -1340,42 +1337,48 @@ static int findUpvalue(Compiler* compiler, const char* name, int length)
return -1;
}
// Look up [name] in the current scope to see what name it is bound to. Returns
// the index of the name either in local scope, or the enclosing function's
// upvalue list. Does not search the module scope. Returns -1 if not found.
// Look up [name] in the current stack of lexical scopes to see what variable
// it refers to. Returns the index of the name either the current local scope,
// the enclosing function's upvalue, or in module scope.
//
// Sets [loadInstruction] to the instruction needed to load the variable. Will
// be [CODE_LOAD_LOCAL] or [CODE_LOAD_UPVALUE].
static int resolveNonmodule(Compiler* compiler, const char* name, int length,
Code* loadInstruction)
{
// Look it up in the local scopes. Look in reverse order so that the most
// nested variable is found first and shadows outer ones.
*loadInstruction = CODE_LOAD_LOCAL;
int local = resolveLocal(compiler, name, length);
if (local != -1) return local;
// If we got here, it's not a local, so lets see if we are closing over an
// outer local.
*loadInstruction = CODE_LOAD_UPVALUE;
return findUpvalue(compiler, name, length);
}
// Look up [name] in the current scope to see what name it is bound to. Returns
// the index of the name either in module scope, local scope, or the enclosing
// function's upvalue list. Returns -1 if not found.
// If not found, implicitly declares a new module-level variable with the name,
// assuming it's a forward reference to a variable that will be explicitly
// defined later.
//
// Sets [loadInstruction] to the instruction needed to load the variable. Will
// be one of [CODE_LOAD_LOCAL], [CODE_LOAD_UPVALUE], or [CODE_LOAD_MODULE_VAR].
static int resolveName(Compiler* compiler, const char* name, int length,
Code* loadInstruction)
{
int nonmodule = resolveNonmodule(compiler, name, length, loadInstruction);
if (nonmodule != -1) return nonmodule;
// Look it up in the local scopes. Look in reverse order so that the most
// nested variable is found first and shadows outer ones.
*loadInstruction = CODE_LOAD_LOCAL;
int local = resolveLocal(compiler, name, length);
if (local != -1) return local;
// If we got here, it's not a local, so lets see if we are closing over an
// outer local.
*loadInstruction = CODE_LOAD_UPVALUE;
int upvalue = findUpvalue(compiler, name, length);
if (upvalue != -1) return upvalue;
// If all else fails, look at the module level.
*loadInstruction = CODE_LOAD_MODULE_VAR;
return wrenSymbolTableFind(&compiler->parser->module->variableNames,
name, length);
int module = wrenSymbolTableFind(&compiler->parser->module->variableNames,
name, length);
// If it wasn't found, implicitly define a module-level variable.
if (module == -1 && (length != 4 || memcmp(name, "this", 4) != 0))
{
// If it's a nonlocal name, implicitly define a module-level variable in
// the hopes that we get a real definition later.
module = wrenDeclareVariable(compiler->parser->vm, compiler->parser->module,
name, length);
if (module == -2) error(compiler, "Too many module variables defined.");
}
return module;
}
static void loadLocal(Compiler* compiler, int slot)
@ -1493,7 +1496,8 @@ typedef enum
PREC_TERM, // + -
PREC_FACTOR, // * / %
PREC_UNARY, // unary - ! ~
PREC_CALL, // . () []
PREC_CALL, // () [] {}
PREC_METHOD, // .
PREC_PRIMARY
} Precedence;
@ -1738,6 +1742,39 @@ static void finishArgumentList(Compiler* compiler, Signature* signature)
ignoreNewlines(compiler);
}
// 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 a method call with [signature] using [instruction].
static void callSignature(Compiler* compiler, Code instruction,
Signature* signature)
@ -1789,37 +1826,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?
@ -1872,7 +1882,7 @@ static void namedCall(Compiler* compiler, bool allowAssignment,
static void loadThis(Compiler* compiler)
{
Code loadInstruction;
int index = resolveNonmodule(compiler, "this", 4, &loadInstruction);
int index = resolveName(compiler, "this", 4, &loadInstruction);
if (loadInstruction == CODE_LOAD_LOCAL)
{
loadLocal(compiler, index);
@ -1899,6 +1909,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 +1989,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)
{
@ -1996,6 +2040,17 @@ static ClassCompiler* getEnclosingClass(Compiler* compiler)
return compiler == NULL ? NULL : compiler->enclosingClass;
}
// Returns `true` if the compiler is currently inside a class definition.
//
// Otherwise, reports an error and returns `false`.
static bool ensureInsideClass(Compiler* compiler, const char* syntax)
{
if (getEnclosingClass(compiler) != NULL) return true;
error(compiler, "Cannot use '%s' outside of a class definition.", syntax);
return false;
}
static void field(Compiler* compiler, bool allowAssignment)
{
// Initialize it with a fake value so we can keep parsing and minimize the
@ -2133,68 +2188,15 @@ static void staticField(Compiler* compiler, bool allowAssignment)
variable(compiler, allowAssignment, index, loadInstruction);
}
// Returns `true` if [name] is a local variable name (starts with a lowercase
// letter).
static bool isLocalName(const char* name)
{
return name[0] >= 'a' && name[0] <= 'z';
}
// Compiles a variable name or method call with an implicit receiver.
// Compiles a reference to a lexically scoped variable name.
static void name(Compiler* compiler, bool allowAssignment)
{
// Look for the name in the scope chain up to the nearest enclosing method.
Token* token = &compiler->parser->previous;
Code loadInstruction;
int index = resolveNonmodule(compiler, token->start, token->length,
&loadInstruction);
if (index != -1)
{
variable(compiler, allowAssignment, index, loadInstruction);
return;
}
int index = resolveName(compiler, token->start, token->length,
&loadInstruction);
// 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 we're inside a method and the name is lowercase, treat it as a method
// on this.
if (isLocalName(token->start) && getEnclosingClass(compiler) != NULL)
{
loadThis(compiler);
namedCall(compiler, allowAssignment, CODE_CALL_0);
return;
}
// Otherwise, look for a module-level variable with the name.
int module = wrenSymbolTableFind(&compiler->parser->module->variableNames,
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.
module = wrenDeclareVariable(compiler->parser->vm, compiler->parser->module,
token->start, token->length);
if (module == -2)
{
error(compiler, "Too many module variables defined.");
}
}
variable(compiler, allowAssignment, module, CODE_LOAD_MODULE_VAR);
variable(compiler, allowAssignment, index, loadInstruction);
}
static void null(Compiler* compiler, bool allowAssignment)
@ -2280,12 +2282,7 @@ static void super_(Compiler* compiler, bool allowAssignment)
static void this_(Compiler* compiler, bool allowAssignment)
{
if (getEnclosingClass(compiler) == NULL)
{
error(compiler, "Cannot use 'this' outside of a method.");
return;
}
if (!ensureInsideClass(compiler, "this")) return;
loadThis(compiler);
}
@ -2312,7 +2309,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 '.'.");
@ -2366,6 +2363,18 @@ static void conditional(Compiler* compiler, bool allowAssignment)
patchJump(compiler, elseJump);
}
// A method call on this like "@method()".
static void thisCall(Compiler* compiler, bool allowAssignment)
{
if (!ensureInsideClass(compiler, "@")) return;
loadThis(compiler);
consume(compiler, TOKEN_NAME, "Expect method name after '@'.");
namedCall(compiler, allowAssignment, CODE_CALL_0);
// TODO: Infix and subscript operators?
}
void infixOp(Compiler* compiler, bool allowAssignment)
{
GrammarRule* rule = getRule(compiler->parser->previous.type);
@ -2531,14 +2540,14 @@ void constructorSignature(Compiler* compiler, Signature* signature)
GrammarRule rules[] =
{
/* TOKEN_LEFT_PAREN */ PREFIX(grouping),
/* TOKEN_LEFT_PAREN */ { grouping, call, NULL, PREC_CALL, NULL },
/* TOKEN_RIGHT_PAREN */ UNUSED,
/* TOKEN_LEFT_BRACKET */ { list, subscript, subscriptSignature, PREC_CALL, NULL },
/* TOKEN_RIGHT_BRACKET */ UNUSED,
/* TOKEN_LEFT_BRACE */ PREFIX(map),
/* TOKEN_LEFT_BRACE */ { map, blockArgument, NULL, PREC_CALL, NULL },
/* TOKEN_RIGHT_BRACE */ UNUSED,
/* TOKEN_COLON */ UNUSED,
/* TOKEN_DOT */ INFIX(PREC_CALL, call),
/* TOKEN_DOT */ INFIX(PREC_METHOD, dot),
/* TOKEN_DOTDOT */ INFIX_OPERATOR(PREC_RANGE, ".."),
/* TOKEN_DOTDOTDOT */ INFIX_OPERATOR(PREC_RANGE, "..."),
/* TOKEN_COMMA */ UNUSED,
@ -2557,6 +2566,7 @@ GrammarRule rules[] =
/* TOKEN_BANG */ PREFIX_OPERATOR("!"),
/* TOKEN_TILDE */ PREFIX_OPERATOR("~"),
/* TOKEN_QUESTION */ INFIX(PREC_ASSIGNMENT, conditional),
/* TOKEN_AT */ PREFIX(thisCall),
/* TOKEN_EQ */ UNUSED,
/* TOKEN_LT */ INFIX_OPERATOR(PREC_COMPARISON, "<"),
/* TOKEN_GT */ INFIX_OPERATOR(PREC_COMPARISON, ">"),
@ -2567,6 +2577,7 @@ GrammarRule rules[] =
/* TOKEN_BREAK */ UNUSED,
/* TOKEN_CLASS */ UNUSED,
/* TOKEN_CONSTRUCT */ { NULL, NULL, constructorSignature, PREC_NONE, NULL },
/* TOKEN_DEF */ UNUSED,
/* TOKEN_ELSE */ UNUSED,
/* TOKEN_FALSE */ PREFIX(boolean),
/* TOKEN_FOR */ UNUSED,
@ -3164,7 +3175,7 @@ static void classDefinition(Compiler* compiler, bool isForeign)
// Load the superclass (if there is one).
if (match(compiler, TOKEN_IS))
{
parsePrecedence(compiler, false, PREC_CALL);
parsePrecedence(compiler, false, PREC_METHOD);
}
else
{
@ -3269,6 +3280,32 @@ static void import(Compiler* compiler)
} while (match(compiler, TOKEN_COMMA));
}
// Compiles a "def" function definition statement.
static void functionDefinition(Compiler* compiler)
{
// 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 a "var" variable definition statement.
static void variableDefinition(Compiler* compiler)
{
@ -3302,6 +3339,10 @@ void definition(Compiler* compiler)
{
classDefinition(compiler, false);
}
else if (match(compiler, TOKEN_DEF))
{
functionDefinition(compiler);
}
else if (match(compiler, TOKEN_FOREIGN))
{
consume(compiler, TOKEN_CLASS, "Expect 'class' after 'foreign'.");

View File

@ -5,19 +5,19 @@ class Null {}
class Num {}
class Sequence {
all(f) {
all(fn) {
var result = true
for (element in this) {
result = f.call(element)
result = fn(element)
if (!result) return result
}
return result
}
any(f) {
any(fn) {
var result = false
for (element in this) {
result = f.call(element)
result = fn(element)
if (result) return result
}
return result
@ -38,54 +38,54 @@ class Sequence {
return result
}
count(f) {
count(fn) {
var result = 0
for (element in this) {
if (f.call(element)) result = result + 1
if (fn(element)) result = result + 1
}
return result
}
each(f) {
each(fn) {
for (element in this) {
f.call(element)
fn(element)
}
}
isEmpty { iterate(null) ? false : true }
isEmpty { @iterate(null) ? false : true }
map(transformation) { MapSequence.new(this, transformation) }
where(predicate) { WhereSequence.new(this, predicate) }
reduce(acc, f) {
reduce(accumulator, fn) {
for (element in this) {
acc = f.call(acc, element)
accumulator = fn(accumulator, element)
}
return acc
return accumulator
}
reduce(f) {
var iter = iterate(null)
if (!iter) Fiber.abort("Can't reduce an empty sequence.")
reduce(fn) {
var iterator = @iterate(null)
if (!iterator) Fiber.abort("Can't reduce an empty sequence.")
// Seed with the first element.
var result = iteratorValue(iter)
while (iter = iterate(iter)) {
result = f.call(result, iteratorValue(iter))
var result = @iteratorValue(iterator)
while (iterator = @iterate(iterator)) {
result = fn(result, @iteratorValue(iterator))
}
return result
}
join() { join("") }
join() { @join("") }
join(sep) {
join(separator) {
var first = true
var result = ""
for (element in this) {
if (!first) result = result + sep
if (!first) result = result + separator
first = false
result = result + element.toString
}
@ -109,7 +109,7 @@ class MapSequence is Sequence {
}
iterate(iterator) { _sequence.iterate(iterator) }
iteratorValue(iterator) { _fn.call(_sequence.iteratorValue(iterator)) }
iteratorValue(iterator) { _fn(_sequence.iteratorValue(iterator)) }
}
class WhereSequence is Sequence {
@ -120,7 +120,7 @@ class WhereSequence is Sequence {
iterate(iterator) {
while (iterator = _sequence.iterate(iterator)) {
if (_fn.call(_sequence.iteratorValue(iterator))) break
if (_fn(_sequence.iteratorValue(iterator))) break
}
return iterator
}
@ -160,12 +160,12 @@ class StringCodePointSequence is Sequence {
class List is Sequence {
addAll(other) {
for (element in other) {
add(element)
@add(element)
}
return other
}
toString { "[%(join(", "))]" }
toString { "[%(@join(", "))]" }
+(other) {
var result = this[0..-1]
@ -184,7 +184,7 @@ class Map {
var first = true
var result = "{"
for (key in keys) {
for (key in @keys) {
if (!first) result = result + ", "
first = false
result = result + "%(key): %(this[key])"
@ -216,35 +216,35 @@ class Range is Sequence {}
class System {
static print() {
writeString_("\n")
@writeString_("\n")
}
static print(obj) {
writeObject_(obj)
writeString_("\n")
return obj
static print(object) {
@writeObject_(object)
@writeString_("\n")
return object
}
static printAll(sequence) {
for (object in sequence) writeObject_(object)
writeString_("\n")
for (object in sequence) @writeObject_(object)
@writeString_("\n")
}
static write(obj) {
writeObject_(obj)
return obj
static write(object) {
@writeObject_(object)
return object
}
static writeAll(sequence) {
for (object in sequence) writeObject_(object)
for (object in sequence) @writeObject_(object)
}
static writeObject_(obj) {
var string = obj.toString
static writeObject_(object) {
var string = object.toString
if (string is String) {
writeString_(string)
@writeString_(string)
} else {
writeString_("[invalid toString]")
@writeString_("[invalid toString]")
}
}
}

View File

@ -7,19 +7,19 @@ static const char* coreModuleSource =
"class Num {}\n"
"\n"
"class Sequence {\n"
" all(f) {\n"
" all(fn) {\n"
" var result = true\n"
" for (element in this) {\n"
" result = f.call(element)\n"
" result = fn(element)\n"
" if (!result) return result\n"
" }\n"
" return result\n"
" }\n"
"\n"
" any(f) {\n"
" any(fn) {\n"
" var result = false\n"
" for (element in this) {\n"
" result = f.call(element)\n"
" result = fn(element)\n"
" if (result) return result\n"
" }\n"
" return result\n"
@ -40,54 +40,54 @@ static const char* coreModuleSource =
" return result\n"
" }\n"
"\n"
" count(f) {\n"
" count(fn) {\n"
" var result = 0\n"
" for (element in this) {\n"
" if (f.call(element)) result = result + 1\n"
" if (fn(element)) result = result + 1\n"
" }\n"
" return result\n"
" }\n"
"\n"
" each(f) {\n"
" each(fn) {\n"
" for (element in this) {\n"
" f.call(element)\n"
" fn(element)\n"
" }\n"
" }\n"
"\n"
" isEmpty { iterate(null) ? false : true }\n"
" isEmpty { @iterate(null) ? false : true }\n"
"\n"
" map(transformation) { MapSequence.new(this, transformation) }\n"
"\n"
" where(predicate) { WhereSequence.new(this, predicate) }\n"
"\n"
" reduce(acc, f) {\n"
" reduce(accumulator, fn) {\n"
" for (element in this) {\n"
" acc = f.call(acc, element)\n"
" accumulator = fn(accumulator, element)\n"
" }\n"
" return acc\n"
" return accumulator\n"
" }\n"
"\n"
" reduce(f) {\n"
" var iter = iterate(null)\n"
" if (!iter) Fiber.abort(\"Can't reduce an empty sequence.\")\n"
" reduce(fn) {\n"
" var iterator = @iterate(null)\n"
" if (!iterator) Fiber.abort(\"Can't reduce an empty sequence.\")\n"
"\n"
" // Seed with the first element.\n"
" var result = iteratorValue(iter)\n"
" while (iter = iterate(iter)) {\n"
" result = f.call(result, iteratorValue(iter))\n"
" var result = @iteratorValue(iterator)\n"
" while (iterator = @iterate(iterator)) {\n"
" result = fn(result, @iteratorValue(iterator))\n"
" }\n"
"\n"
" return result\n"
" }\n"
"\n"
" join() { join(\"\") }\n"
" join() { @join(\"\") }\n"
"\n"
" join(sep) {\n"
" join(separator) {\n"
" var first = true\n"
" var result = \"\"\n"
"\n"
" for (element in this) {\n"
" if (!first) result = result + sep\n"
" if (!first) result = result + separator\n"
" first = false\n"
" result = result + element.toString\n"
" }\n"
@ -111,7 +111,7 @@ static const char* coreModuleSource =
" }\n"
"\n"
" iterate(iterator) { _sequence.iterate(iterator) }\n"
" iteratorValue(iterator) { _fn.call(_sequence.iteratorValue(iterator)) }\n"
" iteratorValue(iterator) { _fn(_sequence.iteratorValue(iterator)) }\n"
"}\n"
"\n"
"class WhereSequence is Sequence {\n"
@ -122,7 +122,7 @@ static const char* coreModuleSource =
"\n"
" iterate(iterator) {\n"
" while (iterator = _sequence.iterate(iterator)) {\n"
" if (_fn.call(_sequence.iteratorValue(iterator))) break\n"
" if (_fn(_sequence.iteratorValue(iterator))) break\n"
" }\n"
" return iterator\n"
" }\n"
@ -162,12 +162,12 @@ static const char* coreModuleSource =
"class List is Sequence {\n"
" addAll(other) {\n"
" for (element in other) {\n"
" add(element)\n"
" @add(element)\n"
" }\n"
" return other\n"
" }\n"
"\n"
" toString { \"[%(join(\", \"))]\" }\n"
" toString { \"[%(@join(\", \"))]\" }\n"
"\n"
" +(other) {\n"
" var result = this[0..-1]\n"
@ -186,7 +186,7 @@ static const char* coreModuleSource =
" var first = true\n"
" var result = \"{\"\n"
"\n"
" for (key in keys) {\n"
" for (key in @keys) {\n"
" if (!first) result = result + \", \"\n"
" first = false\n"
" result = result + \"%(key): %(this[key])\"\n"
@ -218,35 +218,35 @@ static const char* coreModuleSource =
"\n"
"class System {\n"
" static print() {\n"
" writeString_(\"\n\")\n"
" @writeString_(\"\n\")\n"
" }\n"
"\n"
" static print(obj) {\n"
" writeObject_(obj)\n"
" writeString_(\"\n\")\n"
" return obj\n"
" static print(object) {\n"
" @writeObject_(object)\n"
" @writeString_(\"\n\")\n"
" return object\n"
" }\n"
"\n"
" static printAll(sequence) {\n"
" for (object in sequence) writeObject_(object)\n"
" writeString_(\"\n\")\n"
" for (object in sequence) @writeObject_(object)\n"
" @writeString_(\"\n\")\n"
" }\n"
"\n"
" static write(obj) {\n"
" writeObject_(obj)\n"
" return obj\n"
" static write(object) {\n"
" @writeObject_(object)\n"
" return object\n"
" }\n"
"\n"
" static writeAll(sequence) {\n"
" for (object in sequence) writeObject_(object)\n"
" for (object in sequence) @writeObject_(object)\n"
" }\n"
"\n"
" static writeObject_(obj) {\n"
" var string = obj.toString\n"
" static writeObject_(object) {\n"
" var string = object.toString\n"
" if (string is String) {\n"
" writeString_(string)\n"
" @writeString_(string)\n"
" } else {\n"
" writeString_(\"[invalid toString]\")\n"
" @writeString_(\"[invalid toString]\")\n"
" }\n"
" }\n"
"}\n";

View File

@ -11,12 +11,9 @@ class Tree {
}
}
check {
if (_left == null) {
return _item
}
return _item + _left.check - _right.check
check() {
if (_left == null) return _item
return _item + _left.check() - _right.check()
}
}
@ -27,7 +24,7 @@ var stretchDepth = maxDepth + 1
var start = System.clock
System.print("stretch tree of depth %(stretchDepth) check: " +
"%(Tree.new(0, stretchDepth).check)")
"%(Tree.new(0, stretchDepth).check())")
var longLivedTree = Tree.new(0, maxDepth)
@ -41,7 +38,7 @@ var depth = minDepth
while (depth < stretchDepth) {
var check = 0
for (i in 1..iterations) {
check = check + Tree.new(i, depth).check + Tree.new(-i, depth).check
check = check + Tree.new(i, depth).check() + Tree.new(-i, depth).check()
}
System.print("%(iterations * 2) trees of depth %(depth) check: %(check)")
@ -50,5 +47,5 @@ while (depth < stretchDepth) {
}
System.print(
"long lived tree of depth %(maxDepth) check: %(longLivedTree.check)")
"long lived tree of depth %(maxDepth) check: %(longLivedTree.check())")
System.print("elapsed: %(System.clock - start)")

View File

@ -79,7 +79,7 @@ class Constraint {
// Activate this constraint and attempt to satisfy it.
addConstraint() {
addToGraph()
@addToGraph()
ThePlanner.incrementalAdd(this)
}
@ -89,16 +89,16 @@ class Constraint {
// there is one, or nil, if there isn't.
// Assume: I am not already satisfied.
satisfy(mark) {
chooseMethod(mark)
if (!isSatisfied) {
@chooseMethod(mark)
if (!@isSatisfied) {
if (_strength == REQUIRED) {
System.print("Could not satisfy a required constraint!")
}
return null
}
markInputs(mark)
var out = output
@markInputs(mark)
var out = @output
var overridden = out.determinedBy
if (overridden != null) overridden.markUnsatisfied()
out.determinedBy = this
@ -108,8 +108,8 @@ class Constraint {
}
destroyConstraint() {
if (isSatisfied) ThePlanner.incrementalRemove(this)
removeFromGraph()
if (@isSatisfied) ThePlanner.incrementalRemove(this)
@removeFromGraph()
}
// Normal constraints are not input constraints. An input constraint
@ -124,7 +124,7 @@ class UnaryConstraint is Constraint {
super(strength)
_satisfied = false
_myOutput = myOutput
addConstraint()
@addConstraint()
}
// Adds this constraint to the constraint graph.
@ -136,7 +136,7 @@ class UnaryConstraint is Constraint {
// Decides if this constraint can be satisfied and records that decision.
chooseMethod(mark) {
_satisfied = (_myOutput.mark != mark) &&
Strength.stronger(strength, _myOutput.walkStrength)
Strength.stronger(@strength, _myOutput.walkStrength)
}
// Returns true if this constraint is satisfied in the current solution.
@ -153,9 +153,9 @@ class UnaryConstraint is Constraint {
// 'stay', the value for the current output of this constraint. Assume
// this constraint is satisfied.
recalculate() {
_myOutput.walkStrength = strength
_myOutput.stay = !isInput
if (_myOutput.stay) execute() // Stay optimization.
_myOutput.walkStrength = @strength
_myOutput.stay = !@isInput
if (_myOutput.stay) @execute() // Stay optimization.
}
// Records that this constraint is unsatisfied.
@ -213,7 +213,7 @@ class BinaryConstraint is Constraint {
_v1 = v1
_v2 = v2
_direction = NONE
addConstraint()
@addConstraint()
}
direction { _direction }
@ -226,7 +226,7 @@ class BinaryConstraint is Constraint {
chooseMethod(mark) {
if (_v1.mark == mark) {
if (_v2.mark != mark &&
Strength.stronger(strength, _v2.walkStrength)) {
Strength.stronger(@strength, _v2.walkStrength)) {
_direction = FORWARD
} else {
_direction = NONE
@ -235,7 +235,7 @@ class BinaryConstraint is Constraint {
if (_v2.mark == mark) {
if (_v1.mark != mark &&
Strength.stronger(strength, _v1.walkStrength)) {
Strength.stronger(@strength, _v1.walkStrength)) {
_direction = BACKWARD
} else {
_direction = NONE
@ -243,13 +243,13 @@ class BinaryConstraint is Constraint {
}
if (Strength.weaker(_v1.walkStrength, _v2.walkStrength)) {
if (Strength.stronger(strength, _v1.walkStrength)) {
if (Strength.stronger(@strength, _v1.walkStrength)) {
_direction = BACKWARD
} else {
_direction = NONE
}
} else {
if (Strength.stronger(strength, _v2.walkStrength)) {
if (Strength.stronger(@strength, _v2.walkStrength)) {
_direction = FORWARD
} else {
_direction = BACKWARD
@ -269,7 +269,7 @@ class BinaryConstraint is Constraint {
// Mark the input variable with the given mark.
markInputs(mark) {
input.mark = mark
@input.mark = mark
}
// Returns the current input variable
@ -282,11 +282,11 @@ class BinaryConstraint is Constraint {
// 'stay', the value for the current output of this
// constraint. Assume this constraint is satisfied.
recalculate() {
var ihn = input
var out = output
out.walkStrength = Strength.weakest(strength, ihn.walkStrength)
var ihn = @input
var out = @output
out.walkStrength = Strength.weakest(@strength, ihn.walkStrength)
out.stay = ihn.stay
if (out.stay) execute()
if (out.stay) @execute()
}
// Record the fact that this constraint is unsatisfied.
@ -295,7 +295,7 @@ class BinaryConstraint is Constraint {
}
inputsKnown(mark) {
var i = input
var i = @input
return i.mark == mark || i.stay || i.determinedBy == null
}
@ -337,11 +337,11 @@ class ScaleConstraint is BinaryConstraint {
// Enforce this constraint. Assume that it is satisfied.
execute() {
if (direction == FORWARD) {
v2.value = v1.value * _scale.value + _offset.value
if (@direction == FORWARD) {
@v2.value = @v1.value * _scale.value + _offset.value
} else {
// TODO: Is this the same semantics as ~/?
v1.value = ((v2.value - _offset.value) / _scale.value).floor
@v1.value = ((@v2.value - _offset.value) / _scale.value).floor
}
}
@ -349,11 +349,11 @@ class ScaleConstraint is BinaryConstraint {
// 'stay', the value for the current output of this constraint. Assume
// this constraint is satisfied.
recalculate() {
var ihn = input
var out = output
out.walkStrength = Strength.weakest(strength, ihn.walkStrength)
var ihn = @input
var out = @output
out.walkStrength = Strength.weakest(@strength, ihn.walkStrength)
out.stay = ihn.stay && _scale.stay && _offset.stay
if (out.stay) execute()
if (out.stay) @execute()
}
}
@ -365,7 +365,7 @@ class EqualityConstraint is BinaryConstraint {
// Enforce this constraint. Assume that it is satisfied.
execute() {
output.value = input.value
@output.value = @input.value
}
}
@ -448,7 +448,7 @@ class Planner {
// the algorithm to avoid getting into an infinite loop even if the
// constraint graph has an inadvertent cycle.
incrementalAdd(constraint) {
var mark = newMark()
var mark = @newMark()
var overridden = constraint.satisfy(mark)
while (overridden != null) {
overridden = overridden.satisfy(mark)
@ -468,11 +468,11 @@ class Planner {
var out = constraint.output
constraint.markUnsatisfied()
constraint.removeFromGraph()
var unsatisfied = removePropagateFrom(out)
var unsatisfied = @removePropagateFrom(out)
var strength = REQUIRED
while (true) {
for (u in unsatisfied) {
if (u.strength == strength) incrementalAdd(u)
if (u.strength == strength) @incrementalAdd(u)
}
strength = strength.nextWeaker
if (strength == WEAKEST) break
@ -500,7 +500,7 @@ class Planner {
// any constraint.
// Assume: [sources] are all satisfied.
makePlan(sources) {
var mark = newMark()
var mark = @newMark()
var plan = Plan.new()
var todo = sources
while (todo.count > 0) {
@ -508,7 +508,7 @@ class Planner {
if (constraint.output.mark != mark && constraint.inputsKnown(mark)) {
plan.addConstraint(constraint)
constraint.output.mark = mark
addConstraintsConsumingTo(constraint.output, todo)
@addConstraintsConsumingTo(constraint.output, todo)
}
}
return plan
@ -522,7 +522,7 @@ class Planner {
// if not in plan already and eligible for inclusion.
if (constraint.isInput && constraint.isSatisfied) sources.add(constraint)
}
return makePlan(sources)
return @makePlan(sources)
}
// Recompute the walkabout strengths and stay flags of all variables
@ -541,12 +541,12 @@ class Planner {
while (todo.count > 0) {
var d = todo.removeAt(-1)
if (d.output.mark == mark) {
incrementalRemove(constraint)
@incrementalRemove(constraint)
return false
}
d.recalculate()
addConstraintsConsumingTo(d.output, todo)
@addConstraintsConsumingTo(d.output, todo)
}
return true
@ -602,7 +602,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|
def chainTest(n) {
ThePlanner = Planner.new()
var prev = null
var first = null
@ -627,7 +627,7 @@ var chainTest = Fn.new {|n|
}
}
var change = Fn.new {|v, newValue|
def change(v, newValue) {
var edit = EditConstraint.new(v, PREFERRED)
var plan = ThePlanner.extractPlanFromConstraints([edit])
for (i in 0...10) {
@ -642,7 +642,7 @@ var change = Fn.new {|v, newValue|
// 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|
def projectionTest(n) {
ThePlanner = Planner.new()
var scale = Variable.new("scale", 10)
var offset = Variable.new("offset", 1000)
@ -658,22 +658,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")
@ -682,8 +682,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

@ -1,7 +1,7 @@
class Fib {
static get(n) {
if (n < 2) return n
return get(n - 1) + get(n - 2)
return @get(n - 1) + @get(n - 2)
}
}

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

@ -4,7 +4,7 @@ class Toggle {
}
value { _state }
activate {
activate() {
_state = !_state
return this
}
@ -17,10 +17,10 @@ class NthToggle is Toggle {
_count = 0
}
activate {
activate() {
_count = _count + 1
if (_count >= _countMax) {
super.activate
super.activate()
_count = 0
}
@ -34,16 +34,16 @@ var val = true
var toggle = Toggle.new(val)
for (i in 0...n) {
val = toggle.activate.value
val = toggle.activate.value
val = toggle.activate.value
val = toggle.activate.value
val = toggle.activate.value
val = toggle.activate.value
val = toggle.activate.value
val = toggle.activate.value
val = toggle.activate.value
val = toggle.activate.value
val = toggle.activate().value
val = toggle.activate().value
val = toggle.activate().value
val = toggle.activate().value
val = toggle.activate().value
val = toggle.activate().value
val = toggle.activate().value
val = toggle.activate().value
val = toggle.activate().value
val = toggle.activate().value
}
System.print(toggle.value)
@ -52,16 +52,16 @@ val = true
var ntoggle = NthToggle.new(val, 3)
for (i in 0...n) {
val = ntoggle.activate.value
val = ntoggle.activate.value
val = ntoggle.activate.value
val = ntoggle.activate.value
val = ntoggle.activate.value
val = ntoggle.activate.value
val = ntoggle.activate.value
val = ntoggle.activate.value
val = ntoggle.activate.value
val = ntoggle.activate.value
val = ntoggle.activate().value
val = ntoggle.activate().value
val = ntoggle.activate().value
val = ntoggle.activate().value
val = ntoggle.activate().value
val = ntoggle.activate().value
val = ntoggle.activate().value
val = ntoggle.activate().value
val = ntoggle.activate().value
val = ntoggle.activate().value
}
System.print(ntoggle.value)

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

@ -4,6 +4,6 @@ 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
System.print("after") // expect: after
System.print("before") // expect: before
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,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.transfer() // expect runtime error: Cannot transfer to a finished fiber.
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.transfer() // expect: null
// expect: transferred
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() // 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
System.print("main 1") // expect: main 1
fiber.call() // expect: fiber 2
System.print("main 2") // expect: main 2
fiber.call() // expect: fiber 3
System.print("main 3") // expect: main 3
fiber() // expect: fiber 1
System.print("main 1") // expect: main 1
fiber() // expect: fiber 2
System.print("main 2") // expect: main 2
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
// expect: yield 1
System.print(fiber.call()) // expect: fiber 2
// expect: yield 2
System.print(fiber.call()) // expect: fiber 3
// expect: null
System.print(fiber()) // expect: fiber 1
// expect: yield 1
System.print(fiber()) // expect: fiber 2
// expect: yield 2
System.print(fiber()) // expect: fiber 3
// expect: null

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)") }
def f0() { System.print("zero") }
def f1(a) { System.print("one %(a)") }
def f2(a, b) { System.print("two %(a) %(b)") }
def 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.
def f2(a, b) { System.print(a + b) }
f2("a") // expect runtime error: Function expects more arguments.

View File

@ -11,6 +11,6 @@ System.print(Fn.new { 123 } != false) // expect: true
System.print(Fn.new { 123 } != "fn 123") // expect: true
// Equal by identity.
var f = Fn.new { 123 }
def f() { 123 }
System.print(f == f) // expect: true
System.print(f != f) // expect: false

View File

@ -1,14 +1,7 @@
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

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

View File

@ -1 +1,2 @@
unknown = "what" // expect error
unknown = "what"
// expect error

View File

@ -5,5 +5,5 @@ for (i in [1, 2, 3]) {
break
}
f.call()
f()
// expect: 5

View File

@ -5,5 +5,5 @@ while (true) {
break
}
f.call()
f()
// expect: i

View File

@ -1,17 +0,0 @@
class foo {
construct new() {}
static callFoo {
System.print(foo)
}
callFoo {
System.print(foo)
}
foo { "instance foo method" }
static foo { "static foo method" }
}
foo.callFoo // expect: static foo method
foo.new().callFoo // expect: instance foo method

View File

@ -16,10 +16,10 @@ var g = null
}
}
f.call()
f()
// expect: local
// expect: after f
g.call()
g()
// expect: after f
// expect: after g

View File

@ -4,6 +4,6 @@ Fn.new {|param|
f = Fn.new {
System.print(param)
}
}.call("param")
}("param")
f.call() // expect: param
f() // expect: param

View File

@ -9,5 +9,5 @@ Fn.new {
Fn.new {
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.new {
System.print(param)
}
}
}
Foo.new().method("param")
F.call() // expect: param
f() // expect: param

View File

@ -7,4 +7,4 @@ var f = null
}
}
f.call() // expect: local
f() // expect: local

View File

@ -11,11 +11,11 @@ Fn.new {
System.print(b)
System.print(c)
}
}.call()
}.call()
}.call()
}()
}()
}()
f.call()
f()
// expect: a
// expect: b
// expect: c

View File

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

View File

@ -8,6 +8,6 @@ var f = null
}
}
f.call()
f()
// expect: a
// expect: a

View File

@ -10,7 +10,7 @@
// 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

@ -7,5 +7,5 @@
System.print(foo) // expect: shadow
}
System.print(foo) // expect: closure
}.call()
}()
}

View File

@ -15,5 +15,5 @@ var closure
if (false) Fn.new { b }
}
System.print(closure.call()) // expect: a
System.print(closure()) // expect: a
}

View File

@ -1,5 +1,5 @@
class Foo {
this new { // expect error
construct new { // expect error
System.print("ok")
}
}

View File

@ -0,0 +1,2 @@
def f() {}
System.print(f()) // expect: null

View File

@ -0,0 +1,4 @@
def f() {
// Hi.
}
System.print(f()) // expect: null

View File

@ -0,0 +1,4 @@
def f() { System.print("ok") // expect error
}
f()

View File

@ -0,0 +1,2 @@
def f() {
System.print("ok") } // expect error

View File

@ -0,0 +1,50 @@
def f0() { 0 }
System.print(f0()) // expect: 0
def f1(a) { a }
System.print(f1(1)) // expect: 1
def f2(a, b) { a + b }
System.print(f2(1, 2)) // expect: 3
def f3(a, b, c) { a + b + c }
System.print(f3(1, 2, 3)) // expect: 6
def f4(a, b, c, d) { a + b + c + d }
System.print(f4(1, 2, 3, 4)) // expect: 10
def f5(a, b, c, d, e) { a + b + c + d + e }
System.print(f5(1, 2, 3, 4, 5)) // expect: 15
def f6(a, b, c, d, e, f) { a + b + c + d + e + f }
System.print(f6(1, 2, 3, 4, 5, 6)) // expect: 21
def 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
def 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
def 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
def 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
def 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
def 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
def 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
def 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
def 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
def 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,29 @@
// Single expression body.
def f() { System.print("ok") } // expect: ok
f()
// Curly body.
def g() {
System.print("ok") // expect: ok
}
g()
// Multiple statements.
def h() {
System.print("1") // expect: 1
System.print("2") // expect: 2
}
h()
// Extra newlines.
def i() {
System.print("1") // expect: 1
System.print("2") // expect: 2
}
i()

View File

@ -17,9 +17,9 @@ var closure
}
}
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 {
closeOverGet() {
return Fn.new { _field }
}
closeOverSet {
closeOverSet() {
return Fn.new { _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

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

View File

@ -5,7 +5,7 @@ for (i in [1, 2, 3]) {
list.add(Fn.new { 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 {
def 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 {
def f() {
for (i in [1, 2, 3]) {
return Fn.new { 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 {
def f() {
for (i in [1, 2, 3]) {
return i
}
}
System.print(f.call())
System.print(f())
// expect: 1

View File

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

View File

@ -1,4 +1,4 @@
var f = Fn.new {
// 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()
}()

View File

@ -1,50 +1,50 @@
var f0 = Fn.new { 0 }
System.print(f0.call()) // expect: 0
System.print(f0()) // expect: 0
var f1 = Fn.new {|a| a }
System.print(f1.call(1)) // expect: 1
System.print(f1(1)) // expect: 1
var f2 = Fn.new {|a, b| a + b }
System.print(f2.call(1, 2)) // expect: 3
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
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
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
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
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
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
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
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
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
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
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
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
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
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
System.print(f16(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)) // expect: 136

View File

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

View File

@ -0,0 +1,11 @@
Fn.new {
def f1(arg) { System.print(arg) }
f1("string") // expect: string
def f2(block) { System.print(block()) }
f2 { "block" } // expect: block
def f3(a, b, c) { System.print("%(a) %(b) %(c())") }
f3(1, 2) { 3 } // expect: 1 2 3
}()

View File

@ -0,0 +1,8 @@
def f1(arg) { System.print(arg) }
f1("string") // expect: string
def f2(block) { System.print(block()) }
f2 { "block" } // expect: block
def f3(a, b, c) { System.print("%(a) %(b) %(c())") }
f3(1, 2) { 3 } // expect: 1 2 3

View File

@ -0,0 +1,27 @@
class CallArg {
static test() {
this("string") // expect: string
}
static call(arg) { System.print(arg) }
}
class CallBlock {
static test() {
this { "block" } // expect: block
}
static call(block) { System.print(block()) }
}
class CallBoth {
static test() {
this(1, 2) { 3 } // expect: 1 2 3
}
static call(a, b, c) { System.print("%(a) %(b) %(c())") }
}
CallArg.test()
CallBlock.test()
CallBoth.test()

View File

@ -1,25 +0,0 @@
class Foo {
construct new() {}
getter {
System.print("getter")
}
setter=(value) {
System.print("setter")
}
method(a) {
System.print("method")
}
test {
getter // expect: getter
setter = "value" // expect: setter
method("arg") // expect: method
}
}
Foo.new().test
// TODO: Need to decide how these interact with globals.

View File

@ -1,19 +0,0 @@
class Foo {
construct new() {}
bar { "getter" }
test {
System.print(bar) // expect: getter
{
System.print(bar) // expect: getter
var bar = "local"
System.print(bar) // expect: local
}
System.print(bar) // expect: getter
}
}
Foo.new().test

View File

@ -1,22 +0,0 @@
class Foo {
construct new() {}
bar=(value) {
System.print("setter")
return value
}
test {
bar = "value" // expect: setter
{
bar = "value" // expect: setter
var bar = "local"
bar = "value" // no expectation
}
bar = "value" // expect: setter
}
}
Foo.new().test

View File

@ -1,51 +0,0 @@
class Outer {
construct new() {}
getter {
System.print("outer getter")
}
setter=(value) {
System.print("outer setter")
}
method(a) {
System.print("outer method")
}
test {
getter // expect: outer getter
setter = "value" // expect: outer setter
method("arg") // expect: outer method
class Inner {
construct new() {}
getter {
System.print("inner getter")
}
setter=(value) {
System.print("inner setter")
}
method(a) {
System.print("inner method")
}
test {
getter // expect: inner getter
setter = "value" // expect: inner setter
method("arg") // expect: inner method
}
}
Inner.new().test
getter // expect: outer getter
setter = "value" // expect: outer setter
method("arg") // expect: outer method
}
}
Outer.new().test

View File

@ -1,11 +1,11 @@
class Foo {
construct new() { _field = "Foo field" }
closeOverFooGet {
closeOverFooGet() {
return Fn.new { Fn.new { _field } }
}
closeOverFooSet {
closeOverFooSet() {
return Fn.new { Fn.new { _field = "new foo value" } }
}
}
@ -16,21 +16,21 @@ class Bar is Foo {
_field = "Bar field"
}
closeOverBarGet {
closeOverBarGet() {
return Fn.new { Fn.new { _field } }
}
closeOverBarSet {
closeOverBarSet() {
return Fn.new { Fn.new { _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

View File

@ -3,6 +3,6 @@ var fiber = Fiber.new {
Fiber.yield("result")
}
System.print("outer %(fiber.call()) string")
System.print("outer %(fiber()) string")
// expect: in fiber
// expect: outer result string

View File

@ -0,0 +1,18 @@
var variable = "before"
System.print(variable) // expect: before
variable = "after"
System.print(variable) // expect: after
class Foo {
static method {
variable = "method"
}
}
Foo.method
System.print(variable) // expect: method
Fn.new {
variable = "fn"
}()
System.print(variable) // expect: fn

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