Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BQN implementation #619

Open
wants to merge 47 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
01a6fec
BQN: initial commit
dancek Mar 6, 2022
2f39027
bqn: tokenizer
dancek Mar 7, 2022
b382d44
bqn: step1 with bugs
dancek Mar 7, 2022
fe84259
bqn: add some error handling
dancek Mar 7, 2022
3ee9844
bqn: step 2
dancek Mar 8, 2022
112a104
bqn: implement vectors
dancek Mar 12, 2022
72ef2aa
bqn: keywords and kinda hashmaps
dancek Mar 12, 2022
dd5568d
bqn: step3
dancek Mar 15, 2022
d2f91af
bqn: step4 core functions
dancek Mar 17, 2022
cae356e
bqn: step4 if & fn*
dancek Mar 17, 2022
beeed75
bqn: step4 do + prn
dancek Mar 17, 2022
c0995b2
bqn: variadic functions
dancek Mar 17, 2022
bb0e529
bqn: add slurp and read-string
dancek Apr 16, 2022
d6d5143
bqn: step5: let*, do and if TCO
dancek Apr 16, 2022
351d446
bqn: step5: TCO
dancek Apr 16, 2022
6597c57
bqn: implement string escapes and str functions
dancek Apr 16, 2022
87f5bc4
bqn: step6: load-file
dancek Apr 16, 2022
55f2bf1
bqn: step6: atoms
dancek Apr 17, 2022
d7485c5
bqn: support comments
dancek Apr 17, 2022
458b25c
bqn: add @ reader macro
dancek Apr 17, 2022
2bbcdc2
bqn: step7: quote & quasiquote
dancek Apr 22, 2022
85b8ffe
bqn: step7: reader macros
dancek Apr 22, 2022
73f8980
bqn: step8: trivial macros
dancek Jun 11, 2022
7713081
bqn: step8: full macro support
dancek Jun 19, 2022
f1e7d0b
bqn: step8: add 'not to pass tests
dancek Jun 19, 2022
7b2f69e
bqn: step8: add nth, first, rest, cond
dancek Jun 19, 2022
d74e9c8
bqn: step9: throw by returning type ¯1
dancek Jun 19, 2022
f3382b5
bqn: step9: apply, map etc.
dancek Jun 19, 2022
29f12f9
bqn: step9: support deep throws
dancek Jun 20, 2022
2aaedf1
bqn: implement basic hashmap functions
dancek Jul 14, 2022
c5dbeea
bqn: support multi-argument dissoc and assoc
dancek Jul 14, 2022
45bfbe1
bqn: fix (keyword "string")
dancek Jul 18, 2022
7f4065b
bqn: stepA: implement readline
dancek Jul 18, 2022
db7eb90
bqn: stepA: add required functions, some NYI
dancek Jul 18, 2022
6d63f01
bqn: stepA: support loading file as CLI arg
dancek Jul 27, 2022
a141f02
bqn: rewrite tokenizer with •_while_
dancek Jul 27, 2022
6d57d2e
bqn: fix some early-step regressions
dancek Jul 28, 2022
7346926
bqn: full *ARGV* support in stepA and step6
dancek Jul 28, 2022
fb6a77a
bqn: implement list/vector equality
dancek Jul 28, 2022
b0497fb
bqn: fix reading symbols starting with -
dancek Jul 28, 2022
e0e5a5e
bqn: fix reading 1-length strings
dancek Jul 28, 2022
884208e
bqn: implement vec
dancek Jul 28, 2022
158cc72
bqn: fix REPL print
dancek Jul 28, 2022
43c9358
bqn: add Dockerfile
dancek Jul 30, 2022
4daedef
bqn: support ci.sh
dancek Jul 30, 2022
61dfddf
bqn: update README
dancek Jul 30, 2022
2cb5127
bqn: rewrite ReadString (thanks Marshall & suhr!)
dancek Jul 30, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Makefile.impls
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ wasm_MODE = wasmtime
# Implementation specific settings
#

