mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-11 14:18:42 +01:00
Compare commits
9 Commits
0.4.0
...
unify-modu
| Author | SHA1 | Date | |
|---|---|---|---|
| 37800d441c | |||
| d22c5ec737 | |||
| c472a61bff | |||
| 8ff11a3c2c | |||
| 8ed0cde91c | |||
| 969ee0adc4 | |||
| eff4485a56 | |||
| 859a51e22d | |||
| 7f4c5f021d |
@ -1,4 +1,4 @@
|
||||
class Cthulu {
|
||||
construct new() {}
|
||||
message { "Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn" }
|
||||
def message { "Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn" }
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import "cthulu" for Cthulu
|
||||
|
||||
class Lovecraft {
|
||||
construct new() {}
|
||||
say() { Cthulu.new().message }
|
||||
def say() { Cthulu.new().message }
|
||||
}
|
||||
|
||||
System.print(Lovecraft.new().say())
|
||||
|
||||
@ -32,7 +32,7 @@ class SyntaxExample {
|
||||
}
|
||||
|
||||
// Method without arguments
|
||||
variables {
|
||||
def variables {
|
||||
// Valid local variable names.
|
||||
var hi
|
||||
var camelCase
|
||||
@ -42,54 +42,54 @@ class SyntaxExample {
|
||||
}
|
||||
|
||||
// Method with empty argument list
|
||||
fields() {
|
||||
def fields() {
|
||||
// Fields
|
||||
_under_score = 1
|
||||
_field = 2
|
||||
}
|
||||
|
||||
// Static method with single argument
|
||||
static fields(a) {
|
||||
static def fields(a) {
|
||||
// Static field
|
||||
__a = a
|
||||
}
|
||||
|
||||
// Setter
|
||||
field=(value) { _field = value }
|
||||
def field=(value) { _field = value }
|
||||
|
||||
// Method with arguments
|
||||
print(a, b) { System.print(a + b) }
|
||||
def print(a, b) { System.print(a + b) }
|
||||
|
||||
// Operators
|
||||
+(other) { "infix + %(other)" }
|
||||
-(other) { "infix - %(other)" }
|
||||
*(other) { "infix * %(other)" }
|
||||
/(other) { "infix / %(other)" }
|
||||
%(other) { "infix \% %(other)" }
|
||||
<(other) { "infix < %(other)" }
|
||||
>(other) { "infix > %(other)" }
|
||||
<=(other) { "infix <= %(other)" }
|
||||
>=(other) { "infix >= %(other)" }
|
||||
==(other) { "infix == %(other)" }
|
||||
!=(other) { "infix != %(other)" }
|
||||
&(other) { "infix & %(other)" }
|
||||
|(other) { "infix | %(other)" }
|
||||
def +(other) { "infix + %(other)" }
|
||||
def -(other) { "infix - %(other)" }
|
||||
def *(other) { "infix * %(other)" }
|
||||
def /(other) { "infix / %(other)" }
|
||||
def %(other) { "infix \% %(other)" }
|
||||
def <(other) { "infix < %(other)" }
|
||||
def >(other) { "infix > %(other)" }
|
||||
def <=(other) { "infix <= %(other)" }
|
||||
def >=(other) { "infix >= %(other)" }
|
||||
def ==(other) { "infix == %(other)" }
|
||||
def !=(other) { "infix != %(other)" }
|
||||
def &(other) { "infix & %(other)" }
|
||||
def |(other) { "infix | %(other)" }
|
||||
|
||||
! { "prefix !" }
|
||||
~ { "prefix ~" }
|
||||
- { "prefix -" }
|
||||
def ! { "prefix !" }
|
||||
def ~ { "prefix ~" }
|
||||
def - { "prefix -" }
|
||||
}
|
||||
|
||||
// `class`, `is`
|
||||
class ReservedWords is SyntaxExample {
|
||||
reserved {
|
||||
def reserved {
|
||||
// `super`, `true`, `false`
|
||||
super(true, false)
|
||||
// `this`
|
||||
this.foo
|
||||
}
|
||||
|
||||
foo {
|
||||
def foo {
|
||||
// `var`
|
||||
var n = 27
|
||||
// `while`, `if`, `else`
|
||||
@ -106,7 +106,7 @@ class ReservedWords is SyntaxExample {
|
||||
return null
|
||||
}
|
||||
|
||||
imports {
|
||||
def imports {
|
||||
// `import`
|
||||
import "hello"
|
||||
// `import`, `for`
|
||||
@ -120,9 +120,9 @@ class ReservedWords is SyntaxExample {
|
||||
}
|
||||
|
||||
class Literals is SyntaxExample {
|
||||
booleans { true || false }
|
||||
def booleans { true || false }
|
||||
|
||||
numbers {
|
||||
def numbers {
|
||||
0
|
||||
1234
|
||||
-5678
|
||||
@ -133,7 +133,7 @@ class Literals is SyntaxExample {
|
||||
0x1234567890ABCDEF
|
||||
}
|
||||
|
||||
strings {
|
||||
def strings {
|
||||
"hi there"
|
||||
// Escapes:
|
||||
"\0" // The NUL byte: 0.
|
||||
@ -152,20 +152,20 @@ class Literals is SyntaxExample {
|
||||
System.print("\x48\x69\x2e") // "Hi."
|
||||
}
|
||||
|
||||
ranges {
|
||||
def ranges {
|
||||
3..8 // inclusive
|
||||
4...6 // half-inclusive
|
||||
}
|
||||
|
||||
nothing { null }
|
||||
def nothing { null }
|
||||
|
||||
lists {
|
||||
def lists {
|
||||
var list = [1, "banana", true]
|
||||
list[0] = 5
|
||||
list[1..2]
|
||||
}
|
||||
|
||||
maps {
|
||||
def maps {
|
||||
var stringMap = {
|
||||
"George": "Harrison",
|
||||
"John": "Lennon",
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
import "scheduler" for Scheduler
|
||||
|
||||
class Directory {
|
||||
static list(path) {
|
||||
static def list(path) {
|
||||
if (!(path is String)) Fiber.abort("Path must be a string.")
|
||||
|
||||
list_(path, Fiber.current)
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
foreign static list_(path, fiber)
|
||||
foreign static def list_(path, fiber)
|
||||
}
|
||||
|
||||
foreign class File {
|
||||
static open(path) {
|
||||
static def open(path) {
|
||||
if (!(path is String)) Fiber.abort("Path must be a string.")
|
||||
|
||||
open_(path, Fiber.current)
|
||||
@ -20,7 +20,7 @@ foreign class File {
|
||||
return new_(fd)
|
||||
}
|
||||
|
||||
static open(path, fn) {
|
||||
static def open(path, fn) {
|
||||
var file = open(path)
|
||||
var fiber = Fiber.new { fn.call(file) }
|
||||
|
||||
@ -33,18 +33,18 @@ foreign class File {
|
||||
return result
|
||||
}
|
||||
|
||||
static read(path) {
|
||||
static def read(path) {
|
||||
return File.open(path) {|file| file.readBytes(file.size) }
|
||||
}
|
||||
|
||||
static size(path) {
|
||||
static def size(path) {
|
||||
if (!(path is String)) Fiber.abort("Path must be a string.")
|
||||
|
||||
sizePath_(path, Fiber.current)
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
static stat(path) {
|
||||
static def stat(path) {
|
||||
if (!(path is String)) Fiber.abort("Path must be a string.")
|
||||
|
||||
statPath_(path, Fiber.current)
|
||||
@ -53,25 +53,25 @@ foreign class File {
|
||||
|
||||
construct new_(fd) {}
|
||||
|
||||
close() {
|
||||
def close() {
|
||||
if (close_(Fiber.current)) return
|
||||
Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
foreign descriptor
|
||||
foreign def descriptor
|
||||
|
||||
isOpen { descriptor != -1 }
|
||||
def isOpen { descriptor != -1 }
|
||||
|
||||
size {
|
||||
def size {
|
||||
if (!isOpen) Fiber.abort("File is not open.")
|
||||
|
||||
size_(Fiber.current)
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
readBytes(count) { readBytes(count, 0) }
|
||||
def readBytes(count) { readBytes(count, 0) }
|
||||
|
||||
readBytes(count, offset) {
|
||||
def readBytes(count, offset) {
|
||||
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.")
|
||||
@ -85,13 +85,13 @@ foreign class File {
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
foreign static open_(path, fiber)
|
||||
foreign static sizePath_(path, fiber)
|
||||
foreign static statPath_(path, fiber)
|
||||
foreign static def open_(path, fiber)
|
||||
foreign static def sizePath_(path, fiber)
|
||||
foreign static def statPath_(path, fiber)
|
||||
|
||||
foreign close_(fiber)
|
||||
foreign readBytes_(count, start, fiber)
|
||||
foreign size_(fiber)
|
||||
foreign def close_(fiber)
|
||||
foreign def readBytes_(count, start, fiber)
|
||||
foreign def size_(fiber)
|
||||
}
|
||||
|
||||
class Stat {
|
||||
@ -99,20 +99,20 @@ class Stat {
|
||||
_fields = fields
|
||||
}
|
||||
|
||||
device { _fields[0] }
|
||||
inode { _fields[1] }
|
||||
mode { _fields[2] }
|
||||
linkCount { _fields[3] }
|
||||
user { _fields[4] }
|
||||
group { _fields[5] }
|
||||
specialDevice { _fields[6] }
|
||||
size { _fields[7] }
|
||||
blockSize { _fields[8] }
|
||||
blockCount { _fields[9] }
|
||||
def device { _fields[0] }
|
||||
def inode { _fields[1] }
|
||||
def mode { _fields[2] }
|
||||
def linkCount { _fields[3] }
|
||||
def user { _fields[4] }
|
||||
def group { _fields[5] }
|
||||
def specialDevice { _fields[6] }
|
||||
def size { _fields[7] }
|
||||
def blockSize { _fields[8] }
|
||||
def blockCount { _fields[9] }
|
||||
}
|
||||
|
||||
class Stdin {
|
||||
static readLine() {
|
||||
static def readLine() {
|
||||
if (__isClosed == true) {
|
||||
Fiber.abort("Stdin was closed.")
|
||||
}
|
||||
@ -127,7 +127,7 @@ class Stdin {
|
||||
return line
|
||||
}
|
||||
|
||||
static onData_(data) {
|
||||
static def onData_(data) {
|
||||
if (data == null) {
|
||||
__isClosed = true
|
||||
readStop_()
|
||||
@ -163,6 +163,6 @@ class Stdin {
|
||||
}
|
||||
}
|
||||
|
||||
foreign static readStart_()
|
||||
foreign static readStop_()
|
||||
foreign static def readStart_()
|
||||
foreign static def readStop_()
|
||||
}
|
||||
|
||||
@ -3,18 +3,18 @@ static const char* ioModuleSource =
|
||||
"import \"scheduler\" for Scheduler\n"
|
||||
"\n"
|
||||
"class Directory {\n"
|
||||
" static list(path) {\n"
|
||||
" static def list(path) {\n"
|
||||
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
|
||||
"\n"
|
||||
" list_(path, Fiber.current)\n"
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign static list_(path, fiber)\n"
|
||||
" foreign static def list_(path, fiber)\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"foreign class File {\n"
|
||||
" static open(path) {\n"
|
||||
" static def open(path) {\n"
|
||||
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
|
||||
"\n"
|
||||
" open_(path, Fiber.current)\n"
|
||||
@ -22,7 +22,7 @@ static const char* ioModuleSource =
|
||||
" return new_(fd)\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static open(path, fn) {\n"
|
||||
" static def open(path, fn) {\n"
|
||||
" var file = open(path)\n"
|
||||
" var fiber = Fiber.new { fn.call(file) }\n"
|
||||
"\n"
|
||||
@ -35,18 +35,18 @@ static const char* ioModuleSource =
|
||||
" return result\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static read(path) {\n"
|
||||
" static def read(path) {\n"
|
||||
" return File.open(path) {|file| file.readBytes(file.size) }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static size(path) {\n"
|
||||
" static def size(path) {\n"
|
||||
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
|
||||
"\n"
|
||||
" sizePath_(path, Fiber.current)\n"
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static stat(path) {\n"
|
||||
" static def stat(path) {\n"
|
||||
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
|
||||
"\n"
|
||||
" statPath_(path, Fiber.current)\n"
|
||||
@ -55,25 +55,25 @@ static const char* ioModuleSource =
|
||||
"\n"
|
||||
" construct new_(fd) {}\n"
|
||||
"\n"
|
||||
" close() {\n"
|
||||
" def close() {\n"
|
||||
" if (close_(Fiber.current)) return\n"
|
||||
" Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign descriptor\n"
|
||||
" foreign def descriptor\n"
|
||||
"\n"
|
||||
" isOpen { descriptor != -1 }\n"
|
||||
" def isOpen { descriptor != -1 }\n"
|
||||
"\n"
|
||||
" size {\n"
|
||||
" def size {\n"
|
||||
" if (!isOpen) Fiber.abort(\"File is not open.\")\n"
|
||||
"\n"
|
||||
" size_(Fiber.current)\n"
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" readBytes(count) { readBytes(count, 0) }\n"
|
||||
" def readBytes(count) { readBytes(count, 0) }\n"
|
||||
"\n"
|
||||
" readBytes(count, offset) {\n"
|
||||
" def readBytes(count, offset) {\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"
|
||||
@ -87,13 +87,13 @@ static const char* ioModuleSource =
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign static open_(path, fiber)\n"
|
||||
" foreign static sizePath_(path, fiber)\n"
|
||||
" foreign static statPath_(path, fiber)\n"
|
||||
" foreign static def open_(path, fiber)\n"
|
||||
" foreign static def sizePath_(path, fiber)\n"
|
||||
" foreign static def statPath_(path, fiber)\n"
|
||||
"\n"
|
||||
" foreign close_(fiber)\n"
|
||||
" foreign readBytes_(count, start, fiber)\n"
|
||||
" foreign size_(fiber)\n"
|
||||
" foreign def close_(fiber)\n"
|
||||
" foreign def readBytes_(count, start, fiber)\n"
|
||||
" foreign def size_(fiber)\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class Stat {\n"
|
||||
@ -101,20 +101,20 @@ static const char* ioModuleSource =
|
||||
" _fields = fields\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" device { _fields[0] }\n"
|
||||
" inode { _fields[1] }\n"
|
||||
" mode { _fields[2] }\n"
|
||||
" linkCount { _fields[3] }\n"
|
||||
" user { _fields[4] }\n"
|
||||
" group { _fields[5] }\n"
|
||||
" specialDevice { _fields[6] }\n"
|
||||
" size { _fields[7] }\n"
|
||||
" blockSize { _fields[8] }\n"
|
||||
" blockCount { _fields[9] }\n"
|
||||
" def device { _fields[0] }\n"
|
||||
" def inode { _fields[1] }\n"
|
||||
" def mode { _fields[2] }\n"
|
||||
" def linkCount { _fields[3] }\n"
|
||||
" def user { _fields[4] }\n"
|
||||
" def group { _fields[5] }\n"
|
||||
" def specialDevice { _fields[6] }\n"
|
||||
" def size { _fields[7] }\n"
|
||||
" def blockSize { _fields[8] }\n"
|
||||
" def blockCount { _fields[9] }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class Stdin {\n"
|
||||
" static readLine() {\n"
|
||||
" static def readLine() {\n"
|
||||
" if (__isClosed == true) {\n"
|
||||
" Fiber.abort(\"Stdin was closed.\")\n"
|
||||
" }\n"
|
||||
@ -129,7 +129,7 @@ static const char* ioModuleSource =
|
||||
" return line\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static onData_(data) {\n"
|
||||
" static def onData_(data) {\n"
|
||||
" if (data == null) {\n"
|
||||
" __isClosed = true\n"
|
||||
" readStop_()\n"
|
||||
@ -165,6 +165,6 @@ static const char* ioModuleSource =
|
||||
" }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign static readStart_()\n"
|
||||
" foreign static readStop_()\n"
|
||||
" foreign static def readStart_()\n"
|
||||
" foreign static def readStop_()\n"
|
||||
"}\n";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Scheduler {
|
||||
static add(callable) {
|
||||
static def add(callable) {
|
||||
if (__scheduled == null) __scheduled = []
|
||||
|
||||
__scheduled.add(Fiber.new {
|
||||
@ -9,11 +9,11 @@ class Scheduler {
|
||||
}
|
||||
|
||||
// Called by native code.
|
||||
static resume_(fiber) { fiber.transfer() }
|
||||
static resume_(fiber, arg) { fiber.transfer(arg) }
|
||||
static resumeError_(fiber, error) { fiber.transferError(error) }
|
||||
static def resume_(fiber) { fiber.transfer() }
|
||||
static def resume_(fiber, arg) { fiber.transfer(arg) }
|
||||
static def resumeError_(fiber, error) { fiber.transferError(error) }
|
||||
|
||||
static runNextScheduled_() {
|
||||
static def runNextScheduled_() {
|
||||
if (__scheduled == null || __scheduled.isEmpty) {
|
||||
return Fiber.suspend()
|
||||
} else {
|
||||
@ -21,7 +21,7 @@ class Scheduler {
|
||||
}
|
||||
}
|
||||
|
||||
foreign static captureMethods_()
|
||||
foreign static def captureMethods_()
|
||||
}
|
||||
|
||||
Scheduler.captureMethods_()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Generated automatically from src/module/scheduler.wren. Do not edit.
|
||||
static const char* schedulerModuleSource =
|
||||
"class Scheduler {\n"
|
||||
" static add(callable) {\n"
|
||||
" static def add(callable) {\n"
|
||||
" if (__scheduled == null) __scheduled = []\n"
|
||||
"\n"
|
||||
" __scheduled.add(Fiber.new {\n"
|
||||
@ -11,11 +11,11 @@ static const char* schedulerModuleSource =
|
||||
" }\n"
|
||||
"\n"
|
||||
" // Called by native code.\n"
|
||||
" static resume_(fiber) { fiber.transfer() }\n"
|
||||
" static resume_(fiber, arg) { fiber.transfer(arg) }\n"
|
||||
" static resumeError_(fiber, error) { fiber.transferError(error) }\n"
|
||||
" static def resume_(fiber) { fiber.transfer() }\n"
|
||||
" static def resume_(fiber, arg) { fiber.transfer(arg) }\n"
|
||||
" static def resumeError_(fiber, error) { fiber.transferError(error) }\n"
|
||||
"\n"
|
||||
" static runNextScheduled_() {\n"
|
||||
" static def runNextScheduled_() {\n"
|
||||
" if (__scheduled == null || __scheduled.isEmpty) {\n"
|
||||
" return Fiber.suspend()\n"
|
||||
" } else {\n"
|
||||
@ -23,7 +23,7 @@ static const char* schedulerModuleSource =
|
||||
" }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign static captureMethods_()\n"
|
||||
" foreign static def captureMethods_()\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"Scheduler.captureMethods_()\n";
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import "scheduler" for Scheduler
|
||||
|
||||
class Timer {
|
||||
static sleep(milliseconds) {
|
||||
static def sleep(milliseconds) {
|
||||
if (!(milliseconds is Num)) Fiber.abort("Milliseconds must be a number.")
|
||||
if (milliseconds < 0) Fiber.abort("Milliseconds cannot be negative.")
|
||||
|
||||
@ -9,5 +9,5 @@ class Timer {
|
||||
Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
foreign static startTimer_(milliseconds, fiber)
|
||||
foreign static def startTimer_(milliseconds, fiber)
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ static const char* timerModuleSource =
|
||||
"import \"scheduler\" for Scheduler\n"
|
||||
"\n"
|
||||
"class Timer {\n"
|
||||
" static sleep(milliseconds) {\n"
|
||||
" static def sleep(milliseconds) {\n"
|
||||
" if (!(milliseconds is Num)) Fiber.abort(\"Milliseconds must be a number.\")\n"
|
||||
" if (milliseconds < 0) Fiber.abort(\"Milliseconds cannot be negative.\")\n"
|
||||
"\n"
|
||||
@ -11,5 +11,5 @@ static const char* timerModuleSource =
|
||||
" Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign static startTimer_(milliseconds, fiber)\n"
|
||||
" foreign static def startTimer_(milliseconds, fiber)\n"
|
||||
"}\n";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Meta {
|
||||
static eval(source) {
|
||||
static def eval(source) {
|
||||
if (!(source is String)) Fiber.abort("Source code must be a string.")
|
||||
|
||||
var fn = compile_(source)
|
||||
@ -9,5 +9,5 @@ class Meta {
|
||||
Fiber.new(fn).call()
|
||||
}
|
||||
|
||||
foreign static compile_(source)
|
||||
foreign static def compile_(source)
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Generated automatically from src/optional/wren_opt_meta.wren. Do not edit.
|
||||
static const char* metaModuleSource =
|
||||
"class Meta {\n"
|
||||
" static eval(source) {\n"
|
||||
" static def eval(source) {\n"
|
||||
" if (!(source is String)) Fiber.abort(\"Source code must be a string.\")\n"
|
||||
"\n"
|
||||
" var fn = compile_(source)\n"
|
||||
@ -11,5 +11,5 @@ static const char* metaModuleSource =
|
||||
" Fiber.new(fn).call()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign static compile_(source)\n"
|
||||
" foreign static def compile_(source)\n"
|
||||
"}\n";
|
||||
|
||||
@ -35,15 +35,15 @@ foreign class Random {
|
||||
}
|
||||
}
|
||||
|
||||
foreign seed_()
|
||||
foreign seed_(seed)
|
||||
foreign seed_(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16)
|
||||
foreign def seed_()
|
||||
foreign def seed_(seed)
|
||||
foreign def 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 }
|
||||
foreign def float()
|
||||
def float(end) { float() * end }
|
||||
def float(start, end) { float() * (end - start) + start }
|
||||
|
||||
foreign int()
|
||||
int(end) { (float() * end).floor }
|
||||
int(start, end) { (float() * (end - start)).floor + start }
|
||||
foreign def int()
|
||||
def int(end) { (float() * end).floor }
|
||||
def int(start, end) { (float() * (end - start)).floor + start }
|
||||
}
|
||||
|
||||
@ -37,15 +37,15 @@ static const char* randomModuleSource =
|
||||
" }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign seed_()\n"
|
||||
" foreign seed_(seed)\n"
|
||||
" foreign seed_(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16)\n"
|
||||
" foreign def seed_()\n"
|
||||
" foreign def seed_(seed)\n"
|
||||
" foreign def 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"
|
||||
" foreign def float()\n"
|
||||
" def float(end) { float() * end }\n"
|
||||
" def 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"
|
||||
" foreign def int()\n"
|
||||
" def int(end) { (float() * end).floor }\n"
|
||||
" def int(start, end) { (float() * (end - start)).floor + start }\n"
|
||||
"}\n";
|
||||
|
||||
@ -80,6 +80,7 @@ typedef enum
|
||||
TOKEN_BREAK,
|
||||
TOKEN_CLASS,
|
||||
TOKEN_CONSTRUCT,
|
||||
TOKEN_DEF,
|
||||
TOKEN_ELSE,
|
||||
TOKEN_FALSE,
|
||||
TOKEN_FOR,
|
||||
@ -276,21 +277,7 @@ typedef struct
|
||||
int arity;
|
||||
} Signature;
|
||||
|
||||
// Bookkeeping information for compiling a class definition.
|
||||
typedef struct
|
||||
{
|
||||
// Symbol table for the fields of the class.
|
||||
SymbolTable fields;
|
||||
|
||||
// True if the class being compiled is a foreign class.
|
||||
bool isForeign;
|
||||
|
||||
// True if the current method being compiled is static.
|
||||
bool inStatic;
|
||||
|
||||
// The signature of the method being compiled.
|
||||
Signature* signature;
|
||||
} ClassCompiler;
|
||||
typedef struct sClassCompiler ClassCompiler;
|
||||
|
||||
struct sCompiler
|
||||
{
|
||||
@ -352,6 +339,28 @@ struct sCompiler
|
||||
IntBuffer debugSourceLines;
|
||||
};
|
||||
|
||||
// Bookkeeping information for compiling a class definition.
|
||||
struct sClassCompiler
|
||||
{
|
||||
// Symbol table for the fields of the class.
|
||||
SymbolTable fields;
|
||||
|
||||
// True if the class being compiled is a foreign class.
|
||||
bool isForeign;
|
||||
|
||||
// True if the current method being compiled is static.
|
||||
bool inStatic;
|
||||
|
||||
// The signature of the method being compiled.
|
||||
Signature* signature;
|
||||
|
||||
// The compiler used for body code.
|
||||
//
|
||||
// Any code inside a class body that is not a method definition will get
|
||||
// compiled into this function which is then called by each constructor.
|
||||
Compiler body;
|
||||
};
|
||||
|
||||
// The stack effect of each opcode. The index in the array is the opcode, and
|
||||
// the value is the stack effect of that instruction.
|
||||
static const int stackEffects[] = {
|
||||
@ -516,6 +525,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},
|
||||
@ -1434,6 +1444,22 @@ static void loadLocal(Compiler* compiler, int slot)
|
||||
emitByteArg(compiler, CODE_LOAD_LOCAL, slot);
|
||||
}
|
||||
|
||||
// Loads the receiver of the currently enclosing method. Correctly handles
|
||||
// functions defined inside methods.
|
||||
static void loadThis(Compiler* compiler)
|
||||
{
|
||||
Code loadInstruction;
|
||||
int index = resolveNonmodule(compiler, "this", 4, &loadInstruction);
|
||||
if (loadInstruction == CODE_LOAD_LOCAL)
|
||||
{
|
||||
loadLocal(compiler, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
emitByteArg(compiler, loadInstruction, index);
|
||||
}
|
||||
}
|
||||
|
||||
// Discards memory owned by [compiler].
|
||||
static void freeCompiler(Compiler* compiler)
|
||||
{
|
||||
@ -1614,31 +1640,6 @@ static bool finishBlock(Compiler* compiler)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parses a method or function body, after the initial "{" has been consumed.
|
||||
//
|
||||
// It [isInitializer] is `true`, this is the body of a constructor initializer.
|
||||
// In that case, this adds the code to ensure it returns `this`.
|
||||
static void finishBody(Compiler* compiler, bool isInitializer)
|
||||
{
|
||||
bool isExpressionBody = finishBlock(compiler);
|
||||
|
||||
if (isInitializer)
|
||||
{
|
||||
// If the initializer body evaluates to a value, discard it.
|
||||
if (isExpressionBody) emitOp(compiler, CODE_POP);
|
||||
|
||||
// The receiver is always stored in the first local slot.
|
||||
emitOp(compiler, CODE_LOAD_LOCAL_0);
|
||||
}
|
||||
else if (!isExpressionBody)
|
||||
{
|
||||
// Implicitly return null in statement bodies.
|
||||
emitOp(compiler, CODE_NULL);
|
||||
}
|
||||
|
||||
emitOp(compiler, CODE_RETURN);
|
||||
}
|
||||
|
||||
// The VM can only handle a certain number of parameters, so check that we
|
||||
// haven't exceeded that and give a usable error.
|
||||
static void validateNumParameters(Compiler* compiler, int numArgs)
|
||||
@ -1813,6 +1814,43 @@ static void callMethod(Compiler* compiler, int numArgs, const char* name,
|
||||
emitShortArg(compiler, (Code)(CODE_CALL_0 + numArgs), symbol);
|
||||
}
|
||||
|
||||
// Parses a method or function body, after the initial "{" has been consumed.
|
||||
//
|
||||
// It [isInitializer] is `true`, this is the body of a constructor initializer.
|
||||
// In that case, this adds the code to execute the code in the class body and
|
||||
// make the method return `this`.
|
||||
static void finishBody(Compiler* compiler, bool isInitializer)
|
||||
{
|
||||
if (isInitializer)
|
||||
{
|
||||
// Execute the class body code before the initializer code.
|
||||
loadThis(compiler);
|
||||
callMethod(compiler, 0, "<body>", 6);
|
||||
emitOp(compiler, CODE_POP);
|
||||
// TODO: This regresses performance on classes that don't have any code in
|
||||
// their body and where calling this is a no-op. See if there's a way we
|
||||
// can elide the call when not needed.
|
||||
}
|
||||
|
||||
bool isExpressionBody = finishBlock(compiler);
|
||||
|
||||
if (isInitializer)
|
||||
{
|
||||
// If the initializer body evaluates to a value, discard it.
|
||||
if (isExpressionBody) emitOp(compiler, CODE_POP);
|
||||
|
||||
// The receiver is always stored in the first local slot.
|
||||
emitOp(compiler, CODE_LOAD_LOCAL_0);
|
||||
}
|
||||
else if (!isExpressionBody)
|
||||
{
|
||||
// Implicitly return null in statement bodies.
|
||||
emitOp(compiler, CODE_NULL);
|
||||
}
|
||||
|
||||
emitOp(compiler, CODE_RETURN);
|
||||
}
|
||||
|
||||
// Compiles an (optional) argument list for a method call with [methodSignature]
|
||||
// and then calls it.
|
||||
static void methodCall(Compiler* compiler, Code instruction,
|
||||
@ -1913,22 +1951,6 @@ static void namedCall(Compiler* compiler, bool allowAssignment,
|
||||
}
|
||||
}
|
||||
|
||||
// Loads the receiver of the currently enclosing method. Correctly handles
|
||||
// functions defined inside methods.
|
||||
static void loadThis(Compiler* compiler)
|
||||
{
|
||||
Code loadInstruction;
|
||||
int index = resolveNonmodule(compiler, "this", 4, &loadInstruction);
|
||||
if (loadInstruction == CODE_LOAD_LOCAL)
|
||||
{
|
||||
loadLocal(compiler, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
emitByteArg(compiler, loadInstruction, index);
|
||||
}
|
||||
}
|
||||
|
||||
// Pushes the value for a module-level variable implicitly imported from core.
|
||||
static void loadCoreVariable(Compiler* compiler, const char* name)
|
||||
{
|
||||
@ -2042,14 +2064,19 @@ static ClassCompiler* getEnclosingClass(Compiler* compiler)
|
||||
return compiler == NULL ? NULL : compiler->enclosingClass;
|
||||
}
|
||||
|
||||
static void field(Compiler* compiler, bool allowAssignment)
|
||||
// Determines the field index in the current enclosing class for a field with
|
||||
// [name].
|
||||
//
|
||||
// Implicitly defines the field if this is the first time it's used. Reports an
|
||||
// error and returns `255` if the field cannot be defined.
|
||||
static int findField(Compiler* compiler, const char* name, int length)
|
||||
{
|
||||
// Initialize it with a fake value so we can keep parsing and minimize the
|
||||
// number of cascaded errors.
|
||||
int field = 255;
|
||||
|
||||
|
||||
ClassCompiler* enclosingClass = getEnclosingClass(compiler);
|
||||
|
||||
|
||||
if (enclosingClass == NULL)
|
||||
{
|
||||
error(compiler, "Cannot reference a field outside of a class definition.");
|
||||
@ -2066,37 +2093,67 @@ static void field(Compiler* compiler, bool allowAssignment)
|
||||
{
|
||||
// Look up the field, or implicitly define it.
|
||||
field = wrenSymbolTableEnsure(compiler->parser->vm, &enclosingClass->fields,
|
||||
compiler->parser->previous.start,
|
||||
compiler->parser->previous.length);
|
||||
|
||||
name, length);
|
||||
|
||||
if (field >= MAX_FIELDS)
|
||||
{
|
||||
error(compiler, "A class can only have %d fields.", MAX_FIELDS);
|
||||
}
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
static void loadField(Compiler* compiler, int field)
|
||||
{
|
||||
// TODO: Is != NULL check right here?
|
||||
// If we're directly inside a method, use a more optimal instruction.
|
||||
if (compiler->parent != NULL &&
|
||||
compiler->parent->enclosingClass != NULL)
|
||||
{
|
||||
emitByteArg(compiler, CODE_LOAD_FIELD_THIS, field);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadThis(compiler);
|
||||
emitByteArg(compiler, CODE_LOAD_FIELD, field);
|
||||
}
|
||||
}
|
||||
|
||||
static void storeField(Compiler* compiler, int field)
|
||||
{
|
||||
// TODO: Is != NULL check right here?
|
||||
// If we're directly inside a method, use a more optimal instruction.
|
||||
if (compiler->parent != NULL &&
|
||||
compiler->parent->enclosingClass != NULL)
|
||||
{
|
||||
emitByteArg(compiler, CODE_STORE_FIELD_THIS, field);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadThis(compiler);
|
||||
emitByteArg(compiler, CODE_STORE_FIELD, field);
|
||||
}
|
||||
}
|
||||
|
||||
static void field(Compiler* compiler, bool allowAssignment)
|
||||
{
|
||||
int field = findField(compiler,
|
||||
compiler->parser->previous.start,
|
||||
compiler->parser->previous.length);
|
||||
|
||||
// If there's an "=" after a field name, it's an assignment.
|
||||
bool isLoad = true;
|
||||
if (match(compiler, TOKEN_EQ))
|
||||
{
|
||||
if (!allowAssignment) error(compiler, "Invalid assignment.");
|
||||
|
||||
// Compile the right-hand side.
|
||||
expression(compiler);
|
||||
isLoad = false;
|
||||
}
|
||||
|
||||
// If we're directly inside a method, use a more optimal instruction.
|
||||
if (compiler->parent != NULL &&
|
||||
compiler->parent->enclosingClass == enclosingClass)
|
||||
{
|
||||
emitByteArg(compiler, isLoad ? CODE_LOAD_FIELD_THIS : CODE_STORE_FIELD_THIS,
|
||||
field);
|
||||
storeField(compiler, field);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadThis(compiler);
|
||||
emitByteArg(compiler, isLoad ? CODE_LOAD_FIELD : CODE_STORE_FIELD, field);
|
||||
loadField(compiler, field);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2612,7 +2669,8 @@ GrammarRule rules[] =
|
||||
/* TOKEN_BANGEQ */ INFIX_OPERATOR(PREC_EQUALITY, "!="),
|
||||
/* TOKEN_BREAK */ UNUSED,
|
||||
/* TOKEN_CLASS */ UNUSED,
|
||||
/* TOKEN_CONSTRUCT */ { NULL, NULL, constructorSignature, PREC_NONE, NULL },
|
||||
/* TOKEN_CONSTRUCT */ UNUSED,
|
||||
/* TOKEN_DEF */ UNUSED,
|
||||
/* TOKEN_ELSE */ UNUSED,
|
||||
/* TOKEN_FALSE */ PREFIX(boolean),
|
||||
/* TOKEN_FOR */ UNUSED,
|
||||
@ -3125,14 +3183,22 @@ static void defineMethod(Compiler* compiler, int classSlot, bool isStatic,
|
||||
//
|
||||
// Returns `true` if it compiled successfully, or `false` if the method couldn't
|
||||
// be parsed.
|
||||
static bool method(Compiler* compiler, int classSlot)
|
||||
static bool method(Compiler* compiler, int classSlot, bool isForeign,
|
||||
bool isConstructor)
|
||||
{
|
||||
// TODO: What about foreign constructors?
|
||||
bool isForeign = match(compiler, TOKEN_FOREIGN);
|
||||
compiler->enclosingClass->inStatic = match(compiler, TOKEN_STATIC);
|
||||
|
||||
SignatureFn signatureFn = rules[compiler->parser->current.type].method;
|
||||
nextToken(compiler->parser);
|
||||
SignatureFn signatureFn;
|
||||
|
||||
// Methods are declared using "construct" for constructors or "def" for
|
||||
// everything else.
|
||||
if (isConstructor)
|
||||
{
|
||||
signatureFn = constructorSignature;
|
||||
}
|
||||
else
|
||||
{
|
||||
signatureFn = rules[compiler->parser->current.type].method;
|
||||
nextToken(compiler->parser);
|
||||
}
|
||||
|
||||
if (signatureFn == NULL)
|
||||
{
|
||||
@ -3196,9 +3262,102 @@ static bool method(Compiler* compiler, int classSlot)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Defines a synthetic getter method with [getterName] that returns the value
|
||||
// of the field with [fieldName].
|
||||
static void defineGetter(Compiler* compiler, int classSlot,
|
||||
const char* getterName, int getterLength,
|
||||
const char* fieldName, int fieldLength)
|
||||
{
|
||||
Compiler getterCompiler;
|
||||
initCompiler(&getterCompiler, compiler->parser, compiler, false);
|
||||
|
||||
int field = findField(compiler, fieldName, fieldLength);
|
||||
|
||||
// It loads and returns the corresponding field.
|
||||
loadField(&getterCompiler, field);
|
||||
emitOp(&getterCompiler, CODE_RETURN);
|
||||
endCompiler(&getterCompiler, getterName, getterLength);
|
||||
|
||||
// Define the method for it.
|
||||
int symbol = methodSymbol(compiler, getterName, getterLength);
|
||||
defineMethod(compiler, classSlot, compiler->enclosingClass->inStatic,
|
||||
symbol);
|
||||
}
|
||||
|
||||
static void property(Compiler* compiler, int classSlot)
|
||||
{
|
||||
consume(compiler, TOKEN_NAME, "Expect property name after 'var'.");
|
||||
|
||||
const char* propertyName = compiler->parser->previous.start;
|
||||
int propertyLength = compiler->parser->previous.length;
|
||||
|
||||
// Prepend "_" to make a field corresponding to the property.
|
||||
// TODO: Put limit on field length.
|
||||
char fieldName[256];
|
||||
fieldName[0] = '_';
|
||||
memcpy(&fieldName[1], propertyName, propertyLength);
|
||||
|
||||
defineGetter(compiler, classSlot, propertyName, propertyLength,
|
||||
fieldName, propertyLength + 1);
|
||||
|
||||
// Synthesize the setter.
|
||||
Compiler setterCompiler;
|
||||
initCompiler(&setterCompiler, compiler->parser, compiler, false);
|
||||
|
||||
// It stores and returns the corresponding field.
|
||||
int field = findField(compiler, fieldName, propertyLength + 1);
|
||||
storeField(&setterCompiler, field);
|
||||
emitOp(&setterCompiler, CODE_RETURN);
|
||||
endCompiler(&setterCompiler, propertyName, propertyLength);
|
||||
|
||||
// Define the method for it.
|
||||
Signature setterSignature = { propertyName, propertyLength, SIG_SETTER, 1 };
|
||||
int setterSymbol = signatureSymbol(compiler, &setterSignature);
|
||||
defineMethod(compiler, classSlot, compiler->enclosingClass->inStatic,
|
||||
setterSymbol);
|
||||
|
||||
// Compile the initializer into the class body if there is one.
|
||||
if (match(compiler, TOKEN_EQ))
|
||||
{
|
||||
ignoreNewlines(compiler);
|
||||
|
||||
expression(&compiler->enclosingClass->body);
|
||||
storeField(&compiler->enclosingClass->body, field);
|
||||
emitOp(&compiler->enclosingClass->body, CODE_POP);
|
||||
}
|
||||
}
|
||||
|
||||
static int classDefinition(Compiler* compiler, bool isForeign);
|
||||
|
||||
static void nestedClass(Compiler* compiler, int enclosingClassSlot)
|
||||
{
|
||||
// Create the class inside the enclosing class's body code.
|
||||
Compiler* bodyCompiler = &compiler->enclosingClass->body;
|
||||
int slot = classDefinition(bodyCompiler, false);
|
||||
|
||||
// TODO: Once modules are classes and class definitions can only occur inside
|
||||
// other classes (and not inside blocks), then a class will always be stored
|
||||
// in a field, and we can skip storing it in a variable.
|
||||
loadLocal(bodyCompiler, slot);
|
||||
|
||||
// Store the class in a field.
|
||||
// TODO: Should the field name have "_" before it like class variables do?
|
||||
// Do we want to allow methods in the class to assign to it?
|
||||
Local* local = &bodyCompiler->locals[slot];
|
||||
int field = findField(compiler, local->name, local->length);
|
||||
storeField(bodyCompiler, field);
|
||||
emitOp(bodyCompiler, CODE_POP);
|
||||
|
||||
// Create a getter that returns the class.
|
||||
defineGetter(compiler, enclosingClassSlot, local->name, local->length,
|
||||
local->name, local->length);
|
||||
}
|
||||
|
||||
// Compiles a class definition. Assumes the "class" token has already been
|
||||
// consumed (along with a possibly preceding "foreign" token).
|
||||
static void classDefinition(Compiler* compiler, bool isForeign)
|
||||
//
|
||||
// Returns the slot for the variable that the class is stored in.
|
||||
static int classDefinition(Compiler* compiler, bool isForeign)
|
||||
{
|
||||
// Create a variable to store the class in.
|
||||
int slot = declareNamedVariable(compiler);
|
||||
@ -3249,6 +3408,8 @@ static void classDefinition(Compiler* compiler, bool isForeign)
|
||||
wrenSymbolTableInit(&classCompiler.fields);
|
||||
|
||||
compiler->enclosingClass = &classCompiler;
|
||||
|
||||
initCompiler(&classCompiler.body, compiler->parser, compiler, false);
|
||||
|
||||
// Compile the method definitions.
|
||||
consume(compiler, TOKEN_LEFT_BRACE, "Expect '{' after class declaration.");
|
||||
@ -3256,14 +3417,61 @@ static void classDefinition(Compiler* compiler, bool isForeign)
|
||||
|
||||
while (!match(compiler, TOKEN_RIGHT_BRACE))
|
||||
{
|
||||
if (!method(compiler, slot)) break;
|
||||
// Determine if we're defining a method or executing code in the class body.
|
||||
// Methods can have a couple of different preambles using some combinations
|
||||
// of "foreign", "static", "construct", and "def".
|
||||
compiler->enclosingClass->inStatic = false;
|
||||
|
||||
if (match(compiler, TOKEN_FOREIGN))
|
||||
{
|
||||
compiler->enclosingClass->inStatic = match(compiler, TOKEN_STATIC);
|
||||
consume(compiler, TOKEN_DEF,
|
||||
"Expect 'def' before foreign method declaration.");
|
||||
method(compiler, slot, true, false);
|
||||
}
|
||||
else if (match(compiler, TOKEN_STATIC))
|
||||
{
|
||||
compiler->enclosingClass->inStatic = true;
|
||||
consume(compiler, TOKEN_DEF, "Expect 'def' after 'static'.");
|
||||
method(compiler, slot, false, false);
|
||||
}
|
||||
else if (match(compiler, TOKEN_DEF))
|
||||
{
|
||||
method(compiler, slot, false, false);
|
||||
}
|
||||
else if (match(compiler, TOKEN_CONSTRUCT))
|
||||
{
|
||||
method(compiler, slot, false, true);
|
||||
}
|
||||
else if (match(compiler, TOKEN_CLASS))
|
||||
{
|
||||
nestedClass(compiler, slot);
|
||||
}
|
||||
else if (match(compiler, TOKEN_VAR))
|
||||
{
|
||||
// TODO: Static properties.
|
||||
property(compiler, slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Any other code gets compiled into the body method.
|
||||
statement(&classCompiler.body);
|
||||
}
|
||||
|
||||
// Don't require a newline after the last definition.
|
||||
// Don't require a newline after the last definition or statement.
|
||||
if (match(compiler, TOKEN_RIGHT_BRACE)) break;
|
||||
|
||||
consumeLine(compiler, "Expect newline after definition in class.");
|
||||
}
|
||||
|
||||
// Define a method to contain all the code in the class body.
|
||||
// TODO: Better debug name including class name.
|
||||
emitOp(&classCompiler.body, CODE_NULL);
|
||||
emitOp(&classCompiler.body, CODE_RETURN);
|
||||
endCompiler(&classCompiler.body, "<body>", 6);
|
||||
int bodySymbol = methodSymbol(compiler, "<body>", 6);
|
||||
defineMethod(compiler, slot, false, bodySymbol);
|
||||
|
||||
// Update the class with the number of fields.
|
||||
if (!isForeign)
|
||||
{
|
||||
@ -3276,6 +3484,7 @@ static void classDefinition(Compiler* compiler, bool isForeign)
|
||||
compiler->enclosingClass = NULL;
|
||||
|
||||
popScope(compiler);
|
||||
return slot;
|
||||
}
|
||||
|
||||
// Compiles an "import" statement.
|
||||
|
||||
@ -5,7 +5,7 @@ class Null {}
|
||||
class Num {}
|
||||
|
||||
class Sequence {
|
||||
all(f) {
|
||||
def all(f) {
|
||||
var result = true
|
||||
for (element in this) {
|
||||
result = f.call(element)
|
||||
@ -14,7 +14,7 @@ class Sequence {
|
||||
return result
|
||||
}
|
||||
|
||||
any(f) {
|
||||
def any(f) {
|
||||
var result = false
|
||||
for (element in this) {
|
||||
result = f.call(element)
|
||||
@ -23,14 +23,14 @@ class Sequence {
|
||||
return result
|
||||
}
|
||||
|
||||
contains(element) {
|
||||
def contains(element) {
|
||||
for (item in this) {
|
||||
if (element == item) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
count {
|
||||
def count {
|
||||
var result = 0
|
||||
for (element in this) {
|
||||
result = result + 1
|
||||
@ -38,7 +38,7 @@ class Sequence {
|
||||
return result
|
||||
}
|
||||
|
||||
count(f) {
|
||||
def count(f) {
|
||||
var result = 0
|
||||
for (element in this) {
|
||||
if (f.call(element)) result = result + 1
|
||||
@ -46,26 +46,26 @@ class Sequence {
|
||||
return result
|
||||
}
|
||||
|
||||
each(f) {
|
||||
def each(f) {
|
||||
for (element in this) {
|
||||
f.call(element)
|
||||
}
|
||||
}
|
||||
|
||||
isEmpty { iterate(null) ? false : true }
|
||||
def isEmpty { iterate(null) ? false : true }
|
||||
|
||||
map(transformation) { MapSequence.new(this, transformation) }
|
||||
def map(transformation) { MapSequence.new(this, transformation) }
|
||||
|
||||
where(predicate) { WhereSequence.new(this, predicate) }
|
||||
def where(predicate) { WhereSequence.new(this, predicate) }
|
||||
|
||||
reduce(acc, f) {
|
||||
def reduce(acc, f) {
|
||||
for (element in this) {
|
||||
acc = f.call(acc, element)
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
reduce(f) {
|
||||
def reduce(f) {
|
||||
var iter = iterate(null)
|
||||
if (!iter) Fiber.abort("Can't reduce an empty sequence.")
|
||||
|
||||
@ -78,9 +78,9 @@ class Sequence {
|
||||
return result
|
||||
}
|
||||
|
||||
join() { join("") }
|
||||
def join() { join("") }
|
||||
|
||||
join(sep) {
|
||||
def join(sep) {
|
||||
var first = true
|
||||
var result = ""
|
||||
|
||||
@ -93,7 +93,7 @@ class Sequence {
|
||||
return result
|
||||
}
|
||||
|
||||
toList {
|
||||
def toList {
|
||||
var result = List.new()
|
||||
for (element in this) {
|
||||
result.add(element)
|
||||
@ -108,8 +108,8 @@ class MapSequence is Sequence {
|
||||
_fn = fn
|
||||
}
|
||||
|
||||
iterate(iterator) { _sequence.iterate(iterator) }
|
||||
iteratorValue(iterator) { _fn.call(_sequence.iteratorValue(iterator)) }
|
||||
def iterate(iterator) { _sequence.iterate(iterator) }
|
||||
def iteratorValue(iterator) { _fn.call(_sequence.iteratorValue(iterator)) }
|
||||
}
|
||||
|
||||
class WhereSequence is Sequence {
|
||||
@ -118,19 +118,19 @@ class WhereSequence is Sequence {
|
||||
_fn = fn
|
||||
}
|
||||
|
||||
iterate(iterator) {
|
||||
def iterate(iterator) {
|
||||
while (iterator = _sequence.iterate(iterator)) {
|
||||
if (_fn.call(_sequence.iteratorValue(iterator))) break
|
||||
}
|
||||
return iterator
|
||||
}
|
||||
|
||||
iteratorValue(iterator) { _sequence.iteratorValue(iterator) }
|
||||
def iteratorValue(iterator) { _sequence.iteratorValue(iterator) }
|
||||
}
|
||||
|
||||
class String is Sequence {
|
||||
bytes { StringByteSequence.new(this) }
|
||||
codePoints { StringCodePointSequence.new(this) }
|
||||
def bytes { StringByteSequence.new(this) }
|
||||
def codePoints { StringCodePointSequence.new(this) }
|
||||
}
|
||||
|
||||
class StringByteSequence is Sequence {
|
||||
@ -138,11 +138,11 @@ class StringByteSequence is Sequence {
|
||||
_string = string
|
||||
}
|
||||
|
||||
[index] { _string.byteAt_(index) }
|
||||
iterate(iterator) { _string.iterateByte_(iterator) }
|
||||
iteratorValue(iterator) { _string.byteAt_(iterator) }
|
||||
def [index] { _string.byteAt_(index) }
|
||||
def iterate(iterator) { _string.iterateByte_(iterator) }
|
||||
def iteratorValue(iterator) { _string.byteAt_(iterator) }
|
||||
|
||||
count { _string.byteCount_ }
|
||||
def count { _string.byteCount_ }
|
||||
}
|
||||
|
||||
class StringCodePointSequence is Sequence {
|
||||
@ -150,24 +150,24 @@ class StringCodePointSequence is Sequence {
|
||||
_string = string
|
||||
}
|
||||
|
||||
[index] { _string.codePointAt_(index) }
|
||||
iterate(iterator) { _string.iterate(iterator) }
|
||||
iteratorValue(iterator) { _string.codePointAt_(iterator) }
|
||||
def [index] { _string.codePointAt_(index) }
|
||||
def iterate(iterator) { _string.iterate(iterator) }
|
||||
def iteratorValue(iterator) { _string.codePointAt_(iterator) }
|
||||
|
||||
count { _string.count }
|
||||
def count { _string.count }
|
||||
}
|
||||
|
||||
class List is Sequence {
|
||||
addAll(other) {
|
||||
def addAll(other) {
|
||||
for (element in other) {
|
||||
add(element)
|
||||
}
|
||||
return other
|
||||
}
|
||||
|
||||
toString { "[%(join(", "))]" }
|
||||
def toString { "[%(join(", "))]" }
|
||||
|
||||
+(other) {
|
||||
def +(other) {
|
||||
var result = this[0..-1]
|
||||
for (element in other) {
|
||||
result.add(element)
|
||||
@ -177,10 +177,10 @@ class List is Sequence {
|
||||
}
|
||||
|
||||
class Map {
|
||||
keys { MapKeySequence.new(this) }
|
||||
values { MapValueSequence.new(this) }
|
||||
def keys { MapKeySequence.new(this) }
|
||||
def values { MapValueSequence.new(this) }
|
||||
|
||||
toString {
|
||||
def toString {
|
||||
var first = true
|
||||
var result = "{"
|
||||
|
||||
@ -199,8 +199,8 @@ class MapKeySequence is Sequence {
|
||||
_map = map
|
||||
}
|
||||
|
||||
iterate(n) { _map.iterate_(n) }
|
||||
iteratorValue(iterator) { _map.keyIteratorValue_(iterator) }
|
||||
def iterate(n) { _map.iterate_(n) }
|
||||
def iteratorValue(iterator) { _map.keyIteratorValue_(iterator) }
|
||||
}
|
||||
|
||||
class MapValueSequence is Sequence {
|
||||
@ -208,38 +208,38 @@ class MapValueSequence is Sequence {
|
||||
_map = map
|
||||
}
|
||||
|
||||
iterate(n) { _map.iterate_(n) }
|
||||
iteratorValue(iterator) { _map.valueIteratorValue_(iterator) }
|
||||
def iterate(n) { _map.iterate_(n) }
|
||||
def iteratorValue(iterator) { _map.valueIteratorValue_(iterator) }
|
||||
}
|
||||
|
||||
class Range is Sequence {}
|
||||
|
||||
class System {
|
||||
static print() {
|
||||
static def print() {
|
||||
writeString_("\n")
|
||||
}
|
||||
|
||||
static print(obj) {
|
||||
static def print(obj) {
|
||||
writeObject_(obj)
|
||||
writeString_("\n")
|
||||
return obj
|
||||
}
|
||||
|
||||
static printAll(sequence) {
|
||||
static def printAll(sequence) {
|
||||
for (object in sequence) writeObject_(object)
|
||||
writeString_("\n")
|
||||
}
|
||||
|
||||
static write(obj) {
|
||||
static def write(obj) {
|
||||
writeObject_(obj)
|
||||
return obj
|
||||
}
|
||||
|
||||
static writeAll(sequence) {
|
||||
static def writeAll(sequence) {
|
||||
for (object in sequence) writeObject_(object)
|
||||
}
|
||||
|
||||
static writeObject_(obj) {
|
||||
static def writeObject_(obj) {
|
||||
var string = obj.toString
|
||||
if (string is String) {
|
||||
writeString_(string)
|
||||
|
||||
@ -7,7 +7,7 @@ static const char* coreModuleSource =
|
||||
"class Num {}\n"
|
||||
"\n"
|
||||
"class Sequence {\n"
|
||||
" all(f) {\n"
|
||||
" def all(f) {\n"
|
||||
" var result = true\n"
|
||||
" for (element in this) {\n"
|
||||
" result = f.call(element)\n"
|
||||
@ -16,7 +16,7 @@ static const char* coreModuleSource =
|
||||
" return result\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" any(f) {\n"
|
||||
" def any(f) {\n"
|
||||
" var result = false\n"
|
||||
" for (element in this) {\n"
|
||||
" result = f.call(element)\n"
|
||||
@ -25,14 +25,14 @@ static const char* coreModuleSource =
|
||||
" return result\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" contains(element) {\n"
|
||||
" def contains(element) {\n"
|
||||
" for (item in this) {\n"
|
||||
" if (element == item) return true\n"
|
||||
" }\n"
|
||||
" return false\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" count {\n"
|
||||
" def count {\n"
|
||||
" var result = 0\n"
|
||||
" for (element in this) {\n"
|
||||
" result = result + 1\n"
|
||||
@ -40,7 +40,7 @@ static const char* coreModuleSource =
|
||||
" return result\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" count(f) {\n"
|
||||
" def count(f) {\n"
|
||||
" var result = 0\n"
|
||||
" for (element in this) {\n"
|
||||
" if (f.call(element)) result = result + 1\n"
|
||||
@ -48,26 +48,26 @@ static const char* coreModuleSource =
|
||||
" return result\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" each(f) {\n"
|
||||
" def each(f) {\n"
|
||||
" for (element in this) {\n"
|
||||
" f.call(element)\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" isEmpty { iterate(null) ? false : true }\n"
|
||||
" def isEmpty { iterate(null) ? false : true }\n"
|
||||
"\n"
|
||||
" map(transformation) { MapSequence.new(this, transformation) }\n"
|
||||
" def map(transformation) { MapSequence.new(this, transformation) }\n"
|
||||
"\n"
|
||||
" where(predicate) { WhereSequence.new(this, predicate) }\n"
|
||||
" def where(predicate) { WhereSequence.new(this, predicate) }\n"
|
||||
"\n"
|
||||
" reduce(acc, f) {\n"
|
||||
" def reduce(acc, f) {\n"
|
||||
" for (element in this) {\n"
|
||||
" acc = f.call(acc, element)\n"
|
||||
" }\n"
|
||||
" return acc\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" reduce(f) {\n"
|
||||
" def reduce(f) {\n"
|
||||
" var iter = iterate(null)\n"
|
||||
" if (!iter) Fiber.abort(\"Can't reduce an empty sequence.\")\n"
|
||||
"\n"
|
||||
@ -80,9 +80,9 @@ static const char* coreModuleSource =
|
||||
" return result\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" join() { join(\"\") }\n"
|
||||
" def join() { join(\"\") }\n"
|
||||
"\n"
|
||||
" join(sep) {\n"
|
||||
" def join(sep) {\n"
|
||||
" var first = true\n"
|
||||
" var result = \"\"\n"
|
||||
"\n"
|
||||
@ -95,7 +95,7 @@ static const char* coreModuleSource =
|
||||
" return result\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" toList {\n"
|
||||
" def toList {\n"
|
||||
" var result = List.new()\n"
|
||||
" for (element in this) {\n"
|
||||
" result.add(element)\n"
|
||||
@ -110,8 +110,8 @@ static const char* coreModuleSource =
|
||||
" _fn = fn\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" iterate(iterator) { _sequence.iterate(iterator) }\n"
|
||||
" iteratorValue(iterator) { _fn.call(_sequence.iteratorValue(iterator)) }\n"
|
||||
" def iterate(iterator) { _sequence.iterate(iterator) }\n"
|
||||
" def iteratorValue(iterator) { _fn.call(_sequence.iteratorValue(iterator)) }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class WhereSequence is Sequence {\n"
|
||||
@ -120,19 +120,19 @@ static const char* coreModuleSource =
|
||||
" _fn = fn\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" iterate(iterator) {\n"
|
||||
" def iterate(iterator) {\n"
|
||||
" while (iterator = _sequence.iterate(iterator)) {\n"
|
||||
" if (_fn.call(_sequence.iteratorValue(iterator))) break\n"
|
||||
" }\n"
|
||||
" return iterator\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" iteratorValue(iterator) { _sequence.iteratorValue(iterator) }\n"
|
||||
" def iteratorValue(iterator) { _sequence.iteratorValue(iterator) }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class String is Sequence {\n"
|
||||
" bytes { StringByteSequence.new(this) }\n"
|
||||
" codePoints { StringCodePointSequence.new(this) }\n"
|
||||
" def bytes { StringByteSequence.new(this) }\n"
|
||||
" def codePoints { StringCodePointSequence.new(this) }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class StringByteSequence is Sequence {\n"
|
||||
@ -140,11 +140,11 @@ static const char* coreModuleSource =
|
||||
" _string = string\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" [index] { _string.byteAt_(index) }\n"
|
||||
" iterate(iterator) { _string.iterateByte_(iterator) }\n"
|
||||
" iteratorValue(iterator) { _string.byteAt_(iterator) }\n"
|
||||
" def [index] { _string.byteAt_(index) }\n"
|
||||
" def iterate(iterator) { _string.iterateByte_(iterator) }\n"
|
||||
" def iteratorValue(iterator) { _string.byteAt_(iterator) }\n"
|
||||
"\n"
|
||||
" count { _string.byteCount_ }\n"
|
||||
" def count { _string.byteCount_ }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class StringCodePointSequence is Sequence {\n"
|
||||
@ -152,24 +152,24 @@ static const char* coreModuleSource =
|
||||
" _string = string\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" [index] { _string.codePointAt_(index) }\n"
|
||||
" iterate(iterator) { _string.iterate(iterator) }\n"
|
||||
" iteratorValue(iterator) { _string.codePointAt_(iterator) }\n"
|
||||
" def [index] { _string.codePointAt_(index) }\n"
|
||||
" def iterate(iterator) { _string.iterate(iterator) }\n"
|
||||
" def iteratorValue(iterator) { _string.codePointAt_(iterator) }\n"
|
||||
"\n"
|
||||
" count { _string.count }\n"
|
||||
" def count { _string.count }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class List is Sequence {\n"
|
||||
" addAll(other) {\n"
|
||||
" def addAll(other) {\n"
|
||||
" for (element in other) {\n"
|
||||
" add(element)\n"
|
||||
" }\n"
|
||||
" return other\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" toString { \"[%(join(\", \"))]\" }\n"
|
||||
" def toString { \"[%(join(\", \"))]\" }\n"
|
||||
"\n"
|
||||
" +(other) {\n"
|
||||
" def +(other) {\n"
|
||||
" var result = this[0..-1]\n"
|
||||
" for (element in other) {\n"
|
||||
" result.add(element)\n"
|
||||
@ -179,10 +179,10 @@ static const char* coreModuleSource =
|
||||
"}\n"
|
||||
"\n"
|
||||
"class Map {\n"
|
||||
" keys { MapKeySequence.new(this) }\n"
|
||||
" values { MapValueSequence.new(this) }\n"
|
||||
" def keys { MapKeySequence.new(this) }\n"
|
||||
" def values { MapValueSequence.new(this) }\n"
|
||||
"\n"
|
||||
" toString {\n"
|
||||
" def toString {\n"
|
||||
" var first = true\n"
|
||||
" var result = \"{\"\n"
|
||||
"\n"
|
||||
@ -201,8 +201,8 @@ static const char* coreModuleSource =
|
||||
" _map = map\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" iterate(n) { _map.iterate_(n) }\n"
|
||||
" iteratorValue(iterator) { _map.keyIteratorValue_(iterator) }\n"
|
||||
" def iterate(n) { _map.iterate_(n) }\n"
|
||||
" def iteratorValue(iterator) { _map.keyIteratorValue_(iterator) }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class MapValueSequence is Sequence {\n"
|
||||
@ -210,38 +210,38 @@ static const char* coreModuleSource =
|
||||
" _map = map\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" iterate(n) { _map.iterate_(n) }\n"
|
||||
" iteratorValue(iterator) { _map.valueIteratorValue_(iterator) }\n"
|
||||
" def iterate(n) { _map.iterate_(n) }\n"
|
||||
" def iteratorValue(iterator) { _map.valueIteratorValue_(iterator) }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class Range is Sequence {}\n"
|
||||
"\n"
|
||||
"class System {\n"
|
||||
" static print() {\n"
|
||||
" static def print() {\n"
|
||||
" writeString_(\"\n\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static print(obj) {\n"
|
||||
" static def print(obj) {\n"
|
||||
" writeObject_(obj)\n"
|
||||
" writeString_(\"\n\")\n"
|
||||
" return obj\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static printAll(sequence) {\n"
|
||||
" static def printAll(sequence) {\n"
|
||||
" for (object in sequence) writeObject_(object)\n"
|
||||
" writeString_(\"\n\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static write(obj) {\n"
|
||||
" static def write(obj) {\n"
|
||||
" writeObject_(obj)\n"
|
||||
" return obj\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static writeAll(sequence) {\n"
|
||||
" static def writeAll(sequence) {\n"
|
||||
" for (object in sequence) writeObject_(object)\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static writeObject_(obj) {\n"
|
||||
" static def writeObject_(obj) {\n"
|
||||
" var string = obj.toString\n"
|
||||
" if (string is String) {\n"
|
||||
" writeString_(string)\n"
|
||||
|
||||
@ -48,6 +48,7 @@ ObjClass* wrenNewSingleClass(WrenVM* vm, int numFields, ObjString* name)
|
||||
ObjClass* classObj = ALLOCATE(vm, ObjClass);
|
||||
initObj(vm, &classObj->obj, OBJ_CLASS, NULL);
|
||||
classObj->superclass = NULL;
|
||||
classObj->enclosingClass = NULL;
|
||||
classObj->numFields = numFields;
|
||||
classObj->name = name;
|
||||
|
||||
@ -82,7 +83,8 @@ void wrenBindSuperclass(WrenVM* vm, ObjClass* subclass, ObjClass* superclass)
|
||||
}
|
||||
}
|
||||
|
||||
ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass, int numFields,
|
||||
ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass,
|
||||
ObjClass* enclosingClass, int numFields,
|
||||
ObjString* name)
|
||||
{
|
||||
// Create the metaclass.
|
||||
@ -92,6 +94,9 @@ ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass, int numFields,
|
||||
ObjClass* metaclass = wrenNewSingleClass(vm, 0, AS_STRING(metaclassName));
|
||||
metaclass->obj.classObj = vm->classClass;
|
||||
|
||||
// TODO: Is this what we want? Test.
|
||||
metaclass->enclosingClass = enclosingClass;
|
||||
|
||||
wrenPopRoot(vm);
|
||||
|
||||
// Make sure the metaclass isn't collected when we allocate the class.
|
||||
@ -102,6 +107,8 @@ ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass, int numFields,
|
||||
wrenBindSuperclass(vm, metaclass, vm->classClass);
|
||||
|
||||
ObjClass* classObj = wrenNewSingleClass(vm, numFields, name);
|
||||
|
||||
classObj->enclosingClass = enclosingClass;
|
||||
|
||||
// Make sure the class isn't collected while the inherited methods are being
|
||||
// bound.
|
||||
|
||||
@ -376,6 +376,8 @@ struct sObjClass
|
||||
{
|
||||
Obj obj;
|
||||
ObjClass* superclass;
|
||||
|
||||
ObjClass* enclosingClass;
|
||||
|
||||
// The number of fields needed for an instance of this class, including all
|
||||
// of its superclass fields.
|
||||
@ -616,7 +618,8 @@ ObjClass* wrenNewSingleClass(WrenVM* vm, int numFields, ObjString* name);
|
||||
void wrenBindSuperclass(WrenVM* vm, ObjClass* subclass, ObjClass* superclass);
|
||||
|
||||
// Creates a new class object as well as its associated metaclass.
|
||||
ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass, int numFields,
|
||||
ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass,
|
||||
ObjClass* enclosingClass, int numFields,
|
||||
ObjString* name);
|
||||
|
||||
void wrenBindMethod(WrenVM* vm, ObjClass* classObj, int symbol, Method method);
|
||||
|
||||
@ -386,10 +386,36 @@ static void runtimeError(WrenVM* vm)
|
||||
|
||||
// Aborts the current fiber with an appropriate method not found error for a
|
||||
// method with [symbol] on [classObj].
|
||||
static void methodNotFound(WrenVM* vm, ObjClass* classObj, int symbol)
|
||||
static Method* methodNotFound(WrenVM* vm, ObjClass* classObj, int symbol)
|
||||
{
|
||||
ObjClass* originalClass = classObj;
|
||||
|
||||
classObj = originalClass->enclosingClass;
|
||||
while (classObj != NULL)
|
||||
{
|
||||
if (symbol < classObj->methods.count)
|
||||
{
|
||||
Method* method = &classObj->methods.data[symbol];
|
||||
if (method->type != METHOD_NONE)
|
||||
{
|
||||
// TODO: Doing this lazily is a drag. The problem is we don't know all
|
||||
// of the enclosing class's methods at the point in time when the inner
|
||||
// class is created. If we change the grammar to only allow inner
|
||||
// classes to appear after all method definitions, we could do this
|
||||
// eagerly. Worth doing?
|
||||
// Copy it down now so we don't have to walk the enclosing classes
|
||||
// every time.
|
||||
originalClass->methods.data[symbol] = *method;
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
classObj = classObj->enclosingClass;
|
||||
}
|
||||
|
||||
vm->fiber->error = wrenStringFormat(vm, "@ does not implement '$'.",
|
||||
OBJ_VAL(classObj->name), vm->methodNames.data[symbol].buffer);
|
||||
OBJ_VAL(originalClass->name), vm->methodNames.data[symbol].buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Checks that [value], which must be a function or closure, does not require
|
||||
@ -643,15 +669,22 @@ static void createClass(WrenVM* vm, int numFields, ObjModule* module)
|
||||
Value name = vm->fiber->stackTop[-2];
|
||||
Value superclass = vm->fiber->stackTop[-1];
|
||||
|
||||
// TODO: This does the wrong thing when there is no enclosing method (i.e.
|
||||
// top level code). If we make the top level a class definition then this
|
||||
// should work provided we make sure that class is in the first slot of the
|
||||
// first call frame.
|
||||
ObjClass* enclosingClass = wrenGetClass(vm,
|
||||
vm->fiber->frames[vm->fiber->numFrames - 1].stackStart[0]);
|
||||
|
||||
// We have two values on the stack and we are going to leave one, so discard
|
||||
// the other slot.
|
||||
vm->fiber->stackTop--;
|
||||
|
||||
|
||||
vm->fiber->error = validateSuperclass(vm, name, superclass, numFields);
|
||||
if (!IS_NULL(vm->fiber->error)) return;
|
||||
|
||||
ObjClass* classObj = wrenNewClass(vm, AS_CLASS(superclass), numFields,
|
||||
AS_STRING(name));
|
||||
ObjClass* classObj = wrenNewClass(vm, AS_CLASS(superclass), enclosingClass,
|
||||
numFields, AS_STRING(name));
|
||||
vm->fiber->stackTop[-1] = OBJ_VAL(classObj);
|
||||
|
||||
if (numFields == -1) bindForeignClass(vm, classObj, module);
|
||||
@ -919,12 +952,13 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
goto completeCall;
|
||||
|
||||
completeCall:
|
||||
// If the class's method table doesn't include the symbol, bail.
|
||||
// If the class's method table doesn't include the symbol, look in the
|
||||
// enclosing classes, or fail if not found there.
|
||||
if (symbol >= classObj->methods.count ||
|
||||
(method = &classObj->methods.data[symbol])->type == METHOD_NONE)
|
||||
{
|
||||
methodNotFound(vm, classObj, symbol);
|
||||
RUNTIME_ERROR();
|
||||
method = methodNotFound(vm, classObj, symbol);
|
||||
if (method == NULL) RUNTIME_ERROR();
|
||||
}
|
||||
|
||||
switch (method->type)
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
class Call {
|
||||
static noParams {
|
||||
static def noParams {
|
||||
System.print("noParams")
|
||||
}
|
||||
|
||||
static zero() {
|
||||
static def zero() {
|
||||
System.print("zero")
|
||||
}
|
||||
|
||||
static one(one) {
|
||||
static def one(one) {
|
||||
System.print("one %(one)")
|
||||
}
|
||||
|
||||
static two(one, two) {
|
||||
static def two(one, two) {
|
||||
// Don't print null bytes.
|
||||
if (two is String && two.bytes.contains(0)) {
|
||||
two = two.bytes.toList
|
||||
@ -20,7 +20,7 @@ class Call {
|
||||
System.print("two %(one) %(two)")
|
||||
}
|
||||
|
||||
static getValue() { ["a", "b"] }
|
||||
static def getValue() { ["a", "b"] }
|
||||
}
|
||||
|
||||
// expect: noParams
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
class ForeignClass {
|
||||
foreign static finalized
|
||||
foreign static def finalized
|
||||
}
|
||||
|
||||
// Class with a default constructor.
|
||||
foreign class Counter {
|
||||
construct new() {}
|
||||
foreign increment(amount)
|
||||
foreign value
|
||||
foreign def increment(amount)
|
||||
foreign def value
|
||||
}
|
||||
|
||||
var counter = Counter.new()
|
||||
@ -18,7 +18,7 @@ System.print(counter.value) // expect: 4.3
|
||||
|
||||
// Foreign classes can inherit a class as long as it has no fields.
|
||||
class PointBase {
|
||||
inherited() {
|
||||
def inherited() {
|
||||
System.print("inherited method")
|
||||
}
|
||||
}
|
||||
@ -33,8 +33,8 @@ foreign class Point is PointBase {
|
||||
System.print("%(x), %(y), %(z)")
|
||||
}
|
||||
|
||||
foreign translate(x, y, z)
|
||||
foreign toString
|
||||
foreign def translate(x, y, z)
|
||||
foreign def toString
|
||||
}
|
||||
|
||||
var p = Point.new(1, 2, 3) // expect: 1, 2, 3
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import "get_variable_module"
|
||||
|
||||
class GetVariable {
|
||||
foreign static beforeDefined()
|
||||
foreign static afterDefined()
|
||||
foreign static afterAssigned()
|
||||
foreign static otherSlot()
|
||||
foreign static otherModule()
|
||||
foreign static def beforeDefined()
|
||||
foreign static def afterDefined()
|
||||
foreign static def afterAssigned()
|
||||
foreign static def otherSlot()
|
||||
foreign static def otherModule()
|
||||
}
|
||||
|
||||
System.print(GetVariable.beforeDefined()) // expect: null
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
class Lists {
|
||||
foreign static newList()
|
||||
foreign static insert()
|
||||
foreign static def newList()
|
||||
foreign static def insert()
|
||||
}
|
||||
|
||||
var list = Lists.newList()
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
class Slots {
|
||||
foreign static noSet
|
||||
foreign static getSlots(bool, num, string, bytes, value)
|
||||
foreign static setSlots(a, b, c, d)
|
||||
foreign static ensure()
|
||||
foreign static ensureOutsideForeign()
|
||||
foreign static def noSet
|
||||
foreign static def getSlots(bool, num, string, bytes, value)
|
||||
foreign static def setSlots(a, b, c, d)
|
||||
foreign static def ensure()
|
||||
foreign static def ensureOutsideForeign()
|
||||
}
|
||||
|
||||
// If nothing is set in the return slot, it retains its previous value, the
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
class Value {
|
||||
foreign static value=(value)
|
||||
foreign static value
|
||||
foreign static def value=(value)
|
||||
foreign static def value
|
||||
}
|
||||
|
||||
Value.value = ["list", "of", "strings"]
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Benchmark {
|
||||
foreign static arguments(a, b, c, d)
|
||||
foreign static def arguments(a, b, c, d)
|
||||
}
|
||||
|
||||
var start = System.clock
|
||||
|
||||
@ -11,7 +11,7 @@ class Tree {
|
||||
}
|
||||
}
|
||||
|
||||
check {
|
||||
def check {
|
||||
if (_left == null) {
|
||||
return _item
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ class Tree {
|
||||
}
|
||||
}
|
||||
|
||||
check {
|
||||
def check {
|
||||
if (_left == null) {
|
||||
return _item
|
||||
}
|
||||
|
||||
@ -44,15 +44,15 @@ class Strength {
|
||||
_name = name
|
||||
}
|
||||
|
||||
value { _value }
|
||||
name { _name }
|
||||
def value { _value }
|
||||
def name { _name }
|
||||
|
||||
nextWeaker { ORDERED[_value] }
|
||||
def nextWeaker { ORDERED[_value] }
|
||||
|
||||
static stronger(s1, s2) { s1.value < s2.value }
|
||||
static weaker(s1, s2) { s1.value > s2.value }
|
||||
static weakest(s1, s2) { Strength.weaker(s1, s2) ? s1 : s2 }
|
||||
static strongest(s1, s2) { Strength.stronger(s1, s2) ? s1 : s2 }
|
||||
static def stronger(s1, s2) { s1.value < s2.value }
|
||||
static def weaker(s1, s2) { s1.value > s2.value }
|
||||
static def weakest(s1, s2) { Strength.weaker(s1, s2) ? s1 : s2 }
|
||||
static def strongest(s1, s2) { Strength.stronger(s1, s2) ? s1 : s2 }
|
||||
}
|
||||
|
||||
// Compile time computed constants.
|
||||
@ -75,10 +75,10 @@ class Constraint {
|
||||
_strength = strength
|
||||
}
|
||||
|
||||
strength { _strength }
|
||||
def strength { _strength }
|
||||
|
||||
// Activate this constraint and attempt to satisfy it.
|
||||
addConstraint() {
|
||||
def addConstraint() {
|
||||
addToGraph()
|
||||
ThePlanner.incrementalAdd(this)
|
||||
}
|
||||
@ -88,7 +88,7 @@ class Constraint {
|
||||
// graph. Answer the constraint that this constraint overrides, if
|
||||
// there is one, or nil, if there isn't.
|
||||
// Assume: I am not already satisfied.
|
||||
satisfy(mark) {
|
||||
def satisfy(mark) {
|
||||
chooseMethod(mark)
|
||||
if (!isSatisfied) {
|
||||
if (_strength == REQUIRED) {
|
||||
@ -107,7 +107,7 @@ class Constraint {
|
||||
return overridden
|
||||
}
|
||||
|
||||
destroyConstraint() {
|
||||
def destroyConstraint() {
|
||||
if (isSatisfied) ThePlanner.incrementalRemove(this)
|
||||
removeFromGraph()
|
||||
}
|
||||
@ -115,7 +115,7 @@ class Constraint {
|
||||
// Normal constraints are not input constraints. An input constraint
|
||||
// is one that depends on external state, such as the mouse, the
|
||||
// keybord, a clock, or some arbitraty piece of imperative code.
|
||||
isInput { false }
|
||||
def isInput { false }
|
||||
}
|
||||
|
||||
// Abstract superclass for constraints having a single possible output variable.
|
||||
@ -128,44 +128,44 @@ class UnaryConstraint is Constraint {
|
||||
}
|
||||
|
||||
// Adds this constraint to the constraint graph.
|
||||
addToGraph() {
|
||||
def addToGraph() {
|
||||
_myOutput.addConstraint(this)
|
||||
_satisfied = false
|
||||
}
|
||||
|
||||
// Decides if this constraint can be satisfied and records that decision.
|
||||
chooseMethod(mark) {
|
||||
def chooseMethod(mark) {
|
||||
_satisfied = (_myOutput.mark != mark) &&
|
||||
Strength.stronger(strength, _myOutput.walkStrength)
|
||||
}
|
||||
|
||||
// Returns true if this constraint is satisfied in the current solution.
|
||||
isSatisfied { _satisfied }
|
||||
def isSatisfied { _satisfied }
|
||||
|
||||
markInputs(mark) {
|
||||
def markInputs(mark) {
|
||||
// has no inputs.
|
||||
}
|
||||
|
||||
// Returns the current output variable.
|
||||
output { _myOutput }
|
||||
def output { _myOutput }
|
||||
|
||||
// Calculate the walkabout strength, the stay flag, and, if it is
|
||||
// 'stay', the value for the current output of this constraint. Assume
|
||||
// this constraint is satisfied.
|
||||
recalculate() {
|
||||
def recalculate() {
|
||||
_myOutput.walkStrength = strength
|
||||
_myOutput.stay = !isInput
|
||||
if (_myOutput.stay) execute() // Stay optimization.
|
||||
}
|
||||
|
||||
// Records that this constraint is unsatisfied.
|
||||
markUnsatisfied() {
|
||||
def markUnsatisfied() {
|
||||
_satisfied = false
|
||||
}
|
||||
|
||||
inputsKnown(mark) { true }
|
||||
def inputsKnown(mark) { true }
|
||||
|
||||
removeFromGraph() {
|
||||
def removeFromGraph() {
|
||||
if (_myOutput != null) _myOutput.removeConstraint(this)
|
||||
_satisfied = false
|
||||
}
|
||||
@ -180,7 +180,7 @@ class StayConstraint is UnaryConstraint {
|
||||
super(variable, strength)
|
||||
}
|
||||
|
||||
execute() {
|
||||
def execute() {
|
||||
// Stay constraints do nothing.
|
||||
}
|
||||
}
|
||||
@ -193,9 +193,9 @@ class EditConstraint is UnaryConstraint {
|
||||
}
|
||||
|
||||
// Edits indicate that a variable is to be changed by imperative code.
|
||||
isInput { true }
|
||||
def isInput { true }
|
||||
|
||||
execute() {
|
||||
def execute() {
|
||||
// Edit constraints do nothing.
|
||||
}
|
||||
}
|
||||
@ -216,14 +216,14 @@ class BinaryConstraint is Constraint {
|
||||
addConstraint()
|
||||
}
|
||||
|
||||
direction { _direction }
|
||||
v1 { _v1 }
|
||||
v2 { _v2 }
|
||||
def direction { _direction }
|
||||
def v1 { _v1 }
|
||||
def v2 { _v2 }
|
||||
|
||||
// Decides if this constraint can be satisfied and which way it
|
||||
// should flow based on the relative strength of the variables related,
|
||||
// and record that decision.
|
||||
chooseMethod(mark) {
|
||||
def chooseMethod(mark) {
|
||||
if (_v1.mark == mark) {
|
||||
if (_v2.mark != mark &&
|
||||
Strength.stronger(strength, _v2.walkStrength)) {
|
||||
@ -258,30 +258,30 @@ class BinaryConstraint is Constraint {
|
||||
}
|
||||
|
||||
// Add this constraint to the constraint graph.
|
||||
addToGraph() {
|
||||
def addToGraph() {
|
||||
_v1.addConstraint(this)
|
||||
_v2.addConstraint(this)
|
||||
_direction = NONE
|
||||
}
|
||||
|
||||
// Answer true if this constraint is satisfied in the current solution.
|
||||
isSatisfied { _direction != NONE }
|
||||
def isSatisfied { _direction != NONE }
|
||||
|
||||
// Mark the input variable with the given mark.
|
||||
markInputs(mark) {
|
||||
def markInputs(mark) {
|
||||
input.mark = mark
|
||||
}
|
||||
|
||||
// Returns the current input variable
|
||||
input { _direction == FORWARD ? _v1 : _v2 }
|
||||
def input { _direction == FORWARD ? _v1 : _v2 }
|
||||
|
||||
// Returns the current output variable.
|
||||
output { _direction == FORWARD ? _v2 : _v1 }
|
||||
def output { _direction == FORWARD ? _v2 : _v1 }
|
||||
|
||||
// Calculate the walkabout strength, the stay flag, and, if it is
|
||||
// 'stay', the value for the current output of this
|
||||
// constraint. Assume this constraint is satisfied.
|
||||
recalculate() {
|
||||
def recalculate() {
|
||||
var ihn = input
|
||||
var out = output
|
||||
out.walkStrength = Strength.weakest(strength, ihn.walkStrength)
|
||||
@ -290,16 +290,16 @@ class BinaryConstraint is Constraint {
|
||||
}
|
||||
|
||||
// Record the fact that this constraint is unsatisfied.
|
||||
markUnsatisfied() {
|
||||
def markUnsatisfied() {
|
||||
_direction = NONE
|
||||
}
|
||||
|
||||
inputsKnown(mark) {
|
||||
def inputsKnown(mark) {
|
||||
var i = input
|
||||
return i.mark == mark || i.stay || i.determinedBy == null
|
||||
}
|
||||
|
||||
removeFromGraph() {
|
||||
def removeFromGraph() {
|
||||
if (_v1 != null) _v1.removeConstraint(this)
|
||||
if (_v2 != null) _v2.removeConstraint(this)
|
||||
_direction = NONE
|
||||
@ -318,25 +318,25 @@ class ScaleConstraint is BinaryConstraint {
|
||||
}
|
||||
|
||||
// Adds this constraint to the constraint graph.
|
||||
addToGraph() {
|
||||
def addToGraph() {
|
||||
super()
|
||||
_scale.addConstraint(this)
|
||||
_offset.addConstraint(this)
|
||||
}
|
||||
|
||||
removeFromGraph() {
|
||||
def removeFromGraph() {
|
||||
super()
|
||||
if (_scale != null) _scale.removeConstraint(this)
|
||||
if (_offset != null) _offset.removeConstraint(this)
|
||||
}
|
||||
|
||||
markInputs(mark) {
|
||||
def markInputs(mark) {
|
||||
super.markInputs(mark)
|
||||
_scale.mark = _offset.mark = mark
|
||||
}
|
||||
|
||||
// Enforce this constraint. Assume that it is satisfied.
|
||||
execute() {
|
||||
def execute() {
|
||||
if (direction == FORWARD) {
|
||||
v2.value = v1.value * _scale.value + _offset.value
|
||||
} else {
|
||||
@ -348,7 +348,7 @@ class ScaleConstraint is BinaryConstraint {
|
||||
// Calculate the walkabout strength, the stay flag, and, if it is
|
||||
// 'stay', the value for the current output of this constraint. Assume
|
||||
// this constraint is satisfied.
|
||||
recalculate() {
|
||||
def recalculate() {
|
||||
var ihn = input
|
||||
var out = output
|
||||
out.walkStrength = Strength.weakest(strength, ihn.walkStrength)
|
||||
@ -364,7 +364,7 @@ class EqualityConstraint is BinaryConstraint {
|
||||
}
|
||||
|
||||
// Enforce this constraint. Assume that it is satisfied.
|
||||
execute() {
|
||||
def execute() {
|
||||
output.value = input.value
|
||||
}
|
||||
}
|
||||
@ -376,34 +376,26 @@ class EqualityConstraint is BinaryConstraint {
|
||||
class Variable {
|
||||
construct new(name, value) {
|
||||
_constraints = []
|
||||
_determinedBy = null
|
||||
_mark = 0
|
||||
_walkStrength = WEAKEST
|
||||
_stay = true
|
||||
_name = name
|
||||
_value = value
|
||||
}
|
||||
|
||||
constraints { _constraints }
|
||||
determinedBy { _determinedBy }
|
||||
determinedBy=(value) { _determinedBy = value }
|
||||
mark { _mark }
|
||||
mark=(value) { _mark = value }
|
||||
walkStrength { _walkStrength }
|
||||
walkStrength=(value) { _walkStrength = value }
|
||||
stay { _stay }
|
||||
stay=(value) { _stay = value }
|
||||
value { _value }
|
||||
value=(newValue) { _value = newValue }
|
||||
def constraints { _constraints }
|
||||
|
||||
var determinedBy
|
||||
var mark = 0
|
||||
var walkStrength = WEAKEST
|
||||
var stay = true
|
||||
var value
|
||||
|
||||
// Add the given constraint to the set of all constraints that refer
|
||||
// this variable.
|
||||
addConstraint(constraint) {
|
||||
def addConstraint(constraint) {
|
||||
_constraints.add(constraint)
|
||||
}
|
||||
|
||||
// Removes all traces of c from this variable.
|
||||
removeConstraint(constraint) {
|
||||
def removeConstraint(constraint) {
|
||||
_constraints = _constraints.where { |c| c != constraint }
|
||||
if (_determinedBy == constraint) _determinedBy = null
|
||||
}
|
||||
@ -417,13 +409,13 @@ class Plan {
|
||||
_list = []
|
||||
}
|
||||
|
||||
addConstraint(constraint) {
|
||||
def addConstraint(constraint) {
|
||||
_list.add(constraint)
|
||||
}
|
||||
|
||||
size { _list.count }
|
||||
def size { _list.count }
|
||||
|
||||
execute() {
|
||||
def execute() {
|
||||
for (constraint in _list) {
|
||||
constraint.execute()
|
||||
}
|
||||
@ -447,7 +439,7 @@ class Planner {
|
||||
// a unique mark value so that we know where we've been. This allows
|
||||
// the algorithm to avoid getting into an infinite loop even if the
|
||||
// constraint graph has an inadvertent cycle.
|
||||
incrementalAdd(constraint) {
|
||||
def incrementalAdd(constraint) {
|
||||
var mark = newMark()
|
||||
var overridden = constraint.satisfy(mark)
|
||||
while (overridden != null) {
|
||||
@ -464,7 +456,7 @@ class Planner {
|
||||
// strength, strongest first, as a heuristic for avoiding
|
||||
// unnecessarily adding and then overriding weak constraints.
|
||||
// Assume: [c] is satisfied.
|
||||
incrementalRemove(constraint) {
|
||||
def incrementalRemove(constraint) {
|
||||
var out = constraint.output
|
||||
constraint.markUnsatisfied()
|
||||
constraint.removeFromGraph()
|
||||
@ -480,7 +472,7 @@ class Planner {
|
||||
}
|
||||
|
||||
// Select a previously unused mark value.
|
||||
newMark() { _currentMark = _currentMark + 1 }
|
||||
def newMark() { _currentMark = _currentMark + 1 }
|
||||
|
||||
// Extract a plan for resatisfaction starting from the given source
|
||||
// constraints, usually a set of input constraints. This method
|
||||
@ -499,7 +491,7 @@ class Planner {
|
||||
// variables, which are not stay but which are also not computed by
|
||||
// any constraint.
|
||||
// Assume: [sources] are all satisfied.
|
||||
makePlan(sources) {
|
||||
def makePlan(sources) {
|
||||
var mark = newMark()
|
||||
var plan = Plan.new()
|
||||
var todo = sources
|
||||
@ -516,7 +508,7 @@ class Planner {
|
||||
|
||||
// Extract a plan for resatisfying starting from the output of the
|
||||
// given [constraints], usually a set of input constraints.
|
||||
extractPlanFromConstraints(constraints) {
|
||||
def extractPlanFromConstraints(constraints) {
|
||||
var sources = []
|
||||
for (constraint in constraints) {
|
||||
// if not in plan already and eligible for inclusion.
|
||||
@ -536,7 +528,7 @@ class Planner {
|
||||
// the given mark. Thus, encountering a marked node downstream of
|
||||
// the output constraint means that there is a path from the
|
||||
// constraint's output to one of its inputs.
|
||||
addPropagate(constraint, mark) {
|
||||
def addPropagate(constraint, mark) {
|
||||
var todo = [constraint]
|
||||
while (todo.count > 0) {
|
||||
var d = todo.removeAt(-1)
|
||||
@ -555,7 +547,7 @@ class Planner {
|
||||
// Update the walkabout strengths and stay flags of all variables
|
||||
// downstream of the given constraint. Answer a collection of
|
||||
// unsatisfied constraints sorted in order of decreasing strength.
|
||||
removePropagateFrom(out) {
|
||||
def removePropagateFrom(out) {
|
||||
out.determinedBy = null
|
||||
out.walkStrength = WEAKEST
|
||||
out.stay = true
|
||||
@ -579,7 +571,7 @@ class Planner {
|
||||
return unsatisfied
|
||||
}
|
||||
|
||||
addConstraintsConsumingTo(v, coll) {
|
||||
def addConstraintsConsumingTo(v, coll) {
|
||||
var determining = v.determinedBy
|
||||
for (constraint in v.constraints) {
|
||||
if (constraint != determining && constraint.isSatisfied) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Fib {
|
||||
static get(n) {
|
||||
static def get(n) {
|
||||
if (n < 2) return n
|
||||
return get(n - 1) + get(n - 2)
|
||||
}
|
||||
|
||||
@ -3,8 +3,8 @@ class Toggle {
|
||||
_state = startState
|
||||
}
|
||||
|
||||
value { _state }
|
||||
activate {
|
||||
def value { _state }
|
||||
def activate {
|
||||
_state = !_state
|
||||
return this
|
||||
}
|
||||
@ -17,7 +17,7 @@ class NthToggle is Toggle {
|
||||
_count = 0
|
||||
}
|
||||
|
||||
activate {
|
||||
def activate {
|
||||
_count = _count + 1
|
||||
if (_count >= _countMax) {
|
||||
super.activate
|
||||
|
||||
@ -16,7 +16,7 @@ System.print([1, [2, [3], 4], 5].join(",")) // expect: 1,[2, [3], 4],5
|
||||
// Calls toString on elements.
|
||||
class Foo {
|
||||
construct new() {}
|
||||
toString { "Foo.toString" }
|
||||
def toString { "Foo.toString" }
|
||||
}
|
||||
|
||||
System.print([1, Foo.new(), 2].join(", ")) // expect: 1, Foo.toString, 2
|
||||
|
||||
@ -10,7 +10,7 @@ System.print([1, [2, [3], 4], 5]) // expect: [1, [2, [3], 4], 5]
|
||||
// Calls toString on elements.
|
||||
class Foo {
|
||||
construct new() {}
|
||||
toString { "Foo.toString" }
|
||||
def toString { "Foo.toString" }
|
||||
}
|
||||
|
||||
System.print([1, Foo.new(), 2]) // expect: [1, Foo.toString, 2]
|
||||
|
||||
@ -10,7 +10,7 @@ System.print({1: {2: {}}}) // expect: {1: {2: {}}}
|
||||
// Calls toString on elements.
|
||||
class Foo {
|
||||
construct new() {}
|
||||
toString { "Foo.toString" }
|
||||
def toString { "Foo.toString" }
|
||||
}
|
||||
|
||||
System.print({1: Foo.new()}) // expect: {1: Foo.toString}
|
||||
|
||||
@ -35,7 +35,7 @@ System.print(Object.same(foo, Foo.new())) // expect: false
|
||||
// Ignores == operators.
|
||||
class Bar {
|
||||
construct new() {}
|
||||
==(other) { true }
|
||||
def ==(other) { true }
|
||||
}
|
||||
|
||||
var bar = Bar.new()
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
class TestSequence is Sequence {
|
||||
construct new() {}
|
||||
|
||||
iterate(iterator) {
|
||||
def iterate(iterator) {
|
||||
if (iterator == null) return 1
|
||||
if (iterator == 10) return false
|
||||
return iterator + 1
|
||||
}
|
||||
|
||||
iteratorValue(iterator) { iterator }
|
||||
def iteratorValue(iterator) { iterator }
|
||||
}
|
||||
|
||||
System.print(TestSequence.new().count) // expect: 10
|
||||
|
||||
@ -3,8 +3,8 @@ System.print([1].isEmpty) // expect: false
|
||||
|
||||
class InfiniteSequence is Sequence {
|
||||
construct new() {}
|
||||
iterate(iterator) { true }
|
||||
iteratorValue(iterator) { iterator }
|
||||
def iterate(iterator) { true }
|
||||
def iteratorValue(iterator) { iterator }
|
||||
}
|
||||
|
||||
// Should not try to iterate the whole sequence.
|
||||
|
||||
@ -5,25 +5,25 @@ class FibIterator {
|
||||
_next = 1
|
||||
}
|
||||
|
||||
iterate {
|
||||
def iterate {
|
||||
var sum = _current + _next
|
||||
_current = _next
|
||||
_next = sum
|
||||
}
|
||||
|
||||
value { _current }
|
||||
def value { _current }
|
||||
}
|
||||
|
||||
class Fib is Sequence {
|
||||
construct new() {}
|
||||
|
||||
iterate(iterator) {
|
||||
def iterate(iterator) {
|
||||
if (iterator == null) return FibIterator.new()
|
||||
iterator.iterate
|
||||
return iterator
|
||||
}
|
||||
|
||||
iteratorValue(iterator) { iterator.value }
|
||||
def iteratorValue(iterator) { iterator.value }
|
||||
}
|
||||
|
||||
var squareFib = Fib.new().map {|fib| fib * fib }
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
class TestSequence is Sequence {
|
||||
construct new() {}
|
||||
|
||||
iterate(iterator) {
|
||||
def iterate(iterator) {
|
||||
if (iterator == null) return 1
|
||||
if (iterator == 3) return false
|
||||
return iterator + 1
|
||||
}
|
||||
|
||||
iteratorValue(iterator) { iterator }
|
||||
def iteratorValue(iterator) { iterator }
|
||||
}
|
||||
|
||||
System.print(TestSequence.new().toList) // expect: [1, 2, 3]
|
||||
|
||||
@ -5,25 +5,25 @@ class FibIterator {
|
||||
_next = 1
|
||||
}
|
||||
|
||||
iterate {
|
||||
def iterate {
|
||||
var sum = _current + _next
|
||||
_current = _next
|
||||
_next = sum
|
||||
}
|
||||
|
||||
value { _current }
|
||||
def value { _current }
|
||||
}
|
||||
|
||||
class Fib is Sequence {
|
||||
construct new() {}
|
||||
|
||||
iterate(iterator) {
|
||||
def iterate(iterator) {
|
||||
if (iterator == null) return FibIterator.new()
|
||||
iterator.iterate
|
||||
return iterator
|
||||
}
|
||||
|
||||
iteratorValue(iterator) { iterator.value }
|
||||
def iteratorValue(iterator) { iterator.value }
|
||||
}
|
||||
|
||||
var largeFibs = Fib.new().where {|fib| fib > 100 }
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
|
||||
toString { "Foo.toString" }
|
||||
def toString { "Foo.toString" }
|
||||
}
|
||||
|
||||
// Calls toString on argument.
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
|
||||
toString { "Foo" }
|
||||
def toString { "Foo" }
|
||||
}
|
||||
|
||||
System.printAll([]) // expect:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
class BadToString {
|
||||
construct new() {}
|
||||
toString { 3 }
|
||||
def toString { 3 }
|
||||
}
|
||||
|
||||
System.print(BadToString.new()) // expect: [invalid toString]
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
|
||||
toString { "Foo" }
|
||||
def toString { "Foo" }
|
||||
}
|
||||
|
||||
System.writeAll([]) // expect:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
class BadToString {
|
||||
construct new() {}
|
||||
toString { 3 }
|
||||
def toString { 3 }
|
||||
}
|
||||
|
||||
System.write(BadToString.new())
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
var done = false
|
||||
while (!done) {
|
||||
class Foo {
|
||||
method {
|
||||
def method {
|
||||
break // expect error
|
||||
}
|
||||
}
|
||||
|
||||
28
test/language/class/code_in_body.wren
Normal file
28
test/language/class/code_in_body.wren
Normal file
@ -0,0 +1,28 @@
|
||||
class Foo {
|
||||
System.print("one")
|
||||
|
||||
construct new() {
|
||||
System.print("constructor")
|
||||
}
|
||||
|
||||
System.print("two")
|
||||
|
||||
construct other() {
|
||||
System.print("other constructor")
|
||||
}
|
||||
}
|
||||
|
||||
// Does not execute body until object is constructed.
|
||||
System.print("before") // expect: before
|
||||
|
||||
// Executes body before constructor body.
|
||||
Foo.new() // expect: one
|
||||
// expect: two
|
||||
// expect: constructor
|
||||
|
||||
// Executes body for each constructor.
|
||||
Foo.other() // expect: one
|
||||
// expect: two
|
||||
// expect: other constructor
|
||||
|
||||
// TODO: Calling (and not calling) superclass constructor in subclass.
|
||||
@ -1,16 +1,16 @@
|
||||
class foo {
|
||||
construct new() {}
|
||||
|
||||
static callFoo {
|
||||
static def callFoo {
|
||||
System.print(foo)
|
||||
}
|
||||
|
||||
callFoo {
|
||||
def callFoo {
|
||||
System.print(foo)
|
||||
}
|
||||
|
||||
foo { "instance foo method" }
|
||||
static foo { "static foo method" }
|
||||
def foo { "instance foo method" }
|
||||
static def foo { "static foo method" }
|
||||
}
|
||||
|
||||
foo.callFoo // expect: static foo method
|
||||
|
||||
19
test/language/class/multiply_nested.wren
Normal file
19
test/language/class/multiply_nested.wren
Normal file
@ -0,0 +1,19 @@
|
||||
class A {
|
||||
construct new() {}
|
||||
|
||||
class B {
|
||||
construct new() {}
|
||||
|
||||
class C {
|
||||
construct new() {}
|
||||
|
||||
class D {
|
||||
construct new() {}
|
||||
|
||||
def test() { System.print("ok") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
A.new().B.new().C.new().D.new().test() // expect: ok
|
||||
@ -1,15 +1,15 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
|
||||
static sayName {
|
||||
static def sayName {
|
||||
System.print(Foo)
|
||||
}
|
||||
|
||||
sayName {
|
||||
def sayName {
|
||||
System.print(Foo)
|
||||
}
|
||||
|
||||
static toString { "Foo!" }
|
||||
static def toString { "Foo!" }
|
||||
}
|
||||
|
||||
Foo.sayName // expect: Foo!
|
||||
|
||||
18
test/language/class/nested.wren
Normal file
18
test/language/class/nested.wren
Normal file
@ -0,0 +1,18 @@
|
||||
class Outer {
|
||||
construct new() {
|
||||
// TODO: Remove "this." when capitalized names are no longer top-level.
|
||||
System.print(this.Inner) // expect: Inner
|
||||
System.print(this.Inner.new().foo) // expect: Inner foo
|
||||
|
||||
System.print(foo) // expect: Outer foo
|
||||
}
|
||||
|
||||
class Inner {
|
||||
construct new() {}
|
||||
def foo { "Inner foo" }
|
||||
}
|
||||
|
||||
def foo { "Outer foo" }
|
||||
}
|
||||
|
||||
Outer.new()
|
||||
6
test/language/class/newline_after_def.wren
Normal file
6
test/language/class/newline_after_def.wren
Normal file
@ -0,0 +1,6 @@
|
||||
class Foo {
|
||||
def // expect error
|
||||
method {} // expect error
|
||||
}
|
||||
|
||||
// The second error is cascaded.
|
||||
@ -1,6 +1,4 @@
|
||||
class Foo {
|
||||
static // expect error
|
||||
method {} // expect error
|
||||
def method {}
|
||||
}
|
||||
|
||||
// The second error is cascaded.
|
||||
12
test/language/class/reference_nested_before_defined.wren
Normal file
12
test/language/class/reference_nested_before_defined.wren
Normal file
@ -0,0 +1,12 @@
|
||||
class Outer {
|
||||
// TODO: Remove "this." when capitalized names are no longer top-level.
|
||||
this.Inner.new() // expect runtime error: Null does not implement 'new()'.
|
||||
|
||||
class Inner {
|
||||
construct new() {}
|
||||
}
|
||||
|
||||
construct new() {}
|
||||
}
|
||||
|
||||
Outer.new()
|
||||
@ -8,4 +8,4 @@ class B {
|
||||
|
||||
// No newline after last method.
|
||||
class C {
|
||||
method {} }
|
||||
def method {} }
|
||||
|
||||
9
test/language/class/this_in_body.wren
Normal file
9
test/language/class/this_in_body.wren
Normal file
@ -0,0 +1,9 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
|
||||
System.print(this)
|
||||
|
||||
def toString { "foo" }
|
||||
}
|
||||
|
||||
Foo.new() // expect: foo
|
||||
23
test/language/class_var/initializer_order.wren
Normal file
23
test/language/class_var/initializer_order.wren
Normal file
@ -0,0 +1,23 @@
|
||||
class Foo {
|
||||
System.print("one")
|
||||
|
||||
construct new() {
|
||||
System.print("six")
|
||||
}
|
||||
|
||||
var bar = System.print("two")
|
||||
|
||||
System.print("three")
|
||||
|
||||
var baz = System.print("four")
|
||||
|
||||
System.print("five")
|
||||
}
|
||||
|
||||
var foo = Foo.new()
|
||||
// expect: one
|
||||
// expect: two
|
||||
// expect: three
|
||||
// expect: four
|
||||
// expect: five
|
||||
// expect: six
|
||||
7
test/language/class_var/newline_after_equals.wren
Normal file
7
test/language/class_var/newline_after_equals.wren
Normal file
@ -0,0 +1,7 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
var bar =
|
||||
"value"
|
||||
}
|
||||
|
||||
System.print(Foo.new().bar) // expect: value
|
||||
5
test/language/class_var/newline_after_var.wren
Normal file
5
test/language/class_var/newline_after_var.wren
Normal file
@ -0,0 +1,5 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
var // expect error
|
||||
bar
|
||||
}
|
||||
9
test/language/class_var/with_initializer.wren
Normal file
9
test/language/class_var/with_initializer.wren
Normal file
@ -0,0 +1,9 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
var bar = "init"
|
||||
}
|
||||
|
||||
var foo = Foo.new()
|
||||
System.print(foo.bar) // expect: init
|
||||
System.print(foo.bar = "value") // expect: value
|
||||
System.print(foo.bar) // expect: value
|
||||
12
test/language/class_var/without_initializer.wren
Normal file
12
test/language/class_var/without_initializer.wren
Normal file
@ -0,0 +1,12 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
var bar
|
||||
}
|
||||
|
||||
var foo = Foo.new()
|
||||
System.print(foo.bar) // expect: null
|
||||
System.print(foo.bar = "value") // expect: value
|
||||
System.print(foo.bar) // expect: value
|
||||
|
||||
// TODO: Duplicate.
|
||||
// TODO: static var.
|
||||
@ -3,7 +3,7 @@ var F = null
|
||||
class Foo {
|
||||
construct new() {}
|
||||
|
||||
method(param) {
|
||||
def method(param) {
|
||||
F = Fn.new {
|
||||
System.print(param)
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
static bar { true }
|
||||
static baz { 1 }
|
||||
static def bar { true }
|
||||
static def baz { 1 }
|
||||
}
|
||||
|
||||
// Condition precedence.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
this +(value) { // expect error
|
||||
construct +(value) { // expect error
|
||||
System.print("ok")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
this -(value) { // expect error
|
||||
construct -(value) { // expect error
|
||||
System.print("ok")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
this name=(value) { // expect error
|
||||
construct name=(value) { // expect error
|
||||
System.print("ok")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
this [value] { // expect error
|
||||
construct [value] { // expect error
|
||||
System.print("ok")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
this ! { // expect error
|
||||
construct ! { // expect error
|
||||
System.print("ok")
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ class Foo {
|
||||
construct named() { _field = "named" }
|
||||
construct other() { _field = "other" }
|
||||
|
||||
toString { _field }
|
||||
def toString { _field }
|
||||
}
|
||||
|
||||
System.print(Foo.named()) // expect: named
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
this new { // expect error
|
||||
construct new { // expect error
|
||||
System.print("ok")
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ class A {
|
||||
_field = arg
|
||||
}
|
||||
|
||||
aField { _field }
|
||||
def aField { _field }
|
||||
}
|
||||
|
||||
class B is A {
|
||||
@ -14,7 +14,7 @@ class B is A {
|
||||
_field = arg1
|
||||
}
|
||||
|
||||
bField { _field }
|
||||
def bField { _field }
|
||||
}
|
||||
|
||||
class C is B {
|
||||
@ -24,7 +24,7 @@ class C is B {
|
||||
_field = "c"
|
||||
}
|
||||
|
||||
cField { _field }
|
||||
def cField { _field }
|
||||
}
|
||||
|
||||
var c = C.new()
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
class Foo {
|
||||
construct new() { _field = "Foo field" }
|
||||
|
||||
closeOverGet {
|
||||
def closeOverGet {
|
||||
return Fn.new { _field }
|
||||
}
|
||||
|
||||
closeOverSet {
|
||||
def closeOverSet {
|
||||
return Fn.new { _field = "new value" }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
write { System.print(_field) }
|
||||
def write { System.print(_field) }
|
||||
}
|
||||
|
||||
Foo.new().write // expect: null
|
||||
|
||||
16
test/language/field/field_in_body.wren
Normal file
16
test/language/field/field_in_body.wren
Normal file
@ -0,0 +1,16 @@
|
||||
class Foo {
|
||||
construct new() {
|
||||
System.print("constructor %(_field)")
|
||||
}
|
||||
|
||||
System.print("before %(_field)")
|
||||
|
||||
_field = "initialized"
|
||||
|
||||
System.print("after %(_field)")
|
||||
}
|
||||
|
||||
Foo.new()
|
||||
// expect: before null
|
||||
// expect: after initialized
|
||||
// expect: constructor initialized
|
||||
@ -1,5 +1,5 @@
|
||||
foreign class Foo {
|
||||
bar {
|
||||
def bar {
|
||||
// Can't read a field.
|
||||
System.print(_bar) // expect error
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
static bar {
|
||||
static def bar {
|
||||
Fn.new { _field = "wat" } // expect error
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
static bar {
|
||||
static def bar {
|
||||
_field = "wat" // expect error
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
// the enclosing instance is closed over.
|
||||
|
||||
class Outer {
|
||||
foo {
|
||||
def foo {
|
||||
class Inner {
|
||||
static bar {
|
||||
static def bar {
|
||||
_field = "nope" // expect error
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
|
||||
set(a, b, c, d, e) {
|
||||
def set(a, b, c, d, e) {
|
||||
_a = a
|
||||
_b = b
|
||||
_c = c
|
||||
@ -9,7 +9,7 @@ class Foo {
|
||||
_e = e
|
||||
}
|
||||
|
||||
write {
|
||||
def write {
|
||||
System.print(_a)
|
||||
System.print(_b)
|
||||
System.print(_c)
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
class Outer {
|
||||
construct new() {}
|
||||
|
||||
method {
|
||||
def method {
|
||||
_field = "outer"
|
||||
System.print(_field) // expect: outer
|
||||
|
||||
class Inner {
|
||||
construct new() {}
|
||||
|
||||
method {
|
||||
def method {
|
||||
_field = "inner"
|
||||
System.print(_field) // expect: inner
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ class Node {
|
||||
_right = right
|
||||
}
|
||||
|
||||
write() {
|
||||
def write() {
|
||||
if (_left is Node) {
|
||||
_left.write()
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
write { System.print(_field) } // Compile a use of the field...
|
||||
init { _field = "value" } // ...before an assignment to it.
|
||||
def write { System.print(_field) } // Compile a use of the field...
|
||||
def init { _field = "value" } // ...before an assignment to it.
|
||||
}
|
||||
|
||||
var foo = Foo.new()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
class Iter {
|
||||
construct new(value) { _value = value }
|
||||
iterate(iterator) { _value }
|
||||
iteratorValue(iterator) { "value" }
|
||||
def iterate(iterator) { _value }
|
||||
def iteratorValue(iterator) { "value" }
|
||||
}
|
||||
|
||||
// False and null are false.
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
class Foo {
|
||||
static foreign method // expect error
|
||||
static def foreign method // expect error
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
method() {
|
||||
def method() {
|
||||
_field = "value"
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,5 @@
|
||||
class Foo {
|
||||
foreign method { "body" } // expect error
|
||||
}
|
||||
foreign def method { "body" } // expect error
|
||||
} // expect error
|
||||
|
||||
// The second error is cascaded.
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
class Foo {
|
||||
foreign someUnknownMethod // expect runtime error: Could not find foreign method 'someUnknownMethod' for class Foo in module 'main'.
|
||||
foreign def someUnknownMethod // expect runtime error: Could not find foreign method 'someUnknownMethod' for class Foo in module 'main'.
|
||||
}
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
|
||||
getter {
|
||||
def getter {
|
||||
System.print("getter")
|
||||
}
|
||||
|
||||
setter=(value) {
|
||||
def setter=(value) {
|
||||
System.print("setter")
|
||||
}
|
||||
|
||||
method(a) {
|
||||
def method(a) {
|
||||
System.print("method")
|
||||
}
|
||||
}
|
||||
@ -17,7 +17,7 @@ class Foo {
|
||||
class Bar is Foo {
|
||||
construct new() {}
|
||||
|
||||
test {
|
||||
def test {
|
||||
getter // expect: getter
|
||||
setter = "value" // expect: setter
|
||||
method("arg") // expect: method
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
|
||||
getter {
|
||||
def getter {
|
||||
System.print("getter")
|
||||
}
|
||||
|
||||
setter=(value) {
|
||||
def setter=(value) {
|
||||
System.print("setter")
|
||||
}
|
||||
|
||||
method(a) {
|
||||
def method(a) {
|
||||
System.print("method")
|
||||
}
|
||||
|
||||
test {
|
||||
def test {
|
||||
getter // expect: getter
|
||||
setter = "value" // expect: setter
|
||||
method("arg") // expect: method
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
|
||||
bar { "getter" }
|
||||
def bar { "getter" }
|
||||
|
||||
test {
|
||||
def test {
|
||||
System.print(bar) // expect: getter
|
||||
|
||||
{
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
|
||||
bar=(value) {
|
||||
def bar=(value) {
|
||||
System.print("setter")
|
||||
return value
|
||||
}
|
||||
|
||||
test {
|
||||
def test {
|
||||
bar = "value" // expect: setter
|
||||
|
||||
{
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
class Outer {
|
||||
construct new() {}
|
||||
|
||||
getter {
|
||||
def getter {
|
||||
System.print("outer getter")
|
||||
}
|
||||
|
||||
setter=(value) {
|
||||
def setter=(value) {
|
||||
System.print("outer setter")
|
||||
}
|
||||
|
||||
method(a) {
|
||||
def method(a) {
|
||||
System.print("outer method")
|
||||
}
|
||||
|
||||
test {
|
||||
def test {
|
||||
getter // expect: outer getter
|
||||
setter = "value" // expect: outer setter
|
||||
method("arg") // expect: outer method
|
||||
@ -21,19 +21,19 @@ class Outer {
|
||||
class Inner {
|
||||
construct new() {}
|
||||
|
||||
getter {
|
||||
def getter {
|
||||
System.print("inner getter")
|
||||
}
|
||||
|
||||
setter=(value) {
|
||||
def setter=(value) {
|
||||
System.print("inner setter")
|
||||
}
|
||||
|
||||
method(a) {
|
||||
def method(a) {
|
||||
System.print("inner method")
|
||||
}
|
||||
|
||||
test {
|
||||
def test {
|
||||
getter // expect: inner getter
|
||||
setter = "value" // expect: inner setter
|
||||
method("arg") // expect: inner method
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
class Foo {
|
||||
static getter {
|
||||
static def getter {
|
||||
System.print("getter")
|
||||
}
|
||||
|
||||
static setter=(value) {
|
||||
static def setter=(value) {
|
||||
System.print("setter")
|
||||
}
|
||||
|
||||
static method(a) {
|
||||
static def method(a) {
|
||||
System.print("method")
|
||||
}
|
||||
|
||||
static test {
|
||||
static def test {
|
||||
getter // expect: getter
|
||||
setter = "value" // expect: setter
|
||||
method("arg") // expect: method
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Foo {
|
||||
static methodOnFoo { System.print("foo") }
|
||||
static def methodOnFoo { System.print("foo") }
|
||||
}
|
||||
|
||||
class Bar is Foo {}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
|
||||
foo(a, b) {
|
||||
def foo(a, b) {
|
||||
_field1 = a
|
||||
_field2 = b
|
||||
}
|
||||
|
||||
fooPrint {
|
||||
def fooPrint {
|
||||
System.print(_field1)
|
||||
System.print(_field2)
|
||||
}
|
||||
@ -15,12 +15,12 @@ class Foo {
|
||||
class Bar is Foo {
|
||||
construct new() {}
|
||||
|
||||
bar(a, b) {
|
||||
def bar(a, b) {
|
||||
_field1 = a
|
||||
_field2 = b
|
||||
}
|
||||
|
||||
barPrint {
|
||||
def barPrint {
|
||||
System.print(_field1)
|
||||
System.print(_field2)
|
||||
}
|
||||
|
||||
34
test/language/inheritance/inherit_from_enclosing.wren
Normal file
34
test/language/inheritance/inherit_from_enclosing.wren
Normal file
@ -0,0 +1,34 @@
|
||||
class Outer {
|
||||
construct new() {}
|
||||
|
||||
def outerBefore() { System.print("Outer before") }
|
||||
def shadowOuter() { System.print("bad") }
|
||||
|
||||
class Inner {
|
||||
construct new() {}
|
||||
|
||||
def innerBefore() { System.print("Inner before") }
|
||||
def shadowInner() { System.print("bad") }
|
||||
|
||||
class MoreInner {
|
||||
construct new() {}
|
||||
|
||||
def shadowOuter() { System.print("ok") }
|
||||
def shadowInner() { System.print("ok") }
|
||||
}
|
||||
|
||||
def innerAfter() { System.print("Inner after") }
|
||||
}
|
||||
|
||||
def outerAfter() { System.print("Outer after") }
|
||||
}
|
||||
|
||||
var moreInner = Outer.new().Inner.new().MoreInner.new()
|
||||
moreInner.outerBefore() // expect: Outer before
|
||||
moreInner.outerAfter() // expect: Outer after
|
||||
moreInner.innerBefore() // expect: Inner before
|
||||
moreInner.innerAfter() // expect: Inner after
|
||||
moreInner.shadowOuter() // expect: ok
|
||||
moreInner.shadowInner() // expect: ok
|
||||
|
||||
// TODO: Test how super interacts with enclosing classes.
|
||||
@ -1,16 +1,16 @@
|
||||
class Foo {
|
||||
methodOnFoo { System.print("foo") }
|
||||
method(a) { System.print("foo") }
|
||||
method(a, b, c) { System.print("foo") }
|
||||
override { System.print("foo") }
|
||||
def methodOnFoo { System.print("foo") }
|
||||
def method(a) { System.print("foo") }
|
||||
def method(a, b, c) { System.print("foo") }
|
||||
def override { System.print("foo") }
|
||||
}
|
||||
|
||||
class Bar is Foo {
|
||||
construct new() {}
|
||||
methodOnBar { System.print("bar") }
|
||||
method(a, b) { System.print("bar") }
|
||||
method(a, b, c, d) { System.print("bar") }
|
||||
override { System.print("bar") }
|
||||
def methodOnBar { System.print("bar") }
|
||||
def method(a, b) { System.print("bar") }
|
||||
def method(a, b, c, d) { System.print("bar") }
|
||||
def override { System.print("bar") }
|
||||
}
|
||||
|
||||
var bar = Bar.new()
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
class Foo {
|
||||
construct new() { _field = "Foo field" }
|
||||
|
||||
closeOverFooGet {
|
||||
def closeOverFooGet {
|
||||
return Fn.new { Fn.new { _field } }
|
||||
}
|
||||
|
||||
closeOverFooSet {
|
||||
def closeOverFooSet {
|
||||
return Fn.new { Fn.new { _field = "new foo value" } }
|
||||
}
|
||||
}
|
||||
@ -16,11 +16,11 @@ class Bar is Foo {
|
||||
_field = "Bar field"
|
||||
}
|
||||
|
||||
closeOverBarGet {
|
||||
def closeOverBarGet {
|
||||
return Fn.new { Fn.new { _field } }
|
||||
}
|
||||
|
||||
closeOverBarSet {
|
||||
def closeOverBarSet {
|
||||
return Fn.new { Fn.new { _field = "new bar value" } }
|
||||
}
|
||||
}
|
||||
|
||||
15
test/language/inheritance/inherited_shadows_enclosing.wren
Normal file
15
test/language/inheritance/inherited_shadows_enclosing.wren
Normal file
@ -0,0 +1,15 @@
|
||||
class Base {
|
||||
def method() { System.print("Base") }
|
||||
}
|
||||
|
||||
class Outer {
|
||||
construct new() {}
|
||||
|
||||
def method() { System.print("Outer") }
|
||||
|
||||
class Inner is Base {
|
||||
construct new() {}
|
||||
}
|
||||
}
|
||||
|
||||
Outer.new().Inner.new().method() // expect: Base
|
||||
@ -1,23 +1,23 @@
|
||||
class Foo {
|
||||
construct new() {}
|
||||
method { "getter" }
|
||||
method() { "no args" }
|
||||
method(a) { a }
|
||||
method(a, b) { a + b }
|
||||
method(a, b, c) { a + b + c }
|
||||
method(a, b, c, d) { a + b + c + d }
|
||||
method(a, b, c, d, e) { a + b + c + d + e }
|
||||
method(a, b, c, d, e, f) { a + b + c + d + e + f }
|
||||
method(a, b, c, d, e, f, g) { a + b + c + d + e + f + g }
|
||||
method(a, b, c, d, e, f, g, h) { a + b + c + d + e + f + g + h }
|
||||
method(a, b, c, d, e, f, g, h, i) { a + b + c + d + e + f + g + h + i }
|
||||
method(a, b, c, d, e, f, g, h, i, j) { a + b + c + d + e + f + g + h + i + j }
|
||||
method(a, b, c, d, e, f, g, h, i, j, k) { a + b + c + d + e + f + g + h + i + j + k}
|
||||
method(a, b, c, d, e, f, g, h, i, j, k, l) { a + b + c + d + e + f + g + h + i + j + k + l}
|
||||
method(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}
|
||||
method(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}
|
||||
method(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}
|
||||
method(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}
|
||||
def method { "getter" }
|
||||
def method() { "no args" }
|
||||
def method(a) { a }
|
||||
def method(a, b) { a + b }
|
||||
def method(a, b, c) { a + b + c }
|
||||
def method(a, b, c, d) { a + b + c + d }
|
||||
def method(a, b, c, d, e) { a + b + c + d + e }
|
||||
def method(a, b, c, d, e, f) { a + b + c + d + e + f }
|
||||
def method(a, b, c, d, e, f, g) { a + b + c + d + e + f + g }
|
||||
def method(a, b, c, d, e, f, g, h) { a + b + c + d + e + f + g + h }
|
||||
def method(a, b, c, d, e, f, g, h, i) { a + b + c + d + e + f + g + h + i }
|
||||
def method(a, b, c, d, e, f, g, h, i, j) { a + b + c + d + e + f + g + h + i + j }
|
||||
def method(a, b, c, d, e, f, g, h, i, j, k) { a + b + c + d + e + f + g + h + i + j + k}
|
||||
def method(a, b, c, d, e, f, g, h, i, j, k, l) { a + b + c + d + e + f + g + h + i + j + k + l}
|
||||
def method(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}
|
||||
def method(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}
|
||||
def method(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}
|
||||
def method(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}
|
||||
}
|
||||
|
||||
var foo = Foo.new()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user