mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-18 13:49:59 +01:00
Allow "@method()" syntax for explicit self sends, like CoffeeScript.
This commit is contained in:
@ -69,6 +69,7 @@ typedef enum
|
|||||||
TOKEN_BANG,
|
TOKEN_BANG,
|
||||||
TOKEN_TILDE,
|
TOKEN_TILDE,
|
||||||
TOKEN_QUESTION,
|
TOKEN_QUESTION,
|
||||||
|
TOKEN_AT,
|
||||||
TOKEN_EQ,
|
TOKEN_EQ,
|
||||||
TOKEN_LT,
|
TOKEN_LT,
|
||||||
TOKEN_GT,
|
TOKEN_GT,
|
||||||
@ -886,6 +887,7 @@ static void nextToken(Parser* parser)
|
|||||||
case '-': makeToken(parser, TOKEN_MINUS); return;
|
case '-': makeToken(parser, TOKEN_MINUS); return;
|
||||||
case '~': makeToken(parser, TOKEN_TILDE); return;
|
case '~': makeToken(parser, TOKEN_TILDE); return;
|
||||||
case '?': makeToken(parser, TOKEN_QUESTION); 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_PIPEPIPE, TOKEN_PIPE); return;
|
||||||
case '&': twoCharToken(parser, '&', TOKEN_AMPAMP, TOKEN_AMP); return;
|
case '&': twoCharToken(parser, '&', TOKEN_AMPAMP, TOKEN_AMP); return;
|
||||||
@ -1996,6 +1998,17 @@ static ClassCompiler* getEnclosingClass(Compiler* compiler)
|
|||||||
return compiler == NULL ? NULL : compiler->enclosingClass;
|
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)
|
static void field(Compiler* compiler, bool allowAssignment)
|
||||||
{
|
{
|
||||||
// Initialize it with a fake value so we can keep parsing and minimize the
|
// Initialize it with a fake value so we can keep parsing and minimize the
|
||||||
@ -2280,12 +2293,7 @@ static void super_(Compiler* compiler, bool allowAssignment)
|
|||||||
|
|
||||||
static void this_(Compiler* compiler, bool allowAssignment)
|
static void this_(Compiler* compiler, bool allowAssignment)
|
||||||
{
|
{
|
||||||
if (getEnclosingClass(compiler) == NULL)
|
if (!ensureInsideClass(compiler, "this")) return;
|
||||||
{
|
|
||||||
error(compiler, "Cannot use 'this' outside of a method.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadThis(compiler);
|
loadThis(compiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2366,6 +2374,18 @@ static void conditional(Compiler* compiler, bool allowAssignment)
|
|||||||
patchJump(compiler, elseJump);
|
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)
|
void infixOp(Compiler* compiler, bool allowAssignment)
|
||||||
{
|
{
|
||||||
GrammarRule* rule = getRule(compiler->parser->previous.type);
|
GrammarRule* rule = getRule(compiler->parser->previous.type);
|
||||||
@ -2557,6 +2577,7 @@ GrammarRule rules[] =
|
|||||||
/* TOKEN_BANG */ PREFIX_OPERATOR("!"),
|
/* TOKEN_BANG */ PREFIX_OPERATOR("!"),
|
||||||
/* TOKEN_TILDE */ PREFIX_OPERATOR("~"),
|
/* TOKEN_TILDE */ PREFIX_OPERATOR("~"),
|
||||||
/* TOKEN_QUESTION */ INFIX(PREC_ASSIGNMENT, conditional),
|
/* TOKEN_QUESTION */ INFIX(PREC_ASSIGNMENT, conditional),
|
||||||
|
/* TOKEN_AT */ PREFIX(thisCall),
|
||||||
/* TOKEN_EQ */ UNUSED,
|
/* TOKEN_EQ */ UNUSED,
|
||||||
/* TOKEN_LT */ INFIX_OPERATOR(PREC_COMPARISON, "<"),
|
/* TOKEN_LT */ INFIX_OPERATOR(PREC_COMPARISON, "<"),
|
||||||
/* TOKEN_GT */ INFIX_OPERATOR(PREC_COMPARISON, ">"),
|
/* TOKEN_GT */ INFIX_OPERATOR(PREC_COMPARISON, ">"),
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
class Foo {
|
class Foo {
|
||||||
this new { // expect error
|
construct new { // expect error
|
||||||
System.print("ok")
|
System.print("ok")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
test/language/self_call/ignores_locals.wren
Normal file
12
test/language/self_call/ignores_locals.wren
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
class Foo {
|
||||||
|
construct new() {}
|
||||||
|
|
||||||
|
bar { "getter" }
|
||||||
|
|
||||||
|
test() {
|
||||||
|
var bar = "local"
|
||||||
|
System.print(@bar) // expect: getter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Foo.new().test()
|
||||||
13
test/language/self_call/in_closure.wren
Normal file
13
test/language/self_call/in_closure.wren
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
class Foo {
|
||||||
|
construct new(field) {
|
||||||
|
_field = field
|
||||||
|
}
|
||||||
|
|
||||||
|
method() {
|
||||||
|
System.print(_field)
|
||||||
|
}
|
||||||
|
|
||||||
|
makeClosure() { Fn.new { @method() } }
|
||||||
|
}
|
||||||
|
|
||||||
|
Foo.new("value").makeClosure().call() // expect: value
|
||||||
27
test/language/self_call/inherited_methods.wren
Normal file
27
test/language/self_call/inherited_methods.wren
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
class Foo {
|
||||||
|
construct new() {}
|
||||||
|
|
||||||
|
getter {
|
||||||
|
System.print("getter")
|
||||||
|
}
|
||||||
|
|
||||||
|
setter=(value) {
|
||||||
|
System.print("setter")
|
||||||
|
}
|
||||||
|
|
||||||
|
method(a) {
|
||||||
|
System.print("method")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Bar is Foo {
|
||||||
|
construct new() {}
|
||||||
|
|
||||||
|
test() {
|
||||||
|
@getter // expect: getter
|
||||||
|
@setter = "value" // expect: setter
|
||||||
|
@method("arg") // expect: method
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Bar.new().test()
|
||||||
25
test/language/self_call/instance_methods.wren
Normal file
25
test/language/self_call/instance_methods.wren
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
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: Operators.
|
||||||
51
test/language/self_call/nested_class.wren
Normal file
51
test/language/self_call/nested_class.wren
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
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()
|
||||||
1
test/language/self_call/outside_class.wren
Normal file
1
test/language/self_call/outside_class.wren
Normal file
@ -0,0 +1 @@
|
|||||||
|
@method() // expect error
|
||||||
21
test/language/self_call/static_methods.wren
Normal file
21
test/language/self_call/static_methods.wren
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
class Foo {
|
||||||
|
static getter {
|
||||||
|
System.print("getter")
|
||||||
|
}
|
||||||
|
|
||||||
|
static setter=(value) {
|
||||||
|
System.print("setter")
|
||||||
|
}
|
||||||
|
|
||||||
|
static method(a) {
|
||||||
|
System.print("method")
|
||||||
|
}
|
||||||
|
|
||||||
|
static test() {
|
||||||
|
@getter // expect: getter
|
||||||
|
@setter = "value" // expect: setter
|
||||||
|
@method("arg") // expect: method
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Foo.test()
|
||||||
@ -1,8 +1,8 @@
|
|||||||
class Foo {
|
class Foo {
|
||||||
construct new() {}
|
construct new() {}
|
||||||
getClosure { Fn.new { toString } }
|
getClosure() { Fn.new { toString } }
|
||||||
toString { "Foo" }
|
toString { "Foo" }
|
||||||
}
|
}
|
||||||
|
|
||||||
var closure = Foo.new().getClosure
|
var closure = Foo.new().getClosure()
|
||||||
System.print(closure.call()) // expect: Foo
|
System.print(closure.call()) // expect: Foo
|
||||||
|
|||||||
Reference in New Issue
Block a user