IMPLS = ada ada.2 awk bash basic bbc-basic c c.2 chuck clojure coffee common-lisp cpp crystal cs d dart \
IMPLS = ada ada.2 awk bash basic bbc-basic bqn c c.2 chuck clojure coffee common-lisp cpp crystal cs d dart \
elisp elixir elm erlang es6 factor fantom fennel forth fsharp go groovy gnu-smalltalk \
guile haskell haxe hy io janet java java-truffle js jq julia kotlin livescript logo lua make mal \
matlab miniMAL nasm nim objc objpascal ocaml perl perl6 php picolisp pike plpgsql \
Expand Down Expand Up @@ -110,6 +110,7 @@ awk_STEP_TO_PROG = impls/awk/$($(1)).awk
bash_STEP_TO_PROG = impls/bash/$($(1)).sh
basic_STEP_TO_PROG = $(basic_STEP_TO_PROG_$(basic_MODE))
bbc-basic_STEP_TO_PROG = impls/bbc-basic/$($(1)).bas
bqn_STEP_TO_PROG = impls/bqn/$($(1)).bqn
c_STEP_TO_PROG = impls/c/$($(1))
c.2_STEP_TO_PROG = impls/c.2/$($(1))
chuck_STEP_TO_PROG = impls/chuck/$($(1)).ck
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ FAQ](docs/FAQ.md) where I attempt to answer some common questions.
| [Bash 4](#bash-4) | [Joel Martin](https://github.com/kanaka) |
| [BASIC](#basic-c64-and-qbasic) (C64 & QBasic) | [Joel Martin](https://github.com/kanaka) |
| [BBC BASIC V](#bbc-basic-v) | [Ben Harris](https://github.com/bjh21) |
| [BQN](#bqn) | [Hannu Hartikainen](https://github.com/dancek) |
| [C](#c) | [Joel Martin](https://github.com/kanaka) |
| [C #2](#c2) | [Duncan Watts](https://github.com/fungiblecog) |
| [C++](#c-1) | [Stephen Thirlwall](https://github.com/sdt) |
Expand Down Expand Up @@ -289,6 +290,17 @@ Or in ARM BBC BASIC V under RISC OS 3 or later:
*Run stepX_YYY
```

### BQN

The BQN implementation requires [CBQN](https://github.com/dzaima/CBQN) as the non-standard system values `•_while_`, `•GetLine` and `•term.OutRaw` are used. Removing them would be easy, though -- they're just for convenience and REPL usability.

There are no releases of CBQN at the time of writing, but any version from mid 2022 should work. Specifically, commit `88f6585` is known to work.

```
cd impls/bqn
./run
```

### C

The C implementation of mal requires the following libraries (lib and
Expand Down
12 changes: 12 additions & 0 deletions impls/bqn/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM ubuntu:22.04

RUN apt-get update && \
apt-get install -qqy git clang build-essential python3 python-is-python3
RUN git clone https://github.com/dzaima/CBQN.git && \
cd CBQN && \
git reset --hard 88f65850fa6ac28bc50886c5942652f21d5be924 && \
make && \
make install && \
ln -s bqn /usr/local/bin/BQN
RUN mkdir /mal
WORKDIR /mal
3 changes: 3 additions & 0 deletions impls/bqn/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
all:

clean:
118 changes: 118 additions & 0 deletions impls/bqn/core.bqn
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
⟨Atom,nil,Err,_chk,_chks⟩ ← •Import "globals.bqn"
⟨Env⟩ ← •Import "env.bqn"
⟨PrStr,PrStrR⟩ ← •Import "printer.bqn"
⟨ReadStr⟩ ← •Import "reader.bqn"

Q ← {∾´'"'∾𝕩∾'"'}
J ← {0=≠𝕩 ? "" ;
1=≠𝕩 ? ⊑𝕩 ;
∾´(⊑𝕩)∾𝕨∾𝕨J 1↓𝕩}
E ← {𝕊 ⟨t‿a,s‿b⟩:
{(⊑t∊3‿4) ∧ ⊑s∊3‿4 ?
{(≠a)=≠b ? ∧´a E¨b ; 0};
(t≡s)∧a≡b} ;
{2<≠𝕩 ? ∧´E´˘2↕𝕩 ; 1}𝕩}

ns ← ⟨
"+"‿{1∾+´1⊏˘>𝕩}
"-"‿{1∾-´1⊏˘>𝕩}
"*"‿{1∾×´1⊏˘>𝕩}
"/"‿{1∾⌊∘÷´1⊏˘>𝕩}

"="‿{7∾E𝕩}
"<"‿{7∾∧´<´˘2↕1⊸⊑¨𝕩}
"<="‿{7∾∧´≤´˘2↕1⊸⊑¨𝕩}
">"‿{7∾∧´>´˘2↕1⊸⊑¨𝕩}
">="‿{7∾∧´≥´˘2↕1⊸⊑¨𝕩}

"empty?"‿{7∾0=≠1⊑⊑𝕩}
"count"‿{1∾≠1⊑⊑𝕩}

"bqn-show"‿{•Show ⊑ 𝕩}
"pr-str"‿{2∾<" "J PrStrR¨𝕩}
"str"‿{2∾<""J PrStr¨𝕩}
"prn"‿{•Out " "J PrStrR¨𝕩 ⋄ nil}
"println"‿{•Out " "J PrStr¨𝕩 ⋄ nil}

"read-string"‿{ReadStr 1⊑⊑𝕩}
"slurp"‿{2∾<•FChars 1⊑⊑𝕩}

"atom"‿{11∾Atom ⊑𝕩}
"atom?"‿{7∾11=⊑⊑𝕩}
"deref"‿{𝕊 ⟨11‿a⟩: a.Deref@; Err "deref: not an atom"}
"reset!"‿{
𝕊 ⟨11‿a,v⟩: a.Reset v;
Err "reset!: not an atom"}
"swap!"‿{
⟨t‿a,fn⟩ ← 2↑𝕩
args ← (<a.Deref@) ∾ 2↓𝕩
f ← {9‿f: f; 10‿x‿y‿z‿f: f}fn
a.Reset F args}

"cons"‿{𝕊 x‿⟨t,xs⟩: 3∾<(<x)∾xs ; Err "cons"}
"concat"‿{𝕊 ⟨⟩:3‿⟨⟩; 3∾<∾´1⊸⊑¨𝕩}

"nth"‿{𝕊 ⟨t‿xs,1‿n⟩: {n<≠xs ? n⊑xs ; Err "out of bounds"}}
"first"‿{S ⟨t‿xs⟩: {0<≠xs ? ⊑xs ; nil}; nil}
"rest"‿{𝕊 ⟨t‿⟨⟩⟩: 3‿⟨⟩; 𝕊 ⟨t‿xs⟩: 3∾<1↓xs; 3‿⟨⟩}

"throw"‿{𝕊 ⟨x⟩: ¯1‿x}

"apply"‿{𝕊
fn ← {9‿f:f; 10‿b‿a‿e‿f: f}⊑𝕩
args ← (¯1↓1↓𝕩)∾1⊑¯1⊑𝕩
Fn _chks args}
"map"‿{𝕊
fn ← {9‿f:f; 10‿b‿a‿e‿f: f}⊑𝕩
{3∾<𝕩}_chks{Fn ⟨𝕩⟩}¨ 1⊑1⊑𝕩}

"nil?"‿{𝕊 ⟨8‿⟨⟩⟩: 7‿1; 7‿0}
"true?"‿{𝕊 ⟨7‿1⟩: 7‿1; 7‿0}
"false?"‿{𝕊 ⟨7‿0⟩: 7‿1; 7‿0}
"symbol?"‿{𝕊 ⟨0‿x⟩: 7‿1; 7‿0}
"keyword?"‿{𝕊 ⟨6‿x⟩: 7‿1; 7‿0}
"vector?"‿{𝕊 ⟨4‿x⟩: 7‿1; 7‿0}
"list?"‿{𝕊 ⟨3‿x⟩: 7‿1; 7‿0}
"map?"‿{𝕊 ⟨5‿x⟩: 7‿1; 7‿0}
"sequential?"‿{𝕊 ⟨3‿x⟩: 7‿1; 𝕊 ⟨4‿x⟩: 7‿1; 7‿0}
"string?"‿{𝕊 ⟨2‿x⟩: 7‿1; 7‿0}
"number?"‿{𝕊 ⟨1‿x⟩: 7‿1; 7‿0}
"fn?"‿{7∾{9:1;10:1;0}⊑⊑𝕩}

"symbol"‿{0∾<1⊑⊑𝕩}
"keyword"‿{𝕊 ⟨6‿kw⟩: 6‿kw;
𝕊 ⟨2‿s⟩: 6∾<':'∾s}
"list"‿{3‿𝕩}
"vector"‿{4‿𝕩}
"hash-map"‿{5‿𝕩}
"vec"‿{𝕊 ⟨4‿x⟩: 4‿x; 𝕊 ⟨3‿x⟩: 4‿x}

"keys"‿{𝕊 ⟨5‿xs⟩: 3∾<⥊⊏˘∘‿2⥊xs}
"vals"‿{𝕊 ⟨5‿xs⟩: 3∾<⥊1⊏˘∘‿2⥊xs}
"get"‿{𝕊 ⟨5‿xs,k⟩: 1⊑@‿nil∾˜⥊(k⊸≡˘⊑˘)⊸/∘‿2⥊xs; nil}
"contains?"‿{𝕊 ⟨5‿xs,k⟩: 7∾×+´k⊸≡˘⊑˘∘‿2⥊xs}
"dissoc"‿{𝕊 ⟨x⟩: x;
⟨t‿xs,k⟩ ← 2↑𝕩
𝕊 (2↓𝕩)∾˜<5∾<⥊(k⊸≢˘⊑˘)⊸/∘‿2⥊xs}
"assoc"‿{𝕊 ⟨x⟩: x;
⟨t‿xs,k,v⟩ ← 3↑𝕩
𝕊 (3↓𝕩)∾˜<5∾<k‿v∾˜⥊(k⊸≢˘⊑˘)⊸/∘‿2⥊xs}

"readline"‿{𝕊 ⟨2‿s⟩:
•term.OutRaw s
in ← •GetLine 0
{in≡@ ? nil ; 2‿in}}

"time-ms"‿{𝕊⋄1∾⌊1000וMonoTime@}

"meta"‿(¯1‿"NYI")
"with-meta"‿(¯1‿"NYI")
"seq"‿(¯1‿"NYI")
"conj"‿(¯1‿"NYI")

core ⇐ Env@
{𝕊 kw‿f: core.Set kw‿(9‿f)}¨ns

core.Set "*host-language*"‿(2‿"BQN")
core.Set "*ARGV*"‿⟨3,⟨⟩⟩
14 changes: 14 additions & 0 deletions impls/bqn/env.bqn
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
⟨Err⟩ ← •Import "globals.bqn"

Env ⇐ {𝕊 outer:
d ⇐ ↕0‿2
Set ⇐ {𝕊 k‿v:
d ↩ k‿v∾((<k)≢˘⊏˘)⊸/d
}
Get ⇐ {𝕊 k: {
i ← ⊑(⊏˘d)⊐<k
i<⊑≢d ? i‿1⊑d ;
outer≠@ ? outer.Get k ;
Err "'"∾k∾"' not found"
}}
}
9 changes: 9 additions & 0 deletions impls/bqn/globals.bqn
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
nil ⇐ 8‿⟨⟩

Atom⇐ {𝕊 v:
Reset ⇐ {v↩𝕩}
Deref ⇐ {𝕊⋄v}
}
Err ⇐ {¯1‿⟨2,𝕩⟩}
_chk ⇐ {¯1=⊑⎊0𝕩 ? 𝕩 ; 𝕨𝔽𝕩}
_chks ⇐ {⊑¯1∊⊑¨𝕩 ? (⊑(⊑¨𝕩)⊐¯1)⊑𝕩 ; 𝕨𝔽𝕩}
31 changes: 31 additions & 0 deletions impls/bqn/printer.bqn
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Lst ← {0=≠𝕩 ? "" ; {𝕨∾" "∾𝕩}´(𝕨⊸P¨𝕩)}
P ← {
r 𝕊 ¯1‿x: "ERROR: "∾r𝕊x;
r 𝕊 12‿x: "#macro";
r 𝕊 11‿a: "(atom "∾(r P a.Deref@)∾")";
r 𝕊 10‿x‿y‿z‿f: "#function";
r 𝕊 9‿x: "#function";
r 𝕊 8‿x: "nil";
r 𝕊 7‿0: "false";
r 𝕊 7‿1: "true";
r 𝕊 6‿kw: kw;
r 𝕊 5‿l: "{"∾(r Lst l)∾"}";
r 𝕊 4‿l: "["∾(r Lst l)∾"]";
r 𝕊 3‿l: "("∾(r Lst l)∾")";
0 𝕊 2‿s: s;
1 𝕊 2‿s:
∾´{
𝕊 '"': "\""";
𝕊 '
': "\n";
𝕊 '\': "\\";
𝕊 @: """";
""∾𝕩
}¨(@∾s∾@);
r 𝕊 1‿n: {n<0?"-";""}∾•Fmt |n;
r 𝕊 0‿x: x;
"ERROR printing: "∾(@+10)∾•Fmt 𝕩
}

{PrStr ⇐ 0⊸P
PrStrR ⇐ 1⊸P}
129 changes: 129 additions & 0 deletions impls/bqn/reader.bqn
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
⟨nil⟩ ← •Import "globals.bqn"
# Types:
# ¯1 error
# 0 symbol
# 1 int
# 2 string
# 3 list
# 4 vector
# 5 hashmap
# 6 keyword
# 7 boolean
# 8 nil
# 9 function
# 10 fn*
# 11 atom
# 12 macro

Reader ← {𝕊 tokens:
r ← {
ts ← tokens
Peek ⇐ {𝕊 ⋄ 0=≠ts ? @; ⊑ts}
Next ⇐ {𝕊
t ← r.Peek@
ts ↩ 1↓ts
t
}
}
}

T ← {𝕊 s:
v←⟨⟩
{𝕊
@=⊑s ? •Exit 0 ;
'@'=⊑s ? v∾↩<"@" ⋄ s↩1↓s ;
"~@"≡2↑s ? v∾↩<"~@" ⋄ s↩2↓s ;
⊑(⊑s)∊" ,"∾@+10 ? s↩1↓s ;
⊑(⊑s)∊"[]{}()'`~" ? v∾↩<≍⊑s ⋄ s↩1↓s ;
';'=⊑s ?
o←⟨⟩
{𝕊
o ∾↩ ⊑s
s ↩ 1↓s
{0=≠s ? 0 ; (⊑s) ≠ @+10}
} •_while_⊢1
v∾↩<o ;
""""≡s ? •Out "unbalanced quotes: """ ⋄ s↩⟨⟩;
'"'=⊑s ?
o‿p‿r ← """"‿1‿1
{𝕊
s ↩ 1↓s
c ← ⊑s
o ∾↩ c
q ← p∧c='"'
{p ? p↩c≠'\' ; p↩1}
{q?@;r ↩ 1<≠s}
r∧¬q
} •_while_⊢1
{r?@;•Out "unbalanced quotes: "∾o}
v∾↩<o ⋄ s↩1↓s;
o←⟨⟩
{𝕊
o ∾↩ ⊑s
s ↩ 1↓s
{0=≠s ? 0 ; ¬⊑(⊑s)∊"[]{}()'""`~,; "∾@+10}
} •_while_⊢1
v∾↩<o
}•_while_{𝕊 ⋄ 0≠≠s}@
v
}

# Thanks to Marshall for writing this version of readString and
# suhr for suggesting the `out` value of Ctrl-* characters! (My
# version was verbose functional code while this is array style.)
readString ← {
in ← "abefnrtv"
out ← "GH[LJMIK"-64
diff← (out-in)∾0
{
m ← » bs ← <`𝕩='\'
(¬bs) / (diff⊏˜in⊸⊐)⊸+⌾(m⊸/) 𝕩
}
}

Int ← {10⊸×⊸+˜´'0'-˜⌽𝕩}
nums ← "0123456789"
Atom ← {
𝕊 "true": 7‿1 ;
𝕊 "false": 7‿0 ;
𝕊 "nil": nil ;
𝕊 a: {'"': 2∾<ReadString 1↓¯1↓a ;
':': 6‿a ;
';': @ ;
⊑𝕩∊nums ? 1∾Int a ;
(∧´(1↓a)∊nums)∧(𝕩='-')∧1<≠a ? 1∾-Int 1↓a ;
0‿a }⊑a
}

ReadList ← {𝕊 r‿t‿e:
l←⟨⟩
{𝕊
{@: t ↩ ¯1 ⋄ l ↩ "unbalanced parens" ⋄ 0;
𝕩≡e ? r.Next@ ⋄ 0;
v ← ReadForm r
{v≡@ ? @; l∾↩<v}
1
}r.Peek@
}•_while_⊢1
t‿l
}

ReadForm ← {𝕊 r:
{"(": ReadList r‿3‿")" ;
"[": ReadList r‿4‿"]" ;
"{": ReadList r‿5‿"}" ;
"@": 3‿⟨0‿"deref", ReadForm r⟩;
"'": 3‿⟨0‿"quote", ReadForm r⟩;
"`": 3‿⟨0‿"quasiquote", ReadForm r⟩;
"~": 3‿⟨0‿"unquote", ReadForm r⟩;
"~@": 3‿⟨0‿"splice-unquote", ReadForm r⟩;
Atom 𝕩
}r.Next@
}

{
Tokenize ⇐ T
ReadStr ⇐ {
v ← ReadForm Reader Tokenize 𝕩
v≢@ ? v ; nil}
}
2 changes: 2 additions & 0 deletions impls/bqn/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
exec BQN $(dirname $0)/${STEP:-stepA_mal}.bqn "${@}"
11 changes: 11 additions & 0 deletions impls/bqn/step0_repl.bqn
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
READ ← ⊢
EVAL ← ⊢
PRINT ← ⊢
Rep ← PRINT∘EVAL∘READ

{
•Out "user> "
line ← •GetLine 0
•Out Rep line
𝕊𝕩
} @
14 changes: 14 additions & 0 deletions impls/bqn/step1_read_print.bqn
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
⟨ReadStr⟩ ← •Import "reader.bqn"
⟨PrStrR⟩ ← •Import "printer.bqn"

READ ← ReadStr
EVAL ← ⊢
PRINT ← PrStrR
Rep ← PRINT∘EVAL∘READ

{
•Out "user> "
line ← •GetLine 0
•Out Rep line
𝕊𝕩
}@
Loading