Browse Source

a repo for creating a web matzo thingy

Getty Ritter 1 year ago
commit
308b087f2e

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+/target
+/Cargo.lock

+ 12 - 0
Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "matzo-web"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+wasm-bindgen = "0.2"
+matzo = { path = "../matzo" }
+getrandom = { version = "*", features = ["js"] }

+ 47 - 0
build.py

@@ -0,0 +1,47 @@
+import json
+import os
+import shutil
+import subprocess
+
+def main():
+    print('=> rebuilding wasm')
+    subprocess.check_call(['wasm-pack', 'build', '--target', 'web'])
+    os.makedirs('out', exist_ok=True)
+    build_index()
+    print('=> moving wasm output')
+    for root, _, files in os.walk('pkg'):
+        for f in files:
+            shutil.copy(
+                os.path.join(root, f),
+                os.path.join('out', f)
+            )
+
+def build_index():
+    print('=> building index.html')
+    sources = {}
+    for root, _, files in os.walk("examples"):
+        for f in files:
+            if not f.endswith(".matzo"):
+                continue
+
+            with open(os.path.join(root, f)) as s:
+                sources[f.removesuffix(".matzo")] = s.read()
+
+    with open('index.html') as f:
+        template = f.read()
+
+    examples = '\n'.join((
+        f'<option value="{s}">{s}.matzo</option>'
+        for s in sorted(sources.keys())
+        if s != 'hello'
+    ))
+
+    template = (template
+                .replace('%SOURCES%', json.dumps(sources))
+                .replace('%EXAMPLES%', examples))
+
+    with open(os.path.join('out', 'index.html'), 'w') as f:
+        f.write(template)
+
+if __name__ == '__main__':
+    main()

+ 13 - 0
examples/aquan.matzo

