mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-13 23:28:53 +01:00
Compare commits
119 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d4daa0e048 | |||
| 87f9a1782a | |||
| fb67b9833d | |||
| 8d972987bc | |||
| 1aa0aa9f2f | |||
| 9817424fb3 | |||
| 54b3131573 | |||
| 571cd6d049 | |||
| 21f294f5c3 | |||
| 55ade2dcf2 | |||
| 5a680dd3f1 | |||
| 7bfb26e161 | |||
| 5e6924b1f4 | |||
| 8a988a1bcc | |||
| f3d3299199 | |||
| fc9952b5bd | |||
| fade23f3c4 | |||
| 7edbb7cff6 | |||
| 2906fefb86 | |||
| 595cf78ea3 | |||
| 309e85b6e3 | |||
| 1904ef0cbc | |||
| 4fad53b67d | |||
| b95dbd885b | |||
| 23d018726c | |||
| c2e18fb3c9 | |||
| 27a3d2b127 | |||
| 05a7ae9e45 | |||
| 5df2928941 | |||
| ac09c98720 | |||
| 51c5ae0aae | |||
| c7a9e19fc5 | |||
| 301a5cf3c7 | |||
| 30ea6fa8b4 | |||
| a56214433f | |||
| f9a6fc2225 | |||
| 8e711801b9 | |||
| adb26eb4fa | |||
| 63c5ce48f8 | |||
| 5ad0dc199e | |||
| 9abaa46a67 | |||
| 4ec4c3857a | |||
| 28116018b1 | |||
| 366afc54be | |||
| 32979d61d3 | |||
| f29534b639 | |||
| 5e7183fcb6 | |||
| 200bcd0f93 | |||
| f8411449d8 | |||
| ec88d79906 | |||
| a6881381d5 | |||
| 177cc7f42d | |||
| ea082f235b | |||
| d6e64707ff | |||
| 1e9d6c9ebc | |||
| 69265c6b8c | |||
| 6037aa17fe | |||
| b182fb8cf6 | |||
| 769a699b28 | |||
| f9bd2963be | |||
| 358bd1a5fa | |||
| 8d19b3f630 | |||
| 6da577069a | |||
| d68486c696 | |||
| ae8b78362e | |||
| a6d5e622e5 | |||
| bcef1814ce | |||
| 2426ee44b5 | |||
| 2dde59d3b0 | |||
| c3c7196fdd | |||
| e492effb75 | |||
| f3c5ca34a7 | |||
| 84a3201099 | |||
| 323dab79ea | |||
| 99bc2be625 | |||
| 5cac3af2f2 | |||
| 5aba452874 | |||
| 1de14d25d1 | |||
| 5b60e42754 | |||
| bb6d702118 | |||
| 08b24c9dd4 | |||
| 4a121ec1c2 | |||
| 0387e95df4 | |||
| 8b6e6bc677 | |||
| 2db55f7d29 | |||
| 843ff4405f | |||
| 247097c0f8 | |||
| 46c1c08fea | |||
| bc6e1c63b5 | |||
| 61c1c991b1 | |||
| 734b275c4a | |||
| 29080e85b0 | |||
| 714ef30740 | |||
| 200223b0fa | |||
| 1f85ee1aa6 | |||
| 59b07c9141 | |||
| 050b2b0a51 | |||
| 9a9968d278 | |||
| 28309824bf | |||
| 95fea0df56 | |||
| 0a1c30118f | |||
| 81b52f8c06 | |||
| 074545a79a | |||
| 7343aff563 | |||
| e8eabc5a67 | |||
| 846ade932a | |||
| 0a39662a55 | |||
| 8e333df6a5 | |||
| d984ed4ac8 | |||
| bf231f3c48 | |||
| 475d254258 | |||
| 7bbc735545 | |||
| 66c723ab70 | |||
| 8edc610287 | |||
| eb449ccf73 | |||
| 2a1483f675 | |||
| 0708b9dfc1 | |||
| d4c04663f9 | |||
| 3311324aea |
21
LICENSE
21
LICENSE
@ -1,21 +0,0 @@
|
||||
Wren uses the MIT License:
|
||||
|
||||
Copyright (c) 2013 Robert Nystrom
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
66
Makefile
66
Makefile
@ -1,66 +0,0 @@
|
||||
# Compiler flags.
|
||||
CFLAGS = -std=c99 -Wall -Werror
|
||||
# TODO: Add -Wextra.
|
||||
DEBUG_CFLAGS = -O0 -DDEBUG
|
||||
RELEASE_CFLAGS = -Os
|
||||
|
||||
# Files.
|
||||
SOURCES = $(wildcard src/*.c)
|
||||
HEADERS = $(wildcard src/*.h)
|
||||
OBJECTS = $(SOURCES:.c=.o)
|
||||
|
||||
DEBUG_OBJECTS = $(addprefix build/debug/, $(notdir $(OBJECTS)))
|
||||
RELEASE_OBJECTS = $(addprefix build/release/, $(notdir $(OBJECTS)))
|
||||
|
||||
.PHONY: all clean test builtin docs watchdocs
|
||||
|
||||
all: release
|
||||
|
||||
clean:
|
||||
@rm -rf build wren wrend
|
||||
|
||||
prep:
|
||||
@mkdir -p build/debug build/release
|
||||
|
||||
# Debug build.
|
||||
debug: prep wrend
|
||||
|
||||
# Debug command-line interpreter.
|
||||
wrend: $(DEBUG_OBJECTS)
|
||||
$(CC) $(CFLAGS) $(DEBUG_CFLAGS) -Iinclude -lm -o wrend $^
|
||||
|
||||
# Debug object files.
|
||||
build/debug/%.o: src/%.c include/wren.h $(HEADERS)
|
||||
$(CC) -c $(CFLAGS) $(DEBUG_CFLAGS) -Iinclude -o $@ $<
|
||||
|
||||
# Release build.
|
||||
release: prep wren
|
||||
|
||||
# Release command-line interpreter.
|
||||
wren: $(RELEASE_OBJECTS)
|
||||
$(CC) $(CFLAGS) $(RELEASE_CFLAGS) -Iinclude -lm -o wren $^
|
||||
|
||||
# Release object files.
|
||||
build/release/%.o: src/%.c include/wren.h $(HEADERS)
|
||||
$(CC) -c $(CFLAGS) $(RELEASE_CFLAGS) -Iinclude -o $@ $<
|
||||
|
||||
# Run the tests against the debug build of Wren.
|
||||
test: debug
|
||||
@./script/test.py $(suite)
|
||||
|
||||
# Take the contents of the scripts under builtin/ and copy them into their
|
||||
# respective wren_<name>.c source files.
|
||||
builtin:
|
||||
@./script/generate_builtins.py
|
||||
|
||||
# Generate the Wren site.
|
||||
docs:
|
||||
@./script/generate_docs.py
|
||||
|
||||
# Continuously generate the Wren site.
|
||||
watchdocs:
|
||||
@./script/generate_docs.py --watch
|
||||
|
||||
# Build the docs and copy them to a local "gh-pages" directory.
|
||||
gh-pages: docs
|
||||
cp -r build/docs/. gh-pages
|
||||
@ -1,19 +0,0 @@
|
||||
The benchmarks in here attempt to faithfully implement the exact same algorithm in a few different languages. We're using Lua, Python, and Ruby for comparison here because those are all in Wren's ballpark: dynamically-typed, object-oriented, bytecode-compiled.
|
||||
|
||||
A bit about each benchmark:
|
||||
|
||||
### binary_trees
|
||||
|
||||
This benchmark stresses object creation and garbage collection. It builds a few big, deeply nested binaries and then traverses them.
|
||||
|
||||
### fib
|
||||
|
||||
This is just a simple naïve Fibonacci number calculator. It was the first benchmark I wrote when Wren supported little more than function calls and arithmetic. It isn't particularly representative of real-world code, but it does stress function call and arithmetic.
|
||||
|
||||
### for
|
||||
|
||||
This microbenchmark just tests the performance of for loops. Not too useful, but i used it when implementing `for` in Wren to make sure it wasn't too far off the mark.
|
||||
|
||||
### method_call
|
||||
|
||||
This is the most useful benchmark: it tests dynamic dispatch and polymorphism. You'll note that the main iteration loop is unrolled in all of the implementations. This is to ensure that the loop overhead itself doesn't dwarf the method call time.
|
||||
@ -1,54 +0,0 @@
|
||||
-- The Computer Language Benchmarks Game
|
||||
-- http://shootout.alioth.debian.org/
|
||||
-- contributed by Mike Pall
|
||||
|
||||
local function BottomUpTree(item, depth)
|
||||
if depth > 0 then
|
||||
local i = item + item
|
||||
depth = depth - 1
|
||||
local left, right = BottomUpTree(i-1, depth), BottomUpTree(i, depth)
|
||||
return { item, left, right }
|
||||
else
|
||||
return { item }
|
||||
end
|
||||
end
|
||||
|
||||
local function ItemCheck(tree)
|
||||
if tree[2] then
|
||||
return tree[1] + ItemCheck(tree[2]) - ItemCheck(tree[3])
|
||||
else
|
||||
return tree[1]
|
||||
end
|
||||
end
|
||||
|
||||
local N = 12
|
||||
local mindepth = 4
|
||||
local maxdepth = mindepth + 2
|
||||
if maxdepth < N then maxdepth = N end
|
||||
|
||||
local start = os.clock()
|
||||
|
||||
do
|
||||
local stretchdepth = maxdepth + 1
|
||||
local stretchtree = BottomUpTree(0, stretchdepth)
|
||||
io.write(string.format("stretch tree of depth %d check: %d\n",
|
||||
stretchdepth, ItemCheck(stretchtree)))
|
||||
end
|
||||
|
||||
local longlivedtree = BottomUpTree(0, maxdepth)
|
||||
|
||||
for depth=mindepth,maxdepth,2 do
|
||||
local iterations = 2 ^ (maxdepth - depth + mindepth)
|
||||
local check = 0
|
||||
for i=1,iterations do
|
||||
check = check + ItemCheck(BottomUpTree(1, depth)) +
|
||||
ItemCheck(BottomUpTree(-1, depth))
|
||||
end
|
||||
io.write(string.format("%d trees of depth %d check: %d\n",
|
||||
iterations*2, depth, check))
|
||||
end
|
||||
|
||||
io.write(string.format("long lived tree of depth %d check: %d\n",
|
||||
maxdepth, ItemCheck(longlivedtree)))
|
||||
|
||||
io.write(string.format("elapsed: %.8f\n", os.clock() - start))
|
||||
@ -1,48 +0,0 @@
|
||||
# The Computer Language Benchmarks Game
|
||||
# http://shootout.alioth.debian.org/
|
||||
#
|
||||
# contributed by Antoine Pitrou
|
||||
# modified by Dominique Wahli
|
||||
# modified by Heinrich Acker
|
||||
from __future__ import print_function
|
||||
|
||||
import time
|
||||
|
||||
# Map "range" to an efficient range in both Python 2 and 3.
|
||||
try:
|
||||
range = xrange
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
def make_tree(item, depth):
|
||||
if not depth: return item, None, None
|
||||
item2 = item + item
|
||||
depth -= 1
|
||||
return item, make_tree(item2 - 1, depth), make_tree(item2, depth)
|
||||
|
||||
def check_tree(node):
|
||||
item, left, right = node
|
||||
if not left: return item
|
||||
return item + check_tree(left) - check_tree(right)
|
||||
|
||||
min_depth = 4
|
||||
max_depth = 12
|
||||
stretch_depth = max_depth + 1
|
||||
|
||||
start = time.clock()
|
||||
print("stretch tree of depth %d check:" % stretch_depth, check_tree(make_tree(0, stretch_depth)))
|
||||
|
||||
long_lived_tree = make_tree(0, max_depth)
|
||||
|
||||
iterations = 2 ** max_depth
|
||||
for depth in range(min_depth, stretch_depth, 2):
|
||||
|
||||
check = 0
|
||||
for i in range(1, iterations + 1):
|
||||
check += check_tree(make_tree(i, depth)) + check_tree(make_tree(-i, depth))
|
||||
|
||||
print("%d trees of depth %d check:" % (iterations * 2, depth), check)
|
||||
iterations //= 4
|
||||
|
||||
print("long lived tree of depth %d check:" % max_depth, check_tree(long_lived_tree))
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
@ -1,51 +0,0 @@
|
||||
# The Computer Language Shootout Benchmarks
|
||||
# http://shootout.alioth.debian.org
|
||||
#
|
||||
# contributed by Jesse Millikan
|
||||
# Modified by Wesley Moxam
|
||||
|
||||
|
||||
def item_check(left, item, right)
|
||||
return item if left.nil?
|
||||
item + item_check(*left) - item_check(*right)
|
||||
end
|
||||
|
||||
def bottom_up_tree(item, depth)
|
||||
return [nil, item, nil] unless depth > 0
|
||||
item_item = 2 * item
|
||||
depth -= 1
|
||||
[bottom_up_tree(item_item - 1, depth), item, bottom_up_tree(item_item, depth)]
|
||||
end
|
||||
|
||||
max_depth = 12
|
||||
min_depth = 4
|
||||
|
||||
max_depth = min_depth + 2 if min_depth + 2 > max_depth
|
||||
|
||||
stretch_depth = max_depth + 1
|
||||
stretch_tree = bottom_up_tree(0, stretch_depth)
|
||||
|
||||
start = Time.now
|
||||
puts "stretch tree of depth #{stretch_depth} check: #{item_check(*stretch_tree)}"
|
||||
stretch_tree = nil
|
||||
|
||||
long_lived_tree = bottom_up_tree(0, max_depth)
|
||||
|
||||
min_depth.step(max_depth + 1, 2) do |depth|
|
||||
iterations = 2**(max_depth - depth + min_depth)
|
||||
|
||||
check = 0
|
||||
|
||||
for i in 1..iterations
|
||||
temp_tree = bottom_up_tree(i, depth)
|
||||
check += item_check(*temp_tree)
|
||||
|
||||
temp_tree = bottom_up_tree(-i, depth)
|
||||
check += item_check(*temp_tree)
|
||||
end
|
||||
|
||||
puts "#{iterations * 2} trees of depth #{depth} check: #{check}"
|
||||
end
|
||||
|
||||
puts "long lived tree of depth #{max_depth} check: #{item_check(*long_lived_tree)}"
|
||||
puts "elapsed: " + (Time.now - start).to_s
|
||||
@ -1,53 +0,0 @@
|
||||
// Ported from the Python version.
|
||||
|
||||
class Tree {
|
||||
new(item, depth) {
|
||||
_item = item
|
||||
if (depth > 0) {
|
||||
var item2 = item + item
|
||||
depth = depth - 1
|
||||
_left = new Tree(item2 - 1, depth)
|
||||
_right = new Tree(item2, depth)
|
||||
}
|
||||
}
|
||||
|
||||
check {
|
||||
if (_left == null) {
|
||||
return _item
|
||||
}
|
||||
|
||||
return _item + _left.check - _right.check
|
||||
}
|
||||
}
|
||||
|
||||
var minDepth = 4
|
||||
var maxDepth = 12
|
||||
var stretchDepth = maxDepth + 1
|
||||
|
||||
var start = IO.clock
|
||||
|
||||
IO.print("stretch tree of depth ", stretchDepth, " check: ",
|
||||
new Tree(0, stretchDepth).check)
|
||||
|
||||
var longLivedTree = new Tree(0, maxDepth)
|
||||
|
||||
// iterations = 2 ** maxDepth
|
||||
var iterations = 1
|
||||
for (d in 0...maxDepth) {
|
||||
iterations = iterations * 2
|
||||
}
|
||||
|
||||
var depth = minDepth
|
||||
while (depth < stretchDepth) {
|
||||
var check = 0
|
||||
for (i in 1..iterations) {
|
||||
check = check + new Tree(i, depth).check + new Tree(-i, depth).check
|
||||
}
|
||||
|
||||
IO.print((iterations * 2), " trees of depth ", depth, " check: ", check)
|
||||
iterations = iterations / 4
|
||||
depth = depth + 2
|
||||
}
|
||||
|
||||
IO.print("long lived tree of depth ", maxDepth, " check: ", longLivedTree.check)
|
||||
IO.print("elapsed: ", (IO.clock - start))
|
||||
@ -1,914 +0,0 @@
|
||||
-- Copyright 2008 the V8 project authors. All rights reserved.
|
||||
-- Copyright 1996 John Maloney and Mario Wolczko.
|
||||
|
||||
-- This program is free software; you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU General Public License as published by
|
||||
-- the Free Software Foundation; either version 2 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License
|
||||
-- along with this program; if not, write to the Free Software
|
||||
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
-- This implementation of the DeltaBlue benchmark is derived
|
||||
-- from the Smalltalk implementation by John Maloney and Mario
|
||||
-- Wolczko. Some parts have been translated directly, whereas
|
||||
-- others have been modified more aggresively to make it feel
|
||||
-- more like a JavaScript program.
|
||||
|
||||
|
||||
--
|
||||
-- A JavaScript implementation of the DeltaBlue constraint-solving
|
||||
-- algorithm, as described in:
|
||||
--
|
||||
-- "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver"
|
||||
-- Bjorn N. Freeman-Benson and John Maloney
|
||||
-- January 1990 Communications of the ACM,
|
||||
-- also available as University of Washington TR 89-08-06.
|
||||
--
|
||||
-- Beware: this benchmark is written in a grotesque style where
|
||||
-- the constraint model is built by side-effects from constructors.
|
||||
-- I've kept it this way to avoid deviating too much from the original
|
||||
-- implementation.
|
||||
--
|
||||
|
||||
-- From: https://github.com/mraleph/deltablue.lua
|
||||
|
||||
local planner
|
||||
|
||||
--- O b j e c t M o d e l ---
|
||||
|
||||
local function alert (...) print(...) end
|
||||
|
||||
local OrderedCollection = class()
|
||||
|
||||
function OrderedCollection:constructor()
|
||||
self.elms = {}
|
||||
end
|
||||
|
||||
function OrderedCollection:add(elm)
|
||||
self.elms[#self.elms + 1] = elm
|
||||
end
|
||||
|
||||
function OrderedCollection:at (index)
|
||||
return self.elms[index]
|
||||
end
|
||||
|
||||
function OrderedCollection:size ()
|
||||
return #self.elms
|
||||
end
|
||||
|
||||
function OrderedCollection:removeFirst ()
|
||||
local e = self.elms[#self.elms]
|
||||
self.elms[#self.elms] = nil
|
||||
return e
|
||||
end
|
||||
|
||||
function OrderedCollection:remove (elm)
|
||||
local index = 0
|
||||
local skipped = 0
|
||||
|
||||
for i = 1, #self.elms do
|
||||
local value = self.elms[i]
|
||||
if value ~= elm then
|
||||
self.elms[index] = value
|
||||
index = index + 1
|
||||
else
|
||||
skipped = skipped + 1
|
||||
end
|
||||
end
|
||||
|
||||
local l = #self.elms
|
||||
for i = 1, skipped do self.elms[l - i + 1] = nil end
|
||||
end
|
||||
|
||||
--
|
||||
-- S t r e n g t h
|
||||
--
|
||||
|
||||
--
|
||||
-- Strengths are used to measure the relative importance of constraints.
|
||||
-- New strengths may be inserted in the strength hierarchy without
|
||||
-- disrupting current constraints. Strengths cannot be created outside
|
||||
-- this class, so pointer comparison can be used for value comparison.
|
||||
--
|
||||
|
||||
local Strength = class()
|
||||
|
||||
function Strength:constructor(strengthValue, name)
|
||||
self.strengthValue = strengthValue
|
||||
self.name = name
|
||||
end
|
||||
|
||||
function Strength.stronger (s1, s2)
|
||||
return s1.strengthValue < s2.strengthValue
|
||||
end
|
||||
|
||||
function Strength.weaker (s1, s2)
|
||||
return s1.strengthValue > s2.strengthValue
|
||||
end
|
||||
|
||||
function Strength.weakestOf (s1, s2)
|
||||
return Strength.weaker(s1, s2) and s1 or s2
|
||||
end
|
||||
|
||||
function Strength.strongest (s1, s2)
|
||||
return Strength.stronger(s1, s2) and s1 or s2
|
||||
end
|
||||
|
||||
function Strength:nextWeaker ()
|
||||
local v = self.strengthValue
|
||||
if v == 0 then return Strength.WEAKEST
|
||||
elseif v == 1 then return Strength.WEAK_DEFAULT
|
||||
elseif v == 2 then return Strength.NORMAL
|
||||
elseif v == 3 then return Strength.STRONG_DEFAULT
|
||||
elseif v == 4 then return Strength.PREFERRED
|
||||
elseif v == 5 then return Strength.REQUIRED
|
||||
end
|
||||
end
|
||||
|
||||
-- Strength constants.
|
||||
Strength.REQUIRED = Strength.new(0, "required");
|
||||
Strength.STONG_PREFERRED = Strength.new(1, "strongPreferred");
|
||||
Strength.PREFERRED = Strength.new(2, "preferred");
|
||||
Strength.STRONG_DEFAULT = Strength.new(3, "strongDefault");
|
||||
Strength.NORMAL = Strength.new(4, "normal");
|
||||
Strength.WEAK_DEFAULT = Strength.new(5, "weakDefault");
|
||||
Strength.WEAKEST = Strength.new(6, "weakest");
|
||||
|
||||
--
|
||||
-- C o n s t r a i n t
|
||||
--
|
||||
|
||||
--
|
||||
-- An abstract class representing a system-maintainable relationship
|
||||
-- (or "constraint") between a set of variables. A constraint supplies
|
||||
-- a strength instance variable; concrete subclasses provide a means
|
||||
-- of storing the constrained variables and other information required
|
||||
-- to represent a constraint.
|
||||
--
|
||||
|
||||
local Constraint = class ()
|
||||
|
||||
function Constraint:constructor(strength)
|
||||
self.strength = strength
|
||||
end
|
||||
|
||||
--
|
||||
-- Activate this constraint and attempt to satisfy it.
|
||||
--
|
||||
function Constraint:addConstraint ()
|
||||
self:addToGraph()
|
||||
planner:incrementalAdd(self)
|
||||
end
|
||||
|
||||
--
|
||||
-- Attempt to find a way to enforce this constraint. If successful,
|
||||
-- record the solution, perhaps modifying the current dataflow
|
||||
-- graph. Answer the constraint that this constraint overrides, if
|
||||
-- there is one, or nil, if there isn't.
|
||||
-- Assume: I am not already satisfied.
|
||||
--
|
||||
function Constraint:satisfy (mark)
|
||||
self:chooseMethod(mark)
|
||||
if not self:isSatisfied() then
|
||||
if self.strength == Strength.REQUIRED then
|
||||
alert("Could not satisfy a required constraint!")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
self:markInputs(mark)
|
||||
local out = self:output()
|
||||
local overridden = out.determinedBy
|
||||
if overridden ~= nil then overridden:markUnsatisfied() end
|
||||
out.determinedBy = self
|
||||
if not planner:addPropagate(self, mark) then alert("Cycle encountered") end
|
||||
out.mark = mark
|
||||
return overridden
|
||||
end
|
||||
|
||||
function Constraint:destroyConstraint ()
|
||||
if self:isSatisfied()
|
||||
then planner:incrementalRemove(self)
|
||||
else self:removeFromGraph()
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
function Constraint:isInput ()
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- U n a r y C o n s t r a i n t
|
||||
--
|
||||
|
||||
--
|
||||
-- Abstract superclass for constraints having a single possible output
|
||||
-- variable.
|
||||
--
|
||||
|
||||
local UnaryConstraint = class(Constraint)
|
||||
|
||||
function UnaryConstraint:constructor (v, strength)
|
||||
UnaryConstraint.super.constructor(self, strength)
|
||||
self.myOutput = v
|
||||
self.satisfied = false
|
||||
self:addConstraint()
|
||||
end
|
||||
|
||||
--
|
||||
-- Adds this constraint to the constraint graph
|
||||
--
|
||||
function UnaryConstraint:addToGraph ()
|
||||
self.myOutput:addConstraint(self)
|
||||
self.satisfied = false
|
||||
end
|
||||
|
||||
--
|
||||
-- Decides if this constraint can be satisfied and records that
|
||||
-- decision.
|
||||
--
|
||||
function UnaryConstraint:chooseMethod (mark)
|
||||
self.satisfied = (self.myOutput.mark ~= mark)
|
||||
and Strength.stronger(self.strength, self.myOutput.walkStrength);
|
||||
end
|
||||
|
||||
--
|
||||
-- Returns true if this constraint is satisfied in the current solution.
|
||||
--
|
||||
function UnaryConstraint:isSatisfied ()
|
||||
return self.satisfied;
|
||||
end
|
||||
|
||||
function UnaryConstraint:markInputs (mark)
|
||||
-- has no inputs
|
||||
end
|
||||
|
||||
--
|
||||
-- Returns the current output variable.
|
||||
--
|
||||
function UnaryConstraint:output ()
|
||||
return self.myOutput
|
||||
end
|
||||
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
function UnaryConstraint:recalculate ()
|
||||
self.myOutput.walkStrength = self.strength
|
||||
self.myOutput.stay = not self:isInput()
|
||||
if self.myOutput.stay then
|
||||
self:execute() -- Stay optimization
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Records that this constraint is unsatisfied
|
||||
--
|
||||
function UnaryConstraint:markUnsatisfied ()
|
||||
self.satisfied = false
|
||||
end
|
||||
|
||||
function UnaryConstraint:inputsKnown ()
|
||||
return true
|
||||
end
|
||||
|
||||
function UnaryConstraint:removeFromGraph ()
|
||||
if self.myOutput ~= nil then
|
||||
self.myOutput:removeConstraint(self)
|
||||
end
|
||||
self.satisfied = false
|
||||
end
|
||||
|
||||
--
|
||||
-- S t a y C o n s t r a i n t
|
||||
--
|
||||
|
||||
--
|
||||
-- Variables that should, with some level of preference, stay the same.
|
||||
-- Planners may exploit the fact that instances, if satisfied, will not
|
||||
-- change their output during plan execution. This is called "stay
|
||||
-- optimization".
|
||||
--
|
||||
|
||||
local StayConstraint = class(UnaryConstraint)
|
||||
|
||||
function StayConstraint:constructor(v, str)
|
||||
StayConstraint.super.constructor(self, v, str)
|
||||
end
|
||||
|
||||
function StayConstraint:execute ()
|
||||
-- Stay constraints do nothing
|
||||
end
|
||||
|
||||
--
|
||||
-- E d i t C o n s t r a i n t
|
||||
--
|
||||
|
||||
--
|
||||
-- A unary input constraint used to mark a variable that the client
|
||||
-- wishes to change.
|
||||
--
|
||||
|
||||
local EditConstraint = class (UnaryConstraint)
|
||||
|
||||
function EditConstraint:constructor(v, str)
|
||||
EditConstraint.super.constructor(self, v, str)
|
||||
end
|
||||
|
||||
--
|
||||
-- Edits indicate that a variable is to be changed by imperative code.
|
||||
--
|
||||
function EditConstraint:isInput ()
|
||||
return true
|
||||
end
|
||||
|
||||
function EditConstraint:execute ()
|
||||
-- Edit constraints do nothing
|
||||
end
|
||||
|
||||
--
|
||||
-- B i n a r y C o n s t r a i n t
|
||||
--
|
||||
|
||||
local Direction = {}
|
||||
Direction.NONE = 0
|
||||
Direction.FORWARD = 1
|
||||
Direction.BACKWARD = -1
|
||||
|
||||
--
|
||||
-- Abstract superclass for constraints having two possible output
|
||||
-- variables.
|
||||
--
|
||||
|
||||
local BinaryConstraint = class(Constraint)
|
||||
|
||||
function BinaryConstraint:constructor(var1, var2, strength)
|
||||
BinaryConstraint.super.constructor(self, strength);
|
||||
self.v1 = var1
|
||||
self.v2 = var2
|
||||
self.direction = Direction.NONE
|
||||
self:addConstraint()
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
function BinaryConstraint:chooseMethod (mark)
|
||||
if self.v1.mark == mark then
|
||||
self.direction = (self.v2.mark ~= mark and Strength.stronger(self.strength, self.v2.walkStrength)) and Direction.FORWARD or Direction.NONE
|
||||
end
|
||||
if self.v2.mark == mark then
|
||||
self.direction = (self.v1.mark ~= mark and Strength.stronger(self.strength, self.v1.walkStrength)) and Direction.BACKWARD or Direction.NONE
|
||||
end
|
||||
if Strength.weaker(self.v1.walkStrength, self.v2.walkStrength) then
|
||||
self.direction = Strength.stronger(self.strength, self.v1.walkStrength) and Direction.BACKWARD or Direction.NONE
|
||||
else
|
||||
self.direction = Strength.stronger(self.strength, self.v2.walkStrength) and Direction.FORWARD or Direction.BACKWARD
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Add this constraint to the constraint graph
|
||||
--
|
||||
function BinaryConstraint:addToGraph ()
|
||||
self.v1:addConstraint(self)
|
||||
self.v2:addConstraint(self)
|
||||
self.direction = Direction.NONE
|
||||
end
|
||||
|
||||
--
|
||||
-- Answer true if this constraint is satisfied in the current solution.
|
||||
--
|
||||
function BinaryConstraint:isSatisfied ()
|
||||
return self.direction ~= Direction.NONE
|
||||
end
|
||||
|
||||
--
|
||||
-- Mark the input variable with the given mark.
|
||||
--
|
||||
function BinaryConstraint:markInputs (mark)
|
||||
self:input().mark = mark
|
||||
end
|
||||
|
||||
--
|
||||
-- Returns the current input variable
|
||||
--
|
||||
function BinaryConstraint:input ()
|
||||
return (self.direction == Direction.FORWARD) and self.v1 or self.v2
|
||||
end
|
||||
|
||||
--
|
||||
-- Returns the current output variable
|
||||
--
|
||||
function BinaryConstraint:output ()
|
||||
return (self.direction == Direction.FORWARD) and self.v2 or self.v1
|
||||
end
|
||||
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
function BinaryConstraint:recalculate ()
|
||||
local ihn = self:input()
|
||||
local out = self:output()
|
||||
out.walkStrength = Strength.weakestOf(self.strength, ihn.walkStrength);
|
||||
out.stay = ihn.stay
|
||||
if out.stay then self:execute() end
|
||||
end
|
||||
|
||||
--
|
||||
-- Record the fact that self constraint is unsatisfied.
|
||||
--
|
||||
function BinaryConstraint:markUnsatisfied ()
|
||||
self.direction = Direction.NONE
|
||||
end
|
||||
|
||||
function BinaryConstraint:inputsKnown (mark)
|
||||
local i = self:input()
|
||||
return i.mark == mark or i.stay or i.determinedBy == nil
|
||||
end
|
||||
|
||||
function BinaryConstraint:removeFromGraph ()
|
||||
if (self.v1 ~= nil) then self.v1:removeConstraint(self) end
|
||||
if (self.v2 ~= nil) then self.v2:removeConstraint(self) end
|
||||
self.direction = Direction.NONE
|
||||
end
|
||||
|
||||
--
|
||||
-- S c a l e C o n s t r a i n t
|
||||
--
|
||||
|
||||
--
|
||||
-- Relates two variables by the linear scaling relationship: "v2 =
|
||||
-- (v1 * scale) + offset". Either v1 or v2 may be changed to maintain
|
||||
-- this relationship but the scale factor and offset are considered
|
||||
-- read-only.
|
||||
--
|
||||
|
||||
local ScaleConstraint = class (BinaryConstraint)
|
||||
|
||||
function ScaleConstraint:constructor(src, scale, offset, dest, strength)
|
||||
self.direction = Direction.NONE
|
||||
self.scale = scale
|
||||
self.offset = offset
|
||||
ScaleConstraint.super.constructor(self, src, dest, strength)
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Adds this constraint to the constraint graph.
|
||||
--
|
||||
function ScaleConstraint:addToGraph ()
|
||||
ScaleConstraint.super.addToGraph(self)
|
||||
self.scale:addConstraint(self)
|
||||
self.offset:addConstraint(self)
|
||||
end
|
||||
|
||||
function ScaleConstraint:removeFromGraph ()
|
||||
ScaleConstraint.super.removeFromGraph(self)
|
||||
if (self.scale ~= nil) then self.scale:removeConstraint(self) end
|
||||
if (self.offset ~= nil) then self.offset:removeConstraint(self) end
|
||||
end
|
||||
|
||||
function ScaleConstraint:markInputs (mark)
|
||||
ScaleConstraint.super.markInputs(self, mark);
|
||||
self.offset.mark = mark
|
||||
self.scale.mark = mark
|
||||
end
|
||||
|
||||
--
|
||||
-- Enforce this constraint. Assume that it is satisfied.
|
||||
--
|
||||
function ScaleConstraint:execute ()
|
||||
if self.direction == Direction.FORWARD then
|
||||
self.v2.value = self.v1.value * self.scale.value + self.offset.value
|
||||
else
|
||||
self.v1.value = (self.v2.value - self.offset.value) / self.scale.value
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
function ScaleConstraint:recalculate ()
|
||||
local ihn = self:input()
|
||||
local out = self:output()
|
||||
out.walkStrength = Strength.weakestOf(self.strength, ihn.walkStrength)
|
||||
out.stay = ihn.stay and self.scale.stay and self.offset.stay
|
||||
if out.stay then self:execute() end
|
||||
end
|
||||
|
||||
--
|
||||
-- E q u a l i t y C o n s t r a i n t
|
||||
--
|
||||
|
||||
--
|
||||
-- Constrains two variables to have the same value.
|
||||
--
|
||||
|
||||
local EqualityConstraint = class (BinaryConstraint)
|
||||
|
||||
function EqualityConstraint:constructor(var1, var2, strength)
|
||||
EqualityConstraint.super.constructor(self, var1, var2, strength)
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Enforce this constraint. Assume that it is satisfied.
|
||||
--
|
||||
function EqualityConstraint:execute ()
|
||||
self:output().value = self:input().value
|
||||
end
|
||||
|
||||
--
|
||||
-- V a r i a b l e
|
||||
--
|
||||
|
||||
--
|
||||
-- A constrained variable. In addition to its value, it maintain the
|
||||
-- structure of the constraint graph, the current dataflow graph, and
|
||||
-- various parameters of interest to the DeltaBlue incremental
|
||||
-- constraint solver.
|
||||
--
|
||||
local Variable = class ()
|
||||
|
||||
function Variable:constructor(name, initialValue)
|
||||
self.value = initialValue or 0
|
||||
self.constraints = OrderedCollection.new()
|
||||
self.determinedBy = nil
|
||||
self.mark = 0
|
||||
self.walkStrength = Strength.WEAKEST
|
||||
self.stay = true
|
||||
self.name = name
|
||||
end
|
||||
|
||||
--
|
||||
-- Add the given constraint to the set of all constraints that refer
|
||||
-- this variable.
|
||||
--
|
||||
function Variable:addConstraint (c)
|
||||
self.constraints:add(c)
|
||||
end
|
||||
|
||||
--
|
||||
-- Removes all traces of c from this variable.
|
||||
--
|
||||
function Variable:removeConstraint (c)
|
||||
self.constraints:remove(c)
|
||||
if self.determinedBy == c then
|
||||
self.determinedBy = nil
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- P l a n n e r
|
||||
--
|
||||
|
||||
--
|
||||
-- The DeltaBlue planner
|
||||
--
|
||||
local Planner = class()
|
||||
function Planner:constructor()
|
||||
self.currentMark = 0
|
||||
end
|
||||
|
||||
--
|
||||
-- Attempt to satisfy the given constraint and, if successful,
|
||||
-- incrementally update the dataflow graph. Details: If satifying
|
||||
-- the constraint is successful, it may override a weaker constraint
|
||||
-- on its output. The algorithm attempts to resatisfy that
|
||||
-- constraint using some other method. This process is repeated
|
||||
-- until either a) it reaches a variable that was not previously
|
||||
-- determined by any constraint or b) it reaches a constraint that
|
||||
-- is too weak to be satisfied using any of its methods. The
|
||||
-- variables of constraints that have been processed are marked with
|
||||
-- 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.
|
||||
--
|
||||
function Planner:incrementalAdd (c)
|
||||
local mark = self:newMark()
|
||||
local overridden = c:satisfy(mark)
|
||||
while overridden ~= nil do
|
||||
overridden = overridden:satisfy(mark)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Entry point for retracting a constraint. Remove the given
|
||||
-- constraint and incrementally update the dataflow graph.
|
||||
-- Details: Retracting the given constraint may allow some currently
|
||||
-- unsatisfiable downstream constraint to be satisfied. We therefore collect
|
||||
-- a list of unsatisfied downstream constraints and attempt to
|
||||
-- satisfy each one in turn. This list is traversed by constraint
|
||||
-- strength, strongest first, as a heuristic for avoiding
|
||||
-- unnecessarily adding and then overriding weak constraints.
|
||||
-- Assume: c is satisfied.
|
||||
--
|
||||
function Planner:incrementalRemove (c)
|
||||
local out = c:output()
|
||||
c:markUnsatisfied()
|
||||
c:removeFromGraph()
|
||||
local unsatisfied = self:removePropagateFrom(out)
|
||||
local strength = Strength.REQUIRED
|
||||
repeat
|
||||
for i = 1, unsatisfied:size() do
|
||||
local u = unsatisfied:at(i)
|
||||
if u.strength == strength then
|
||||
self:incrementalAdd(u)
|
||||
end
|
||||
end
|
||||
strength = strength:nextWeaker()
|
||||
until strength == Strength.WEAKEST
|
||||
end
|
||||
|
||||
--
|
||||
-- Select a previously unused mark value.
|
||||
--
|
||||
function Planner:newMark ()
|
||||
self.currentMark = self.currentMark + 1
|
||||
return self.currentMark
|
||||
end
|
||||
|
||||
--
|
||||
-- Extract a plan for resatisfaction starting from the given source
|
||||
-- constraints, usually a set of input constraints. This method
|
||||
-- assumes that stay optimization is desired; the plan will contain
|
||||
-- only constraints whose output variables are not stay. Constraints
|
||||
-- that do no computation, such as stay and edit constraints, are
|
||||
-- not included in the plan.
|
||||
-- Details: The outputs of a constraint are marked when it is added
|
||||
-- to the plan under construction. A constraint may be appended to
|
||||
-- the plan when all its input variables are known. A variable is
|
||||
-- known if either a) the variable is marked (indicating that has
|
||||
-- been computed by a constraint appearing earlier in the plan), b)
|
||||
-- the variable is 'stay' (i.e. it is a constant at plan execution
|
||||
-- time), or c) the variable is not determined by any
|
||||
-- constraint. The last provision is for past states of history
|
||||
-- variables, which are not stay but which are also not computed by
|
||||
-- any constraint.
|
||||
-- Assume: sources are all satisfied.
|
||||
--
|
||||
local Plan -- FORWARD DECLARATION
|
||||
function Planner:makePlan (sources)
|
||||
local mark = self:newMark()
|
||||
local plan = Plan.new()
|
||||
local todo = sources
|
||||
while todo:size() > 0 do
|
||||
local c = todo:removeFirst()
|
||||
if c:output().mark ~= mark and c:inputsKnown(mark) then
|
||||
plan:addConstraint(c)
|
||||
c:output().mark = mark
|
||||
self:addConstraintsConsumingTo(c:output(), todo)
|
||||
end
|
||||
end
|
||||
return plan
|
||||
end
|
||||
|
||||
--
|
||||
-- Extract a plan for resatisfying starting from the output of the
|
||||
-- given constraints, usually a set of input constraints.
|
||||
--
|
||||
function Planner:extractPlanFromConstraints (constraints)
|
||||
local sources = OrderedCollection.new()
|
||||
for i = 1, constraints:size() do
|
||||
local c = constraints:at(i)
|
||||
if c:isInput() and c:isSatisfied() then
|
||||
-- not in plan already and eligible for inclusion
|
||||
sources:add(c)
|
||||
end
|
||||
end
|
||||
return self:makePlan(sources)
|
||||
end
|
||||
|
||||
--
|
||||
-- Recompute the walkabout strengths and stay flags of all variables
|
||||
-- downstream of the given constraint and recompute the actual
|
||||
-- values of all variables whose stay flag is true. If a cycle is
|
||||
-- detected, remove the given constraint and answer
|
||||
-- false. Otherwise, answer true.
|
||||
-- Details: Cycles are detected when a marked variable is
|
||||
-- encountered downstream of the given constraint. The sender is
|
||||
-- assumed to have marked the inputs of the given constraint with
|
||||
-- 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.
|
||||
--
|
||||
function Planner:addPropagate (c, mark)
|
||||
local todo = OrderedCollection.new()
|
||||
todo:add(c)
|
||||
while todo:size() > 0 do
|
||||
local d = todo:removeFirst()
|
||||
if d:output().mark == mark then
|
||||
self:incrementalRemove(c)
|
||||
return false
|
||||
end
|
||||
d:recalculate()
|
||||
self:addConstraintsConsumingTo(d:output(), todo)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
function Planner:removePropagateFrom (out)
|
||||
out.determinedBy = nil
|
||||
out.walkStrength = Strength.WEAKEST
|
||||
out.stay = true
|
||||
local unsatisfied = OrderedCollection.new()
|
||||
local todo = OrderedCollection.new()
|
||||
todo:add(out)
|
||||
while todo:size() > 0 do
|
||||
local v = todo:removeFirst()
|
||||
for i = 1, v.constraints:size() do
|
||||
local c = v.constraints:at(i)
|
||||
if not c:isSatisfied() then unsatisfied:add(c) end
|
||||
end
|
||||
local determining = v.determinedBy
|
||||
for i = 1, v.constraints:size() do
|
||||
local next = v.constraints:at(i);
|
||||
if next ~= determining and next:isSatisfied() then
|
||||
next:recalculate()
|
||||
todo:add(next:output())
|
||||
end
|
||||
end
|
||||
end
|
||||
return unsatisfied
|
||||
end
|
||||
|
||||
function Planner:addConstraintsConsumingTo (v, coll)
|
||||
local determining = v.determinedBy
|
||||
local cc = v.constraints
|
||||
for i = 1, cc:size() do
|
||||
local c = cc:at(i)
|
||||
if c ~= determining and c:isSatisfied() then
|
||||
coll:add(c)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- P l a n
|
||||
--
|
||||
|
||||
--
|
||||
-- A Plan is an ordered list of constraints to be executed in sequence
|
||||
-- to resatisfy all currently satisfiable constraints in the face of
|
||||
-- one or more changing inputs.
|
||||
--
|
||||
Plan = class()
|
||||
function Plan:constructor()
|
||||
self.v = OrderedCollection.new()
|
||||
end
|
||||
|
||||
function Plan:addConstraint (c)
|
||||
self.v:add(c)
|
||||
end
|
||||
|
||||
function Plan:size ()
|
||||
return self.v:size()
|
||||
end
|
||||
|
||||
function Plan:constraintAt (index)
|
||||
return self.v:at(index)
|
||||
end
|
||||
|
||||
function Plan:execute ()
|
||||
for i = 1, self:size() do
|
||||
local c = self:constraintAt(i)
|
||||
c:execute()
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- M a i n
|
||||
--
|
||||
|
||||
--
|
||||
-- This is the standard DeltaBlue benchmark. A long chain of equality
|
||||
-- constraints is constructed with a stay constraint on one end. An
|
||||
-- edit constraint is then added to the opposite end and the time is
|
||||
-- measured for adding and removing this constraint, and extracting
|
||||
-- and executing a constraint satisfaction plan. There are two cases.
|
||||
-- In case 1, the added constraint is stronger than the stay
|
||||
-- constraint and values must propagate down the entire length of the
|
||||
-- chain. In case 2, the added constraint is weaker than the stay
|
||||
-- constraint so it cannot be accomodated. The cost in this case is,
|
||||
-- of course, very low. Typical situations lie somewhere between these
|
||||
-- two extremes.
|
||||
--
|
||||
local function chainTest(n)
|
||||
planner = Planner.new()
|
||||
local prev = nil
|
||||
local first = nil
|
||||
local last = nil
|
||||
|
||||
-- Build chain of n equality constraints
|
||||
for i = 0, n do
|
||||
local name = "v" .. i;
|
||||
local v = Variable.new(name)
|
||||
if prev ~= nil then EqualityConstraint.new(prev, v, Strength.REQUIRED) end
|
||||
if i == 0 then first = v end
|
||||
if i == n then last = v end
|
||||
prev = v
|
||||
end
|
||||
|
||||
StayConstraint.new(last, Strength.STRONG_DEFAULT)
|
||||
local edit = EditConstraint.new(first, Strength.PREFERRED)
|
||||
local edits = OrderedCollection.new()
|
||||
edits:add(edit)
|
||||
local plan = planner:extractPlanFromConstraints(edits)
|
||||
for i = 0, 99 do
|
||||
first.value = i
|
||||
plan:execute()
|
||||
if last.value ~= i then
|
||||
alert("Chain test failed.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function change(v, newValue)
|
||||
local edit = EditConstraint.new(v, Strength.PREFERRED)
|
||||
local edits = OrderedCollection.new()
|
||||
edits:add(edit)
|
||||
local plan = planner:extractPlanFromConstraints(edits)
|
||||
for i = 1, 10 do
|
||||
v.value = newValue
|
||||
plan:execute()
|
||||
end
|
||||
edit:destroyConstraint()
|
||||
end
|
||||
|
||||
--
|
||||
-- This test constructs a two sets of variables related to each
|
||||
-- other by a simple linear transformation (scale and offset). The
|
||||
-- time is measured to change a variable on either side of the
|
||||
-- mapping and to change the scale and offset factors.
|
||||
--
|
||||
local function projectionTest(n)
|
||||
planner = Planner.new();
|
||||
local scale = Variable.new("scale", 10);
|
||||
local offset = Variable.new("offset", 1000);
|
||||
local src = nil
|
||||
local dst = nil;
|
||||
|
||||
local dests = OrderedCollection.new();
|
||||
for i = 0, n - 1 do
|
||||
src = Variable.new("src" .. i, i);
|
||||
dst = Variable.new("dst" .. i, i);
|
||||
dests:add(dst);
|
||||
StayConstraint.new(src, Strength.NORMAL);
|
||||
ScaleConstraint.new(src, scale, offset, dst, Strength.REQUIRED);
|
||||
end
|
||||
|
||||
change(src, 17)
|
||||
if dst.value ~= 1170 then alert("Projection 1 failed") end
|
||||
change(dst, 1050)
|
||||
if src.value ~= 5 then alert("Projection 2 failed") end
|
||||
change(scale, 5)
|
||||
for i = 0, n - 2 do
|
||||
if dests:at(i + 1).value ~= i * 5 + 1000 then
|
||||
alert("Projection 3 failed")
|
||||
end
|
||||
end
|
||||
change(offset, 2000)
|
||||
for i = 0, n - 2 do
|
||||
if dests:at(i + 1).value ~= i * 5 + 2000 then
|
||||
alert("Projection 4 failed")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function deltaBlue()
|
||||
chainTest(100);
|
||||
projectionTest(100);
|
||||
end
|
||||
|
||||
DeltaBlue = BenchmarkSuite.new('DeltaBlue', 66118, {
|
||||
Benchmark.new('DeltaBlue', deltaBlue)
|
||||
})
|
||||
@ -1,637 +0,0 @@
|
||||
"""
|
||||
deltablue.py
|
||||
============
|
||||
|
||||
Ported for the PyPy project.
|
||||
|
||||
This implementation of the DeltaBlue benchmark was directly ported
|
||||
from the `V8's source code`_, which was in turn derived
|
||||
from the Smalltalk implementation by John Maloney and Mario
|
||||
Wolczko. The original Javascript implementation was licensed under the GPL.
|
||||
|
||||
It's been updated in places to be more idiomatic to Python (for loops over
|
||||
collections, a couple magic methods, ``OrderedCollection`` being a list & things
|
||||
altering those collections changed to the builtin methods) but largely retains
|
||||
the layout & logic from the original. (Ugh.)
|
||||
|
||||
.. _`V8's source code`: (http://code.google.com/p/v8/source/browse/branches/bleeding_edge/benchmarks/deltablue.js)
|
||||
|
||||
From: https://gist.github.com/toastdriven/6408132
|
||||
|
||||
I (Bob Nystrom) tweaked it a bit more. It now prints some output just to be
|
||||
sure it's doing the same work, and I use normal lists instead of wrapping it in
|
||||
OrderedCollection.
|
||||
|
||||
"""
|
||||
from __future__ import print_function
|
||||
import time
|
||||
|
||||
__author__ = 'Daniel Lindsley'
|
||||
__license__ = 'BSD'
|
||||
|
||||
|
||||
class Strength(object):
|
||||
REQUIRED = None
|
||||
STRONG_PREFERRED = None
|
||||
PREFERRED = None
|
||||
STRONG_DEFAULT = None
|
||||
NORMAL = None
|
||||
WEAK_DEFAULT = None
|
||||
WEAKEST = None
|
||||
|
||||
def __init__(self, strength, name):
|
||||
super(Strength, self).__init__()
|
||||
self.strength = strength
|
||||
self.name = name
|
||||
|
||||
@classmethod
|
||||
def stronger(cls, s1, s2):
|
||||
return s1.strength < s2.strength
|
||||
|
||||
@classmethod
|
||||
def weaker(cls, s1, s2):
|
||||
return s1.strength > s2.strength
|
||||
|
||||
@classmethod
|
||||
def weakest_of(cls, s1, s2):
|
||||
if cls.weaker(s1, s2):
|
||||
return s1
|
||||
|
||||
return s2
|
||||
|
||||
@classmethod
|
||||
def strongest(cls, s1, s2):
|
||||
if cls.stronger(s1, s2):
|
||||
return s1
|
||||
|
||||
return s2
|
||||
|
||||
def next_weaker(self):
|
||||
strengths = {
|
||||
0: self.__class__.WEAKEST,
|
||||
1: self.__class__.WEAK_DEFAULT,
|
||||
2: self.__class__.NORMAL,
|
||||
3: self.__class__.STRONG_DEFAULT,
|
||||
4: self.__class__.PREFERRED,
|
||||
# TODO: This looks like a bug in the original code. Shouldn't this be
|
||||
# ``STRONG_PREFERRED? Keeping for porting sake...
|
||||
5: self.__class__.REQUIRED,
|
||||
}
|
||||
return strengths[self.strength]
|
||||
|
||||
|
||||
# This is a terrible pattern IMO, but true to the original JS implementation.
|
||||
Strength.REQUIRED = Strength(0, "required")
|
||||
Strength.STONG_PREFERRED = Strength(1, "strongPreferred")
|
||||
Strength.PREFERRED = Strength(2, "preferred")
|
||||
Strength.STRONG_DEFAULT = Strength(3, "strongDefault")
|
||||
Strength.NORMAL = Strength(4, "normal")
|
||||
Strength.WEAK_DEFAULT = Strength(5, "weakDefault")
|
||||
Strength.WEAKEST = Strength(6, "weakest")
|
||||
|
||||
|
||||
class Constraint(object):
|
||||
def __init__(self, strength):
|
||||
super(Constraint, self).__init__()
|
||||
self.strength = strength
|
||||
|
||||
def add_constraint(self):
|
||||
global planner
|
||||
self.add_to_graph()
|
||||
planner.incremental_add(self)
|
||||
|
||||
def satisfy(self, mark):
|
||||
global planner
|
||||
self.choose_method(mark)
|
||||
|
||||
if not self.is_satisfied():
|
||||
if self.strength == Strength.REQUIRED:
|
||||
print('Could not satisfy a required constraint!')
|
||||
|
||||
return None
|
||||
|
||||
self.mark_inputs(mark)
|
||||
out = self.output()
|
||||
overridden = out.determined_by
|
||||
|
||||
if overridden is not None:
|
||||
overridden.mark_unsatisfied()
|
||||
|
||||
out.determined_by = self
|
||||
|
||||
if not planner.add_propagate(self, mark):
|
||||
print('Cycle encountered')
|
||||
|
||||
out.mark = mark
|
||||
return overridden
|
||||
|
||||
def destroy_constraint(self):
|
||||
global planner
|
||||
if self.is_satisfied():
|
||||
planner.incremental_remove(self)
|
||||
else:
|
||||
self.remove_from_graph()
|
||||
|
||||
def is_input(self):
|
||||
return False
|
||||
|
||||
|
||||
class UrnaryConstraint(Constraint):
|
||||
def __init__(self, v, strength):
|
||||
super(UrnaryConstraint, self).__init__(strength)
|
||||
self.my_output = v
|
||||
self.satisfied = False
|
||||
self.add_constraint()
|
||||
|
||||
def add_to_graph(self):
|
||||
self.my_output.add_constraint(self)
|
||||
self.satisfied = False
|
||||
|
||||
def choose_method(self, mark):
|
||||
if self.my_output.mark != mark and \
|
||||
Strength.stronger(self.strength, self.my_output.walk_strength):
|
||||
self.satisfied = True
|
||||
else:
|
||||
self.satisfied = False
|
||||
|
||||
def is_satisfied(self):
|
||||
return self.satisfied
|
||||
|
||||
def mark_inputs(self, mark):
|
||||
# No-ops.
|
||||
pass
|
||||
|
||||
def output(self):
|
||||
# Ugh. Keeping it for consistency with the original. So much for
|
||||
# "we're all adults here"...
|
||||
return self.my_output
|
||||
|
||||
def recalculate(self):
|
||||
self.my_output.walk_strength = self.strength
|
||||
self.my_output.stay = not self.is_input()
|
||||
|
||||
if self.my_output.stay:
|
||||
self.execute()
|
||||
|
||||
def mark_unsatisfied(self):
|
||||
self.satisfied = False
|
||||
|
||||
def inputs_known(self, mark):
|
||||
return True
|
||||
|
||||
def remove_from_graph(self):
|
||||
if self.my_output is not None:
|
||||
self.my_output.remove_constraint(self)
|
||||
self.satisfied = False
|
||||
|
||||
|
||||
class StayConstraint(UrnaryConstraint):
|
||||
def __init__(self, v, string):
|
||||
super(StayConstraint, self).__init__(v, string)
|
||||
|
||||
def execute(self):
|
||||
# The methods, THEY DO NOTHING.
|
||||
pass
|
||||
|
||||
|
||||
class EditConstraint(UrnaryConstraint):
|
||||
def __init__(self, v, string):
|
||||
super(EditConstraint, self).__init__(v, string)
|
||||
|
||||
def is_input(self):
|
||||
return True
|
||||
|
||||
def execute(self):
|
||||
# This constraint also does nothing.
|
||||
pass
|
||||
|
||||
|
||||
class Direction(object):
|
||||
# Hooray for things that ought to be structs!
|
||||
NONE = 0
|
||||
FORWARD = 1
|
||||
BACKWARD = -1
|
||||
|
||||
|
||||
class BinaryConstraint(Constraint):
|
||||
def __init__(self, v1, v2, strength):
|
||||
super(BinaryConstraint, self).__init__(strength)
|
||||
self.v1 = v1
|
||||
self.v2 = v2
|
||||
self.direction = Direction.NONE
|
||||
self.add_constraint()
|
||||
|
||||
def choose_method(self, mark):
|
||||
if self.v1.mark == mark:
|
||||
if self.v2.mark != mark and Strength.stronger(self.strength, self.v2.walk_strength):
|
||||
self.direction = Direction.FORWARD
|
||||
else:
|
||||
self.direction = Direction.BACKWARD
|
||||
|
||||
if self.v2.mark == mark:
|
||||
if self.v1.mark != mark and Strength.stronger(self.strength, self.v1.walk_strength):
|
||||
self.direction = Direction.BACKWARD
|
||||
else:
|
||||
self.direction = Direction.NONE
|
||||
|
||||
if Strength.weaker(self.v1.walk_strength, self.v2.walk_strength):
|
||||
if Strength.stronger(self.strength, self.v1.walk_strength):
|
||||
self.direction = Direction.BACKWARD
|
||||
else:
|
||||
self.direction = Direction.NONE
|
||||
else:
|
||||
if Strength.stronger(self.strength, self.v2.walk_strength):
|
||||
self.direction = Direction.FORWARD
|
||||
else:
|
||||
self.direction = Direction.BACKWARD
|
||||
|
||||
def add_to_graph(self):
|
||||
self.v1.add_constraint(self)
|
||||
self.v2.add_constraint(self)
|
||||
self.direction = Direction.NONE
|
||||
|
||||
def is_satisfied(self):
|
||||
return self.direction != Direction.NONE
|
||||
|
||||
def mark_inputs(self, mark):
|
||||
self.input().mark = mark
|
||||
|
||||
def input(self):
|
||||
if self.direction == Direction.FORWARD:
|
||||
return self.v1
|
||||
|
||||
return self.v2
|
||||
|
||||
def output(self):
|
||||
if self.direction == Direction.FORWARD:
|
||||
return self.v2
|
||||
|
||||
return self.v1
|
||||
|
||||
def recalculate(self):
|
||||
ihn = self.input()
|
||||
out = self.output()
|
||||
out.walk_strength = Strength.weakest_of(self.strength, ihn.walk_strength)
|
||||
out.stay = ihn.stay
|
||||
|
||||
if out.stay:
|
||||
self.execute()
|
||||
|
||||
def mark_unsatisfied(self):
|
||||
self.direction = Direction.NONE
|
||||
|
||||
def inputs_known(self, mark):
|
||||
i = self.input()
|
||||
return i.mark == mark or i.stay or i.determined_by == None
|
||||
|
||||
def remove_from_graph(self):
|
||||
if self.v1 is not None:
|
||||
self.v1.remove_constraint(self)
|
||||
|
||||
if self.v2 is not None:
|
||||
self.v2.remove_constraint(self)
|
||||
|
||||
self.direction = Direction.NONE
|
||||
|
||||
|
||||
class ScaleConstraint(BinaryConstraint):
|
||||
def __init__(self, src, scale, offset, dest, strength):
|
||||
self.direction = Direction.NONE
|
||||
self.scale = scale
|
||||
self.offset = offset
|
||||
super(ScaleConstraint, self).__init__(src, dest, strength)
|
||||
|
||||
def add_to_graph(self):
|
||||
super(ScaleConstraint, self).add_to_graph()
|
||||
self.scale.add_constraint(self)
|
||||
self.offset.add_constraint(self)
|
||||
|
||||
def remove_from_graph(self):
|
||||
super(ScaleConstraint, self).remove_from_graph()
|
||||
|
||||
if self.scale is not None:
|
||||
self.scale.remove_constraint(self)
|
||||
|
||||
if self.offset is not None:
|
||||
self.offset.remove_constraint(self)
|
||||
|
||||
def mark_inputs(self, mark):
|
||||
super(ScaleConstraint, self).mark_inputs(mark)
|
||||
self.scale.mark = mark
|
||||
self.offset.mark = mark
|
||||
|
||||
def execute(self):
|
||||
if self.direction == Direction.FORWARD:
|
||||
self.v2.value = self.v1.value * self.scale.value + self.offset.value
|
||||
else:
|
||||
self.v1.value = (self.v2.value - self.offset.value) / self.scale.value
|
||||
|
||||
def recalculate(self):
|
||||
ihn = self.input()
|
||||
out = self.output()
|
||||
out.walk_strength = Strength.weakest_of(self.strength, ihn.walk_strength)
|
||||
out.stay = ihn.stay and self.scale.stay and self.offset.stay
|
||||
|
||||
if out.stay:
|
||||
self.execute()
|
||||
|
||||
|
||||
class EqualityConstraint(BinaryConstraint):
|
||||
def execute(self):
|
||||
self.output().value = self.input().value
|
||||
|
||||
|
||||
class Variable(object):
|
||||
def __init__(self, name, initial_value=0):
|
||||
super(Variable, self).__init__()
|
||||
self.name = name
|
||||
self.value = initial_value
|
||||
self.constraints = []
|
||||
self.determined_by = None
|
||||
self.mark = 0
|
||||
self.walk_strength = Strength.WEAKEST
|
||||
self.stay = True
|
||||
|
||||
def __repr__(self):
|
||||
# To make debugging this beast from pdb easier...
|
||||
return '<Variable: %s - %s>' % (
|
||||
self.name,
|
||||
self.value
|
||||
)
|
||||
|
||||
def add_constraint(self, constraint):
|
||||
self.constraints.append(constraint)
|
||||
|
||||
def remove_constraint(self, constraint):
|
||||
self.constraints.remove(constraint)
|
||||
|
||||
if self.determined_by == constraint:
|
||||
self.determined_by = None
|
||||
|
||||
|
||||
class Planner(object):
|
||||
def __init__(self):
|
||||
super(Planner, self).__init__()
|
||||
self.current_mark = 0
|
||||
|
||||
def incremental_add(self, constraint):
|
||||
mark = self.new_mark()
|
||||
overridden = constraint.satisfy(mark)
|
||||
|
||||
while overridden is not None:
|
||||
overridden = overridden.satisfy(mark)
|
||||
|
||||
def incremental_remove(self, constraint):
|
||||
out = constraint.output()
|
||||
constraint.mark_unsatisfied()
|
||||
constraint.remove_from_graph()
|
||||
unsatisfied = self.remove_propagate_from(out)
|
||||
strength = Strength.REQUIRED
|
||||
# Do-while, the Python way.
|
||||
repeat = True
|
||||
|
||||
while repeat:
|
||||
for u in unsatisfied:
|
||||
if u.strength == strength:
|
||||
self.incremental_add(u)
|
||||
|
||||
strength = strength.next_weaker()
|
||||
|
||||
repeat = strength != Strength.WEAKEST
|
||||
|
||||
def new_mark(self):
|
||||
self.current_mark += 1
|
||||
return self.current_mark
|
||||
|
||||
def make_plan(self, sources):
|
||||
mark = self.new_mark()
|
||||
plan = Plan()
|
||||
todo = sources
|
||||
|
||||
while len(todo):
|
||||
c = todo.pop(0)
|
||||
|
||||
if c.output().mark != mark and c.inputs_known(mark):
|
||||
plan.add_constraint(c)
|
||||
c.output().mark = mark
|
||||
self.add_constraints_consuming_to(c.output(), todo)
|
||||
|
||||
return plan
|
||||
|
||||
def extract_plan_from_constraints(self, constraints):
|
||||
sources = []
|
||||
|
||||
for c in constraints:
|
||||
if c.is_input() and c.is_satisfied():
|
||||
sources.append(c)
|
||||
|
||||
return self.make_plan(sources)
|
||||
|
||||
def add_propagate(self, c, mark):
|
||||
todo = []
|
||||
todo.append(c)
|
||||
|
||||
while len(todo):
|
||||
d = todo.pop(0)
|
||||
|
||||
if d.output().mark == mark:
|
||||
self.incremental_remove(c)
|
||||
return False
|
||||
|
||||
d.recalculate()
|
||||
self.add_constraints_consuming_to(d.output(), todo)
|
||||
|
||||
return True
|
||||
|
||||
def remove_propagate_from(self, out):
|
||||
out.determined_by = None
|
||||
out.walk_strength = Strength.WEAKEST
|
||||
out.stay = True
|
||||
unsatisfied = []
|
||||
todo = []
|
||||
todo.append(out)
|
||||
|
||||
while len(todo):
|
||||
v = todo.pop(0)
|
||||
|
||||
for c in v.constraints:
|
||||
if not c.is_satisfied():
|
||||
unsatisfied.append(c)
|
||||
|
||||
determining = v.determined_by
|
||||
|
||||
for c in v.constraints:
|
||||
if c != determining and c.is_satisfied():
|
||||
c.recalculate()
|
||||
todo.append(c.output())
|
||||
|
||||
return unsatisfied
|
||||
|
||||
def add_constraints_consuming_to(self, v, coll):
|
||||
determining = v.determined_by
|
||||
cc = v.constraints
|
||||
|
||||
for c in cc:
|
||||
if c != determining and c.is_satisfied():
|
||||
# I guess we're just updating a reference (``coll``)? Seems
|
||||
# inconsistent with the rest of the implementation, where they
|
||||
# return the lists...
|
||||
coll.append(c)
|
||||
|
||||
|
||||
class Plan(object):
|
||||
def __init__(self):
|
||||
super(Plan, self).__init__()
|
||||
self.v = []
|
||||
|
||||
def add_constraint(self, c):
|
||||
self.v.append(c)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.v)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.v[index]
|
||||
|
||||
def execute(self):
|
||||
for c in self.v:
|
||||
c.execute()
|
||||
|
||||
|
||||
# Main
|
||||
total = 0
|
||||
|
||||
def chain_test(n):
|
||||
"""
|
||||
This is the standard DeltaBlue benchmark. A long chain of equality
|
||||
constraints is constructed with a stay constraint on one end. An
|
||||
edit constraint is then added to the opposite end and the time is
|
||||
measured for adding and removing this constraint, and extracting
|
||||
and executing a constraint satisfaction plan. There are two cases.
|
||||
In case 1, the added constraint is stronger than the stay
|
||||
constraint and values must propagate down the entire length of the
|
||||
chain. In case 2, the added constraint is weaker than the stay
|
||||
constraint so it cannot be accomodated. The cost in this case is,
|
||||
of course, very low. Typical situations lie somewhere between these
|
||||
two extremes.
|
||||
"""
|
||||
global planner
|
||||
global total
|
||||
|
||||
planner = Planner()
|
||||
prev, first, last = None, None, None
|
||||
|
||||
# We need to go up to n inclusively.
|
||||
for i in range(n + 1):
|
||||
name = "v%s" % i
|
||||
v = Variable(name)
|
||||
|
||||
if prev is not None:
|
||||
EqualityConstraint(prev, v, Strength.REQUIRED)
|
||||
|
||||
if i == 0:
|
||||
first = v
|
||||
|
||||
if i == n:
|
||||
last = v
|
||||
|
||||
prev = v
|
||||
|
||||
StayConstraint(last, Strength.STRONG_DEFAULT)
|
||||
edit = EditConstraint(first, Strength.PREFERRED)
|
||||
edits = []
|
||||
edits.append(edit)
|
||||
plan = planner.extract_plan_from_constraints(edits)
|
||||
|
||||
for i in range(100):
|
||||
first.value = i
|
||||
plan.execute()
|
||||
|
||||
total += int(last.value)
|
||||
if last.value != i:
|
||||
print("Chain test failed.")
|
||||
|
||||
|
||||
def projection_test(n):
|
||||
"""
|
||||
This test constructs a two sets of variables related to each
|
||||
other by a simple linear transformation (scale and offset). The
|
||||
time is measured to change a variable on either side of the
|
||||
mapping and to change the scale and offset factors.
|
||||
"""
|
||||
global planner
|
||||
global total
|
||||
|
||||
planner = Planner()
|
||||
scale = Variable("scale", 10)
|
||||
offset = Variable("offset", 1000)
|
||||
src, dest = None, None
|
||||
|
||||
dests = []
|
||||
|
||||
for i in range(n):
|
||||
src = Variable("src%s" % i, i)
|
||||
dst = Variable("dst%s" % i, i)
|
||||
dests.append(dst)
|
||||
StayConstraint(src, Strength.NORMAL)
|
||||
ScaleConstraint(src, scale, offset, dst, Strength.REQUIRED)
|
||||
|
||||
change(src, 17)
|
||||
|
||||
total += int(dst.value)
|
||||
if dst.value != 1170:
|
||||
print("Projection 1 failed")
|
||||
|
||||
change(dst, 1050)
|
||||
|
||||
total += int(src.value)
|
||||
if src.value != 5:
|
||||
print("Projection 2 failed")
|
||||
|
||||
change(scale, 5)
|
||||
|
||||
for i in range(n - 1):
|
||||
total += int(dests[i].value)
|
||||
if dests[i].value != (i * 5 + 1000):
|
||||
print("Projection 3 failed")
|
||||
|
||||
change(offset, 2000)
|
||||
|
||||
for i in range(n - 1):
|
||||
total += int(dests[i].value)
|
||||
if dests[i].value != (i * 5 + 2000):
|
||||
print("Projection 4 failed")
|
||||
|
||||
|
||||
def change(v, new_value):
|
||||
global planner
|
||||
edit = EditConstraint(v, Strength.PREFERRED)
|
||||
edits = []
|
||||
edits.append(edit)
|
||||
|
||||
plan = planner.extract_plan_from_constraints(edits)
|
||||
|
||||
for i in range(10):
|
||||
v.value = new_value
|
||||
plan.execute()
|
||||
|
||||
edit.destroy_constraint()
|
||||
|
||||
|
||||
# HOORAY FOR GLOBALS... Oh wait.
|
||||
# In spirit of the original, we'll keep it, but ugh.
|
||||
planner = None
|
||||
|
||||
|
||||
def delta_blue():
|
||||
global total
|
||||
start = time.clock()
|
||||
for i in range(20):
|
||||
chain_test(100)
|
||||
projection_test(100)
|
||||
print(total)
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
delta_blue()
|
||||
@ -1,717 +0,0 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
// Copyright 1996 John Maloney and Mario Wolczko
|
||||
//
|
||||
// This file is part of GNU Smalltalk.
|
||||
//
|
||||
// GNU Smalltalk is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 2, or (at your option) any later version.
|
||||
//
|
||||
// GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
// details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// GNU Smalltalk; see the file COPYING. If not, write to the Free Software
|
||||
// Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// Translated first from Smalltalk to JavaScript, and finally to
|
||||
// Dart by Google 2008-2010.
|
||||
//
|
||||
// Translated to Wren by Bob Nystrom 2014.
|
||||
|
||||
// A Wren implementation of the DeltaBlue constraint-solving
|
||||
// algorithm, as described in:
|
||||
//
|
||||
// "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver"
|
||||
// Bjorn N. Freeman-Benson and John Maloney
|
||||
// January 1990 Communications of the ACM,
|
||||
// also available as University of Washington TR 89-08-06.
|
||||
//
|
||||
// Beware: this benchmark is written in a grotesque style where
|
||||
// the constraint model is built by side-effects from constructors.
|
||||
// I've kept it this way to avoid deviating too much from the original
|
||||
// implementation.
|
||||
|
||||
// TODO: Support forward declarations of globals.
|
||||
var REQUIRED = null
|
||||
var STRONG_REFERRED = null
|
||||
var PREFERRED = null
|
||||
var STRONG_DEFAULT = null
|
||||
var NORMAL = null
|
||||
var WEAK_DEFAULT = null
|
||||
var WEAKEST = null
|
||||
|
||||
var ORDERED = null
|
||||
|
||||
// Strengths are used to measure the relative importance of constraints.
|
||||
// New strengths may be inserted in the strength hierarchy without
|
||||
// disrupting current constraints. Strengths cannot be created outside
|
||||
// this class, so == can be used for value comparison.
|
||||
class Strength {
|
||||
new(value, name) {
|
||||
_value = value
|
||||
_name = name
|
||||
}
|
||||
|
||||
value { _value }
|
||||
name { _name }
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
// Compile time computed constants.
|
||||
REQUIRED = new Strength(0, "required")
|
||||
STRONG_REFERRED = new Strength(1, "strongPreferred")
|
||||
PREFERRED = new Strength(2, "preferred")
|
||||
STRONG_DEFAULT = new Strength(3, "strongDefault")
|
||||
NORMAL = new Strength(4, "normal")
|
||||
WEAK_DEFAULT = new Strength(5, "weakDefault")
|
||||
WEAKEST = new Strength(6, "weakest")
|
||||
|
||||
ORDERED = [
|
||||
WEAKEST, WEAK_DEFAULT, NORMAL, STRONG_DEFAULT, PREFERRED, STRONG_REFERRED
|
||||
]
|
||||
|
||||
// TODO: Forward declarations.
|
||||
var planner
|
||||
|
||||
class Constraint {
|
||||
new(strength) {
|
||||
_strength = strength
|
||||
}
|
||||
|
||||
strength { _strength }
|
||||
|
||||
// Activate this constraint and attempt to satisfy it.
|
||||
addConstraint {
|
||||
addToGraph
|
||||
planner.incrementalAdd(this)
|
||||
}
|
||||
|
||||
// Attempt to find a way to enforce this constraint. If successful,
|
||||
// record the solution, perhaps modifying the current dataflow
|
||||
// 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) {
|
||||
chooseMethod(mark)
|
||||
if (!isSatisfied) {
|
||||
if (_strength == REQUIRED) {
|
||||
IO.print("Could not satisfy a required constraint!")
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
markInputs(mark)
|
||||
var out = output
|
||||
var overridden = out.determinedBy
|
||||
if (overridden != null) overridden.markUnsatisfied
|
||||
out.determinedBy = this
|
||||
if (!planner.addPropagate(this, mark)) IO.print("Cycle encountered")
|
||||
out.mark = mark
|
||||
return overridden
|
||||
}
|
||||
|
||||
destroyConstraint {
|
||||
if (isSatisfied) planner.incrementalRemove(this)
|
||||
removeFromGraph
|
||||
}
|
||||
|
||||
// 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 }
|
||||
}
|
||||
|
||||
// Abstract superclass for constraints having a single possible output variable.
|
||||
class UnaryConstraint is Constraint {
|
||||
new(myOutput, strength) {
|
||||
super(strength)
|
||||
_satisfied = false
|
||||
_myOutput = myOutput
|
||||
addConstraint
|
||||
}
|
||||
|
||||
// Adds this constraint to the constraint graph.
|
||||
addToGraph {
|
||||
_myOutput.addConstraint(this)
|
||||
_satisfied = false;
|
||||
}
|
||||
|
||||
// Decides if this constraint can be satisfied and records that decision.
|
||||
chooseMethod(mark) {
|
||||
_satisfied = (_myOutput.mark != mark) &&
|
||||
Strength.stronger(strength, _myOutput.walkStrength)
|
||||
}
|
||||
|
||||
// Returns true if this constraint is satisfied in the current solution.
|
||||
isSatisfied { _satisfied }
|
||||
|
||||
markInputs(mark) {
|
||||
// has no inputs.
|
||||
}
|
||||
|
||||
// Returns the current output variable.
|
||||
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 {
|
||||
_myOutput.walkStrength = strength
|
||||
_myOutput.stay = !isInput
|
||||
if (_myOutput.stay) execute // Stay optimization.
|
||||
}
|
||||
|
||||
// Records that this constraint is unsatisfied.
|
||||
markUnsatisfied {
|
||||
_satisfied = false
|
||||
}
|
||||
|
||||
inputsKnown(mark) { true }
|
||||
|
||||
removeFromGraph {
|
||||
if (_myOutput != null) _myOutput.removeConstraint(this)
|
||||
_satisfied = false
|
||||
}
|
||||
}
|
||||
|
||||
// Variables that should, with some level of preference, stay the same.
|
||||
// Planners may exploit the fact that instances, if satisfied, will not
|
||||
// change their output during plan execution. This is called "stay
|
||||
// optimization".
|
||||
class StayConstraint is UnaryConstraint {
|
||||
new(variable, strength) {
|
||||
super(variable, strength)
|
||||
}
|
||||
|
||||
execute {
|
||||
// Stay constraints do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
// A unary input constraint used to mark a variable that the client
|
||||
// wishes to change.
|
||||
class EditConstraint is UnaryConstraint {
|
||||
EditConstraint(variable, strength) {
|
||||
super(variable, strength)
|
||||
}
|
||||
|
||||
// Edits indicate that a variable is to be changed by imperative code.
|
||||
isInput { true }
|
||||
|
||||
execute {
|
||||
// Edit constraints do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
// Directions.
|
||||
var NONE = 1
|
||||
var FORWARD = 2
|
||||
var BACKWARD = 0
|
||||
|
||||
// Abstract superclass for constraints having two possible output
|
||||
// variables.
|
||||
class BinaryConstraint is Constraint {
|
||||
new(v1, v2, strength) {
|
||||
super(strength)
|
||||
_v1 = v1
|
||||
_v2 = v2
|
||||
_direction = NONE
|
||||
addConstraint
|
||||
}
|
||||
|
||||
direction { _direction }
|
||||
v1 { _v1 }
|
||||
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) {
|
||||
if (_v1.mark == mark) {
|
||||
if (_v2.mark != mark &&
|
||||
Strength.stronger(strength, _v2.walkStrength)) {
|
||||
_direction = FORWARD
|
||||
} else {
|
||||
_direction = NONE
|
||||
}
|
||||
}
|
||||
|
||||
if (_v2.mark == mark) {
|
||||
if (_v1.mark != mark &&
|
||||
Strength.stronger(strength, _v1.walkStrength)) {
|
||||
_direction = BACKWARD
|
||||
} else {
|
||||
_direction = NONE
|
||||
}
|
||||
}
|
||||
|
||||
if (Strength.weaker(_v1.walkStrength, _v2.walkStrength)) {
|
||||
if (Strength.stronger(strength, _v1.walkStrength)) {
|
||||
_direction = BACKWARD
|
||||
} else {
|
||||
_direction = NONE
|
||||
}
|
||||
} else {
|
||||
if (Strength.stronger(strength, _v2.walkStrength)) {
|
||||
_direction = FORWARD
|
||||
} else {
|
||||
_direction = BACKWARD
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add this constraint to the constraint graph.
|
||||
addToGraph {
|
||||
_v1.addConstraint(this)
|
||||
_v2.addConstraint(this)
|
||||
_direction = NONE
|
||||
}
|
||||
|
||||
// Answer true if this constraint is satisfied in the current solution.
|
||||
isSatisfied { _direction != NONE }
|
||||
|
||||
// Mark the input variable with the given mark.
|
||||
markInputs(mark) {
|
||||
input.mark = mark
|
||||
}
|
||||
|
||||
// Returns the current input variable
|
||||
input {
|
||||
if (_direction == FORWARD) return _v1
|
||||
return _v2
|
||||
}
|
||||
|
||||
// Returns the current output variable.
|
||||
output {
|
||||
if (_direction == FORWARD) return _v2
|
||||
return _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 {
|
||||
var ihn = input
|
||||
var out = output
|
||||
out.walkStrength = Strength.weakest(strength, ihn.walkStrength)
|
||||
out.stay = ihn.stay
|
||||
if (out.stay) execute
|
||||
}
|
||||
|
||||
// Record the fact that this constraint is unsatisfied.
|
||||
markUnsatisfied {
|
||||
_direction = NONE
|
||||
}
|
||||
|
||||
inputsKnown(mark) {
|
||||
var i = input
|
||||
return i.mark == mark || i.stay || i.determinedBy == null
|
||||
}
|
||||
|
||||
removeFromGraph {
|
||||
if (_v1 != null) _v1.removeConstraint(this)
|
||||
if (_v2 != null) _v2.removeConstraint(this)
|
||||
_direction = NONE
|
||||
}
|
||||
}
|
||||
|
||||
// Relates two variables by the linear scaling relationship: "v2 =
|
||||
// (v1 * scale) + offset". Either v1 or v2 may be changed to maintain
|
||||
// this relationship but the scale factor and offset are considered
|
||||
// read-only.
|
||||
class ScaleConstraint is BinaryConstraint {
|
||||
new(src, scale, offset, dest, strength) {
|
||||
_scale = scale
|
||||
_offset = offset
|
||||
super(src, dest, strength)
|
||||
}
|
||||
|
||||
// Adds this constraint to the constraint graph.
|
||||
addToGraph {
|
||||
super.addToGraph
|
||||
_scale.addConstraint(this)
|
||||
_offset.addConstraint(this)
|
||||
}
|
||||
|
||||
removeFromGraph {
|
||||
super.removeFromGraph
|
||||
if (_scale != null) _scale.removeConstraint(this)
|
||||
if (_offset != null) _offset.removeConstraint(this)
|
||||
}
|
||||
|
||||
markInputs(mark) {
|
||||
super.markInputs(mark)
|
||||
_scale.mark = _offset.mark = mark
|
||||
}
|
||||
|
||||
// Enforce this constraint. Assume that it is satisfied.
|
||||
execute {
|
||||
if (direction == FORWARD) {
|
||||
v2.value = v1.value * _scale.value + _offset.value;
|
||||
} else {
|
||||
// TODO: Is this the same semantics as ~/?
|
||||
v1.value = ((v2.value - _offset.value) / _scale.value).floor;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
var ihn = input
|
||||
var out = output
|
||||
out.walkStrength = Strength.weakest(strength, ihn.walkStrength)
|
||||
out.stay = ihn.stay && _scale.stay && _offset.stay
|
||||
if (out.stay) execute
|
||||
}
|
||||
}
|
||||
|
||||
// Constrains two variables to have the same value.
|
||||
class EqualityConstraint is BinaryConstraint {
|
||||
new(v1, v2, strength) {
|
||||
super(v1, v2, strength)
|
||||
}
|
||||
|
||||
// Enforce this constraint. Assume that it is satisfied.
|
||||
execute {
|
||||
output.value = input.value
|
||||
}
|
||||
}
|
||||
|
||||
// A constrained variable. In addition to its value, it maintain the
|
||||
// structure of the constraint graph, the current dataflow graph, and
|
||||
// various parameters of interest to the DeltaBlue incremental
|
||||
// constraint solver.
|
||||
class Variable {
|
||||
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 }
|
||||
|
||||
// Add the given constraint to the set of all constraints that refer
|
||||
// this variable.
|
||||
addConstraint(constraint) {
|
||||
_constraints.add(constraint)
|
||||
}
|
||||
|
||||
// Removes all traces of c from this variable.
|
||||
removeConstraint(constraint) {
|
||||
_constraints = _constraints.where { |c| c != constraint }
|
||||
if (_determinedBy == constraint) _determinedBy = null
|
||||
}
|
||||
}
|
||||
|
||||
// A Plan is an ordered list of constraints to be executed in sequence
|
||||
// to resatisfy all currently satisfiable constraints in the face of
|
||||
// one or more changing inputs.
|
||||
class Plan {
|
||||
new {
|
||||
_list = []
|
||||
}
|
||||
|
||||
addConstraint(constraint) {
|
||||
_list.add(constraint)
|
||||
}
|
||||
|
||||
size { _list.count }
|
||||
|
||||
execute {
|
||||
for (constraint in _list) {
|
||||
constraint.execute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Planner {
|
||||
new {
|
||||
_currentMark = 0
|
||||
}
|
||||
|
||||
// Attempt to satisfy the given constraint and, if successful,
|
||||
// incrementally update the dataflow graph. Details: If satifying
|
||||
// the constraint is successful, it may override a weaker constraint
|
||||
// on its output. The algorithm attempts to resatisfy that
|
||||
// constraint using some other method. This process is repeated
|
||||
// until either a) it reaches a variable that was not previously
|
||||
// determined by any constraint or b) it reaches a constraint that
|
||||
// is too weak to be satisfied using any of its methods. The
|
||||
// variables of constraints that have been processed are marked with
|
||||
// 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) {
|
||||
var mark = newMark
|
||||
var overridden = constraint.satisfy(mark)
|
||||
while (overridden != null) {
|
||||
overridden = overridden.satisfy(mark)
|
||||
}
|
||||
}
|
||||
|
||||
// Entry point for retracting a constraint. Remove the given
|
||||
// constraint and incrementally update the dataflow graph.
|
||||
// Details: Retracting the given constraint may allow some currently
|
||||
// unsatisfiable downstream constraint to be satisfied. We therefore collect
|
||||
// a list of unsatisfied downstream constraints and attempt to
|
||||
// satisfy each one in turn. This list is traversed by constraint
|
||||
// strength, strongest first, as a heuristic for avoiding
|
||||
// unnecessarily adding and then overriding weak constraints.
|
||||
// Assume: [c] is satisfied.
|
||||
incrementalRemove(constraint) {
|
||||
var out = constraint.output
|
||||
constraint.markUnsatisfied
|
||||
constraint.removeFromGraph
|
||||
var unsatisfied = removePropagateFrom(out)
|
||||
var strength = REQUIRED
|
||||
while (true) {
|
||||
for (i in 0...unsatisfied.count) {
|
||||
var u = unsatisfied[i]
|
||||
if (u.strength == strength) incrementalAdd(u)
|
||||
}
|
||||
strength = strength.nextWeaker
|
||||
if (strength == WEAKEST) break
|
||||
}
|
||||
}
|
||||
|
||||
// Select a previously unused mark value.
|
||||
newMark {
|
||||
_currentMark = _currentMark + 1
|
||||
return _currentMark
|
||||
}
|
||||
|
||||
// Extract a plan for resatisfaction starting from the given source
|
||||
// constraints, usually a set of input constraints. This method
|
||||
// assumes that stay optimization is desired; the plan will contain
|
||||
// only constraints whose output variables are not stay. Constraints
|
||||
// that do no computation, such as stay and edit constraints, are
|
||||
// not included in the plan.
|
||||
// Details: The outputs of a constraint are marked when it is added
|
||||
// to the plan under construction. A constraint may be appended to
|
||||
// the plan when all its input variables are known. A variable is
|
||||
// known if either a) the variable is marked (indicating that has
|
||||
// been computed by a constraint appearing earlier in the plan), b)
|
||||
// the variable is 'stay' (i.e. it is a constant at plan execution
|
||||
// time), or c) the variable is not determined by any
|
||||
// constraint. The last provision is for past states of history
|
||||
// variables, which are not stay but which are also not computed by
|
||||
// any constraint.
|
||||
// Assume: [sources] are all satisfied.
|
||||
makePlan(sources) {
|
||||
var mark = newMark
|
||||
var plan = new Plan
|
||||
var todo = sources
|
||||
while (todo.count > 0) {
|
||||
var constraint = todo.removeAt(-1)
|
||||
if (constraint.output.mark != mark && constraint.inputsKnown(mark)) {
|
||||
plan.addConstraint(constraint)
|
||||
constraint.output.mark = mark
|
||||
addConstraintsConsumingTo(constraint.output, todo)
|
||||
}
|
||||
}
|
||||
return plan
|
||||
}
|
||||
|
||||
// Extract a plan for resatisfying starting from the output of the
|
||||
// given [constraints], usually a set of input constraints.
|
||||
extractPlanFromConstraints(constraints) {
|
||||
var sources = []
|
||||
for (i in 0...constraints.count) {
|
||||
var constraint = constraints[i]
|
||||
// if not in plan already and eligible for inclusion.
|
||||
if (constraint.isInput && constraint.isSatisfied) sources.add(constraint)
|
||||
}
|
||||
return makePlan(sources)
|
||||
}
|
||||
|
||||
// Recompute the walkabout strengths and stay flags of all variables
|
||||
// downstream of the given constraint and recompute the actual
|
||||
// values of all variables whose stay flag is true. If a cycle is
|
||||
// detected, remove the given constraint and answer
|
||||
// false. Otherwise, answer true.
|
||||
// Details: Cycles are detected when a marked variable is
|
||||
// encountered downstream of the given constraint. The sender is
|
||||
// assumed to have marked the inputs of the given constraint with
|
||||
// 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) {
|
||||
var todo = [constraint]
|
||||
while (todo.count > 0) {
|
||||
var d = todo.removeAt(-1)
|
||||
if (d.output.mark == mark) {
|
||||
incrementalRemove(constraint)
|
||||
return false
|
||||
}
|
||||
|
||||
d.recalculate
|
||||
addConstraintsConsumingTo(d.output, todo)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 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) {
|
||||
out.determinedBy = null
|
||||
out.walkStrength = WEAKEST
|
||||
out.stay = true
|
||||
var unsatisfied = []
|
||||
var todo = [out]
|
||||
while (todo.count > 0) {
|
||||
var v = todo.removeAt(-1)
|
||||
for (i in 0...v.constraints.count) {
|
||||
var constraint = v.constraints[i]
|
||||
if (!constraint.isSatisfied) unsatisfied.add(constraint)
|
||||
}
|
||||
|
||||
var determining = v.determinedBy
|
||||
for (i in 0...v.constraints.count) {
|
||||
var next = v.constraints[i]
|
||||
if (next != determining && next.isSatisfied) {
|
||||
next.recalculate
|
||||
todo.add(next.output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return unsatisfied
|
||||
}
|
||||
|
||||
addConstraintsConsumingTo(v, coll) {
|
||||
var determining = v.determinedBy
|
||||
for (i in 0...v.constraints.count) {
|
||||
var constraint = v.constraints[i]
|
||||
if (constraint != determining && constraint.isSatisfied) {
|
||||
coll.add(constraint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var total = 0
|
||||
|
||||
// This is the standard DeltaBlue benchmark. A long chain of equality
|
||||
// constraints is constructed with a stay constraint on one end. An
|
||||
// edit constraint is then added to the opposite end and the time is
|
||||
// measured for adding and removing this constraint, and extracting
|
||||
// and executing a constraint satisfaction plan. There are two cases.
|
||||
// In case 1, the added constraint is stronger than the stay
|
||||
// constraint and values must propagate down the entire length of the
|
||||
// chain. In case 2, the added constraint is weaker than the stay
|
||||
// constraint so it cannot be accomodated. The cost in this case is,
|
||||
// of course, very low. Typical situations lie somewhere between these
|
||||
// two extremes.
|
||||
var chainTest = new Fn {|n|
|
||||
planner = new Planner
|
||||
var prev = null
|
||||
var first = null
|
||||
var last = null
|
||||
|
||||
// Build chain of n equality constraints.
|
||||
for (i in 0..n) {
|
||||
var v = new Variable("v", 0)
|
||||
if (prev != null) new EqualityConstraint(prev, v, REQUIRED)
|
||||
if (i == 0) first = v
|
||||
if (i == n) last = v
|
||||
prev = v
|
||||
}
|
||||
|
||||
new StayConstraint(last, STRONG_DEFAULT)
|
||||
var edit = new EditConstraint(first, PREFERRED)
|
||||
var plan = planner.extractPlanFromConstraints([edit])
|
||||
for (i in 0...100) {
|
||||
first.value = i
|
||||
plan.execute
|
||||
total = total + last.value
|
||||
}
|
||||
}
|
||||
|
||||
var change = new Fn {|v, newValue|
|
||||
var edit = new EditConstraint(v, PREFERRED)
|
||||
var plan = planner.extractPlanFromConstraints([edit])
|
||||
for (i in 0...10) {
|
||||
v.value = newValue
|
||||
plan.execute
|
||||
}
|
||||
|
||||
edit.destroyConstraint
|
||||
}
|
||||
|
||||
// This test constructs a two sets of variables related to each
|
||||
// other by a simple linear transformation (scale and offset). The
|
||||
// time is measured to change a variable on either side of the
|
||||
// mapping and to change the scale and offset factors.
|
||||
var projectionTest = new Fn {|n|
|
||||
planner = new Planner
|
||||
var scale = new Variable("scale", 10)
|
||||
var offset = new Variable("offset", 1000)
|
||||
var src = null
|
||||
var dst = null
|
||||
|
||||
var dests = []
|
||||
for (i in 0...n) {
|
||||
src = new Variable("src", i)
|
||||
dst = new Variable("dst", i)
|
||||
dests.add(dst)
|
||||
new StayConstraint(src, NORMAL)
|
||||
new ScaleConstraint(src, scale, offset, dst, REQUIRED)
|
||||
}
|
||||
|
||||
change.call(src, 17)
|
||||
total = total + dst.value
|
||||
if (dst.value != 1170) IO.print("Projection 1 failed")
|
||||
|
||||
change.call(dst, 1050)
|
||||
|
||||
total = total + src.value
|
||||
if (src.value != 5) IO.print("Projection 2 failed")
|
||||
|
||||
change.call(scale, 5)
|
||||
for (i in 0...n - 1) {
|
||||
total = total + dests[i].value
|
||||
if (dests[i].value != i * 5 + 1000) IO.print("Projection 3 failed")
|
||||
}
|
||||
|
||||
change.call(offset, 2000)
|
||||
for (i in 0...n - 1) {
|
||||
total = total + dests[i].value
|
||||
if (dests[i].value != i * 5 + 2000) IO.print("Projection 4 failed")
|
||||
}
|
||||
}
|
||||
|
||||
var start = IO.clock
|
||||
for (i in 0...20) {
|
||||
chainTest.call(100)
|
||||
projectionTest.call(100)
|
||||
}
|
||||
|
||||
IO.print(total)
|
||||
IO.print("elapsed: ", IO.clock - start)
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
-- The Computer Language Benchmarks Game
|
||||
-- http://benchmarksgame.alioth.debian.org/
|
||||
-- contributed by Mike Pall
|
||||
|
||||
local function fannkuch(n)
|
||||
local p, q, s, sign, maxflips, sum = {}, {}, {}, 1, 0, 0
|
||||
for i=1,n do p[i] = i; q[i] = i; s[i] = i end
|
||||
repeat
|
||||
-- Copy and flip.
|
||||
local q1 = p[1] -- Cache 1st element.
|
||||
if q1 ~= 1 then
|
||||
for i=2,n do q[i] = p[i] end -- Work on a copy.
|
||||
local flips = 1
|
||||
repeat
|
||||
local qq = q[q1]
|
||||
if qq == 1 then -- ... until 1st element is 1.
|
||||
sum = sum + sign*flips
|
||||
if flips > maxflips then maxflips = flips end -- New maximum?
|
||||
break
|
||||
end
|
||||
q[q1] = q1
|
||||
if q1 >= 4 then
|
||||
local i, j = 2, q1 - 1
|
||||
repeat q[i], q[j] = q[j], q[i]; i = i + 1; j = j - 1; until i >= j
|
||||
end
|
||||
q1 = qq; flips = flips + 1
|
||||
until false
|
||||
end
|
||||
-- Permute.
|
||||
if sign == 1 then
|
||||
p[2], p[1] = p[1], p[2]; sign = -1 -- Rotate 1<-2.
|
||||
else
|
||||
p[2], p[3] = p[3], p[2]; sign = 1 -- Rotate 1<-2 and 1<-2<-3.
|
||||
for i=3,n do
|
||||
local sx = s[i]
|
||||
if sx ~= 1 then s[i] = sx-1; break end
|
||||
if i == n then return sum, maxflips end -- Out of permutations.
|
||||
s[i] = i
|
||||
-- Rotate 1<-...<-i+1.
|
||||
local t = p[1]; for j=1,i do p[j] = p[j+1] end; p[i+1] = t
|
||||
end
|
||||
end
|
||||
until false
|
||||
end
|
||||
|
||||
local n = 9
|
||||
local sum, flips = fannkuch(n)
|
||||
io.write(sum, "\nPfannkuchen(", n, ") = ", flips, "\n")
|
||||
@ -1,56 +0,0 @@
|
||||
# The Computer Language Benchmarks Game
|
||||
# http://benchmarksgame.alioth.debian.org/
|
||||
|
||||
# contributed by Isaac Gouy
|
||||
# converted to Java by Oleg Mazurov
|
||||
# converted to Python by Buck Golemon
|
||||
# modified by Justin Peel
|
||||
|
||||
def fannkuch(n):
|
||||
maxFlipsCount = 0
|
||||
permSign = True
|
||||
checksum = 0
|
||||
|
||||
perm1 = list(range(n))
|
||||
count = perm1[:]
|
||||
rxrange = range(2, n - 1)
|
||||
nm = n - 1
|
||||
while 1:
|
||||
k = perm1[0]
|
||||
if k:
|
||||
perm = perm1[:]
|
||||
flipsCount = 1
|
||||
kk = perm[k]
|
||||
while kk:
|
||||
perm[:k+1] = perm[k::-1]
|
||||
flipsCount += 1
|
||||
k = kk
|
||||
kk = perm[kk]
|
||||
if maxFlipsCount < flipsCount:
|
||||
maxFlipsCount = flipsCount
|
||||
checksum += flipsCount if permSign else -flipsCount
|
||||
|
||||
# Use incremental change to generate another permutation
|
||||
if permSign:
|
||||
perm1[0],perm1[1] = perm1[1],perm1[0]
|
||||
permSign = False
|
||||
else:
|
||||
perm1[1],perm1[2] = perm1[2],perm1[1]
|
||||
permSign = True
|
||||
for r in rxrange:
|
||||
if count[r]:
|
||||
break
|
||||
count[r] = r
|
||||
perm0 = perm1[0]
|
||||
perm1[:r+1] = perm1[1:r+2]
|
||||
perm1[r+1] = perm0
|
||||
else:
|
||||
r = nm
|
||||
if not count[r]:
|
||||
print( checksum )
|
||||
return maxFlipsCount
|
||||
count[r] -= 1
|
||||
|
||||
n = 9
|
||||
|
||||
print(( "Pfannkuchen(%i) = %i" % (n, fannkuch(n)) ))
|
||||
@ -1,60 +0,0 @@
|
||||
def fannkuch(n)
|
||||
p = (0..n).to_a
|
||||
s = p.dup
|
||||
q = p.dup
|
||||
sign = 1
|
||||
sum = maxflips = 0
|
||||
while(true)
|
||||
# flip.
|
||||
|
||||
if (q1 = p[1]) != 1
|
||||
q[0..-1] = p
|
||||
flips = 1
|
||||
until (qq = q[q1]) == 1
|
||||
q[q1] = q1
|
||||
if q1 >= 4
|
||||
i, j = 2, q1 - 1
|
||||
while i < j
|
||||
q[i], q[j] = q[j], q[i]
|
||||
i += 1
|
||||
j -= 1
|
||||
end
|
||||
end
|
||||
q1 = qq
|
||||
flips += 1
|
||||
end
|
||||
sum += sign * flips
|
||||
maxflips = flips if flips > maxflips # New maximum?
|
||||
|
||||
end
|
||||
# Permute.
|
||||
|
||||
if sign == 1
|
||||
# Rotate 1<-2.
|
||||
|
||||
p[1], p[2] = p[2], p[1]
|
||||
sign = -1
|
||||
else
|
||||
# Rotate 1<-2 and 1<-2<-3.
|
||||
|
||||
p[2], p[3] = p[3], p[2]
|
||||
sign = 1
|
||||
i = 3
|
||||
while i <= n && s[i] == 1
|
||||
return [sum, maxflips] if i == n # Out of permutations.
|
||||
|
||||
s[i] = i
|
||||
# Rotate 1<-...<-i+1.
|
||||
|
||||
t = p.delete_at(1)
|
||||
i += 1
|
||||
p.insert(i, t)
|
||||
end
|
||||
s[i] -= 1 if i <= n
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
n = 9
|
||||
sum, flips = fannkuch(n)
|
||||
printf "%d\nPfannkuchen(%d) = %d\n", sum, n, flips
|
||||
@ -1,10 +0,0 @@
|
||||
function fib(n)
|
||||
if n < 2 then return n end
|
||||
return fib(n - 2) + fib(n - 1)
|
||||
end
|
||||
|
||||
local start = os.clock()
|
||||
for i = 1, 5 do
|
||||
io.write(fib(28) .. "\n")
|
||||
end
|
||||
io.write(string.format("elapsed: %.8f\n", os.clock() - start))
|
||||
@ -1,12 +0,0 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import time
|
||||
|
||||
def fib(n):
|
||||
if n < 2: return n
|
||||
return fib(n - 1) + fib(n - 2)
|
||||
|
||||
start = time.clock()
|
||||
for i in range(0, 5):
|
||||
print(fib(28))
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
@ -1,13 +0,0 @@
|
||||
def fib(n)
|
||||
if n < 2 then
|
||||
n
|
||||
else
|
||||
fib(n - 1) + fib(n - 2)
|
||||
end
|
||||
end
|
||||
|
||||
start = Time.now
|
||||
for i in 0...5
|
||||
puts fib(28)
|
||||
end
|
||||
puts "elapsed: " + (Time.now - start).to_s
|
||||
@ -1,10 +0,0 @@
|
||||
var fib = new Fn {|n|
|
||||
if (n < 2) return n
|
||||
return fib.call(n - 1) + fib.call(n - 2)
|
||||
}
|
||||
|
||||
var start = IO.clock
|
||||
for (i in 1..5) {
|
||||
IO.print(fib.call(28))
|
||||
}
|
||||
IO.print("elapsed: ", IO.clock - start)
|
||||
@ -1,12 +0,0 @@
|
||||
local start = os.clock()
|
||||
local list = {}
|
||||
for i = 0, 999999 do
|
||||
list[i] = i
|
||||
end
|
||||
|
||||
local sum = 0
|
||||
for k, i in pairs(list) do
|
||||
sum = sum + i
|
||||
end
|
||||
io.write(sum .. "\n")
|
||||
io.write(string.format("elapsed: %.8f\n", os.clock() - start))
|
||||
@ -1,20 +0,0 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import time
|
||||
|
||||
# Map "range" to an efficient range in both Python 2 and 3.
|
||||
try:
|
||||
range = xrange
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
start = time.clock()
|
||||
list = []
|
||||
for i in range(0, 1000000):
|
||||
list.append(i)
|
||||
|
||||
sum = 0
|
||||
for i in list:
|
||||
sum += i
|
||||
print(sum)
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
@ -1,8 +0,0 @@
|
||||
start = Time.now
|
||||
list = []
|
||||
1000000.times {|i| list << i}
|
||||
|
||||
sum = 0
|
||||
list.each {|i| sum += i}
|
||||
puts sum
|
||||
puts "elapsed: " + (Time.now - start).to_s
|
||||
@ -1,10 +0,0 @@
|
||||
var list = []
|
||||
|
||||
var start = IO.clock
|
||||
for (i in 0...1000000) list.add(i)
|
||||
|
||||
var sum = 0
|
||||
for (i in list) sum = sum + i
|
||||
|
||||
IO.print(sum)
|
||||
IO.print("elapsed: ", IO.clock - start)
|
||||
@ -1,94 +0,0 @@
|
||||
-- $Id: methcall.lua,v 1.2 2004-06-12 16:19:43 bfulgham Exp $
|
||||
-- http://shootout.alioth.debian.org
|
||||
-- contributed by Roberto Ierusalimschy
|
||||
|
||||
--------------------------------------------------------------
|
||||
-- Toggle class
|
||||
--------------------------------------------------------------
|
||||
|
||||
Toggle = {}
|
||||
|
||||
function Toggle:value ()
|
||||
return self.state
|
||||
end
|
||||
|
||||
function Toggle:activate ()
|
||||
self.state = not self.state
|
||||
return self
|
||||
end
|
||||
|
||||
function Toggle:new (start_state)
|
||||
local o = {state = start_state}
|
||||
self.__index =self
|
||||
setmetatable(o, self)
|
||||
return o
|
||||
end
|
||||
|
||||
|
||||
--------------------------------------------------------------
|
||||
-- NthToggle class
|
||||
--------------------------------------------------------------
|
||||
|
||||
NthToggle = Toggle:new()
|
||||
|
||||
function NthToggle:activate ()
|
||||
self.counter = self.counter + 1
|
||||
if self.counter >= self.count_max then
|
||||
Toggle.activate(self)
|
||||
self.counter = 0
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function NthToggle:new (start_state, max_counter)
|
||||
local o = Toggle.new(self, start_state)
|
||||
o.count_max = max_counter
|
||||
o.counter = 0
|
||||
return o
|
||||
end
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
-- main
|
||||
-----------------------------------------------------------
|
||||
|
||||
function main ()
|
||||
local start = os.clock()
|
||||
local N = 100000
|
||||
|
||||
local val = 1
|
||||
local toggle = Toggle:new(val)
|
||||
for i=1,N do
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
end
|
||||
print(val and "true" or "false")
|
||||
|
||||
val = 1
|
||||
local ntoggle = NthToggle:new(val, 3)
|
||||
for i=1,N do
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
end
|
||||
print(val and "true" or "false")
|
||||
io.write(string.format("elapsed: %.8f\n", os.clock() - start))
|
||||
end
|
||||
|
||||
main()
|
||||
|
||||
@ -1,80 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# http://www.bagley.org/~doug/shootout/
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
# Map "range" to an efficient range in both Python 2 and 3.
|
||||
try:
|
||||
range = xrange
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
class Toggle(object):
|
||||
def __init__(self, start_state):
|
||||
self.bool = start_state
|
||||
def value(self):
|
||||
return(self.bool)
|
||||
def activate(self):
|
||||
self.bool = not self.bool
|
||||
return(self)
|
||||
|
||||
class NthToggle(Toggle):
|
||||
def __init__(self, start_state, max_counter):
|
||||
Toggle.__init__(self, start_state)
|
||||
self.count_max = max_counter
|
||||
self.counter = 0
|
||||
def activate(self):
|
||||
self.counter += 1
|
||||
if (self.counter >= self.count_max):
|
||||
super(NthToggle, self).activate()
|
||||
self.counter = 0
|
||||
return(self)
|
||||
|
||||
|
||||
def main():
|
||||
start = time.clock()
|
||||
|
||||
NUM = 100000
|
||||
|
||||
val = 1
|
||||
toggle = Toggle(val)
|
||||
for i in range(0,NUM):
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
if val:
|
||||
print("true")
|
||||
else:
|
||||
print("false")
|
||||
|
||||
val = 1
|
||||
ntoggle = NthToggle(val, 3)
|
||||
for i in range(0,NUM):
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
if val:
|
||||
print("true")
|
||||
else:
|
||||
print("false")
|
||||
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
|
||||
|
||||
main()
|
||||
@ -1,79 +0,0 @@
|
||||
#!/usr/bin/ruby
|
||||
# -*- mode: ruby -*-
|
||||
# $Id: methcall.ruby,v 1.1 2004-05-19 18:10:41 bfulgham Exp $
|
||||
# http://www.bagley.org/~doug/shootout/
|
||||
# with help from Aristarkh Zagorodnikov
|
||||
|
||||
class Toggle
|
||||
def initialize(start_state)
|
||||
@bool = start_state
|
||||
end
|
||||
|
||||
def value
|
||||
@bool
|
||||
end
|
||||
|
||||
def activate
|
||||
@bool = !@bool
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
class NthToggle < Toggle
|
||||
def initialize(start_state, max_counter)
|
||||
super start_state
|
||||
@count_max = max_counter
|
||||
@counter = 0
|
||||
end
|
||||
|
||||
def activate
|
||||
@counter += 1
|
||||
if @counter >= @count_max
|
||||
super
|
||||
@counter = 0
|
||||
end
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
def main()
|
||||
start = Time.now
|
||||
|
||||
n = 100000
|
||||
|
||||
val = 1
|
||||
toggle = Toggle.new(val)
|
||||
n.times do
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
end
|
||||
if val then puts "true" else puts "false" end
|
||||
|
||||
val = 1
|
||||
ntoggle = NthToggle.new(val, 3)
|
||||
n.times do
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
end
|
||||
if val then puts "true" else puts "false" end
|
||||
|
||||
puts "elapsed: " + (Time.now - start).to_s
|
||||
end
|
||||
|
||||
main()
|
||||
@ -1,68 +0,0 @@
|
||||
class Toggle {
|
||||
new(startState) {
|
||||
_state = startState
|
||||
}
|
||||
|
||||
value { _state }
|
||||
activate {
|
||||
_state = !_state
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
class NthToggle is Toggle {
|
||||
new(startState, maxCounter) {
|
||||
super(startState)
|
||||
_countMax = maxCounter
|
||||
_count = 0
|
||||
}
|
||||
|
||||
activate {
|
||||
_count = _count + 1
|
||||
if (_count >= _countMax) {
|
||||
super.activate
|
||||
_count = 0
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
var start = IO.clock
|
||||
var n = 100000
|
||||
var val = true
|
||||
var toggle = new Toggle(val)
|
||||
|
||||
for (i in 0...n) {
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
}
|
||||
|
||||
IO.print(toggle.value)
|
||||
|
||||
val = true
|
||||
var ntoggle = new NthToggle(val, 3)
|
||||
|
||||
for (i in 0...n) {
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
}
|
||||
|
||||
IO.print(ntoggle.value)
|
||||
IO.print("elapsed: ", IO.clock - start)
|
||||
@ -1,286 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import argparse
|
||||
import math
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# Runs the tests.
|
||||
WREN_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
BENCHMARK_DIR = os.path.join(WREN_DIR, 'benchmark')
|
||||
|
||||
# How many times to run a given benchmark.
|
||||
NUM_TRIALS = 10
|
||||
|
||||
BENCHMARKS = []
|
||||
|
||||
def BENCHMARK(name, pattern):
|
||||
regex = re.compile(pattern + "\n" + r"elapsed: (\d+\.\d+)", re.MULTILINE)
|
||||
BENCHMARKS.append([name, regex, None])
|
||||
|
||||
BENCHMARK("binary_trees", """stretch tree of depth 13 check: -1
|
||||
8192 trees of depth 4 check: -8192
|
||||
2048 trees of depth 6 check: -2048
|
||||
512 trees of depth 8 check: -512
|
||||
128 trees of depth 10 check: -128
|
||||
32 trees of depth 12 check: -32
|
||||
long lived tree of depth 12 check: -1""")
|
||||
|
||||
BENCHMARK("delta_blue", "7032700")
|
||||
|
||||
BENCHMARK("fib", r"""317811
|
||||
317811
|
||||
317811
|
||||
317811
|
||||
317811""")
|
||||
|
||||
BENCHMARK("for", r"""499999500000""")
|
||||
|
||||
BENCHMARK("method_call", r"""true
|
||||
false""")
|
||||
|
||||
LANGUAGES = [
|
||||
("wren", ["../wren"], ".wren"),
|
||||
("lua", ["lua"], ".lua"),
|
||||
("luajit (-joff)", ["luajit", "-joff"], ".lua"),
|
||||
("python", ["python"], ".py"),
|
||||
("python3", ["python3"], ".py"),
|
||||
("ruby", ["ruby"], ".rb")
|
||||
]
|
||||
|
||||
results = {}
|
||||
|
||||
def green(text):
|
||||
if sys.platform == 'win32':
|
||||
return text
|
||||
return '\033[32m' + text + '\033[0m'
|
||||
|
||||
def red(text):
|
||||
if sys.platform == 'win32':
|
||||
return text
|
||||
return '\033[31m' + text + '\033[0m'
|
||||
|
||||
def yellow(text):
|
||||
if sys.platform == 'win32':
|
||||
return text
|
||||
return '\033[33m' + text + '\033[0m'
|
||||
|
||||
|
||||
def get_score(time):
|
||||
"""
|
||||
Converts time into a "score". This is the inverse of the time with an
|
||||
arbitrary scale applied to get the number in a nice range. The goal here is
|
||||
to have benchmark results where faster = bigger number.
|
||||
"""
|
||||
return 1000.0 / time
|
||||
|
||||
|
||||
def run_trial(benchmark, language):
|
||||
"""Runs one benchmark one time for one language."""
|
||||
args = []
|
||||
args.extend(language[1])
|
||||
args.append(os.path.join(BENCHMARK_DIR, benchmark[0] + language[2]))
|
||||
out = subprocess.check_output(args, universal_newlines=True)
|
||||
match = benchmark[1].match(out)
|
||||
if match:
|
||||
return float(match.group(1))
|
||||
else:
|
||||
print "Incorrect output:"
|
||||
print out
|
||||
return None
|
||||
|
||||
|
||||
def run_benchmark_language(benchmark, language, benchmark_result):
|
||||
"""
|
||||
Runs one benchmark for a number of trials for one language.
|
||||
|
||||
Adds the result to benchmark_result, which is a map of language names to
|
||||
results.
|
||||
"""
|
||||
|
||||
name = "{0} - {1}".format(benchmark[0], language[0])
|
||||
print "{0:30s}".format(name),
|
||||
|
||||
if not os.path.exists(os.path.join(
|
||||
BENCHMARK_DIR, benchmark[0] + language[2])):
|
||||
print "No implementation for this language"
|
||||
return
|
||||
|
||||
times = []
|
||||
for i in range(0, NUM_TRIALS):
|
||||
time = run_trial(benchmark, language)
|
||||
if not time:
|
||||
return
|
||||
times.append(time)
|
||||
sys.stdout.write(".")
|
||||
|
||||
best = min(times)
|
||||
score = get_score(best)
|
||||
|
||||
comparison = ""
|
||||
if language[0] == "wren":
|
||||
if benchmark[2] != None:
|
||||
ratio = 100 * score / benchmark[2]
|
||||
comparison = "{:6.2f}% relative to baseline".format(ratio)
|
||||
if ratio > 105:
|
||||
comparison = green(comparison)
|
||||
if ratio < 95:
|
||||
comparison = red(comparison)
|
||||
else:
|
||||
comparison = "no baseline"
|
||||
else:
|
||||
# Hack: assumes wren gets run first.
|
||||
wren_score = benchmark_result["wren"]["score"]
|
||||
ratio = 100.0 * wren_score / score
|
||||
comparison = "{:6.2f}%".format(ratio)
|
||||
if ratio > 105:
|
||||
comparison = green(comparison)
|
||||
if ratio < 95:
|
||||
comparison = red(comparison)
|
||||
|
||||
print " {:5.0f} {:4.2f}s {:s}".format(score, best, comparison)
|
||||
|
||||
benchmark_result[language[0]] = {
|
||||
"desc": name,
|
||||
"times": times,
|
||||
"score": score
|
||||
}
|
||||
|
||||
return score
|
||||
|
||||
|
||||
def run_benchmark(benchmark, languages):
|
||||
"""Runs one benchmark for the given languages (or all of them)."""
|
||||
|
||||
benchmark_result = {}
|
||||
results[benchmark[0]] = benchmark_result
|
||||
|
||||
num_languages = 0
|
||||
for language in LANGUAGES:
|
||||
if not languages or language[0] in languages:
|
||||
num_languages += 1
|
||||
run_benchmark_language(benchmark, language, benchmark_result)
|
||||
|
||||
if num_languages > 1:
|
||||
graph_results(benchmark_result)
|
||||
|
||||
|
||||
def graph_results(benchmark_result):
|
||||
print
|
||||
|
||||
INCREMENT = {
|
||||
'-': 'o',
|
||||
'o': 'O',
|
||||
'O': '0',
|
||||
'0': '0'
|
||||
}
|
||||
|
||||
# Scale everything by the highest score.
|
||||
highest = 0
|
||||
for language, result in benchmark_result.items():
|
||||
score = get_score(min(result["times"]))
|
||||
if score > highest: highest = score
|
||||
|
||||
print "{0:30s}0 {1:66.0f}".format("", highest)
|
||||
for language, result in benchmark_result.items():
|
||||
line = ["-"] * 68
|
||||
for time in result["times"]:
|
||||
index = int(get_score(time) / highest * 67)
|
||||
line[index] = INCREMENT[line[index]]
|
||||
print "{0:30s}{1}".format(result["desc"], "".join(line))
|
||||
print
|
||||
|
||||
|
||||
def read_baseline():
|
||||
if os.path.exists("baseline.txt"):
|
||||
with open("baseline.txt") as f:
|
||||
for line in f.readlines():
|
||||
name, best = line.split(",")
|
||||
for benchmark in BENCHMARKS:
|
||||
if benchmark[0] == name:
|
||||
benchmark[2] = float(best)
|
||||
|
||||
|
||||
def generate_baseline():
|
||||
print "generating baseline"
|
||||
baseline_text = ""
|
||||
for benchmark in BENCHMARKS:
|
||||
best = run_benchmark_language(benchmark, LANGUAGES[0], {})
|
||||
baseline_text += ("{},{}\n".format(benchmark[0], best))
|
||||
|
||||
# Write them to a file.
|
||||
with open("baseline.txt", 'w') as out:
|
||||
out.write(baseline_text)
|
||||
|
||||
|
||||
def print_html():
|
||||
'''Print the results as an HTML chart.'''
|
||||
|
||||
def print_benchmark(benchmark, name):
|
||||
print '<h3>{}</h3>'.format(name)
|
||||
print '<table class="chart">'
|
||||
|
||||
# Scale everything by the highest score.
|
||||
highest = 0
|
||||
for language, result in results[benchmark].items():
|
||||
score = get_score(min(result["times"]))
|
||||
if score > highest: highest = score
|
||||
|
||||
languages = sorted(results[benchmark].keys(),
|
||||
key=lambda lang: results[benchmark][lang]["score"], reverse=True)
|
||||
|
||||
for language in languages:
|
||||
result = results[benchmark][language]
|
||||
score = int(result["score"])
|
||||
ratio = int(100 * score / highest)
|
||||
css_class = "chart-bar"
|
||||
if language == "wren":
|
||||
css_class += " wren"
|
||||
print ' <tr>'
|
||||
print ' <th>{}</th><td><div class="{}" style="width: {}%;">{} </div></td>'.format(
|
||||
language, css_class, ratio, score)
|
||||
print ' </tr>'
|
||||
print '</table>'
|
||||
|
||||
print_benchmark("method_call", "Method Call")
|
||||
print_benchmark("delta_blue", "DeltaBlue")
|
||||
print_benchmark("binary_trees", "Binary Trees")
|
||||
print_benchmark("fib", "Recursive Fibonacci")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Run the benchmarks")
|
||||
parser.add_argument("benchmark", nargs='?',
|
||||
default="all",
|
||||
help="The benchmark to run")
|
||||
parser.add_argument("--generate-baseline",
|
||||
action="store_true",
|
||||
help="Generate a baseline file")
|
||||
parser.add_argument("-l", "--language",
|
||||
action="append",
|
||||
help="Which language(s) to run benchmarks for")
|
||||
parser.add_argument("--output-html",
|
||||
action="store_true",
|
||||
help="Output the results chart as HTML")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.generate_baseline:
|
||||
generate_baseline()
|
||||
return
|
||||
|
||||
read_baseline()
|
||||
|
||||
# Run the benchmarks.
|
||||
for benchmark in BENCHMARKS:
|
||||
if benchmark[0] == args.benchmark or args.benchmark == "all":
|
||||
run_benchmark(benchmark, args.language)
|
||||
|
||||
if args.output_html:
|
||||
print_html()
|
||||
|
||||
|
||||
main()
|
||||
105
blog/0-hello-wren.html
Normal file
105
blog/0-hello-wren.html
Normal file
@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Hello Wren – Wren</title>
|
||||
<script type="application/javascript" src="../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../"><img src="../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../getting-started.html">Getting Started</a></li>
|
||||
<li><a href="../contributing.html">Contributing</a></li>
|
||||
<li><a href="../blog">Blog</a></li>
|
||||
<li><a href="../try">Try it!</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="../getting-started.html">Getting Started</a></li>
|
||||
<li><a href="../contributing.html">Contributing</a></li>
|
||||
<li><a href="../blog">Blog</a></li>
|
||||
<li><a href="../try">Try it!</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>Hello Wren</h2>
|
||||
<p>4 Feb 2019</p>
|
||||
<hr />
|
||||
<p>Welcome to the new Wren development blog!</p>
|
||||
<p>Around November 2018 on the Wren mailing list, munificent announced that a new maintainer is taking over the development and maintainence of the Wren language. <a href="https://groups.google.com/forum/#!topic/wren-lang/cMUwij-NIn0">The original post is here, with all the details.</a></p>
|
||||
<p>In short, <a href="https://github.com/ruby0x1">I’m (ruby0x1)</a> taking over from <a href="https://github.com/munificent">Bob (munificent)</a> as maintainer, but Bob is sticking around as a contributor!</p>
|
||||
<h3>The Wren blog <a href="#the-wren-blog" name="the-wren-blog" class="header-anchor">#</a></h3>
|
||||
<p>One of the first things I felt Wren needed going forward is a consistent and centralized place to talk about the language. The existing design choices and goals, and especially the future and evolution of Wren are something a lot of people want to read about, in detail. Now we have a place to do exactly that!</p>
|
||||
<p>The blog will be keeping in the spirit of Wren by remaining simple. Posts are just regular markdown files in the repository alongside the rest of the site, and are considered part of the documentation.</p>
|
||||
<p>Since Wren as a project aims to help others learn, having the in depth thought processes, development details and technical intricacy be documented in the same place, over a timeline, is valuable.</p>
|
||||
<h3>What’s next for Wren? <a href="#what's-next-for-wren" name="what's-next-for-wren" class="header-anchor">#</a></h3>
|
||||
<p>First and foremost, I wanted to state explicitly that <strong>Wren is going to be changing</strong> but it is <strong>not going to become something else</strong>. </p>
|
||||
<p>Wren attracted me as a language because of what it <em>is</em>, not because it isn’t {<em>other language</em>}. If I wanted to use {<em>other language</em>} I would have, but I chose Wren because I wanted what it was. </p>
|
||||
<p>So, Wren is going to be changing in ways that align with it’s existing design intentions. Staying small, simple, learnable and hackable is all vital to what makes Wren valuable, and will remain. </p>
|
||||
<p>We’re just as excited as you are to get to longer term changes and fun tweaks (we have lots of work done already in local projects like <a href="https://i.imgur.com/dazexnY.gifv">the debugger</a>). There’s plenty of ideas we’ve tried since we’ve been <a href="https://luxeengine.com">using Wren full time</a> the last 2.5+ years, and can’t wait to get started with bring those into the main branch (and optional modules). There’s a lot to do!</p>
|
||||
<p>In the next blog I want to try talk a bit more about the short to medium term goals and roadmap (rather than mixing it here with the meta/hello world post). Be sure to keep an eye out for that one, as it has more juicy details on what we’re gonna get up to.</p>
|
||||
<p>There are immediate term goals, though.</p>
|
||||
<h3>First steps <a href="#first-steps" name="first-steps" class="header-anchor">#</a></h3>
|
||||
<p>I think it’s important to reset the baseline before we shake things up too much. Think of it as a ramp up to gain momentum, rather than running into a china store with arms flailing. </p>
|
||||
<ul>
|
||||
<li>We’re gonna clear out a bit of backlog, tidying up issues and PRs</li>
|
||||
<li>Tidy up the website a bit, visually and structurally</li>
|
||||
<li>Make sure all documentation is up to date with the current development</li>
|
||||
<li>Tag 0.2.0 with a list of relevant changes since 0.1.0</li>
|
||||
</ul>
|
||||
<p>Once we tag 0.2.0, we’ll be in a good place to move forward. And, everything up until now will have a well defined checkpoint preserved, if people want to refer to it.</p>
|
||||
<h3>Steps so far <a href="#steps-so-far" name="steps-so-far" class="header-anchor">#</a></h3>
|
||||
<p>Since the announcement and transition, I’ve been making my way through all the mailing list posts, issues and PRs in the backlog and reading all the way back to the early days. </p>
|
||||
<p>I’ve also been talking to community members one on one and getting personal experiences and thoughts on Wren. Forming a full picture will help us since we’ll have an overview of what’s most relevant (and what isn’t) as time has passed, and gives us actionable things to do for the next milestone. I think it’s an important step. </p>
|
||||
<p>We’ve also been investigating some of the PRs with the community to get those sorted out, since they’re in the way.</p>
|
||||
<p>Lastly, I’ve already done a bit of clean up on the website and documentation theme, added a new logo, and of course added the blog.</p>
|
||||
<h3>Thanks! <a href="#thanks" name="thanks" class="header-anchor">#</a></h3>
|
||||
<p>Lastly, I wanted to say thanks to munificent, the community and all the contributors that have made Wren possible to this point. It’s a wonderful thing and I look forward to seeing where we take it, together.</p>
|
||||
<p>I hope you’ll join us on the journey!</p>
|
||||
<h3>More <a href="#more" name="more" class="header-anchor">#</a></h3>
|
||||
<ul>
|
||||
<li><a href="http://wren.io/blog/rss.xml">The Wren Blog RSS</a></li>
|
||||
<li>Join the <a href="https://discord.gg/Kx6PxSX">discord community</a></li>
|
||||
<li>Visit the <a href="https://github.com/wren-lang">wren-lang organization</a> on GitHub.</li>
|
||||
<li>Follow <a href="https://twitter.com/munificentbob">@munificentbob</a> or <a href="https://twitter.com/ruby0x1">@ruby0x1</a> on twitter</li>
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
123
blog/1-0.2.0-and-beyond.html
Normal file
123
blog/1-0.2.0-and-beyond.html
Normal file
@ -0,0 +1,123 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>0.2.0 and beyond – Wren</title>
|
||||
<script type="application/javascript" src="../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../"><img src="../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../getting-started.html">Getting Started</a></li>
|
||||
<li><a href="../contributing.html">Contributing</a></li>
|
||||
<li><a href="../blog">Blog</a></li>
|
||||
<li><a href="../try">Try it!</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="../getting-started.html">Getting Started</a></li>
|
||||
<li><a href="../contributing.html">Contributing</a></li>
|
||||
<li><a href="../blog">Blog</a></li>
|
||||
<li><a href="../try">Try it!</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>0.2.0 and beyond</h2>
|
||||
<p>30 Sep 2019</p>
|
||||
<hr />
|
||||
<h3>0.2.0 is here <a href="#0.2.0-is-here" name="0.2.0-is-here" class="header-anchor">#</a></h3>
|
||||
<p>It’s time to tag a release!
|
||||
Let’s check our goals from <a href="0-hello-wren.html">the last blog post</a>:</p>
|
||||
<ul>
|
||||
<li><s>We’re gonna clear out a bit of backlog, tidying up issues and PRs</s></li>
|
||||
<li><s>Tidy up the website a bit, visually and structurally</s></li>
|
||||
<li><s>Make sure all documentation is up to date with the current development</s></li>
|
||||
<li><s>Tag 0.2.0 with a list of relevant changes since 0.1.0</s></li>
|
||||
</ul>
|
||||
<p>So far so good.</p>
|
||||
<h3>Backlog <a href="#backlog" name="backlog" class="header-anchor">#</a></h3>
|
||||
<p>Clearing out the issues on a repo after some time has passed is always a bit tricky. </p>
|
||||
<p>Many issues are outdated (or unrelated), and some need a proper response. Some are related to future ideals, things that will take a while to get to. Some are related to the Wren CLI. It can be difficult to reason about the state of the tasks when they’re all over the place, so we’ve been consolidating.</p>
|
||||
<p>The good news is the issue list has been drastically reduced, 70+ issues being closed (or resolved). Around 21 of those are marked for future consideration, and 23 moved to the Wren CLI repository. More consolidation will still continue.</p>
|
||||
<p><strong>The goal is that the active issues are as relevant as possible in the immediate term.</strong></p>
|
||||
<p>A tricky but important aspect to mention here is the perception of closing an issue…
|
||||
A closed issue doesn’t necessarily mean anything final, it’s just a categorization tool!</p>
|
||||
<p>The other categorization tool which operates <em>within</em> open/closed categories, is the <em>label</em>. Labels allow us to distinguish clearly the different types of issues, which makes open/closed less binary and more nuanced and rich. We rely on both to make sense of the list.</p>
|
||||
<p>For example, discussions of future tasks, ideas or goals <a href="https://github.com/wren-lang/wren/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3Afuture+">are tagged <code>future</code></a>. Now we can refer to them later, and re-open them when they become active and relevant again.</p>
|
||||
<p><strong>We’re in this together.</strong> <br />
|
||||
Please don’t be discouraged if an issue is closed! Discussion is absolutely encouraged and ideas, proposals and input is very necessary. Feel free to keep a discussion going, even if the issue it’s attached to has been marked as closed.</p>
|
||||
<h3>0.2.0 <a href="#0.2.0" name="0.2.0" class="header-anchor">#</a></h3>
|
||||
<p>We’ve been hammering away on Wren since 0.1.0 for quite a while. The <a href="https://github.com/wren-lang/wren/compare/0.1.0...5338275dcdd97fd8d9fc614f420a645500836a59">change list on GitHub</a> is too long to display!</p>
|
||||
<p>Most importantly, before we start iterating on the language further, I wanted to make sure we had a checkpoint to look back to. That’s largely what 0.2.0 is about.</p>
|
||||
<p>There’s quite a lot of good changes, with <strong>290 commits from 41 contributors!</strong>
|
||||
Thanks to everyone getting involved, every little bit has helped Wren, no matter how small the contribution.</p>
|
||||
<h3>0.3.0 <a href="#0.3.0" name="0.3.0" class="header-anchor">#</a></h3>
|
||||
<p>With 0.2.0 wrapped up, our next release won’t be as far away this time.</p>
|
||||
<p><strong>The primary goal for 0.3.0 is separating the VM from the CLI.</strong></p>
|
||||
<p>This includes updated documentation, splitting the source repos, migrating all the tests, issues and more.
|
||||
All the code and documentation will still be easy to access in one place, but clarity around Wren as a project will improve a lot.</p>
|
||||
<p>The migration has already started, you can <a href="https://github.com/wren-lang/wren-cli">find the wren-cli repository here</a>.
|
||||
I’m working on some of the refactoring on the <a href="https://github.com/wren-lang/wren/tree/wren-cli-refactor">wren-cli-refactor branch.</a></p>
|
||||
<p>With that, we’ll also have a cleaner build process for the CLI.
|
||||
On some platforms (Windows especially), there have been several pain points, these will be addressed.
|
||||
There’s also gonna be an additional build target, namely emscripten, so we can easily run Wren examples on the Wren website and documentation.</p>
|
||||
<p>And finally, we’ll have some proper prebuilt releases with 0.3.0.
|
||||
I know many people have just wanted to grab an executable and give the language a go, but that hasn’t been an option.
|
||||
We’ll fix that with 0.3.0.</p>
|
||||
<p>The 0.3.0 goals in simple form:
|
||||
- VM / CLI split
|
||||
- Build consistency/reliablity
|
||||
- Web build for embedding in docs
|
||||
- Prebuilt releases</p>
|
||||
<h3>Beyond <a href="#beyond" name="beyond" class="header-anchor">#</a></h3>
|
||||
<p>I don’t have any concrete plans for 0.4.0 right now, but once the dust settles from 0.3.0 we’ll have a clearer view.</p>
|
||||
<p>There’s definitely things in the pipeline though, I’ve been playing with <a href="https://github.com/wren-lang/wren/pull/701">adding compound assignments like <code>+=</code></a>.</p>
|
||||
<p>More details about in development features and fixes can be found on the repo in the meantime.</p>
|
||||
<p>Thanks for reading!</p>
|
||||
<h3>More <a href="#more" name="more" class="header-anchor">#</a></h3>
|
||||
<ul>
|
||||
<li><a href="http://wren.io/blog/rss.xml">The Wren Blog RSS</a></li>
|
||||
<li>Join the <a href="https://discord.gg/Kx6PxSX">discord community</a></li>
|
||||
<li>Visit the <a href="https://github.com/wren-lang">wren-lang organization</a> on GitHub to get involved.</li>
|
||||
<li>Follow the developers <a href="https://twitter.com/munificentbob">@munificentbob</a> or <a href="https://twitter.com/ruby0x1">@ruby0x1</a> on twitter</li>
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
170
blog/2-0.3.0-released.html
Normal file
170
blog/2-0.3.0-released.html
Normal file
@ -0,0 +1,170 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>0.3.0 released! – Wren</title>
|
||||
<script type="application/javascript" src="../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../"><img src="../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../getting-started.html">Getting Started</a></li>
|
||||
<li><a href="../contributing.html">Contributing</a></li>
|
||||
<li><a href="../blog">Blog</a></li>
|
||||
<li><a href="../try">Try it!</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="../getting-started.html">Getting Started</a></li>
|
||||
<li><a href="../contributing.html">Contributing</a></li>
|
||||
<li><a href="../blog">Blog</a></li>
|
||||
<li><a href="../try">Try it!</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>0.3.0 released!</h2>
|
||||
<p>5 June 2020</p>
|
||||
<hr />
|
||||
<p>In this post we’ll cover 0.3.0 and the goals for 0.4.0 <a href="#goals-for-0.4.0">#</a>.</p>
|
||||
<h2>About the 0.3.0 release <a href="#about-the-0.3.0-release" name="about-the-0.3.0-release" class="header-anchor">#</a></h2>
|
||||
<p>Let’s revisit our goals from <a href="1-0.2.0-and-beyond.html">the last blog post</a>, <br />
|
||||
and mark what we managed to get done:</p>
|
||||
<ul>
|
||||
<li><s>VM / CLI split</s> <a href="#vm--cli-split">#</a></li>
|
||||
<li><s>Build consistency/reliablity</s> <a href="#build-consistencyreliability">#</a></li>
|
||||
<li><s>Web build for embedding in docs</s> <a href="#web-build-for-embedding-in-docs">#</a></li>
|
||||
<li><s>Prebuilt releases</s> <a href="#prebuilt-releases">#</a></li>
|
||||
</ul>
|
||||
<h2>The details <a href="#the-details" name="the-details" class="header-anchor">#</a></h2>
|
||||
<h3>VM / CLI split <a href="#vm--cli-split" name="vm--cli-split" class="header-anchor">#</a></h3>
|
||||
<p>With 0.3.0 we’ve separated the CLI from the Wren repo,
|
||||
and updated the docs to make the distinction clearer.</p>
|
||||
<p>The <a href="../cli">CLI now has its own corner of the docs</a>, so that the modules
|
||||
and API docs aren’t overlapped like before. This opens up space for the
|
||||
CLI to get better, fuller documentation, and removes confusion about
|
||||
built in modules vs ones that are in the CLI only.</p>
|
||||
<p>The code structure is clearer, too, and all the tests and utils are now specific.</p>
|
||||
<h3>Build consistency/reliability <a href="#build-consistencyreliability" name="build-consistencyreliability" class="header-anchor">#</a></h3>
|
||||
<p>Previously, builds on Windows could be a little fickle, and there was sometimes
|
||||
issues with the dependencies on the CLI side.</p>
|
||||
<p>To solve this, premake is now used to generate platform specific project files that
|
||||
‘just work’, making it a one step process to build the VM or CLI. Both projects
|
||||
now have a <code>projects/</code> folder which includes ready to go project files for primary platforms.</p>
|
||||
<p><small>The original <code>Makefile</code> and <code>util/wren.mk</code> no longer exist, so there might be some work needed
|
||||
to reintegrate if you relied on those. You can find the updated makefile in <code>projects/make/</code>, or <code>projects/make.mac/</code>.</small></p>
|
||||
<p>The <strong>amalgamated build</strong> was fixed too, so that embedding in your own project is as simple as
|
||||
including a single c file (and the <code>wren.h</code> header).</p>
|
||||
<p>On the <strong>CLI</strong> side, the pre-build steps were removed and dependencies vendored in repo,
|
||||
so that the project just builds with less potential points of error, especially across platforms.</p>
|
||||
<p>And finally the <strong>docs</strong>! Previously <a href="https://sass-lang.com/">SASS</a> was used, and code highlighting
|
||||
was done at generation time using pygments, a python code highlighter. Both of these dependencies
|
||||
have been removed, code highlighting is now done on the client side instead (see another reason why below).
|
||||
The benefit here that it is now <em>easy</em> to edit the docs, just a simple python command, no setup!</p>
|
||||
<h3>Web build for embedding in docs <a href="#web-build-for-embedding-in-docs" name="web-build-for-embedding-in-docs" class="header-anchor">#</a></h3>
|
||||
<p>The goal was two part here, one is to have a page to just try out Wren.
|
||||
Type in some code, run it. That’s the first big step and we’ve now got that on the docs page.</p>
|
||||
<h4><a href="../try" target="_blank" class="dark-link">Try Wren directly in your browser!</a></h4>
|
||||
<p>This should work on desktop or mobile, and will continue to be improved over time.</p>
|
||||
<p>The second part of that goal is having the VM available to make examples on each page interactive.
|
||||
This is implemented, <em>but not activated on any pages yet</em>.</p>
|
||||
<p>In the near future inline doc examples will have a small button that you can
|
||||
press to see the code result right there, live. Since there’s a lot of examples,
|
||||
and sometimes they’re fragments of code that don’t run in isolation,
|
||||
it will take time to propagate it through the pages.</p>
|
||||
<p>Mainly, I didn’t want this to hold up 0.3.0, but expect to start seeing it soon.</p>
|
||||
<h3>Prebuilt releases <a href="#prebuilt-releases" name="prebuilt-releases" class="header-anchor">#</a></h3>
|
||||
<p>In addition to the browser based build that removes a barrier to trying out Wren,
|
||||
Wren CLI has prebuilt binaries for Mac, Windows and Linux now! This gives
|
||||
an easy path to just tinkering with Wren before embedding it.</p>
|
||||
<hr />
|
||||
<h2>Goals for 0.4.0 <a href="#goals-for-0.4.0" name="goals-for-0.4.0" class="header-anchor">#</a></h2>
|
||||
<p>With 0.4.0 the goal is to address a couple of bigger todos, but also to push the language
|
||||
itself, and the embedding experience forward.</p>
|
||||
<p>You can see some of the <a href="https://github.com/wren-lang/wren/pulls?q=is%3Apr+is%3Aopen+label%3A0.4.0">work in progress tasks</a> here,
|
||||
but there’s a few things I’d like to resolve in 0.4.0.</p>
|
||||
<p><strong>Compound operators</strong> <br />
|
||||
I’ve really missed having <code>+=</code> and friends, <br />
|
||||
so I’ve been working on a (broken, wip) <a href="https://github.com/wren-lang/wren/pull/701">PR here</a>.
|
||||
I’ve since had a better idea to implement it and will hope to address that in 0.4.0.</p>
|
||||
<p><strong>Chained methods (‘fluent interfaces’)</strong> <br />
|
||||
Currently in Wren it’s required that the period (<code>.</code>) be on the same line as the method.</p>
|
||||
<pre class="snippet">
|
||||
example.
|
||||
some().
|
||||
functions().
|
||||
here()
|
||||
</pre>
|
||||
<p>This isn’t as elegant as we’d want for this form of API,
|
||||
so <strong>in 0.4.0 the goal is</strong> allowing a newline, as you’d expect:</p>
|
||||
<pre class="snippet">
|
||||
example
|
||||
.some()
|
||||
.functions()
|
||||
.here()
|
||||
</pre>
|
||||
<p>This doesn’t seem like a big deal but when your calls are wider,
|
||||
longer and possibly accept block functions. It’s hard to read,
|
||||
and can be less fun to track down a missing <code>.</code> in a big chunk of code.</p>
|
||||
<pre class="snippet">
|
||||
example.
|
||||
some {|args, and, stuff|
|
||||
...
|
||||
}.
|
||||
here()
|
||||
</pre>
|
||||
|
||||
<p><strong>C Side APIs</strong> <br />
|
||||
Some APIs for dealing with <code>Map</code> have been proposed several times,
|
||||
it’s time to bring that into the API. There’s some additions for <code>List</code> as well,
|
||||
like a helper to set an element in a list.</p>
|
||||
<p><strong>Other goals</strong> <br />
|
||||
There’s a few more things but I’m still exploring their viability. <br />
|
||||
Keep an eye on the <a href="https://github.com/wren-lang/wren">PRs/issues</a> or the <a href="https://github.com/wren-lang/wren/pulls?q=is%3Apr+is%3Aopen+label%3A0.4.0">0.4.0 label</a> to see when they’re discussed.</p>
|
||||
<h2>Till next time <a href="#till-next-time" name="till-next-time" class="header-anchor">#</a></h2>
|
||||
<hr />
|
||||
<ul>
|
||||
<li><a href="http://wren.io/blog/rss.xml">The Wren Blog RSS</a></li>
|
||||
<li>Join the <a href="https://discord.gg/Kx6PxSX">discord community</a></li>
|
||||
<li>Visit the <a href="https://github.com/wren-lang">wren-lang organization</a> on GitHub to get involved.</li>
|
||||
<li>Follow the developers <a href="https://twitter.com/munificentbob">@munificentbob</a> or <a href="https://twitter.com/ruby0x1">@ruby0x1</a> on twitter</li>
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
206
blog/3-0.4.0-released.html
Normal file
206
blog/3-0.4.0-released.html
Normal file
@ -0,0 +1,206 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>0.4.0 released! – Wren</title>
|
||||
<script type="application/javascript" src="../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../"><img src="../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../getting-started.html">Getting Started</a></li>
|
||||
<li><a href="../contributing.html">Contributing</a></li>
|
||||
<li><a href="../blog">Blog</a></li>
|
||||
<li><a href="../try">Try it!</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="../getting-started.html">Getting Started</a></li>
|
||||
<li><a href="../contributing.html">Contributing</a></li>
|
||||
<li><a href="../blog">Blog</a></li>
|
||||
<li><a href="../try">Try it!</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>0.4.0 released!</h2>
|
||||
<p>8 April 2021</p>
|
||||
<hr />
|
||||
<p>This post is all about the 0.4.0 release since it’s a big one! <br />
|
||||
<small>(A separate post for 0.5.0 goals would likely come later.)</small></p>
|
||||
<h2>0.4.0 details <a href="#0.4.0-details" name="0.4.0-details" class="header-anchor">#</a></h2>
|
||||
<p><strong>0.4.0 contains 145 commits from 28 contributors.</strong></p>
|
||||
<p>The <a href="https://github.com/wren-lang/wren/releases/tag/0.4.0">full release notes</a>
|
||||
link to each PR or commit, and contains a lot more details than this post.</p>
|
||||
<p><strong>Goals</strong> <br />
|
||||
As usual, let’s revisit the goals from the <a href="2-0.3.0-released.html#goals-for-0.4.0">0.3.0 post</a>.</p>
|
||||
<p>Most importantly - compound operators didn’t land in 0.4.0 for various reasons.
|
||||
Still working on it, it’s just a fun and nuanced problem and I don’t want to
|
||||
keep 0.4.0 back cos of it.</p>
|
||||
<p>With that out the way, let’s see what 0.4.0 contains! </p>
|
||||
<h2>0.4.0 highlights <a href="#0.4.0-highlights" name="0.4.0-highlights" class="header-anchor">#</a></h2>
|
||||
<p>Below we’ll highlight some key features, fixes and improvements from the release. </p>
|
||||
<p><strong>A lot of work came from the community, much thanks to everyone contributing!</strong></p>
|
||||
<p>You can find all the details and the contributions in the <a href="https://github.com/wren-lang/wren/releases/tag/0.4.0">release notes</a>.</p>
|
||||
<p><strong>Take note!</strong> There are two minor breaking changes in the API on the release notes. </p>
|
||||
<hr />
|
||||
<h3>Bug fixes <a href="#bug-fixes" name="bug-fixes" class="header-anchor">#</a></h3>
|
||||
<p>Several important bugs have been fixed, sneaky stack corruptions and some user
|
||||
experience fixes that clarify confusing states. </p>
|
||||
<h3>Documentation <a href="#documentation" name="documentation" class="header-anchor">#</a></h3>
|
||||
<p>A lot of work has gone into documentation this release, revising, fixing, adding
|
||||
and closing gaps that were left. For example, Wren supports multi-line strings
|
||||
but this was never mentioned anywhere! </p>
|
||||
<h3>New <strong>continue</strong> keyword <a href="#new-continue-keyword" name="new-continue-keyword" class="header-anchor">#</a></h3>
|
||||
<p>Loops can now use continue, which is a welcome addition.</p>
|
||||
<h3>New <strong>as</strong> keyword <a href="#new-as-keyword" name="new-as-keyword" class="header-anchor">#</a></h3>
|
||||
<p>You can now use <code>import "..." for Name as OtherName</code> to avoid name conflicts,
|
||||
or to use aliases/shorthand for imported variables.</p>
|
||||
<h3>Raw strings <a href="#raw-strings" name="raw-strings" class="header-anchor">#</a></h3>
|
||||
<p>Wren now supports triple quotes for a string <code>"""</code>.</p>
|
||||
<p>This type of string is only unique in how it’s parsed, the content of the
|
||||
string is ignored (no interpolation or escapes are processed), which allows
|
||||
complex strings to be expressed without needing to escape things. </p>
|
||||
<p>A common example is json or regex, where there’s a lot of escaping that obscures
|
||||
the string content and makes it hard to read and maintain. </p>
|
||||
<p>If they span multiple lines, the string ignores the open and closing newlines
|
||||
and whitespace and preserves anything in between.</p>
|
||||
<pre class="snippet">
|
||||
var json = """
|
||||
{
|
||||
"hello": "wren",
|
||||
"from" : "json"
|
||||
}
|
||||
"""
|
||||
</pre>
|
||||
|
||||
<h3>Attributes <a href="#attributes" name="attributes" class="header-anchor">#</a></h3>
|
||||
<p>Attributes are user-defined metadata associated with a class or method that
|
||||
can be used at runtime, by external tools (and potentially by Wren itself).</p>
|
||||
<pre class="snippet">
|
||||
#hidden = true
|
||||
#doc = "A simple example class"
|
||||
class Example {}
|
||||
</pre>
|
||||
|
||||
<p>They can be:</p>
|
||||
<ul>
|
||||
<li>a <code>#key</code> on it’s own</li>
|
||||
<li>a <code>#key = value</code></li>
|
||||
<li>a <code>#group(with, multiple = true, keys = "value")</code></li>
|
||||
</ul>
|
||||
<p><strong>Example</strong> </p>
|
||||
<p>Below you can one obvious use case, a wip version where attributes for docs were
|
||||
parsed and sent over to <a href="https://code.visualstudio.com/">vscode</a> to display.</p>
|
||||
<video preload="auto" controls="" loop="loop" style="max-width:100%; width:auto; margin:auto; display:block;">
|
||||
<source src="https://i.imgur.com/W9DWysP.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
<p><strong>Runtime access</strong> <br />
|
||||
By default, attributes are compiled out and ignored.
|
||||
For an attribute to be visible at runtime, mark it for runtime access using an
|
||||
exclamation:</p>
|
||||
<pre class="snippet">
|
||||
#doc = "not runtime data"
|
||||
#!runtimeAccess = true
|
||||
#!maxIterations = 16
|
||||
</pre>
|
||||
|
||||
<p>Attributes at runtime are stored on the class itself. You can access them via
|
||||
<code>YourClass.attributes</code>. If any attributes are made available, they’ll be found here:</p>
|
||||
<ul>
|
||||
<li><code>YourClass.attributes.self</code> for the class attributes</li>
|
||||
<li><code>YourClass.attributes.methods</code> for the method attributes</li>
|
||||
</ul>
|
||||
<p>All the details for <a href="https://wren.io/classes.html#attributes">Attributes can be found here</a>.</p>
|
||||
<h3>Chained methods fixes (‘fluent interfaces’) <a href="#chained-methods-fixes-('fluent-interfaces')" name="chained-methods-fixes-('fluent-interfaces')" class="header-anchor">#</a></h3>
|
||||
<p>Mentioned in the last post, you can now use this pattern in code as intended,
|
||||
the same-line requirement for the <code>.</code> has been removed.</p>
|
||||
<pre class="snippet">
|
||||
example
|
||||
.some()
|
||||
.functions()
|
||||
.here()
|
||||
</pre>
|
||||
|
||||
<h3>List additions <a href="#list-additions" name="list-additions" class="header-anchor">#</a></h3>
|
||||
<p>Lists are now sortable via <code>list.sort()</code> and <code>list.sort {|a, b| ... }</code>.
|
||||
You can find an index of something via <code>list.indexOf(value)</code>, and remove a value
|
||||
via <code>list.remove(value)</code>. There’s also <code>list.swap(index0, index1)</code> for moving
|
||||
items around within a list.</p>
|
||||
<p>For the API, <code>wrenSetListElement</code> now exists, and both set and
|
||||
<code>wrenGetListElement</code> now accept negative indices same as the language side.</p>
|
||||
<h3>Num additions <a href="#num-additions" name="num-additions" class="header-anchor">#</a></h3>
|
||||
<p>A few new constants:</p>
|
||||
<ul>
|
||||
<li><code>Num.tau</code></li>
|
||||
<li><code>Num.nan</code> </li>
|
||||
<li><code>Num.infinity</code></li>
|
||||
<li><code>Num.minSafeInteger</code>/<code>Num.maxSafeInteger</code></li>
|
||||
</ul>
|
||||
<p>And some new methods on a number:</p>
|
||||
<ul>
|
||||
<li><code>num.min(other)</code></li>
|
||||
<li><code>num.max(other)</code></li>
|
||||
<li><code>num.clamp(min, max)</code></li>
|
||||
<li><code>num.cbrt</code></li>
|
||||
<li><code>num.exp</code></li>
|
||||
<li><code>num.log2</code></li>
|
||||
</ul>
|
||||
<h3>Map access from the API <a href="#map-access-from-the-api" name="map-access-from-the-api" class="header-anchor">#</a></h3>
|
||||
<p>You can now create and access maps from the API:</p>
|
||||
<ul>
|
||||
<li><code>wrenSetSlotNewMap</code></li>
|
||||
<li><code>wrenGetMapCount</code></li>
|
||||
<li><code>wrenGetMapContainsKey</code></li>
|
||||
<li><code>wrenGetMapValue</code></li>
|
||||
<li><code>wrenSetMapValue</code></li>
|
||||
<li><code>wrenRemoveMapValue</code></li>
|
||||
</ul>
|
||||
<h2>Till next time <a href="#till-next-time" name="till-next-time" class="header-anchor">#</a></h2>
|
||||
<hr />
|
||||
<ul>
|
||||
<li><a href="http://wren.io/blog/rss.xml">The Wren Blog RSS</a></li>
|
||||
<li>Join the <a href="https://discord.gg/Kx6PxSX">discord community</a></li>
|
||||
<li>Visit the <a href="https://github.com/wren-lang">wren-lang organization</a> on GitHub to get involved.</li>
|
||||
<li>Follow the developers <a href="https://twitter.com/munificentbob">@munificentbob</a> or <a href="https://twitter.com/ruby0x1">@ruby0x1</a> on twitter</li>
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
81
blog/index.html
Normal file
81
blog/index.html
Normal file
@ -0,0 +1,81 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Development blogs – Wren</title>
|
||||
<script type="application/javascript" src="../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../"><img src="../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../getting-started.html">Getting Started</a></li>
|
||||
<li><a href="../contributing.html">Contributing</a></li>
|
||||
<li><a href="../blog">Blog</a></li>
|
||||
<li><a href="../try">Try it!</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="../getting-started.html">Getting Started</a></li>
|
||||
<li><a href="../contributing.html">Contributing</a></li>
|
||||
<li><a href="../blog">Blog</a></li>
|
||||
<li><a href="../try">Try it!</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>Development blogs</h2>
|
||||
<p><a href="3-0.4.0-released.html"><h3>0.4.0 released!</h3></a></p>
|
||||
<blockquote>
|
||||
<p><date>8 April 2021</date> • 0.4.0 is a big release, here’s all the info! </p>
|
||||
</blockquote>
|
||||
<p><a href="2-0.3.0-released.html"><h3>0.3.0 released!</h3></a></p>
|
||||
<blockquote>
|
||||
<p><date>5 June 2020</date> • 0.3.0 release info! Plus some notes and goals for the next release, 0.4.0.</p>
|
||||
</blockquote>
|
||||
<p><a href="1-0.2.0-and-beyond.html"><h3>0.2.0 and beyond</h3></a></p>
|
||||
<blockquote>
|
||||
<p><date>30 Sep 2019</date> • Checkpoints, and the plans for 0.3.0.</p>
|
||||
</blockquote>
|
||||
<p><a href="0-hello-wren.html"><h3>System.print(“hello wren”)</h3></a></p>
|
||||
<blockquote>
|
||||
<p><date>4 Feb 2019</date> • A short post introducing the blog, the new maintainer, and the immediate term plans for Wren.</p>
|
||||
</blockquote>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
35
blog/rss.xml
Normal file
35
blog/rss.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<rss version="2.0">
|
||||
<channel><title>Wren - development blog</title>
|
||||
<link>https://wren.io/</link>
|
||||
<description>The development blog of the Wren programming language.</description>
|
||||
<language>en-us</language>
|
||||
<item>
|
||||
<title>0.4.0 released</title>
|
||||
<link>https://wren.io/blog/3-0.4.0-released.html</link>
|
||||
<description>0.4.0 is a big release, here's all the info!</description>
|
||||
<guid>https://wren.io/blog/3-0.4.0-released.html</guid>
|
||||
<pubDate>Thu, 08 Apr 2021 00:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>0.3.0 released</title>
|
||||
<link>https://wren.io/blog/2-0.3.0-released.html</link>
|
||||
<description>0.3.0 release info! Plus some notes and goals for the next release, 0.4.0.</description>
|
||||
<guid>https://wren.io/blog/2-0.3.0-released.html</guid>
|
||||
<pubDate>Mon, 05 Jun 2020 00:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>0.2.0 and beyond</title>
|
||||
<link>https://wren.io/blog/1-0.2.0-and-beyond.html</link>
|
||||
<description>Checkpoints, and the plans for 0.3.0.</description>
|
||||
<guid>https://wren.io/blog/1-0.2.0-and-beyond.html</guid>
|
||||
<pubDate>Mon, 30 Sep 2019 00:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>System.print("hello wren")</title>
|
||||
<link>https://wren.io/blog/0-hello-wren.html</link>
|
||||
<description>A short post introducing the blog, the new maintainer, and the immediate term plans for Wren.</description>
|
||||
<guid>https://wren.io/blog/0-hello-wren.html</guid>
|
||||
<pubDate>Mon, 04 Feb 2019 00:00:00 GMT</pubDate>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
@ -1,7 +0,0 @@
|
||||
The Wren scripts in this directory get converted to C string literals and then
|
||||
inserted into their respective .c files so that the interpreter can load them
|
||||
directly without having to do any file IO.
|
||||
|
||||
The script that does this copying is `script/generate_builtins.py`.
|
||||
|
||||
You can invoke using `make builtin`.
|
||||
@ -1,46 +0,0 @@
|
||||
class Sequence {
|
||||
map(f) {
|
||||
var result = []
|
||||
for (element in this) {
|
||||
result.add(f.call(element))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
where(f) {
|
||||
var result = []
|
||||
for (element in this) {
|
||||
if (f.call(element)) result.add(element)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class List is Sequence {
|
||||
addAll(other) {
|
||||
for (element in other) {
|
||||
add(element)
|
||||
}
|
||||
return other
|
||||
}
|
||||
|
||||
toString {
|
||||
var result = "["
|
||||
for (i in 0...count) {
|
||||
if (i > 0) result = result + ", "
|
||||
result = result + this[i].toString
|
||||
}
|
||||
result = result + "]"
|
||||
return result
|
||||
}
|
||||
|
||||
+(other) {
|
||||
var result = this[0..-1]
|
||||
for (element in other) {
|
||||
result.add(element)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class Range is Sequence {}
|
||||
@ -1,81 +0,0 @@
|
||||
class IO {
|
||||
static print {
|
||||
IO.writeString_("\n")
|
||||
}
|
||||
|
||||
static print(obj) {
|
||||
IO.writeString_(obj.toString)
|
||||
IO.writeString_("\n")
|
||||
return obj
|
||||
}
|
||||
|
||||
static print(a1, a2) {
|
||||
printList_([a1, a2])
|
||||
}
|
||||
|
||||
static print(a1, a2, a3) {
|
||||
printList_([a1, a2, a3])
|
||||
}
|
||||
|
||||
static print(a1, a2, a3, a4) {
|
||||
printList_([a1, a2, a3, a4])
|
||||
}
|
||||
|
||||
static print(a1, a2, a3, a4, a5) {
|
||||
printList_([a1, a2, a3, a4, a5])
|
||||
}
|
||||
|
||||
static print(a1, a2, a3, a4, a5, a6) {
|
||||
printList_([a1, a2, a3, a4, a5, a6])
|
||||
}
|
||||
|
||||
static print(a1, a2, a3, a4, a5, a6, a7) {
|
||||
printList_([a1, a2, a3, a4, a5, a6, a7])
|
||||
}
|
||||
|
||||
static print(a1, a2, a3, a4, a5, a6, a7, a8) {
|
||||
printList_([a1, a2, a3, a4, a5, a6, a7, a8])
|
||||
}
|
||||
|
||||
static print(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
|
||||
printList_([a1, a2, a3, a4, a5, a6, a7, a8, a9])
|
||||
}
|
||||
|
||||
static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {
|
||||
printList_([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10])
|
||||
}
|
||||
|
||||
static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) {
|
||||
printList_([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11])
|
||||
}
|
||||
|
||||
static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) {
|
||||
printList_([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12])
|
||||
}
|
||||
|
||||
static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) {
|
||||
printList_([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13])
|
||||
}
|
||||
|
||||
static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) {
|
||||
printList_([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14])
|
||||
}
|
||||
|
||||
static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) {
|
||||
printList_([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15])
|
||||
}
|
||||
|
||||
static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) {
|
||||
printList_([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16])
|
||||
}
|
||||
|
||||
static printList_(objects) {
|
||||
for (object in objects) IO.writeString_(object.toString)
|
||||
IO.writeString_("\n")
|
||||
}
|
||||
|
||||
static write(obj) {
|
||||
IO.writeString_(obj.toString)
|
||||
return obj
|
||||
}
|
||||
}
|
||||
778
classes.html
Normal file
778
classes.html
Normal file
@ -0,0 +1,778 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Classes – Wren</title>
|
||||
<script type="application/javascript" src="prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="codejar.js"></script>
|
||||
<script type="application/javascript" src="wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="./">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="./"><img src="./wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="contributing.html">Contributing</a></li>
|
||||
<li><a href="blog">Blog</a></li>
|
||||
<li><a href="try">Try it!</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>guides</h2>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>API docs</h2>
|
||||
<ul>
|
||||
<li><a href="modules">Modules</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>reference</h2>
|
||||
<ul>
|
||||
<li><a href="cli">Wren CLI</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<div><a href="getting-started.html">Getting Started</a></div>
|
||||
<div><a href="contributing.html">Contributing</a></div>
|
||||
<div><a href="blog">Blog</a></div>
|
||||
<div><a href="try">Try it!</a></div>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>guides</h2></td>
|
||||
<td><h2>reference</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="modules">API/Modules</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>Classes</h2>
|
||||
<p>Every value in Wren is an object, and every object is an instance of a class.
|
||||
Even <code>true</code> and <code>false</code> are full-featured objects—instances of the
|
||||
<a href="modules/core/bool.html">Bool</a> class.</p>
|
||||
<p>Classes define an objects <em>behavior</em> and <em>state</em>. Behavior is defined by
|
||||
<a href="method-calls.html"><em>methods</em></a> which live in the class. Every object of the same
|
||||
class supports the same methods. State is defined in <em>fields</em>, whose values are
|
||||
stored in each instance.</p>
|
||||
<h2>Defining a class <a href="#defining-a-class" name="defining-a-class" class="header-anchor">#</a></h2>
|
||||
<p>Classes are created using the <code>class</code> keyword, unsurprisingly:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {}
|
||||
</pre>
|
||||
|
||||
<p>This creates a class named <code>Unicorn</code> with no methods or fields.</p>
|
||||
<h2>Methods <a href="#methods" name="methods" class="header-anchor">#</a></h2>
|
||||
<p>To let our unicorn do stuff, we need to give it methods.</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
prance() {
|
||||
System.print("The unicorn prances in a fancy manner!")
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>This defines a <code>prance()</code> method that takes no arguments. To add parameters, put
|
||||
their names inside the parentheses:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
prance(where, when) {
|
||||
System.print("The unicorn prances in %(where) at %(when).")
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Since the number of parameters is part of a method’s <a href="method-calls.html#signature">signature</a> a class can
|
||||
define multiple methods with the same name:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
prance() {
|
||||
System.print("The unicorn prances in a fancy manner!")
|
||||
}
|
||||
|
||||
prance(where) {
|
||||
System.print("The unicorn prances in %(where).")
|
||||
}
|
||||
|
||||
prance(where, when) {
|
||||
System.print("The unicorn prances in %(where) at %(when).")
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>It’s often natural to have the same conceptual operation work with different
|
||||
sets of arguments. In other languages, you’d define a single method for the
|
||||
operation and have to check for missing optional arguments. In Wren, they are
|
||||
different methods that you implement separately.</p>
|
||||
<p>In addition to named methods with parameter lists, Wren has a bunch of other
|
||||
different syntaxes for methods. Your classes can define all of them.</p>
|
||||
<h3>Getters <a href="#getters" name="getters" class="header-anchor">#</a></h3>
|
||||
<p>A getter leaves off the parameter list and the parentheses:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
// Unicorns are always fancy.
|
||||
isFancy { true }
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3>Setters <a href="#setters" name="setters" class="header-anchor">#</a></h3>
|
||||
<p>A setter has <code>=</code> after the name, followed by a single parenthesized parameter:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
rider=(value) {
|
||||
System.print("I am being ridden by %(value).")
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>By convention, the parameter is usually named <code>value</code> but you can call it
|
||||
whatever makes your heart flutter.</p>
|
||||
<h3>Operators <a href="#operators" name="operators" class="header-anchor">#</a></h3>
|
||||
<p>Prefix operators, like getters, have no parameter list:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
- {
|
||||
System.print("Negating a unicorn is weird.")
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Infix operators, like setters, have a single parenthesized parameter for the
|
||||
right-hand operand:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
-(other) {
|
||||
System.print("Subtracting %(other) from a unicorn is weird.")
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>A subscript operator puts the parameters inside square brackets and can have
|
||||
more than one:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
[index] {
|
||||
System.print("Unicorns are not lists!")
|
||||
}
|
||||
|
||||
[x, y] {
|
||||
System.print("Unicorns are not matrices either!")
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Unlike with named methods, you can’t define a subscript operator with an empty
|
||||
parameter list.</p>
|
||||
<p>As the name implies, a subscript setter looks like a combination of a subscript
|
||||
operator and a setter:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
[index]=(value) {
|
||||
System.print("You can't stuff %(value) into me at %(index)!")
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h2>Method Scope <a href="#method-scope" name="method-scope" class="header-anchor">#</a></h2>
|
||||
<p>Up to this point, “<a href="variables.html#scope">scope</a>” has been used to talk exclusively about
|
||||
<a href="variables.html">variables</a>. In a procedural language like C, or a functional one like Scheme,
|
||||
that’s the only kind of scope there is. But object-oriented languages like Wren
|
||||
introduce another kind of scope: <em>object scope</em>. It contains the methods that
|
||||
are available on an object. When you write:</p>
|
||||
<pre class="snippet">
|
||||
unicorn.isFancy
|
||||
</pre>
|
||||
|
||||
<p>You’re saying “look up the method <code>isFancy</code> in the scope of the object
|
||||
<code>unicorn</code>”. In this case, the fact that you want to look up a <em>method</em>
|
||||
<code>isFancy</code> and not a <em>variable</em> <code>isFancy</code> is explicit. That’s what <code>.</code> does and
|
||||
the object to the left of the period is the object you want to look up the
|
||||
method on.</p>
|
||||
<h3><code>this</code> <a href="#this" name="this" class="header-anchor">#</a></h3>
|
||||
<p>Things get more interesting when you’re inside the body of a method. When the
|
||||
method is called on some object and the body is being executed, you often need
|
||||
to access that object itself. You can do that using <code>this</code>.</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
name { "Francis" }
|
||||
|
||||
printName() {
|
||||
System.print(this.name) //> Francis
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The <code>this</code> keyword works sort of like a variable, but has special behavior. It
|
||||
always refers to the instance whose method is currently being executed. This
|
||||
lets you invoke methods on “yourself”.</p>
|
||||
<p>It’s an error to refer to <code>this</code> outside of a method. However, it’s perfectly
|
||||
fine to use it inside a <a href="functions.html">function</a> declared <em>inside</em> a method. When you do,
|
||||
<code>this</code> still refers to the instance whose <em>method</em> is being called:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
name { "Francis" }
|
||||
|
||||
printNameThrice() {
|
||||
(1..3).each {
|
||||
// Use "this" inside the function passed to each().
|
||||
System.print(this.name) //> Francis
|
||||
} //> Francis
|
||||
} //> Francis
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>This is unlike Lua and JavaScript which can “forget” <code>this</code> when you create a
|
||||
callback inside a method. Wren does what you want here and retains the
|
||||
reference to the original object.</p>
|
||||
<p>(In technical terms, a function’s closure includes <code>this</code>. Wren can do this
|
||||
because it makes a distinction between methods and functions.)</p>
|
||||
<h3>Implicit <code>this</code> <a href="#implicit-this" name="implicit-this" class="header-anchor">#</a></h3>
|
||||
<p>Using <code>this.</code> every time you want to call a method on yourself works, but it’s
|
||||
tedious and verbose, which is why some languages don’t require it. You can do a
|
||||
“self send” by calling a method (or getter or setter) without any explicit
|
||||
receiver:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
name { "Francis" }
|
||||
|
||||
printName() {
|
||||
System.print(name) //> Francis
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Code like this gets tricky when there is also a variable outside of the class
|
||||
with the same name. Consider:</p>
|
||||
<pre class="snippet">
|
||||
var name = "variable"
|
||||
|
||||
class Unicorn {
|
||||
name { "Francis" }
|
||||
|
||||
printName() {
|
||||
System.print(name) // ???
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Should <code>printName()</code> print “variable” or “Francis”? A method body has a foot in
|
||||
each of two worlds. It is surrounded by the lexical scope where it’s defined in
|
||||
the program, but it also has the object scope of the methods on <code>this</code>.</p>
|
||||
<p>Which scope wins? Every language has to decide how to handle this and there
|
||||
is a surprising plethora of approaches. Wren’s approach to resolving a name
|
||||
inside a method works like this:</p>
|
||||
<ol>
|
||||
<li>If there is a local variable inside the method with that name, that wins.</li>
|
||||
<li>Else, if the name starts with a lowercase letter, treat it like a method on
|
||||
<code>this</code>.</li>
|
||||
<li>Otherwise, look for a variable with that name in the surrounding scope.</li>
|
||||
</ol>
|
||||
<p>So, in the above example, we hit case #2 and it prints “Francis”. Distinguishing
|
||||
self sends from outer variables based on the <em>case</em> of the first letter in the
|
||||
name probably seems weird but it works surprisingly well. Method names are
|
||||
lowercase in Wren. Class names are capitalized.</p>
|
||||
<p>Most of the time, when you’re in a method and want to access a name from outside
|
||||
of the class, it’s usually the name of some other class. This rule makes that
|
||||
work.</p>
|
||||
<p>Here’s an example that shows all three cases:</p>
|
||||
<pre class="snippet">
|
||||
var shadowed = "surrounding"
|
||||
var lowercase = "surrounding"
|
||||
var Capitalized = "surrounding"
|
||||
|
||||
class Scope {
|
||||
shadowed { "object" }
|
||||
lowercase { "object" }
|
||||
Capitalized { "object" }
|
||||
|
||||
test() {
|
||||
var shadowed = "local"
|
||||
|
||||
System.print(shadowed) //> local
|
||||
System.print(lowercase) //> object
|
||||
System.print(Capitalized) //> surrounding
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>It’s a bit of a strange rule, but Ruby works more or less the same way.</p>
|
||||
<h2>Constructors <a href="#constructors" name="constructors" class="header-anchor">#</a></h2>
|
||||
<p>We’ve seen how to define kinds of objects and how to declare methods on them.
|
||||
Our unicorns can prance around, but we don’t actually <em>have</em> any unicorns to do
|
||||
it. To create <em>instances</em> of a class, we need a <em>constructor</em>. You define one
|
||||
like so:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
construct new(name, color) {
|
||||
System.print("My name is " + name + " and I am " + color + ".")
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The <code>construct</code> keyword says we’re defining a constructor, and <code>new</code> is its
|
||||
name. In Wren, all constructors have names. The word “new” isn’t special to
|
||||
Wren, it’s just a common constructor name.</p>
|
||||
<p>To make a unicorn now, we call the constructor method on the class itself:</p>
|
||||
<pre class="snippet">
|
||||
var fred = Unicorn.new("Fred", "palomino")
|
||||
</pre>
|
||||
|
||||
<p>Giving constructors names is handy because it means you can have more than one,
|
||||
and each can clarify how it creates the instance:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
construct brown(name) {
|
||||
System.print("My name is " + name + " and I am brown.")
|
||||
}
|
||||
}
|
||||
|
||||
var dave = Unicorn.brown("Dave")
|
||||
</pre>
|
||||
|
||||
<p>Note that we have to declare a constructor because, unlike some other
|
||||
languages, Wren doesn’t give you a default one. This is useful because some
|
||||
classes aren’t designed to be constructed. If you have an abstract base class
|
||||
that just contains methods to be inherited by other classes, it doesn’t need
|
||||
and won’t have a constructor.</p>
|
||||
<p>Like other methods, constructors can obviously have arguments, and can be
|
||||
overloaded by <a href="#signature">arity</a>. A constructor <em>must</em> be a named method with
|
||||
a (possibly empty) argument list. Operators, getters, and setters cannot be
|
||||
constructors.</p>
|
||||
<p>A constructor returns the instance of the class being created, even if you
|
||||
don’t explicitly use <code>return</code>. It is valid to use <code>return</code> inside of a
|
||||
constructor, but it is an error to have an expression after the return.
|
||||
That rule applies to <code>return this</code> as well, return handles that implicitly inside
|
||||
a constructor, so just <code>return</code> is enough.</p>
|
||||
<pre class="snippet">
|
||||
return //> valid, returns 'this'
|
||||
|
||||
return variable //> invalid
|
||||
return null //> invalid
|
||||
return this //> also invalid
|
||||
</pre>
|
||||
|
||||
<p>A constructor is actually a pair of methods. You get a method on the class:</p>
|
||||
<pre class="snippet">
|
||||
Unicorn.brown("Dave")
|
||||
</pre>
|
||||
|
||||
<p>That creates the new instance, then it invokes the <em>initializer</em> on that
|
||||
instance. This is where the constructor body you defined gets run.</p>
|
||||
<p>This distinction is important because it means inside the body of the
|
||||
constructor, you can access <code>this</code>, assign <a href="#fields">fields</a>, call superclass
|
||||
constructors, etc.</p>
|
||||
<h2>Fields <a href="#fields" name="fields" class="header-anchor">#</a></h2>
|
||||
<p>All state stored in instances is stored in <em>fields</em>. Each field has a name
|
||||
that starts with an underscore.</p>
|
||||
<pre class="snippet">
|
||||
class Rectangle {
|
||||
area { _width * _height }
|
||||
|
||||
// Other stuff...
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Here, <code>_width</code> and <code>_height</code> in the <code>area</code> <a href="classes.html#methods">getter</a> refer
|
||||
to fields on the rectangle instance. You can think of them like <code>this.width</code>
|
||||
and <code>this.height</code> in other languages.</p>
|
||||
<p>When a field name appears, Wren looks for the nearest enclosing class and looks
|
||||
up the field on the instance of that class. Field names cannot be used outside
|
||||
of an instance method. They <em>can</em> be used inside a <a href="functions.html">function</a>
|
||||
in a method. Wren will look outside any nested functions until it finds an
|
||||
enclosing method.</p>
|
||||
<p>Unlike <a href="variables.html">variables</a>, fields are implicitly declared by simply
|
||||
assigning to them. If you access a field before it has been initialized, its
|
||||
value is <code>null</code>.</p>
|
||||
<h3>Encapsulation <a href="#encapsulation" name="encapsulation" class="header-anchor">#</a></h3>
|
||||
<p>All fields are <em>private</em> in Wren—an object’s fields can only be directly
|
||||
accessed from within methods defined on the object’s class. </p>
|
||||
<p>In short, if you want to make a property of an object visible,
|
||||
<strong>you need to define a getter to expose it</strong>:</p>
|
||||
<pre class="snippet">
|
||||
class Rectangle {
|
||||
width { _width }
|
||||
height { _height }
|
||||
|
||||
// ...
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>To allow outside code to modify the field,
|
||||
<strong>you need to provide setters to provide access</strong>:</p>
|
||||
<pre class="snippet">
|
||||
class Rectangle {
|
||||
width=(value) { _width = value }
|
||||
height=(value) { _height = value }
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>This might be different from what you’re used to, so here are two important facts:</p>
|
||||
<ul>
|
||||
<li>You can’t access fields from a base class.</li>
|
||||
<li>You can’t access fields on another instance of your own class.</li>
|
||||
</ul>
|
||||
<p>Here is an example in code:</p>
|
||||
<pre class="snippet">
|
||||
class Shape {
|
||||
construct new() {
|
||||
_shape = "none"
|
||||
}
|
||||
}
|
||||
|
||||
class Rectangle is Shape {
|
||||
construct new() {
|
||||
//This will print null!
|
||||
//_shape from the parent class is private,
|
||||
//we are reading `_shape` from `this`,
|
||||
//which has not been set, so returns null.
|
||||
System.print("I am a %(_shape)")
|
||||
|
||||
//a local variable, all variables are private
|
||||
_width = 10
|
||||
var other = Rectangle.new()
|
||||
|
||||
//other._width is not accessible from here,
|
||||
//even though we are also a rectangle. The field
|
||||
//is private, and other._width is invalid syntax!
|
||||
}
|
||||
}
|
||||
...
|
||||
</pre>
|
||||
|
||||
<p>One thing we’ve learned in the past forty years of software engineering is that
|
||||
encapsulating state tends to make code easier to maintain, so Wren defaults to
|
||||
keeping your object’s state pretty tightly bundled up. Don’t feel that you have
|
||||
to or even should define getters or setters for most of your object’s fields.</p>
|
||||
<h2>Metaclasses and static members <a href="#metaclasses-and-static-members" name="metaclasses-and-static-members" class="header-anchor">#</a></h2>
|
||||
<p><strong>TODO</strong></p>
|
||||
<h3>Static fields <a href="#static-fields" name="static-fields" class="header-anchor">#</a></h3>
|
||||
<p>A name that starts with <em>two</em> underscores is a <em>static</em> field. They work
|
||||
similar to <a href="#fields">fields</a> except the data is stored on the class itself, and
|
||||
not the instance. They can be used in <em>both</em> instance and static methods.</p>
|
||||
<pre class="snippet">
|
||||
class Foo {
|
||||
construct new() {}
|
||||
|
||||
static setFromStatic(a) { __a = a }
|
||||
setFromInstance(a) { __a = a }
|
||||
|
||||
static printFromStatic() {
|
||||
System.print(__a)
|
||||
}
|
||||
|
||||
printFromInstance() {
|
||||
System.print(__a)
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Just like instance fields, static fields are initially <code>null</code>:</p>
|
||||
<pre class="snippet">
|
||||
Foo.printFromStatic() //> null
|
||||
</pre>
|
||||
|
||||
<p>They can be used from static methods:</p>
|
||||
<pre class="snippet">
|
||||
Foo.setFromStatic("first")
|
||||
Foo.printFromStatic() //> first
|
||||
</pre>
|
||||
|
||||
<p>And also instance methods. When you do so, there is still only one static field
|
||||
shared among all instances of the class:</p>
|
||||
<pre class="snippet">
|
||||
var foo1 = Foo.new()
|
||||
var foo2 = Foo.new()
|
||||
|
||||
foo1.setFromInstance("second")
|
||||
foo2.printFromInstance() //> second
|
||||
</pre>
|
||||
|
||||
<h2>Inheritance <a href="#inheritance" name="inheritance" class="header-anchor">#</a></h2>
|
||||
<p>A class can inherit from a “parent” or <em>superclass</em>. When you invoke a method
|
||||
on an object of some class, if it can’t be found, it walks up the chain of
|
||||
superclasses looking for it there.</p>
|
||||
<p>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 <code>is</code> when you declare the class:</p>
|
||||
<pre class="snippet">
|
||||
class Pegasus is Unicorn {}
|
||||
</pre>
|
||||
|
||||
<p>This declares a new class Pegasus that inherits from Unicorn.</p>
|
||||
<p>Note that you should not create classes that inherit from the built-in types
|
||||
(Bool, Num, String, Range, List). The built-in types expect their internal bit
|
||||
representation to be very specific and get horribly confused when you invoke one
|
||||
of the inherited built-in methods on the derived type.</p>
|
||||
<p>The metaclass hierarchy does <em>not</em> parallel the regular class hierarchy. So, if
|
||||
Pegasus inherits from Unicorn, Pegasus’s metaclass does not inherit from
|
||||
Unicorn’s metaclass. In more prosaic terms, this means that static methods are
|
||||
not inherited.</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
// Unicorns cannot fly. :(
|
||||
static canFly { false }
|
||||
}
|
||||
|
||||
class Pegasus is Unicorn {}
|
||||
|
||||
Pegasus.canFly //! Static methods are not inherited.
|
||||
</pre>
|
||||
|
||||
<p>This also means constructors are not inherited:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
construct new(name) {
|
||||
System.print("My name is " + name + ".")
|
||||
}
|
||||
}
|
||||
|
||||
class Pegasus is Unicorn {}
|
||||
|
||||
Pegasus.new("Fred") //! Pegasus does not define new().
|
||||
</pre>
|
||||
|
||||
<p>Each class gets to control how it may be constructed independently of its base
|
||||
classes. However, constructor <em>initializers</em> are inherited since those are
|
||||
instance methods on the new object.</p>
|
||||
<p>This means you can do <code>super</code> calls inside a constructor:</p>
|
||||
<pre class="snippet">
|
||||
class Unicorn {
|
||||
construct new(name) {
|
||||
System.print("My name is " + name + ".")
|
||||
}
|
||||
}
|
||||
|
||||
class Pegasus is Unicorn {
|
||||
construct new(name) {
|
||||
super(name)
|
||||
}
|
||||
}
|
||||
|
||||
Pegasus.new("Fred") //> My name is Fred
|
||||
</pre>
|
||||
|
||||
<h2>Super <a href="#super" name="super" class="header-anchor">#</a></h2>
|
||||
<p><strong>TODO: Integrate better into page. Should explain this before mentioning
|
||||
super above.</strong></p>
|
||||
<p>Sometimes you want to invoke a method on yourself, but using methods defined in
|
||||
one of your <a href="classes.html#inheritance">superclasses</a>. You typically do this in
|
||||
an overridden method when you want to access the original method being
|
||||
overridden.</p>
|
||||
<p>To do that, you can use the special <code>super</code> keyword as the receiver in a method
|
||||
call:</p>
|
||||
<pre class="snippet">
|
||||
class Base {
|
||||
method() {
|
||||
System.print("base method")
|
||||
}
|
||||
}
|
||||
|
||||
class Derived is Base {
|
||||
method() {
|
||||
super.method() //> base method
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>You can also use <code>super</code> without a method name inside a constructor to invoke a
|
||||
base class constructor:</p>
|
||||
<pre class="snippet">
|
||||
class Base {
|
||||
construct new(arg) {
|
||||
System.print("base got " + arg)
|
||||
}
|
||||
}
|
||||
|
||||
class Derived is Base {
|
||||
construct new() {
|
||||
super("value") //> base got value
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h2>Attributes <a href="#attributes" name="attributes" class="header-anchor">#</a></h2>
|
||||
<p><small><strong>experimental stage</strong>: subject to minor changes</small></p>
|
||||
<p>A class and methods within a class can be tagged with ‘meta attributes’.</p>
|
||||
<p>Like this:</p>
|
||||
<pre class="snippet">
|
||||
#hidden = true
|
||||
class Example {}
|
||||
</pre>
|
||||
|
||||
<p>These attributes are metadata, they give you a way to annotate and store
|
||||
any additional information about a class, which you can optionally access at runtime.
|
||||
This information can also be used by external tools, to provide additional
|
||||
hints and information from code to the tool.</p>
|
||||
<p><small>
|
||||
Since this feature has just been introduced, <strong>take note</strong>.</p>
|
||||
<p><strong>Currently</strong> there are no attributes with a built-in meaning.
|
||||
Attributes are user-defined metadata. This may not remain
|
||||
true as some may become well defined through convention or potentially
|
||||
through use by Wren itself.
|
||||
</small></p>
|
||||
<p>Attributes are placed before a class or method definition,
|
||||
and use the <code>#</code> hash/pound symbol. </p>
|
||||
<p>They can be </p>
|
||||
<ul>
|
||||
<li>a <code>#key</code> on it’s own</li>
|
||||
<li>a <code>#key = value</code></li>
|
||||
<li>a <code>#group(with, multiple = true, keys = "value")</code></li>
|
||||
</ul>
|
||||
<p>An attribute <em>key</em> can only be a <code>Name</code>. This is the same type of name
|
||||
as a method name, a class name or variable name, an identifier that matches
|
||||
the Wren identifier rules. A name results in a String value at runtime.</p>
|
||||
<p>An attribute <em>value</em> can be any of these literal values: <code>Name, String, Bool, Num</code>.
|
||||
Values cannot contain expressions, just a value, there is no compile time
|
||||
evaluation.</p>
|
||||
<p>Groups can span multiple lines, methods have their own attributes, and duplicate
|
||||
keys are valid.</p>
|
||||
<pre class="snippet">
|
||||
#key
|
||||
#key = value
|
||||
#group(
|
||||
multiple,
|
||||
lines = true,
|
||||
lines = 0
|
||||
)
|
||||
class Example {
|
||||
#test(skip = true, iterations = 32)
|
||||
doStuff() {}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3>Accessing attributes at runtime <a href="#accessing-attributes-at-runtime" name="accessing-attributes-at-runtime" class="header-anchor">#</a></h3>
|
||||
<p>By default, attributes are compiled out and ignored.</p>
|
||||
<p>For an attribute to be visible at runtime, mark it for runtime
|
||||
access using an exclamation:</p>
|
||||
<pre class="snippet">
|
||||
#doc = "not runtime data"
|
||||
#!runtimeAccess = true
|
||||
#!maxIterations = 16
|
||||
</pre>
|
||||
|
||||
<p>Attributes at runtime are stored on the class. You can access them via
|
||||
<code>YourClass.attributes</code>. The <code>attributes</code> field on a class will
|
||||
be null if a class has no attributes or if it’s attributes aren’t marked.</p>
|
||||
<p>If the class contains class or method attributes, it will be an object with
|
||||
two getters:</p>
|
||||
<ul>
|
||||
<li><code>YourClass.attributes.self</code> for the class attributes</li>
|
||||
<li><code>YourClass.attributes.methods</code> for the method attributes</li>
|
||||
</ul>
|
||||
<p>Attributes are stored by group in a regular Wren Map.
|
||||
Keys that are not grouped, use <code>null</code> as the group key.</p>
|
||||
<p>Values are stored in a list, since duplicate keys are allowed, multiple
|
||||
values need to be stored. They’re stored in order of definition.</p>
|
||||
<p>Method attributes are stored in a map by method signature, and each method
|
||||
has it’s own attributes that match the above structure. The method signature
|
||||
is prefixed by <code>static</code> or <code>foreign static</code> as needed.</p>
|
||||
<p>Let’s see what that looks like:</p>
|
||||
<pre class="snippet">
|
||||
// Example.attributes.self =
|
||||
// {
|
||||
// null: { "key":[null] },
|
||||
// group: { "key":[value, 32, false] }
|
||||
// }
|
||||
|
||||
#!key
|
||||
#ignored //compiled out
|
||||
#!group(key=value, key=32, key=false)
|
||||
class Example {
|
||||
#!getter
|
||||
getter {}
|
||||
|
||||
// { regular(_,_): { null: { regular:[null] } } }
|
||||
#!regular
|
||||
regular(arg0, arg1) {}
|
||||
|
||||
// { static other(): { null: { isStatic:[true] } } }
|
||||
#!isStatic = true
|
||||
static other() {}
|
||||
|
||||
// { foreign static example(): { null: { isForeignStatic:[32] } } }
|
||||
#!isForeignStatic=32
|
||||
foreign static example()
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p><br><hr>
|
||||
<a class="right" href="concurrency.html">Concurrency →</a>
|
||||
<a href="functions.html">← Functions</a></p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
108
cli/index.html
Normal file
108
cli/index.html
Normal file
@ -0,0 +1,108 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Wren CLI – Wren</title>
|
||||
<script type="application/javascript" src="../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../"><img src="../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../">Back to Wren</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>Wren CLI</h2>
|
||||
<ul>
|
||||
<li><a href="./">About</a></li>
|
||||
<li><a target="_blank" href="https://github.com/wren-lang/wren-cli/releases">Downloads</a></li>
|
||||
<li><a href="usage.html">Usage</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>API docs</h2>
|
||||
<ul>
|
||||
<li><a href="modules">CLI Modules</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<div><a href="../">Back to Wren</a></div>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><h2>CLI</h2></td>
|
||||
<td><h2>API</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="./">About</a></li>
|
||||
<li><a target="_blank" href="https://github.com/wren-lang/wren-cli/releases">Downloads</a></li>
|
||||
<li><a href="usage.html">Usage</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="modules">CLI Modules</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>Wren CLI</h2>
|
||||
<hr />
|
||||
<h2>What is it? <a href="#what-is-it" name="what-is-it" class="header-anchor">#</a></h2>
|
||||
<p><strong>The Wren Command-Line Interface</strong> is a tool you can run which gives you a way to run Wren code, and
|
||||
also includes modules for talking to the operating system—file IO,
|
||||
networking, stuff like that. It depends on <a href="http://libuv.org/">libuv</a> for that
|
||||
functionality.</p>
|
||||
<p>Wren as a language is intentionally designed to be minimal. <br />
|
||||
That includes the built in language features, the standard library and the VM itself.</p>
|
||||
<p>In order to access files, networks and other IO, you’d need to make a tool <em>using</em> the language VM.
|
||||
That’s what the CLI project is! It is not bundled as part of the wren project,
|
||||
instead it is its own project as a standalone tool you can run.
|
||||
It exposes its own standard library and modules that may be of interest
|
||||
if looking for a general purpose single binary scriptable tool.</p>
|
||||
<p>Wren CLI is a work in progress, and contributions are welcome to make it more useful over time.</p>
|
||||
<h2>Why does it exist? <a href="#why-does-it-exist" name="why-does-it-exist" class="header-anchor">#</a></h2>
|
||||
<ul>
|
||||
<li>It’s fun to make things.</li>
|
||||
<li>It’s always a good idea to test the language you’re making!</li>
|
||||
<li>Interest was expressed in a scriptable tool using the Wren language.</li>
|
||||
<li>It’s helpful for others to learn from, since it is a real world usage example showing several concepts.</li>
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
105
cli/modules/index.html
Normal file
105
cli/modules/index.html
Normal file
@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>CLI Modules – Wren</title>
|
||||
<script type="application/javascript" src="../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../"><img src="../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../">Back to Wren CLI</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>Built In</h2>
|
||||
<ul>
|
||||
<li><a href="../../modules">Wren modules</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>CLI modules</h2>
|
||||
<ul>
|
||||
<li><a href="io">io</a></li>
|
||||
<li><a href="os">os</a></li>
|
||||
<li><a href="scheduler">scheduler</a></li>
|
||||
<li><a href="timer">timer</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><h2>core</h2></td>
|
||||
<td><h2>optional</h2></td>
|
||||
<td><h2>cli</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="core">core</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="meta">meta</a></li>
|
||||
<li><a href="random">random</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="io">io</a></li>
|
||||
<li><a href="os">os</a></li>
|
||||
<li><a href="scheduler">scheduler</a></li>
|
||||
<li><a href="timer">timer</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>CLI Modules</h1>
|
||||
<p>The Wren CLI executable extends the built in language modules with its own,
|
||||
which offer access to IO and other facilities for scripting.</p>
|
||||
<p>The CLI modules are deeply tied to <a href="http://libuv.org">libuv</a>, each other, and other internals
|
||||
of the command-line app, so can’t easily be separated out and pulled into host
|
||||
applications that want to embed Wren. Scripts written for the CLI then,
|
||||
are specific to the CLI unless another host implements the same API.</p>
|
||||
<ul>
|
||||
<li><a href="io">io</a></li>
|
||||
<li><a href="os">os</a></li>
|
||||
<li><a href="scheduler">scheduler</a></li>
|
||||
<li><a href="timer">timer</a></li>
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
96
cli/modules/io/directory.html
Normal file
96
cli/modules/io/directory.html
Normal file
@ -0,0 +1,96 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Directory Class – Wren</title>
|
||||
<script type="application/javascript" src="../../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../../">Back to Wren CLI</a></li>
|
||||
<li><a href="../">Back to CLI Modules</a></li>
|
||||
<li><a href="./">io module</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>io classes</h2>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Back to CLI Modules</a></td>
|
||||
<td><a href="./">io module</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>io classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Directory Class</h1>
|
||||
<p>A directory on the file system.</p>
|
||||
<h2>Static Methods <a href="#static-methods" name="static-methods" class="header-anchor">#</a></h2>
|
||||
<h3>Directory.<strong>exists</strong>(path) <a href="#directory.exists(path)" name="directory.exists(path)" class="header-anchor">#</a></h3>
|
||||
<p>Whether a directory exists at <code>path</code>. This returns <code>false</code> for files or other
|
||||
special file system entities.</p>
|
||||
<h3>Directory.<strong>list</strong>(path) <a href="#directory.list(path)" name="directory.list(path)" class="header-anchor">#</a></h3>
|
||||
<p>Lists the contents of the directory at <code>path</code>. Returns a sorted list of path
|
||||
strings for all of the contents of the directory.</p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
115
cli/modules/io/file-flags.html
Normal file
115
cli/modules/io/file-flags.html
Normal file
@ -0,0 +1,115 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>FileFlags Class – Wren</title>
|
||||
<script type="application/javascript" src="../../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../../">Back to Wren CLI</a></li>
|
||||
<li><a href="../">Back to CLI Modules</a></li>
|
||||
<li><a href="./">io module</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>io classes</h2>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Back to CLI Modules</a></td>
|
||||
<td><a href="./">io module</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>io classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>FileFlags Class</h1>
|
||||
<p>Contains constants for the various file flags used to open or create a file.
|
||||
These correspond directly to the flags that can be passed to the POSIX
|
||||
<a href="http://linux.die.net/man/2/open"><code>open()</code></a> syscall.</p>
|
||||
<p>They are integers and can be bitwise or’ed together to produce a composite
|
||||
flag.</p>
|
||||
<h2>Static Methods <a href="#static-methods" name="static-methods" class="header-anchor">#</a></h2>
|
||||
<h3>FileFlags.<strong>readOnly</strong> <a href="#fileflags.readonly" name="fileflags.readonly" class="header-anchor">#</a></h3>
|
||||
<p>The file can be read from but not written. Equivalent to <code>O_RDONLY</code>.</p>
|
||||
<h3>FileFlags.<strong>writeOnly</strong> <a href="#fileflags.writeonly" name="fileflags.writeonly" class="header-anchor">#</a></h3>
|
||||
<p>The file can be written but not read from. Equivalent to <code>O_WRONLY</code>.</p>
|
||||
<h3>FileFlags.<strong>readWrite</strong> <a href="#fileflags.readwrite" name="fileflags.readwrite" class="header-anchor">#</a></h3>
|
||||
<p>The file can be both read from and written to. Equivalent to <code>O_RDWR</code>.</p>
|
||||
<h3>FileFlags.<strong>sync</strong> <a href="#fileflags.sync" name="fileflags.sync" class="header-anchor">#</a></h3>
|
||||
<p>Writes will block until the data has been physically written to the underling
|
||||
hardware. This does <em>not</em> affect whether or the file API is synchronous. File
|
||||
operations are always asynchronous in Wren and may allow other scheduled fibers
|
||||
to run.</p>
|
||||
<p>This is a lower-level flag that ensures that when a write completes, it has
|
||||
been flushed all the way to disc.</p>
|
||||
<h3>FileFlags.<strong>create</strong> <a href="#fileflags.create" name="fileflags.create" class="header-anchor">#</a></h3>
|
||||
<p>Creates a new file if a file at the given path does not already exist.</p>
|
||||
<h3>FileFlags.<strong>truncate</strong> <a href="#fileflags.truncate" name="fileflags.truncate" class="header-anchor">#</a></h3>
|
||||
<p>If the file already exists and can be written to, its previous contents are
|
||||
discarded.</p>
|
||||
<h3>FileFlags.<strong>exclusive</strong> <a href="#fileflags.exclusive" name="fileflags.exclusive" class="header-anchor">#</a></h3>
|
||||
<p>Ensures that a new file must be created. If a file already exists at the given
|
||||
path, this flag will cause the operation to fail.</p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
185
cli/modules/io/file.html
Normal file
185
cli/modules/io/file.html
Normal file
@ -0,0 +1,185 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>File Class – Wren</title>
|
||||
<script type="application/javascript" src="../../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../../">Back to Wren CLI</a></li>
|
||||
<li><a href="../">Back to CLI Modules</a></li>
|
||||
<li><a href="./">io module</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>io classes</h2>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Back to CLI Modules</a></td>
|
||||
<td><a href="./">io module</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>io classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>File Class</h1>
|
||||
<p>Lets you work with files on the file system. An instance of this class
|
||||
represents an open file with a file descriptor.</p>
|
||||
<p>When you are done with a file object, it’s a good idea to explicitly close it.
|
||||
If you don’t, the GC will close it when the file is no longer used and gets
|
||||
finalized, but that may take a while. In the meantime, leaving it open wastes
|
||||
a file descriptor.</p>
|
||||
<h2>Static Methods <a href="#static-methods" name="static-methods" class="header-anchor">#</a></h2>
|
||||
<h3>File.<strong>create</strong>(path, fn) <a href="#file.create(path,-fn)" name="file.create(path,-fn)" class="header-anchor">#</a></h3>
|
||||
<p>Opens the file at <code>path</code> for writing and passes it to <code>fn</code>. If there is already
|
||||
a file at that path, it is truncated. After the function returns, the file is
|
||||
automatically closed.</p>
|
||||
<pre class="snippet">
|
||||
File.create("numbers.txt") {|file|
|
||||
file.writeBytes("one two three")
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3>File.<strong>delete</strong>(path) <a href="#file.delete(path)" name="file.delete(path)" class="header-anchor">#</a></h3>
|
||||
<p>Deletes the file at <code>path</code>.</p>
|
||||
<h3>File.<strong>exists</strong>(path) <a href="#file.exists(path)" name="file.exists(path)" class="header-anchor">#</a></h3>
|
||||
<p>Whether a regular file exists at <code>path</code>. This returns <code>false</code> for directories
|
||||
or other special file system entities.</p>
|
||||
<h3>File.<strong>open</strong>(path, fn) <a href="#file.open(path,-fn)" name="file.open(path,-fn)" class="header-anchor">#</a></h3>
|
||||
<p>Opens the file at <code>path</code> for reading and passes it to <code>fn</code>. After the function
|
||||
returns, the file is automatically closed.</p>
|
||||
<pre class="snippet">
|
||||
File.open("words.txt") {|file|
|
||||
file.readBytes(5)
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3>File.<strong>read</strong>(path) <a href="#file.read(path)" name="file.read(path)" class="header-anchor">#</a></h3>
|
||||
<p>Reads the entire contents of the file at <code>path</code> and returns it as a string.</p>
|
||||
<pre class="snippet">
|
||||
File.read("words.txt")
|
||||
</pre>
|
||||
|
||||
<p>No encoding or decoding is done. If the file is UTF-8, then the resulting
|
||||
string will be a UTF-8 string. Otherwise, it will be a string of bytes in
|
||||
whatever encoding the file uses.</p>
|
||||
<h3>File.<strong>realPath</strong>(path) <a href="#file.realpath(path)" name="file.realpath(path)" class="header-anchor">#</a></h3>
|
||||
<p>Resolves <code>path</code>, traversing symlinks and removining any unneeded <code>./</code> and <code>../</code>
|
||||
components. Returns the canonical absolute path to the file.</p>
|
||||
<pre class="snippet">
|
||||
var path = "/some/./symlink/a/../b/file.txt"
|
||||
System.print(File.realPath(path)) //> /real/path/a/file.txt
|
||||
</pre>
|
||||
|
||||
<h3>File.<strong>size</strong>(path) <a href="#file.size(path)" name="file.size(path)" class="header-anchor">#</a></h3>
|
||||
<p>Returns the size in bytes of the contents of the file at <code>path</code>.</p>
|
||||
<h2>Constructors <a href="#constructors" name="constructors" class="header-anchor">#</a></h2>
|
||||
<h3>File.<strong>create</strong>(path) <a href="#file.create(path)" name="file.create(path)" class="header-anchor">#</a></h3>
|
||||
<p>Opens the file at <code>path</code> for writing. If there is already a file at that path,
|
||||
it is truncated.</p>
|
||||
<pre class="snippet">
|
||||
var file = File.create("colors.txt")
|
||||
file.writeBytes("chartreuse lime teal")
|
||||
file.close()
|
||||
</pre>
|
||||
|
||||
<h3>File.<strong>open</strong>(path) <a href="#file.open(path)" name="file.open(path)" class="header-anchor">#</a></h3>
|
||||
<p>Opens the file at <code>path</code> for reading. You are responsible for closing it when
|
||||
done with it.</p>
|
||||
<h2>Methods <a href="#methods" name="methods" class="header-anchor">#</a></h2>
|
||||
<h3><strong>descriptor</strong> <a href="#descriptor" name="descriptor" class="header-anchor">#</a></h3>
|
||||
<p>The numeric file descriptor used to access the file.</p>
|
||||
<h3><strong>isOpen</strong> <a href="#isopen" name="isopen" class="header-anchor">#</a></h3>
|
||||
<p>Whether the file is still open or has been closed.</p>
|
||||
<h3><strong>size</strong> <a href="#size" name="size" class="header-anchor">#</a></h3>
|
||||
<p>The size of the contents of the file in bytes.</p>
|
||||
<h3><strong>close</strong>() <a href="#close()" name="close()" class="header-anchor">#</a></h3>
|
||||
<p>Closes the file. After calling this, you can’t read or write from it.</p>
|
||||
<h3><strong>readBytes</strong>(count) <a href="#readbytes(count)" name="readbytes(count)" class="header-anchor">#</a></h3>
|
||||
<p>Reads up to <code>count</code> bytes starting from the beginning of the file.</p>
|
||||
<pre class="snippet">
|
||||
// Assume this file contains "I am a file!".
|
||||
File.open("example.txt") {|file|
|
||||
System.print(file.readBytes(6)) //> I am a
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3><strong>readBytes</strong>(count, offset) <a href="#readbytes(count,-offset)" name="readbytes(count,-offset)" class="header-anchor">#</a></h3>
|
||||
<p>Reads up to <code>count</code> bytes starting at <code>offset</code> bytes from the beginning of
|
||||
the file.</p>
|
||||
<pre class="snippet">
|
||||
// Assume this file contains "I am a file!".
|
||||
File.open("example.txt") {|file|
|
||||
System.print(file.readBytes(6, 2)) //> am a f
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3><strong>writeBytes</strong>(bytes) <a href="#writebytes(bytes)" name="writebytes(bytes)" class="header-anchor">#</a></h3>
|
||||
<p>Writes the raw bytes of the string <code>bytes</code> to the end of the file.</p>
|
||||
<h3><strong>writeBytes</strong>(bytes, offset) <a href="#writebytes(bytes,-offset)" name="writebytes(bytes,-offset)" class="header-anchor">#</a></h3>
|
||||
<p>Writes the raw bytes of the string <code>bytes</code> to the to the file, starting at
|
||||
<code>offset</code>. Any overlapping bytes already in the file at the offset are
|
||||
overwritten.</p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
96
cli/modules/io/index.html
Normal file
96
cli/modules/io/index.html
Normal file
@ -0,0 +1,96 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Module "io" – Wren</title>
|
||||
<script type="application/javascript" src="../../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../../">Back to Wren CLI</a></li>
|
||||
<li><a href="../">Back to CLI Modules</a></li>
|
||||
<li><a href="./">io module</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>io classes</h2>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Back to CLI Modules</a></td>
|
||||
<td><a href="./">io module</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>io classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Module "io"</h1>
|
||||
<p>Provides access to operating system streams and the file system.</p>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
119
cli/modules/io/stat.html
Normal file
119
cli/modules/io/stat.html
Normal file
@ -0,0 +1,119 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Stat Class – Wren</title>
|
||||
<script type="application/javascript" src="../../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../../">Back to Wren CLI</a></li>
|
||||
<li><a href="../">Back to CLI Modules</a></li>
|
||||
<li><a href="./">io module</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>io classes</h2>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Back to CLI Modules</a></td>
|
||||
<td><a href="./">io module</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>io classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Stat Class</h1>
|
||||
<p>A data structure describing the low-level details of a file system entry.</p>
|
||||
<h2>Static Methods <a href="#static-methods" name="static-methods" class="header-anchor">#</a></h2>
|
||||
<h3>Stat.<strong>path</strong>(path) <a href="#stat.path(path)" name="stat.path(path)" class="header-anchor">#</a></h3>
|
||||
<p>“Stats” the file or directory at <code>path</code>.</p>
|
||||
<h2>Methods <a href="#methods" name="methods" class="header-anchor">#</a></h2>
|
||||
<h3><strong>blockCount</strong> <a href="#blockcount" name="blockcount" class="header-anchor">#</a></h3>
|
||||
<p>The number of system blocks allocated on disk for the file.</p>
|
||||
<h3><strong>blockSize</strong> <a href="#blocksize" name="blocksize" class="header-anchor">#</a></h3>
|
||||
<p>The preferred block size in bytes for interacting with the file. It may vary
|
||||
from file to file.</p>
|
||||
<h3><strong>device</strong> <a href="#device" name="device" class="header-anchor">#</a></h3>
|
||||
<p>The ID of the device containing the entry.</p>
|
||||
<h3><strong>group</strong> <a href="#group" name="group" class="header-anchor">#</a></h3>
|
||||
<p>Numeric group ID of the file’s owner.</p>
|
||||
<h3><strong>inode</strong> <a href="#inode" name="inode" class="header-anchor">#</a></h3>
|
||||
<p>The <a href="https://en.wikipedia.org/wiki/Inode">inode</a> number of the entry.</p>
|
||||
<h3><strong>isDirectory</strong> <a href="#isdirectory" name="isdirectory" class="header-anchor">#</a></h3>
|
||||
<p>Whether the file system entity is a directory.</p>
|
||||
<h3><strong>isFile</strong> <a href="#isfile" name="isfile" class="header-anchor">#</a></h3>
|
||||
<p>Whether the file system entity is a regular file, as opposed to a directory or
|
||||
other special entity.</p>
|
||||
<h3><strong>linkCount</strong> <a href="#linkcount" name="linkcount" class="header-anchor">#</a></h3>
|
||||
<p>The number of hard links to the entry.</p>
|
||||
<h3><strong>mode</strong> <a href="#mode" name="mode" class="header-anchor">#</a></h3>
|
||||
<p>A bit field describing the entry’s type and protection flags.</p>
|
||||
<h3><strong>size</strong> <a href="#size" name="size" class="header-anchor">#</a></h3>
|
||||
<p>The size of the entry in bytes.</p>
|
||||
<h3><strong>specialDevice</strong> <a href="#specialdevice" name="specialdevice" class="header-anchor">#</a></h3>
|
||||
<p>The device ID for the entry, if it’s a special file.</p>
|
||||
<h3><strong>user</strong> <a href="#user" name="user" class="header-anchor">#</a></h3>
|
||||
<p>Numeric user ID of the file’s owner.</p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
115
cli/modules/io/stdin.html
Normal file
115
cli/modules/io/stdin.html
Normal file
@ -0,0 +1,115 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Stdin Class – Wren</title>
|
||||
<script type="application/javascript" src="../../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../../">Back to Wren CLI</a></li>
|
||||
<li><a href="../">Back to CLI Modules</a></li>
|
||||
<li><a href="./">io module</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>io classes</h2>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Back to CLI Modules</a></td>
|
||||
<td><a href="./">io module</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>io classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Stdin Class</h1>
|
||||
<p>The standard input stream.</p>
|
||||
<h2>Static Methods <a href="#static-methods" name="static-methods" class="header-anchor">#</a></h2>
|
||||
<h3><strong>isRaw</strong> <a href="#israw" name="israw" class="header-anchor">#</a></h3>
|
||||
<p>Returns <code>true</code> if stdin is in raw mode. When in raw mode, input is not echoed
|
||||
or buffered, and all characters, even non-printing and control characters go
|
||||
into stdin.</p>
|
||||
<p>Defaults to <code>false</code>.</p>
|
||||
<h3><strong>isRaw</strong>=(value) <a href="#israw=(value)" name="israw=(value)" class="header-anchor">#</a></h3>
|
||||
<p>Sets raw mode on or off.</p>
|
||||
<h3><strong>isTerminal</strong> <a href="#isterminal" name="isterminal" class="header-anchor">#</a></h3>
|
||||
<p>Returns <code>true</code> if Stdin is connected to a “TTY”. This is true when the user is
|
||||
running Wren in an interactive terminal, and false if it its input is coming
|
||||
from a pipe.</p>
|
||||
<h3><strong>readByte</strong>() <a href="#readbyte()" name="readbyte()" class="header-anchor">#</a></h3>
|
||||
<p>Reads one byte of input from stdin. Blocks the current fiber until a byte has
|
||||
been received.</p>
|
||||
<p>Returns the byte value as a number or <code>null</code> if stdin is closed.</p>
|
||||
<p>Note that output is not automatically flushed when calling this. If you want to
|
||||
display a prompt before reading input, you’ll want to call <code>Stdout.flush()</code>
|
||||
after printing the prompt.</p>
|
||||
<h3><strong>readLine</strong>() <a href="#readline()" name="readline()" class="header-anchor">#</a></h3>
|
||||
<p>Reads one line of input from stdin. Blocks the current fiber until a full line
|
||||
of input has been received.</p>
|
||||
<p>Returns the string of input or <code>null</code> if stdin is closed.</p>
|
||||
<p>Note that output is not automatically flushed when calling this. If you want to
|
||||
display a prompt before reading input, you’ll want to call <code>Stdout.flush()</code>
|
||||
after printing the prompt.</p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
94
cli/modules/io/stdout.html
Normal file
94
cli/modules/io/stdout.html
Normal file
@ -0,0 +1,94 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Stdout Class – Wren</title>
|
||||
<script type="application/javascript" src="../../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../../">Back to Wren CLI</a></li>
|
||||
<li><a href="../">Back to CLI Modules</a></li>
|
||||
<li><a href="./">io module</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>io classes</h2>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Back to CLI Modules</a></td>
|
||||
<td><a href="./">io module</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>io classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="directory.html">Directory</a></li>
|
||||
<li><a href="file.html">File</a></li>
|
||||
<li><a href="file-flags.html">FileFlags</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="stat.html">Stat</a></li>
|
||||
<li><a href="stdin.html">Stdin</a></li>
|
||||
<li><a href="stdout.html">Stdout</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Stdout Class</h1>
|
||||
<p>The standard output stream.</p>
|
||||
<h2>Static Methods <a href="#static-methods" name="static-methods" class="header-anchor">#</a></h2>
|
||||
<h3><strong>flush()</strong> <a href="#flush()" name="flush()" class="header-anchor">#</a></h3>
|
||||
<p>Flushes all buffered data to the stream. Ensures any data written to stdout
|
||||
that is in the buffer gets written to the file or terminal that stdout is
|
||||
connected to.</p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
86
cli/modules/os/index.html
Normal file
86
cli/modules/os/index.html
Normal file
@ -0,0 +1,86 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Module "os" – Wren</title>
|
||||
<script type="application/javascript" src="../../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../../">Back to Wren CLI</a></li>
|
||||
<li><a href="../">Back to CLI Modules</a></li>
|
||||
<li><a href="./">os module</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>os classes</h2>
|
||||
<ul>
|
||||
<li><a href="platform.html">Platform</a></li>
|
||||
<li><a href="process.html">Process</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Back to CLI Modules</a></td>
|
||||
<td><a href="./">os module</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>os classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="platform.html">Platform</a></li>
|
||||
<li><a href="process.html">Process</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Module "os"</h1>
|
||||
<p>The os module exposes classes for accessing capabilities provided by the
|
||||
underlying operating system.</p>
|
||||
<ul>
|
||||
<li><a href="platform.html">Platform</a></li>
|
||||
<li><a href="process.html">Process</a></li>
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
101
cli/modules/os/platform.html
Normal file
101
cli/modules/os/platform.html
Normal file
@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Platform Class – Wren</title>
|
||||
<script type="application/javascript" src="../../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../../">Back to Wren CLI</a></li>
|
||||
<li><a href="../">Back to CLI Modules</a></li>
|
||||
<li><a href="./">os module</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>os classes</h2>
|
||||
<ul>
|
||||
<li><a href="platform.html">Platform</a></li>
|
||||
<li><a href="process.html">Process</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Back to CLI Modules</a></td>
|
||||
<td><a href="./">os module</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>os classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="platform.html">Platform</a></li>
|
||||
<li><a href="process.html">Process</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Platform Class</h1>
|
||||
<p>The Platform class exposes basic information about the operating system Wren is
|
||||
running on top of.</p>
|
||||
<h2>Static Methods <a href="#static-methods" name="static-methods" class="header-anchor">#</a></h2>
|
||||
<h3><strong>name</strong> <a href="#name" name="name" class="header-anchor">#</a></h3>
|
||||
<p>The name of the platform. This roughly describes the operating system, and is
|
||||
usually one of:</p>
|
||||
<ul>
|
||||
<li>“iOS”</li>
|
||||
<li>“Linux”</li>
|
||||
<li>“OS X”</li>
|
||||
<li>“POSIX”</li>
|
||||
<li>“Unix”</li>
|
||||
<li>“Windows”</li>
|
||||
</ul>
|
||||
<p>If Wren was compiled for an unknown operating system, returns “Unknown”.</p>
|
||||
<h3><strong>isPosix</strong> <a href="#isposix" name="isposix" class="header-anchor">#</a></h3>
|
||||
<p>Returns <code>true</code> if the host operating system is known to support the POSIX
|
||||
standard. This is true for Linux and other Unices, as well as the various Apple
|
||||
operating systems.</p>
|
||||
<h3><strong>isWindows</strong> <a href="#iswindows" name="iswindows" class="header-anchor">#</a></h3>
|
||||
<p>Returns <code>true</code> if the host operating system is some flavor of Windows.</p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
106
cli/modules/os/process.html
Normal file
106
cli/modules/os/process.html
Normal file
@ -0,0 +1,106 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Process Class – Wren</title>
|
||||
<script type="application/javascript" src="../../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../../">Back to Wren CLI</a></li>
|
||||
<li><a href="../">Back to CLI Modules</a></li>
|
||||
<li><a href="./">os module</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>os classes</h2>
|
||||
<ul>
|
||||
<li><a href="platform.html">Platform</a></li>
|
||||
<li><a href="process.html">Process</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Back to CLI Modules</a></td>
|
||||
<td><a href="./">os module</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>os classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="platform.html">Platform</a></li>
|
||||
<li><a href="process.html">Process</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Process Class</h1>
|
||||
<p>The Process class lets you work with operating system processes, including the
|
||||
currently running one.</p>
|
||||
<h2>Static Methods <a href="#static-methods" name="static-methods" class="header-anchor">#</a></h2>
|
||||
<h3><strong>allArguments</strong> <a href="#allarguments" name="allarguments" class="header-anchor">#</a></h3>
|
||||
<p>The list of command-line arguments that were passed when the Wren process was
|
||||
spawned. This includes the Wren executable itself, the path to the file being
|
||||
run (if any), and any other options passed to Wren itself.</p>
|
||||
<p>If you run:</p>
|
||||
<pre><code>$ wren file.wren arg
|
||||
</code></pre>
|
||||
<p>This returns:</p>
|
||||
<pre class="snippet">
|
||||
System.print(Process.allArguments) //> ["wren", "file.wren", "arg"]
|
||||
</pre>
|
||||
|
||||
<h3><strong>arguments</strong> <a href="#arguments" name="arguments" class="header-anchor">#</a></h3>
|
||||
<p>The list of command-line arguments that were passed to your program when the
|
||||
Wren process was spawned. This does not include arguments handled by Wren
|
||||
itself.</p>
|
||||
<p>If you run:</p>
|
||||
<pre><code>$ wren file.wren arg
|
||||
</code></pre>
|
||||
<p>This returns:</p>
|
||||
<pre class="snippet">
|
||||
System.print(Process.arguments) //> ["arg"]
|
||||
</pre>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
83
cli/modules/scheduler/index.html
Normal file
83
cli/modules/scheduler/index.html
Normal file
@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Module "scheduler" – Wren</title>
|
||||
<script type="application/javascript" src="../../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../../">Back to Wren CLI</a></li>
|
||||
<li><a href="../">Back to CLI Modules</a></li>
|
||||
<li><a href="./">scheduler module</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>scheduler classes</h2>
|
||||
<ul>
|
||||
<li><a href="scheduler.html">Scheduler</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Back to CLI Modules</a></td>
|
||||
<td><a href="./">scheduler module</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>scheduler classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="scheduler.html">Scheduler</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Module "scheduler"</h1>
|
||||
<p>This module provides a vehicle to allow other operations to be performed asynchronously whilst waiting for the main operation to be completed.</p>
|
||||
<p>It contains a single class:</p>
|
||||
<ul>
|
||||
<li><a href="scheduler.html">Scheduler</a></li>
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
98
cli/modules/scheduler/scheduler.html
Normal file
98
cli/modules/scheduler/scheduler.html
Normal file
@ -0,0 +1,98 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Scheduler Class – Wren</title>
|
||||
<script type="application/javascript" src="../../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../../">Back to Wren CLI</a></li>
|
||||
<li><a href="../">Back to CLI Modules</a></li>
|
||||
<li><a href="./">scheduler module</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>scheduler classes</h2>
|
||||
<ul>
|
||||
<li><a href="scheduler.html">Scheduler</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Back to CLI Modules</a></td>
|
||||
<td><a href="./">scheduler module</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>scheduler classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="scheduler.html">Scheduler</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Scheduler Class</h1>
|
||||
<p>The Scheduler class maintains a list of fibers, to be started one after the other, when a signal to do so is received. The signal (a private method call) is typically transmitted by <em>long running</em> methods in the File or Timer classes which suspend the current fiber so that Wren can carry out other tasks in the meantime.</p>
|
||||
<h2>Static Method <a href="#static-method" name="static-method" class="header-anchor">#</a></h2>
|
||||
<h3>Scheduler.<strong>add</strong>(callable) <a href="#scheduler.add(callable)" name="scheduler.add(callable)" class="header-anchor">#</a></h3>
|
||||
<p>Adds a new fiber to the scheduler’s fibers list. This fiber calls <code>callable</code> and then transfers to the next fiber in the list, if there is one.</p>
|
||||
<p><code>callable</code> is a function or other object which has a call() method.</p>
|
||||
<pre class="snippet">
|
||||
var a = 3
|
||||
|
||||
Scheduler.add {
|
||||
a = a * a
|
||||
}
|
||||
|
||||
Scheduler.add {
|
||||
a = a + 1
|
||||
}
|
||||
|
||||
System.print(a) // still 3
|
||||
Timer.sleep(3000) // wait 3 seconds
|
||||
System.print(a) // now 3 * 3 + 1 = 10
|
||||
</pre>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
83
cli/modules/timer/index.html
Normal file
83
cli/modules/timer/index.html
Normal file
@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Module "timer" – Wren</title>
|
||||
<script type="application/javascript" src="../../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../../">Back to Wren CLI</a></li>
|
||||
<li><a href="../">Back to CLI Modules</a></li>
|
||||
<li><a href="./">timer module</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>timer classes</h2>
|
||||
<ul>
|
||||
<li><a href="timer.html">Timer</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Back to CLI Modules</a></td>
|
||||
<td><a href="./">timer module</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>timer classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="timer.html">Timer</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Module "timer"</h1>
|
||||
<p>This module provides a mechanism to suspend the current fiber for a given period of time either as a simple delay or to allow other operations to be performed asynchronously in the meantime.</p>
|
||||
<p>It contains a single class:</p>
|
||||
<ul>
|
||||
<li><a href="timer.html">Timer</a></li>
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
83
cli/modules/timer/timer.html
Normal file
83
cli/modules/timer/timer.html
Normal file
@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Timer Class – Wren</title>
|
||||
<script type="application/javascript" src="../../../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../../../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../../../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="module">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../../../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../../../"><img src="../../../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../../">Back to Wren CLI</a></li>
|
||||
<li><a href="../">Back to CLI Modules</a></li>
|
||||
<li><a href="./">timer module</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>timer classes</h2>
|
||||
<ul>
|
||||
<li><a href="timer.html">Timer</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="../">Back to CLI Modules</a></td>
|
||||
<td><a href="./">timer module</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>timer classes</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="timer.html">Timer</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Timer Class</h1>
|
||||
<h2>Static Method <a href="#static-method" name="static-method" class="header-anchor">#</a></h2>
|
||||
<h3>Timer.<strong>sleep</strong>(milliseconds) <a href="#timer.sleep(milliseconds)" name="timer.sleep(milliseconds)" class="header-anchor">#</a></h3>
|
||||
<p>Suspends the current fiber for the given number of milliseconds. It is a runtime error if this is not a non-negative number.</p>
|
||||
<p>This method is often used in conjunction with the Scheduler class which runs any scheduled tasks in separate fibers whilst the current fiber is sleeping.</p>
|
||||
<p>Note that this method also suspends the System.clock method which will not give the correct running time for a program as a result.</p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
131
cli/usage.html
Normal file
131
cli/usage.html
Normal file
@ -0,0 +1,131 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Wren CLI Usage – Wren</title>
|
||||
<script type="application/javascript" src="../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../"><img src="../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../">Back to Wren</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>Wren CLI</h2>
|
||||
<ul>
|
||||
<li><a href="./">About</a></li>
|
||||
<li><a target="_blank" href="https://github.com/wren-lang/wren-cli/releases">Downloads</a></li>
|
||||
<li><a href="usage.html">Usage</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>API docs</h2>
|
||||
<ul>
|
||||
<li><a href="modules">CLI Modules</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<div><a href="../">Back to Wren</a></div>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><h2>CLI</h2></td>
|
||||
<td><h2>API</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="./">About</a></li>
|
||||
<li><a target="_blank" href="https://github.com/wren-lang/wren-cli/releases">Downloads</a></li>
|
||||
<li><a href="usage.html">Usage</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="modules">CLI Modules</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>Wren CLI Usage</h2>
|
||||
<hr />
|
||||
<p>You can <a href="https://github.com/wren-lang/wren-cli/releases">download a build for your OS from the releases page</a>.</p>
|
||||
<h3>Interactive mode <a href="#interactive-mode" name="interactive-mode" class="header-anchor">#</a></h3>
|
||||
<p>If you just run <code>wren_cli</code> without any arguments, it starts the interpreter in
|
||||
interactive mode, where you can type in a line of code, and it immediately executes
|
||||
it. You can exit the interpreter using good old Ctrl-C or Ctrl-D.</p>
|
||||
<p>Here’s something to try:</p>
|
||||
<pre class="snippet">
|
||||
System.print("Hello, world!")
|
||||
</pre>
|
||||
|
||||
<p>Or a little more exciting:</p>
|
||||
<pre class="snippet">
|
||||
for (i in 1..10) System.print("Counting up %(i)")
|
||||
</pre>
|
||||
|
||||
<h3>Running scripts <a href="#running-scripts" name="running-scripts" class="header-anchor">#</a></h3>
|
||||
<p>The standalone interpreter can also load scripts from files and run them. Just
|
||||
pass the name of the script to <code>wren_cli</code>. Create a file named “my_script.wren” in
|
||||
your favorite text editor and paste this into it:</p>
|
||||
<pre class="snippet">
|
||||
for (yPixel in 0...24) {
|
||||
var y = yPixel / 12 - 1
|
||||
for (xPixel in 0...80) {
|
||||
var x = xPixel / 30 - 2
|
||||
var x0 = x
|
||||
var y0 = y
|
||||
var iter = 0
|
||||
while (iter < 11 && x0 * x0 + y0 * y0 <= 4) {
|
||||
var x1 = (x0 * x0) - (y0 * y0) + x
|
||||
var y1 = 2 * x0 * y0 + y
|
||||
x0 = x1
|
||||
y0 = y1
|
||||
iter = iter + 1
|
||||
}
|
||||
System.write(" .-:;+=xX$& "[iter])
|
||||
}
|
||||
System.print("")
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Now run:</p>
|
||||
<pre><code>$ ./wren_cli my_script.wren
|
||||
</code></pre>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
51
codejar-linenumbers.js
Normal file
51
codejar-linenumbers.js
Normal file
@ -0,0 +1,51 @@
|
||||
function withLineNumbers(highlight, options = {}) {
|
||||
const opts = Object.assign({ class: "codejar-linenumbers", wrapClass: "codejar-wrap", width: "35px" }, options);
|
||||
let lineNumbers;
|
||||
return function (editor) {
|
||||
highlight(editor);
|
||||
if (!lineNumbers) {
|
||||
lineNumbers = init(editor, opts);
|
||||
}
|
||||
const code = editor.textContent || "";
|
||||
const linesCount = code.replace(/\n+$/, "\n").split("\n").length + 1;
|
||||
let text = "";
|
||||
for (let i = 1; i < linesCount; i++) {
|
||||
text += `${i}\n`;
|
||||
}
|
||||
lineNumbers.innerText = text;
|
||||
};
|
||||
}
|
||||
function init(editor, opts) {
|
||||
const css = getComputedStyle(editor);
|
||||
const wrap = document.createElement("div");
|
||||
wrap.className = opts.wrapClass;
|
||||
wrap.style.position = "relative";
|
||||
const lineNumbers = document.createElement("div");
|
||||
lineNumbers.className = opts.class;
|
||||
wrap.appendChild(lineNumbers);
|
||||
// Add own styles
|
||||
lineNumbers.style.position = "absolute";
|
||||
lineNumbers.style.top = "0px";
|
||||
lineNumbers.style.left = "0px";
|
||||
lineNumbers.style.bottom = "0px";
|
||||
lineNumbers.style.width = opts.width;
|
||||
lineNumbers.style.overflow = "hidden";
|
||||
lineNumbers.style.backgroundColor = "rgba(255, 255, 255, 0.05)";
|
||||
lineNumbers.style.color = "#fff";
|
||||
lineNumbers.style.setProperty("mix-blend-mode", "difference");
|
||||
// Copy editor styles
|
||||
lineNumbers.style.fontFamily = css.fontFamily;
|
||||
lineNumbers.style.fontSize = css.fontSize;
|
||||
lineNumbers.style.lineHeight = css.lineHeight;
|
||||
lineNumbers.style.paddingTop = css.paddingTop;
|
||||
lineNumbers.style.paddingLeft = css.paddingLeft;
|
||||
lineNumbers.style.borderTopLeftRadius = css.borderTopLeftRadius;
|
||||
lineNumbers.style.borderBottomLeftRadius = css.borderBottomLeftRadius;
|
||||
// Tweak editor styles
|
||||
editor.style.paddingLeft = `calc(${opts.width} + ${lineNumbers.style.paddingLeft})`;
|
||||
editor.style.whiteSpace = "pre";
|
||||
// Swap editor with a wrap
|
||||
editor.parentNode.insertBefore(wrap, editor);
|
||||
wrap.appendChild(editor);
|
||||
return lineNumbers;
|
||||
}
|
||||
401
codejar.js
Normal file
401
codejar.js
Normal file
@ -0,0 +1,401 @@
|
||||
function CodeJar(editor, highlight, opt = {}) {
|
||||
const options = Object.assign({ tab: "\t" }, opt);
|
||||
let listeners = [];
|
||||
let history = [];
|
||||
let at = -1;
|
||||
let focus = false;
|
||||
let callback;
|
||||
let prev; // code content prior keydown event
|
||||
let isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
|
||||
editor.setAttribute("contentEditable", isFirefox ? "true" : "plaintext-only");
|
||||
editor.setAttribute("spellcheck", "false");
|
||||
editor.style.outline = "none";
|
||||
editor.style.overflowWrap = "break-word";
|
||||
editor.style.overflowY = "auto";
|
||||
editor.style.resize = "vertical";
|
||||
editor.style.whiteSpace = "pre-wrap";
|
||||
highlight(editor);
|
||||
const debounceHighlight = debounce(() => {
|
||||
const pos = save();
|
||||
highlight(editor);
|
||||
restore(pos);
|
||||
}, 30);
|
||||
let recording = false;
|
||||
const shouldRecord = (event) => {
|
||||
return !isUndo(event) && !isRedo(event)
|
||||
&& event.key !== "Meta"
|
||||
&& event.key !== "Control"
|
||||
&& event.key !== "Alt"
|
||||
&& !event.key.startsWith("Arrow");
|
||||
};
|
||||
const debounceRecordHistory = debounce((event) => {
|
||||
if (shouldRecord(event)) {
|
||||
recordHistory();
|
||||
recording = false;
|
||||
}
|
||||
}, 300);
|
||||
const on = (type, fn) => {
|
||||
listeners.push([type, fn]);
|
||||
editor.addEventListener(type, fn);
|
||||
};
|
||||
on("keydown", event => {
|
||||
if (event.defaultPrevented)
|
||||
return;
|
||||
prev = toString();
|
||||
handleNewLine(event);
|
||||
handleTabCharacters(event);
|
||||
handleJumpToBeginningOfLine(event);
|
||||
handleSelfClosingCharacters(event);
|
||||
handleUndoRedo(event);
|
||||
if (shouldRecord(event) && !recording) {
|
||||
recordHistory();
|
||||
recording = true;
|
||||
}
|
||||
});
|
||||
on("keyup", event => {
|
||||
if (event.defaultPrevented)
|
||||
return;
|
||||
if (event.isComposing)
|
||||
return;
|
||||
if (prev !== toString())
|
||||
debounceHighlight();
|
||||
debounceRecordHistory(event);
|
||||
if (callback)
|
||||
callback(toString());
|
||||
});
|
||||
on("focus", _event => {
|
||||
focus = true;
|
||||
});
|
||||
on("blur", _event => {
|
||||
focus = false;
|
||||
});
|
||||
on("paste", event => {
|
||||
recordHistory();
|
||||
handlePaste(event);
|
||||
recordHistory();
|
||||
if (callback)
|
||||
callback(toString());
|
||||
});
|
||||
function save() {
|
||||
const s = window.getSelection();
|
||||
const pos = { start: 0, end: 0, dir: undefined };
|
||||
visit(editor, el => {
|
||||
if (el === s.anchorNode && el === s.focusNode) {
|
||||
pos.start += s.anchorOffset;
|
||||
pos.end += s.focusOffset;
|
||||
pos.dir = s.anchorOffset <= s.focusOffset ? "->" : "<-";
|
||||
return "stop";
|
||||
}
|
||||
if (el === s.anchorNode) {
|
||||
pos.start += s.anchorOffset;
|
||||
if (!pos.dir) {
|
||||
pos.dir = "->";
|
||||
}
|
||||
else {
|
||||
return "stop";
|
||||
}
|
||||
}
|
||||
else if (el === s.focusNode) {
|
||||
pos.end += s.focusOffset;
|
||||
if (!pos.dir) {
|
||||
pos.dir = "<-";
|
||||
}
|
||||
else {
|
||||
return "stop";
|
||||
}
|
||||
}
|
||||
if (el.nodeType === Node.TEXT_NODE) {
|
||||
if (pos.dir != "->")
|
||||
pos.start += el.nodeValue.length;
|
||||
if (pos.dir != "<-")
|
||||
pos.end += el.nodeValue.length;
|
||||
}
|
||||
});
|
||||
return pos;
|
||||
}
|
||||
function restore(pos) {
|
||||
const s = window.getSelection();
|
||||
let startNode, startOffset = 0;
|
||||
let endNode, endOffset = 0;
|
||||
if (!pos.dir)
|
||||
pos.dir = "->";
|
||||
if (pos.start < 0)
|
||||
pos.start = 0;
|
||||
if (pos.end < 0)
|
||||
pos.end = 0;
|
||||
// Flip start and end if the direction reversed
|
||||
if (pos.dir == "<-") {
|
||||
const { start, end } = pos;
|
||||
pos.start = end;
|
||||
pos.end = start;
|
||||
}
|
||||
let current = 0;
|
||||
visit(editor, el => {
|
||||
if (el.nodeType !== Node.TEXT_NODE)
|
||||
return;
|
||||
const len = (el.nodeValue || "").length;
|
||||
if (current + len >= pos.start) {
|
||||
if (!startNode) {
|
||||
startNode = el;
|
||||
startOffset = pos.start - current;
|
||||
}
|
||||
if (current + len >= pos.end) {
|
||||
endNode = el;
|
||||
endOffset = pos.end - current;
|
||||
return "stop";
|
||||
}
|
||||
}
|
||||
current += len;
|
||||
});
|
||||
// If everything deleted place cursor at editor
|
||||
if (!startNode)
|
||||
startNode = editor;
|
||||
if (!endNode)
|
||||
endNode = editor;
|
||||
// Flip back the selection
|
||||
if (pos.dir == "<-") {
|
||||
[startNode, startOffset, endNode, endOffset] = [endNode, endOffset, startNode, startOffset];
|
||||
}
|
||||
s.setBaseAndExtent(startNode, startOffset, endNode, endOffset);
|
||||
}
|
||||
function beforeCursor() {
|
||||
const s = window.getSelection();
|
||||
const r0 = s.getRangeAt(0);
|
||||
const r = document.createRange();
|
||||
r.selectNodeContents(editor);
|
||||
r.setEnd(r0.startContainer, r0.startOffset);
|
||||
return r.toString();
|
||||
}
|
||||
function afterCursor() {
|
||||
const s = window.getSelection();
|
||||
const r0 = s.getRangeAt(0);
|
||||
const r = document.createRange();
|
||||
r.selectNodeContents(editor);
|
||||
r.setStart(r0.endContainer, r0.endOffset);
|
||||
return r.toString();
|
||||
}
|
||||
function handleNewLine(event) {
|
||||
if (event.key === "Enter") {
|
||||
const before = beforeCursor();
|
||||
const after = afterCursor();
|
||||
let [padding] = findPadding(before);
|
||||
let newLinePadding = padding;
|
||||
// If last symbol is "{" ident new line
|
||||
if (before[before.length - 1] === "{") {
|
||||
newLinePadding += options.tab;
|
||||
}
|
||||
if (isFirefox) {
|
||||
preventDefault(event);
|
||||
insert("\n" + newLinePadding);
|
||||
}
|
||||
else {
|
||||
// Normal browsers
|
||||
if (newLinePadding.length > 0) {
|
||||
preventDefault(event);
|
||||
insert("\n" + newLinePadding);
|
||||
}
|
||||
}
|
||||
// Place adjacent "}" on next line
|
||||
if (newLinePadding !== padding && after[0] === "}") {
|
||||
const pos = save();
|
||||
insert("\n" + padding);
|
||||
restore(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleSelfClosingCharacters(event) {
|
||||
const open = `([{'"`;
|
||||
const close = `)]}'"`;
|
||||
const codeAfter = afterCursor();
|
||||
if (close.includes(event.key) && codeAfter.substr(0, 1) === event.key) {
|
||||
const pos = save();
|
||||
preventDefault(event);
|
||||
pos.start = ++pos.end;
|
||||
restore(pos);
|
||||
}
|
||||
else if (open.includes(event.key)) {
|
||||
const pos = save();
|
||||
preventDefault(event);
|
||||
const text = event.key + close[open.indexOf(event.key)];
|
||||
insert(text);
|
||||
pos.start = ++pos.end;
|
||||
restore(pos);
|
||||
}
|
||||
}
|
||||
function handleTabCharacters(event) {
|
||||
if (event.key === "Tab") {
|
||||
preventDefault(event);
|
||||
if (event.shiftKey) {
|
||||
const before = beforeCursor();
|
||||
let [padding, start,] = findPadding(before);
|
||||
if (padding.length > 0) {
|
||||
const pos = save();
|
||||
// Remove full length tab or just remaining padding
|
||||
const len = Math.min(options.tab.length, padding.length);
|
||||
restore({ start, end: start + len });
|
||||
document.execCommand("delete");
|
||||
pos.start -= len;
|
||||
pos.end -= len;
|
||||
restore(pos);
|
||||
}
|
||||
}
|
||||
else {
|
||||
insert(options.tab);
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleJumpToBeginningOfLine(event) {
|
||||
if (event.key === "ArrowLeft" && event.metaKey) {
|
||||
preventDefault(event);
|
||||
const before = beforeCursor();
|
||||
let [padding, start, end] = findPadding(before);
|
||||
if (before.endsWith(padding)) {
|
||||
if (event.shiftKey) {
|
||||
const pos = save();
|
||||
restore({ start, end: pos.end }); // Select from line start.
|
||||
}
|
||||
else {
|
||||
restore({ start, end: start }); // Jump to line start.
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (event.shiftKey) {
|
||||
const pos = save();
|
||||
restore({ start: end, end: pos.end }); // Select from beginning of text.
|
||||
}
|
||||
else {
|
||||
restore({ start: end, end }); // Jump to beginning of text.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleUndoRedo(event) {
|
||||
if (isUndo(event)) {
|
||||
preventDefault(event);
|
||||
at--;
|
||||
const record = history[at];
|
||||
if (record) {
|
||||
editor.innerHTML = record.html;
|
||||
restore(record.pos);
|
||||
}
|
||||
if (at < 0)
|
||||
at = 0;
|
||||
}
|
||||
if (isRedo(event)) {
|
||||
preventDefault(event);
|
||||
at++;
|
||||
const record = history[at];
|
||||
if (record) {
|
||||
editor.innerHTML = record.html;
|
||||
restore(record.pos);
|
||||
}
|
||||
if (at >= history.length)
|
||||
at--;
|
||||
}
|
||||
}
|
||||
function recordHistory() {
|
||||
if (!focus)
|
||||
return;
|
||||
const html = editor.innerHTML;
|
||||
const pos = save();
|
||||
const lastRecord = history[at];
|
||||
if (lastRecord) {
|
||||
if (lastRecord.html === html
|
||||
&& lastRecord.pos.start === pos.start
|
||||
&& lastRecord.pos.end === pos.end)
|
||||
return;
|
||||
}
|
||||
at++;
|
||||
history[at] = { html, pos };
|
||||
history.splice(at + 1);
|
||||
const maxHistory = 300;
|
||||
if (at > maxHistory) {
|
||||
at = maxHistory;
|
||||
history.splice(0, 1);
|
||||
}
|
||||
}
|
||||
function handlePaste(event) {
|
||||
preventDefault(event);
|
||||
const text = (event.originalEvent || event).clipboardData.getData("text/plain");
|
||||
const pos = save();
|
||||
insert(text);
|
||||
highlight(editor);
|
||||
restore({ start: pos.end + text.length, end: pos.end + text.length });
|
||||
}
|
||||
function visit(editor, visitor) {
|
||||
const queue = [];
|
||||
if (editor.firstChild)
|
||||
queue.push(editor.firstChild);
|
||||
let el = queue.pop();
|
||||
while (el) {
|
||||
if (visitor(el) === "stop")
|
||||
break;
|
||||
if (el.nextSibling)
|
||||
queue.push(el.nextSibling);
|
||||
if (el.firstChild)
|
||||
queue.push(el.firstChild);
|
||||
el = queue.pop();
|
||||
}
|
||||
}
|
||||
function isCtrl(event) {
|
||||
return event.metaKey || event.ctrlKey;
|
||||
}
|
||||
function isUndo(event) {
|
||||
return isCtrl(event) && !event.shiftKey && event.code === "KeyZ";
|
||||
}
|
||||
function isRedo(event) {
|
||||
return isCtrl(event) && event.shiftKey && event.code === "KeyZ";
|
||||
}
|
||||
function insert(text) {
|
||||
text = text
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
document.execCommand("insertHTML", false, text);
|
||||
}
|
||||
function debounce(cb, wait) {
|
||||
let timeout = 0;
|
||||
return (...args) => {
|
||||
clearTimeout(timeout);
|
||||
timeout = window.setTimeout(() => cb(...args), wait);
|
||||
};
|
||||
}
|
||||
function findPadding(text) {
|
||||
// Find beginning of previous line.
|
||||
let i = text.length - 1;
|
||||
while (i >= 0 && text[i] !== "\n")
|
||||
i--;
|
||||
i++;
|
||||
// Find padding of the line.
|
||||
let j = i;
|
||||
while (j < text.length && /[ \t]/.test(text[j]))
|
||||
j++;
|
||||
return [text.substring(i, j) || "", i, j];
|
||||
}
|
||||
function toString() {
|
||||
return editor.textContent || "";
|
||||
}
|
||||
function preventDefault(event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
return {
|
||||
updateOptions(options) {
|
||||
options = Object.assign(Object.assign({}, options), options);
|
||||
},
|
||||
updateCode(code) {
|
||||
editor.textContent = code;
|
||||
highlight(editor);
|
||||
},
|
||||
onUpdate(cb) {
|
||||
callback = cb;
|
||||
},
|
||||
toString,
|
||||
destroy() {
|
||||
for (let [type, fn] of listeners) {
|
||||
editor.removeEventListener(type, fn);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
287
concurrency.html
Normal file
287
concurrency.html
Normal file
@ -0,0 +1,287 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Concurrency – Wren</title>
|
||||
<script type="application/javascript" src="prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="codejar.js"></script>
|
||||
<script type="application/javascript" src="wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="./">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="./"><img src="./wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="contributing.html">Contributing</a></li>
|
||||
<li><a href="blog">Blog</a></li>
|
||||
<li><a href="try">Try it!</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>guides</h2>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>API docs</h2>
|
||||
<ul>
|
||||
<li><a href="modules">Modules</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>reference</h2>
|
||||
<ul>
|
||||
<li><a href="cli">Wren CLI</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<div><a href="getting-started.html">Getting Started</a></div>
|
||||
<div><a href="contributing.html">Contributing</a></div>
|
||||
<div><a href="blog">Blog</a></div>
|
||||
<div><a href="try">Try it!</a></div>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>guides</h2></td>
|
||||
<td><h2>reference</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="modules">API/Modules</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>Concurrency</h2>
|
||||
<p>Lightweight concurrency is a key feature of Wren and it is expressed using
|
||||
<em>fibers</em>. They control how all code is executed, and take the place of
|
||||
exceptions in <a href="error-handling.html">error handling</a>.</p>
|
||||
<p>Fibers are a bit like threads except they are <em>cooperatively</em> scheduled. That
|
||||
means Wren doesn’t pause one fiber and switch to another until you tell it to.
|
||||
You don’t have to worry about context switches at random times and all of the
|
||||
headaches those cause.</p>
|
||||
<p>Wren takes care of all of the fibers in the VM, so they don’t use OS thread
|
||||
resources, or require heavyweight context switches. Each just needs a bit of
|
||||
memory for its stack. A fiber will get garbage collected like any other object
|
||||
when not referenced any more, so you can create them freely.</p>
|
||||
<p>They are lightweight enough that you can, for example, have a separate fiber for
|
||||
each entity in a game. Wren can handle thousands of them without breaking a
|
||||
sweat. For example, when you run Wren in interactive mode, it creates a new
|
||||
fiber for every line of code you type in.</p>
|
||||
<h2>Creating fibers <a href="#creating-fibers" name="creating-fibers" class="header-anchor">#</a></h2>
|
||||
<p>All Wren code runs within the context of a fiber. When you first start a Wren
|
||||
script, a main fiber is created for you automatically. You can spawn new fibers
|
||||
using the Fiber class’s constructor:</p>
|
||||
<pre class="snippet">
|
||||
var fiber = Fiber.new {
|
||||
System.print("This runs in a separate fiber.")
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>It takes a <a href="functions.html">function</a> containing the code the fiber should execute. The
|
||||
function can take zero or one parameter, but no more than that. Creating the
|
||||
fiber does not immediately run it. It just wraps the function and sits there,
|
||||
waiting to be activated.</p>
|
||||
<h2>Invoking fibers <a href="#invoking-fibers" name="invoking-fibers" class="header-anchor">#</a></h2>
|
||||
<p>Once you’ve created a fiber, you run it by calling its <code>call()</code> method:</p>
|
||||
<pre class="snippet">
|
||||
fiber.call()
|
||||
</pre>
|
||||
|
||||
<p>This suspends the current fiber and executes the called one until it reaches the
|
||||
end of its body or until it passes control to yet another fiber. If it reaches
|
||||
the end of its body, it is considered <em>done</em>:</p>
|
||||
<pre class="snippet">
|
||||
var fiber = Fiber.new {
|
||||
System.print("It's alive!")
|
||||
}
|
||||
|
||||
System.print(fiber.isDone) //> false
|
||||
fiber.call() //> It's alive!
|
||||
System.print(fiber.isDone) //> true
|
||||
</pre>
|
||||
|
||||
<p>When a called fiber finishes, it automatically passes control <em>back</em> to the
|
||||
fiber that called it. It’s a runtime error to try to call a fiber that is
|
||||
already done.</p>
|
||||
<h2>Yielding <a href="#yielding" name="yielding" class="header-anchor">#</a></h2>
|
||||
<p>The main difference between fibers and functions is that a fiber can be
|
||||
suspended in the middle of its operation and then resumed later. Calling
|
||||
another fiber is one way to suspend a fiber, but that’s more or less the same
|
||||
as one function calling another.</p>
|
||||
<p>Things get interesting when a fiber <em>yields</em>. A yielded fiber passes control
|
||||
<em>back</em> to the fiber that ran it, but <em>remembers where it is</em>. The next time the
|
||||
fiber is called, it picks up right where it left off and keeps going.</p>
|
||||
<p>You make a fiber yield by calling the static <code>yield()</code> method on Fiber:</p>
|
||||
<pre class="snippet">
|
||||
var fiber = Fiber.new {
|
||||
System.print("Before yield")
|
||||
Fiber.yield()
|
||||
System.print("Resumed")
|
||||
}
|
||||
|
||||
System.print("Before call") //> Before call
|
||||
fiber.call() //> Before yield
|
||||
System.print("Calling again") //> Calling again
|
||||
fiber.call() //> Resumed
|
||||
System.print("All done") //> All done
|
||||
</pre>
|
||||
|
||||
<p>Note that even though this program uses <em>concurrency</em>, it is still
|
||||
<em>deterministic</em>. You can reason precisely about what it’s doing and aren’t at
|
||||
the mercy of a thread scheduler playing Russian roulette with your code.</p>
|
||||
<h2>Passing values <a href="#passing-values" name="passing-values" class="header-anchor">#</a></h2>
|
||||
<p>Calling and yielding fibers is used for passing control, but it can also pass
|
||||
<em>data</em>. When you call a fiber, you can optionally pass a value to it.</p>
|
||||
<p>If you create a fiber using a function that takes a parameter, you can pass a
|
||||
value to it through <code>call()</code>:</p>
|
||||
<pre class="snippet">
|
||||
var fiber = Fiber.new {|param|
|
||||
System.print(param)
|
||||
}
|
||||
|
||||
fiber.call("Here you go") //> Here you go
|
||||
</pre>
|
||||
|
||||
<p>If the fiber has yielded and is waiting to resume, the value you pass to call
|
||||
becomes the return value of the <code>yield()</code> call when it resumes:</p>
|
||||
<pre class="snippet">
|
||||
var fiber = Fiber.new {|param|
|
||||
System.print(param)
|
||||
var result = Fiber.yield()
|
||||
System.print(result)
|
||||
}
|
||||
|
||||
fiber.call("First") //> First
|
||||
fiber.call("Second") //> Second
|
||||
</pre>
|
||||
|
||||
<p>Fibers can also pass values <em>back</em> when they yield. If you pass an argument to
|
||||
<code>yield()</code>, that will become the return value of the <code>call()</code> that was used to
|
||||
invoke the fiber:</p>
|
||||
<pre class="snippet">
|
||||
var fiber = Fiber.new {
|
||||
Fiber.yield("Reply")
|
||||
}
|
||||
|
||||
System.print(fiber.call()) //> Reply
|
||||
</pre>
|
||||
|
||||
<p>This is sort of like how a function call may return a value, except that a fiber
|
||||
may return a whole sequence of values, one every time it yields.</p>
|
||||
<h2>Full coroutines <a href="#full-coroutines" name="full-coroutines" class="header-anchor">#</a></h2>
|
||||
<p>What we’ve seen so far is very similar to what you can do with languages like
|
||||
Python and C# that have <em>generators</em>. Those let you define a function call that
|
||||
you can suspend and resume. When using the function, it appears like a sequence
|
||||
you can iterate over.</p>
|
||||
<p>Wren’s fibers can do that, but they can do much more. Like Lua, they are full
|
||||
<em>coroutines</em>—they can suspend from anywhere in the callstack. The function
|
||||
you use to create a fiber can call a method that calls another method that calls
|
||||
some third method which finally calls yield. When that happens, <em>all</em> of those
|
||||
method calls — the entire callstack — gets suspended. For example:</p>
|
||||
<pre class="snippet">
|
||||
var fiber = Fiber.new {
|
||||
(1..10).each {|i|
|
||||
Fiber.yield(i)
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Here, we’re calling <code>yield()</code> from within a <a href="functions.html">function</a> being
|
||||
passed to the <code>each()</code> method. This works fine in Wren because that inner
|
||||
<code>yield()</code> call will suspend the call to <code>each()</code> and the function passed to it
|
||||
as a callback.</p>
|
||||
<h2>Transferring control <a href="#transferring-control" name="transferring-control" class="header-anchor">#</a></h2>
|
||||
<p>Fibers have one more trick up their sleeves. When you execute a fiber using
|
||||
<code>call()</code>, the fiber tracks which fiber it will return to when it yields. This
|
||||
lets you build up a chain of fiber calls that will eventually unwind back to
|
||||
the main fiber when all of the called ones yield or finish.</p>
|
||||
<p>This is usually what you want. But if you’re doing something low level, like
|
||||
writing your own scheduler to manage a pool of fibers, you may not want to treat
|
||||
them explicitly like a stack.</p>
|
||||
<p>For rare cases like that, fibers also have a <code>transfer()</code> method. This switches
|
||||
execution to the transferred fiber and “forgets” the fiber that was transferred
|
||||
<em>from</em>. The previous one is suspended, leaving it in whatever state it was in.
|
||||
You can resume the previous fiber by explicitly transferring back to it, or even
|
||||
calling it. If you don’t, execution stops when the last transferred fiber
|
||||
returns.</p>
|
||||
<p>Where <code>call()</code> and <code>yield()</code> are analogous to calling and returning from
|
||||
functions, <code>transfer()</code> works more like an unstructured goto. It lets you freely
|
||||
switch control between a number of fibers, all of which act as peers to one
|
||||
another.</p>
|
||||
<p><br><hr>
|
||||
<a class="right" href="error-handling.html">Error Handling →</a>
|
||||
<a href="classes.html">← Classes</a></p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
221
contributing.html
Normal file
221
contributing.html
Normal file
@ -0,0 +1,221 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Contributing – Wren</title>
|
||||
<script type="application/javascript" src="prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="codejar.js"></script>
|
||||
<script type="application/javascript" src="wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="./">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="./"><img src="./wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="contributing.html">Contributing</a></li>
|
||||
<li><a href="blog">Blog</a></li>
|
||||
<li><a href="try">Try it!</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>guides</h2>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>API docs</h2>
|
||||
<ul>
|
||||
<li><a href="modules">Modules</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>reference</h2>
|
||||
<ul>
|
||||
<li><a href="cli">Wren CLI</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<div><a href="getting-started.html">Getting Started</a></div>
|
||||
<div><a href="contributing.html">Contributing</a></div>
|
||||
<div><a href="blog">Blog</a></div>
|
||||
<div><a href="try">Try it!</a></div>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>guides</h2></td>
|
||||
<td><h2>reference</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="modules">API/Modules</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>Contributing</h2>
|
||||
<p>Like the bird, Wren’s ecosystem is small but full of life. Almost everything is
|
||||
under active development and there’s lots to do. We’d be delighted to have you
|
||||
help.</p>
|
||||
<p>The first thing to do is to join <a href="https://discord.gg/Kx6PxSX">the discord community</a> (or <a href="https://groups.google.com/forum/#!forum/wren-lang">the mailing list</a>) and say,
|
||||
“Hi”. There are no strangers to Wren, just friends we haven’t met yet.</p>
|
||||
<h2>Growing the ecosystem <a href="#growing-the-ecosystem" name="growing-the-ecosystem" class="header-anchor">#</a></h2>
|
||||
<p>The simplest and often most helpful way to join the Wren party is to be a Wren
|
||||
<em>user</em>. Create an application that embeds Wren. Write a library or a handy
|
||||
utility in Wren. Add syntax highlighting support for Wren to your favorite text
|
||||
editor. Share that stuff and it will help the next Wren user to come along.</p>
|
||||
<p>If you do any of the above, let us know by adding it to <a href="https://github.com/wren-lang/wren/wiki">the wiki</a>. <br />
|
||||
We like to keep track of:</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/wren-lang/wren/wiki/Applications">Applications</a> that host Wren as a scripting language.</li>
|
||||
<li><a href="https://github.com/wren-lang/wren/wiki/Modules">Modules</a> written in Wren that others can use.</li>
|
||||
<li><a href="https://github.com/wren-lang/wren/wiki/Language-Bindings">Language bindings</a> that let you interact with Wren from other
|
||||
languages.</li>
|
||||
<li><a href="https://github.com/wren-lang/wren/wiki/Tools">Tools and utilities</a> that make it easier to be a Wren programmer.</li>
|
||||
</ul>
|
||||
<h2>Contributing to Wren <a href="#contributing-to-wren" name="contributing-to-wren" class="header-anchor">#</a></h2>
|
||||
<p>You’re also more than welcome to contribute to Wren itself, both the core VM and
|
||||
the command-line interpreter. The source is developed <a href="https://github.com/wren-lang/">on GitHub</a>. Our
|
||||
hope is that the codebase, tests, and <a href="https://github.com/wren-lang/wren/tree/main/doc/site">documentation</a> are easy to
|
||||
understand and contribute to. If they aren’t, that’s a bug.</p>
|
||||
<p>You can learn how to build wren on the <a href="getting-started.html#building-wren">getting started page</a>.</p>
|
||||
<h3>Finding something to hack on <a href="#finding-something-to-hack-on" name="finding-something-to-hack-on" class="header-anchor">#</a></h3>
|
||||
<p>Between the <a href="https://github.com/wren-lang/wren/issues">issue tracker</a> and searching for <code>TODO</code> comments in the
|
||||
code, it’s pretty easy to find something that needs doing, though we don’t
|
||||
always do a good job of writing everything down.</p>
|
||||
<p>If nothing there suits your fancy, new ideas are welcome as well! If you have an
|
||||
idea for a significant change or addition, please file a <a href="https://github.com/wren-lang/wren/labels/proposal">proposal</a> to discuss
|
||||
it before writing lots of code. Wren tries very <em>very</em> hard to be minimal which
|
||||
means often having to say “no” to language additions, even really cool ones.</p>
|
||||
<h3>Hacking on docs <a href="#hacking-on-docs" name="hacking-on-docs" class="header-anchor">#</a></h3>
|
||||
<p>The <a href="/">documentation</a> is one of the easiest—and most
|
||||
important!—parts of Wren to contribute to. The source for the site is
|
||||
written in <a href="http://daringfireball.net/projects/markdown/">Markdown</a> and lives under <code>doc/site</code>. A
|
||||
simple Python 3 script, <code>util/generate_docs.py</code>, converts that to HTML and CSS.</p>
|
||||
<pre><code>$ python util/generate_docs.py
|
||||
</code></pre>
|
||||
<p>This generates the site in <code>build/docs/</code>. You can run any simple static web
|
||||
server from there. Python includes one:</p>
|
||||
<pre><code>$ cd build/docs
|
||||
$ python -m http.server
|
||||
</code></pre>
|
||||
<p>Running that script every time you change a line of Markdown can be slow,
|
||||
so there is also a file watching version that will automatically regenerate the
|
||||
docs when you edit a file:</p>
|
||||
<pre><code>$ python util/generate_docs.py --watch
|
||||
</code></pre>
|
||||
<h3>Hacking on the VM <a href="#hacking-on-the-vm" name="hacking-on-the-vm" class="header-anchor">#</a></h3>
|
||||
<p>The basic process is simple:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><strong>Make sure you can build and run the tests locally.</strong> It’s good to ensure
|
||||
you’re starting from a happy place before you poke at the code. Running the
|
||||
tests is as simple as <a href="getting-started.html#building-wren">building the vm project</a>,
|
||||
which generates <code>bin/wren_test</code> and then running the following python 3 script:</p>
|
||||
<pre><code>$ python util/test.py
|
||||
</code></pre>
|
||||
<p>If there are no failures, you’re good to go.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong><a href="https://help.github.com/articles/fork-a-repo/">Fork the repo</a> so you can change it locally.</strong> Please make your
|
||||
changes in separate <a href="https://www.atlassian.com/git/tutorials/comparing-workflows/centralized-workflow">feature branches</a> to make things a little easier.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Change the code.</strong> Please follow the style of the surrounding code. That
|
||||
basically means <code>camelCase</code> names, <code>{</code> on the next line, keep within 80
|
||||
columns, and two spaces of indentation. If you see places where the existing
|
||||
code is inconsistent, let us know.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Write some tests for your new functionality.</strong> They live under <code>test/</code>.
|
||||
Take a look at some existing tests to get an idea of how to define
|
||||
expectations.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Make sure the tests all pass, both the old ones and your new ones.</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Add your name and email to the <a href="https://github.com/wren-lang/wren/tree/main/AUTHORS">AUTHORS</a> file if you haven’t already.</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Send a <a href="https://github.com/wren-lang/wren/pulls">pull request</a>.</strong> Pat yourself on the back for contributing to a
|
||||
fun open source project! </p>
|
||||
</li>
|
||||
</ol>
|
||||
<h2>Getting help <a href="#getting-help" name="getting-help" class="header-anchor">#</a></h2>
|
||||
<p>If at any point you have questions, feel free to <a href="https://github.com/wren-lang/wren/issues">file an issue</a> or ask
|
||||
on the <a href="https://discord.gg/Kx6PxSX">discord community</a> (or the <a href="https://groups.google.com/forum/#!forum/wren-lang">mailing list</a>). If you’re a Redditor, try the
|
||||
<a href="https://www.reddit.com/r/wren_lang/">/r/wren_lang</a> subreddit. You can also email me directly (<code>robert</code> at
|
||||
<code>stuffwithstuff.com</code>) if you want something less public.</p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
361
control-flow.html
Normal file
361
control-flow.html
Normal file
@ -0,0 +1,361 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Control Flow – Wren</title>
|
||||
<script type="application/javascript" src="prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="codejar.js"></script>
|
||||
<script type="application/javascript" src="wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="./">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="./"><img src="./wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="contributing.html">Contributing</a></li>
|
||||
<li><a href="blog">Blog</a></li>
|
||||
<li><a href="try">Try it!</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>guides</h2>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>API docs</h2>
|
||||
<ul>
|
||||
<li><a href="modules">Modules</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>reference</h2>
|
||||
<ul>
|
||||
<li><a href="cli">Wren CLI</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<div><a href="getting-started.html">Getting Started</a></div>
|
||||
<div><a href="contributing.html">Contributing</a></div>
|
||||
<div><a href="blog">Blog</a></div>
|
||||
<div><a href="try">Try it!</a></div>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>guides</h2></td>
|
||||
<td><h2>reference</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="modules">API/Modules</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>Control Flow</h2>
|
||||
<p>Control flow is used to determine which chunks of code are executed and how many
|
||||
times. <em>Branching</em> statements and expressions decide whether or not to execute
|
||||
some code and <em>looping</em> ones execute something more than once.</p>
|
||||
<h2>Truth <a href="#truth" name="truth" class="header-anchor">#</a></h2>
|
||||
<p>All control flow is based on <em>deciding</em> whether or not to do something. This
|
||||
decision depends on some expression’s value. We take the entire universe of
|
||||
possible objects 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 do one thing. Otherwise, we do something else.</p>
|
||||
<p>Obviously, the boolean <code>true</code> is in the “true” bucket and <code>false</code> 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:</p>
|
||||
<ul>
|
||||
<li>The boolean value <code>false</code> is false.</li>
|
||||
<li>The null value <code>null</code> is false.</li>
|
||||
<li>Everything else is true.</li>
|
||||
</ul>
|
||||
<p>This means <code>0</code>, empty strings, and empty collections are all considered “true”
|
||||
values.</p>
|
||||
<h2>If statements <a href="#if-statements" name="if-statements" class="header-anchor">#</a></h2>
|
||||
<p>The simplest branching statement, <code>if</code> lets you conditionally skip a chunk of
|
||||
code. It looks like this:</p>
|
||||
<pre class="snippet">
|
||||
if (ready) System.print("go!")
|
||||
</pre>
|
||||
|
||||
<p>That evaluates the parenthesized expression after <code>if</code>. If it’s true, then the
|
||||
statement after the condition is evaluated. Otherwise it is skipped. Instead of
|
||||
a statement, you can have a <a href="syntax.html#blocks">block</a>:</p>
|
||||
<pre class="snippet">
|
||||
if (ready) {
|
||||
System.print("getSet")
|
||||
System.print("go!")
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>You may also provide an <code>else</code> branch. It will be executed if the condition is
|
||||
false:</p>
|
||||
<pre class="snippet">
|
||||
if (ready) System.print("go!") else System.print("not ready!")
|
||||
</pre>
|
||||
|
||||
<p>And, of course, it can take a block too:</p>
|
||||
<pre class="snippet">
|
||||
if (ready) {
|
||||
System.print("go!")
|
||||
} else {
|
||||
System.print("not ready!")
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h2>Logical operators <a href="#logical-operators" name="logical-operators" class="header-anchor">#</a></h2>
|
||||
<p>Unlike most other <a href="method-calls.html#operators">operators</a> in Wren which are just a special syntax for
|
||||
<a href="method-calls.html">method calls</a>, the <code>&&</code> and <code>||</code> operators are special. This is because they
|
||||
only conditionally evaluate the right operand—they short-circuit.</p>
|
||||
<p>A <code>&&</code> (“logical and”) expression evaluates the left-hand argument. If it’s
|
||||
false, it returns that value. Otherwise it evaluates and returns the right-hand
|
||||
argument.</p>
|
||||
<pre class="snippet">
|
||||
System.print(false && 1) //> false
|
||||
System.print(1 && 2) //> 2
|
||||
</pre>
|
||||
|
||||
<p>A <code>||</code> (“logical or”) expression is reversed. If the left-hand argument is
|
||||
<em>true</em>, it’s returned, otherwise the right-hand argument is evaluated and
|
||||
returned:</p>
|
||||
<pre class="snippet">
|
||||
System.print(false || 1) //> 1
|
||||
System.print(1 || 2) //> 1
|
||||
</pre>
|
||||
|
||||
<h2>The conditional operator <code>?:</code> <a href="#the-conditional-operator-" name="the-conditional-operator-" class="header-anchor">#</a></h2>
|
||||
<p>Also known as the “ternary” operator since it takes three arguments, Wren has
|
||||
the little “if statement in the form of an expression” you know and love from C
|
||||
and similar languages.</p>
|
||||
<pre class="snippet">
|
||||
System.print(1 != 2 ? "math is sane" : "math is not sane!")
|
||||
</pre>
|
||||
|
||||
<p>It takes a condition expression, followed by <code>?</code>, followed by a then
|
||||
expression, a <code>:</code>, then an else expression. Just like <code>if</code>, it evaluates the
|
||||
condition. If true, it evaluates and returns the then expression. Otherwise
|
||||
it does the else expression.</p>
|
||||
<h2>While statements <a href="#while-statements" name="while-statements" class="header-anchor">#</a></h2>
|
||||
<p>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.</p>
|
||||
<p>The simplest, a <code>while</code> statement executes a chunk of code as long as a
|
||||
condition continues to hold. For example:</p>
|
||||
<pre class="snippet">
|
||||
// Hailstone sequence.
|
||||
var n = 27
|
||||
while (n != 1) {
|
||||
if (n % 2 == 0) {
|
||||
n = n / 2
|
||||
} else {
|
||||
n = 3 * n + 1
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>This evaluates the expression <code>n != 1</code>. If it is true, 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.</p>
|
||||
<p>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:</p>
|
||||
<pre class="snippet">
|
||||
var n = 27
|
||||
while (n != 1) if (n % 2 == 0) n = n / 2 else n = 3 * n + 1
|
||||
</pre>
|
||||
|
||||
<h2>For statements <a href="#for-statements" name="for-statements" class="header-anchor">#</a></h2>
|
||||
<p>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 <a href="lists.html">list</a>, a series of numbers, or some other “sequence” object.
|
||||
That’s what <code>for</code> is, uh, for. It looks like this:</p>
|
||||
<pre class="snippet">
|
||||
for (beatle in ["george", "john", "paul", "ringo"]) {
|
||||
System.print(beatle)
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>A <code>for</code> loop has three components:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>A <em>variable name</em> to bind. In the example, that’s <code>beatle</code>. Wren will create
|
||||
a new variable with that name whose scope is the body of the loop.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>A <em>sequence expression</em>. This determines what you’re looping over. It gets
|
||||
evaluated <em>once</em> before the body of the loop. In this case, it’s a list
|
||||
literal, but it can be any expression.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>A <em>body</em>. This is a curly block or a single statement. It gets executed once
|
||||
for each iteration of the loop.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<h2>Break statements <a href="#break-statements" name="break-statements" class="header-anchor">#</a></h2>
|
||||
<p>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 <code>break</code> statement. It’s just the <code>break</code>
|
||||
keyword all by itself. That immediately exits out of the nearest enclosing
|
||||
<code>while</code> or <code>for</code> loop.</p>
|
||||
<pre class="snippet">
|
||||
for (i in [1, 2, 3, 4]) {
|
||||
System.print(i) //> 1
|
||||
if (i == 3) break //> 2
|
||||
} //> 3
|
||||
</pre>
|
||||
|
||||
<h2>Continue statements <a href="#continue-statements" name="continue-statements" class="header-anchor">#</a></h2>
|
||||
<p>During the execution of a loop body, you might decide that you want to skip the
|
||||
rest of this iteration and move on to the next one. You can use a <code>continue</code>
|
||||
statement to do that. It’s just the <code>continue</code> keyword all by itself. Execution
|
||||
will immediately jump to the beginning of the next loop iteration (and check the
|
||||
loop conditions).</p>
|
||||
<pre class="snippet">
|
||||
for (i in [1, 2, 3, 4]) {
|
||||
if (i == 2) continue //> 1
|
||||
System.print(i) //> 3
|
||||
} //> 4
|
||||
</pre>
|
||||
|
||||
<h2>Numeric ranges <a href="#numeric-ranges" name="numeric-ranges" class="header-anchor">#</a></h2>
|
||||
<p>Lists are one common use for <code>for</code> loops, but sometimes you want to walk over a
|
||||
sequence of numbers, or loop a number of times. For that, you can create a
|
||||
<a href="values.html#ranges">range</a>, like so:</p>
|
||||
<pre class="snippet">
|
||||
for (i in 1..100) {
|
||||
System.print(i)
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>This loops 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:</p>
|
||||
<pre class="snippet">
|
||||
for (i in 1...100) {
|
||||
System.print(i)
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>This looks like some special “range” syntax in the <code>for</code> loop, but it’s actually
|
||||
just a pair of operators. The <code>..</code> and <code>...</code> syntax are infix “range” operators.
|
||||
Like <a href="method-calls.html#operators">other operators</a>, they are special syntax for a regular method
|
||||
call. The number type implements them and returns a <a href="values.html#ranges">range object</a> that knows
|
||||
how to iterate over a series of numbers.</p>
|
||||
<h2>The iterator protocol <a href="#the-iterator-protocol" name="the-iterator-protocol" class="header-anchor">#</a></h2>
|
||||
<p>Lists and ranges cover the two most common kinds of loops, but you should also
|
||||
be able to define your own sequences. To enable that, the semantics of <code>for</code>
|
||||
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.</p>
|
||||
<p>When you write a loop like this:</p>
|
||||
<pre class="snippet">
|
||||
for (i in 1..100) {
|
||||
System.print(i)
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Wren sees it something like this:</p>
|
||||
<pre class="snippet">
|
||||
var iter_ = null
|
||||
var seq_ = 1..100
|
||||
while (iter_ = seq_.iterate(iter_)) {
|
||||
var i = seq_.iteratorValue(iter_)
|
||||
System.print(i)
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>First, Wren evaluates the sequence expression and stores it in a hidden
|
||||
variable (written <code>seq_</code> in the example but in reality it doesn’t have a name
|
||||
you can use). It also creates a hidden “iterator” variable and initializes it
|
||||
to <code>null</code>.</p>
|
||||
<p>Each iteration, it calls <code>iterate()</code> on the sequence, passing in the current
|
||||
iterator value. (In the first iteration, it passes in <code>null</code>.) The sequence’s
|
||||
job is to take that iterator and advance it to the next element in the
|
||||
sequence. (Or, in the case where the iterator is <code>null</code>, to advance it to the
|
||||
<em>first</em> element). It then returns either the new iterator, or <code>false</code> to
|
||||
indicate that there are no more elements.</p>
|
||||
<p>If <code>false</code> 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 <code>iteratorValue()</code> on the sequence and passes in the
|
||||
iterator value that it just got from calling <code>iterate()</code>. The sequence uses
|
||||
that to look up and return the appropriate element.</p>
|
||||
<p>The built-in <a href="lists.html">List</a> and <a href="values.html#ranges">Range</a> types implement
|
||||
<code>iterate()</code> and <code>iteratorValue()</code> to walk over their respective sequences. You
|
||||
can implement the same methods in your classes to make your own types iterable.</p>
|
||||
<p><br><hr>
|
||||
<a class="right" href="variables.html">Variables →</a>
|
||||
<a href="method-calls.html">← Method Calls</a></p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,80 +0,0 @@
|
||||
Q: Can we use fibers for error-handling?
|
||||
|
||||
The goal here is to avoid adding support for exception handling if we're already
|
||||
going to support fibers. A potential bonus would be being able to have
|
||||
restartable error-handling.
|
||||
|
||||
The general idea is that instead of putting code in a "try" block, you throw it
|
||||
onto a new fiber. If an error occurs, that fiber is paused, and returns control
|
||||
back to the spawning fiber. The parent fiber can then decipher the error and
|
||||
either abandon the fiber, or try to fix the error and resume somehow.
|
||||
|
||||
The first question is what kinds of errors is this useful for. For things like
|
||||
parsing strings where failure is common and error-handling needs to be
|
||||
lightweight, I think using fibers is too heavy, both in performance and code.
|
||||
A better answer there is to lean on dynamic typing and return null on parse
|
||||
failure.
|
||||
|
||||
On the other hand, it might be nice to be able to resume here if the code that
|
||||
provided the string is far away and you don't want to have to manually propagate
|
||||
the error out.
|
||||
|
||||
Programmatic errors like unvalid argument types should halt the fiber but the
|
||||
programmer will not want to resume that at runtime. Using the mechanism here is
|
||||
fine since it would then dump a stack trace, etc. But it won't take advantage
|
||||
of resuming.
|
||||
|
||||
Resuming is probably useful for things like IO errors where the error can't be
|
||||
easily predicted beforehand but where you may want to handle it gracefully. For
|
||||
example, if a file can't be opened, the caller may want to wait a while and
|
||||
try again.
|
||||
|
||||
--
|
||||
|
||||
After thinking about it, maybe resuming is a bridge too far. Erlang's model is
|
||||
that a failure just kills the process. I'll note that Erlang does have try and
|
||||
catch, though.
|
||||
|
||||
The goals for error-handling in a scripting language are:
|
||||
|
||||
0. Have simple semantics and implementation.
|
||||
|
||||
1. Make it easy for developers to track down programmatic errors so they can
|
||||
fix them. This means bugs like wrong argument types should fail immediately
|
||||
and loudly, and should provide context (a callstack) about where the error
|
||||
occurred.
|
||||
|
||||
2. For runtime errors like parsing an invalid string or opening a missing file,
|
||||
the program should be able to easily detect the error at handle it.
|
||||
|
||||
3. It *may* be useful for programmers to be able to trap all errors and try to
|
||||
keep the program alive, or at least log the error in a meaningful way. When
|
||||
you have user-defined scripts, or a lot of code, or code authored by
|
||||
non-technical people, it's nice if a failure in one part can be reported but
|
||||
not take down the entire system.
|
||||
|
||||
Two close-at-hand examples:
|
||||
|
||||
- The REPL. A bug in code in the REPL shouldn't kill the whole REPL session.
|
||||
|
||||
- The test framework. In order to write tests in Wren that test programmatic
|
||||
runtime errors, we need to be able to detect them and output something.
|
||||
The test runner could just parse the error output when the entire process
|
||||
dies, but that means you can only have one error test per test file.
|
||||
|
||||
Given those, I'm thinking:
|
||||
|
||||
1. Programmatic errors take down the entire fiber and dump a callstack.
|
||||
Normally, they will also take down the parent fiber and so on until the
|
||||
entire program goes down.
|
||||
|
||||
2. Runtime errors return error codes (or null). Things like parsing a string to
|
||||
a number, etc. should just return an error that you are responsible for
|
||||
handling.
|
||||
|
||||
3. When handing off control to a fiber, there is a "guarded run" method that
|
||||
will run the fiber. If it fails with a programmatic error, the invoked fiber
|
||||
dies, but the parent does not. It gets the callstack and error as some sort
|
||||
of object it can poke at.
|
||||
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
Q: Can fields be implicitly declared?
|
||||
|
||||
The idea is that just using a name starting with "_" somewhere in a class
|
||||
automatically defines a field with that name. Implicit fields are particularly
|
||||
nice because it means they don't have to be defined all before methods. (Since
|
||||
we have a single-pass compiler, we would have to otherwise a method could
|
||||
only refer to previously defined fields.)
|
||||
|
||||
One potential problem is with nested classes. This is more important if we
|
||||
consider a module effectively a class. Consider:
|
||||
|
||||
class Outer {
|
||||
foo {
|
||||
_blah = "value"
|
||||
}
|
||||
|
||||
class Inner {
|
||||
IO.write(_blah) // Does this declare field in Inner, or access Outer?
|
||||
}
|
||||
}
|
||||
|
||||
Looking at this, though, I think there's already a question how referring to an
|
||||
outer field would work. Having an instance of Inner doesn't imply you also have
|
||||
an instance of Outer. We definitely don't want to recapitulate inner classes
|
||||
in Java.
|
||||
|
||||
Q: What about static fields?
|
||||
|
||||
A: Different naming convention? __foo?
|
||||
@ -1,65 +0,0 @@
|
||||
This is the number of times each instruction was executed when running the
|
||||
delta_blue benchmark:
|
||||
|
||||
3753021 CODE_LOAD_LOCAL
|
||||
2233991 CODE_RETURN
|
||||
2151580 CODE_LOAD_FIELD_THIS
|
||||
2121398 CODE_CALL_1
|
||||
1827535 CODE_CALL_0
|
||||
1328364 CODE_POP
|
||||
1136064 CODE_JUMP_IF
|
||||
715071 CODE_LOAD_GLOBAL
|
||||
428374 CODE_STORE_FIELD_THIS
|
||||
424999 CODE_NULL
|
||||
355344 CODE_STORE_LOCAL
|
||||
341762 CODE_LOOP
|
||||
118855 CODE_CONSTANT
|
||||
93048 CODE_CALL_2
|
||||
75280 CODE_AND
|
||||
59920 CODE_JUMP
|
||||
16842 CODE_LIST
|
||||
16660 CODE_TRUE
|
||||
10040 CODE_OR
|
||||
8200 CODE_LOAD_UPVALUE
|
||||
8140 CODE_SUPER_1
|
||||
6540 CODE_FALSE
|
||||
6076 CODE_STORE_GLOBAL
|
||||
4000 CODE_SUPER_3
|
||||
2020 CODE_SUPER_2
|
||||
2000 CODE_SUPER_0
|
||||
2000 CODE_CALL_5
|
||||
2000 CODE_CALL_3
|
||||
160 CODE_CLOSURE
|
||||
74 CODE_METHOD_INSTANCE
|
||||
11 CODE_CLASS
|
||||
4 CODE_METHOD_STATIC
|
||||
0 CODE_SUPER_9
|
||||
0 CODE_SUPER_8
|
||||
0 CODE_SUPER_7
|
||||
0 CODE_SUPER_6
|
||||
0 CODE_SUPER_5
|
||||
0 CODE_SUPER_4
|
||||
0 CODE_SUPER_16
|
||||
0 CODE_SUPER_15
|
||||
0 CODE_SUPER_14
|
||||
0 CODE_SUPER_13
|
||||
0 CODE_SUPER_12
|
||||
0 CODE_SUPER_11
|
||||
0 CODE_SUPER_10
|
||||
0 CODE_STORE_UPVALUE
|
||||
0 CODE_STORE_FIELD
|
||||
0 CODE_LOAD_FIELD
|
||||
0 CODE_IS
|
||||
0 CODE_CLOSE_UPVALUE
|
||||
0 CODE_CALL_9
|
||||
0 CODE_CALL_8
|
||||
0 CODE_CALL_7
|
||||
0 CODE_CALL_6
|
||||
0 CODE_CALL_4
|
||||
0 CODE_CALL_16
|
||||
0 CODE_CALL_15
|
||||
0 CODE_CALL_14
|
||||
0 CODE_CALL_13
|
||||
0 CODE_CALL_12
|
||||
0 CODE_CALL_11
|
||||
0 CODE_CALL_10
|
||||
@ -1,146 +0,0 @@
|
||||
var baz = "top level"
|
||||
|
||||
class Foo {
|
||||
bar {
|
||||
baz
|
||||
_baz
|
||||
}
|
||||
|
||||
baz { "getter" }
|
||||
_baz { "private getter" }
|
||||
|
||||
this {
|
||||
_baz = "field"
|
||||
}
|
||||
}
|
||||
|
||||
Given `_foo`, how do we tell if it is:
|
||||
1. A call to a private getter
|
||||
2. Accessing a private field
|
||||
3. Tearing off a reference to a private method
|
||||
|
||||
It's not 3 because of arity overloading. Wren doesn't really have method
|
||||
tear-off because of this.)
|
||||
|
||||
This is hard because the getter may not be defined yet. One option is:
|
||||
It's always a call to a private getter. After the class is defined, we see if
|
||||
there are any private getters that were not implemented and define implicit
|
||||
getters for them that return fields.
|
||||
|
||||
That's weird if you take into account setters, though. Consider:
|
||||
|
||||
class Foo {
|
||||
a { IO.write(_prop) }
|
||||
_prop = value { ... }
|
||||
}
|
||||
|
||||
For first reference to `_prop`, compile it to getter call. Then see setter
|
||||
defined for it, so we no longer implicitly make a field. But there's no getter,
|
||||
so now the above call will fail.
|
||||
|
||||
Probably do want call to fail here, so that may be OK.
|
||||
|
||||
---
|
||||
|
||||
Given `_foo(arg)`, how do we tell if it is:
|
||||
|
||||
1. A call to a private method
|
||||
2. A call to a private getter, which returns a field that's a fn, and invoking
|
||||
it.
|
||||
|
||||
Since arity is part of the name, the answer here is 1.
|
||||
|
||||
---
|
||||
|
||||
Given `foo(arg)` inside a class, how do we tell if it is:
|
||||
|
||||
1. A call to a method on this.
|
||||
2. Accessing a field `foo` on this, which returns a fn, and invoking it.
|
||||
3. Calling a getter `foo` on this, which returns a fn, and invoking it.
|
||||
4. A call to a top-level fn.
|
||||
|
||||
Let's just dismiss 3. Since arity affects naming, `foo(arg)` and `(foo)(arg)`
|
||||
are really different things in Wren. The parentheses and args are effectively
|
||||
part of the name.
|
||||
|
||||
That covers 2 as well. If we ditch top level fns, we're left with 1. This is
|
||||
good, I think. It means the common case of calling methods on yourself is nice
|
||||
and terse.
|
||||
|
||||
---
|
||||
|
||||
Given `foo` inside a class, how do we tell if it is:
|
||||
|
||||
1. Accessing a field on this.
|
||||
2. Calling a getter on this.
|
||||
3. Accessing a global variable.
|
||||
4. Accessing a top-level getter.
|
||||
5. Accessing a local variable.
|
||||
|
||||
We can probably ditch 4. We can ditch 1 because Wren doesn't have public fields.
|
||||
|
||||
Because both getters and global variables can be used before they are defined,
|
||||
we can't determine statically (in a single pass compiler) if there is a global
|
||||
variable or getter named `foo` in order to disambiguate. Even if we could, we'd
|
||||
still have to answer the ambiguous case where it's both.
|
||||
|
||||
If we assume it's a global and the user wants a getter, they can always do
|
||||
`this.foo` to be explicit. If we assume it's getter, how would they indicate a
|
||||
global?
|
||||
|
||||
One option is to have a different naming convention for globals, like a
|
||||
capitalized initial variable. That lines up with class names at the top level
|
||||
anyway. It just means if we have variables for imported modules, we'll want to
|
||||
capitalize those.
|
||||
|
||||
We still have to distinguish locals, but since those are declared before use, we
|
||||
can determine that statically. I.e. locals will shadow implicit getters.
|
||||
|
||||
---
|
||||
|
||||
OK, so here's one proposal:
|
||||
|
||||
class MyClass {
|
||||
method {
|
||||
_foo // access field
|
||||
_foo(arg) // not valid
|
||||
foo // local var or getter on this
|
||||
foo(arg) // method on this
|
||||
Foo // global variable
|
||||
}
|
||||
}
|
||||
|
||||
This is simple, and straightforward to compile. Using capitalization for globals
|
||||
is a bit weird, and not having private methods is a bummer, but maybe simplicity
|
||||
is the right answer.
|
||||
|
||||
Here's another:
|
||||
|
||||
class MyClass {
|
||||
method {
|
||||
_foo // private getter on this
|
||||
_foo(arg) // private method on this
|
||||
foo // local var or getter on this
|
||||
foo(arg) // method on this
|
||||
Foo // global variable
|
||||
}
|
||||
}
|
||||
|
||||
To get rid of the weird capitalization rule for globals, one option is to not
|
||||
allow forward references to globals. That would break mutually recursive
|
||||
references to classes, though:
|
||||
|
||||
class A {
|
||||
foo { B.new }
|
||||
}
|
||||
|
||||
class B {
|
||||
foo { A.new }
|
||||
}
|
||||
|
||||
So, not a fan of that.
|
||||
|
||||
Ignoring that, the main difference between the two proposals is the second has
|
||||
private methods. Since the first proposal is practically a subset of the second,
|
||||
let's start with that one first.
|
||||
|
||||
@ -1,76 +0,0 @@
|
||||
Q1: What does this resolve to:
|
||||
|
||||
foo(arg)
|
||||
|
||||
It could be:
|
||||
1. this.foo(arg)
|
||||
2. EnclosingClass.foo(arg) // i.e. a static method call
|
||||
3. a call to a top-level function foo()
|
||||
|
||||
If we adopt the idea that a module is just a class definition (with some
|
||||
syntactic differences) and classes can be nested, then 3 really means "a call
|
||||
to a static method on the class surrounding the enclosing class".
|
||||
|
||||
I *don't* think we want the answer to the question to vary based on the name
|
||||
in question. We can't rely on name resolution to disambiguate because we don't
|
||||
know the full set of surrounding names in a single pass compiler. Also, it's
|
||||
semantically squishier.
|
||||
|
||||
I think the right answer is 1, it's an implicit call on this. That's what you
|
||||
want most often, I think. For imported modules, we could import them with a
|
||||
"prefix" (really import them as objects bound to named variables), so calling
|
||||
a top-level function in another module would be something like:
|
||||
|
||||
someModule.foo(arg)
|
||||
|
||||
This leaves the question of how *do* you call top level functions in your own
|
||||
module? I.e., how do we call foo here:
|
||||
|
||||
def foo(arg) { IO.write("called foo!") }
|
||||
|
||||
class SomeClass {
|
||||
bar {
|
||||
// Want to call foo here...
|
||||
}
|
||||
}
|
||||
|
||||
This is analogous to:
|
||||
|
||||
class SomeModule {
|
||||
static foo(arg) { IO.write("called foo!") }
|
||||
|
||||
class SomeClass {
|
||||
bar {
|
||||
// Want to call foo here...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
The obvious solution is to use the class name:
|
||||
|
||||
class SomeModule {
|
||||
static foo(arg) { IO.print("called foo!") }
|
||||
|
||||
class SomeClass {
|
||||
bar {
|
||||
SomeModule.foo(arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Which just leaves the question of what the class name of a top-level "module
|
||||
class" is.
|
||||
|
||||
Idea: it's unnamed, so you just use a leading ".":
|
||||
|
||||
def foo(arg) { IO.print("called foo!") }
|
||||
|
||||
class SomeClass {
|
||||
bar {
|
||||
.foo(arg)
|
||||
}
|
||||
}
|
||||
|
||||
This mirrors C++'s unnamed scope thing:
|
||||
|
||||
::foo(arg);
|
||||
@ -1,70 +0,0 @@
|
||||
^title Branching
|
||||
^category language
|
||||
|
||||
*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).
|
||||
|
||||
## Truth
|
||||
|
||||
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.
|
||||
|
||||
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 false.
|
||||
* The null value `null` is false.
|
||||
* Everything else is true.
|
||||
|
||||
This means `0`, empty strings, and empty collections are all considered "true" values.
|
||||
|
||||
## If statements
|
||||
|
||||
The simplest branching statement, `if` lets you conditionally skip a chunk of code. It looks like this:
|
||||
|
||||
:::dart
|
||||
if (ready) IO.print("go!")
|
||||
|
||||
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](syntax.html#blocks):
|
||||
|
||||
:::dart
|
||||
if (ready) {
|
||||
IO.print("getSet")
|
||||
IO.print("go!")
|
||||
}
|
||||
|
||||
You may also provide an `else` branch. It will be executed if the condition is false:
|
||||
|
||||
:::dart
|
||||
if (ready) IO.print("go!") else IO.print("not ready!")
|
||||
|
||||
And, of course, it can take a block too:
|
||||
|
||||
:::dart
|
||||
if (ready) {
|
||||
IO.print("go!")
|
||||
} else {
|
||||
IO.print("not ready!")
|
||||
}
|
||||
|
||||
## The logical operators `&&` and `||`
|
||||
|
||||
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 false, it returns that value. Otherwise it evaluates and returns the right-hand argument.
|
||||
|
||||
:::dart
|
||||
IO.print(false && 1) // false
|
||||
IO.print(1 && 2) // 2
|
||||
|
||||
An `||` ("logical or") expression is reversed. If the left-hand argument is true, it's returned, otherwise the right-hand argument is evaluated and returned:
|
||||
|
||||
:::dart
|
||||
IO.print(false || 1) // 1
|
||||
IO.print(1 || 2) // 1
|
||||
|
||||
## The conditional operator `?:`
|
||||
|
||||
Also known as the "ternary" operator since it takes three arguments, Wren has the little "if statement in the form of an expression" you know and love from C and its bretheren.
|
||||
|
||||
:::dart
|
||||
IO.print(1 != 2 ? "math is sane" : "math is not sane!")
|
||||
|
||||
It takes a condition expression, followed by `?`, followed by a then expression, a `:`, then an else expression. Just like `if`, it evaluates the condition. If true, it evaluates (and returns) the then expression. Otherwise it does the else expression.
|
||||
@ -1,172 +0,0 @@
|
||||
^title Classes
|
||||
^category types
|
||||
|
||||
Every value in Wren is an object, and every object is an instance of a class.
|
||||
Even `true` and `false` are full-featured objects—instances of the `Bool` class.
|
||||
|
||||
Classes contain both *behavior* and *state*. Behavior is defined in *methods* which are stored in the class. State is defined in *fields*, whose values are stored in each instance.
|
||||
|
||||
## Defining a class
|
||||
|
||||
Classes are created using the `class` keyword, unsurprisingly:
|
||||
|
||||
:::dart
|
||||
class Unicorn {}
|
||||
|
||||
This creates a class named `Unicorn` with no methods or fields.
|
||||
|
||||
## Methods
|
||||
|
||||
To let our unicorn do stuff, we need to give it methods.
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
prance {
|
||||
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:
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
prance(where, 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 technical terms, you can *overload by arity*. So this class is fine:
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
prance {
|
||||
IO.print("The unicorn prances in a fancy manner!")
|
||||
}
|
||||
|
||||
prance(where) {
|
||||
IO.print("The unicorn prances in " + where)
|
||||
}
|
||||
|
||||
prance(where, when) {
|
||||
IO.print("The unicorn prances in " + where + " at " + when)
|
||||
}
|
||||
}
|
||||
|
||||
When you [call](method-calls.html) the `prance` method, it selects the right one based on how many arguments you pass it.
|
||||
|
||||
## Constructors
|
||||
|
||||
To create a new instance of a class, you use the `new` keyword. We can make a unicorn like so:
|
||||
|
||||
:::dart
|
||||
new Unicorn
|
||||
|
||||
You almost always want to define some state or do some other initialization on a new object. For that, you'll want to define a constructor, like so:
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
new {
|
||||
IO.print("I am a constructor!")
|
||||
}
|
||||
}
|
||||
|
||||
When you create an instance with `new`, its constructor will be invoked. It's just a method with a special name. Like methods, you can pass arguments to the constructor by adding a parenthesized parameter list after `new`:
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
new(name, color) {
|
||||
IO.print("My name is " + name + " and I am " + color + ".")
|
||||
}
|
||||
}
|
||||
|
||||
Values are passed to the constructor like so:
|
||||
|
||||
:::dart
|
||||
new Unicorn("Flicker", "purple")
|
||||
|
||||
Like other methods, you can overload constructors by arity.
|
||||
|
||||
## Operators
|
||||
|
||||
Operators are just special syntax for regular [method calls](method-calls.html) on the left hand operand (or only operand in the case of unary operators like `!` and `~`). You can define them like so:
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
// Infix:
|
||||
+(other) {
|
||||
IO.print("Adding to a unicorn?")
|
||||
}
|
||||
|
||||
// Prefix:
|
||||
! {
|
||||
IO.print("Negating a unicorn?!")
|
||||
}
|
||||
}
|
||||
|
||||
This can be used to define any of these operators:
|
||||
|
||||
:::dart
|
||||
// Infix:
|
||||
+ - * / % < > <= >= == != & |
|
||||
|
||||
// Prefix:
|
||||
! ~ -
|
||||
|
||||
Note that `-` can be both a prefix and infix operator. If there's a parameter list, it's the infix one, otherwise, it's prefix. Since Wren supports overloading by arity, it's no problem for a class to define both.
|
||||
|
||||
Operator overloading is really useful for types like vectors and complex numbers where the reader knows what the operators will do, but can make code deeply confusing if overused. When in doubt, use a real name.
|
||||
|
||||
## Setters
|
||||
|
||||
|
||||
**TODO: ...**
|
||||
|
||||
## Fields
|
||||
|
||||
**TODO**
|
||||
|
||||
## Metaclasses and static members
|
||||
|
||||
**TODO**
|
||||
|
||||
## Inheritance
|
||||
|
||||
A class can inherit from a "parent" or *superclass*. When you invoke a method on an object of some class, if it can't be found, it walks up the chain of superclasses looking for it there.
|
||||
|
||||
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:
|
||||
|
||||
:::dart
|
||||
class Pegasus is Unicorn {}
|
||||
|
||||
This declares a new class `Pegasus` that inherits from `Unicorn`.
|
||||
|
||||
The metaclass hierarchy does *not* parallel the regular class hierarchy. So, if `Pegasus` inherits from `Unicorn`, `Pegasus`'s metaclass will not inherit from `Unicorn`'s metaclass. In more prosaic terms, this means that static methods are not inherited.
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
// Unicorns cannot fly. :(
|
||||
static canFly { false }
|
||||
}
|
||||
|
||||
class Pegasus is Unicorn {}
|
||||
|
||||
Pegasus.canFly // ERROR: Static methods are not inherited.
|
||||
|
||||
Constructors, however, initialize the instance *after* it has been created. They are defined as instance methods on the class and not on the metaclass. That means that constructors *are* inherited.
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
new(name) {
|
||||
IO.print("My name is " + name + ".")
|
||||
}
|
||||
}
|
||||
|
||||
class Pegasus is Unicorn {}
|
||||
|
||||
new Pegasus("Fred") // Prints "My name is Fred.".
|
||||
|
||||
## Superclass method calls
|
||||
|
||||
**TODO**
|
||||
|
||||
**TODO: this**
|
||||
@ -1,10 +0,0 @@
|
||||
^title Contributing
|
||||
^category reference
|
||||
|
||||
It should be obvious by now that Wren is under active development and there's lots left to do. I'd be delighted to have your help.
|
||||
|
||||
Wren uses the OSI-approved [MIT license](http://opensource.org/licenses/MIT). I'm not sure exactly what that means, but I went with the most permissive license I could find.
|
||||
|
||||
The source is developed [on GitHub](https://github.com/munificent/wren). My hope is that the codebase, tests, and documentation are easy to understand and contribute to. If they aren't, that's a bug. If you're looking for somewhere to get started, try writing some example programs in Wren and see what rough spots you run into. You can also look through to code for `// TODO` or help improve [these docs](https://github.com/munificent/wren/tree/master/doc/site).
|
||||
|
||||
If you have questions, feel free to [file an issue](https://github.com/munificent/wren/issues) or email me (`robert` at `stuffwithstuff.com`).
|
||||
@ -1,313 +0,0 @@
|
||||
^title Core Library
|
||||
^category reference
|
||||
|
||||
## Object Class
|
||||
|
||||
### **==**(other) and **!=**(other) operators
|
||||
|
||||
Compares two objects using built-in equality. This compares numbers by value, and all other objects are compared by identity—two objects are equal only if they are the exact same object.
|
||||
|
||||
### **toString**
|
||||
|
||||
A default string representation of the object.
|
||||
|
||||
### **type**
|
||||
|
||||
The [Class](#class-class) of the object.
|
||||
|
||||
## Class Class
|
||||
|
||||
### **name**
|
||||
|
||||
The name of the class.
|
||||
|
||||
## Bool Class
|
||||
|
||||
Boolean values. There are two instances, `true` and `false`.
|
||||
|
||||
### **!** operator
|
||||
|
||||
Negates the value.
|
||||
|
||||
> !true
|
||||
false
|
||||
> !false
|
||||
true
|
||||
|
||||
### toString
|
||||
|
||||
The string representation of the value, either `"true"` or `"false"`.
|
||||
|
||||
## Fiber Class
|
||||
|
||||
A lightweight coroutine. [Here](fibers.html) is a gentle introduction.
|
||||
|
||||
### new **Fiber**(function)
|
||||
|
||||
Creates a new fiber that executes `function` in a separate coroutine when the fiber is run. Does not immediately start running the fiber.
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
IO.print("I won't get printed")
|
||||
}
|
||||
|
||||
### Fiber.**yield**
|
||||
|
||||
Pauses the current fiber and transfers control to the parent fiber. "Parent" here means the last fiber that was started using `call` and not `run`.
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
IO.print("Before yield")
|
||||
Fiber.yield
|
||||
IO.print("After yield")
|
||||
}
|
||||
|
||||
fiber.call // "Before yield"
|
||||
IO.print("After call") // "After call"
|
||||
fiber.call // "After yield"
|
||||
|
||||
When resumed, the parent fiber's `call` method returns `null`.
|
||||
|
||||
If a yielded fiber is resumed by calling `call()` or `run()` with an argument, `yield` returns that value.
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
IO.print(Fiber.yield) // "value"
|
||||
}
|
||||
|
||||
fiber.call // Run until the first yield.
|
||||
fiber.call("value") // Resume the fiber.
|
||||
|
||||
If it was resumed by calling `call` or `run` with no argument, returns `null`.
|
||||
|
||||
It is a runtime error to call this when there is no parent fiber to return to.
|
||||
|
||||
:::dart
|
||||
Fiber.yield // ERROR
|
||||
|
||||
new Fiber {
|
||||
Fiber.yield // ERROR
|
||||
}.run
|
||||
|
||||
### Fiber.**yield**(value)
|
||||
|
||||
Similar to `Fiber.yield` but provides a value to return to the parent fiber's `call`.
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
Fiber.yield("value")
|
||||
}
|
||||
|
||||
IO.print(fiber.call) // "value"
|
||||
|
||||
### **call**
|
||||
|
||||
**TODO**
|
||||
|
||||
### **call**(value)
|
||||
|
||||
**TODO**
|
||||
|
||||
### **isDone**
|
||||
|
||||
Whether the fiber's main function has completed and the fiber can no longer be run. This returns `false` if the fiber is currently running or has yielded.
|
||||
|
||||
### **run**
|
||||
|
||||
**TODO**
|
||||
|
||||
### **run**(value)
|
||||
|
||||
**TODO**
|
||||
|
||||
## Fn Class
|
||||
|
||||
A first class function—an object that wraps an executable chunk of code. [Here](functions.html) is a friendly introduction.
|
||||
|
||||
### new **Fn**(function)
|
||||
|
||||
Creates a new function from... `function`. Of course, `function` is already be a function, so this really just returns the argument. It exists mainly to let you create a "bare" function when you don't want to immediately pass it as a [block argument](functions.html#creating-functions) to some other method.
|
||||
|
||||
:::dart
|
||||
var fn = new Fn {
|
||||
IO.print("The body")
|
||||
}
|
||||
|
||||
It is a runtime error if `block` is not a function.
|
||||
|
||||
### **call**(args...)
|
||||
|
||||
**TODO**
|
||||
|
||||
## Num Class
|
||||
|
||||
**TODO**
|
||||
|
||||
### **abs**
|
||||
|
||||
The absolute value of the number.
|
||||
|
||||
:::dart
|
||||
-123.abs // 123
|
||||
|
||||
### **ceil**
|
||||
|
||||
**TODO**
|
||||
|
||||
### **cos**
|
||||
|
||||
The cosine of the number.
|
||||
|
||||
### **floor**
|
||||
|
||||
**TODO**
|
||||
|
||||
### **isNan**
|
||||
|
||||
Whether the number is [not a number](http://en.wikipedia.org/wiki/NaN). This is `false` for normal number values and infinities, and `true` for the result of `0/0`, the square root of a negative number, etc.
|
||||
|
||||
### **sin**
|
||||
|
||||
The sine of the number.
|
||||
|
||||
### **sqrt**
|
||||
|
||||
The square root of the number. Returns `nan` if the number is negative.
|
||||
|
||||
### **-** operator
|
||||
|
||||
Negates the number.
|
||||
|
||||
:::dart
|
||||
var a = 123
|
||||
-a // -123
|
||||
|
||||
### **-**(other), **+**(other), **/**(other), **\***(other) operators
|
||||
|
||||
The usual arithmetic operators you know and love. All of them do 64-bit floating point arithmetic. It is a runtime error if the right-hand operand is not a number. Wren doesn't roll with implicit conversions.
|
||||
|
||||
### **%**(denominator) operator
|
||||
|
||||
The floating-point remainder of this number divided by `denominator`.
|
||||
|
||||
It is a runtime error if `denominator` is not a number.
|
||||
|
||||
### **<**(other), **>**(other), **<=**(other), **>=**(other) operators
|
||||
|
||||
Compares this and `other`, returning `true` or `false` based on how the numbers are ordered. It is a runtime error if `other` is not a number.
|
||||
|
||||
### **~** operator
|
||||
|
||||
Performs *bitwise* negation on the number. The number is first converted to a 32-bit unsigned value, which will truncate any floating point value. The bits of the result of that are then negated, yielding the result.
|
||||
|
||||
### **&**(other) operator
|
||||
|
||||
Performs bitwise and on the number. Both numbers are first converted to 32-bit unsigned values. The result is then a 32-bit unsigned number where each bit is `true` only where the corresponding bits of both inputs were `true`.
|
||||
|
||||
It is a runtime error if `other` is not a number.
|
||||
|
||||
### **|**(other) operator
|
||||
|
||||
Performs bitwise or on the number. Both numbers are first converted to 32-bit unsigned values. The result is then a 32-bit unsigned number where each bit is `true` only where the corresponding bits of both inputs were `true`.
|
||||
|
||||
It is a runtime error if `other` is not a number.
|
||||
|
||||
### **..**(other) operator
|
||||
|
||||
**TODO**
|
||||
|
||||
### **...**(other) operator
|
||||
|
||||
**TODO**
|
||||
|
||||
## String Class
|
||||
|
||||
**TODO**
|
||||
|
||||
### **contains**(other)
|
||||
|
||||
**TODO**
|
||||
|
||||
### **count**
|
||||
|
||||
**TODO**
|
||||
|
||||
### **+**(other) operator
|
||||
|
||||
**TODO**
|
||||
|
||||
### **==**(other) operator
|
||||
|
||||
**TODO**
|
||||
|
||||
### **!=**(other) operator
|
||||
|
||||
**TODO**
|
||||
|
||||
### **[**index**]** operator
|
||||
|
||||
**TODO**
|
||||
|
||||
## List Class
|
||||
|
||||
**TODO**
|
||||
|
||||
### **add**(item)
|
||||
|
||||
Appends `item` onto the end of the list.
|
||||
|
||||
### **clear**
|
||||
|
||||
Removes all items from the list.
|
||||
|
||||
### **count**
|
||||
|
||||
The number of items in the list.
|
||||
|
||||
### **insert**(item, index)
|
||||
|
||||
**TODO**
|
||||
|
||||
### **iterate**(iterator), **iteratorValue**(iterator)
|
||||
|
||||
**TODO**
|
||||
|
||||
### **removeAt**(index)
|
||||
|
||||
**TODO**
|
||||
|
||||
### **[**index**]** operator
|
||||
|
||||
**TODO**
|
||||
|
||||
### **[**index**]=**(item) operator
|
||||
|
||||
**TODO**
|
||||
|
||||
## Range Class
|
||||
|
||||
**TODO**
|
||||
|
||||
### **from**
|
||||
|
||||
**TODO**
|
||||
|
||||
### **to**
|
||||
|
||||
**TODO**
|
||||
|
||||
### **min**
|
||||
|
||||
**TODO**
|
||||
|
||||
### **max**
|
||||
|
||||
**TODO**
|
||||
|
||||
### **isInclusive**
|
||||
|
||||
**TODO**
|
||||
|
||||
### **iterate**(iterator), **iteratorValue**(iterator)
|
||||
|
||||
**TODO**
|
||||
@ -1,6 +0,0 @@
|
||||
^title Embedding API
|
||||
^category reference
|
||||
|
||||
As an embedded scripting language, the C API your host app uses to interact with Wren is one of the key facets of the system. It's so important that... I haven't fleshed it out much yet.
|
||||
|
||||
I believe good API design can't be done in a vacuum and I haven't built many applications that embed Wren yet, so I don't have a good testbed for the embedding API. Now that the language itself is further along, I'm starting to work on this, but it isn't quite there yet. Feedback and contributions are definitely welcome!
|
||||
@ -1,118 +0,0 @@
|
||||
^title Error Handling
|
||||
^category language
|
||||
|
||||
Errors come in a few fun flavors.
|
||||
|
||||
## Syntax errors
|
||||
|
||||
The first errors you're likely to run into are syntax errors. These include simple bugs where your code doesn't follow the language's grammar, like:
|
||||
|
||||
:::dart
|
||||
1 + * 2
|
||||
|
||||
Wren will detect these errors as soon as it tries to read your code. When it hits one, you'll get a friendly error message, like:
|
||||
|
||||
:::text
|
||||
[script.wren line 1] Error on '*': Unexpected token for expression.
|
||||
|
||||
Some slightly more "semantic" errors fall into this bucket too. Things like using a variable that hasn't been defined, or declaring two variables with the same name in the same scope. So if you do:
|
||||
|
||||
:::dart
|
||||
var a = "once"
|
||||
var a = "twice"
|
||||
|
||||
Wren tells you:
|
||||
|
||||
:::text
|
||||
[script.wren line 2] Error on 'a': Global variable is already defined.
|
||||
|
||||
Note that it does this before it executes *any* code. Unlike some other scripting languages, Wren tries to help you find your errors as soon as possible when it can.
|
||||
|
||||
If it starts running your code, you can be sure you don't have any errors related to syntax or variable scope.
|
||||
|
||||
## Runtime errors
|
||||
|
||||
Alas, just fixing all of the "compile-time" errors doesn't mean your code does what you want. Your program may still have errors that can't be detected statically. Since they can't be found until your code is run, they're called "runtime" errors.
|
||||
|
||||
Most runtime errors come from the VM itself. They arise from code trying to perform an operation that the VM can't do. The most common error is a "method not found" one. If you call a method on an object and its class (and all of its superclasses) don't define that method, there's nothing Wren can do:
|
||||
|
||||
:::dart
|
||||
class Foo {}
|
||||
|
||||
var foo = new Foo
|
||||
foo.someRandomMethod
|
||||
|
||||
If you run this, Wren will print:
|
||||
|
||||
:::text
|
||||
Foo does not implement method 'someRandomMethod'.
|
||||
|
||||
Then it stops executing code. Unlike some other languages, Wren doesn't keep plugging away after a runtime error has occurred. A runtime error implies there's a bug in your code and it wants to draw your attention to it. To help you out, it prints a stack trace showing where in the code the error occurred, and all of the method calls that led to it.
|
||||
|
||||
Another common runtime error is passing an argument of the wrong type to a method. For example, lists are indexed using a number. If you try to pass some other type, it's an error:
|
||||
|
||||
:::dart
|
||||
var list = ["a", "b", "c"]
|
||||
list["1"]
|
||||
|
||||
This exits with:
|
||||
|
||||
:::text
|
||||
Subscript must be a number or a range.
|
||||
[script.wren line 2] in (script)
|
||||
|
||||
These are the most two common kinds of runtime errors, but there are others. Stuff like out of bounds errors on lists, calling a function with the wrong number of arguments, etc.
|
||||
|
||||
## Handling runtime errors
|
||||
|
||||
Most of the time, runtime errors indicate a bug in your code and the best solution is to fix the bug. However, sometimes it's useful to be able to handle them at, uh, runtime.
|
||||
|
||||
To keep the language simpler, Wren does not have exception handling. Instead, it takes advantage of [fibers](fibers.html) for handling errors. When a runtime error occurs, the current fiber is aborted. Normally, Wren will also abort any fibers that invoked that one, all the way to the main fiber, and then exit the VM.
|
||||
|
||||
However, you can run a fiber using the `try` method. If a runtime error occurs in the called fiber, the error is captured and the `try` method returns the error message as a string.
|
||||
|
||||
For example, if you run this program:
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
123.badMethod
|
||||
}
|
||||
|
||||
var error = fiber.try
|
||||
IO.print("Caught error: ", error)
|
||||
|
||||
It prints:
|
||||
|
||||
:::text
|
||||
Num does not implement method 'badMethod'.
|
||||
|
||||
The called fiber can no longer be used, but any other fibers can proceed as usual. When a fiber has been aborted because of a runtime error, you can also get the error from the fiber object. Continuing the above example:
|
||||
|
||||
:::dart
|
||||
IO.print(fiber.error)
|
||||
|
||||
This also prints:
|
||||
|
||||
:::text
|
||||
Num does not implement method 'badMethod'.
|
||||
|
||||
If you have a chain of fiber calls and a runtime error occurs, it will walk the chain looking for a `try` call, so this can also be used to capture runtime errors generated in fibers that are invoked by the one you called `try` on.
|
||||
|
||||
## Creating runtime errors
|
||||
|
||||
Most runtime errors come from within the Wren VM, but you may want to be able to cause your own runtime errors to occur. This can be done by calling the `abort()` static method on `Fiber`:
|
||||
|
||||
:::dart
|
||||
Fiber.abort("Something bad happened")
|
||||
|
||||
You must pass in an error message, and it must be a string.
|
||||
|
||||
## Failures
|
||||
|
||||
The last flavor of errors is the highest-level one. All of the above errors indicate *bugs*—places where the code itself is incorrect. But some errors indicate that the code simply couldn't accomplish its task for unforeseeable reasons. We'll call these "failures".
|
||||
|
||||
Consider a program that reads in a string of input from the user and parses it to a number. Many strings are not valid numbers, so this parsing can fail. The only way the program could prevent that failure is by validating the string before its parsed, but validating that a string is a number is pretty much the same thing as parsing it.
|
||||
|
||||
For cases like this where failure can occur and the program *will* want to handle it, fibers and `try` are too coarse-grained to work with. Instead, these operations will indicate failure by *returning* some sort of error indication.
|
||||
|
||||
For example, a method for parsing a number could return a number on success and `null` to indicate parsing failed. Since Wren is dynamically typed, it's easy and natural for a method to return different types of values.
|
||||
@ -1,119 +0,0 @@
|
||||
^title Fibers
|
||||
^category types
|
||||
|
||||
Fibers are a key part of Wren. They form its execution model, its concurrency story and take the place of exceptions in [error handling](error-handling.html).
|
||||
|
||||
They are a bit like threads except they are *cooperatively* scheduled. That means Wren doesn't pause one fiber and switch to another until you tell it to. You don't have to worry about context switches at random times and all of the headaches those cause.
|
||||
|
||||
Fibers are managed entirely by Wren, so they don't use OS thread resources, or require heavyweight context switches. They just need a bit of memory for their stacks. A fiber will get garbage collected like any other object when not referenced any more, so you can create them freely.
|
||||
|
||||
They are lightweight enough that you can, for example, have a separate fiber for each entity in a game. Wren can handle thousands of them without any trouble. For example, when you run Wren in interactive mode, it creates a new fiber for every line of code you type in.
|
||||
|
||||
## Creating fibers
|
||||
|
||||
All Wren code runs within the context of a fiber. When you first start a Wren script, a main fiber is created for you automatically. You can spawn new fibers using the `Fiber` class's constructor:
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
IO.print("This runs in a separate fiber.")
|
||||
}
|
||||
|
||||
Creating a fiber does not immediately run it. It's just a first class bundle of code sitting there waiting to be activated, a bit like a [function](functions.html).
|
||||
|
||||
## Invoking fibers
|
||||
|
||||
Once you've created a fiber, you can invoke it (which suspends the current fiber) by calling its `call` method:
|
||||
|
||||
:::dart
|
||||
fiber.call
|
||||
|
||||
The called fiber will execute its code until it reaches the end of its body or until it passes control to another fiber. If it reaches the end of its body, it's considered *done*:
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber { IO.print("Hi") }
|
||||
fiber.isDone // false
|
||||
fiber.call
|
||||
fiber.isDone // true
|
||||
|
||||
When it finishes, it automatically resumes the fiber that called it. It's a runtime error to try to call a fiber that is already done.
|
||||
|
||||
## Yielding
|
||||
|
||||
The main difference between fibers and functions is that a fiber can be suspended in the middle of its operation and then resumed later. Calling another fiber is one way to suspend a fiber, but that's more or less the same as one function calling another.
|
||||
|
||||
Things get interesting when a fiber *yields*. A yielded fiber passes control *back* to the fiber that ran it, but *remembers where it is*. The next time the fiber is called, it picks up right where it left off and keeps going.
|
||||
|
||||
You can make a fiber yield by calling the static `yield` method on `Fiber`:
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
IO.print("fiber 1")
|
||||
Fiber.yield
|
||||
IO.print("fiber 2")
|
||||
}
|
||||
|
||||
IO.print("main 1")
|
||||
fiber.call
|
||||
IO.print("main 2")
|
||||
fiber.call
|
||||
IO.print("main 3")
|
||||
|
||||
This program prints:
|
||||
|
||||
:::text
|
||||
main 1
|
||||
fiber 1
|
||||
main 2
|
||||
fiber 2
|
||||
main 3
|
||||
|
||||
Note that even though this program has *concurrency*, it's still *deterministic*. You can reason precisely about what it's doing and aren't at the mercy of a thread scheduler playing Russian roulette with your code.
|
||||
|
||||
## Passing values
|
||||
|
||||
Calling and yielding fibers is used for passing control, but it can also pass *data*. When you call a fiber, you can optionally pass a value to it. If the fiber has yielded and is waiting to resume, the value becomes the return value of the `yield` call:
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
var result = Fiber.yield
|
||||
IO.print(result)
|
||||
}
|
||||
|
||||
fiber.call("discarded")
|
||||
fiber.call("sent")
|
||||
|
||||
This prints "sent". Note that the first value sent to the fiber through call is ignored. That's because the fiber isn't waiting on a `yield` call, so there's no where for the sent value to go.
|
||||
|
||||
Fibers can also pass values *back* when they yield. If you pass an argument to `yield`, that will become the return value of the `call` that was used to invoke the fiber:
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
Fiber.yield("sent")
|
||||
}
|
||||
|
||||
IO.print(fiber.call)
|
||||
|
||||
This also prints "sent".
|
||||
|
||||
## Full coroutines
|
||||
|
||||
What we've seen so far is very similar to what you can do with languages like Python and C# that have *generators*. Those let you define a function call that you can suspend and resume. When using the function, it appears like a sequence you can iterate over.
|
||||
|
||||
Wren's fibers can do that, but they can do much more. Like Lua, they are full *coroutines*—they can suspend from anywhere in the callstack. For example:
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
(1..10).map {|i|
|
||||
Fiber.yield(i)
|
||||
}
|
||||
}
|
||||
|
||||
Here, we're calling `yield` from within a [function](functions.html) being passed to the `map` method. This works fine in Wren because that inner `yield` call will suspend the call to `map` and the function passed to it as a callback.
|
||||
|
||||
## Transferring control
|
||||
|
||||
Fibers have one more trick up their sleeves. When you execute a fiber using `call`, the fiber tracks which fiber it will return to when it yields. This lets you build up a chain of fiber calls that will eventually unwind back to the main fiber when all of the called ones yield or finish.
|
||||
|
||||
This works fine for most uses, but sometimes you want something a little more freeform. For example, you may be creating a [state machine](http://en.wikipedia.org/wiki/Finite-state_machine) where each state is a fiber. When you switch from one state to the next, you *don't* want to build an implicit stack of fibers to return to. There is no "returning" in this case. You just want to *transfer* to the next fiber and forget about the previous one entirely. (This is analogous to [tail call elimination](http://en.wikipedia.org/wiki/Tail_call) for regular function calls.)
|
||||
|
||||
To enable this, fibers also have a `run` method. This begins executing that fiber, and "forgets" the previous one. If the running fiber yields or ends, it will transfer control back to the last *called* one. (If there are no called fibers, it will end execution.)
|
||||
@ -1,125 +0,0 @@
|
||||
^title Functions
|
||||
^category types
|
||||
|
||||
No self-respecting language today can get by without functions—first class little bundles of code. Since Wren is object-oriented, most of your code will live in methods on classes, but free-floating functions are still eminently handy.
|
||||
|
||||
Functions are objects like everything else in Wren, instances of the `Fn` class.
|
||||
|
||||
## Creating functions
|
||||
|
||||
Most of the time you create a function just to pass it to some method. For example, if you want to filter a [list](lists.html) by some criteria, you'll call its `where` method, passing in a function that defines the predicate you're filtering on.
|
||||
|
||||
Since that's the most common usage pattern, Wren's syntax optimizes for that. Taking a page from Ruby, a function is created by passing a *block argument* to a method. At its simplest, it looks like this:
|
||||
|
||||
:::dart
|
||||
blondie.callMe {
|
||||
IO.print("This is the body!")
|
||||
}
|
||||
|
||||
Here we're invoking the `callMe` method on `blondie`. We're passing one argument, a function whose body is everything between that pair of curly braces.
|
||||
|
||||
Methods that take a block argument receive it as a normal parameter. `callMe` could be defined like so:
|
||||
|
||||
:::dart
|
||||
class Blondie {
|
||||
callMe(fn) {
|
||||
// Call it...
|
||||
}
|
||||
}
|
||||
|
||||
A method can take other arguments in addition to the block. They appear before the block just like a regular argument list. For example:
|
||||
|
||||
:::dart
|
||||
blondie.callMeAt(867, 5309) {
|
||||
IO.print("This is the body!")
|
||||
}
|
||||
|
||||
Of course, you don't *have* to use a block argument to pass a function to a method. If you already have a function object, you can pass it like a regular argument:
|
||||
|
||||
:::dart
|
||||
var someFn = // Get a function...
|
||||
blondie.callMe(someFn)
|
||||
|
||||
Block arguments are purely sugar for creating a function and passing it in one little blob of syntax. There are some times when you want to create a function but *don't* need to pass it to a method. For that, you can call the `Fn` class's constructor:
|
||||
|
||||
:::dart
|
||||
var someFn = new Fn {
|
||||
IO.print("Hi!")
|
||||
}
|
||||
|
||||
As you can see it takes a block argument too! All the constructor does it return that, so this exists purely as a convenience method for you.
|
||||
|
||||
## Calling functions
|
||||
|
||||
Once you have a function, how do you invoke it? Like everything in Wren, you do so by calling a method on it:
|
||||
|
||||
:::dart
|
||||
class Blondie {
|
||||
callMe(fn) {
|
||||
fn.call
|
||||
}
|
||||
}
|
||||
|
||||
Functions expose a `call` method that executes the body of the function. This method is dynamically-dispatched like any other, so you can define your own "function-like" classes and pass them to methods that expect "real" functions.
|
||||
|
||||
:::dart
|
||||
class FakeFn {
|
||||
call {
|
||||
IO.print("I'm feeling functional!")
|
||||
}
|
||||
}
|
||||
|
||||
blondie.callMe(new FakeFn)
|
||||
|
||||
## Function parameters
|
||||
|
||||
Of course, functions aren't very useful if you can't pass values to them. The functions that we've seen so far take no arguments. To change that, you can provide a parameter list surrounded by `|` immediately after the opening brace of the body, like so:
|
||||
|
||||
:::dart
|
||||
blondie.callMe {|first, last|
|
||||
IO.print("Hi, " + first + " " + last + "!")
|
||||
}
|
||||
|
||||
Here we're passing a function to `greet` that takes two parameters, `first` and `last`. They are passed to the function when it's called:
|
||||
|
||||
:::dart
|
||||
class Blondie {
|
||||
callMe(fn) {
|
||||
fn.call("Debbie", "Harry")
|
||||
}
|
||||
}
|
||||
|
||||
It's an error to call a function with fewer or more arguments than its parameter list expects.
|
||||
|
||||
## Returning values
|
||||
|
||||
The body of a function is a [block](syntax.html#blocks). If it is a single expression—more precisely if there is no newline after the `{` or parameter list—then the function implicitly returns the value of the expression.
|
||||
|
||||
Otherwise, the body returns `null` by default. You can explicitly return a value using a `return` statement. In other words, these two functions do the same thing:
|
||||
|
||||
:::dart
|
||||
new Fn { "return value" }
|
||||
|
||||
new Fn {
|
||||
return "return value"
|
||||
}
|
||||
|
||||
## Closures
|
||||
|
||||
As you expect, functions are closures—they can access variables defined outside of their scope. They will hold onto closed-over variables even after leaving the scope where the function is defined:
|
||||
|
||||
:::dart
|
||||
class Counter {
|
||||
static create {
|
||||
var i = 0
|
||||
return new Fn { i = i + 1 }
|
||||
}
|
||||
}
|
||||
|
||||
Here, the `create` method returns the function created on its second line. That function references a variable `i` declared outside of the function. Even after the function is returned from `create`, it is still able to read and assign to`i`:
|
||||
|
||||
:::dart
|
||||
var counter = Counter.create
|
||||
IO.print(counter.call) // Prints "1".
|
||||
IO.print(counter.call) // Prints "2".
|
||||
IO.print(counter.call) // Prints "3".
|
||||
@ -1,64 +0,0 @@
|
||||
^title Getting Started
|
||||
|
||||
Getting Wren up and running on your machine should be pretty straightforward. Tiny C programs with no dependencies are nice that way. If you're on a Unix or Mac and you can rock a command line, it's just:
|
||||
|
||||
:::bash
|
||||
$ git clone https://github.com/munificent/wren.git
|
||||
$ cd wren
|
||||
$ make
|
||||
$ ./wren
|
||||
|
||||
For Mac users, there is also an XCode project in the repo that you can use to hack on Wren. That's what I develop in. It builds fine from there but *may* not have the exact same build settings. The Makefile is the canonical way to compile it.
|
||||
|
||||
For our Windows bretheren, there's still a little work to be done. Ideally, the repo would include a Visual Studio solution for building Wren. I don't have a Windows machine, but if you do, I would be delighted to take a patch for this.
|
||||
|
||||
## Interactive mode
|
||||
|
||||
The above instructions will drop you into Wren's standalone interpreter in interactive mode. You can type in a line of code, and it will immediately execute it. Here's something to try:
|
||||
|
||||
:::dart
|
||||
IO.print("Hello, world!")
|
||||
|
||||
Or a little more exciting:
|
||||
|
||||
:::dart
|
||||
for (i in 1..10) IO.print("Counting up ", i)
|
||||
|
||||
You can exit the interpreter using good old Ctrl-C or Ctrl-D, or just throw your computer to the ground and storm off.
|
||||
|
||||
## Running scripts
|
||||
|
||||
The standalone interpreter can also load scripts from files and run them. Just pass the name of the script to wren. Create a file named "my_script.wren" in your favorite text editor and paste this into it:
|
||||
|
||||
:::dart
|
||||
for (yPixel in 0...24) {
|
||||
var y = yPixel / 12 - 1
|
||||
for (xPixel in 0...80) {
|
||||
var x = xPixel / 30 - 2
|
||||
var x0 = x
|
||||
var y0 = y
|
||||
var iter = 0
|
||||
while (iter < 11 && x0 * x0 + y0 * y0 <= 4) {
|
||||
var x1 = (x0 * x0) - (y0 * y0) + x
|
||||
var y1 = 2 * x0 * y0 + y
|
||||
x0 = x1
|
||||
y0 = y1
|
||||
iter = iter + 1
|
||||
}
|
||||
IO.write(" .-:;+=xX$& "[iter])
|
||||
}
|
||||
|
||||
IO.print("")
|
||||
}
|
||||
|
||||
Now run:
|
||||
|
||||
:::bash
|
||||
$ ./wren my_script.wren
|
||||
|
||||
Neat, right? You're a Wren programmer now! The next step is to [read more docs](syntax.html) and learn your way around the language. If you run into bugs, or have ideas or questions, any and all of the following work:
|
||||
|
||||
* Tell me on twitter at [`@munificentbob`](https://twitter.com/intent/user?screen_name=munificentbob).
|
||||
* [File a ticket](https://github.com/munificent/wren/issues) at [the GitHub repo](https://github.com/munificent/wren).
|
||||
* Send a pull request.
|
||||
* Email me at [`bob@stuffwithstuff.com`](mailto:bob@stuffwithstuff.com).
|
||||
@ -1,39 +0,0 @@
|
||||
^title Welcome
|
||||
|
||||
Wren is a *small, clean, fast, class-based scripting language.* Think Smalltalk
|
||||
in a Lua-sized package.
|
||||
|
||||
:::dart
|
||||
IO.print("Hello, world!")
|
||||
|
||||
class Wren {
|
||||
adjectives = ["sm\nall", "clean", "fast"]
|
||||
languageType {
|
||||
"scripting"
|
||||
}
|
||||
}
|
||||
|
||||
* **Wren is small.** The codebase is under 4,000 semicolons which keeps the
|
||||
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][nan]. It's written in warning-free standard C99.
|
||||
|
||||
* **Wren is fast.** A fast single-pass compiler to 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.
|
||||
|
||||
* **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][embedding].
|
||||
|
||||
[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
|
||||
[embedding]: embedding.html
|
||||
@ -1,88 +0,0 @@
|
||||
^title Lists
|
||||
^category types
|
||||
|
||||
A list is a compound object that holds a collection of elements identified by integer index. You can create a list by placing a sequence of comma-separated expressions inside square brackets:
|
||||
|
||||
:::dart
|
||||
[1, "banana", true]
|
||||
|
||||
Here, we've created a list of three elements. Notice that the elements don't have to be the same type.
|
||||
|
||||
## Accessing elements
|
||||
|
||||
You can access an element from a list by calling the [subscript operator](method-calls.html#subscript-operators) on it with the index of the element you want. Like most languages, indexes start at zero:
|
||||
|
||||
:::dart
|
||||
var hirsute = ["sideburns", "porkchops", "'stache", "goatee"]
|
||||
hirsute[0] // "sideburns".
|
||||
hirsute[1] // "porkchops".
|
||||
|
||||
Negative indices counts backwards from the end:
|
||||
|
||||
:::dart
|
||||
hirsute[-1] // "goatee".
|
||||
hirsute[-2] // "'stache".
|
||||
|
||||
It's a runtime error to pass an index outside of the bounds of the list. If you don't know what those bounds are, you can find out using count:
|
||||
|
||||
:::dart
|
||||
hirsute.count // 3.
|
||||
|
||||
## Slices and ranges
|
||||
|
||||
Sometimes you want to slice out a chunk of elements from a list. You can do that by passing a [range](values.html#ranges) to the subscript operator, like so:
|
||||
|
||||
:::dart
|
||||
hirsute[1..2] // ["porkchops", "'stache"].
|
||||
|
||||
This returns a new list containing the elements of the original list whose indices are within the given range. Both inclusive and exclusive ranges work and do what you expect.
|
||||
|
||||
Negative bounds also work like they do when passing a single number, so to copy a list, you can just do:
|
||||
|
||||
:::dart
|
||||
hirsute[0..-1]
|
||||
|
||||
## Adding elements
|
||||
|
||||
Lists are *mutable*, meaning their contents can be changed. You can swap out an existing element in the list using the subscript setter:
|
||||
|
||||
:::dart
|
||||
hirsute[1] = "muttonchops"
|
||||
IO.print(hirsute[1]) // muttonchops.
|
||||
|
||||
It's an error to set an element that's out of bounds. To grow a list, you can use `add` to append a single item to the end:
|
||||
|
||||
:::dart
|
||||
hirsute.add("goatee")
|
||||
IO.print(hirsute.count) // 4.
|
||||
|
||||
You can insert a new element at a specific position using `insert`:
|
||||
|
||||
:::dart
|
||||
hirsute.insert("soul patch", 2)
|
||||
|
||||
The first argument is the value to insert, and the second is the index to insert it at. All elements following the inserted one will be pushed down to make room for it.
|
||||
|
||||
It's valid to "insert" after the last element in the list, but only *right* after it. Like other methods, you can use a negative index to count from the back. Doing so counts back from the size of the list *after* it's grown by one:
|
||||
|
||||
:::dart
|
||||
var letters = ["a", "b", "c"]
|
||||
letters.insert("d", 3) // OK: inserts at end.
|
||||
IO.print(letters) // ["a", "b", "c", "d"]
|
||||
letters.insert("e", -2) // Counts back from size after insert.
|
||||
IO.print(letters) // ["a", "b", "c", "e", "d"]
|
||||
|
||||
## Removing elements
|
||||
|
||||
The opposite of `insert` is `removeAt`. It removes a single element from a given position in the list. All following items are shifted up to fill in the gap:
|
||||
|
||||
:::dart
|
||||
var letters = ["a", "b", "c"]
|
||||
letters.removeAt(1)
|
||||
IO.print(letters) // ["a", "c"]
|
||||
|
||||
If you want to remove everything from the list, you can clear it:
|
||||
|
||||
:::dart
|
||||
hirsute.clear
|
||||
IO.print(hirsute) // []
|
||||
@ -1,103 +0,0 @@
|
||||
^title Looping
|
||||
^category language
|
||||
|
||||
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:
|
||||
|
||||
:::dart
|
||||
// Hailstone sequence.
|
||||
var n = 27
|
||||
while (n != 1) {
|
||||
if (n % 2 == 0) {
|
||||
n = n / 2
|
||||
} else {
|
||||
n = 3 * n + 1
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
:::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), 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.print(beatle)
|
||||
}
|
||||
|
||||
A `for` loop has three components:
|
||||
|
||||
1. A *variable name* to bind. In the example, that's `beatle`. Wren will create a new variable with that name whose scope is the body of the loop.
|
||||
|
||||
2. A *sequence expression*. This determines what you're looping over. It gets evaluated *once* before the body of the loop. In this case, it's a list literal, but it can be any expression.
|
||||
|
||||
3. A *body*. This is a curly block or a single statement. It gets executed once for each iteration of the loop.
|
||||
|
||||
## 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.
|
||||
|
||||
## Numeric ranges
|
||||
|
||||
Lists are one common use for `for` loops, but sometimes you want to walk over a sequence of numbers, or loop a number of times. For that, you can create a [range object](values.html#ranges), like so:
|
||||
|
||||
:::dart
|
||||
for (i in 1..100) {
|
||||
IO.print(i)
|
||||
}
|
||||
|
||||
This loops 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
|
||||
|
||||
Lists and ranges cover the two most common kinds of loops, but you should also be able to define your own sequences. 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.
|
||||
|
||||
When you write a loop like this:
|
||||
|
||||
:::dart
|
||||
for (i in 1..100) {
|
||||
IO.print(i)
|
||||
}
|
||||
|
||||
Wren sees it something like this:
|
||||
|
||||
:::dart
|
||||
var iter_ = null
|
||||
var seq_ = 1..100
|
||||
while (iter_ = seq_.iterate(iter_)) {
|
||||
var i = seq_.iteratorValue(iter_)
|
||||
IO.print(i)
|
||||
}
|
||||
|
||||
First, Wren evaluates the sequence expression and stores it in a hidden variable (written `seq_` in the example but in reality it doesn't have a name you can use). It also creates a hidden "iterator" variable and initializes it to `null`.
|
||||
|
||||
Each iteration, it calls `iterate()` on the sequence, passing in the current iterator value. (In the first iteration, it 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 the iterator is `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 the iterator value that it just got from calling `iterate()`. The sequence uses that to look up and return the appropriate element.
|
||||
|
||||
The built-in [List](lists.html) and [Range](values.html#ranges) 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.
|
||||
@ -1,4 +0,0 @@
|
||||
^title Maps
|
||||
^category types
|
||||
|
||||
Wren will have a built-in hash table type similar to objects in JavaScript and tables in Lua, but they haven't been implemented yet.
|
||||
@ -1,106 +0,0 @@
|
||||
^title Method Calls
|
||||
^category language
|
||||
|
||||
**TODO: Refactor `method-calls` and `classes` into using and creating classes.**
|
||||
|
||||
Wren is object-oriented, so most code consists of method calls. Most of them
|
||||
look like so:
|
||||
|
||||
:::dart
|
||||
IO.print("hello")
|
||||
items.add("another")
|
||||
items.insert(1, "value")
|
||||
|
||||
You have a *receiver* on the left, followed by a `.`, then a name and an
|
||||
argument list in parentheses. Semantically, a method call works like this:
|
||||
|
||||
1. Look up the class of the receiver.
|
||||
2. Look up the method on it by name.
|
||||
3. Invoke the method.
|
||||
|
||||
Methods that do not take any arguments leave off the `()`:
|
||||
|
||||
:::dart
|
||||
text.length
|
||||
|
||||
These are special "getters" or "accessors" in other languages. In Wren, they're
|
||||
just methods.
|
||||
|
||||
## Arity
|
||||
|
||||
Unlike most dynamic languages, the number of arguments to a method is part of
|
||||
its call signature. Methods with different signatures are distinct from each
|
||||
other. In technical terms, this means you can overload by *arity*.
|
||||
|
||||
In normal human terms, it means you can overload by number of parameters. These
|
||||
are calls to two different methods:
|
||||
|
||||
:::dart
|
||||
items.add("one arg")
|
||||
items.add("first", "second")
|
||||
|
||||
Instead of having a single `add` method where you have to check for "undefined"
|
||||
or missing arguments, Wren just treats them as different methods that you can
|
||||
implement separately.
|
||||
|
||||
## Setters
|
||||
|
||||
Modifying a public property of some object looks like you expect:
|
||||
|
||||
:::dart
|
||||
point.x = 123
|
||||
|
||||
You can probably guess by now, but again this is just another special syntax
|
||||
for a regular method call. The semantics for the above are "invoke the `x=`
|
||||
method on `point`, passing `123` as an argument."
|
||||
|
||||
## Operators
|
||||
|
||||
Wren has most of the same operators you know and love from C and friends, with
|
||||
the same precedence and associativity. They are listed here because they are
|
||||
just a special syntax for regular method calls.
|
||||
|
||||
Wren has a few prefix operators:
|
||||
|
||||
:::dart
|
||||
! ~ -
|
||||
|
||||
They are just method calls on the operand without any other arguments. An
|
||||
expression like `!possible` just means "call the `!` method on `possible`".
|
||||
|
||||
We have a few other operators to play with. The remaining ones are
|
||||
infix—they have operators on either side. In order of increasing
|
||||
precedence, they are:
|
||||
|
||||
:::dart
|
||||
== !=
|
||||
< > <= >=
|
||||
.. ...
|
||||
| &
|
||||
+ -
|
||||
* / %
|
||||
|
||||
Like prefix operators, they are just a funny way of writing a method call. The
|
||||
left operand is the receiver, and the right operand gets passed to it. So
|
||||
`a + b` is semantically interpreted as "invoke the `+` method on `a`, passing
|
||||
it `b`".
|
||||
|
||||
Most of these are probably familiar already. The `..` and `...` operators are
|
||||
"range" operators. The number type implements those and returns a range object,
|
||||
which can in turn be iterated over using a `for` [loop](looping.html).
|
||||
|
||||
## Subscript operators
|
||||
|
||||
Most languages use square brackets (`[]`) for working with collection-like
|
||||
objects. For example:
|
||||
|
||||
:::dart
|
||||
first = list[0]
|
||||
map["key"] = "value"
|
||||
|
||||
You know the refrain by now. In Wren, these are just method calls. Subscript
|
||||
operators can also be overloaded by arity, which is useful for things like
|
||||
multi-dimensional arrays:
|
||||
|
||||
:::dart
|
||||
table[3, 5] = "value"
|
||||
@ -1,155 +0,0 @@
|
||||
^title Performance
|
||||
^category reference
|
||||
|
||||
Even though most benchmarks aren't worth the pixels they're printed on, people seem to like them, so here's a few:
|
||||
|
||||
<h3>Method Call</h3>
|
||||
<table class="chart">
|
||||
<tr>
|
||||
<th>wren</th><td><div class="chart-bar wren" style="width: 99%;">5100 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>luajit (-joff)</th><td><div class="chart-bar" style="width: 87%;">4441 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>ruby</th><td><div class="chart-bar" style="width: 56%;">2868 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>lua</th><td><div class="chart-bar" style="width: 34%;">1742 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python3</th><td><div class="chart-bar" style="width: 17%;">884 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python</th><td><div class="chart-bar" style="width: 15%;">779 </div></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h3>DeltaBlue</h3>
|
||||
<table class="chart">
|
||||
<tr>
|
||||
<th>wren</th><td><div class="chart-bar wren" style="width: 99%;">7006 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python3</th><td><div class="chart-bar" style="width: 33%;">2333 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python</th><td><div class="chart-bar" style="width: 30%;">2141 </div></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h3>Binary Trees</h3>
|
||||
<table class="chart">
|
||||
<tr>
|
||||
<th>luajit (-joff)</th><td><div class="chart-bar" style="width: 99%;">6165 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>wren</th><td><div class="chart-bar wren" style="width: 54%;">3338 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>ruby</th><td><div class="chart-bar" style="width: 43%;">2685 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python3</th><td><div class="chart-bar" style="width: 31%;">1952 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>lua</th><td><div class="chart-bar" style="width: 22%;">1409 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python</th><td><div class="chart-bar" style="width: 21%;">1340 </div></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h3>Recursive Fibonacci</h3>
|
||||
<table class="chart">
|
||||
<tr>
|
||||
<th>luajit (-joff)</th><td><div class="chart-bar" style="width: 99%;">7061 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>ruby</th><td><div class="chart-bar" style="width: 43%;">3100 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>lua</th><td><div class="chart-bar" style="width: 40%;">2860 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>wren</th><td><div class="chart-bar wren" style="width: 34%;">2410 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python</th><td><div class="chart-bar" style="width: 17%;">1253 </div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>python3</th><td><div class="chart-bar" style="width: 17%;">1252 </div></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Longer bars are better. The score is the inverse of the running time, so if one language's score is twice another's, that means the language is twice as fast. Each benchmark is run ten times and the best time is kept. It only measures the time taken to execute the benchmarked code itself, not interpreter startup.
|
||||
|
||||
These were run on my MacBook Pro 2.3 GHz Intel Core i7 with 16 GB of 1,600 MHz DDR3 RAM. Tested against Lua 5.2.3, LuaJIT 2.0.2, Python 2.7.5, Python 3.3.4, ruby 2.0.0p247. LuaJIT is run with the JIT *disabled* (i.e. in bytecode interpreter mode) since I want to support platforms where JIT-compilation is disallowed. LuaJIT with the JIT enabled is *much* faster than all of the other languages benchmarked, including Wren, because Mike Pall is a robot from the future.
|
||||
|
||||
The benchmark harness and programs are [here](https://github.com/munificent/wren/tree/master/benchmark).
|
||||
|
||||
## Why is Wren fast?
|
||||
|
||||
Languages come in four rough performance buckets, from slowest to fastest:
|
||||
|
||||
1. Tree-walk interpreters: Ruby 1.8.7 and earlier, Io, that
|
||||
interpreter you wrote for a class in college.
|
||||
|
||||
2. Bytecode interpreters: CPython,
|
||||
Ruby 1.9 and later, Lua, early JavaScript VMs.
|
||||
|
||||
3. JIT compiled dynamically typed languages: Modern JavaScript VMs,
|
||||
LuaJIT, PyPy, some Lisp/Scheme implementations.
|
||||
|
||||
4. Statically typed languages: C, C++, Java, C#, Haskell, etc.
|
||||
|
||||
Most languages in the first bucket aren't suitable for production use. (Servers are one exception, because you can throw more hardware at a slow language there.) Languages in the second bucket are fast enough for many use cases, even on client hardware, as the success of the listed languages shows. Languages in the third bucket are quite fast, but their implementations are breathtakingly complex, often rivaling that of compilers for statically-typed languages.
|
||||
|
||||
Wren is in the second bucket. If you want a simple implementation that's fast enough for real use, this is the sweet spot. In addition, Wren has a few tricks up its sleeve:
|
||||
|
||||
### A compact value representation
|
||||
|
||||
A core piece of a dynamic language implementation is the data structure used for variables. It needs to be able to store (or reference) a value of any type, while also being as compact as possible. Wren uses a technique called *[NaN tagging][]* for this.
|
||||
|
||||
[nan tagging]: http://wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations
|
||||
|
||||
All values are stored internally in Wren as small, eight-byte double-precision floats. Since that is also Wren's number type, in order to do arithmetic, no conversion is needed before the "raw" number can be accessed: a value holding a number *is* a valid double. This keeps arithmetic fast.
|
||||
|
||||
To store values of other types, it turns out there's a ton of unused bits in a NaN double. You can stuff a pointer for heap-allocated objects, with room left over for special values like `true`, `false`, and `null`. This means numbers, bools, and null are unboxed. It also means an entire value is only eight bytes, the native word size on 64-bit machines. Smaller = faster when you take into account CPU caching and the cost of passing values around.
|
||||
|
||||
### Fixed object layout
|
||||
|
||||
Most dynamic languages treat objects as loose bags of named properties. You can freely add and remove properties from an object after you've created it. Languages like Lua and JavaScript don't even have a well-defined concept of a "type" of object.
|
||||
|
||||
Wren is strictly class-based. Every object is an instance of a class. Classes in turn have a well-defined declarative syntax, and cannot be imperatively modified. In addition, fields in Wren are private to the class—they can only be accessed from methods defined directly on that class.
|
||||
|
||||
Put all of that together and it means you can determine at *compile* time exactly how many fields an object has and what they are. In other languages, when you create an object, you allocate some initial memory for it, but that may have to be reallocated multiple times as fields are added and the object grows. Wren just does a single allocation up front for exactly the right number of fields.
|
||||
|
||||
Likewise, when you access a field in other languages, the interpreter has to look it up by name in a hash table in the object, and then maybe walk its inheritance chain if it can't find it. It must do this every time since fields may be added freely. In Wren, field access is just accessing a slot in the instance by an offset known at compile time: it's just adding a few pointers.
|
||||
|
||||
### Copy-down inheritance
|
||||
|
||||
When you call a method on an object, the method must be located. It could be defined directly on the object's class, or it may be inheriting it from some superclass. This means that in the worst case, you may have to walk the inheritance chain to find it.
|
||||
|
||||
Advanced implementations do very smart things to optimize this, but it's made more difficult by the mutable nature of the underlying language: if you can add new methods to existing classes freely or change the inheritance hierarchy, the lookup for a given method may actually change over time. You have to check for that which costs CPU cycles.
|
||||
|
||||
Wren's inheritance hierarchy is static and fixed at class definition time. This means that we can copy down all inherited methods in the subclass when it's created since we know those will never change. Method dispatch then just requires locating the method in the class of the receiver.
|
||||
|
||||
### Computed gotos
|
||||
|
||||
On compilers that support it, Wren's core bytecode interpreter loop uses something called [*computed gotos*](http://eli.thegreenplace.net/2012/07/12/computed-goto-for-efficient-dispatch-tables/). The hot core of a bytecode interpreter is effectively a giant `switch` on the instruction being executed.
|
||||
|
||||
Doing that using an actual `switch` confounds the CPU's [branch predictor](http://en.wikipedia.org/wiki/Branch_predictor): there is basically a single branch point for the entire interpreter. That quickly saturates the predictor and it just gets confused and fails to predict anything, which leads to more CPU stalls and pipeline flushes.
|
||||
|
||||
Using computed gotos gives you a separate branch point at the end of each instruction. Each gets its own branch prediction, which often succeeds since some instruction pairs are more common than others. In my rough testing, this makes a 5-10% performance difference.
|
||||
|
||||
### A single-pass compiler
|
||||
|
||||
Compile time is a relatively small component of a language's performance: code only has to be compiled once but a given line of code may be run many times. However, fast compilation helps with *startup* speed—the time it takes to get anything up and running. For that, Wren's compiler is quite fast.
|
||||
|
||||
It's modeled after Lua's compiler. Instead of tokenizing and then parsing to create a bunch of AST structures which are then consumed and deallocated by later phases, it emits code directly during parsing. This means it does minimal memory allocation during a parse and has very little overhead.
|
||||
|
||||
## Why don't other languages do this?
|
||||
|
||||
Most of Wren's performance comes from language design decisions. While it's dynamically *typed* and *dispatched*, classes are relatively statically *defined*. That makes a lot of things much easier. Other languages have a much more mutable object model, and cannot change that without breaking lots of existing code.
|
||||
|
||||
Wren's closest sibling, by far, is Lua. Lua is more dynamic than Wren which makes its job harder. Lua also tries very hard to be compatible across a wide range of hardware and compilers. If you have a C89 compiler for it, odds are very good that you can run Lua on it.
|
||||
|
||||
Wren cares about compatibility, but it requires C99 and IEEE double precision floats. That may exclude some edge case hardware, but makes things like NaN tagging, computed gotos, and some other tricks possible.
|
||||
@ -1,74 +0,0 @@
|
||||
^title Q & A
|
||||
^category reference
|
||||
|
||||
## Why did you create Wren?
|
||||
|
||||
Other creative endeavors aren't immediately met with existential crises, but for some reason programmers don't seem to like new languages. Here's the niche I'm trying to fill:
|
||||
|
||||
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".
|
||||
|
||||
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 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.
|
||||
|
||||
## Why classes?
|
||||
|
||||
Thanks to JavaScript's popularity, lots of people are discovering prototypes right now, and the paradigm is experiencing a popularity boom. I think prototypes are interesting, but after [several years playing with them](https://github.com/munificent/finch), I concluded (like many people on the original Self project that invented prototypes) that classes are more usable.
|
||||
|
||||
Here's an example of that kind of object-oriented programming in Lua:
|
||||
|
||||
:::lua
|
||||
Account = {}
|
||||
Account.__index = Account
|
||||
|
||||
function Account.create(balance)
|
||||
local acnt = {} -- our new object
|
||||
setmetatable(acnt,Account) -- make Account handle lookup
|
||||
acnt.balance = balance -- initialize our object
|
||||
return acnt
|
||||
end
|
||||
|
||||
function Account:withdraw(amount)
|
||||
self.balance = self.balance - amount
|
||||
end
|
||||
|
||||
-- create and use an Account
|
||||
acc = Account.create(1000)
|
||||
acc:withdraw(100)
|
||||
|
||||
Here's the same example in Wren:
|
||||
|
||||
:::dart
|
||||
class Account {
|
||||
new(balance) { _balance = balance }
|
||||
withdraw(amount) { _balance = _balance - amount }
|
||||
}
|
||||
|
||||
// create and use an Account
|
||||
var acc = new Account(100)
|
||||
acc.withdraw(100)
|
||||
|
||||
Classes have a reputation for complexity because most of the widely used languages with them are quite complex: C++, Java, C#, Ruby, and Python. I hope to show with Wren that is those languages that are complex, and not classes themselves.
|
||||
|
||||
Smalltalk, the language that inspired most of those languages, is famously simple. Its syntax [fits on an index card](http://www.jarober.com/blog/blogView?showComments=true&title=Readability+is+Key&entry=3506312690). My aim is to keep Wren that minimal while still having the expressive power of [classes](classes.html).
|
||||
|
||||
## Why compile to bytecode?
|
||||
|
||||
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:
|
||||
|
||||
* Many devices like iPhones and game consoles don't allow executing code generated at runtime, which rules out just-in-time compilation.
|
||||
* I think [fibers](fibers.html) are a really powerful tool, and implementing them is straightforward in a bytecode VM that 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](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/).
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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 pattern matching and multiple dispatch. I like how it integrates the event-based IO of [libuv](https://github.com/joyent/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.
|
||||
|
||||
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.
|
||||
@ -1,354 +0,0 @@
|
||||
$header: "Sanchez", helvetica, arial, sans-serif;
|
||||
$subheader: "Lato", helvetica, arial, sans-serif;
|
||||
$body: "Source Sans Pro", georgia, serif;
|
||||
$code: "Source Code Pro", Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
$dark: hsl(210, 10%, 25%);
|
||||
$darker: hsl(210, 20%, 10%);
|
||||
$light: hsl(0, 0%, 100%);
|
||||
$gray-10: mix($dark, $light, 10%);
|
||||
$gray-20: mix($dark, $light, 20%);
|
||||
$gray-30: mix($dark, $light, 30%);
|
||||
$gray-40: mix($dark, $light, 40%);
|
||||
$gray-50: mix($dark, $light, 50%);
|
||||
$gray-60: mix($dark, $light, 60%);
|
||||
$gray-80: mix($dark, $light, 80%);
|
||||
|
||||
$text: mix($light, #000, 20%);
|
||||
|
||||
$code-color: hsl(210, 20%, 40%);
|
||||
$code-bg: hsl(210, 20%, 98%);
|
||||
|
||||
$link: hsl(200, 60%, 50%);
|
||||
$link-hover: hsl(210, 100%, 80%);
|
||||
$link-dark: hsl(210, 60%, 20%);
|
||||
$link-glow: hsla(210, 100%, 50%, 0.4);
|
||||
|
||||
* {
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body, code, h1, h2, h3, p, pre {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background: $light;
|
||||
color: $text;
|
||||
font: 16px/25px $body;
|
||||
}
|
||||
|
||||
.page {
|
||||
margin: 0 auto;
|
||||
width: 800px;
|
||||
|
||||
// Clear contents.
|
||||
&:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
.main-column {
|
||||
position: relative;
|
||||
width: 560px;
|
||||
}
|
||||
|
||||
header {
|
||||
text-shadow: 0 1px 1px $darker;
|
||||
|
||||
.page {
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
background: $dark;
|
||||
border-bottom: solid 1px $darker;
|
||||
|
||||
h1 {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 63px;
|
||||
padding: 0;
|
||||
font: 400 48px $header;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 72px;
|
||||
padding: 0;
|
||||
font: 500 13px $subheader;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
color: $gray-50;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $gray-20;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: $link-hover;
|
||||
text-shadow: 0 0 6px $link-glow;
|
||||
}
|
||||
}
|
||||
|
||||
nav {
|
||||
float: right;
|
||||
width: 160px;
|
||||
padding-top: 110px;
|
||||
|
||||
h2 {
|
||||
color: $gray-30;
|
||||
font: 500 11px $subheader;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 6px 0 20px 0;
|
||||
}
|
||||
|
||||
li {
|
||||
font: 16px $body;
|
||||
color: $gray-30;
|
||||
list-style-type: none;
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding-top: 30px;
|
||||
font: 500 36px/60px $header;
|
||||
color: $link;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font: 500 24px $header;
|
||||
margin: 24px 0 0 0;
|
||||
color: $link;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font: 20px $body;
|
||||
margin: 24px 0 0 0;
|
||||
color: $link;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $link;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s, text-shadow 0.2s;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
main {
|
||||
@extend .main-column;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: $link-dark;
|
||||
}
|
||||
|
||||
.header-anchor {
|
||||
color: $light;
|
||||
}
|
||||
|
||||
h2:hover > .header-anchor,
|
||||
h3:hover > .header-anchor {
|
||||
color: $gray-10;
|
||||
}
|
||||
|
||||
h2:hover > .header-anchor:hover,
|
||||
h3:hover > .header-anchor:hover {
|
||||
color: $link-dark;
|
||||
}
|
||||
|
||||
p, li {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
p + p {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
code, pre {
|
||||
color: $code-color;
|
||||
font: 13px $code;
|
||||
background: $code-bg;
|
||||
border-radius: 2px;
|
||||
border: solid 1px hsl(200, 20%, 95%);
|
||||
border-bottom: solid 1px hsl(200, 20%, 90%);
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 1px 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 10px 0;
|
||||
line-height: 18px;
|
||||
padding: 10px;
|
||||
|
||||
// Scroll horizontally if not wide enough.
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 40px;
|
||||
padding: 20px 0 40px 0;
|
||||
font: 14px $body;
|
||||
background: $dark;
|
||||
color: $gray-20;
|
||||
border-top: solid 1px $darker;
|
||||
text-align: center;
|
||||
|
||||
text-shadow: 0 1px 1px $darker;
|
||||
|
||||
a {
|
||||
color: $link-hover;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: $link;
|
||||
}
|
||||
}
|
||||
|
||||
// Syntax highlighting.
|
||||
.codehilite pre {
|
||||
// Comments.
|
||||
span.c1, span.cm { color: mix($code-color, $code-bg, 60%); }
|
||||
|
||||
// Keywords.
|
||||
span.k, span.kd, span.kc { color: hsl(210, 80%, 60%); }
|
||||
|
||||
// Numbers.
|
||||
span.m, span.mi { color: hsl(90, 80%, 35%); }
|
||||
|
||||
// Strings.
|
||||
span.s, span.s2 { color: hsl(40, 80%, 50%); }
|
||||
span.se { color: hsl(30, 80%, 50%); }
|
||||
}
|
||||
|
||||
// Bar charts on the performance page.
|
||||
table.chart {
|
||||
width: 100%;
|
||||
padding-left: 25px;
|
||||
|
||||
td, th {
|
||||
line-height: 14px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
th {
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.chart-bar {
|
||||
display: inline-block;
|
||||
font: 13px $body;
|
||||
color: $light;
|
||||
background: $link;
|
||||
border-bottom: solid 1px $link-dark;
|
||||
text-align: right;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.chart-bar.wren {
|
||||
background: mix($link, $link-dark, 30%);
|
||||
border-bottom: solid 1px $link-dark;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 839px) {
|
||||
// 36 pixel columns.
|
||||
.page { width: 720px; }
|
||||
nav { width: 144px; }
|
||||
.main-column { width: 504px; }
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 759px) {
|
||||
// 32 pixel columns.
|
||||
.page { width: 640px; }
|
||||
nav { width: 128px; }
|
||||
.main-column { width: 448px; }
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 679px) {
|
||||
// 28 pixel columns.
|
||||
.page { width: 560px; }
|
||||
nav { width: 112px; }
|
||||
.main-column { width: 392px; }
|
||||
|
||||
header h2 {
|
||||
font-size: 12px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 639px) {
|
||||
.page { width: 100%; }
|
||||
|
||||
nav {
|
||||
float: none;
|
||||
width: 100%;
|
||||
padding: 10px 10px;
|
||||
margin: 0;
|
||||
background: $gray-10;
|
||||
text-align: center;
|
||||
|
||||
section {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
width: 30%;
|
||||
}
|
||||
}
|
||||
|
||||
.main-column {
|
||||
padding: 0 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
header {
|
||||
h1 {
|
||||
position: relative;
|
||||
top: 10px;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h2 {
|
||||
position: relative;
|
||||
top: 0;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
float: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 20px 20px 40px 20px;
|
||||
}
|
||||
}
|
||||
@ -1,107 +0,0 @@
|
||||
^title Syntax
|
||||
^category language
|
||||
|
||||
Wren's syntax is designed to be familiar to people coming from C-like languages while being as simple and expressive as possible within that framework.
|
||||
|
||||
Scripts are stored in plain text files with a `.wren` file extension. Wren does
|
||||
not compile ahead of time: programs are run directly from source, from top to
|
||||
bottom like a typical scripting language. (Internally, programs are compiled to
|
||||
bytecode for efficiency, but that's an implementation detail).
|
||||
|
||||
## Comments
|
||||
|
||||
Line comments start with `//` and end at the end of the line:
|
||||
|
||||
:::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:
|
||||
|
||||
:::dart
|
||||
/* This is /* a nested */ comment. */
|
||||
|
||||
## Reserved words
|
||||
|
||||
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:
|
||||
|
||||
:::dart
|
||||
break class else for if in is return static var while
|
||||
|
||||
Wren also has a few predefined identifiers:
|
||||
|
||||
:::dart
|
||||
false null this true
|
||||
|
||||
## Names
|
||||
|
||||
Identifiers are similar to other programming languages. They start with a letter or underscore and may contain letters, digits, and underscores. Case is sensitive.
|
||||
|
||||
:::dart
|
||||
hi
|
||||
camelCase
|
||||
PascalCase
|
||||
_under_score
|
||||
abc123
|
||||
ALL_CAPS
|
||||
|
||||
Identifiers that start with underscore (`_`) are special in Wren. They are used to indicate fields in [classes](classes.html).
|
||||
|
||||
## Statement terminators
|
||||
|
||||
Officially, statements are terminated by a semicolon (`;`) like in other
|
||||
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.
|
||||
|
||||
:::dart
|
||||
// Two statements:
|
||||
IO.print("hi")
|
||||
IO.print("bye")
|
||||
|
||||
Sometimes, though, a statement doesn't fit on a single line and treating the
|
||||
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.
|
||||
|
||||
Everywhere else, a newline is treated just like a `;`. Note that this is a very
|
||||
different system from how JavaScript handles semicolons. If you've been burned
|
||||
there, don't worry, you should be fine here.
|
||||
|
||||
## Blocks
|
||||
|
||||
Wren uses curly braces to define *blocks*. Things like [control flow](branching.html) and [looping](looping.html) allow block bodies. [Method](method-calls.html) and [function](functions.html) bodies are also blocks. For example:
|
||||
|
||||
:::dart
|
||||
if (happy && knowIt) {
|
||||
hands.clap
|
||||
} else IO.print("sad")
|
||||
|
||||
Here we have a block for the then case, and just a single expression for the else. Blocks have two similar but not identical forms. If a there is a newline after the opening `{`, then the body contains a series of statements:
|
||||
|
||||
:::dart
|
||||
{
|
||||
IO.print("one")
|
||||
IO.print("two")
|
||||
IO.print("three")
|
||||
}
|
||||
|
||||
If there is no newline, the block may only contain a single expression:
|
||||
|
||||
:::dart
|
||||
{ "this is fine" }
|
||||
{ while (this) "is an error" }
|
||||
|
||||
These are useful when defining method and function bodies. A normal block body implicitly returns `null`. If you want your method or function to return something different, you need an explicit `return` statement. However, a single-expression block with no newline after the `{` implicitly returns the result of that expression. This is a nice convenience for short methods and functions that just evaluate and return an expression.
|
||||
|
||||
**TODO: Move this somewhere else:**
|
||||
|
||||
## The `is` operator
|
||||
|
||||
The `is` keyword can be used as an infix operator in expression. It performs a
|
||||
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, maps**
|
||||
@ -1,72 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>{title} – Wren</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="index.html">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>language</h2>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="branching.html">Branching</a></li>
|
||||
<li><a href="looping.html">Looping</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>types</h2>
|
||||
<ul>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="fibers.html">Fibers</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>reference</h2>
|
||||
<ul>
|
||||
<li><a href="core-library.html">Core Library</a></li>
|
||||
<li><a href="embedding-api.html">Embedding API</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="contributing.html">Contributing</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>{title}</h1>
|
||||
{html}
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives <a href="https://github.com/munificent/wren">on GitHub</a> — Made with ❤ by <a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a>.</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,66 +0,0 @@
|
||||
^title Values
|
||||
^category types
|
||||
|
||||
Values are the built-in object types that all other objects are composed of.
|
||||
They can be created through *literals*, expressions that evaluate to a value.
|
||||
|
||||
## Booleans
|
||||
|
||||
A boolean value represents truth or falsehood. There are two boolean literals,
|
||||
`true` and `false`. Its class is `Bool`.
|
||||
|
||||
## Numbers
|
||||
|
||||
Like other scripting languages, Wren has a single numeric type: double-precision floating point. Number literals look like you expect coming from other languages:
|
||||
|
||||
:::dart
|
||||
0
|
||||
1234
|
||||
-5678
|
||||
3.14159
|
||||
1.0
|
||||
-12.34
|
||||
|
||||
Numbers are instances of the `Num` class.
|
||||
|
||||
## Strings
|
||||
|
||||
Strings are chunks of text. String literals are surrounded in double quotes:
|
||||
|
||||
:::dart
|
||||
"hi there"
|
||||
|
||||
A couple of escape characters are supported:
|
||||
|
||||
:::dart
|
||||
"\n" // Newline.
|
||||
"\"" // A double quote character.
|
||||
"\\" // A backslash.
|
||||
|
||||
Their class is `String`.
|
||||
|
||||
## Ranges
|
||||
|
||||
A range is a little object that represents a consecutive range of numbers. They don't have their own dedicated literal syntax. Instead, the number class implements `..` and `...` operators to create them:
|
||||
|
||||
:::dart
|
||||
3..8
|
||||
|
||||
This creates a range from three two eight, including eight itself. If you want a half-inclusive range, use `...`:
|
||||
|
||||
:::dart
|
||||
4..6
|
||||
|
||||
This creates a range from four to six *not* including six itself. Ranges are commonly used for [looping](looping.html) over a sequences of numbers, but are useful in other places too. You can pass them to a [list](lists.html)'s subscript operator to return a subset of the list, for example:
|
||||
|
||||
:::dart
|
||||
var list = ["a", "b", "c", "d", "e"]
|
||||
var slice = list[1..3]
|
||||
IO.print(slice) // ["b", "c", "d"]
|
||||
|
||||
## Null
|
||||
|
||||
Wren has a special value `null`, which is the only instance of the class `Null`.
|
||||
(Note the difference in case.) It functions a bit like `void` in some
|
||||
languages: it indicates the absence of a value. If you call a method that
|
||||
doesn't return anything and get its returned value, you get `null` back.
|
||||
@ -1,67 +0,0 @@
|
||||
^title Variables
|
||||
^category language
|
||||
|
||||
Variables are named slots for storing values. You can define a new variable in
|
||||
Wren using a `var` statement, like so:
|
||||
|
||||
:::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.
|
||||
|
||||
:::dart
|
||||
var animal = "Slow Loris"
|
||||
IO.print(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.
|
||||
|
||||
:::dart
|
||||
{
|
||||
IO.print(a) // ERROR! a doesn't exist yet.
|
||||
var a = 123
|
||||
IO.print(a) // "123"
|
||||
}
|
||||
IO.print(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).
|
||||
|
||||
:::dart
|
||||
var a = "outer"
|
||||
{
|
||||
var a = "inner"
|
||||
IO.print(a) // Prints "inner".
|
||||
}
|
||||
IO.print(a) // Prints "outer".
|
||||
|
||||
Declaring a variable with the same name in the *same* scope *is* an error.
|
||||
|
||||
:::dart
|
||||
var a = "hi"
|
||||
var a = "again" // ERROR!
|
||||
|
||||
## Assignment
|
||||
|
||||
After a variable has been declared, you can assign to it using `=`:
|
||||
|
||||
:::dart
|
||||
var a = 123
|
||||
a = 234
|
||||
|
||||
An assignment walks up the scope stack to find where the named variable is
|
||||
declared. It's an error to assign to a variable that isn't defined. Wren
|
||||
doesn't roll with implicit variable definition.
|
||||
|
||||
When used in a larger expression, an assignment expression evaluates to the
|
||||
assigned value.
|
||||
|
||||
:::dart
|
||||
var a = "before"
|
||||
IO.print(a = "after") // Prints "after".
|
||||
203
embedding/calling-c-from-wren.html
Normal file
203
embedding/calling-c-from-wren.html
Normal file
@ -0,0 +1,203 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Calling C from Wren – Wren</title>
|
||||
<script type="application/javascript" src="../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="embedding">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../"><img src="../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../">Back to Wren</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>embedding</h2>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
|
||||
<li><a href="storing-c-data.html">Storing C Data</a></li>
|
||||
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><h2>embedding</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
|
||||
<li><a href="storing-c-data.html">Storing C Data</a></li>
|
||||
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Calling C from Wren</h1>
|
||||
<p>When we are ensconced within the world of Wren, the external C world is
|
||||
“foreign” to us. There are two reasons we might want to bring some foreign
|
||||
flavor into our VM:</p>
|
||||
<ul>
|
||||
<li>We want to execute code written in C.</li>
|
||||
<li>We want to store raw C data.</li>
|
||||
</ul>
|
||||
<p>Since Wren is object-oriented, behavior lives in methods, so for the former we
|
||||
have <strong>foreign methods</strong>. Likewise, data lives in objects, so for the latter, we
|
||||
define <strong>foreign classes</strong>. This page is about the first, foreign methods. The
|
||||
<a href="/embedding/storing-c-data.html">next page</a> covers foreign classes.</p>
|
||||
<p>A foreign method looks to Wren like a regular method. It is defined on a Wren
|
||||
class, it has a name and signature, and calls to it are dynamically dispatched.
|
||||
The only difference is that the <em>body</em> of the method is written in C.</p>
|
||||
<p>A foreign method is declared in Wren like so:</p>
|
||||
<pre class="snippet">
|
||||
class Math {
|
||||
foreign static add(a, b)
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The <code>foreign</code> keyword tells Wren that the method <code>add()</code> is declared on <code>Math</code>,
|
||||
but implemented in C. Both static and instance methods can be foreign.</p>
|
||||
<h2>Binding Foreign Methods <a href="#binding-foreign-methods" name="binding-foreign-methods" class="header-anchor">#</a></h2>
|
||||
<p>When you call a foreign method, Wren needs to figure out which C function to
|
||||
execute. This process is called <em>binding</em>. Binding is performed on-demand by the
|
||||
VM. When a class that declares a foreign method is executed – when the <code>class</code>
|
||||
statement itself is evaluated – the VM asks the host application for the C
|
||||
function that should be used for the foreign method.</p>
|
||||
<p>It does this through the <code>bindForeignMethodFn</code> callback you give it when you
|
||||
first <a href="configuring-the-vm.html">configure the VM</a>. This callback isn’t the foreign method itself.
|
||||
It’s the binding function your app uses to <em>look up</em> foreign methods.</p>
|
||||
<p>Its signature is:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenForeignMethodFn bindForeignMethodFn(
|
||||
WrenVM* vm,
|
||||
const char* module,
|
||||
const char* className,
|
||||
bool isStatic,
|
||||
const char* signature);
|
||||
</pre>
|
||||
|
||||
<p>Every time a foreign method is first declared, the VM invokes this callback. It
|
||||
passes in the module containing the class declaration, the name of the class
|
||||
containing the method, the method’s signature, and whether or not it’s a static
|
||||
method. In the above example, it would pass something like:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
bindForeignMethodFn(vm, "main", "Math", true, "add(_,_)");
|
||||
</pre>
|
||||
|
||||
<p>When you configure the VM, you give it a C callback that looks up the
|
||||
appropriate function for the given foreign method and returns a pointer to it.
|
||||
Something like:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenForeignMethodFn bindForeignMethod(
|
||||
WrenVM* vm,
|
||||
const char* module,
|
||||
const char* className,
|
||||
bool isStatic,
|
||||
const char* signature)
|
||||
{
|
||||
if (strcmp(module, "main") == 0)
|
||||
{
|
||||
if (strcmp(className, "Math") == 0)
|
||||
{
|
||||
if (isStatic && strcmp(signature, "add(_,_)") == 0)
|
||||
{
|
||||
return mathAdd; // C function for Math.add(_,_).
|
||||
}
|
||||
// Other foreign methods on Math...
|
||||
}
|
||||
// Other classes in main...
|
||||
}
|
||||
// Other modules...
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>This implementation is pretty tedious, but you get the idea. Feel free to do
|
||||
something more clever here in your host application.</p>
|
||||
<p>The important part is that it returns a pointer to a C function to use for that
|
||||
foreign method. Wren does this binding step <em>once</em> when the class definition is
|
||||
first executed. It then keeps the function pointer you return and associates it
|
||||
with that method. This way, <em>calls</em> to the foreign method are fast.</p>
|
||||
<h2>Implementing a Foreign Method <a href="#implementing-a-foreign-method" name="implementing-a-foreign-method" class="header-anchor">#</a></h2>
|
||||
<p>All C functions for foreign methods have the same signature:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void foreignMethod(WrenVM* vm);
|
||||
</pre>
|
||||
|
||||
<p>Arguments passed from Wren are not passed as C arguments, and the method’s
|
||||
return value is not a C return value. Instead – you guessed it – we go through
|
||||
the <a href="/embedding/slots-and-handles.html">slot array</a>.</p>
|
||||
<p>When a foreign method is called from Wren, the VM sets up the slot array with
|
||||
the receiver and arguments to the call. As in calling Wren from C, the receiver
|
||||
object is in slot zero, and arguments are in consecutive slots after that.</p>
|
||||
<p>You use the slot API to read those arguments, and then perform whatever work you
|
||||
want to in C. If you want the foreign method to return a value, place it in slot
|
||||
zero. Like so:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void mathAdd(WrenVM* vm)
|
||||
{
|
||||
double a = wrenGetSlotDouble(vm, 1);
|
||||
double b = wrenGetSlotDouble(vm, 2);
|
||||
wrenSetSlotDouble(vm, 0, a + b);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>While your foreign method is executing, the VM is completely suspended. No other
|
||||
fibers run until your foreign method returns. You should <em>not</em> try to resume the
|
||||
VM from within a foreign method by calling <code>wrenCall()</code> or <code>wrenInterpret()</code>.
|
||||
The VM is not re-entrant.</p>
|
||||
<p>This covers foreign behavior, but what about foreign <em>state</em>? For that, we need
|
||||
a foreign <em>class</em>…</p>
|
||||
<p><a class="right" href="storing-c-data.html">Storing C Data →</a>
|
||||
<a href="calling-wren-from-c.html">← Calling Wren from C</a></p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
237
embedding/calling-wren-from-c.html
Normal file
237
embedding/calling-wren-from-c.html
Normal file
@ -0,0 +1,237 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Calling Wren from C – Wren</title>
|
||||
<script type="application/javascript" src="../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="embedding">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../"><img src="../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../">Back to Wren</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>embedding</h2>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
|
||||
<li><a href="storing-c-data.html">Storing C Data</a></li>
|
||||
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><h2>embedding</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
|
||||
<li><a href="storing-c-data.html">Storing C Data</a></li>
|
||||
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Calling Wren from C</h1>
|
||||
<p>From C, we can tell Wren to do stuff by calling <code>wrenInterpret()</code>, but that’s
|
||||
not always the ideal way to drive the VM. First of all, it’s slow. It has to
|
||||
parse and compile the string of source code you give it. Wren has a pretty fast
|
||||
compiler, but that’s still a good bit of work.</p>
|
||||
<p>It’s also not an effective way to communicate. You can’t pass arguments to
|
||||
Wren—at least, not without doing something nasty like converting them to
|
||||
literals in a string of source code—and you can’t get a result value back.</p>
|
||||
<p><code>wrenInterpret()</code> is great for loading code into the VM, but it’s not the best
|
||||
way to execute code that’s already been loaded. What we want to do is invoke
|
||||
some already compiled chunk of code. Since Wren is an object-oriented language,
|
||||
“chunk of code” means a <a href="../method-calls.html">method</a>, not a <a href="../functions.html">function</a>.</p>
|
||||
<p>The C API for doing this is <code>wrenCall()</code>. In order to invoke a Wren method from
|
||||
C, we need a few things:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>The method to call.</strong> Wren is dynamically typed, so this means we’ll look it
|
||||
up by name. Further, since Wren supports overloading by arity, we actually
|
||||
need its entire <a href="../method-calls.html#signature">signature</a>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>The receiver object to invoke the method on.</strong> The receiver’s class
|
||||
determines which method is actually called.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>The arguments to pass to the method.</strong></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>We’ll tackle these one at a time.</p>
|
||||
<h3>Getting a Method Handle <a href="#getting-a-method-handle" name="getting-a-method-handle" class="header-anchor">#</a></h3>
|
||||
<p>When you run a chunk of Wren code like this:</p>
|
||||
<pre class="snippet">
|
||||
object.someMethod(1, 2, 3)
|
||||
</pre>
|
||||
|
||||
<p>At runtime, the VM has to look up the class of <code>object</code> and find a method there
|
||||
whose signature is <code>someMethod(_,_,_)</code>. This sounds like it’s doing some string
|
||||
manipulation—at the very least hashing the signature—every time a
|
||||
method is called. That’s how many dynamic languages work.</p>
|
||||
<p>But, as you can imagine, that’s pretty slow. So, instead, Wren does as much of
|
||||
that work at compile time as it can. When it’s compiling the above code to
|
||||
bytecode, it takes that method signature a converts it to a <em>method symbol</em>, a
|
||||
number that uniquely identifes that method. That’s the only part of the process
|
||||
that requires treating a signature as a string.</p>
|
||||
<p>At runtime, the VM just looks for the method <em>symbol</em> in the receiver’s class’s
|
||||
method table. In fact, the way it’s implemented today, the symbol is simply the
|
||||
array index into the table. That’s <a href="../performance.html">why method calls are so fast</a> in Wren.</p>
|
||||
<p>It would be a shame if calling a method from C didn’t have that same speed
|
||||
benefit. To achieve that, we split the process of calling a method into two
|
||||
steps. First, we create a handle that represents a “compiled” method signature:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature);
|
||||
</pre>
|
||||
|
||||
<p>That takes a method signature as a string and gives you back an opaque handle
|
||||
that represents the compiled method symbol. Now you have a <em>reusable</em> handle
|
||||
that can be used to very quickly call a certain method given a receiver and some
|
||||
arguments.</p>
|
||||
<p>This is just a regular WrenHandle, which means you can hold onto it as long as
|
||||
you like. Typically, you’d call this once outside of your application’s
|
||||
performance critical loops and reuse it as long as you need. It is us up to you
|
||||
to release it when you no longer need it by calling <code>wrenReleaseHandle()</code>.</p>
|
||||
<h2>Setting Up a Receiver <a href="#setting-up-a-receiver" name="setting-up-a-receiver" class="header-anchor">#</a></h2>
|
||||
<p>OK, we have a method, but who are we calling it on? We need a receiver, and as
|
||||
you can probably guess after reading the <a href="slots-and-handles.html">last section</a>, we give that to Wren
|
||||
by storing it in a slot. In particular, <strong>the receiver for a method call goes in
|
||||
slot zero.</strong></p>
|
||||
<p>Any object you store in that slot can be used as a receiver. You could even call
|
||||
<code>+</code> on a number by storing a number in there if you felt like it.</p>
|
||||
<p>Needing a receiver to call some Wren code from C might feel strange. C is
|
||||
procedural, so it’s natural to want to just invoke a bare <em>function</em> from Wren,
|
||||
but Wren isn’t procedural. Instead, if you want to define some executable
|
||||
operation that isn’t logically tied to a specific object, the natural way is to
|
||||
define a static method on an appropriate class.</p>
|
||||
<p>For example, say we’re making a game engine. From C, we want to tell the game
|
||||
engine to update all of the entities each frame. We’ll keep track of the list of
|
||||
entities within Wren, so from C, there’s no obvious object to call <code>update(_)</code>
|
||||
on. Instead, we’ll just make it a static method:</p>
|
||||
<pre class="snippet">
|
||||
class GameEngine {
|
||||
static update(elapsedTime) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Often, when you call a Wren method from C, you’ll be calling a static method.
|
||||
But, even then, you need a receiver. Now, though, the receiver is the <em>class
|
||||
itself</em>. Classes are first class objects in Wren, and when you define a named
|
||||
class, you’re really declaring a variable with the class’s name and storing a
|
||||
reference to the class object in it.</p>
|
||||
<p>Assuming you declared that class at the top level, the C API <a href="slots-and-handles.html#looking-up-variables">gives you a way to
|
||||
look it up</a>. We can get a handle to the above class like so:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
// Load the class into slot 0.
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "main", "GameEngine", 0);
|
||||
</pre>
|
||||
|
||||
<p>We could do this every time we call <code>update()</code>, but, again, that’s kind of slow
|
||||
because we’re looking up “GameEngine” by name each time. A faster solution is to
|
||||
create a handle to the class once and use it each time:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
// Load the class into slot 0.
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "main", "GameEngine", 0);
|
||||
WrenHandle* gameEngineClass = wrenGetSlotHandle(vm, 0);
|
||||
</pre>
|
||||
|
||||
<p>Now, each time we want to call a method on GameEngine, we store that value back
|
||||
in slot zero:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
wrenSetSlotHandle(vm, 0, gameEngineClass);
|
||||
</pre>
|
||||
|
||||
<p>Just like we hoisted <code>wrenMakeCallHandle()</code> out of our performance critical
|
||||
loop, we can hoist the call to <code>wrenGetVariable()</code> out. Of course, if your code
|
||||
isn’t performance critical, you don’t have to do this.</p>
|
||||
<h2>Passing Arguments <a href="#passing-arguments" name="passing-arguments" class="header-anchor">#</a></h2>
|
||||
<p>We’ve got a receiver in slot zero now, next we need to pass in any other
|
||||
arguments. In our GameEngine example, that’s just the elapsed time. Method
|
||||
arguments go in consecutive slots after the receiver. So the elapsed time goes
|
||||
into slot one. You can use any of the slot functions to set this up. For the
|
||||
example, it’s just:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
wrenSetSlotDouble(vm, 1, elapsedTime);
|
||||
</pre>
|
||||
|
||||
<h2>Calling the Method <a href="#calling-the-method" name="calling-the-method" class="header-anchor">#</a></h2>
|
||||
<p>We have all of the data in place, so all that’s left is to pull the trigger and
|
||||
tell the VM to start running some code:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method);
|
||||
</pre>
|
||||
|
||||
<p>It takes the method handle we created using <code>wrenMakeCallHandle()</code>. Now Wren
|
||||
starts running code. It looks up the method on the receiver, executes it and
|
||||
keeps running until either the method returns or a fiber <a href="../modules/core/fiber.html#fiber.suspend()">suspends</a>.</p>
|
||||
<p><code>wrenCall()</code> returns the same WrenInterpretResult enum as <code>wrenInterpret()</code> to
|
||||
tell you if the method completed successfully or a runtime error occurred.
|
||||
(<code>wrenCall()</code> never returns <code>WREN_ERROR_COMPILE</code> since it doesn’t compile
|
||||
anything.)</p>
|
||||
<h2>Getting the Return Value <a href="#getting-the-return-value" name="getting-the-return-value" class="header-anchor">#</a></h2>
|
||||
<p>When <code>wrenCall()</code> returns, it leaves the slot array in place. In slot zero, you
|
||||
can find the method’s return value, which you can access using any of the slot
|
||||
reading functions. If you don’t need the return value, you can ignore it.</p>
|
||||
<p>This is how you drive Wren from C, but how do you put control in Wren’s hands?
|
||||
For that, you’ll need the next section…</p>
|
||||
<p><a class="right" href="calling-c-from-wren.html">Calling C From Wren →</a>
|
||||
<a href="slots-and-handles.html">← Slots and Handles</a></p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
272
embedding/configuring-the-vm.html
Normal file
272
embedding/configuring-the-vm.html
Normal file
@ -0,0 +1,272 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Configuring the VM – Wren</title>
|
||||
<script type="application/javascript" src="../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="embedding">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../"><img src="../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../">Back to Wren</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>embedding</h2>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
|
||||
<li><a href="storing-c-data.html">Storing C Data</a></li>
|
||||
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><h2>embedding</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
|
||||
<li><a href="storing-c-data.html">Storing C Data</a></li>
|
||||
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Configuring the VM</h1>
|
||||
<p>When you create a Wren VM, you tweak it by passing in a pointer to a
|
||||
WrenConfiguration structure. Since Wren has no global state, you can configure
|
||||
each VM differently if your application happens to run multiple.</p>
|
||||
<p>The struct looks like:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
typedef struct
|
||||
{
|
||||
WrenReallocateFn reallocateFn;
|
||||
WrenLoadModuleFn loadModuleFn;
|
||||
WrenBindForeignMethodFn bindForeignMethodFn;
|
||||
WrenBindForeignClassFn bindForeignClassFn;
|
||||
WrenWriteFn writeFn;
|
||||
WrenErrorFn errorFn;
|
||||
size_t initialHeapSize;
|
||||
size_t minHeapSize;
|
||||
int heapGrowthPercent;
|
||||
} WrenConfiguration;
|
||||
</pre>
|
||||
|
||||
<p>Most fields have useful defaults, which you can (and should) initialize by
|
||||
calling:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
wrenInitConfiguration(&configuration);
|
||||
</pre>
|
||||
|
||||
<p>Calling this ensures that your VM doesn’t get uninitialized configuration when
|
||||
new fields are added to WrenConfiguration. Here is what each field does, roughly
|
||||
categorized:</p>
|
||||
<h2>Binding <a href="#binding" name="binding" class="header-anchor">#</a></h2>
|
||||
<p>The VM is isolated from the outside world. These callbacks let the VM request
|
||||
access to imported code and foreign functionality.</p>
|
||||
<h3><strong><code>loadModuleFn</code></strong> <a href="#loadmodulefn" name="loadmodulefn" class="header-anchor">#</a></h3>
|
||||
<p>This is the callback Wren uses to load an imported module. The VM itself does
|
||||
not know how to talk to the file system, so when an <code>import</code> statement is
|
||||
executed, it relies on the host application to locate and read the source code
|
||||
for a module.</p>
|
||||
<p>The signature of this function is:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenLoadModuleResult loadModule(WrenVM* vm, const char* name)
|
||||
</pre>
|
||||
|
||||
<p>When a module is imported, Wren calls this and passes in the module’s name. The
|
||||
host should return the source code for that module in a <code>WrenLoadModuleResult</code> struct.</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenLoadModuleResult myLoadModule(WrenVM* vm, const char* name) {
|
||||
WrenLoadModuleResult result = {0};
|
||||
result.source = getSourceForModule(name);
|
||||
return result;
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The module loader is only be called once for any given module name. Wren caches
|
||||
the result internally so subsequent imports of the same module use the
|
||||
previously loaded code.</p>
|
||||
<p>If your host application isn’t able to load a module with some name, it should
|
||||
make sure the <code>source</code> value is <code>NULL</code> when returned. Wren will then report that as a runtime error.</p>
|
||||
<p>If you don’t use any <code>import</code> statements, you can leave the <code>loadModuleFn</code> field in
|
||||
the configuration set to <code>NULL</code> (the default).</p>
|
||||
<p>Additionally, the <code>WrenLoadModuleResult</code> allows us to add a callback for when Wren is
|
||||
done with the <code>source</code>, so we can free the memory if needed.</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
|
||||
static void loadModuleComplete(WrenVM* vm,
|
||||
const char* module,
|
||||
WrenLoadModuleResult result)
|
||||
{
|
||||
if(result.source) {
|
||||
//for example, if we used malloc to allocate
|
||||
//our source string, we use free to release it.
|
||||
free((void*)result.source);
|
||||
}
|
||||
}
|
||||
|
||||
WrenLoadModuleResult myLoadModule(WrenVM* vm, const char* name) {
|
||||
WrenLoadModuleResult result = {0};
|
||||
result.onComplete = loadModuleComplete;
|
||||
result.source = getSourceForModule(name);
|
||||
return result;
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3><strong><code>bindForeignMethodFn</code></strong> <a href="#bindforeignmethodfn" name="bindforeignmethodfn" class="header-anchor">#</a></h3>
|
||||
<p>The callback Wren uses to find a foreign method and bind it to a class. See
|
||||
<a href="/embedding/calling-c-from-wren.html">this page</a> for details. If your application defines no foreign
|
||||
methods, you can leave this <code>NULL</code>.</p>
|
||||
<h3><strong><code>bindForeignClassFn</code></strong> <a href="#bindforeignclassfn" name="bindforeignclassfn" class="header-anchor">#</a></h3>
|
||||
<p>The callback Wren uses to find a foreign class and get its foreign methods. See
|
||||
<a href="/embedding/storing-c-data.html">this page</a> for details. If your application defines no foreign
|
||||
classes, you can leave this <code>NULL</code>.</p>
|
||||
<h2>Diagnostics <a href="#diagnostics" name="diagnostics" class="header-anchor">#</a></h2>
|
||||
<p>These let you wire up some minimal output so you can tell if your code is doing
|
||||
what you expect.</p>
|
||||
<h3><strong><code>writeFn</code></strong> <a href="#writefn" name="writefn" class="header-anchor">#</a></h3>
|
||||
<p>This is the callback Wren uses to output text when <code>System.print()</code> or the other
|
||||
related functions are called. This is the minimal connection the VM has with the
|
||||
outside world and lets you do rudimentary “printf debugging”. Its signature is:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void write(WrenVM* vm, const char* text)
|
||||
</pre>
|
||||
|
||||
<p>Wren does <em>not</em> have a default implementation for this. It’s up to you to wire
|
||||
it up to <code>printf()</code> or some other way to show the text. If you leave it <code>NULL</code>,
|
||||
calls to <code>System.print()</code> and others silently do nothing.</p>
|
||||
<h3><strong><code>errorFn</code></strong> <a href="#errorfn" name="errorfn" class="header-anchor">#</a></h3>
|
||||
<p>Wren uses this callback to report compile time and runtime errors. Its signature
|
||||
is:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void error(
|
||||
WrenVM* vm,
|
||||
WrenErrorType type,
|
||||
const char* module,
|
||||
int line,
|
||||
const char* message)
|
||||
</pre>
|
||||
|
||||
<p>The <code>type</code> parameter is one of:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
typedef enum
|
||||
{
|
||||
// A syntax or resolution error detected at compile time.
|
||||
WREN_ERROR_COMPILE,
|
||||
|
||||
// The error message for a runtime error.
|
||||
WREN_ERROR_RUNTIME,
|
||||
|
||||
// One entry of a runtime error's stack trace.
|
||||
WREN_ERROR_STACK_TRACE
|
||||
} WrenErrorType;
|
||||
</pre>
|
||||
|
||||
<p>When a compile error occurs, <code>errorFn</code> is called once with type
|
||||
<code>WREN_ERROR_COMPILE</code>, the name of the module and line where the error occurs,
|
||||
and the error message.</p>
|
||||
<p>Runtime errors include stack traces. To handle this, Wren first calls <code>errorFn</code>
|
||||
with <code>WREN_ERROR_RUNTIME</code>, no module or line, and the runtime error’s message.
|
||||
After that, it calls <code>errorFn</code> again using type <code>WREN_ERROR_STACK_TRACE</code>, once
|
||||
for each line in the stack trace. Each of those calls has the module and line
|
||||
where the method or function is defined and <code>message</code> is the name of the method
|
||||
or function.</p>
|
||||
<p>If you leave this <code>NULL</code>, Wren does not report any errors.</p>
|
||||
<h2>Memory Management <a href="#memory-management" name="memory-management" class="header-anchor">#</a></h2>
|
||||
<p>These fields control how the VM allocates and manages memory.</p>
|
||||
<h3><strong><code>reallocateFn</code></strong> <a href="#reallocatefn" name="reallocatefn" class="header-anchor">#</a></h3>
|
||||
<p>This lets you provide a custom memory allocation function. Its signature is:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void* reallocate(void* memory, size_t newSize, void* userData)
|
||||
</pre>
|
||||
|
||||
<p>Wren uses this one function to allocate, grow, shrink, and deallocate memory.
|
||||
When called, <code>memory</code> is the existing pointer to the block of memory if an
|
||||
allocation is being changed or freed. If Wren is requesting new memory, then
|
||||
<code>memory</code> is <code>NULL</code>.</p>
|
||||
<p><code>newSize</code> is the number of bytes of memory being requested. If memory is being
|
||||
freed, this is zero. Your callback should allocate the proper amount of memory
|
||||
and return it.</p>
|
||||
<p>If you don’t provide a custom allocator, the VM uses a default one that relies
|
||||
on <code>realloc</code> and <code>free</code>.</p>
|
||||
<h3><strong><code>initialHeapSize</code></strong> <a href="#initialheapsize" name="initialheapsize" class="header-anchor">#</a></h3>
|
||||
<p>This defines the total number of bytes of memory the VM will allocate before
|
||||
triggering the first garbage collection. Setting this to a smaller number
|
||||
reduces the amount of memory Wren will have allocated at one time, but causes it
|
||||
to collect garbage more frequently.</p>
|
||||
<p>If you set this to zero, Wren uses a default size of 10MB.</p>
|
||||
<h3><strong><code>minHeapSize</code></strong> <a href="#minheapsize" name="minheapsize" class="header-anchor">#</a></h3>
|
||||
<p>After a garbage collection occurs, the threshold for the <em>next</em> collection is
|
||||
determined based on the number of bytes remaining in use. This allows Wren to
|
||||
grow or shrink its memory usage automatically based on how much memory is
|
||||
actually needed.</p>
|
||||
<p>This can be used to ensure that the heap does not get <em>too</em> small, which can
|
||||
in turn lead to a large number of collections afterwards as the heap grows
|
||||
back to a usable size.</p>
|
||||
<p>If zero, this defaults to 1MB.</p>
|
||||
<h3><strong><code>heapGrowthPercent</code></strong> <a href="#heapgrowthpercent" name="heapgrowthpercent" class="header-anchor">#</a></h3>
|
||||
<p>Wren tunes the rate of garbage collection based on how much memory is still in
|
||||
use after a collection. This number controls that. It determines the amount of
|
||||
additional memory Wren will use after a collection, as a percentage of the
|
||||
current heap size.</p>
|
||||
<p>For example, say that this is 50. After a garbage collection, there are 400
|
||||
bytes of memory still in use. That means the next collection will be triggered
|
||||
after a total of 600 bytes are allocated (including the 400 already in use.)</p>
|
||||
<p>Setting this to a smaller number wastes less memory, but triggers more
|
||||
frequent garbage collections.</p>
|
||||
<p>If set to zero, the VM uses a default of 50.</p>
|
||||
<p><a href="storing-c-data.html">← Storing C Data</a></p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
315
embedding/index.html
Normal file
315
embedding/index.html
Normal file
@ -0,0 +1,315 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Embedding – Wren</title>
|
||||
<script type="application/javascript" src="../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="embedding">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../"><img src="../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../">Back to Wren</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>embedding</h2>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
|
||||
<li><a href="storing-c-data.html">Storing C Data</a></li>
|
||||
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><h2>embedding</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
|
||||
<li><a href="storing-c-data.html">Storing C Data</a></li>
|
||||
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Embedding</h1>
|
||||
<p>Wren is designed to be a scripting language that lives inside a host
|
||||
application, so the embedding API is as important as any of its language
|
||||
features. Designing this API well requires satisfying several constraints:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><strong>Wren is dynamically typed, but C is not.</strong> A variable can hold a value of
|
||||
any type in Wren, but that’s definitely not the case in C unless you define
|
||||
some sort of variant type, which ultimately just kicks the problem down the
|
||||
road. Eventually, we have to move data across the boundary between statically and dynamically typed code.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Wren uses garbage collection, but C manages memory manually.</strong> GC adds a
|
||||
few constraints on the API. The VM must be able to find every Wren object
|
||||
that is still usable, even if that object is being referenced from native C
|
||||
code. Otherwise, Wren could free an object that’s still in use.</p>
|
||||
<p>Also, we ideally don’t want to let native C code see a bare pointer to a
|
||||
chunk of memory managed by Wren. Many garbage collection strategies involve
|
||||
<a href="https://en.wikipedia.org/wiki/Tracing_garbage_collection#Copying_vs._mark-and-sweep_vs._mark-and-don.27t-sweep">moving objects</a> in memory. If we allow C code to point directly to an
|
||||
object, that pointer will be left dangling when the object moves. Wren’s GC
|
||||
doesn’t move objects today, but we would like to keep that option for the
|
||||
future.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>The embedding API needs to be fast.</strong> Users may add layers of abstraction
|
||||
on top of the API to make it more pleasant to work with, but the base API
|
||||
defines the <em>maximum</em> performance you can get out of the system. It’s the
|
||||
bottom of the stack, so there’s no way for a user to optimize around it if
|
||||
it’s too slow. There is no lower level alternative.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>We want the API to be pleasant to use.</strong> This is the last constraint
|
||||
because it’s the softest. Of course, we want a beautiful, usable API. But we
|
||||
really <em>need</em> to handle the above, so we’re willing to make things a bit more
|
||||
of a chore to reach the first three goals.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>Fortunately, we aren’t the first people to tackle this. If you’re familiar with
|
||||
<a href="https://www.lua.org/pil/24.html">Lua’s C API</a>, you’ll find Wren’s similar.</p>
|
||||
<h3>Performance and safety <a href="#performance-and-safety" name="performance-and-safety" class="header-anchor">#</a></h3>
|
||||
<p>When code is safely snuggled within the confines of the VM, it’s pretty safe.
|
||||
Method calls are dynamically checked and generate runtime errors which can be
|
||||
caught and handled. The stack grows if it gets close to overflowing. In general,
|
||||
when you’re within Wren code, it tries very hard to avoid crashing and burning.</p>
|
||||
<p>This is why you use a high level language after all—it’s safer and more
|
||||
productive than C. C, meanwhile, really assumes you know what you’re doing. You
|
||||
can cast pointers in invalid ways, misinterpret bits, use memory after freeing
|
||||
it, etc. What you get in return is blazing performance. Many of the reasons C is
|
||||
fast are because it takes all the governors and guardrails off.</p>
|
||||
<p>Wren’s embedding API defines the border between those worlds, and takes on some
|
||||
of the characteristics of C. When you call any of the embedding API functions,
|
||||
it assumes you are calling them correctly. If you invoke a Wren method from C
|
||||
that expects three arguments, it trusts that you gave it three arguments.</p>
|
||||
<p>In debug builds, Wren has assertions to check as many things as it can, but in
|
||||
release builds, Wren expects you to do the right thing. This means you need to
|
||||
take care when using the embedding API, just like you do in all C code you
|
||||
write. In return, you get an API that is quite fast.</p>
|
||||
<h2>Including Wren <a href="#including-wren" name="including-wren" class="header-anchor">#</a></h2>
|
||||
<p>There are two (well, three) ways to get the Wren VM into your program:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><strong>Link to the static or dynamic library.</strong> When you <a href="../getting-started.html">build Wren</a>, it
|
||||
generates both shared and static libraries in <code>lib</code> that you can link to.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Include the source directly in your application.</strong> If you want to include
|
||||
the source directly in your program, you don’t need to run any build steps.
|
||||
Just add the source files in <code>src/vm</code> to your project. They should compile
|
||||
cleanly as C99 or C++98 or anything later.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>In either case, you also want to add <code>src/include</code> to your include path so you
|
||||
can find the <a href="https://github.com/wren-lang/wren/blob/main/src/include/wren.h">public header for Wren</a>:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
#include "wren.h"
|
||||
</pre>
|
||||
|
||||
<p>Wren depends only on the C standard library, so you don’t usually need to link
|
||||
to anything else. On some platforms (at least BSD and Linux) some of the math
|
||||
functions in <code>math.h</code> are implemented in a separate library, <a href="https://en.wikipedia.org/wiki/C_mathematical_functions#libm">libm</a>, that you
|
||||
have to explicitly link to.</p>
|
||||
<p>If your program is in C++ but you are linking to the Wren library compiled as C,
|
||||
this header handles the differences in calling conventions between C and C++:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
#include "wren.hpp"
|
||||
</pre>
|
||||
|
||||
<h2>Creating a Wren VM <a href="#creating-a-wren-vm" name="creating-a-wren-vm" class="header-anchor">#</a></h2>
|
||||
<p>Once you’ve integrated the code into your executable, you need to create a
|
||||
virtual machine. To do that, you create a <code>WrenConfiguration</code> object and
|
||||
initialize it.</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenConfiguration config;
|
||||
wrenInitConfiguration(&config);
|
||||
</pre>
|
||||
|
||||
<p>This gives you a basic configuration that has reasonable defaults for
|
||||
everything. We’ll <a href="configuring-the-vm.html">learn more</a> about what you can configure later,
|
||||
but for now we’ll just add the <code>writeFn</code>, so that we can print text.</p>
|
||||
<p>First we need a function that will do something with the output
|
||||
that Wren sends us from <code>System.print</code> (or <code>System.write</code>). <em>Note that it doesn’t
|
||||
include a newline in the output.</em></p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void writeFn(WrenVM* vm, const char* text) {
|
||||
printf("%s", text);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>And then, we update the configuration to point to it.</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenConfiguration config;
|
||||
wrenInitConfiguration(&config);
|
||||
config.writeFn = &writeFn;
|
||||
</pre>
|
||||
|
||||
<p>With this ready, you can create the VM:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenVM* vm = wrenNewVM(&config);
|
||||
</pre>
|
||||
|
||||
<p>This allocates memory for a new VM and initializes it. The Wren C implementation
|
||||
has no global state, so every single bit of data Wren uses is bundled up inside
|
||||
a WrenVM. You can have multiple Wren VMs running independently of each other
|
||||
without any problems, even concurrently on different threads.</p>
|
||||
<p><code>wrenNewVM()</code> stores its own copy of the configuration, so after calling it, you
|
||||
can discard the WrenConfiguration struct you filled in. Now you have a live
|
||||
VM, waiting to run some code!</p>
|
||||
<h2>Executing Wren code <a href="#executing-wren-code" name="executing-wren-code" class="header-anchor">#</a></h2>
|
||||
<p>You execute a string of Wren source code like so:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenInterpretResult result = wrenInterpret(
|
||||
vm,
|
||||
"my_module",
|
||||
"System.print(\"I am running in a VM!\")");
|
||||
</pre>
|
||||
|
||||
<p>The string is a series of one or more statements separated by newlines. Wren
|
||||
copies the string, so you can free it after calling this. When you call
|
||||
<code>wrenInterpret()</code>, Wren first compiles your source to bytecode. If an error
|
||||
occurs, it returns immediately with <code>WREN_RESULT_COMPILE_ERROR</code>.</p>
|
||||
<p>Otherwise, Wren spins up a new <a href="../concurrency.html">fiber</a> and executes the code in that. Your
|
||||
code can in turn spawn whatever other fibers it wants. It keeps running fibers
|
||||
until they all complete or one <a href="../modules/core/fiber.html#fiber.suspend()">suspends</a>.</p>
|
||||
<p>If a <a href="../error-handling.html">runtime error</a> occurs (and another fiber doesn’t handle it), Wren aborts
|
||||
fibers all the way back to the main one and returns <code>WREN_RESULT_RUNTIME_ERROR</code>.
|
||||
Otherwise, when the last fiber successfully returns, it returns
|
||||
<code>WREN_RESULT_SUCCESS</code>.</p>
|
||||
<p>All code passed to <code>wrenInterpret()</code> runs in a special “main” module. That way,
|
||||
top-level names defined in one call can be accessed in later ones. It’s similar
|
||||
to a REPL session.</p>
|
||||
<h2>Shutting down a VM <a href="#shutting-down-a-vm" name="shutting-down-a-vm" class="header-anchor">#</a></h2>
|
||||
<p>Once the party is over and you’re ready to end your relationship with a VM, you
|
||||
need to free any memory it allocated. You do that like so:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
wrenFreeVM(vm);
|
||||
</pre>
|
||||
|
||||
<p>After calling that, you obviously cannot use the <code>WrenVM*</code> you passed to it
|
||||
again. It’s dead.</p>
|
||||
<p>Note that Wren will yell at you if you still have any live <a href="slots-and-handles.html#handles">WrenHandle</a>
|
||||
objects when you call this. This makes sure you haven’t lost track of any of
|
||||
them (which leaks memory) and you don’t try to use any of them after the VM has
|
||||
been freed.</p>
|
||||
<h2>A complete example <a href="#a-complete-example" name="a-complete-example" class="header-anchor">#</a></h2>
|
||||
<p>Below is a complete example of the above.
|
||||
You can find this file in the <a href="https://github.com/wren-lang/wren/blob/main/example/embedding/main.c">example</a> folder.</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
//For more details, visit https://wren.io/embedding/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "wren.h"
|
||||
|
||||
static void writeFn(WrenVM* vm, const char* text)
|
||||
{
|
||||
printf("%s", text);
|
||||
}
|
||||
|
||||
void errorFn(WrenVM* vm, WrenErrorType errorType,
|
||||
const char* module, const int line,
|
||||
const char* msg)
|
||||
{
|
||||
switch (errorType)
|
||||
{
|
||||
case WREN_ERROR_COMPILE:
|
||||
{
|
||||
printf("[%s line %d] [Error] %s\n", module, line, msg);
|
||||
} break;
|
||||
case WREN_ERROR_STACK_TRACE:
|
||||
{
|
||||
printf("[%s line %d] in %s\n", module, line, msg);
|
||||
} break;
|
||||
case WREN_ERROR_RUNTIME:
|
||||
{
|
||||
printf("[Runtime Error] %s\n", msg);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
WrenConfiguration config;
|
||||
wrenInitConfiguration(&config);
|
||||
config.writeFn = &writeFn;
|
||||
config.errorFn = &errorFn;
|
||||
WrenVM* vm = wrenNewVM(&config);
|
||||
|
||||
const char* module = "main";
|
||||
const char* script = "System.print(\"I am running in a VM!\")";
|
||||
|
||||
WrenInterpretResult result = wrenInterpret(vm, module, script);
|
||||
|
||||
switch (result) {
|
||||
case WREN_RESULT_COMPILE_ERROR:
|
||||
{ printf("Compile Error!\n"); } break;
|
||||
case WREN_RESULT_RUNTIME_ERROR:
|
||||
{ printf("Runtime Error!\n"); } break;
|
||||
case WREN_RESULT_SUCCESS:
|
||||
{ printf("Success!\n"); } break;
|
||||
}
|
||||
|
||||
wrenFreeVM(vm);
|
||||
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Next, we’ll learn to make that VM do useful stuff…</p>
|
||||
<p><a class="right" href="slots-and-handles.html">Slots and Handles →</a></p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
310
embedding/slots-and-handles.html
Normal file
310
embedding/slots-and-handles.html
Normal file
@ -0,0 +1,310 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Slots and Handles – Wren</title>
|
||||
<script type="application/javascript" src="../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="embedding">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../"><img src="../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../">Back to Wren</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>embedding</h2>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
|
||||
<li><a href="storing-c-data.html">Storing C Data</a></li>
|
||||
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><h2>embedding</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
|
||||
<li><a href="storing-c-data.html">Storing C Data</a></li>
|
||||
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Slots and Handles</h1>
|
||||
<p>With <code>wrenInterpret()</code>, we can execute code, but that code can’t do anything
|
||||
particularly interesting. By default, the VM is isolated from the rest of the
|
||||
world, so pretty much all it can do is turn your laptop into a lap warmer.</p>
|
||||
<p>To make our Wren code <em>useful</em>, the VM needs to communicate with the outside
|
||||
world. Wren uses a single unified set of functions for passing data into and out
|
||||
of the VM. These functions are based on two fundamental concepts: <strong>slots</strong> and
|
||||
<strong>handles</strong>.</p>
|
||||
<h2>The Slot Array <a href="#the-slot-array" name="the-slot-array" class="header-anchor">#</a></h2>
|
||||
<p>When you want to send data to Wren, read data from it, or generally monkey
|
||||
around with Wren objects from C, you do so by going through an array of slots.
|
||||
Think of it as a shared message board that both the VM and your C code leave
|
||||
notes on for the other side to process.</p>
|
||||
<p>The array is zero-based, and each slot can hold a value of any type. It is
|
||||
dynamically sized, but it’s your responsibility to ensure there are enough slots
|
||||
<em>before</em> you use them. You do this by calling:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
wrenEnsureSlots(WrenVM* vm, int slotCount);
|
||||
</pre>
|
||||
|
||||
<p>This grows the slot array if needed to ensure that many slots are available. If
|
||||
it’s already big enough, this does nothing. You’ll typically call this once
|
||||
before populating the slots with data that you want to send to Wren.</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
wrenEnsureSlots(vm, 4);
|
||||
// Can now use slots 0 through 3, inclusive.
|
||||
</pre>
|
||||
|
||||
<p>After you ensure an array of slots, you can only rely on them being there until
|
||||
you pass control back to Wren. That includes calling <code>wrenCall()</code> or
|
||||
<code>wrenInterpret()</code>, or returning from a <a href="calling-c-from-wren.html">foreign method</a>.</p>
|
||||
<p>If you read or write from a slot that you haven’t ensured is valid, Wren makes
|
||||
no guarantees about what will happen. I’ve heard rumors of smoke and feathers
|
||||
flying out of a user’s computer.</p>
|
||||
<p>If you want to see how big the slot array is, use:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
int wrenGetSlotCount(WrenVM* vm);
|
||||
</pre>
|
||||
|
||||
<p>It returns the number of slots in the array. Note that this may be higher than
|
||||
the size you’ve ensured. Wren reuses the memory for this array when possible,
|
||||
so you may get one bigger than you need if it happened to be laying around.</p>
|
||||
<p>When Wren <a href="calling-c-from-wren.html">calls your C code</a> and passes data to you, it ensures there are
|
||||
enough slots for the objects it is sending you.</p>
|
||||
<h3>Writing slots <a href="#writing-slots" name="writing-slots" class="header-anchor">#</a></h3>
|
||||
<p>Once you have some slots, you store data in them using a number of functions all
|
||||
named <code>wrenSetSlot<type>()</code> where <code><type></code> is the kind of data. We’ll start with
|
||||
the simple ones:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void wrenSetSlotBool(WrenVM* vm, int slot, bool value);
|
||||
void wrenSetSlotDouble(WrenVM* vm, int slot, double value);
|
||||
void wrenSetSlotNull(WrenVM* vm, int slot);
|
||||
</pre>
|
||||
|
||||
<p>Each of these takes a primitive C value and converts it to the corresponding
|
||||
<a href="../values.html">Wren value</a>. (Since Wren’s <a href="../values.html#numbers">native number type</a> <em>is</em> a double, there’s not
|
||||
really much <em>conversion</em> going on, but you get the idea.)</p>
|
||||
<p>You can also pass string data to Wren:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void wrenSetSlotBytes(WrenVM* vm, int slot,
|
||||
const char* bytes, size_t length);
|
||||
|
||||
void wrenSetSlotString(WrenVM* vm, int slot,
|
||||
const char* text);
|
||||
</pre>
|
||||
|
||||
<p>Both of these copy the bytes into a new <a href="../values.html#strings">String</a> object managed by Wren’s
|
||||
garbage collector, so you can free your copy of it after you call this. The
|
||||
difference between the two is that <code>wrenSetSlotBytes()</code> takes an explicit
|
||||
length. Since Wren strings may contain arbitrary byte values, including the null
|
||||
byte, this lets you pass those in. It’s also a little faster to use this for
|
||||
regular strings if you happen to know the length. The latter calculates the
|
||||
length of the string using <code>strlen()</code>.</p>
|
||||
<h3>Reading slots <a href="#reading-slots" name="reading-slots" class="header-anchor">#</a></h3>
|
||||
<p>You can, of course, also pull data out of slots. Here are the simple ones:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
bool wrenGetSlotBool(WrenVM* vm, int slot);
|
||||
double wrenGetSlotDouble(WrenVM* vm, int slot);
|
||||
</pre>
|
||||
|
||||
<p>These take a Wren value of the corresponding type and convert it to its raw C
|
||||
representation. For strings, we have:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
const char* wrenGetSlotString(WrenVM* vm, int slot);
|
||||
const char* wrenGetSlotBytes(WrenVM* vm, int slot,
|
||||
int* length);
|
||||
</pre>
|
||||
|
||||
<p>These return a pointer to the first byte of the string. If you want to know the
|
||||
length, the latter stores it in the variable pointed to by <code>length</code>. Both of
|
||||
these return a direct pointer to the bytes managed by Wren. You should not hold
|
||||
on to this pointer for long. Wren does not promise that it won’t move or free
|
||||
the data.</p>
|
||||
<p>With these functions, you are going from dynamically typed Wren data to
|
||||
statically typed C. It’s up to <em>you</em> to ensure that you read a value as the
|
||||
correct type. If you read a number from a slot that currently holds a string,
|
||||
you’re gonna have a bad time.</p>
|
||||
<p>Fortunately, you usually know what type of data you have in a slot. If not, you
|
||||
can ask:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenType wrenGetSlotType(WrenVM* vm, int slot);
|
||||
</pre>
|
||||
|
||||
<p>This returns an enum defining what type of value is in the slot. It only covers
|
||||
the primitive values that are supported by the C API. Things like ranges and
|
||||
instances of classes come back as <code>WREN_TYPE_UNKNOWN</code>. If you want to move that
|
||||
kind of data between Wren and C, you’ll have to pull the object apart into
|
||||
simple primitive values first or use a <a href="storing-c-data.html">foreign class</a>.</p>
|
||||
<h3>Looking up variables <a href="#looking-up-variables" name="looking-up-variables" class="header-anchor">#</a></h3>
|
||||
<p>There are a few other utility functions that move data into and out of slots.
|
||||
Here’s the first:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void wrenGetVariable(WrenVM* vm, const char* module,
|
||||
const char* name, int slot);
|
||||
</pre>
|
||||
|
||||
<p>This looks up a top level variable with the given name in the module with the
|
||||
given name and stores its value in the given slot. Note that classes are just
|
||||
objects stored in variables too, so you can use this to look up a class by its
|
||||
name. Handy for calling static methods on it.</p>
|
||||
<p>Like any method that works with strings, this one is a bit slow. It has to hash
|
||||
the name and look it up in the module’s string table. You might want to avoid
|
||||
calling this in the middle of a hot loop where performance is critical. Instead,
|
||||
it’s faster to look up the variable once outside the loop and store a reference
|
||||
to the object using a <a href="#handles">handle</a>.</p>
|
||||
<h3>Working with lists <a href="#working-with-lists" name="working-with-lists" class="header-anchor">#</a></h3>
|
||||
<p>The slot array is fine for moving a fixed number of objects between Wren and
|
||||
C, but sometimes you need to shuttle a larger or dynamically-sized ball of
|
||||
stuff. <a href="../lists.html">List objects</a> work well for that, so the C API lets you work
|
||||
with them directly.</p>
|
||||
<p>You can create a new empty list from C using:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void wrenSetSlotNewList(WrenVM* vm, int slot);
|
||||
</pre>
|
||||
|
||||
<p>It stores the resulting list in the given slot. If you have a list in a
|
||||
slot—either one you created from C or from Wren—you can add elements
|
||||
to it using:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void wrenInsertInList(WrenVM* vm, int listSlot, int index,
|
||||
int elementSlot);
|
||||
</pre>
|
||||
|
||||
<p>That’s a lot of int parameters:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>listSlot</code> is the slot where the list object is stored. That’s the list you’ll
|
||||
be modifying. If you created the list from C, it will be the slot you passed
|
||||
to <code>wrenSetSlotNewList()</code>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>index</code> is the index within the list where you want to insert the element.
|
||||
Just like from within Wren, you can use a negative number to count back from
|
||||
the end, so <code>-1</code> appends to the list.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>elementSlot</code> identifies the slot where the value you want to insert in the
|
||||
list can be found.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>This API means getting a value from C into a list is a two step operation. First
|
||||
you move the value into a slot, then you take it from the slot and insert it in
|
||||
the list. This is kind of tedious, but it lets us use the same set of functions
|
||||
for moving values into slots of each primitive type. Otherwise, we’d need
|
||||
<code>wrenInsertInListDouble()</code>, <code>wrenInsertInListBool()</code>, etc.</p>
|
||||
<h2>Handles <a href="#handles" name="handles" class="header-anchor">#</a></h2>
|
||||
<p>Slots are pretty good for shuttling primitive data between C and Wren, but they
|
||||
have two limitations:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><strong>They are short-lived.</strong> As soon as you execute some more Wren code, the
|
||||
slot array is invalidated. You can’t use a slot to persistently keep track
|
||||
of some object.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>They only support primitive types.</strong> A slot can hold a value of any type,
|
||||
but the C API we’ve seen so far doesn’t let you <em>do</em> anything with values
|
||||
that aren’t simple primitive ones. If you want to grab a reference to,
|
||||
say, an instance of some class, how do you do it?</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>To address those, we have handles. A handle wraps a reference to an object of
|
||||
any kind—strings, numbers, instances of classes, collections, whatever.
|
||||
You create a handle using this:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot);
|
||||
</pre>
|
||||
|
||||
<p>This takes the object stored in the given slot, creates a new WrenHandle to wrap
|
||||
it, and returns a pointer to it back to you. You can send that wrapped object
|
||||
back to Wren by calling:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle);
|
||||
</pre>
|
||||
|
||||
<p>Note that this doesn’t invalidate your WrenHandle. You can still keep using it.</p>
|
||||
<h3>Retaining and releasing handles <a href="#retaining-and-releasing-handles" name="retaining-and-releasing-handles" class="header-anchor">#</a></h3>
|
||||
<p>A handle is an opaque wrapper around an object of any type, but just as
|
||||
important, it’s a <em>persistent</em> one. When Wren gives you a pointer to a
|
||||
WrenHandle, it guarantees that that pointer remains valid. You can keep it
|
||||
around as long as you want. Even if a garbage collection occurs, Wren will
|
||||
ensure the handle and the object it wraps are kept safely in memory.</p>
|
||||
<p>Internally, Wren keeps a list of all of the WrenHandles that have been created.
|
||||
That way, during garbage collection, it can find them all and make sure their
|
||||
objects aren’t freed. But what if you don’t want it to be kept around any more?
|
||||
Since C relies on manual memory management, WrenHandle does too. When you are
|
||||
done with one, you must explicitly release it by calling:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle);
|
||||
</pre>
|
||||
|
||||
<p>This does not immediately delete the wrapped object—after all, there may
|
||||
be other references to the same object in the program. It just invalidates the
|
||||
WrenHandle wrapper itself. After you call this, you cannot use that pointer
|
||||
again.</p>
|
||||
<p>You must release every WrenHandle you’ve created before shutting down the VM.
|
||||
Wren warns you if you don’t, since it implies you’ve probably leaked a resource
|
||||
somewhere.</p>
|
||||
<p>Now we know how to pass values between Wren and C, but we don’t know how to
|
||||
actually <em>do</em> anything with them. Next, we’ll learn how to use slots to pass
|
||||
parameters to a Wren method from C…</p>
|
||||
<p><a class="right" href="calling-wren-from-c.html">Calling Wren from C →</a>
|
||||
<a href="index.html">← Introduction</a></p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
421
embedding/storing-c-data.html
Normal file
421
embedding/storing-c-data.html
Normal file
@ -0,0 +1,421 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Storing C Data – Wren</title>
|
||||
<script type="application/javascript" src="../prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="../wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="embedding">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="../"><img src="../wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="../">Back to Wren</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>embedding</h2>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
|
||||
<li><a href="storing-c-data.html">Storing C Data</a></li>
|
||||
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><h2>embedding</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
|
||||
<li><a href="storing-c-data.html">Storing C Data</a></li>
|
||||
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>Storing C Data</h1>
|
||||
<p>An embedded language often needs to work with native data. You may want a
|
||||
pointer to some memory managed in the C heap, or maybe you want to store a chunk
|
||||
of data more efficiently than Wren’s dynamism allows. You may want a Wren object
|
||||
that represents a native resource like a file handle or database connection.</p>
|
||||
<p>For those cases, you can define a <strong>foreign class</strong>, a chimera whose state is
|
||||
half Wren and half C. It is a real Wren class with a name, constructor, and
|
||||
methods. You can define methods on it written in Wren, or <a href="calling-c-from-wren.html">foreign methods</a>
|
||||
written in C. It produces real Wren objects that you can pass around, do <code>is</code>
|
||||
checks on, etc. But it also wraps a blob of raw memory that is opaque to Wren
|
||||
but accessible from C.</p>
|
||||
<h2>Defining a Foreign Class <a href="#defining-a-foreign-class" name="defining-a-foreign-class" class="header-anchor">#</a></h2>
|
||||
<p>You define one like so:</p>
|
||||
<pre class="snippet">
|
||||
foreign class Point {
|
||||
// ...
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The <code>foreign</code> keyword tells Wren to loop in the host application when it
|
||||
constructs instances of the class. The host tells Wren how many bytes of extra
|
||||
memory the foreign instance should contain and in return, Wren gives the host
|
||||
the opportunity to initialize that data.</p>
|
||||
<p>To talk to the host app, Wren needs a C function it can call when it constructs
|
||||
an instance of the foreign class. This function is found through a binding
|
||||
process similar to <a href="calling-c-from-wren.html#binding-foreign-methods">how foreign methods are bound</a>. When you <a href="configuring-the-vm.html">configure
|
||||
the VM</a>, you set the <code>bindForeignClassFn</code> field in WrenConfiguration to point
|
||||
to a C callback you define. Its signature must be:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenForeignClassMethods bindForeignClass(
|
||||
WrenVM* vm, const char* module, const char* className);
|
||||
</pre>
|
||||
|
||||
<p>Wren invokes this callback once when a foreign class declaration is executed.
|
||||
Wren passes in the name of the module containing the foreign class, and the name
|
||||
of the class being declared. The host’s responsibility is to return one of these
|
||||
structs:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
typedef struct
|
||||
{
|
||||
WrenForeignMethodFn allocate;
|
||||
WrenFinalizerFn finalize;
|
||||
} WrenForeignClassMethods;
|
||||
</pre>
|
||||
|
||||
<p>It’s a pair of function pointers. The first, <code>allocate</code>, is called by Wren
|
||||
whenever an instance of the foreign class is created. (We’ll get to the optional
|
||||
<code>finalize</code> callback later.) The allocation callback has the same signature as a
|
||||
foreign method:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void allocate(WrenVM* vm);
|
||||
</pre>
|
||||
|
||||
<h2>Initializing an Instance <a href="#initializing-an-instance" name="initializing-an-instance" class="header-anchor">#</a></h2>
|
||||
<p>When you create an instance of a foreign class by calling one its
|
||||
<a href="../classes.html#constructors">constructors</a>, Wren invokes the <code>allocate</code> callback you gave it when binding
|
||||
the foreign class. Your primary responsibility in that callback is to tell Wren
|
||||
how many bytes of raw memory you need. You do that by calling:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void* wrenSetSlotNewForeign(WrenVM* vm,
|
||||
int slot, int classSlot, size_t size);
|
||||
</pre>
|
||||
|
||||
<p>Like other <a href="slots-and-handles.html">slot manipulation functions</a>, it both reads from and writes to
|
||||
the slot array. It has a few parameters to make it more general purpose since it
|
||||
can also be used in other foreign methods:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>The <code>slot</code> parameter is the destination slot where the new foreign object
|
||||
should be placed. When you’re calling this in a foreign class’s allocate
|
||||
callback, this should be 0.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The <code>classSlot</code> parameter is the slot where the foreign class being
|
||||
constructed can be found. When the VM calls an allocate callback for a
|
||||
foreign class, the class itself is already in slot 0, so you’ll pass 0 for
|
||||
this too.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Finally, the <code>size</code> parameter is the interesting one. Here, you pass in the
|
||||
number of extra raw bytes of data you want the foreign instance to store.
|
||||
This is the memory you get to play with from C.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>So, for example, if you wanted to create a foreign instance that contains eight
|
||||
bytes of C data, you’d call:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void* data = wrenSetSlotNewForeign(vm, 0, 0, 8);
|
||||
</pre>
|
||||
|
||||
<p>The value returned by <code>wrenSetSlotNewForeign()</code> is the raw pointer to the
|
||||
requested bytes. You can cast that to whatever C type makes sense (as long as it
|
||||
fits within the requested number of bytes) and initialize it as you see fit.</p>
|
||||
<p>Any parameters passed to the constructor are also available in subsequent slots
|
||||
in the slot array. That way you can initialize the foreign data based on values
|
||||
passed to the constructor from Wren.</p>
|
||||
<p>After the allocate callback returns, the class’s constructor in Wren is run and
|
||||
execution proceeds like normal. From here on out, within Wren, it appears you
|
||||
have a normal instance of a class. It just happens to have some extra bytes
|
||||
hiding inside it that can be accessed from foreign methods.</p>
|
||||
<h2>Accessing Foreign Data <a href="#accessing-foreign-data" name="accessing-foreign-data" class="header-anchor">#</a></h2>
|
||||
<p>Typically, the way you make use of the data stored in an instance of a foreign
|
||||
class is through other foreign methods. Those are usually defined on the same
|
||||
foreign class, but can be defined on other classes as well. Wren doesn’t care.</p>
|
||||
<p>Once you have a foreign instance in a slot, you can access the raw bytes it
|
||||
stores by calling:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void* wrenGetSlotForeign(WrenVM* vm, int slot);
|
||||
</pre>
|
||||
|
||||
<p>You pass in the slot index containing the foreign object and it gives you back a
|
||||
pointer to the raw memory the object wraps. As usual, the C API doesn’t do any
|
||||
type or bounds checking, so it’s on you to make sure the object in that slot
|
||||
actually <em>is</em> an instance of a foreign class and contains as much memory as you
|
||||
access.</p>
|
||||
<p>Given that void pointer, you can now freely read and modify the data it points
|
||||
to. They’re your bits, Wren just holds them for you.</p>
|
||||
<h2>Freeing Resources <a href="#freeing-resources" name="freeing-resources" class="header-anchor">#</a></h2>
|
||||
<p>If your foreign instances are just holding memory and you’re OK with Wren’s
|
||||
garbage collector managing the lifetime of that memory, then you’re done. Wren
|
||||
will keep the bytes around as long as there is still a reference to them. When
|
||||
the instance is no longer reachable, eventually the garbage collector will do
|
||||
its thing and free the memory.</p>
|
||||
<p>But, often, your foreign data refers to some resource whose lifetime needs to
|
||||
be explicitly managed. For example, if you have a foreign object that wraps an
|
||||
open file handle, you need to ensure that handle doesn’t get left open when the
|
||||
GC frees the foreign instance.</p>
|
||||
<p>Of course, you can (and usually should) add a method on your foreign class, like
|
||||
<code>close()</code> so the user can explicitly release the resource managed by the object.
|
||||
But if they forget to do that and the object is no longer reachable, you want to
|
||||
make sure the resource isn’t leaked.</p>
|
||||
<p>To that end, you can also provide a <em>finalizer</em> function when binding the
|
||||
foreign class. That’s the other callback in the WrenForeignClassMethods struct.
|
||||
If you provide that callback, then Wren will invoke it when an instance of your
|
||||
foreign class is about to be freed by the garbage collector. This gives you one
|
||||
last chance to clean up the object’s resources.</p>
|
||||
<p>Because this is called during the middle of a garbage collection, you do not
|
||||
have unfettered access to the VM. It’s not like a normal foreign method where
|
||||
you can monkey around with slots and other stuff. Doing that while the GC is
|
||||
running could leave Wren in a weird state.</p>
|
||||
<p>Instead, the finalize callback’s signature is only:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void finalize(void* data);
|
||||
</pre>
|
||||
|
||||
<p>Wren gives you the pointer to your foreign function’s memory, and that’s it. The
|
||||
<em>only</em> thing you should do inside a finalizer is release any external resources
|
||||
referenced by that memory.</p>
|
||||
<h2>A Full Example <a href="#a-full-example" name="a-full-example" class="header-anchor">#</a></h2>
|
||||
<p>That’s a lot to take in, so let’s walk through a full example of a foreign class
|
||||
with a finalizer and a couple of methods. We’ll do a File class that wraps the
|
||||
C standard file API.</p>
|
||||
<p>In Wren, the class we want looks like this:</p>
|
||||
<pre class="snippet">
|
||||
foreign class File {
|
||||
construct create(path) {}
|
||||
|
||||
foreign write(text)
|
||||
foreign close()
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>So you can create a new file given a path. Once you have one, you can write to
|
||||
it and then explicitly close it if you want. We also need to make sure the file
|
||||
gets closed if the user forgets to and the GC cleans up the object.</p>
|
||||
<h3>Setting up the VM <a href="#setting-up-the-vm" name="setting-up-the-vm" class="header-anchor">#</a></h3>
|
||||
<p>Over in the host, first we’ll set up the VM:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
#include "wren.h"
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
WrenConfiguration config;
|
||||
wrenInitConfiguration(&config);
|
||||
|
||||
config.bindForeignClassFn = bindForeignClass;
|
||||
config.bindForeignMethodFn = bindForeignMethod;
|
||||
|
||||
WrenVM* vm = wrenNewVM(&config);
|
||||
wrenInterpret(vm, "my_module", "some code...");
|
||||
|
||||
return 0;
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3>Binding the foreign class <a href="#binding-the-foreign-class" name="binding-the-foreign-class" class="header-anchor">#</a></h3>
|
||||
<p>We give the VM two callbacks. The first is for wiring up the foreign class
|
||||
itself:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenForeignClassMethods bindForeignClass(
|
||||
WrenVM* vm, const char* module, const char* className)
|
||||
{
|
||||
WrenForeignClassMethods methods;
|
||||
|
||||
if (strcmp(className, "File") == 0)
|
||||
{
|
||||
methods.allocate = fileAllocate;
|
||||
methods.finalize = fileFinalize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown class.
|
||||
methods.allocate = NULL;
|
||||
methods.finalize = NULL;
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>When our binding callback is invoked for the File class, we return the allocate
|
||||
and finalize functions the VM should call. Allocation looks like:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
#include <stdio.h>
|
||||
#include "wren.h"
|
||||
|
||||
void fileAllocate(WrenVM* vm)
|
||||
{
|
||||
FILE** file = (FILE**)wrenSetSlotNewForeign(vm,
|
||||
0, 0, sizeof(FILE*));
|
||||
const char* path = wrenGetSlotString(vm, 1);
|
||||
*file = fopen(path, "w");
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>First we create the instance by calling <code>wrenSetSlotNewForeign()</code>. We tell it to
|
||||
add enough extra bytes to store a <code>FILE*</code> in it, which is C’s representation of
|
||||
a file handle. We’re given back a pointer to the bytes. Since the file handle is
|
||||
itself a pointer, we end up with a double indirection, hence the <code>FILE**</code>. In
|
||||
most cases, you’ll just have a single <code>*</code>.</p>
|
||||
<p>We also pull the file path from the slot array. Then we tell C to create a new
|
||||
file at that path. That gives us back a new file handle – a <code>FILE*</code> – and we
|
||||
store that back into the foreign instance using <code>*file</code>. Now we have a foreign
|
||||
object that wraps an open file handle.</p>
|
||||
<p>The finalizer simply casts the foreign instance’s data back to the proper type
|
||||
and closes the file:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void fileFinalize(void* data)
|
||||
{
|
||||
closeFile((FILE**) data);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>It uses this little utility function:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
static void closeFile(FILE** file)
|
||||
{
|
||||
// Already closed.
|
||||
if (*file == NULL) return;
|
||||
|
||||
fclose(*file);
|
||||
*file = NULL;
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>This closes the file (if it’s not already closed) and also nulls out the file
|
||||
handle so that we don’t try to use the file after it’s been closed.</p>
|
||||
<h3>Binding the foreign methods <a href="#binding-the-foreign-methods" name="binding-the-foreign-methods" class="header-anchor">#</a></h3>
|
||||
<p>That’s the foreign <em>class</em> part. Now we have a couple of foreign <em>methods</em> to
|
||||
handle. The host tells the VM how to find them by giving Wren a pointer to this
|
||||
function:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
WrenForeignMethodFn bindForeignMethod(WrenVM* vm, const char* module,
|
||||
const char* className, bool isStatic, const char* signature)
|
||||
{
|
||||
if (strcmp(className, "File") == 0)
|
||||
{
|
||||
if (!isStatic && strcmp(signature, "write(_)") == 0)
|
||||
{
|
||||
return fileWrite;
|
||||
}
|
||||
|
||||
if (!isStatic && strcmp(signature, "close()") == 0)
|
||||
{
|
||||
return fileClose;
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown method.
|
||||
return NULL;
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>When Wren calls this, we look at the class and method name to figure out which
|
||||
method it’s binding, and then return a pointer to the appropriate function. The
|
||||
foreign method for writing to the file is:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void fileWrite(WrenVM* vm)
|
||||
{
|
||||
FILE** file = (FILE**)wrenGetSlotForeign(vm, 0);
|
||||
|
||||
// Make sure the file is still open.
|
||||
if (*file == NULL)
|
||||
{
|
||||
wrenSetSlotString(vm, 0, "Cannot write to a closed file.");
|
||||
wrenAbortFiber(vm, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const char* text = wrenGetSlotString(vm, 1);
|
||||
fwrite(text, sizeof(char), strlen(text), *file);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>We use <code>wrenGetSlotForeign()</code> to pull the foreign data out of the slot array.
|
||||
Since this method is called on the file itself, the foreign object is in slot
|
||||
zero. We take the resulting pointer and cast it to a pointer of the proper type.
|
||||
Again, because our foreign data is <em>itself</em> a pointer, we get a pointer to a
|
||||
pointer.</p>
|
||||
<p>We do a little sanity checking to make sure the user isn’t writing to a file
|
||||
they already closed. If not, we call <code>fwrite()</code> to write to the file.</p>
|
||||
<p>The other method is <code>close()</code> to let them explicitly close the file:</p>
|
||||
<pre class="snippet" data-lang="c">
|
||||
void fileClose(WrenVM* vm)
|
||||
{
|
||||
FILE** file = (FILE**)wrenGetSlotForeign(vm, 0);
|
||||
closeFile(file);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>It uses the same helper we defined above. And that’s it, a complete foreign
|
||||
class with a finalizer and a couple of foreign methods. In Wren, you can use it
|
||||
like so:</p>
|
||||
<pre class="snippet">
|
||||
var file = File.create("some/path.txt")
|
||||
file.write("some text")
|
||||
file.close()
|
||||
</pre>
|
||||
|
||||
<p>Pretty neat, right? The resulting class looks and feels like a normal Wren
|
||||
class, but it has the functionality and much of the performance of native C
|
||||
code.</p>
|
||||
<p><a class="right" href="configuring-the-vm.html">Configuring the VM →</a>
|
||||
<a href="calling-c-from-wren.html">← Calling C from Wren</a></p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
264
error-handling.html
Normal file
264
error-handling.html
Normal file
@ -0,0 +1,264 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Error Handling – Wren</title>
|
||||
<script type="application/javascript" src="prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="codejar.js"></script>
|
||||
<script type="application/javascript" src="wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="./">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="./"><img src="./wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="contributing.html">Contributing</a></li>
|
||||
<li><a href="blog">Blog</a></li>
|
||||
<li><a href="try">Try it!</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>guides</h2>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>API docs</h2>
|
||||
<ul>
|
||||
<li><a href="modules">Modules</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>reference</h2>
|
||||
<ul>
|
||||
<li><a href="cli">Wren CLI</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<div><a href="getting-started.html">Getting Started</a></div>
|
||||
<div><a href="contributing.html">Contributing</a></div>
|
||||
<div><a href="blog">Blog</a></div>
|
||||
<div><a href="try">Try it!</a></div>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>guides</h2></td>
|
||||
<td><h2>reference</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="modules">API/Modules</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>Error Handling</h2>
|
||||
<p>Errors come in a few fun flavors.</p>
|
||||
<h2>Syntax errors <a href="#syntax-errors" name="syntax-errors" class="header-anchor">#</a></h2>
|
||||
<p>The first errors you’re likely to run into are syntax errors. These include
|
||||
simple bugs where your code doesn’t follow the language’s grammar, like:</p>
|
||||
<pre class="snippet">
|
||||
1 + * 2
|
||||
</pre>
|
||||
|
||||
<p>Wren detects these errors as soon as it tries to read your code. When it hits
|
||||
one, you get a friendly error message, like:</p>
|
||||
<pre><code>[main line 1] Error on '*': Unexpected token for expression.
|
||||
</code></pre>
|
||||
<p>Some slightly more “semantic” errors fall into this bucket too. Things like
|
||||
using a variable that hasn’t been defined, or declaring two variables with the
|
||||
same name in the same scope. So if you do:</p>
|
||||
<pre class="snippet">
|
||||
var a = "once"
|
||||
var a = "twice"
|
||||
</pre>
|
||||
|
||||
<p>Wren tells you:</p>
|
||||
<pre><code>[main line 2] Error on 'a': Top-level variable is already defined.
|
||||
</code></pre>
|
||||
<p>Note that it does this before it executes <em>any</em> code. Unlike some other
|
||||
scripting languages, Wren tries to help you find your errors as soon as
|
||||
possible when it can.</p>
|
||||
<p>If it starts running your code, you can be sure you don’t have any errors
|
||||
related to syntax or variable scope.</p>
|
||||
<h2>Runtime errors <a href="#runtime-errors" name="runtime-errors" class="header-anchor">#</a></h2>
|
||||
<p>Alas, just fixing all of the “compile-time” errors doesn’t mean your code does
|
||||
what you want. Your program may still have errors that can’t be detected
|
||||
statically. Since they can’t be found until your code is run, they’re called
|
||||
“runtime” errors.</p>
|
||||
<p>Most runtime errors come from the VM itself. They arise from code trying to
|
||||
perform an operation that the VM can’t do. The most common error is a “method
|
||||
not found” one. If you call a method on an object and its class (and all of its
|
||||
superclasses) don’t define that method, there’s nothing Wren can do:</p>
|
||||
<pre class="snippet">
|
||||
class Foo {
|
||||
construct new() {}
|
||||
}
|
||||
|
||||
var foo = Foo.new()
|
||||
foo.someRandomMethod
|
||||
</pre>
|
||||
|
||||
<p>If you run this, Wren will print:</p>
|
||||
<pre><code>Foo does not implement method 'someRandomMethod'.
|
||||
</code></pre>
|
||||
<p>Then it stops executing code. Unlike some other languages, Wren doesn’t keep
|
||||
plugging away after a runtime error has occurred. A runtime error implies
|
||||
there’s a bug in your code and it wants to draw your attention to it. To help
|
||||
you out, it prints a stack trace showing where in the code the error occurred,
|
||||
and all of the method calls that led to it.</p>
|
||||
<p>Another common runtime error is passing an argument of the wrong type to a
|
||||
method. For example, lists are indexed using a number. If you try to pass some
|
||||
other type, it’s an error:</p>
|
||||
<pre class="snippet">
|
||||
var list = ["a", "b", "c"]
|
||||
list["1"]
|
||||
</pre>
|
||||
|
||||
<p>This exits with:</p>
|
||||
<pre><code>Subscript must be a number or a range.
|
||||
[main line 2] in (script)
|
||||
</code></pre>
|
||||
<p>These are the two most common kinds of runtime errors, but there are others.
|
||||
Stuff like out of bounds errors on lists, calling a function with the wrong
|
||||
number of arguments, etc.</p>
|
||||
<h2>Handling runtime errors <a href="#handling-runtime-errors" name="handling-runtime-errors" class="header-anchor">#</a></h2>
|
||||
<p>Most of the time, runtime errors indicate a bug in your code and the best
|
||||
solution is to fix the bug. However, sometimes it’s useful to be able to handle
|
||||
them at, uh, runtime.</p>
|
||||
<p>To keep the language simpler, Wren does not have exception handling. Instead, it
|
||||
takes advantage of <a href="concurrency.html">fibers</a> for handling errors. When a runtime error occurs,
|
||||
the current fiber is aborted. Normally, Wren will also abort any fibers that
|
||||
invoked that one, all the way to the main fiber, and then exit the VM.</p>
|
||||
<p>However, you can run a fiber using the <code>try</code> method. If a runtime error occurs
|
||||
in the called fiber, the error is captured and the <code>try</code> method returns the
|
||||
error message as a string.</p>
|
||||
<p>For example, if you run this program:</p>
|
||||
<pre class="snippet">
|
||||
var fiber = Fiber.new {
|
||||
123.badMethod
|
||||
}
|
||||
|
||||
var error = fiber.try()
|
||||
System.print("Caught error: " + error)
|
||||
</pre>
|
||||
|
||||
<p>It prints:</p>
|
||||
<pre><code>Caught error: Num does not implement method 'badMethod'.
|
||||
</code></pre>
|
||||
<p>The called fiber can no longer be used, but any other fibers can proceed as
|
||||
usual. When a fiber has been aborted because of a runtime error, you can also
|
||||
get the error from the fiber object. Continuing the above example:</p>
|
||||
<pre class="snippet">
|
||||
System.print(fiber.error)
|
||||
</pre>
|
||||
|
||||
<p>This also prints:</p>
|
||||
<pre><code>Num does not implement method 'badMethod'.
|
||||
</code></pre>
|
||||
<p>If you have a chain of fiber calls and a runtime error occurs, it will walk the
|
||||
chain looking for a <code>try</code> call, so this can also be used to capture runtime
|
||||
errors generated in fibers that are invoked by the one you called <code>try</code> on.</p>
|
||||
<h2>Creating runtime errors <a href="#creating-runtime-errors" name="creating-runtime-errors" class="header-anchor">#</a></h2>
|
||||
<p>Most runtime errors come from within the Wren VM, but you may want to be able
|
||||
to cause your own runtime errors to occur. This can be done by calling the
|
||||
<code>abort()</code> static method on <code>Fiber</code>:</p>
|
||||
<pre class="snippet">
|
||||
Fiber.abort("Something bad happened")
|
||||
</pre>
|
||||
|
||||
<p>You must pass in an error message, and it must be a string.</p>
|
||||
<p>If the provided message is <code>null</code>, no runtime error is raised.</p>
|
||||
<h2>Failures <a href="#failures" name="failures" class="header-anchor">#</a></h2>
|
||||
<p>The last flavor of errors is the highest-level one. All of the above errors
|
||||
indicate <em>bugs</em>—places where the code itself is incorrect. But some
|
||||
errors indicate that the code simply couldn’t accomplish its task for
|
||||
unforeseeable reasons. We’ll call these “failures”.</p>
|
||||
<p>Consider a program that reads in a string of input from the user and parses it
|
||||
to a number. Many strings are not valid numbers, so this parsing can fail. The
|
||||
only way the program could prevent that failure is by validating the string
|
||||
before its parsed, but validating that a string is a number is pretty much the
|
||||
same thing as parsing it.</p>
|
||||
<p>For cases like this where failure can occur and the program <em>will</em> want to
|
||||
handle it, fibers and <code>try()</code> are too coarse-grained to work with. Instead,
|
||||
these operations will indicate failure by <em>returning</em> some sort of error
|
||||
indication.</p>
|
||||
<p>For example, a method for parsing a number could return a number on success and
|
||||
<code>null</code> to indicate parsing failed. Since Wren is dynamically typed, it’s easy
|
||||
and natural for a method to return different types of values.</p>
|
||||
<p><br><hr>
|
||||
<a class="right" href="modularity.html">Modularity →</a>
|
||||
<a href="concurrency.html">← Concurrency</a></p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1 +0,0 @@
|
||||
IO.print("Hello, world!")
|
||||
@ -1,36 +0,0 @@
|
||||
var yMin = -0.2
|
||||
var yMax = 0.1
|
||||
var xMin = -1.5
|
||||
var xMax = -1.1
|
||||
|
||||
for (yPixel in 0...24) {
|
||||
var y = (yPixel / 24) * (yMax - yMin) + yMin
|
||||
for (xPixel in 0...80) {
|
||||
var x = (xPixel / 79) * (xMax - xMin) + xMin
|
||||
var pixel = " "
|
||||
var x0 = x
|
||||
var y0 = y
|
||||
for (iter in 0...80) {
|
||||
var x1 = (x0 * x0) - (y0 * y0)
|
||||
var y1 = 2 * x0 * y0
|
||||
|
||||
// Add the seed.
|
||||
x1 = x1 + x
|
||||
y1 = y1 + y
|
||||
|
||||
x0 = x1
|
||||
y0 = y1
|
||||
|
||||
// Stop if the point escaped.
|
||||
var d = (x0 * x0) + (y0 * y0)
|
||||
if (d > 4) {
|
||||
pixel = " .:;+=xX$&"[(iter / 8).floor]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
IO.write(pixel)
|
||||
}
|
||||
|
||||
IO.print("")
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
IO.print("Hello, world!")
|
||||
332
functions.html
Normal file
332
functions.html
Normal file
@ -0,0 +1,332 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Functions – Wren</title>
|
||||
<script type="application/javascript" src="prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="codejar.js"></script>
|
||||
<script type="application/javascript" src="wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="./">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="./"><img src="./wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="contributing.html">Contributing</a></li>
|
||||
<li><a href="blog">Blog</a></li>
|
||||
<li><a href="try">Try it!</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>guides</h2>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>API docs</h2>
|
||||
<ul>
|
||||
<li><a href="modules">Modules</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>reference</h2>
|
||||
<ul>
|
||||
<li><a href="cli">Wren CLI</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<div><a href="getting-started.html">Getting Started</a></div>
|
||||
<div><a href="contributing.html">Contributing</a></div>
|
||||
<div><a href="blog">Blog</a></div>
|
||||
<div><a href="try">Try it!</a></div>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>guides</h2></td>
|
||||
<td><h2>reference</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="modules">API/Modules</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>Functions</h2>
|
||||
<p>Like many languages today, functions in Wren are little bundles of code
|
||||
you can store in a variable, or pass as an argument to a method. </p>
|
||||
<p>Notice there’s a difference between <em>function</em> and <em>method</em>.</p>
|
||||
<p>Since Wren is object-oriented, most of your code will live in methods on
|
||||
classes, but free-floating functions are still eminently handy. </p>
|
||||
<p>Functions are objects like everything else in Wren, instances of the <code>Fn</code>
|
||||
class.</p>
|
||||
<h2>Creating a function <a href="#creating-a-function" name="creating-a-function" class="header-anchor">#</a></h2>
|
||||
<p>To create a function, we call <code>Fn.new</code>, which takes a block to execute.
|
||||
To call the function, we use <code>.call()</code> on the function instance.</p>
|
||||
<pre class="snippet">
|
||||
var sayHello = Fn.new { System.print("hello") }
|
||||
|
||||
sayHello.call() //> hello
|
||||
</pre>
|
||||
|
||||
<p>Note that we’ll see a shorthand syntax for creating a function below.</p>
|
||||
<h2>Function parameters <a href="#function-parameters" name="function-parameters" class="header-anchor">#</a></h2>
|
||||
<p>Of course, functions aren’t very useful if you can’t pass values to them. The
|
||||
function above takes no arguments. To change that, you can provide a parameter
|
||||
list surrounded by <code>|</code> immediately after the opening brace of the body.</p>
|
||||
<p>To pass arguments to the function, pass them to the <code>call</code> method:</p>
|
||||
<pre class="snippet">
|
||||
var sayMessage = Fn.new {|recipient, message|
|
||||
System.print("message for %(recipient): %(message)")
|
||||
}
|
||||
|
||||
sayMessage.call("Bob", "Good day!")
|
||||
</pre>
|
||||
|
||||
<p>It’s an error to call a function with fewer arguments than its parameter list
|
||||
expects. If you pass too <em>many</em> arguments, the extras are ignored.</p>
|
||||
<h2>Returning values <a href="#returning-values" name="returning-values" class="header-anchor">#</a></h2>
|
||||
<p>The body of a function is a <a href="syntax.html#blocks">block</a>. If it is a single
|
||||
expression—more precisely if there is no newline after the <code>{</code> or
|
||||
parameter list—then the function implicitly returns the value of the
|
||||
expression.</p>
|
||||
<p>Otherwise, the body returns <code>null</code> by default. You can explicitly return a
|
||||
value using a <code>return</code> statement. In other words, these two functions do the
|
||||
same thing:</p>
|
||||
<pre class="snippet">
|
||||
Fn.new { "return value" }
|
||||
|
||||
Fn.new {
|
||||
return "return value"
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The return value is handed back to you when using <code>call</code>:</p>
|
||||
<pre class="snippet">
|
||||
var fn = Fn.new { "some value" }
|
||||
var result = fn.call()
|
||||
System.print(result) //> some value
|
||||
</pre>
|
||||
|
||||
<h2>Closures <a href="#closures" name="closures" class="header-anchor">#</a></h2>
|
||||
<p>As you expect, functions are closures—they can access variables defined
|
||||
outside of their scope. They will hold onto closed-over variables even after
|
||||
leaving the scope where the function is defined:</p>
|
||||
<pre class="snippet">
|
||||
class Counter {
|
||||
static create() {
|
||||
var i = 0
|
||||
return Fn.new { i = i + 1 }
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Here, the <code>create</code> method returns the function created on its second line. That
|
||||
function references a variable <code>i</code> declared outside of the function. Even after
|
||||
the function is returned from <code>create</code>, it is still able to read and assign
|
||||
to<code>i</code>:</p>
|
||||
<pre class="snippet">
|
||||
var counter = Counter.create()
|
||||
System.print(counter.call()) //> 1
|
||||
System.print(counter.call()) //> 2
|
||||
System.print(counter.call()) //> 3
|
||||
</pre>
|
||||
|
||||
<h2>Callable classes <a href="#callable-classes" name="callable-classes" class="header-anchor">#</a></h2>
|
||||
<p>Because <code>Fn</code> is a class, and responds to <code>call()</code>, any class can respond to
|
||||
<code>call()</code> and be used in place of a function. This is particularly handy when
|
||||
the function is passed to a method to be called, like a callback or event.</p>
|
||||
<pre class="snippet">
|
||||
class Callable {
|
||||
construct new() {}
|
||||
call(name, version) {
|
||||
System.print("called %(name) with version %(version)")
|
||||
}
|
||||
}
|
||||
|
||||
var fn = Callable.new()
|
||||
fn.call("wren", "0.4.0")
|
||||
</pre>
|
||||
|
||||
<h2>Block arguments <a href="#block-arguments" name="block-arguments" class="header-anchor">#</a></h2>
|
||||
<p>Very frequently, functions are passed to methods to be called. There are
|
||||
countless examples of this in Wren, like <a href="lists.html">list</a> can be filtered
|
||||
using a method <code>where</code> which accepts a function:</p>
|
||||
<pre class="snippet">
|
||||
var list = [1, 2, 3, 4, 5]
|
||||
var filtered = list.where(Fn.new {|value| value > 3 })
|
||||
System.print(filtered.toList) //> [4, 5]
|
||||
</pre>
|
||||
|
||||
<p>This syntax is a bit less fun to read and write, so Wren implements the
|
||||
<em>block argument</em> concept. When a function is being passed to a method,
|
||||
and is the last argument to the method, it can use a shorter syntax:
|
||||
<em>just the block part</em>.</p>
|
||||
<p>Let’s use a block argument for <code>list.where</code>, it’s the last (only) argument:</p>
|
||||
<pre class="snippet">
|
||||
var list = [1, 2, 3, 4, 5]
|
||||
var filtered = list.where {|value| value > 3 }
|
||||
System.print(filtered.toList) //> [4, 5]
|
||||
</pre>
|
||||
|
||||
<p>We’ve seen this before in a previous page using <code>map</code> and <code>where</code>:</p>
|
||||
<pre class="snippet">
|
||||
numbers.map {|n| n * 2 }.where {|n| n < 100 }
|
||||
</pre>
|
||||
|
||||
<h2>Block argument example <a href="#block-argument-example" name="block-argument-example" class="header-anchor">#</a></h2>
|
||||
<p>Let’s look at a complete example, so we can see both ends.</p>
|
||||
<p>Here’s a fictional class for something that will call a function
|
||||
when a click event is sent to it. It allows us to pass just a
|
||||
function and assume the left mouse button, or to pass a button and a function.</p>
|
||||
<pre class="snippet">
|
||||
class Clickable {
|
||||
construct new() {
|
||||
_fn = null
|
||||
_button = 0
|
||||
}
|
||||
|
||||
onClick(fn) {
|
||||
_fn = fn
|
||||
}
|
||||
|
||||
onClick(button, fn) {
|
||||
_button = button
|
||||
_fn = fn
|
||||
}
|
||||
|
||||
fireEvent(button) {
|
||||
if(_fn && button == _button) {
|
||||
_fn.call(button)
|
||||
}
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Now that we’ve got the clickable class, let’s use it.
|
||||
We’ll start by using the method that accepts just a function
|
||||
because we’re fine with it just being the default left mouse button.</p>
|
||||
<pre class="snippet">
|
||||
var link = Clickable.new()
|
||||
|
||||
link.onClick {|button|
|
||||
System.print("I was clicked by button %(button)")
|
||||
}
|
||||
|
||||
// send a left mouse click
|
||||
// normally this would happen from elsewhere
|
||||
|
||||
link.fireEvent(0) //> I was clicked by button 0
|
||||
</pre>
|
||||
|
||||
<p>Now let’s try with the extra button argument:</p>
|
||||
<pre class="snippet">
|
||||
var contextMenu = Clickable.new()
|
||||
|
||||
contextMenu.onClick(1) {|button|
|
||||
System.print("I was right-clicked")
|
||||
}
|
||||
|
||||
link.fireEvent(0) //> (nothing happened)
|
||||
link.fireEvent(1) //> I was right-clicked
|
||||
</pre>
|
||||
|
||||
<p>Notice that we still pass the other arguments normally,
|
||||
it’s only the last argument that is special.</p>
|
||||
<p><strong>Just a regular function</strong> </p>
|
||||
<p>Block arguments are purely syntax sugar for creating a function and passing it
|
||||
in one little blob of syntax. These two are equivalent:</p>
|
||||
<pre class="snippet">
|
||||
onClick(Fn.new { System.print("clicked") })
|
||||
onClick { System.print("clicked") }
|
||||
</pre>
|
||||
|
||||
<p>And this is just as valid:</p>
|
||||
<pre class="snippet">
|
||||
var onEvent = Fn.new {|button|
|
||||
System.print("clicked by button %(button)")
|
||||
}
|
||||
|
||||
onClick(onEvent)
|
||||
onClick(1, onEvent)
|
||||
</pre>
|
||||
|
||||
<p><strong>Fn.new</strong> <br />
|
||||
As you may have noticed by now, <code>Fn</code> accepts a block argument for the <code>Fn.new</code>.
|
||||
All the constructor does is return that argument right back to you!</p>
|
||||
<p><br><hr>
|
||||
<a class="right" href="classes.html">Classes →</a>
|
||||
<a href="variables.html">← Variables</a></p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
190
getting-started.html
Normal file
190
getting-started.html
Normal file
@ -0,0 +1,190 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Getting Started – Wren</title>
|
||||
<script type="application/javascript" src="prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="codejar.js"></script>
|
||||
<script type="application/javascript" src="wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="./">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="./"><img src="./wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="contributing.html">Contributing</a></li>
|
||||
<li><a href="blog">Blog</a></li>
|
||||
<li><a href="try">Try it!</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>guides</h2>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>API docs</h2>
|
||||
<ul>
|
||||
<li><a href="modules">Modules</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>reference</h2>
|
||||
<ul>
|
||||
<li><a href="cli">Wren CLI</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<div><a href="getting-started.html">Getting Started</a></div>
|
||||
<div><a href="contributing.html">Contributing</a></div>
|
||||
<div><a href="blog">Blog</a></div>
|
||||
<div><a href="try">Try it!</a></div>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>guides</h2></td>
|
||||
<td><h2>reference</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="modules">API/Modules</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>Getting Started</h2>
|
||||
<h2>Trying out the language <a href="#trying-out-the-language" name="trying-out-the-language" class="header-anchor">#</a></h2>
|
||||
<p>If you’d like to try Wren, you have a few options.</p>
|
||||
<ul>
|
||||
<li><strong>In your browser.</strong> You can try Wren <strong><a href="./try/">right here</a></strong>!</li>
|
||||
<li><strong>On your computer.</strong> The <a href="cli">Wren CLI</a> project is a downloadable executable
|
||||
to run scripts with access to file io and more. See the <a href="cli">Wren CLI docs</a>.</li>
|
||||
<li><strong>Embedded in your code.</strong> See how to <a href="#embed-the-vm">build and embed Wren</a> below. <br />
|
||||
And then read the <a href="embedding">embedding guide</a>!</li>
|
||||
</ul>
|
||||
<p>Once you have somewhere to explore, it’s time to <a href="syntax.html">learn the
|
||||
language</a>.</p>
|
||||
<hr />
|
||||
<h2>Embed the VM <a href="#embed-the-vm" name="embed-the-vm" class="header-anchor">#</a></h2>
|
||||
<p><strong>The Wren Virtual Machine</strong> is the core of the language that executes Wren
|
||||
source code. It is just a library, not a standalone application. It’s
|
||||
designed to be <a href="embedding">embedded</a> in a larger host application.</p>
|
||||
<p>It has no dependencies beyond the C standard library.
|
||||
You can use it as a static library, shared library, or simply compile the source into your app.</p>
|
||||
<h3>Building Wren <a href="#building-wren" name="building-wren" class="header-anchor">#</a></h3>
|
||||
<p>To build the Wren library, we look inside the <code>projects/</code> folder.
|
||||
In here you’ll find ready to go projects for <code>Visual Studio</code>, <code>XCode</code> and tools like <code>make</code>.</p>
|
||||
<ul>
|
||||
<li><strong>Windows</strong> Open <code>wren.sln</code> inside <code>projects/vs2019/</code> (or <code>vs2017</code>), hit build.</li>
|
||||
<li><strong>Mac</strong> Open <code>wren.xcworkspace</code> inside <code>projects/xcode/</code>, hit build.</li>
|
||||
<li><strong>Linux</strong> Run <code>make</code> inside of <code>projects/make/</code>.</li>
|
||||
</ul>
|
||||
<p>In each case, <strong>there will be library files generated into the root <code>lib/</code> folder</strong>. <br />
|
||||
These are what you’ll link into your project, based on your needs.</p>
|
||||
<ul>
|
||||
<li><strong>Static Linking</strong> <code>wren.lib</code> on Windows, <code>libwren.a</code> elsewhere.</li>
|
||||
<li><strong>Dynamic Linking</strong> <code>wren.dll</code> on Windows, <code>libwren.so</code> on Linux, and <code>libwren.dylib</code> on Mac.</li>
|
||||
</ul>
|
||||
<p><small>
|
||||
Note that the default build will also generate <code>wren_test</code> inside of <code>bin/</code>, <br />
|
||||
a binary that is used to run the language tests. It can execute simple scripts.
|
||||
</small></p>
|
||||
<p><strong>Other platforms</strong> <br />
|
||||
If your platform isn’t explicitly supported,
|
||||
it is recommended that you include the Wren source
|
||||
in your project for a portable experience.</p>
|
||||
<h3>Including the code in your project <a href="#including-the-code-in-your-project" name="including-the-code-in-your-project" class="header-anchor">#</a></h3>
|
||||
<p><strong>all source files</strong> <br />
|
||||
The alternative to building via the provided projects is to include the wren source code in your project.
|
||||
Since it has no dependencies this is simple, all the code in <code>src/</code> comes along. There’s a readme in <code>src/</code> for details.</p>
|
||||
<p><strong>‘amalgamated’ build</strong> <br />
|
||||
If you want an even simpler way, there’s an ‘amalgamated’ build (often called <code>blob</code>, or <code>unity</code> builds.).
|
||||
This is <em>all of the wren source code in one file</em>.</p>
|
||||
<p>This file can be generated by running <code>python3 util/generate_amalgamation.py > build/wren.c</code>,
|
||||
which saves the generated output in <code>build/wren.c</code>.</p>
|
||||
<p>Include <code>build/wren.c</code> and <code>src/include/wren.h</code> in your project code and you’re good to go.
|
||||
<small>Ideally later we can automate generating this and include it in the repo.</small></p>
|
||||
<hr />
|
||||
<p>If you run into bugs, or have ideas or questions, any of
|
||||
the following work:</p>
|
||||
<ul>
|
||||
<li>Join the <a href="https://discord.gg/Kx6PxSX">discord community</a>.</li>
|
||||
<li>Ask on the <a href="https://groups.google.com/forum/#!forum/wren-lang">Wren mailing list</a> (which is pretty quiet).</li>
|
||||
<li>Tell us on twitter at <a href="https://twitter.com/intent/user?screen_name=munificentbob">@munificentbob</a> or <a href="https://twitter.com/intent/user?screen_name=ruby0x1">@ruby0x1</a>.</li>
|
||||
<li><a href="https://github.com/wren-lang/wren/issues">File a ticket</a> at <a href="https://github.com/wren-lang/wren">the GitHub repo</a>.</li>
|
||||
<li>The CLI also has <a href="https://github.com/wren-lang/wren-cli/issues">tickets</a> and a <a href="https://github.com/wren-lang/wren-cli">GitHub repo</a> too.</li>
|
||||
<li>Pull requests are welcome.</li>
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
147
include/wren.h
147
include/wren.h
@ -1,147 +0,0 @@
|
||||
#ifndef wren_h
|
||||
#define wren_h
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct WrenVM WrenVM;
|
||||
|
||||
// A generic allocation function that handles all explicit memory management
|
||||
// used by Wren. It's used like so:
|
||||
//
|
||||
// - To allocate new memory, [memory] is NULL and [oldSize] is zero. It should
|
||||
// return the allocated memory or NULL on failure.
|
||||
//
|
||||
// - To attempt to grow an existing allocation, [memory] is the memory,
|
||||
// [oldSize] is its previous size, and [newSize] is the desired size.
|
||||
// It should return [memory] if it was able to grow it in place, or a new
|
||||
// pointer if it had to move it.
|
||||
//
|
||||
// - To shrink memory, [memory], [oldSize], and [newSize] are the same as above
|
||||
// but it will always return [memory].
|
||||
//
|
||||
// - To free memory, [memory] will be the memory to free and [newSize] and
|
||||
// [oldSize] will be zero. It should return NULL.
|
||||
typedef void* (*WrenReallocateFn)(void* memory, size_t oldSize, size_t newSize);
|
||||
|
||||
typedef void (*WrenForeignMethodFn)(WrenVM* vm);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// The callback Wren will use to allocate, reallocate, and deallocate memory.
|
||||
//
|
||||
// If `NULL`, defaults to a built-in function that uses `realloc` and `free`.
|
||||
WrenReallocateFn reallocateFn;
|
||||
|
||||
// The number of bytes Wren will allocate before triggering the first garbage
|
||||
// collection.
|
||||
//
|
||||
// If zero, defaults to 10MB.
|
||||
size_t initialHeapSize;
|
||||
|
||||
// After a collection occurs, the threshold for the next collection is
|
||||
// determined based on the number of bytes remaining in use. This allows Wren
|
||||
// to shrink its memory usage automatically after reclaiming a large amount
|
||||
// of memory.
|
||||
//
|
||||
// This can be used to ensure that the heap does not get too small, which can
|
||||
// in turn lead to a large number of collections afterwards as the heap grows
|
||||
// back to a usable size.
|
||||
//
|
||||
// If zero, defaults to 1MB.
|
||||
size_t minHeapSize;
|
||||
|
||||
// Wren will grow (and shrink) the heap automatically as the number of bytes
|
||||
// remaining in use after a collection changes. This number determines the
|
||||
// amount of additional memory Wren will use after a collection, as a
|
||||
// percentage of the current heap size.
|
||||
//
|
||||
// For example, say that this is 50. After a garbage collection, Wren there
|
||||
// are 400 bytes of memory still in use. That means the next collection will
|
||||
// be triggered after a total of 600 bytes are allocated (including the 400
|
||||
// already in use.
|
||||
//
|
||||
// Setting this to a smaller number wastes less memory, but triggers more
|
||||
// frequent garbage collections.
|
||||
//
|
||||
// If zero, defaults to 50.
|
||||
int heapGrowthPercent;
|
||||
} WrenConfiguration;
|
||||
|
||||
typedef enum {
|
||||
WREN_RESULT_SUCCESS,
|
||||
WREN_RESULT_COMPILE_ERROR,
|
||||
WREN_RESULT_RUNTIME_ERROR
|
||||
} WrenInterpretResult;
|
||||
|
||||
// Creates a new Wren virtual machine using the given [configuration]. Wren
|
||||
// will copy the configuration data, so the argument passed to this can be
|
||||
// freed after calling this. If [configuration] is `NULL`, uses a default
|
||||
// configuration.
|
||||
WrenVM* wrenNewVM(WrenConfiguration* configuration);
|
||||
|
||||
// Disposes of all resources is use by [vm], which was previously created by a
|
||||
// call to [wrenNewVM].
|
||||
void wrenFreeVM(WrenVM* vm);
|
||||
|
||||
// Runs [source], a string of Wren source code in a new fiber in [vm].
|
||||
WrenInterpretResult wrenInterpret(WrenVM* vm, const char* sourcePath,
|
||||
const char* source);
|
||||
|
||||
// Defines a foreign method implemented by the host application. Looks for a
|
||||
// global class named [className] to bind the method to. If not found, it will
|
||||
// be created automatically.
|
||||
//
|
||||
// Defines a method on that class named [methodName] accepting [numParams]
|
||||
// parameters. If a method already exists with that name and arity, it will be
|
||||
// replaced. When invoked, the method will call [method].
|
||||
void wrenDefineMethod(WrenVM* vm, const char* className,
|
||||
const char* methodName, int numParams,
|
||||
WrenForeignMethodFn method);
|
||||
|
||||
// Defines a static foreign method implemented by the host application. Looks
|
||||
// for a global class named [className] to bind the method to. If not found, it
|
||||
// will be created automatically.
|
||||
//
|
||||
// Defines a static method on that class named [methodName] accepting
|
||||
// [numParams] parameters. If a method already exists with that name and arity,
|
||||
// it will be replaced. When invoked, the method will call [method].
|
||||
void wrenDefineStaticMethod(WrenVM* vm, const char* className,
|
||||
const char* methodName, int numParams,
|
||||
WrenForeignMethodFn method);
|
||||
|
||||
// Reads an numeric argument for a foreign call. This must only be called within
|
||||
// a function provided to [wrenDefineMethod]. Retrieves the argument at [index]
|
||||
// which ranges from 0 to the number of parameters the method expects - 1.
|
||||
double wrenGetArgumentDouble(WrenVM* vm, int index);
|
||||
|
||||
// Reads an string argument for a foreign call. This must only be called within
|
||||
// a function provided to [wrenDefineMethod]. Retrieves the argument at [index]
|
||||
// which ranges from 0 to the number of parameters the method expects - 1.
|
||||
//
|
||||
// The memory for the returned string is owned by Wren. You can inspect it
|
||||
// while in your foreign function, but cannot keep a pointer to it after the
|
||||
// function returns, since the garbage collector may reclaim it.
|
||||
const char* wrenGetArgumentString(WrenVM* vm, int index);
|
||||
|
||||
// Provides a numeric return value for a foreign call. This must only be called
|
||||
// within a function provided to [wrenDefineMethod]. Once this is called, the
|
||||
// foreign call is done, and no more arguments can be read or return calls made.
|
||||
void wrenReturnDouble(WrenVM* vm, double value);
|
||||
|
||||
// Provides a null return value for a foreign call. This must only be called
|
||||
// within a function provided to [wrenDefineMethod]. Once this is called, the
|
||||
// foreign call is done, and no more arguments can be read or return calls made.
|
||||
void wrenReturnNull(WrenVM* vm);
|
||||
|
||||
// Provides a string return value for a foreign call. This must only be called
|
||||
// within a function provided to [wrenDefineMethod]. Once this is called, the
|
||||
// foreign call is done, and no more arguments can be read or return calls made.
|
||||
//
|
||||
// The [text] will be copied to a new string within Wren's heap, so you can
|
||||
// free memory used by it after this is called. If [length] is non-zero, Wren
|
||||
// will copy that many bytes from [text]. If it is -1, then the length of
|
||||
// [text] will be calculated using `strlen()`.
|
||||
void wrenReturnString(WrenVM* vm, const char* text, int length);
|
||||
|
||||
#endif
|
||||
180
index.html
Normal file
180
index.html
Normal file
@ -0,0 +1,180 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title> – Wren</title>
|
||||
<script type="application/javascript" src="prism.js" data-manual></script>
|
||||
<script type="application/javascript" src="codejar.js"></script>
|
||||
<script type="application/javascript" src="wren.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="prism.css" />
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="./">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<a href="./"><img src="./wren.svg" class="logo"></a>
|
||||
<ul>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="contributing.html">Contributing</a></li>
|
||||
<li><a href="blog">Blog</a></li>
|
||||
<li><a href="try">Try it!</a></li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>guides</h2>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>API docs</h2>
|
||||
<ul>
|
||||
<li><a href="modules">Modules</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>reference</h2>
|
||||
<ul>
|
||||
<li><a href="cli">Wren CLI</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<div><a href="getting-started.html">Getting Started</a></div>
|
||||
<div><a href="contributing.html">Contributing</a></div>
|
||||
<div><a href="blog">Blog</a></div>
|
||||
<div><a href="try">Try it!</a></div>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><h2>guides</h2></td>
|
||||
<td><h2>reference</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="syntax.html">Syntax</a></li>
|
||||
<li><a href="values.html">Values</a></li>
|
||||
<li><a href="lists.html">Lists</a></li>
|
||||
<li><a href="maps.html">Maps</a></li>
|
||||
<li><a href="method-calls.html">Method Calls</a></li>
|
||||
<li><a href="control-flow.html">Control Flow</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="variables.html">Variables</a></li>
|
||||
<li><a href="classes.html">Classes</a></li>
|
||||
<li><a href="functions.html">Functions</a></li>
|
||||
<li><a href="concurrency.html">Concurrency</a></li>
|
||||
<li><a href="error-handling.html">Error Handling</a></li>
|
||||
<li><a href="modularity.html">Modularity</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="modules">API/Modules</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h2></h2>
|
||||
<h2>Wren is a small, fast, class-based concurrent scripting language <a href="#wren-is-a-small,-fast,-class-based-concurrent-scripting-language" name="wren-is-a-small,-fast,-class-based-concurrent-scripting-language" class="header-anchor">#</a></h2>
|
||||
<hr />
|
||||
<p>Think Smalltalk in a Lua-sized package with a dash of Erlang and wrapped up in
|
||||
a familiar, modern <a href="syntax.html">syntax</a>.</p>
|
||||
<pre class="snippet">
|
||||
System.print("Hello, world!")
|
||||
|
||||
class Wren {
|
||||
flyTo(city) {
|
||||
System.print("Flying to %(city)")
|
||||
}
|
||||
}
|
||||
|
||||
var adjectives = Fiber.new {
|
||||
["small", "clean", "fast"].each {|word| Fiber.yield(word) }
|
||||
}
|
||||
|
||||
while (!adjectives.isDone) System.print(adjectives.call())
|
||||
</pre>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>Wren is small.</strong> The VM implementation is under <a href="https://github.com/wren-lang/wren/tree/main/src">4,000 semicolons</a>.
|
||||
You can skim the whole thing in an afternoon. It’s <em>small</em>, but not
|
||||
<em>dense</em>. It is readable and <a href="https://github.com/wren-lang/wren/blob/46c1ba92492e9257aba6418403161072d640cb29/src/wren_value.h#L378-L433">lovingly-commented</a>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Wren is fast.</strong> A fast single-pass compiler to tight bytecode, and a
|
||||
compact object representation help Wren <a href="performance.html">compete with other dynamic
|
||||
languages</a>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Wren is class-based.</strong> There are lots of scripting languages out there,
|
||||
but many have unusual or non-existent object models. Wren places
|
||||
<a href="classes.html">classes</a> front and center.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Wren is concurrent.</strong> Lightweight <a href="concurrency.html">fibers</a> are core to the execution
|
||||
model and let you organize your program into a flock of communicating
|
||||
coroutines.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Wren is a scripting language.</strong> Wren is intended for embedding in
|
||||
applications. It has no dependencies, a small standard library,
|
||||
and <a href="embedding">an easy-to-use C API</a>. It compiles cleanly as C99, C++98
|
||||
or anything later.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<p>You can try it <a href="try">in your browser</a>! <br />
|
||||
If you like the sound of this, <a href="getting-started.html">let’s get started</a>. <br />
|
||||
Excited? You’re also welcome to <a href="contributing.html">get involved</a>!</p>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user