mirror of
https://github.com/kanaka/mal.git
synced 2024-09-11 13:55:55 +03:00
commit
3a9018f17b
@ -111,6 +111,7 @@ matrix:
|
||||
- {env: IMPL=wasm wasm_MODE=node NO_SELF_HOST_PERF=1, services: [docker]}
|
||||
- {env: IMPL=wasm wasm_MODE=warpy NO_SELF_HOST_PERF=1, services: [docker]}
|
||||
- {env: IMPL=wasm wasm_MODE=wace_libc NO_SELF_HOST_PERF=1, services: [docker]}
|
||||
- {env: IMPL=wren, services: [docker]}
|
||||
- {env: IMPL=yorick, services: [docker]}
|
||||
|
||||
script:
|
||||
|
3
Makefile
3
Makefile
@ -94,7 +94,7 @@ IMPLS = ada ada.2 awk bash basic bbc-basic c chuck clojure coffee common-lisp cp
|
||||
guile haskell haxe hy io java js julia kotlin livescript logo lua make mal \
|
||||
matlab miniMAL nasm nim objc objpascal ocaml perl perl6 php picolisp pike plpgsql \
|
||||
plsql powershell ps python python.2 r racket rexx rpython ruby rust scala scheme skew \
|
||||
swift swift3 swift4 tcl ts vala vb vhdl vimscript wasm yorick
|
||||
swift swift3 swift4 tcl ts vala vb vhdl vimscript wasm wren yorick
|
||||
|
||||
EXTENSION = .mal
|
||||
|
||||
@ -262,6 +262,7 @@ vb_STEP_TO_PROG = vb/$($(1)).exe
|
||||
vhdl_STEP_TO_PROG = vhdl/$($(1))
|
||||
vimscript_STEP_TO_PROG = vimscript/$($(1)).vim
|
||||
wasm_STEP_TO_PROG = wasm/$($(1)).$(if $(filter lucet,$(wasm_MODE)),so,wasm)
|
||||
wren_STEP_TO_PROG = wren/$($(1)).wren
|
||||
yorick_STEP_TO_PROG = yorick/$($(1)).i
|
||||
|
||||
|
||||
|
12
README.md
12
README.md
@ -6,7 +6,7 @@
|
||||
|
||||
**1. Mal is a Clojure inspired Lisp interpreter**
|
||||
|
||||
**2. Mal is implemented in 78 languages (80 different implementations and 101 runtime modes)**
|
||||
**2. Mal is implemented in 79 languages (81 different implementations and 102 runtime modes)**
|
||||
|
||||
| Language | Creator |
|
||||
| -------- | ------- |
|
||||
@ -89,6 +89,7 @@
|
||||
| [Vimscript](#vimscript) | [Dov Murik](https://github.com/dubek) |
|
||||
| [Visual Basic.NET](#visual-basicnet) | [Joel Martin](https://github.com/kanaka) |
|
||||
| [WebAssembly](#webassembly-wasm) (wasm) | [Joel Martin](https://github.com/kanaka) |
|
||||
| [Wren](#wren) | [Dov Murik](https://github.com/dubek) |
|
||||
| [Yorick](#yorick) | [Dov Murik](https://github.com/dubek) |
|
||||
|
||||
|
||||
@ -1119,6 +1120,15 @@ make wasm_MODE=warpy
|
||||
warpy --argv --memory-pages 256 ./stepX_YYY.wasm
|
||||
```
|
||||
|
||||
### Wren
|
||||
|
||||
The Wren implementation of mal was tested on Wren 0.2.0.
|
||||
|
||||
```
|
||||
cd wren
|
||||
wren ./stepX_YYY.wren
|
||||
```
|
||||
|
||||
### Yorick
|
||||
|
||||
The Yorick implementation of mal was tested on Yorick 2.2.04.
|
||||
|
33
wren/Dockerfile
Normal file
33
wren/Dockerfile
Normal file
@ -0,0 +1,33 @@
|
||||
FROM ubuntu:18.04
|
||||
MAINTAINER Joel Martin <github@martintribe.org>
|
||||
|
||||
##########################################################
|
||||
# General requirements for testing or common across many
|
||||
# implementations
|
||||
##########################################################
|
||||
|
||||
RUN apt-get -y update
|
||||
|
||||
# Required for running tests
|
||||
RUN apt-get -y install make python
|
||||
|
||||
# Some typical implementation and test requirements
|
||||
RUN apt-get -y install curl libreadline-dev libedit-dev
|
||||
|
||||
RUN mkdir -p /mal
|
||||
WORKDIR /mal
|
||||
|
||||
##########################################################
|
||||
# Specific implementation requirements
|
||||
##########################################################
|
||||
|
||||
RUN apt-get -y install g++
|
||||
RUN apt-get -y install git
|
||||
|
||||
COPY wren-add-gettimeofday.patch /tmp/
|
||||
RUN cd /tmp && git clone --depth=1 https://github.com/wren-lang/wren.git \
|
||||
&& cd wren \
|
||||
&& patch -p1 < /tmp/wren-add-gettimeofday.patch \
|
||||
&& make \
|
||||
&& cp ./wren /usr/local/bin/ \
|
||||
&& cd /tmp && rm -rf wren
|
14
wren/Makefile
Normal file
14
wren/Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
SOURCES = types.wren env.wren printer.wren reader.wren readline.wren interop.wren core.wren stepA_mal.wren
|
||||
|
||||
all:
|
||||
true
|
||||
|
||||
dist: mal
|
||||
|
||||
mal.wren: $(SOURCES)
|
||||
cat $+ | grep -v '^import "./' > $@
|
||||
|
||||
mal: mal.wren
|
||||
echo "#!/usr/bin/env wren" > $@
|
||||
cat $< >> $@
|
||||
chmod +x $@
|
15
wren/README.md
Normal file
15
wren/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Wren implementation
|
||||
|
||||
### Adding a time function
|
||||
|
||||
Since Wren doesn't have a time function, we add a `System.gettimeofday`
|
||||
function which returns a float with the number of seconds since epoch (with
|
||||
fractions of seconds).
|
||||
|
||||
This is done by applying the patch in `wren-add-gettimeofday.path` to Wren's
|
||||
source code before compiling it (see `Dockerfile`).
|
||||
|
||||
### Wren interop
|
||||
|
||||
See examples in `tests/stepA_mal.mal` for usage of `wren-eval` to evaluate Wren
|
||||
expressions inside a Mal program.
|
105
wren/core.wren
Normal file
105
wren/core.wren
Normal file
@ -0,0 +1,105 @@
|
||||
import "io" for File
|
||||
import "./reader" for MalReader
|
||||
import "./readline" for Readline
|
||||
import "./printer" for Printer
|
||||
import "./types" for MalVal, MalSymbol, MalSequential, MalList, MalVector, MalMap, MalNativeFn, MalFn, MalAtom, MalException
|
||||
import "./interop" for Interop
|
||||
|
||||
class Core {
|
||||
static fn(func) { MalNativeFn.new(func) }
|
||||
static ns {
|
||||
return {
|
||||
"=": fn { |a| a[0] == a[1] },
|
||||
"throw": fn { |a|
|
||||
MalException.set(a[0])
|
||||
Fiber.abort("___MalException___")
|
||||
},
|
||||
|
||||
"nil?": fn { |a| a[0] == null },
|
||||
"true?": fn { |a| a[0] == true },
|
||||
"false?": fn { |a| a[0] == false },
|
||||
"string?": fn { |a| a[0] is String && !MalVal.isKeyword(a[0]) },
|
||||
"symbol": fn { |a| a[0] is MalSymbol ? a[0] : MalSymbol.new(a[0]) },
|
||||
"symbol?": fn { |a| a[0] is MalSymbol },
|
||||
"keyword": fn { |a| MalVal.isKeyword(a[0]) ? a[0] : MalVal.newKeyword(a[0]) },
|
||||
"keyword?": fn { |a| MalVal.isKeyword(a[0]) },
|
||||
"number?": fn { |a| a[0] is Num },
|
||||
"fn?": fn { |a| a[0] is MalNativeFn || (a[0] is MalFn && !a[0].isMacro) },
|
||||
"macro?": fn { |a| a[0] is MalFn && a[0].isMacro },
|
||||
|
||||
"pr-str": fn { |a| a.map { |e| Printer.pr_str(e, true) }.join(" ") },
|
||||
"str": fn { |a| a.map { |e| Printer.pr_str(e, false) }.join() },
|
||||
"prn": fn { |a|
|
||||
System.print(a.map { |e| Printer.pr_str(e, true) }.join(" "))
|
||||
return null
|
||||
},
|
||||
"println": fn { |a|
|
||||
System.print(a.map { |e| Printer.pr_str(e, false) }.join(" "))
|
||||
return null
|
||||
},
|
||||
"read-string": fn { |a| MalReader.read_str(a[0]) },
|
||||
"readline": fn { |a| Readline.readLine(a[0]) },
|
||||
"slurp": fn { |a| File.read(a[0]) },
|
||||
|
||||
"<": fn { |a| a[0] < a[1] },
|
||||
"<=": fn { |a| a[0] <= a[1] },
|
||||
">": fn { |a| a[0] > a[1] },
|
||||
">=": fn { |a| a[0] >= a[1] },
|
||||
"+": fn { |a| a[0] + a[1] },
|
||||
"-": fn { |a| a[0] - a[1] },
|
||||
"*": fn { |a| a[0] * a[1] },
|
||||
"/": fn { |a| a[0] / a[1] },
|
||||
"time-ms": fn { |a| (System.gettimeofday * 1000).floor },
|
||||
|
||||
"list": fn { |a| MalList.new(a) },
|
||||
"list?": fn { |a| a[0] is MalList },
|
||||
"vector": fn { |a| MalVector.new(a) },
|
||||
"vector?": fn { |a| a[0] is MalVector },
|
||||
"hash-map": fn { |a| MalMap.fromList(a) },
|
||||
"map?": fn { |a| a[0] is MalMap },
|
||||
"assoc": fn { |a| a[0].assoc(a[1...a.count]) },
|
||||
"dissoc": fn { |a| a[0].dissoc(a[1...a.count]) },
|
||||
"get": fn { |a| a[0] == null ? null : a[0].data[a[1]] },
|
||||
"contains?": fn { |a| a[0].data.containsKey(a[1]) },
|
||||
"keys": fn { |a| MalList.new(a[0].data.keys.toList) },
|
||||
"vals": fn { |a| MalList.new(a[0].data.values.toList) },
|
||||
|
||||
"sequential?": fn { |a| a[0] is MalSequential },
|
||||
"cons": fn { |a| MalList.new([a[0]] + a[1].elements) },
|
||||
"concat": fn { |a| MalList.new(a.reduce([]) { |acc,e| acc + e.elements }) },
|
||||
"nth": fn { |a| a[1] < a[0].count ? a[0][a[1]] : Fiber.abort("nth: index out of range") },
|
||||
"first": fn { |a| a[0] == null ? null : a[0].first },
|
||||
"rest": fn { |a| a[0] == null ? MalList.new([]) : a[0].rest },
|
||||
"empty?": fn { |a| a[0].isEmpty },
|
||||
"count": fn { |a| a[0] == null ? 0 : a[0].count },
|
||||
"apply": fn { |a| a[0].call(a[1...(a.count - 1)] + a[-1].elements) },
|
||||
"map": fn { |a| MalList.new(a[1].elements.map { |e| a[0].call([e]) }.toList) },
|
||||
|
||||
"conj": fn { |a|
|
||||
if (a[0] is MalList) return MalList.new(a[-1..1] + a[0].elements)
|
||||
if (a[0] is MalVector) return MalVector.new(a[0].elements + a[1..-1])
|
||||
},
|
||||
"seq": fn { |a|
|
||||
if (a[0] == null) return null
|
||||
if (a[0].count == 0) return null
|
||||
if (a[0] is String) return MalList.new(a[0].toList)
|
||||
if (a[0] is MalVector) return MalList.new(a[0].elements)
|
||||
return a[0]
|
||||
},
|
||||
|
||||
"meta": fn { |a| a[0].meta },
|
||||
"with-meta": fn { |a|
|
||||
var x = a[0].clone()
|
||||
x.meta = a[1]
|
||||
return x
|
||||
},
|
||||
"atom": fn { |a| MalAtom.new(a[0]) },
|
||||
"atom?": fn { |a| a[0] is MalAtom },
|
||||
"deref": fn { |a| a[0].value },
|
||||
"reset!": fn { |a| a[0].value = a[1] },
|
||||
"swap!": fn { |a| a[0].value = a[1].call([a[0].value] + a[2..-1]) },
|
||||
|
||||
"wren-eval": fn { |a| Interop.wren_eval(a[0]) }
|
||||
}
|
||||
}
|
||||
}
|
40
wren/env.wren
Normal file
40
wren/env.wren
Normal file
@ -0,0 +1,40 @@
|
||||
import "./types" for MalList
|
||||
|
||||
class Env {
|
||||
construct new() {
|
||||
_outer = null
|
||||
_data = {}
|
||||
}
|
||||
construct new(outer) {
|
||||
_outer = outer
|
||||
_data = {}
|
||||
}
|
||||
construct new(outer, binds, exprs) {
|
||||
_outer = outer
|
||||
_data = {}
|
||||
for (i in 0...binds.count) {
|
||||
if (binds[i].value == "&") {
|
||||
_data[binds[i + 1].value] = MalList.new(exprs[i..-1])
|
||||
break
|
||||
} else {
|
||||
_data[binds[i].value] = exprs[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set(k, v) { _data[k] = v }
|
||||
|
||||
find(k) {
|
||||
if (_data.containsKey(k)) return this
|
||||
if (_outer) return _outer.find(k)
|
||||
return null
|
||||
}
|
||||
|
||||
get(k) {
|
||||
var foundEnv = find(k)
|
||||
if (!foundEnv) Fiber.abort("'%(k)' not found")
|
||||
return foundEnv.getValue(k)
|
||||
}
|
||||
|
||||
getValue(k) { _data[k] }
|
||||
}
|
23
wren/interop.wren
Normal file
23
wren/interop.wren
Normal file
@ -0,0 +1,23 @@
|
||||
import "meta" for Meta
|
||||
import "./types" for MalList, MalMap
|
||||
|
||||
class Interop {
|
||||
static wren_eval(str) {
|
||||
var f = Meta.compileExpression(str)
|
||||
return f == null ? null : wren2mal(f.call())
|
||||
}
|
||||
|
||||
static wren2mal(v) {
|
||||
if (v == null || v == true || v == false) return v
|
||||
if (v is Num || v is String) return v
|
||||
if (v is Map) {
|
||||
var m = {}
|
||||
for (e in v) {
|
||||
m[wren2mal(e.key)] = wren2mal(e.value)
|
||||
}
|
||||
return MalMap.new(m)
|
||||
}
|
||||
if (v is Sequence) return MalList.new(v.map { |e| wren2mal(e) }.toList)
|
||||
return null
|
||||
}
|
||||
}
|
30
wren/printer.wren
Normal file
30
wren/printer.wren
Normal file
@ -0,0 +1,30 @@
|
||||
import "./types" for MalVal, MalList, MalVector, MalMap, MalNativeFn, MalFn, MalAtom
|
||||
|
||||
class Printer {
|
||||
static joinElements(elements, print_readably) {
|
||||
return elements.map { |e| pr_str(e, print_readably) }.join(" ")
|
||||
}
|
||||
|
||||
static joinMapElements(data, print_readably) {
|
||||
return data.map { |e| pr_str(e.key, print_readably) + " " + pr_str(e.value, print_readably) }.join(" ")
|
||||
}
|
||||
|
||||
static escape(s) {
|
||||
return "\"" + s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n") + "\""
|
||||
}
|
||||
|
||||
static pr_str(obj) { pr_str(obj, true) }
|
||||
|
||||
static pr_str(obj, print_readably) {
|
||||
if (obj == null) return "nil"
|
||||
if (obj is MalList) return "(%(joinElements(obj.elements, print_readably)))"
|
||||
if (obj is MalVector) return "[%(joinElements(obj.elements, print_readably))]"
|
||||
if (obj is MalMap) return "{%(joinMapElements(obj.data, print_readably))}"
|
||||
if (obj is MalNativeFn) return "#<MalNativeFn>"
|
||||
if (obj is MalFn) return "#<MalFn>"
|
||||
if (obj is MalAtom) return "(atom %(pr_str(obj.value, print_readably)))"
|
||||
if (MalVal.isKeyword(obj)) return ":%(obj[1..-1])"
|
||||
if (obj is String) return print_readably ? escape(obj) : obj
|
||||
return obj.toString
|
||||
}
|
||||
}
|
170
wren/reader.wren
Normal file
170
wren/reader.wren
Normal file
@ -0,0 +1,170 @@
|
||||
import "./types" for MalVal, MalSymbol, MalList, MalVector, MalMap
|
||||
|
||||
class Tokenizer {
|
||||
construct new(s) {
|
||||
_s = s
|
||||
}
|
||||
|
||||
tokenize() {
|
||||
_pos = 0
|
||||
var tokens = []
|
||||
while (true) {
|
||||
var token = nextToken()
|
||||
if (token == null) break
|
||||
if (token.count > 0) tokens.add(token)
|
||||
}
|
||||
return tokens
|
||||
}
|
||||
|
||||
static eolChars { "\r\n" }
|
||||
static whitespace { " ,\r\n\t" }
|
||||
static delimiters { "[]{}()'`^@" }
|
||||
static separators { Tokenizer.whitespace + "[]{}()'\"`,;" }
|
||||
|
||||
nextToken() {
|
||||
if (isEOF()) return null
|
||||
var ch = curr
|
||||
if (Tokenizer.whitespace.contains(ch)) {
|
||||
advance()
|
||||
return ""
|
||||
}
|
||||
if (Tokenizer.delimiters.contains(ch)) {
|
||||
advance()
|
||||
return ch
|
||||
}
|
||||
if (ch == "~") {
|
||||
advance()
|
||||
if (!isEOF() && curr == "@") {
|
||||
advance()
|
||||
return "~@"
|
||||
} else {
|
||||
return "~"
|
||||
}
|
||||
}
|
||||
if (ch == ";") {
|
||||
advance()
|
||||
while (!isEOF() && !Tokenizer.eolChars.contains(curr)) advance()
|
||||
return ""
|
||||
}
|
||||
if (ch == "\"") {
|
||||
var s = ch
|
||||
advance()
|
||||
while (!isEOF() && curr != "\"") {
|
||||
if (curr == "\\") {
|
||||
s = s + curr
|
||||
advance()
|
||||
if (isEOF()) Fiber.abort("expected '\"', got EOF 111")
|
||||
}
|
||||
s = s + curr
|
||||
advance()
|
||||
}
|
||||
if (isEOF()) Fiber.abort("expected '\"', got EOF 222")
|
||||
s = s + curr
|
||||
advance()
|
||||
return s
|
||||
}
|
||||
var token = ch
|
||||
advance()
|
||||
while (!isEOF() && !Tokenizer.separators.contains(curr)) {
|
||||
token = token + curr
|
||||
advance()
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
curr { _s[_pos] }
|
||||
isEOF() { _pos >= _s.count }
|
||||
advance() { _pos = _pos + 1 }
|
||||
}
|
||||
|
||||
class Reader {
|
||||
construct new(tokens) {
|
||||
_tokens = tokens
|
||||
_pos = 0
|
||||
}
|
||||
|
||||
next() {
|
||||
if (_pos >= _tokens.count) return null
|
||||
var token = _tokens[_pos]
|
||||
_pos = _pos + 1
|
||||
return token
|
||||
}
|
||||
|
||||
peek() {
|
||||
if (_pos >= _tokens.count) return null
|
||||
return _tokens[_pos]
|
||||
}
|
||||
}
|
||||
|
||||
class MalReader {
|
||||
static parse_str(token) {
|
||||
if (token.count <= 2) return ""
|
||||
return token[1..-2].replace("\\\\", "\u029e").replace("\\\"", "\"").replace("\\n", "\n").replace("\u029e", "\\")
|
||||
}
|
||||
|
||||
static is_all_digits(s) {
|
||||
if (s.count == 0) return false
|
||||
return s.all { |c| c.bytes[0] >= 0x30 && c.bytes[0] <= 0x39 }
|
||||
}
|
||||
|
||||
static is_number(token) {
|
||||
return token.startsWith("-") ? is_all_digits(token[1..-1]) : is_all_digits(token)
|
||||
}
|
||||
|
||||
static read_atom(rdr) {
|
||||
var token = rdr.next()
|
||||
if (is_number(token)) return Num.fromString(token)
|
||||
if (token.startsWith("\"")) return parse_str(token)
|
||||
if (token.startsWith(":")) return MalVal.newKeyword(token[1..-1])
|
||||
if (token == "nil") return null
|
||||
if (token == "true") return true
|
||||
if (token == "false") return false
|
||||
return MalSymbol.new(token)
|
||||
}
|
||||
|
||||
static read_seq(rdr, start, end) {
|
||||
var token = rdr.next()
|
||||
if (token != start) Fiber.abort("expected '%(start)'")
|
||||
var elements = []
|
||||
token = rdr.peek()
|
||||
while (token != end) {
|
||||
if (!token) Fiber.abort("expected '%(end)', got EOF")
|
||||
elements.add(read_form(rdr))
|
||||
token = rdr.peek()
|
||||
}
|
||||
rdr.next()
|
||||
return elements
|
||||
}
|
||||
|
||||
static reader_macro(rdr, sym) {
|
||||
rdr.next()
|
||||
return MalList.new([MalSymbol.new(sym), read_form(rdr)])
|
||||
}
|
||||
|
||||
static read_form(rdr) {
|
||||
var token = rdr.peek()
|
||||
if (token == "'") return reader_macro(rdr, "quote")
|
||||
if (token == "`") return reader_macro(rdr, "quasiquote")
|
||||
if (token == "~") return reader_macro(rdr, "unquote")
|
||||
if (token == "~@") return reader_macro(rdr, "splice-unquote")
|
||||
if (token == "^") {
|
||||
rdr.next()
|
||||
var meta = read_form(rdr)
|
||||
return MalList.new([MalSymbol.new("with-meta"), read_form(rdr), meta])
|
||||
}
|
||||
if (token == "@") return reader_macro(rdr, "deref")
|
||||
if (token == "(") return MalList.new(read_seq(rdr, "(", ")"))
|
||||
if (token == ")") Fiber.abort("unexpected ')'")
|
||||
if (token == "[") return MalVector.new(read_seq(rdr, "[", "]"))
|
||||
if (token == "]") Fiber.abort("unexpected ']'")
|
||||
if (token == "{") return MalMap.fromList(read_seq(rdr, "{", "}"))
|
||||
if (token == "}") Fiber.abort("unexpected '}'")
|
||||
return read_atom(rdr)
|
||||
}
|
||||
|
||||
static read_str(s) {
|
||||
var tokens = Tokenizer.new(s).tokenize()
|
||||
if (tokens.count == 0) return null
|
||||
return read_form(Reader.new(tokens))
|
||||
}
|
||||
}
|
14
wren/readline.wren
Normal file
14
wren/readline.wren
Normal file
@ -0,0 +1,14 @@
|
||||
import "io" for Stdin, Stdout
|
||||
|
||||
class Readline {
|
||||
static readLine(prompt) {
|
||||
var line = null
|
||||
var fiber = Fiber.new {
|
||||
System.write(prompt)
|
||||
Stdout.flush()
|
||||
line = Stdin.readLine()
|
||||
}
|
||||
var error = fiber.try()
|
||||
return error ? null : line
|
||||
}
|
||||
}
|
2
wren/run
Executable file
2
wren/run
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
exec wren $(dirname $0)/${STEP:-stepA_mal}.wren "${@}"
|
30
wren/step0_repl.wren
Normal file
30
wren/step0_repl.wren
Normal file
@ -0,0 +1,30 @@
|
||||
import "./readline" for Readline
|
||||
|
||||
class Mal {
|
||||
static read(str) {
|
||||
return str
|
||||
}
|
||||
|
||||
static eval(ast, env) {
|
||||
return ast
|
||||
}
|
||||
|
||||
static print(ast) {
|
||||
return ast
|
||||
}
|
||||
|
||||
static rep(str) {
|
||||
return print(eval(read(str), null))
|
||||
}
|
||||
|
||||
static main() {
|
||||
while (true) {
|
||||
var line = Readline.readLine("user> ")
|
||||
if (line == null) break
|
||||
if (line != "") System.print(rep(line))
|
||||
}
|
||||
System.print()
|
||||
}
|
||||
}
|
||||
|
||||
Mal.main()
|
36
wren/step1_read_print.wren
Normal file
36
wren/step1_read_print.wren
Normal file
@ -0,0 +1,36 @@
|
||||
import "./readline" for Readline
|
||||
import "./reader" for MalReader
|
||||
import "./printer" for Printer
|
||||
|
||||
class Mal {
|
||||
static read(str) {
|
||||
return MalReader.read_str(str)
|
||||
}
|
||||
|
||||
static eval(ast, env) {
|
||||
return ast
|
||||
}
|
||||
|
||||
static print(ast) {
|
||||
return Printer.pr_str(ast)
|
||||
}
|
||||
|
||||
static rep(str) {
|
||||
return print(eval(read(str), null))
|
||||
}
|
||||
|
||||
static main() {
|
||||
while (true) {
|
||||
var line = Readline.readLine("user> ")
|
||||
if (line == null) break
|
||||
if (line != "") {
|
||||
var fiber = Fiber.new { System.print(rep(line)) }
|
||||
fiber.try()
|
||||
if (fiber.error) System.print("Error: %(fiber.error)")
|
||||
}
|
||||
}
|
||||
System.print()
|
||||
}
|
||||
}
|
||||
|
||||
Mal.main()
|
66
wren/step2_eval.wren
Normal file
66
wren/step2_eval.wren
Normal file
@ -0,0 +1,66 @@
|
||||
import "./readline" for Readline
|
||||
import "./reader" for MalReader
|
||||
import "./printer" for Printer
|
||||
import "./types" for MalSymbol, MalList, MalVector, MalMap
|
||||
|
||||
class Mal {
|
||||
static read(str) {
|
||||
return MalReader.read_str(str)
|
||||
}
|
||||
|
||||
static eval_ast(ast, env) {
|
||||
if (ast is MalSymbol) {
|
||||
if (!env.containsKey(ast.value)) Fiber.abort("'%(ast.value)' not found")
|
||||
return env[ast.value]
|
||||
} else if (ast is MalList) {
|
||||
return MalList.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalVector) {
|
||||
return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalMap) {
|
||||
var m = {}
|
||||
for (e in ast.data) {
|
||||
m[e.key] = eval(e.value, env)
|
||||
}
|
||||
return MalMap.new(m)
|
||||
} else {
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
static eval(ast, env) {
|
||||
if (!(ast is MalList)) return eval_ast(ast, env)
|
||||
if (ast.isEmpty) return ast
|
||||
var evaled_ast = eval_ast(ast, env)
|
||||
var f = evaled_ast[0]
|
||||
return f.call(evaled_ast[1..-1])
|
||||
}
|
||||
|
||||
static print(ast) {
|
||||
return Printer.pr_str(ast)
|
||||
}
|
||||
|
||||
static rep(str) {
|
||||
return print(eval(read(str), __repl_env))
|
||||
}
|
||||
|
||||
static main() {
|
||||
__repl_env = {
|
||||
"+": Fn.new { |a| a[0] + a[1] },
|
||||
"-": Fn.new { |a| a[0] - a[1] },
|
||||
"*": Fn.new { |a| a[0] * a[1] },
|
||||
"/": Fn.new { |a| a[0] / a[1] }
|
||||
}
|
||||
while (true) {
|
||||
var line = Readline.readLine("user> ")
|
||||
if (line == null) break
|
||||
if (line != "") {
|
||||
var fiber = Fiber.new { System.print(rep(line)) }
|
||||
fiber.try()
|
||||
if (fiber.error) System.print("Error: %(fiber.error)")
|
||||
}
|
||||
}
|
||||
System.print()
|
||||
}
|
||||
}
|
||||
|
||||
Mal.main()
|
78
wren/step3_env.wren
Normal file
78
wren/step3_env.wren
Normal file
@ -0,0 +1,78 @@
|
||||
import "./env" for Env
|
||||
import "./readline" for Readline
|
||||
import "./reader" for MalReader
|
||||
import "./printer" for Printer
|
||||
import "./types" for MalSymbol, MalList, MalVector, MalMap
|
||||
|
||||
class Mal {
|
||||
static read(str) {
|
||||
return MalReader.read_str(str)
|
||||
}
|
||||
|
||||
static eval_ast(ast, env) {
|
||||
if (ast is MalSymbol) {
|
||||
return env.get(ast.value)
|
||||
} else if (ast is MalList) {
|
||||
return MalList.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalVector) {
|
||||
return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalMap) {
|
||||
var m = {}
|
||||
for (e in ast.data) {
|
||||
m[e.key] = eval(e.value, env)
|
||||
}
|
||||
return MalMap.new(m)
|
||||
} else {
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
static eval(ast, env) {
|
||||
if (!(ast is MalList)) return eval_ast(ast, env)
|
||||
if (ast.isEmpty) return ast
|
||||
if (ast[0] is MalSymbol) {
|
||||
if (ast[0].value == "def!") {
|
||||
return env.set(ast[1].value, eval(ast[2], env))
|
||||
} else if (ast[0].value == "let*") {
|
||||
var letEnv = Env.new(env)
|
||||
var i = 0
|
||||
while (i < ast[1].count) {
|
||||
letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))
|
||||
i = i + 2
|
||||
}
|
||||
return eval(ast[2], letEnv)
|
||||
}
|
||||
}
|
||||
var evaled_ast = eval_ast(ast, env)
|
||||
var f = evaled_ast[0]
|
||||
return f.call(evaled_ast[1..-1])
|
||||
}
|
||||
|
||||
static print(ast) {
|
||||
return Printer.pr_str(ast)
|
||||
}
|
||||
|
||||
static rep(str) {
|
||||
return print(eval(read(str), __repl_env))
|
||||
}
|
||||
|
||||
static main() {
|
||||
__repl_env = Env.new()
|
||||
__repl_env.set("+", Fn.new { |a| a[0] + a[1] })
|
||||
__repl_env.set("-", Fn.new { |a| a[0] - a[1] })
|
||||
__repl_env.set("*", Fn.new { |a| a[0] * a[1] })
|
||||
__repl_env.set("/", Fn.new { |a| a[0] / a[1] })
|
||||
while (true) {
|
||||
var line = Readline.readLine("user> ")
|
||||
if (line == null) break
|
||||
if (line != "") {
|
||||
var fiber = Fiber.new { System.print(rep(line)) }
|
||||
fiber.try()
|
||||
if (fiber.error) System.print("Error: %(fiber.error)")
|
||||
}
|
||||
}
|
||||
System.print()
|
||||
}
|
||||
}
|
||||
|
||||
Mal.main()
|
90
wren/step4_if_fn_do.wren
Normal file
90
wren/step4_if_fn_do.wren
Normal file
@ -0,0 +1,90 @@
|
||||
import "./env" for Env
|
||||
import "./readline" for Readline
|
||||
import "./reader" for MalReader
|
||||
import "./printer" for Printer
|
||||
import "./types" for MalSymbol, MalList, MalVector, MalMap
|
||||
import "./core" for Core
|
||||
|
||||
class Mal {
|
||||
static read(str) {
|
||||
return MalReader.read_str(str)
|
||||
}
|
||||
|
||||
static eval_ast(ast, env) {
|
||||
if (ast is MalSymbol) {
|
||||
return env.get(ast.value)
|
||||
} else if (ast is MalList) {
|
||||
return MalList.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalVector) {
|
||||
return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalMap) {
|
||||
var m = {}
|
||||
for (e in ast.data) {
|
||||
m[e.key] = eval(e.value, env)
|
||||
}
|
||||
return MalMap.new(m)
|
||||
} else {
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
static eval(ast, env) {
|
||||
if (!(ast is MalList)) return eval_ast(ast, env)
|
||||
if (ast.isEmpty) return ast
|
||||
if (ast[0] is MalSymbol) {
|
||||
if (ast[0].value == "def!") {
|
||||
return env.set(ast[1].value, eval(ast[2], env))
|
||||
} else if (ast[0].value == "let*") {
|
||||
var letEnv = Env.new(env)
|
||||
var i = 0
|
||||
while (i < ast[1].count) {
|
||||
letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))
|
||||
i = i + 2
|
||||
}
|
||||
return eval(ast[2], letEnv)
|
||||
} else if (ast[0].value == "do") {
|
||||
return eval_ast(ast.rest, env)[-1]
|
||||
} else if (ast[0].value == "if") {
|
||||
var condval = eval(ast[1], env)
|
||||
if (condval) {
|
||||
return eval(ast[2], env)
|
||||
} else {
|
||||
return ast.count > 3 ? eval(ast[3], env) : null
|
||||
}
|
||||
} else if (ast[0].value == "fn*") {
|
||||
return Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) }
|
||||
}
|
||||
}
|
||||
var evaled_ast = eval_ast(ast, env)
|
||||
var f = evaled_ast[0]
|
||||
return f.call(evaled_ast[1..-1])
|
||||
}
|
||||
|
||||
static print(ast) {
|
||||
return Printer.pr_str(ast)
|
||||
}
|
||||
|
||||
static rep(str) {
|
||||
return print(eval(read(str), __repl_env))
|
||||
}
|
||||
|
||||
static main() {
|
||||
__repl_env = Env.new()
|
||||
// core.wren: defined in wren
|
||||
for (e in Core.ns) { __repl_env.set(e.key, e.value) }
|
||||
// core.mal: defined using the language itself
|
||||
rep("(def! not (fn* (a) (if a false true)))")
|
||||
while (true) {
|
||||
var line = Readline.readLine("user> ")
|
||||
if (line == null) break
|
||||
if (line != "") {
|
||||
var fiber = Fiber.new { System.print(rep(line)) }
|
||||
fiber.try()
|
||||
if (fiber.error) System.print("Error: %(fiber.error)")
|
||||
}
|
||||
}
|
||||
System.print()
|
||||
}
|
||||
}
|
||||
|
||||
Mal.main()
|
112
wren/step5_tco.wren
Normal file
112
wren/step5_tco.wren
Normal file
@ -0,0 +1,112 @@
|
||||
import "./env" for Env
|
||||
import "./readline" for Readline
|
||||
import "./reader" for MalReader
|
||||
import "./printer" for Printer
|
||||
import "./types" for MalSymbol, MalList, MalVector, MalMap, MalNativeFn, MalFn
|
||||
import "./core" for Core
|
||||
|
||||
class Mal {
|
||||
static read(str) {
|
||||
return MalReader.read_str(str)
|
||||
}
|
||||
|
||||
static eval_ast(ast, env) {
|
||||
if (ast is MalSymbol) {
|
||||
return env.get(ast.value)
|
||||
} else if (ast is MalList) {
|
||||
return MalList.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalVector) {
|
||||
return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalMap) {
|
||||
var m = {}
|
||||
for (e in ast.data) {
|
||||
m[e.key] = eval(e.value, env)
|
||||
}
|
||||
return MalMap.new(m)
|
||||
} else {
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
static eval(ast, env) {
|
||||
while (true) {
|
||||
var tco = false
|
||||
if (!(ast is MalList)) return eval_ast(ast, env)
|
||||
if (ast.isEmpty) return ast
|
||||
if (ast[0] is MalSymbol) {
|
||||
if (ast[0].value == "def!") {
|
||||
return env.set(ast[1].value, eval(ast[2], env))
|
||||
} else if (ast[0].value == "let*") {
|
||||
var letEnv = Env.new(env)
|
||||
var i = 0
|
||||
while (i < ast[1].count) {
|
||||
letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))
|
||||
i = i + 2
|
||||
}
|
||||
ast = ast[2]
|
||||
env = letEnv
|
||||
tco = true
|
||||
} else if (ast[0].value == "do") {
|
||||
for (i in 1...(ast.count - 1)) {
|
||||
eval(ast[i], env)
|
||||
}
|
||||
ast = ast[-1]
|
||||
tco = true
|
||||
} else if (ast[0].value == "if") {
|
||||
var condval = eval(ast[1], env)
|
||||
if (condval) {
|
||||
ast = ast[2]
|
||||
} else {
|
||||
if (ast.count <= 3) return null
|
||||
ast = ast[3]
|
||||
}
|
||||
tco = true
|
||||
} else if (ast[0].value == "fn*") {
|
||||
return MalFn.new(ast[2], ast[1].elements, env,
|
||||
Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) })
|
||||
}
|
||||
}
|
||||
if (!tco) {
|
||||
var evaled_ast = eval_ast(ast, env)
|
||||
var f = evaled_ast[0]
|
||||
if (f is MalNativeFn) {
|
||||
return f.call(evaled_ast[1..-1])
|
||||
} else if (f is MalFn) {
|
||||
ast = f.ast
|
||||
env = Env.new(f.env, f.params, evaled_ast[1..-1])
|
||||
tco = true
|
||||
} else {
|
||||
Fiber.abort("unknown function type")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static print(ast) {
|
||||
return Printer.pr_str(ast)
|
||||
}
|
||||
|
||||
static rep(str) {
|
||||
return print(eval(read(str), __repl_env))
|
||||
}
|
||||
|
||||
static main() {
|
||||
__repl_env = Env.new()
|
||||
// core.wren: defined in wren
|
||||
for (e in Core.ns) { __repl_env.set(e.key, e.value) }
|
||||
// core.mal: defined using the language itself
|
||||
rep("(def! not (fn* (a) (if a false true)))")
|
||||
while (true) {
|
||||
var line = Readline.readLine("user> ")
|
||||
if (line == null) break
|
||||
if (line != "") {
|
||||
var fiber = Fiber.new { System.print(rep(line)) }
|
||||
fiber.try()
|
||||
if (fiber.error) System.print("Error: %(fiber.error)")
|
||||
}
|
||||
}
|
||||
System.print()
|
||||
}
|
||||
}
|
||||
|
||||
Mal.main()
|
122
wren/step6_file.wren
Normal file
122
wren/step6_file.wren
Normal file
@ -0,0 +1,122 @@
|
||||
import "os" for Process
|
||||
import "./env" for Env
|
||||
import "./readline" for Readline
|
||||
import "./reader" for MalReader
|
||||
import "./printer" for Printer
|
||||
import "./types" for MalSymbol, MalList, MalVector, MalMap, MalNativeFn, MalFn
|
||||
import "./core" for Core
|
||||
|
||||
class Mal {
|
||||
static read(str) {
|
||||
return MalReader.read_str(str)
|
||||
}
|
||||
|
||||
static eval_ast(ast, env) {
|
||||
if (ast is MalSymbol) {
|
||||
return env.get(ast.value)
|
||||
} else if (ast is MalList) {
|
||||
return MalList.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalVector) {
|
||||
return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalMap) {
|
||||
var m = {}
|
||||
for (e in ast.data) {
|
||||
m[e.key] = eval(e.value, env)
|
||||
}
|
||||
return MalMap.new(m)
|
||||
} else {
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
static eval(ast, env) {
|
||||
while (true) {
|
||||
var tco = false
|
||||
if (!(ast is MalList)) return eval_ast(ast, env)
|
||||
if (ast.isEmpty) return ast
|
||||
if (ast[0] is MalSymbol) {
|
||||
if (ast[0].value == "def!") {
|
||||
return env.set(ast[1].value, eval(ast[2], env))
|
||||
} else if (ast[0].value == "let*") {
|
||||
var letEnv = Env.new(env)
|
||||
var i = 0
|
||||
while (i < ast[1].count) {
|
||||
letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))
|
||||
i = i + 2
|
||||
}
|
||||
ast = ast[2]
|
||||
env = letEnv
|
||||
tco = true
|
||||
} else if (ast[0].value == "do") {
|
||||
for (i in 1...(ast.count - 1)) {
|
||||
eval(ast[i], env)
|
||||
}
|
||||
ast = ast[-1]
|
||||
tco = true
|
||||
} else if (ast[0].value == "if") {
|
||||
var condval = eval(ast[1], env)
|
||||
if (condval) {
|
||||
ast = ast[2]
|
||||
} else {
|
||||
if (ast.count <= 3) return null
|
||||
ast = ast[3]
|
||||
}
|
||||
tco = true
|
||||
} else if (ast[0].value == "fn*") {
|
||||
return MalFn.new(ast[2], ast[1].elements, env,
|
||||
Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) })
|
||||
}
|
||||
}
|
||||
if (!tco) {
|
||||
var evaled_ast = eval_ast(ast, env)
|
||||
var f = evaled_ast[0]
|
||||
if (f is MalNativeFn) {
|
||||
return f.call(evaled_ast[1..-1])
|
||||
} else if (f is MalFn) {
|
||||
ast = f.ast
|
||||
env = Env.new(f.env, f.params, evaled_ast[1..-1])
|
||||
tco = true
|
||||
} else {
|
||||
Fiber.abort("unknown function type")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static print(ast) {
|
||||
return Printer.pr_str(ast)
|
||||
}
|
||||
|
||||
static rep(str) {
|
||||
return print(eval(read(str), __repl_env))
|
||||
}
|
||||
|
||||
static main() {
|
||||
__repl_env = Env.new()
|
||||
// core.wren: defined in wren
|
||||
for (e in Core.ns) { __repl_env.set(e.key, e.value) }
|
||||
__repl_env.set("eval", MalNativeFn.new { |a| eval(a[0], __repl_env) })
|
||||
__repl_env.set("*ARGV*", MalList.new(Process.arguments.count > 0 ? Process.arguments[1..-1] : []))
|
||||
// core.mal: defined using the language itself
|
||||
rep("(def! not (fn* (a) (if a false true)))")
|
||||
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))")
|
||||
|
||||
if (Process.arguments.count > 0) {
|
||||
rep("(load-file \"%(Process.arguments[0])\")")
|
||||
return
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var line = Readline.readLine("user> ")
|
||||
if (line == null) break
|
||||
if (line != "") {
|
||||
var fiber = Fiber.new { System.print(rep(line)) }
|
||||
fiber.try()
|
||||
if (fiber.error) System.print("Error: %(fiber.error)")
|
||||
}
|
||||
}
|
||||
System.print()
|
||||
}
|
||||
}
|
||||
|
||||
Mal.main()
|
141
wren/step7_quote.wren
Normal file
141
wren/step7_quote.wren
Normal file
@ -0,0 +1,141 @@
|
||||
import "os" for Process
|
||||
import "./env" for Env
|
||||
import "./readline" for Readline
|
||||
import "./reader" for MalReader
|
||||
import "./printer" for Printer
|
||||
import "./types" for MalSymbol, MalSequential, MalList, MalVector, MalMap, MalNativeFn, MalFn
|
||||
import "./core" for Core
|
||||
|
||||
class Mal {
|
||||
static read(str) {
|
||||
return MalReader.read_str(str)
|
||||
}
|
||||
|
||||
static isPair(x) { x is MalSequential && !x.isEmpty }
|
||||
|
||||
static quasiquote(ast) {
|
||||
if (!isPair(ast)) {
|
||||
return MalList.new([MalSymbol.new("quote"), ast])
|
||||
} else if (ast[0] is MalSymbol && ast[0].value == "unquote") {
|
||||
return ast[1]
|
||||
} else if (isPair(ast[0]) && ast[0][0] is MalSymbol && ast[0][0].value == "splice-unquote") {
|
||||
return MalList.new([MalSymbol.new("concat"), ast[0][1], quasiquote(ast.rest)])
|
||||
} else {
|
||||
return MalList.new([MalSymbol.new("cons"), quasiquote(ast[0]), quasiquote(ast.rest)])
|
||||
}
|
||||
}
|
||||
|
||||
static eval_ast(ast, env) {
|
||||
if (ast is MalSymbol) {
|
||||
return env.get(ast.value)
|
||||
} else if (ast is MalList) {
|
||||
return MalList.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalVector) {
|
||||
return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalMap) {
|
||||
var m = {}
|
||||
for (e in ast.data) {
|
||||
m[e.key] = eval(e.value, env)
|
||||
}
|
||||
return MalMap.new(m)
|
||||
} else {
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
static eval(ast, env) {
|
||||
while (true) {
|
||||
var tco = false
|
||||
if (!(ast is MalList)) return eval_ast(ast, env)
|
||||
if (ast.isEmpty) return ast
|
||||
if (ast[0] is MalSymbol) {
|
||||
if (ast[0].value == "def!") {
|
||||
return env.set(ast[1].value, eval(ast[2], env))
|
||||
} else if (ast[0].value == "let*") {
|
||||
var letEnv = Env.new(env)
|
||||
var i = 0
|
||||
while (i < ast[1].count) {
|
||||
letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))
|
||||
i = i + 2
|
||||
}
|
||||
ast = ast[2]
|
||||
env = letEnv
|
||||
tco = true
|
||||
} else if (ast[0].value == "quote") {
|
||||
return ast[1]
|
||||
} else if (ast[0].value == "quasiquote") {
|
||||
ast = quasiquote(ast[1])
|
||||
tco = true
|
||||
} else if (ast[0].value == "do") {
|
||||
for (i in 1...(ast.count - 1)) {
|
||||
eval(ast[i], env)
|
||||
}
|
||||
ast = ast[-1]
|
||||
tco = true
|
||||
} else if (ast[0].value == "if") {
|
||||
var condval = eval(ast[1], env)
|
||||
if (condval) {
|
||||
ast = ast[2]
|
||||
} else {
|
||||
if (ast.count <= 3) return null
|
||||
ast = ast[3]
|
||||
}
|
||||
tco = true
|
||||
} else if (ast[0].value == "fn*") {
|
||||
return MalFn.new(ast[2], ast[1].elements, env,
|
||||
Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) })
|
||||
}
|
||||
}
|
||||
if (!tco) {
|
||||
var evaled_ast = eval_ast(ast, env)
|
||||
var f = evaled_ast[0]
|
||||
if (f is MalNativeFn) {
|
||||
return f.call(evaled_ast[1..-1])
|
||||
} else if (f is MalFn) {
|
||||
ast = f.ast
|
||||
env = Env.new(f.env, f.params, evaled_ast[1..-1])
|
||||
tco = true
|
||||
} else {
|
||||
Fiber.abort("unknown function type")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static print(ast) {
|
||||
return Printer.pr_str(ast)
|
||||
}
|
||||
|
||||
static rep(str) {
|
||||
return print(eval(read(str), __repl_env))
|
||||
}
|
||||
|
||||
static main() {
|
||||
__repl_env = Env.new()
|
||||
// core.wren: defined in wren
|
||||
for (e in Core.ns) { __repl_env.set(e.key, e.value) }
|
||||
__repl_env.set("eval", MalNativeFn.new { |a| eval(a[0], __repl_env) })
|
||||
__repl_env.set("*ARGV*", MalList.new(Process.arguments.count > 0 ? Process.arguments[1..-1] : []))
|
||||
// core.mal: defined using the language itself
|
||||
rep("(def! not (fn* (a) (if a false true)))")
|
||||
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))")
|
||||
|
||||
if (Process.arguments.count > 0) {
|
||||
rep("(load-file \"%(Process.arguments[0])\")")
|
||||
return
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var line = Readline.readLine("user> ")
|
||||
if (line == null) break
|
||||
if (line != "") {
|
||||
var fiber = Fiber.new { System.print(rep(line)) }
|
||||
fiber.try()
|
||||
if (fiber.error) System.print("Error: %(fiber.error)")
|
||||
}
|
||||
}
|
||||
System.print()
|
||||
}
|
||||
}
|
||||
|
||||
Mal.main()
|
165
wren/step8_macros.wren
Normal file
165
wren/step8_macros.wren
Normal file
@ -0,0 +1,165 @@
|
||||
import "os" for Process
|
||||
import "./env" for Env
|
||||
import "./readline" for Readline
|
||||
import "./reader" for MalReader
|
||||
import "./printer" for Printer
|
||||
import "./types" for MalSymbol, MalSequential, MalList, MalVector, MalMap, MalNativeFn, MalFn
|
||||
import "./core" for Core
|
||||
|
||||
class Mal {
|
||||
static read(str) {
|
||||
return MalReader.read_str(str)
|
||||
}
|
||||
|
||||
static isPair(x) { x is MalSequential && !x.isEmpty }
|
||||
|
||||
static quasiquote(ast) {
|
||||
if (!isPair(ast)) {
|
||||
return MalList.new([MalSymbol.new("quote"), ast])
|
||||
} else if (ast[0] is MalSymbol && ast[0].value == "unquote") {
|
||||
return ast[1]
|
||||
} else if (isPair(ast[0]) && ast[0][0] is MalSymbol && ast[0][0].value == "splice-unquote") {
|
||||
return MalList.new([MalSymbol.new("concat"), ast[0][1], quasiquote(ast.rest)])
|
||||
} else {
|
||||
return MalList.new([MalSymbol.new("cons"), quasiquote(ast[0]), quasiquote(ast.rest)])
|
||||
}
|
||||
}
|
||||
|
||||
static isMacro(ast, env) {
|
||||
return (ast is MalList &&
|
||||
!ast.isEmpty &&
|
||||
ast[0] is MalSymbol &&
|
||||
env.find(ast[0].value) &&
|
||||
env.get(ast[0].value) is MalFn &&
|
||||
env.get(ast[0].value).isMacro)
|
||||
}
|
||||
|
||||
static macroexpand(ast, env) {
|
||||
while (isMacro(ast, env)) {
|
||||
var macro = env.get(ast[0].value)
|
||||
ast = macro.call(ast.elements[1..-1])
|
||||
}
|
||||
return ast
|
||||
}
|
||||
|
||||
static eval_ast(ast, env) {
|
||||
if (ast is MalSymbol) {
|
||||
return env.get(ast.value)
|
||||
} else if (ast is MalList) {
|
||||
return MalList.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalVector) {
|
||||
return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalMap) {
|
||||
var m = {}
|
||||
for (e in ast.data) {
|
||||
m[e.key] = eval(e.value, env)
|
||||
}
|
||||
return MalMap.new(m)
|
||||
} else {
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
static eval(ast, env) {
|
||||
while (true) {
|
||||
var tco = false
|
||||
if (!(ast is MalList)) return eval_ast(ast, env)
|
||||
ast = macroexpand(ast, env)
|
||||
if (!(ast is MalList)) return eval_ast(ast, env)
|
||||
if (ast.isEmpty) return ast
|
||||
if (ast[0] is MalSymbol) {
|
||||
if (ast[0].value == "def!") {
|
||||
return env.set(ast[1].value, eval(ast[2], env))
|
||||
} else if (ast[0].value == "let*") {
|
||||
var letEnv = Env.new(env)
|
||||
var i = 0
|
||||
while (i < ast[1].count) {
|
||||
letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))
|
||||
i = i + 2
|
||||
}
|
||||
ast = ast[2]
|
||||
env = letEnv
|
||||
tco = true
|
||||
} else if (ast[0].value == "quote") {
|
||||
return ast[1]
|
||||
} else if (ast[0].value == "quasiquote") {
|
||||
ast = quasiquote(ast[1])
|
||||
tco = true
|
||||
} else if (ast[0].value == "defmacro!") {
|
||||
return env.set(ast[1].value, eval(ast[2], env).makeMacro())
|
||||
} else if (ast[0].value == "macroexpand") {
|
||||
return macroexpand(ast[1], env)
|
||||
} else if (ast[0].value == "do") {
|
||||
for (i in 1...(ast.count - 1)) {
|
||||
eval(ast[i], env)
|
||||
}
|
||||
ast = ast[-1]
|
||||
tco = true
|
||||
} else if (ast[0].value == "if") {
|
||||
var condval = eval(ast[1], env)
|
||||
if (condval) {
|
||||
ast = ast[2]
|
||||
} else {
|
||||
if (ast.count <= 3) return null
|
||||
ast = ast[3]
|
||||
}
|
||||
tco = true
|
||||
} else if (ast[0].value == "fn*") {
|
||||
return MalFn.new(ast[2], ast[1].elements, env,
|
||||
Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) })
|
||||
}
|
||||
}
|
||||
if (!tco) {
|
||||
var evaled_ast = eval_ast(ast, env)
|
||||
var f = evaled_ast[0]
|
||||
if (f is MalNativeFn) {
|
||||
return f.call(evaled_ast[1..-1])
|
||||
} else if (f is MalFn) {
|
||||
ast = f.ast
|
||||
env = Env.new(f.env, f.params, evaled_ast[1..-1])
|
||||
tco = true
|
||||
} else {
|
||||
Fiber.abort("unknown function type")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static print(ast) {
|
||||
return Printer.pr_str(ast)
|
||||
}
|
||||
|
||||
static rep(str) {
|
||||
return print(eval(read(str), __repl_env))
|
||||
}
|
||||
|
||||
static main() {
|
||||
__repl_env = Env.new()
|
||||
// core.wren: defined in wren
|
||||
for (e in Core.ns) { __repl_env.set(e.key, e.value) }
|
||||
__repl_env.set("eval", MalNativeFn.new { |a| eval(a[0], __repl_env) })
|
||||
__repl_env.set("*ARGV*", MalList.new(Process.arguments.count > 0 ? Process.arguments[1..-1] : []))
|
||||
// core.mal: defined using the language itself
|
||||
rep("(def! not (fn* (a) (if a false true)))")
|
||||
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))")
|
||||
rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
|
||||
|
||||
if (Process.arguments.count > 0) {
|
||||
rep("(load-file \"%(Process.arguments[0])\")")
|
||||
return
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var line = Readline.readLine("user> ")
|
||||
if (line == null) break
|
||||
if (line != "") {
|
||||
var fiber = Fiber.new { System.print(rep(line)) }
|
||||
fiber.try()
|
||||
if (fiber.error) System.print("Error: %(fiber.error)")
|
||||
}
|
||||
}
|
||||
System.print()
|
||||
}
|
||||
}
|
||||
|
||||
Mal.main()
|
186
wren/step9_try.wren
Normal file
186
wren/step9_try.wren
Normal file
@ -0,0 +1,186 @@
|
||||
import "os" for Process
|
||||
import "./env" for Env
|
||||
import "./readline" for Readline
|
||||
import "./reader" for MalReader
|
||||
import "./printer" for Printer
|
||||
import "./types" for MalSymbol, MalSequential, MalList, MalVector, MalMap, MalNativeFn, MalFn, MalException
|
||||
import "./core" for Core
|
||||
|
||||
class Mal {
|
||||
static read(str) {
|
||||
return MalReader.read_str(str)
|
||||
}
|
||||
|
||||
static isPair(x) { x is MalSequential && !x.isEmpty }
|
||||
|
||||
static quasiquote(ast) {
|
||||
if (!isPair(ast)) {
|
||||
return MalList.new([MalSymbol.new("quote"), ast])
|
||||
} else if (ast[0] is MalSymbol && ast[0].value == "unquote") {
|
||||
return ast[1]
|
||||
} else if (isPair(ast[0]) && ast[0][0] is MalSymbol && ast[0][0].value == "splice-unquote") {
|
||||
return MalList.new([MalSymbol.new("concat"), ast[0][1], quasiquote(ast.rest)])
|
||||
} else {
|
||||
return MalList.new([MalSymbol.new("cons"), quasiquote(ast[0]), quasiquote(ast.rest)])
|
||||
}
|
||||
}
|
||||
|
||||
static isMacro(ast, env) {
|
||||
return (ast is MalList &&
|
||||
!ast.isEmpty &&
|
||||
ast[0] is MalSymbol &&
|
||||
env.find(ast[0].value) &&
|
||||
env.get(ast[0].value) is MalFn &&
|
||||
env.get(ast[0].value).isMacro)
|
||||
}
|
||||
|
||||
static macroexpand(ast, env) {
|
||||
while (isMacro(ast, env)) {
|
||||
var macro = env.get(ast[0].value)
|
||||
ast = macro.call(ast.elements[1..-1])
|
||||
}
|
||||
return ast
|
||||
}
|
||||
|
||||
static eval_ast(ast, env) {
|
||||
if (ast is MalSymbol) {
|
||||
return env.get(ast.value)
|
||||
} else if (ast is MalList) {
|
||||
return MalList.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalVector) {
|
||||
return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalMap) {
|
||||
var m = {}
|
||||
for (e in ast.data) {
|
||||
m[e.key] = eval(e.value, env)
|
||||
}
|
||||
return MalMap.new(m)
|
||||
} else {
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
static eval(ast, env) {
|
||||
while (true) {
|
||||
var tco = false
|
||||
if (!(ast is MalList)) return eval_ast(ast, env)
|
||||
ast = macroexpand(ast, env)
|
||||
if (!(ast is MalList)) return eval_ast(ast, env)
|
||||
if (ast.isEmpty) return ast
|
||||
if (ast[0] is MalSymbol) {
|
||||
if (ast[0].value == "def!") {
|
||||
return env.set(ast[1].value, eval(ast[2], env))
|
||||
} else if (ast[0].value == "let*") {
|
||||
var letEnv = Env.new(env)
|
||||
var i = 0
|
||||
while (i < ast[1].count) {
|
||||
letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))
|
||||
i = i + 2
|
||||
}
|
||||
ast = ast[2]
|
||||
env = letEnv
|
||||
tco = true
|
||||
} else if (ast[0].value == "quote") {
|
||||
return ast[1]
|
||||
} else if (ast[0].value == "quasiquote") {
|
||||
ast = quasiquote(ast[1])
|
||||
tco = true
|
||||
} else if (ast[0].value == "defmacro!") {
|
||||
return env.set(ast[1].value, eval(ast[2], env).makeMacro())
|
||||
} else if (ast[0].value == "macroexpand") {
|
||||
return macroexpand(ast[1], env)
|
||||
} else if (ast[0].value == "try*") {
|
||||
if (ast.count > 2 && ast[2][0] is MalSymbol && ast[2][0].value == "catch*") {
|
||||
var fiber = Fiber.new { eval(ast[1], env) }
|
||||
var result = fiber.try()
|
||||
var error = fiber.error
|
||||
if (!error) return result
|
||||
if (error == "___MalException___") {
|
||||
error = MalException.value
|
||||
MalException.set(null)
|
||||
}
|
||||
return eval(ast[2][2], Env.new(env, [ast[2][1]], [error]))
|
||||
} else {
|
||||
return eval(ast[1], env)
|
||||
}
|
||||
} else if (ast[0].value == "do") {
|
||||
for (i in 1...(ast.count - 1)) {
|
||||
eval(ast[i], env)
|
||||
}
|
||||
ast = ast[-1]
|
||||
tco = true
|
||||
} else if (ast[0].value == "if") {
|
||||
var condval = eval(ast[1], env)
|
||||
if (condval) {
|
||||
ast = ast[2]
|
||||
} else {
|
||||
if (ast.count <= 3) return null
|
||||
ast = ast[3]
|
||||
}
|
||||
tco = true
|
||||
} else if (ast[0].value == "fn*") {
|
||||
return MalFn.new(ast[2], ast[1].elements, env,
|
||||
Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) })
|
||||
}
|
||||
}
|
||||
if (!tco) {
|
||||
var evaled_ast = eval_ast(ast, env)
|
||||
var f = evaled_ast[0]
|
||||
if (f is MalNativeFn) {
|
||||
return f.call(evaled_ast[1..-1])
|
||||
} else if (f is MalFn) {
|
||||
ast = f.ast
|
||||
env = Env.new(f.env, f.params, evaled_ast[1..-1])
|
||||
tco = true
|
||||
} else {
|
||||
Fiber.abort("unknown function type")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static print(ast) {
|
||||
return Printer.pr_str(ast)
|
||||
}
|
||||
|
||||
static rep(str) {
|
||||
return print(eval(read(str), __repl_env))
|
||||
}
|
||||
|
||||
static main() {
|
||||
__repl_env = Env.new()
|
||||
// core.wren: defined in wren
|
||||
for (e in Core.ns) { __repl_env.set(e.key, e.value) }
|
||||
__repl_env.set("eval", MalNativeFn.new { |a| eval(a[0], __repl_env) })
|
||||
__repl_env.set("*ARGV*", MalList.new(Process.arguments.count > 0 ? Process.arguments[1..-1] : []))
|
||||
// core.mal: defined using the language itself
|
||||
rep("(def! not (fn* (a) (if a false true)))")
|
||||
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))")
|
||||
rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
|
||||
|
||||
if (Process.arguments.count > 0) {
|
||||
rep("(load-file \"%(Process.arguments[0])\")")
|
||||
return
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var line = Readline.readLine("user> ")
|
||||
if (line == null) break
|
||||
if (line != "") {
|
||||
var fiber = Fiber.new { System.print(rep(line)) }
|
||||
fiber.try()
|
||||
var error = fiber.error
|
||||
if (error) {
|
||||
if (error == "___MalException___") {
|
||||
error = Printer.pr_str(MalException.value, false)
|
||||
MalException.set(null)
|
||||
}
|
||||
System.print("Error: %(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
System.print()
|
||||
}
|
||||
}
|
||||
|
||||
Mal.main()
|
188
wren/stepA_mal.wren
Normal file
188
wren/stepA_mal.wren
Normal file
@ -0,0 +1,188 @@
|
||||
import "os" for Process
|
||||
import "./env" for Env
|
||||
import "./readline" for Readline
|
||||
import "./reader" for MalReader
|
||||
import "./printer" for Printer
|
||||
import "./types" for MalSymbol, MalSequential, MalList, MalVector, MalMap, MalNativeFn, MalFn, MalException
|
||||
import "./core" for Core
|
||||
|
||||
class Mal {
|
||||
static read(str) {
|
||||
return MalReader.read_str(str)
|
||||
}
|
||||
|
||||
static isPair(x) { x is MalSequential && !x.isEmpty }
|
||||
|
||||
static quasiquote(ast) {
|
||||
if (!isPair(ast)) {
|
||||
return MalList.new([MalSymbol.new("quote"), ast])
|
||||
} else if (ast[0] is MalSymbol && ast[0].value == "unquote") {
|
||||
return ast[1]
|
||||
} else if (isPair(ast[0]) && ast[0][0] is MalSymbol && ast[0][0].value == "splice-unquote") {
|
||||
return MalList.new([MalSymbol.new("concat"), ast[0][1], quasiquote(ast.rest)])
|
||||
} else {
|
||||
return MalList.new([MalSymbol.new("cons"), quasiquote(ast[0]), quasiquote(ast.rest)])
|
||||
}
|
||||
}
|
||||
|
||||
static isMacro(ast, env) {
|
||||
return (ast is MalList &&
|
||||
!ast.isEmpty &&
|
||||
ast[0] is MalSymbol &&
|
||||
env.find(ast[0].value) &&
|
||||
env.get(ast[0].value) is MalFn &&
|
||||
env.get(ast[0].value).isMacro)
|
||||
}
|
||||
|
||||
static macroexpand(ast, env) {
|
||||
while (isMacro(ast, env)) {
|
||||
var macro = env.get(ast[0].value)
|
||||
ast = macro.call(ast.elements[1..-1])
|
||||
}
|
||||
return ast
|
||||
}
|
||||
|
||||
static eval_ast(ast, env) {
|
||||
if (ast is MalSymbol) {
|
||||
return env.get(ast.value)
|
||||
} else if (ast is MalList) {
|
||||
return MalList.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalVector) {
|
||||
return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)
|
||||
} else if (ast is MalMap) {
|
||||
var m = {}
|
||||
for (e in ast.data) {
|
||||
m[e.key] = eval(e.value, env)
|
||||
}
|
||||
return MalMap.new(m)
|
||||
} else {
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
static eval(ast, env) {
|
||||
while (true) {
|
||||
var tco = false
|
||||
if (!(ast is MalList)) return eval_ast(ast, env)
|
||||
ast = macroexpand(ast, env)
|
||||
if (!(ast is MalList)) return eval_ast(ast, env)
|
||||
if (ast.isEmpty) return ast
|
||||
if (ast[0] is MalSymbol) {
|
||||
if (ast[0].value == "def!") {
|
||||
return env.set(ast[1].value, eval(ast[2], env))
|
||||
} else if (ast[0].value == "let*") {
|
||||
var letEnv = Env.new(env)
|
||||
var i = 0
|
||||
while (i < ast[1].count) {
|
||||
letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))
|
||||
i = i + 2
|
||||
}
|
||||
ast = ast[2]
|
||||
env = letEnv
|
||||
tco = true
|
||||
} else if (ast[0].value == "quote") {
|
||||
return ast[1]
|
||||
} else if (ast[0].value == "quasiquote") {
|
||||
ast = quasiquote(ast[1])
|
||||
tco = true
|
||||
} else if (ast[0].value == "defmacro!") {
|
||||
return env.set(ast[1].value, eval(ast[2], env).makeMacro())
|
||||
} else if (ast[0].value == "macroexpand") {
|
||||
return macroexpand(ast[1], env)
|
||||
} else if (ast[0].value == "try*") {
|
||||
if (ast.count > 2 && ast[2][0] is MalSymbol && ast[2][0].value == "catch*") {
|
||||
var fiber = Fiber.new { eval(ast[1], env) }
|
||||
var result = fiber.try()
|
||||
var error = fiber.error
|
||||
if (!error) return result
|
||||
if (error == "___MalException___") {
|
||||
error = MalException.value
|
||||
MalException.set(null)
|
||||
}
|
||||
return eval(ast[2][2], Env.new(env, [ast[2][1]], [error]))
|
||||
} else {
|
||||
return eval(ast[1], env)
|
||||
}
|
||||
} else if (ast[0].value == "do") {
|
||||
for (i in 1...(ast.count - 1)) {
|
||||
eval(ast[i], env)
|
||||
}
|
||||
ast = ast[-1]
|
||||
tco = true
|
||||
} else if (ast[0].value == "if") {
|
||||
var condval = eval(ast[1], env)
|
||||
if (condval) {
|
||||
ast = ast[2]
|
||||
} else {
|
||||
if (ast.count <= 3) return null
|
||||
ast = ast[3]
|
||||
}
|
||||
tco = true
|
||||
} else if (ast[0].value == "fn*") {
|
||||
return MalFn.new(ast[2], ast[1].elements, env,
|
||||
Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) })
|
||||
}
|
||||
}
|
||||
if (!tco) {
|
||||
var evaled_ast = eval_ast(ast, env)
|
||||
var f = evaled_ast[0]
|
||||
if (f is MalNativeFn) {
|
||||
return f.call(evaled_ast[1..-1])
|
||||
} else if (f is MalFn) {
|
||||
ast = f.ast
|
||||
env = Env.new(f.env, f.params, evaled_ast[1..-1])
|
||||
tco = true
|
||||
} else {
|
||||
Fiber.abort("unknown function type")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static print(ast) {
|
||||
return Printer.pr_str(ast)
|
||||
}
|
||||
|
||||
static rep(str) {
|
||||
return print(eval(read(str), __repl_env))
|
||||
}
|
||||
|
||||
static main() {
|
||||
__repl_env = Env.new()
|
||||
// core.wren: defined in wren
|
||||
for (e in Core.ns) { __repl_env.set(e.key, e.value) }
|
||||
__repl_env.set("eval", MalNativeFn.new { |a| eval(a[0], __repl_env) })
|
||||
__repl_env.set("*ARGV*", MalList.new(Process.arguments.count > 0 ? Process.arguments[1..-1] : []))
|
||||
// core.mal: defined using the language itself
|
||||
rep("(def! *host-language* \"wren\")")
|
||||
rep("(def! not (fn* (a) (if a false true)))")
|
||||
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))")
|
||||
rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
|
||||
|
||||
if (Process.arguments.count > 0) {
|
||||
rep("(load-file \"%(Process.arguments[0])\")")
|
||||
return
|
||||
}
|
||||
|
||||
rep("(println (str \"Mal [\" *host-language* \"]\"))")
|
||||
while (true) {
|
||||
var line = Readline.readLine("user> ")
|
||||
if (line == null) break
|
||||
if (line != "") {
|
||||
var fiber = Fiber.new { System.print(rep(line)) }
|
||||
fiber.try()
|
||||
var error = fiber.error
|
||||
if (error) {
|
||||
if (error == "___MalException___") {
|
||||
error = Printer.pr_str(MalException.value, false)
|
||||
MalException.set(null)
|
||||
}
|
||||
System.print("Error: %(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
System.print()
|
||||
}
|
||||
}
|
||||
|
||||
Mal.main()
|
2
wren/tests/step5_tco.mal
Normal file
2
wren/tests/step5_tco.mal
Normal file
@ -0,0 +1,2 @@
|
||||
;; Wren: skipping non-TCO recursion
|
||||
;; Reason: completes up to 1,000,000 (with extended timeout)
|
34
wren/tests/stepA_mal.mal
Normal file
34
wren/tests/stepA_mal.mal
Normal file
@ -0,0 +1,34 @@
|
||||
;; Testing basic Wren interop
|
||||
|
||||
;;; wren-eval evaluates the given string as an expression.
|
||||
|
||||
(wren-eval "7")
|
||||
;=>7
|
||||
|
||||
(wren-eval "0x41")
|
||||
;=>65
|
||||
|
||||
(wren-eval "\"7\"")
|
||||
;=>"7"
|
||||
|
||||
(wren-eval "[ 7,8,9 ]")
|
||||
;=>(7 8 9)
|
||||
|
||||
(wren-eval "{ \"abc\": 789 }")
|
||||
;=>{"abc" 789}
|
||||
|
||||
(wren-eval "System.print(\"hello\")")
|
||||
;/hello
|
||||
;=>"hello"
|
||||
|
||||
(wren-eval "[\"a\", \"b\", \"c\"].map { |x| \"X%(x)Y\" }.join(\" \")")
|
||||
;=>"XaY XbY XcY"
|
||||
|
||||
(wren-eval "[1,2,3].map { |x| 1 + x }")
|
||||
;=>(2 3 4)
|
||||
|
||||
(wren-eval "[null, (1 == 1), (1 == 2)]")
|
||||
;=>(nil true false)
|
||||
|
||||
(wren-eval "Fiber.abort(\"AAA\" + \"BBB\")")
|
||||
;/Error: AAABBB
|
130
wren/types.wren
Normal file
130
wren/types.wren
Normal file
@ -0,0 +1,130 @@
|
||||
class MalVal {
|
||||
static newKeyword(value) { "\u029e%(value)" }
|
||||
static isKeyword(obj) { obj is String && obj.count > 0 && obj[0] == "\u029e" }
|
||||
meta { _meta }
|
||||
meta=(value) { _meta = value }
|
||||
}
|
||||
|
||||
class MalSymbol is MalVal {
|
||||
construct new(value) { _value = value }
|
||||
value { _value }
|
||||
toString { _value }
|
||||
==(other) { other is MalSymbol && other.value == _value }
|
||||
!=(other) { !(this == other) }
|
||||
}
|
||||
|
||||
class MalSequential is MalVal {
|
||||
construct new(elements) { _elements = elements }
|
||||
elements { _elements }
|
||||
[index] { _elements[index] }
|
||||
isEmpty { _elements.count == 0 }
|
||||
count { _elements.count }
|
||||
first { isEmpty ? null : _elements[0] }
|
||||
rest { MalList.new(isEmpty ? [] : elements[1..-1]) }
|
||||
==(other) {
|
||||
if (!(other is MalSequential)) return false
|
||||
if (other.count != count) return false
|
||||
for (i in 0...count) {
|
||||
if (other[i] != this[i]) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
!=(other) { !(this == other) }
|
||||
}
|
||||
|
||||
class MalList is MalSequential {
|
||||
construct new(elements) { super(elements) }
|
||||
clone() { MalList.new(elements) }
|
||||
}
|
||||
|
||||
class MalVector is MalSequential {
|
||||
construct new(elements) { super(elements) }
|
||||
clone() { MalVector.new(elements) }
|
||||
}
|
||||
|
||||
class MalMap is MalVal {
|
||||
construct new(data) { _data = data }
|
||||
construct fromList(elements) {
|
||||
_data = {}
|
||||
var i = 0
|
||||
while (i < elements.count) {
|
||||
_data[elements[i]] = elements[i + 1]
|
||||
i = i + 2
|
||||
}
|
||||
}
|
||||
clone() { MalMap.new(_data) }
|
||||
data { _data }
|
||||
assoc(pairsList) {
|
||||
var newData = {}
|
||||
for (e in _data) {
|
||||
newData[e.key] = e.value
|
||||
}
|
||||
var i = 0
|
||||
while (i < pairsList.count) {
|
||||
newData[pairsList[i]] = pairsList[i + 1]
|
||||
i = i + 2
|
||||
}
|
||||
return MalMap.new(newData)
|
||||
}
|
||||
dissoc(keysList) {
|
||||
var newData = {}
|
||||
for (e in _data) {
|
||||
newData[e.key] = e.value
|
||||
}
|
||||
for (k in keysList) {
|
||||
newData.remove(k)
|
||||
}
|
||||
return MalMap.new(newData)
|
||||
}
|
||||
==(other) {
|
||||
if (!(other is MalMap)) return false
|
||||
if (other.data.count != data.count) return false
|
||||
for (e in _data) {
|
||||
if (other.data[e.key] != e.value) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
!=(other) { !(this == other) }
|
||||
}
|
||||
|
||||
class MalNativeFn is MalVal {
|
||||
construct new(fn) { _fn = fn }
|
||||
call(args) { _fn.call(args) }
|
||||
clone() { MalNativeFn.new(_fn) }
|
||||
}
|
||||
|
||||
class MalFn is MalVal {
|
||||
construct new(ast, params, env, fn) {
|
||||
_ast = ast
|
||||
_params = params
|
||||
_env = env
|
||||
_fn = fn
|
||||
_isMacro = false
|
||||
}
|
||||
construct new(ast, params, env, fn, isMacro) {
|
||||
_ast = ast
|
||||
_params = params
|
||||
_env = env
|
||||
_fn = fn
|
||||
_isMacro = isMacro
|
||||
}
|
||||
ast { _ast }
|
||||
params { _params }
|
||||
env { _env }
|
||||
isMacro { _isMacro }
|
||||
clone() { MalFn.new(_ast, _params, _env, _fn, _isMacro) }
|
||||
makeMacro() { MalFn.new(_ast, _params, _env, _fn, true) }
|
||||
call(args) { _fn.call(args) }
|
||||
}
|
||||
|
||||
class MalAtom is MalVal {
|
||||
construct new(value) { _value = value }
|
||||
value { _value }
|
||||
value=(other) { _value = other }
|
||||
clone() { MalAtom.new(value) }
|
||||
}
|
||||
|
||||
class MalException {
|
||||
static value { __exception }
|
||||
static set(exception) { __exception = exception }
|
||||
}
|
34
wren/wren-add-gettimeofday.patch
Normal file
34
wren/wren-add-gettimeofday.patch
Normal file
@ -0,0 +1,34 @@
|
||||
diff --git a/src/vm/wren_core.c b/src/vm/wren_core.c
|
||||
index 34a13c8b..3c4e6ab8 100644
|
||||
--- a/src/vm/wren_core.c
|
||||
+++ b/src/vm/wren_core.c
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
+#include <sys/time.h>
|
||||
|
||||
#include "wren_common.h"
|
||||
#include "wren_core.h"
|
||||
@@ -1121,6 +1122,13 @@ DEF_PRIMITIVE(string_toString)
|
||||
RETURN_VAL(args[0]);
|
||||
}
|
||||
|
||||
+DEF_PRIMITIVE(system_gettimeofday)
|
||||
+{
|
||||
+ struct timeval tv;
|
||||
+ gettimeofday(&tv, NULL);
|
||||
+ RETURN_NUM((double)tv.tv_sec + (double)tv.tv_usec/1000000.0);
|
||||
+}
|
||||
+
|
||||
DEF_PRIMITIVE(system_clock)
|
||||
{
|
||||
RETURN_NUM((double)clock() / CLOCKS_PER_SEC);
|
||||
@@ -1374,6 +1382,7 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
PRIMITIVE(vm->rangeClass, "toString", range_toString);
|
||||
|
||||
ObjClass* systemClass = AS_CLASS(wrenFindVariable(vm, coreModule, "System"));
|
||||
+ PRIMITIVE(systemClass->obj.classObj, "gettimeofday", system_gettimeofday);
|
||||
PRIMITIVE(systemClass->obj.classObj, "clock", system_clock);
|
||||
PRIMITIVE(systemClass->obj.classObj, "gc()", system_gc);
|
||||
PRIMITIVE(systemClass->obj.classObj, "writeString_(_)", system_writeString);
|
Loading…
Reference in New Issue
Block a user