@@ -0,0 +1,13 @@
+(* Below is a literal assignment, which is
+ * identical to cons := "p"| "t" | "k" | "w" | "h" | "n"; *)
+cons ::= p t k w h n;
+
+(* And this could also be done with two rules and a literal
+ * assignment *)
+vowel := ("a" | "e" | "i" | "o" | "u") (4: "" | "'");
+
+(* Here is a weighted disjunction *)
+syll := 4: cons vowel | vowel;
+
+(* And finally, here's an output statement *)
+puts syll rep[1..6, syll];

+ 8 - 0
examples/auran.matzo

@@ -0,0 +1,8 @@
+word := start (rep[1..4, syll]) end;
+syll := vowel cons;
+start := 3: cons | 2: '';
+end := 3: vowel | vowel 's'| vowel 'n';
+cons ::= p b m m f v t t t d n n s s s z l k g g gg sh j l r th th th;
+vowel ::= a a e e i i i o u eu au ae ai oi;
+
+puts word;

+ 18 - 0
examples/celestial.matzo

@@ -0,0 +1,18 @@
+word
+  := syll cons end
+   | init penult end
+   | init syll penult end
+   | init syll syll penult end
+   ;
+
+syll := cons vowel;
+init := 4: cons vowel | 4: vowel | vowel cons vowel;
+penult := cons vowel cons;
+
+end ::= el ael as as as es us us em em um um arm
+        is os os os on at ar or or ir o a a a;
+cons ::= b g t t t d d h v v z z z ch j x c c c
+         l l l m n s s s p f r r r s;
+vowel := 3: 'a'| 2: 'i' | 'u' | 2: 'e' | 'o' | 3: 'æ';
+
+puts word;

+ 11 - 0
examples/druidic.matzo

@@ -0,0 +1,11 @@
+(* Druidic *)
+word := init (rep[1|2, syll]) end;
+init := 3: vowel | 2: '';
+syll := cons vowel;
+end ::= a e er ir;
+cons ::= b c c c d d f g h j k l m n p qu r
+         s s s š š š t t t ts v w y z;
+vowel ::= a e i o u â ä î ai au äu ea ei eu eai eau ia ie io
+          iu ii oa oi ou uä ui;
+
+puts word;

+ 12 - 0
examples/giant.matzo

@@ -0,0 +1,12 @@
+word := begin rep[1..3, syll] end;
+syll := vowel cons;
+end := vowel ('n'| 'ns' | 's' | '');
+begin := 2: cons | beginvowel;
+
+cons ::= b c ch d f g gh h j l m n p r s t th v x wh;
+vowel := 3: normalvowel | accentvowel;
+normalvowel ::= a i u e o;
+accentvowel ::= á í ú é ó y;
+beginvowel  ::= a á i u e o;
+
+puts word;

+ 14 - 0
examples/gnoll.matzo

@@ -0,0 +1,14 @@
+word := syll | 4: syll ending | 2: syll syll ending | syll syll syll ending;
+syll := vowel cons | glide vowel cons;
+
+ending := 'in'| 'en' | 'e' | 'än' | 'ën' | 'er' | 'inn' | cons 's'
+        | 'ich' | 'ert' | 'urt' | 'ern' | 'urn';
+cons ::= p b pf m v t d tz n s zr l tsc sc ch y k g h;
+vowel := 3: basevowel | umlvowel | dipth;
+
+basevowel ::= a i u e o;
+umlvowel  ::= ä ï ü ë ö;
+dipth     ::= ei au eu eu äu;
+glide     ::= l r ch t;
+
+puts word;

+ 19 - 0
examples/gnomish.matzo

@@ -0,0 +1,19 @@
+word
+  :=    cons vowel
+   | 3: cons vowel cons vowel
+   | 2: cons vowel cons
+   | 3: cons vowel cons vowel cons vowel
+   | 3: cons vowel cluster vowel
+   | 3: cons vowel cons vowel cluster vowel
+   | 3: cons vowel cluster vowel cons vowel
+   ;
+
+cons ::= p b t d k g ts f v s z sh zh cz m n r l l ch;
+cluster ::=
+  lt ld lk lg lv lf ls lz lsh lzh lcz lm ln
+  rt rd rf rv rsh rzh rcz nd nt ns nz;
+vowel
+  := 2: 'a'| 2: 'i' | 2: 'u' | 2: 'e' | 2: 'o'
+   | 'ya' | 'yi' | 'yu' | 'ye' | 'yo' | 2: 'y';
+
+puts word;

+ 12 - 0
examples/goblin.matzo

@@ -0,0 +1,12 @@
+word := init ending | init syll ending | init syll syll ending;
+
+init := initcons vowel | vowel | vowel cons vowel;
+syll := cons vowel;
+ending := cons | cons vowel;
+
+cons := 10: basecons | 'l' initcons | 'r' initcons;
+basecons ::= b d f g j k l m n ñ p r s t x z tx dd ll rr ts tt tz sk;
+initcons ::= b d f g j k l m n n ñ p r s t t x z;
+vowel    ::= a a i u e e o o;
+
+puts word;

+ 13 - 0
examples/guel-ban.matzo

@@ -0,0 +1,13 @@
+cons ::= g b d r l zh dzh m n ŋ;
+coda ::= l r m n ŋ;
+vowel :=
+  let
+    simple ::= a e i o u;
+    compound ::= ue ua ui ia io iu
+  in {
+    3: simple | compound
+};
+
+syll := cons vowel (2: coda | "");
+
+puts tuple/join[tuple/rep[3, syll], " "];

+ 15 - 0
examples/halfling.matzo

@@ -0,0 +1,15 @@
+word := begin rep[1..4, syll] end;
+syll := cons vowel;
+end := cons vowel final | cons "é" final | cons "a" final | "r" | "n";
+
+begin := vowel | initcons vowel;
+
+cons ::= b c d d ch d f g g gn h j l l l lh m m m n n n nh
+         p qu r s s s ss ss ss sc sc t t t tg tj tz tz tr rt v z;
+initcons ::= b c d ch d f g gn h j l m n p qu r s sc t v;
+vowel := 4: commonvowel | uncommonvowel;
+commonvowel ::= a i u e é o;
+uncommonvowel ::= à è ó ò í ú;
+final := "s" | 6: "";
+
+puts word;

+ 48 - 0
examples/halfling_ipa.matzo

@@ -0,0 +1,48 @@
+(* these should probably be part of the stdlib eventually *)
+flatten := {[t] => tuple/fold[{ [x, xs] => x xs }, "", t]};
+
+word := {[w] => flatten[tuple/map[orthography, w]]};
+wd := tuple/concat[<begin, middle, end>];
+middle := syll | tuple/concat[<syll, syll>] | tuple/concat[<syll, syll, syll>] | tuple/concat[<syll, syll, syll, syll>];
+syll := <cons, vowel>;
+end := <cons, vowel, final>
+     | 2: <cons, ("eː" | "a"), final>
+     | <"r"> | <"n"> ;
+
+orthography := { [x] => case x in
+  { "z"  => "s"
+  ; "s"  => "ss"
+  ; "dz" => "z"
+  ; "kʷ" => "qu"
+  ; "k"  => "c"
+  ; "x"  => "ch"
+  ; "ʎ"  => "lh"
+  ; "ɲ"  => "nh"
+  ; "ʃ"  => "sc"
+  ; "ts" => "tz"
+  ; "aː" => "á"
+  ; "eː" => "é"
+  ; "iː" => "í"
+  ; "oː" => "ó"
+  ; "uː" => "ú"
+  ; "ɛ"  => "è"
+  ; "ɔ"  => "o"
+  ; "ɾ"  => "r"
+  ; "tɾ" => "tr"
+  ; "ɾt" => "rt"
+  ; _    => x
+  } };
+
+begin := <vowel> | <initcons, vowel>;
+
+cons ::= b k d d x d f g g ɲ h j l l l ʎ m m m n n n
+         p kʷ ɾ z z z s s s ʃ ʃ t t t tg tj ts ts tɾ ɾt v dz;
+initcons ::= b k d x d f g ɲ h j l m n p kʷ ɾ s ʃ t v;
+vowel := 4: commonvowel | uncommonvowel;
+commonvowel ::= a i u e eː o;
+uncommonvowel ::= aː ɛ oː ɔ iː uː;
+final := "s" | 6: "";
+
+
+fix wd;
+puts word[wd] " (pronounced /" flatten[wd] "/)";

+ 1 - 0
examples/hello.matzo

@@ -0,0 +1 @@
+puts "Hello, world!";

+ 33 - 0
examples/peano.matzo

@@ -0,0 +1,33 @@
+isZero := { [Z] => True; [<S,_>] => False };
+add := { [Z]     => { [y] => y }
+       ; [<S,x>] => { [y] => add[x][<S,y>] } };
+incr := add[<S,Z>];
+
+two  := <S,<S,Z>>;
+four := <S,<S,<S,<S,Z>>>>;
+
+if := { [True]  => { [x] => { [_] => x } }
+      ; [False] => { [_] => { [y] => y } }
+      };
+minusOne := { [Z] => Z; [<S,x>] => x };
+
+puts "S is " S;
+puts "Z is " Z;
+
+puts "True is " True;
+puts "False is " False;
+
+puts "Is 0 zero?";
+puts isZero[Z];
+
+puts "Is 4 zero?";
+puts isZero[four];
+
+puts "2 + 4 = ";
+puts add[four][two];
+
+puts "Is zero zero?";
+puts if[isZero[Z]]["yes"]["no"];
+
+puts "Is 0+1 zero?";
+puts if[isZero[incr[Z]]]["yes"]["no"];

+ 72 - 0
examples/person.matzo

@@ -0,0 +1,72 @@
+gender:= Male | Female | Unspec;
+byGender := { [m,f,n] => { [Male] => m; [Female] => f; [_] => n }};
+
+ending  := byGender["o","a","e"];
+pronoun := byGender["he","she","they"];
+noun    := byGender["man","woman","person"];
+are     := byGender["is","is","are"];
+have    := byGender["has", "has", "have"];
+
+cons  ::= p t c d g r l m n x;
+vowel ::= a e i o u;
+
+name := { [g] => (vowel | "") rep[1..3, cons vowel] cons ending[g] };
+
+hairColor ::= black brown blonde;
+eyeColor  ::= brown green blue;
+
+job := { [g] =>
+    "stonemason"
+  | "baker"
+  | "accountant"
+  | case g in
+      { Male   => "fisherman"
+      ; Female => "fisherwoman"
+      ; _      => "fisher"
+      } };
+tool := { ["stonemason"] => "chisel"
+        ; ["baker"]      => "bowl"
+        ; ["accountant"] => "tablet"
+        ; [_]            => "fishing pole"
+        };
+
+adjective ::= happy cheerful focused quiet meek rash;
+
+person :=
+  let fix my-gender := gender in {
+  let fix my-job := job[my-gender] in {
+    se[
+      "You come across",
+      str/capitalize[name[my-gender]],
+      ", a",
+      noun[my-gender],
+      "from the city of",
+      str/capitalize[name[Female]]
+      "."
+    ]
+    " "
+    se[
+      pronoun[my-gender],
+      are[my-gender],
+      "a hardworking",
+      my-job,
+      "with",
+      hairColor,
+      "hair and",
+      eyeColor,
+      "eyes."
+    ]
+    " "
+    se[
+      pronoun[my-gender],
+      have[my-gender],
+      "a",
+      tool[my-job],
+      "and",
+      are[my-gender],
+      "very",
+      adjective,
+      "."
+    ]
+  } };
+puts person;

+ 13 - 0
examples/simple.matzo

@@ -0,0 +1,13 @@
+(* Below is a literal assignment, which is
+ * identical to cons := "p"| "t" | "k" | "w" | "h" | "n"; *)
+cons ::= p t k w h n;
+
+(* And this could also be done with two rules and a literal
+ * assignment *)
+vowel := ("a" | "e" | "i" | "o" | "u") (4: "" | "'");
+
+(* Here is a weighted disjunction *)
+syll := 4: cons vowel | vowel;
+
+(* And finally, here's an output statement *)
+puts syll rep[1..6, syll];

+ 22 - 0
examples/simple_ipa.matzo

@@ -0,0 +1,22 @@
+cons := "p" | "t" | "k"
+      | "m" | "n" | "ŋ"
+      | "s" | "ʃ" | "ɾ";
+vowel ::= a i u;
+rime := 4: <vowel> | <vowel, "ʔ">;
+syll := tuple/flatten[cons, rime];
+
+word := tuple/flatten[tuple/rep[2..4, syll]];
+
+orthography := {
+  ["ʃ"] => "sh";
+  ["ŋ"] => "ng";
+  ["ɾ"] => "r";
+  ["ʔ"] => "'";
+  [ch]  => ch
+};
+
+fix word;
+
+ortho := tuple/join[tuple/map[orthography, word]];
+ipa := tuple/join[word];
+puts ortho " (pronounced /ˈ" ipa "/)";

+ 19 - 0
examples/sylvan.matzo

@@ -0,0 +1,19 @@
+word := syll rep[1..4, syll];
+
+syll := 2: cons vowel | cons vowel;
+
+(* Vowels *)
+vowel := 5: cardinal | 2: accented | diphthongs;
+cardinal ::= a e i o u;
+accented ::= á é í ó ú;
+diphthongs ::= ai au ia ua eu ue;
+
+(* Consonants *)
+cons := 2: avgcons | 4: commcons | 6: vercomcons | othercons;
+
+avgcons ::= p b k g m n f v s;
+commcons ::= t d;
+vercomcons ::= r l;
+othercons ::= ph bh kh gh mh nh fh;
+
+puts word;

+ 20 - 0
examples/terran.matzo

@@ -0,0 +1,20 @@
+word
+  := syll
+   | 2: syll syll
+   | 3: syll syll syll
+   | 2: syll syll syll syll
+   ;
+
+syll
+  := 6:  vowel
+   | 12: cons vowel
+   | 4:  vowel "n"
+   |     vowel "ng"
+   | 3:  cons vowel "n"
+   |     cons vowel "ng"
+   ;
+
+cons ::= k g t d s z r r r j m m n n w;
+vowel := (10: "" | "y") ("a" | "i" | "u" | "e" | "o");
+
+puts word;

+ 6 - 0
examples/tuenar.matzo

@@ -0,0 +1,6 @@
+cons ::= m n ny ng p py ph t ty th c cy ch k ky kh r rh l ly lh h s sh z zh;
+short := 3: "a" | "e" | "i" | 5: "o" | 3: "u" | "ü" | "ö";
+long := 3: "ā" | "ē" | "ī" | 5: "ō" | 3: "ū";
+vowel := 5: short | long;
+
+puts (4: "" | vowel) rep.<2..4, cons vowel>;

+ 16 - 0
examples/vexed.matzo

@@ -0,0 +1,16 @@
+(*
+An example from the Tracery tutorial:
+  {"name": ["Arjun","Yuuma","Darcy","Mia","Chiaki","Izzi","Azra","Lina"]
+  ,"animal": ["unicorn","raven","sparrow","scorpion","coyote","eagle","owl","lizard","zebra","duck","kitten"]
+  ,"mood": ["vexed","indignant","impassioned","wistful","astute","courteous"]
+  ,"story": ["#hero# traveled with her pet #heroPet#.  #hero# was never #mood#, for the #heroPet# was always too #mood#."]
+  ,"origin": ["#[hero:#name#][heroPet:#animal#]story#"]
+  }
+*)
+
+fix name := "Arjun" | "Yuuma" | "Darcy" | "Mia" | "Chiaki" | "Izzi" | "Azra" | "Lina";
+fix pet ::= unicorn raven sparrow scorpion coyote eagle owl lizard zebra duck kitten;
+mood ::= vexed indignant impassioned wistful astute corteous;
+
+puts name " traveled with her pet " pet ". "
+     name " was never " mood " for the " pet " was always too " mood ".";

+ 69 - 0
index.html

@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script type="module">
+     import init, {matzo_run} from "./matzo_web.js";
+
+     const sources = %SOURCES%;
+
+     window.onload = () => {
+       const run = document.getElementById('run');
+       const src = document.getElementById('src');
+       const output = document.getElementById('output');
+       const examples = document.getElementById('examples');
+
+       examples.onchange = () => {
+         src.value = sources[examples.value];
+       };
+
+       init()
+         .then(() => {
+           run.onclick = () => {
+             let result = matzo_run(src.value);
+             output.innerHTML = result;
+           }
+         });
+     };
+    </script>
+    <style type="text/css">
+     body, html {
+       height: 100%;
+       margin: 0em;
+     }
+
+     .all {
+       margin-left: auto;
+       margin-right: auto;
+       height: 100%;
+       display: flex;
+     }
+     .all > div {
+       margin: 1em;
+       padding: 1em;
+       border: 1px solid;
+       width: 50%;
+       height: 90%;
+     }
+     #output {
+       font-family: "Fira Mono";
+       white-space: pre-wrap;
+     }
+    </style>
+  </head>
+  <body>
+    <div class="all">
+      <div class="panel">
+        <select id="examples">
+          <option value="hello">hello.matzo</option>
+          %EXAMPLES%
+        </select><br/>
+
+        <textarea id="src" cols="80" rows="30">puts "hello, world!";</textarea><br/>
+        <input type="submit" id="run" value="Run" />
+      </div>
+      <div>
+        <pre id="output"></pre>
+      </div>
+    </div>
+  </body>
+</html>

+ 13 - 0
src/lib.rs

@@ -0,0 +1,13 @@
+use matzo::interp::State;
+use wasm_bindgen::prelude::*;
+
+#[wasm_bindgen]
+pub fn matzo_run(source: &str) -> String {
+    let state = State::new();
+    let mut buf = Vec::<u8>::new();
+    if let Err(err) = state.run_with_writer(&source, &mut buf) {
+        return err.to_string();
+    } else {
+        return String::from_utf8(buf).expect("Invalid UTF-8 somehow");
+    }
+}