mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-11 22:28:45 +01:00
Lots of work on docs.
This commit is contained in:
@ -1,27 +1,27 @@
|
||||
^title Branching
|
||||
|
||||
*Flow control* is used to determine which chunks of code are executed and how many times. Expressions and statements for deciding whether or not to execute some code are called *branching* and are covered here. To execute something more than once, you'll want [*looping*](looping.html).
|
||||
*Control flow* is used to determine which chunks of code are executed and how many times. Expressions and statements for deciding whether or not to execute some code are called *branching* and are covered here. To execute something more than once, you'll want [*looping*](looping.html).
|
||||
|
||||
## Truthiness
|
||||
|
||||
Flow control is about evaluating an expression and then choosing which code to execute based on whether or not the result is "true". That's easy if the value happens to be a boolean, but what if it's some other type?
|
||||
Branching is conditional on the value of some expression. We take the entire universe of possible values and divide them into two buckets: some we consider "true" and the rest are "false". If the expression results in a value in the true bucket, we branch one way. Otherwise, we go the other way.
|
||||
|
||||
Languages handle this by having a set of rules for what values of any given type are "true" and will cause a condition to be met. Wren calls this "truthiness" and "falsiness". The rules are simple (and follow Ruby):
|
||||
Obviously, the boolean `true` is in the "true" bucket and `false` is in "false", but what about values of other types? The choice is ultimately arbitrary, and different languages have different rules. Wren's rules follow Ruby:
|
||||
|
||||
* The boolean value `false` is falsey.
|
||||
* The null value `null` is falsey.
|
||||
* Everything else is truthy.
|
||||
* The boolean value `false` is false.
|
||||
* The null value `null` is false.
|
||||
* Everything else is true.
|
||||
|
||||
This means `0`, empty strings, and empty collections are all considered truthy values.
|
||||
This means `0`, empty strings, and empty collections are all considered "true" values.
|
||||
|
||||
## If statements
|
||||
|
||||
The simplest flow control structure, `if` lets you conditionally skip a chunk of code. It looks like this:
|
||||
The simplest branching statement, `if` lets you conditionally skip a chunk of code. It looks like this:
|
||||
|
||||
:::wren
|
||||
if (ready) io.write("go!")
|
||||
|
||||
That will evaluate the parenthesized expression after `if`. If it's truthy, then the expression after the condition is evaluated. Otherwise it is skipped. Instead of an expression, you can have a block:
|
||||
That evaluates the parenthesized expression after `if`. If it's true, then the statement after the condition is evaluated. Otherwise it is skipped. Instead of a statement, you can have a block:
|
||||
|
||||
:::wren
|
||||
if (ready) {
|
||||
@ -29,7 +29,7 @@ That will evaluate the parenthesized expression after `if`. If it's truthy, then
|
||||
io.write("go!")
|
||||
}
|
||||
|
||||
You may also provide an `else` branch. It will be evaluated if the condition is falsey:
|
||||
You may also provide an `else` branch. It will be executed if the condition is false:
|
||||
|
||||
:::wren
|
||||
if (ready) io.write("go!") else io.write("not ready!")
|
||||
@ -44,7 +44,7 @@ And, of course, it can take a block too:
|
||||
|
||||
## The logical operators `&&` and `||`
|
||||
|
||||
The `&&` and `||` operators are lumped here under flow control because they conditionally execute some code—they short-circuit. Both of them are infix operators, and, depending on the value of the left-hand side, the right-hand operand expression may or may not be evaluated.
|
||||
The `&&` and `||` operators are lumped here under branching because they conditionally execute some code—they short-circuit. Both of them are infix operators, and, depending on the value of the left-hand side, the right-hand operand expression may or may not be evaluated.
|
||||
|
||||
An `&&` ("logical and") expression evaluates the left-hand argument. If it's falsey, it returns that value. Otherwise it evaluates and returns the right-hand argument.
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Classes contain both *behavior* and *state*. Behavior is defined in *methods* wh
|
||||
|
||||
Classes are created using the `class` keyword, unsurprisingly:
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
class Unicorn {}
|
||||
|
||||
This creates a class named `Unicorn` with no methods or fields.
|
||||
@ -18,35 +18,35 @@ This creates a class named `Unicorn` with no methods or fields.
|
||||
|
||||
To make our unicorn do stuff, we need to give it methods.
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
class Unicorn {
|
||||
prance {
|
||||
IO.write("The unicorn prances in a fancy manner!")
|
||||
IO.print("The unicorn prances in a fancy manner!")
|
||||
}
|
||||
}
|
||||
|
||||
This defines a `prance` method that takes no arguments. To support parameters, add a parenthesized parameter list after the method's name:
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
// Inside class...
|
||||
prance(where, when) {
|
||||
IO.write("The unicorn prances in " + where + " at " + when)
|
||||
IO.print("The unicorn prances in " + where + " at " + when)
|
||||
}
|
||||
|
||||
Unlike most other dynamically-typed languages, in Wren you can have multiple methods in a class with the same name, as long as they take a different number of parameters. In other words, you can overload by arity. So this class is fine:
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
class Unicorn {
|
||||
prance {
|
||||
IO.write("The unicorn prances in a fancy manner!")
|
||||
IO.print("The unicorn prances in a fancy manner!")
|
||||
}
|
||||
|
||||
prance(where) {
|
||||
IO.write("The unicorn prances in " + where)
|
||||
IO.print("The unicorn prances in " + where)
|
||||
}
|
||||
|
||||
prance(where, when) {
|
||||
IO.write("The unicorn prances in " + where + " at " + when)
|
||||
IO.print("The unicorn prances in " + where + " at " + when)
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ A class can inherit from a "parent" or *superclass*. When you invoke a method on
|
||||
|
||||
By default, any new class inherits from `Object`, which is the superclass from which all other classes ultimately descend. You can specify a different parent class using `is` when you declare the class:
|
||||
|
||||
:::class
|
||||
:::dart
|
||||
class Pegasus is Unicorn {}
|
||||
|
||||
This declares a new class `Pegasus` that inherits from `Unicorn`.
|
||||
|
||||
@ -14,19 +14,26 @@ in a Lua-sized package.
|
||||
}
|
||||
|
||||
* **Wren is small.** The codebase stays under 5,000 semicolons to keep the
|
||||
language and libraries small enough to fit in your head. You can skim the
|
||||
whole thing in one sitting.
|
||||
language and libraries small enough to fit in your head. You can skim
|
||||
[the whole thing][src] in one sitting.
|
||||
|
||||
* **Wren is clean.** The codebase is *small*, but not *dense*. It is readable
|
||||
and lovingly-commented. It's written in warning-free standard C99.
|
||||
and [lovingly-commented][nan]. It's written in warning-free standard C99.
|
||||
|
||||
* **Wren is fast.** Wren has a fast single-pass compiler, tight bytecode, and
|
||||
a compact object representation.
|
||||
* **Wren is fast.** A fast single-pass compiler, tight bytecode, and a
|
||||
compact object representation help Wren [compete with other dynamic
|
||||
languages][perf].
|
||||
|
||||
* **Wren is class-based.** There are lots of scripting languages out there,
|
||||
but many have unusual or non-existent object models. Wren places
|
||||
classes front and center.
|
||||
[classes][] front and center.
|
||||
|
||||
* **Wren is a scripting language.** Wren is intended for embedding in
|
||||
applications. It has no dependencies, a small standard library,
|
||||
and an easy-to-use C API.
|
||||
and [an easy-to-use C API][usage].
|
||||
|
||||
[src]: https://github.com/munificent/wren/tree/master/src
|
||||
[nan]: https://github.com/munificent/wren/blob/46c1ba92492e9257aba6418403161072d640cb29/src/wren_value.h#L378-L433
|
||||
[perf]: performance.html
|
||||
[classes]: classes.html
|
||||
[usage]: usage.html
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
^title Looping
|
||||
|
||||
It's hard to write a useful program without executing some chunk of code repeatedly. To do that in Wren, you use looping statements. There are two in Wren, and they should be familiar if you've used other imperative languages.
|
||||
It's hard to write a useful program without executing some chunk of code repeatedly. To do that, you use looping statements. There are two in Wren, and they should be familiar if you've used other imperative languages.
|
||||
|
||||
## While statements
|
||||
|
||||
A `while` statement executes a chunk of code as long as a condition continues to hold. For example:
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
// Hailstone sequence.
|
||||
var n = 27
|
||||
while (n != 1) {
|
||||
if (n % 2 == 0) {
|
||||
@ -16,20 +17,21 @@ A `while` statement executes a chunk of code as long as a condition continues to
|
||||
}
|
||||
}
|
||||
|
||||
This evaluates the expression `n != 1`. If it is [truthy](flow-control.html), then it executes the body: `n = 3 * n + 1`. After that, it loops back to the top, and evaluates the condition again. It keeps doing this as long as the condition is evaluates to something truthy.
|
||||
This evaluates the expression `n != 1`. If it is [true](branching.html), then it executes the following body. After that, it loops back to the top, and evaluates the condition again. It keeps doing this as long as the condition evaluates to something true.
|
||||
|
||||
The condition for a while loop can be any expression, and must be surrounded by parentheses. The body of the loop is usually a curly block but can also be a single statement:
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
var n = 27
|
||||
while (n != 1) if (n % 2 == 0) n = n / 2 else n = 3 * n + 1
|
||||
|
||||
## For statements
|
||||
|
||||
While statements are useful when you want to loop indefinitely or according to some complex condition. But in most cases, you're looping through a [list](lists.html) or some other "sequence" object, or you're terating through a range of numbers. That's what `for` is for. It looks like this:
|
||||
While statements are useful when you want to loop indefinitely or according to some complex condition. But in most cases, you're looping through a [list](lists.html), a series of numbers, or some other "sequence" object. That's what `for` is for. It looks like this:
|
||||
|
||||
:::dart
|
||||
for (beatle in ["george", "john", "paul", "ringo"]) {
|
||||
IO.write(beatle)
|
||||
IO.print(beatle)
|
||||
}
|
||||
|
||||
A `for` loop has three components:
|
||||
@ -40,50 +42,58 @@ A `for` loop has three components:
|
||||
|
||||
3. A *body*. This is a curly block or a single statement. It gets executed once for each iteration of the loop.
|
||||
|
||||
### Numeric ranges
|
||||
|
||||
As you can see, a `for` loop works with lists. To loop over a range of consecutive integers, or loop a fixed number of times, you can use a *range* expression, like so:
|
||||
|
||||
:::dart
|
||||
for (i in 1..100) {
|
||||
IO.print(i)
|
||||
}
|
||||
|
||||
This will loop over the numbers from 1 to 100, including 100 itself. If you want to leave off the last value, use three dots instead of two:
|
||||
|
||||
:::dart
|
||||
for (i in 1...100) {
|
||||
IO.print(i)
|
||||
}
|
||||
|
||||
This looks like some special "range" syntax in the `for` loop, but it's actually just a pair of operators. The `..` and `...` syntax are infix "range" operators. Like [other operators](method-calls.html), they are just special syntax for a regular method call.
|
||||
|
||||
The number type implements them and returns instances of a `Range` class. That class in turn knows how to iterate over a series of numbers.
|
||||
|
||||
### The iterator protocol
|
||||
|
||||
It's important to be able to use looping constructs over user-defined sequence-like objects and not just built-in lists. To make that happen, the semantics of a `for` are defined in terms of an "iterator protocol". The loop itself doesn't know anything about lists or numbers, it just knows how to call two particular methods on the object that resulted from evaluating the sequence expression.
|
||||
Lists and ranges cover the two most common kinds of loops, but you should also be able to walk over your own sequence-like objects. To enable that, the semantics of a `for` are defined in terms of an "iterator protocol". The loop itself doesn't know anything about lists or ranges, it just knows how to call two particular methods on the object that resulted from evaluating the sequence expression.
|
||||
|
||||
It works like this. First Wren evaluates the sequence expression and stores it in a hidden variable. In our example, it will just be the list object. It also creates a hidden "iterator" variable an initializes it to `null`.
|
||||
It works like this. First Wren evaluates the sequence expression and stores it in a hidden variable. In the first example, it will just be the list object. It also creates a hidden "iterator" variable and initializes it to `null`.
|
||||
|
||||
At the beginning of each iteration, it calls `iterate()` on the sequence, and passes in the iterator. So in the first iteration, it always passes in `null`. The sequence's job is to take that iterator and advance it to the next element in the sequence (or, in the case where it's `null`, to advance it to the *first* element). It then returns either the new iterator, or `false` to indicate that there are no more elements.
|
||||
At the beginning of each iteration, it calls `iterate()` on the sequence, and passes in the iterator. (So in the first iteration, it always passes in `null`.) The sequence's job is to take that iterator and advance it to the next element in the sequence (or, in the case where it's `null`, to advance it to the *first* element). It then returns either the new iterator, or `false` to indicate that there are no more elements.
|
||||
|
||||
If `false` is returned, Wren exits out of the loop and we're done. If anything else is returned, that means that we have advanced to a new valid element. To get that, Wren then calls `iteratorValue()` on the sequence and passes in that iterator value that it just got from the sequence. The sequence uses that to look up and return the appropriate element.
|
||||
If `false` is returned, Wren exits out of the loop and we're done. If anything else is returned, that means that we have advanced to a new valid element. To get that, Wren then calls `iteratorValue()` on the sequence and passes in the iterator value that it just got from calling `iterate()`. The sequence uses that to look up and return the appropriate element.
|
||||
|
||||
In other words, from Wren's perspective, the above loop looks something like this:
|
||||
In other words, from Wren's perspective, the example loop looks something like this:
|
||||
|
||||
:::dart
|
||||
{
|
||||
var iter_
|
||||
var seq_ = ["george", "john", "paul", "ringo"]
|
||||
while (iter_ = seq_.iterate(iter_)) {
|
||||
var beatle = seq_.iteratorValue(iter_)
|
||||
IO.write(beatle)
|
||||
IO.print(beatle)
|
||||
}
|
||||
}
|
||||
|
||||
The built-in list type implements `iterate()` and `iteratorValue()` to walk over the list elements. You can implement the same methods in your classes to make your own types iterable.
|
||||
|
||||
### Numeric ranges
|
||||
|
||||
That just leaves iterating over numbers. Often you want to do something a fixed number of times, or with one of each of a range of consecutive numbers. In Wren, that looks like this:
|
||||
|
||||
for (i in 1..100) {
|
||||
IO.write(i)
|
||||
}
|
||||
|
||||
This looks like some special range support in the `for` statement, but it's actually just the iterator protocol all over again. The `..` is a "range" operator. Like all other operators in Wren, it's just syntax for a method call. In this case, we're calling `..` on `1` and passing in `100`. The above example could just as well be written:
|
||||
|
||||
var nums = 1..100
|
||||
for (i in nums) IO.write(i)
|
||||
|
||||
The number class implements the `..` method and returns a `Range` object. This is just a simple data structure that tracks a minimum and maximum (here `1` and `100`). The range class implements `iterate()` and `iteratorValue()` to generate a series of consecutive numbers.
|
||||
|
||||
If you don't want to execute the body of the loop for the last value, you can use `...` instead to get a half-open interval:
|
||||
|
||||
for (i in 1...100) {
|
||||
IO.write(i)
|
||||
}
|
||||
|
||||
This will print up to `99`, but not `100`.
|
||||
The built-in List and Range types implement `iterate()` and `iteratorValue()` to walk over their respective sequences. You can implement the same methods in your classes to make your own types iterable.
|
||||
|
||||
## Break statements
|
||||
|
||||
Sometimes, right in the middle of a loop body, you decide you want to bail out and stop. To do that, you can use a `break` statement. It's just the `break` keyword all by itself. That will immediately exit out of the nearest enclosing `while` or `for` loop.
|
||||
|
||||
:::dart
|
||||
for (i in [1, 2, 3, 4]) {
|
||||
IO.print(i)
|
||||
if (i == 3) break
|
||||
}
|
||||
|
||||
So this program will print the numbers from 1 to 3, but will not print 4.
|
||||
|
||||
@ -2,33 +2,13 @@
|
||||
|
||||
## Why did you create Wren?
|
||||
|
||||
Most creative endeavors aren't immediately met with existential crises, but for some reason many programmers don't seem to like the idea of new languages. My reason for creating Wren is pretty simple: I really enjoy building things.
|
||||
Most creative endeavors aren't immediately met with existential crises, but for some reason many programmers don't seem to like the idea of new languages. Here's the niche I'm trying to fill:
|
||||
|
||||
I find it deeply soothing to write a bit of code, see a new test pass and check it in. I seem to be wired to seek concrete accomplishment. I'm not very good at relaxing and doing nothing. I need to feel like I earned a gold star, or a few XP, or levelled up, or something. Too many years as a teacher's pet has left it's mark on me.
|
||||
There are a few scripting languages used for embedding in applications. Lua is the main one. TCL used to be. There's also Guile, increasingly JavaScript, and some applications embed Python. I'm an ex-game developer, so when I think "scripting", I tend to think "game scripting".
|
||||
|
||||
## Why did you create *Wren?*
|
||||
Lua is nice: it's small, simple, and fast. But—and I don't mean this as a criticism—it's also weird if you're used to languages like C++ and Java. The syntax is different. The semantics, especially the object model are unusual. Anyone can get used to 1-based indexing, but things like metatables really show that objects were bolted onto Lua after the fact.
|
||||
|
||||
I guess that's not the question you were asking. You want to know why I created *Wren* as opposed to some other language?
|
||||
|
||||
* I already know C and it's fun to code in. When I code in my free time, I have limited patience for learning new things. I want to *make*, not *learn-how-to-make*.
|
||||
|
||||
* Dynamically-typed languages are a lot simpler to design and build. I like static types, but good type systems are *hard*. Implementing a type-checker isn't too hard, but doing things like stack maps for GC didn't seem like fun to me.
|
||||
|
||||
* Creating a scripting language for embedding means I don't have to make a huge standard library before the langauge is useful. Creating a new language is a ton of work. You have to design it, document it, implement it, optimize it. On top of that, you need a standard library, packages, tooling, a while ecosystem. Doing a scripting language cuts that problem down to a much more manageable size while still yielding a language that real people can use for real things.
|
||||
|
||||
* I really like classes and objects. I could go into this at length, but I'll spare you.
|
||||
|
||||
## Why should *I* care that you created Wren?
|
||||
|
||||
Ah, here's the real question. You want to know why Wren might be relevant to *you*. Good question! Here's the niche I'm trying to fill:
|
||||
|
||||
There are a handful of scripting languages that are in use for embedding in applications. Lua is the big one. There's also Guile, increasingly JavaScript, and some applications embed Python. I'm an ex-game developer, so when I think "scripting", I tend to think "game scripting".
|
||||
|
||||
Lua is a good answer there: it's small, simple, and fast. But, and I don't mean this as a criticism, it's also weird if you're used to languages like C++ and Java. The syntax is clean but different. The semantics, especially the object model are unusual. Anyone can get used to 1-based indexing, but things like metatables really show that objects were bolted onto Lua after the fact.
|
||||
|
||||
I feel like there's an opportunity for a language that's as small, simple, and fast as Lua, but also one that feels familiar and natural to someone with a conventional OOP background. Wren is my attempt at that.
|
||||
|
||||
Here's an example of object-oriented programming in Lua:
|
||||
I think there's room for a language as simple as Lua, but that feels natural to someone with an OOP background. Wren is my attempt at that. Here's an example of object-oriented programming in Lua:
|
||||
|
||||
:::lua
|
||||
Account = {}
|
||||
@ -61,32 +41,23 @@ Here's the same example in Wren:
|
||||
var acc = new Account(100)
|
||||
acc.withdraw(100)
|
||||
|
||||
I also feel there's room for a language with a minimal, beautiful *implementation*. Lua's code is clean, but not well documented or easy to read. With Wren, I'm trying to make the code as approachable as possible so that others feel confident using it and extending it.
|
||||
|
||||
## Why compile to bytecode?
|
||||
|
||||
Interpreters come in three flavors, in order of increasing performance: "tree-walk" or line-based interpreters run the code directly from the source or a parse tree. Bytecode interpreters compile the parsed source code to a set of instructions for a virtual machine which they then implement. JIT compilers do something similar but actually compile to the host machine's native instruction set.
|
||||
The [performance page](performance.html) has more details, but the short answer is that bytecode is a nice trade-off between performance and simplicity. Also:
|
||||
|
||||
JIT compilers are nice, but:
|
||||
|
||||
* They are *much* more complex to implement and debug.
|
||||
* Since they use the machine's native stack, cooperative multitasking (fibers) have to do lower-level shenanigans.
|
||||
* Many devices like iPhones and game consoles do not allow runtime generated code to be executed.
|
||||
|
||||
Bytecode, meanwhile is quite simple while also fast enough for real-world usage. It also makes supporting fibers straightforward since the virtual machine defines its own stack(s) and can switch between them easily.
|
||||
* Many devices like iPhones and game consoles don't allow executing code generated at runtime, which rules out a JIT.
|
||||
* I think fibers are a really powerful tool, and implementing them is straightforward in a bytecode VM which doesn't use the native stack.
|
||||
|
||||
## What about your other languages?
|
||||
|
||||
This is a strange question if you don't happen to know who I am. In the past, I've hacked on and blogged about a couple of other hobby languages. The two most frequent are [Finch](http://finch.stuffwithstuff.com/) and [Magpie](http://magpie-lang.org/). Why a third?
|
||||
This is a strange question if you don't happen to know [who I am](http://journal.stuffwithstuff.com/). In the past, I've hacked on and blogged about a couple of other hobby languages like [Finch](http://finch.stuffwithstuff.com/) and [Magpie](http://magpie-lang.org/).
|
||||
|
||||
Well, actually, Wren isn't my *third*, it's probably closer to tenth at this point. I try out lots of different ideas. Wren grew directly out of Finch. I started Finch to learn more about implementing an interpreter and also about the prototype paradigm. I learned a ton about both.
|
||||
I started Finch to learn more about implementing an interpreter and also about the prototype paradigm. I learned a ton about both. Critically, I learned that I really prefer classes over prototypes. I started retrofitting classes into Finch but realized it was too big of a change, and thus Wren was born.
|
||||
|
||||
What I learned in particular is that C++ is nice but not that necessary if your codebase is relatively small. And I learned that I really prefer classes over prototypes. I started retrofitting classes into Finch but eventually realized it was too big of a change to do through evolution, and thus Wren was born.
|
||||
Wren is a replacement for Finch to me. I gave it a new name mainly so that I can keep Finch around in case other people want to take it and do something with it. I don't have any intention to work on it anymore.
|
||||
|
||||
Wren is effectively a replacement for Finch to me. I gave it a new name mainly so that I can keep Finch around in case other people want to take it and do something with it. I don't have any intention to work on Finch anymore. Wren scratches that same itch 10x.
|
||||
Magpie is a trickier one. I really like the ideas behind Magpie. It's the general-purpose language I wish I had much of the time. I love patterns and multiple dispatch. I like how it integrates the event-based IO of libuv with the simplicity of fibers.
|
||||
|
||||
Magpie is a trickier one. I really really like the ideas behind Magpie. It's the general-purpose language I wish I had most of the time. I love patterns and multiple-dispatch. I like how it integrates the event-based IO of libuv with the simplicity of fibers.
|
||||
But it's also a much more challenging project. As a general-purpose language, there's a ton of library work to do before Magpie is useful for anything. It has some unresolved GC issues. And I'm frankly not skilled enough right now to implement multiple dispatch efficiently.
|
||||
|
||||
But it's also a much more challenging project. As a general-purpose language, there's a ton of library work to do before Magpie is useful for anything. It has some unresolved GC issues. And I'm frankly not skilled enough right now to implement multiple dispatch efficiently. Meanwhile, since I started working on Magpie, [Julia](http://julialang.org/) has appeared and [Dylan](http://opendylan.org/) has reappeared. I was really astonished at how much Magpie had in common with Julia when it came out. I created Magpie partially to carry the torch of multiple dispatch, but others are starting to spread that light now.
|
||||
|
||||
I think I have a greater chance of success with Wren, but that doesn't mean I don't still love Magpie and want to work on it. I tend to rotate through projects. I rarely abandon them forever, I just take long breaks. Ask me about my roguelike sometime.
|
||||
Meanwhile, since I started working on Magpie, [Julia](http://julialang.org/) appeared and [Dylan](http://opendylan.org/) *re*appeared. I created Magpie partially to carry the torch of multiple dispatch, but others are starting to spread that light now.
|
||||
|
||||
@ -11,13 +11,13 @@ bytecode for efficiency, but that's an implementation detail).
|
||||
|
||||
Line comments start with `//` and end at the end of the line:
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
// This is a comment.
|
||||
|
||||
Block comments start with `/*` and end with `*/`. They can span multiple lines
|
||||
or be within a single one. Unlike C, block comments can nest in Wren:
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
/* This is /* a nested */ comment. */
|
||||
|
||||
## Reserved Words
|
||||
@ -25,7 +25,7 @@ or be within a single one. Unlike C, block comments can nest in Wren:
|
||||
Some people like to see all of the reserved words in a programming language in
|
||||
one lump. If you're one of those folks, here you go:
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
break class else false fn for if in is
|
||||
null return static this true var while
|
||||
|
||||
@ -36,7 +36,7 @@ languages in the C tradition. However, Wren treats newlines as equivalent
|
||||
to a semicolon whenever it makes sense. In practice, this means you almost
|
||||
never write `;` unless you want to cram a bunch of statements on one line.
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
// Two statements:
|
||||
io.write("hi")
|
||||
io.write("bye")
|
||||
@ -46,7 +46,7 @@ newline as a semicolon would trip things up. To handle that, Wren has a very
|
||||
simple rule. It ignores a newline following any token that can't end a
|
||||
statement. Specifically, that means any of these:
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
( [ { . , * / % + - | || & && ! ~ = < > <= >= == !=
|
||||
class else if is static var while
|
||||
|
||||
@ -58,7 +58,7 @@ there, don't worry, you should be fine here.
|
||||
|
||||
Identifiers are similar to other programming languages. They start with a letter or underscore and may contain letters, digits, and underscores. Case is sensitive.
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
hi
|
||||
camelCase
|
||||
PascalCase
|
||||
@ -77,4 +77,4 @@ type test. The left operand is an object and the right operand is a class. It
|
||||
evaluates to `true` if the object is an instance of the class (or one of its
|
||||
subclasses).
|
||||
|
||||
**TODO: blocks, assignment, functions, lists, maps**
|
||||
**TODO: blocks, assignment, maps**
|
||||
|
||||
@ -41,6 +41,8 @@
|
||||
</ul>
|
||||
<h2>Usage</h2>
|
||||
<ul>
|
||||
<li>Concurrency</li>
|
||||
<li>Error handling</li>
|
||||
<li>Standalone</li>
|
||||
<li>Embedding</li>
|
||||
</ul>
|
||||
|
||||
@ -15,7 +15,7 @@ A boolean value represents truth or falsehood. There are two boolean literals,
|
||||
|
||||
Like other scripting languages, Wren has a single numeric type: double-precision floating point. Number literals look like you expect coming from other languages:
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
0
|
||||
1234
|
||||
-5678
|
||||
@ -29,12 +29,12 @@ Numbers are instances of the `Num` class.
|
||||
|
||||
Strings are chunks of text. String literals are surrounded in double quotes:
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
"hi there"
|
||||
|
||||
A couple of escape characters are supported:
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
"\n" // Newline.
|
||||
"\"" // A double quote character.
|
||||
"\\" // A backslash.
|
||||
|
||||
@ -3,36 +3,36 @@
|
||||
Variables are named slots for storing values. You can define a new variable in
|
||||
Wren using a `var` statement, like so:
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
var a = 1 + 2
|
||||
|
||||
This creates a new variable `a` in the current scope and initializes it with
|
||||
the result of the expression following the `=`. Once a variable has been
|
||||
defined, it can be accessed by name as you would expect.
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
var animal = "Slow Loris"
|
||||
io.write(animal) // prints "Slow Loris"
|
||||
io.write(animal) // Prints "Slow Loris".
|
||||
|
||||
## Scope
|
||||
|
||||
Wren has true block scope: a variable exists from the point where it is
|
||||
defined until the end of the block where that definition appears.
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
{
|
||||
io.write(a) // ERROR! a doesn't exist yet
|
||||
io.write(a) // ERROR! a doesn't exist yet.
|
||||
var a = 123
|
||||
io.write(a) // "123"
|
||||
}
|
||||
io.write(a) // ERROR! a doesn't exist anymore
|
||||
io.write(a) // ERROR! a doesn't exist anymore.
|
||||
|
||||
Variables defined at the top level of a script are *global*. All other variables
|
||||
are *local*. Declaring a variable in an inner scope with the same name as an
|
||||
outer one is called *shadowing* and is not an error (although it's not
|
||||
something you likely intend to do much).
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
var a = "outer"
|
||||
{
|
||||
var a = "inner"
|
||||
@ -42,7 +42,7 @@ something you likely intend to do much).
|
||||
|
||||
Declaring a variable with the same name in the *same* scope *is* an error.
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
var a = "hi"
|
||||
var a = "again" // ERROR!
|
||||
|
||||
@ -50,6 +50,7 @@ Declaring a variable with the same name in the *same* scope *is* an error.
|
||||
|
||||
After a variable has been declared, you can assign to it using `=`:
|
||||
|
||||
:::dart
|
||||
var a = 123
|
||||
a = 234
|
||||
|
||||
@ -60,7 +61,7 @@ doesn't roll with implicit variable definition.
|
||||
When used in a larger expression, an assignment expression evaluates to the
|
||||
assigned value.
|
||||
|
||||
:::wren
|
||||
:::dart
|
||||
var a = "before"
|
||||
io.write(a = "after") // Prints "after".
|
||||
|
||||
|
||||
Reference in New Issue
Block a user