txlyre 1 年間 前
コミット
2d2589bfd8
9 ファイル変更1619 行追加63 行削除
  1. 249 0
      langs/wm/.history
  2. 4 1
      langs/wm/Dockerfile
  3. 2 0
      langs/wm/Dockerfile.user
  4. 10 5
      langs/wm/run.sh
  5. 16 0
      langs/wm/stdio.wc
  6. 18 0
      langs/wm/string.wc
  7. 9 8
      langs/wm/wm.c
  8. 72 49
      langs/wm/wma.py
  9. 1239 0
      langs/wm/wmc.py

+ 249 - 0
langs/wm/.history

@@ -1,3 +1,29 @@
+130%64
+15/64
+64/64
+63/64
+32/64
+127%64
+0%64
+0/64
+0//64
+1//64
+2//64
+63//64
+64//64
+64%64
+65%64
+63%63
+64%63
+64/64
+63%64
+64%64
+65%64
+64%64
+64//64
+abs -1128
+absolute
+abs
 0x18
 0xd
 0xa
@@ -5,3 +31,226 @@
 0x51
 0xa1
 0x1a
+-1100 + 1000
+64%64
+128%64
+127%64
+100%64
+64%100
+64%128
+64%256
+64%64
+1%64
+10%64
+64%64
+128%64
+127%64
+129%64
+130%64
+15/64
+64/64
+63/64
+32/64
+127%64
+0%64
+0/64
+0//64
+1//64
+2//64
+63//64
+64//64
+64%64
+65%64
+63%63
+64%63
+64/64
+63%64
+64%64
+65%64
+64%64
+64//64
+abs -1128
+0x18
+0xd
+0xa
+0x6
+0x51
+0xa1
+0x1a
+-1100 + 1000
+64%64
+128%64
+127%64
+100%64
+64%100
+64%128
+64%256
+64%64
+1%64
+10%64
+64%64
+128%64
+127%64
+129%64
+130%64
+15/64
+64/64
+63/64
+32/64
+127%64
+0%64
+0/64
+0//64
+1//64
+2//64
+63//64
+64//64
+64%64
+65%64
+63%63
+64%63
+64/64
+63%64
+64%64
+65%64
+64%64
+64//64
+abs -1128
+absolute
+0x18
+0xd
+0xa
+0x6
+0x51
+0xa1
+0x1a
+-1100 + 1000
+64%64
+128%64
+127%64
+100%64
+64%100
+64%128
+64%256
+64%64
+1%64
+10%64
+64%64
+128%64
+127%64
+129%64
+130%64
+15/64
+64/64
+63/64
+32/64
+127%64
+0%64
+0/64
+0//64
+1//64
+2//64
+63//64
+64//64
+64%64
+65%64
+63%63
+64%63
+64/64
+63%64
+64%64
+65%64
+64%64
+64//64
+abs -1128
+0x18
+0xd
+0xa
+0x6
+0x51
+0xa1
+0x1a
+-1100 + 1000
+64%64
+128%64
+127%64
+100%64
+64%100
+64%128
+64%256
+64%64
+1%64
+10%64
+64%64
+128%64
+127%64
+129%64
+130%64
+15/64
+64/64
+63/64
+32/64
+127%64
+0%64
+0/64
+0//64
+1//64
+2//64
+63//64
+64//64
+64%64
+65%64
+63%63
+64%63
+64/64
+63%64
+64%64
+65%64
+64%64
+64//64
+abs -1128
+absolute
+abs
+sign
+sign?
+-(-1128)
+1128 - 1000
+-20 - -20
+-20 - -20 - -20
+xor
+xor 5 5
+5 xor 5
+5 xor 6
+10 xor
+xor 5 3
+xor 8 3
+6 - 3
+5 - 3
+5 - 5
+5 - 8
+5 - 5
+5 - 6
+0x6c
+0xd6
+0x8
+-1 < -1000
+-1001 < -1000
+1 < -1000
+0x3
+px8
+0x8
+[3, 8, 8, 5, 1, 0, 0, -1, 0]
+zip ([..9], [3, 8, 8, 5, 1, 0, 0, -1, 0])
+zip [..9] [3, 8, 8, 5, 1, 0, 0, -1, 0]
+zip [0..8] [3, 8, 8, 5, 1, 0, 0, -1, 0]
+0xc5
+10-10
+9-10
+10-10
+0xf
+0-1
+1-2
+1-1
+0x54
+0*80
+0x80

+ 4 - 1
langs/wm/Dockerfile

@@ -4,7 +4,10 @@ RUN ln -s /usr/bin/python3 /usr/bin/python
 COPY ./wm/wm.c ./wm.c
 COPY ./wm/wma.py /usr/bin/wma
 RUN chmod +x /usr/bin/wma
-RUN chmod 705 /usr/bin/wma 
+RUN chmod 705 /usr/bin/wma
+COPY ./wm/wmc.py /usr/bin/wmc
+RUN chmod +x /usr/bin/wmc
+RUN chmod 705 /usr/bin/wmc
 RUN gcc -Os -ansi wm.c -o /usr/bin/wm -lm
 RUN rm wm.c
 RUN chmod +x /usr/bin/wm

+ 2 - 0
langs/wm/Dockerfile.user

@@ -0,0 +1,2 @@
+RUN mkdir .wclib
+COPY *.wc ./.wclib

+ 10 - 5
langs/wm/run.sh

@@ -1,12 +1,17 @@
-IN="$(mktemp --suffix .s)"
+IN="$(mktemp --suffix .wc)"
+OUT_S="$(mktemp --suffix .s)"
 OUT="$(mktemp)"
 
 cat > "$IN"
 
-wma "$IN" "$OUT"
+WC_I="/home/user/.wclib" wmc "$IN" "$OUT_S"
 
 if [ $? -eq 0 ]; then
-  SIZE="$(wc -c $OUT)"
+  wma "$OUT_S" "$OUT"
 
-  wm -Dd -s "$SIZE" "$OUT"
-fi
+  if [ $? -eq 0 ]; then
+    SIZE="$(wc -c $OUT)"
+
+    wm -Dd -s "$SIZE" "$OUT"
+  fi
+fi

+ 16 - 0
langs/wm/stdio.wc

@@ -0,0 +1,16 @@
+putc(c) {
+  asm("mov {c} IO");
+}
+
+putd(n) {
+  asm("mov {n} Z");
+}
+
+puts(s) {
+  for (i=0;s[i];i++)
+    putc(s[i]);
+}
+
+nl() {
+  putc('\n');
+}

+ 18 - 0
langs/wm/string.wc

@@ -0,0 +1,18 @@
+strlen(s) {
+  l=0;
+
+  while (*s++)
+    l++;
+
+  return l;
+}
+
+strcpy(s, d) {
+  i=0;
+
+  while (s[i]) {
+    d[i] = s[i];
+
+    i++;
+  }
+}

+ 9 - 8
langs/wm/wm.c

@@ -35,7 +35,7 @@ L tj(){if(jp<0)e("jump frame underflow.");R m[JA+jp--];}
 L lj(){if(jp<0)e("jump frame underflow.");R m[JA+jp];}
 Z g(){if(m[0]>=MZ)m[0]=K;R m[m[0]++];}
 L iL(){C b[64];if(!fgets(b,SIZ(b),stdin))R 0;R strtoll(b,0,10);}
-V ro(I);L r(L);V w(L,L);V j(I a){if(a==-K){ST=1;R;}ELIF(a==-5)a=tj();if(a<0||a>=MZ)e("attempt to jump to an illegal address: 0x%x.",a);m[0]=a;}
+V ro(I);L r(L);V w(L,L);V j(I a){if(a==-K){ST=K;R;}ELIF(a==-5)a=tj();if(a<0||a>=MZ)e("attempt to jump to an illegal address: 0x%x.",a);m[0]=a;}
 S rS(L e){L i=e;S s=a(K);Z k=0,z=K;LP(){I c=r(i++);if(c<=0)break;if(k==z-K){z++;s=ra(s,z);}s[k++]=c;}s[i]=0;R s;}
 V fo(L a){L re=a++;I m=r(a++);S n=rS(r(a));I mo=m;m=m==0?O_WRONLY:m==K?(O_RDONLY|O_CREAT):m==2?O_RDWR:m==3?O_APPEND:-K;if(m<0)e("illegal mode flag: 0x%x.",mo);I dc=open(n,m);free(n);w(re,dc);}
 V fw(L a){I dc=r(a++);L re=a++;S s=rS(r(a));Z z=strlen(s);L c=write(dc,s,z);free(s);w(re,c);}
@@ -65,18 +65,19 @@ SW(d)DR1(0,~a);DR2(1,b&a);DR2(2,b|a);
   DR2(3,b^a);DR2(4,b<<a);DR2(5,b>>a);
   DR2(6,a==0?0:a<0?-1:1);DR2(7,b+a);
   DR2(8,b-a);DR2(9,b*a);DR2(10,dI(b,a));
-  DR2(11,mO(b,a));DR2(12,pow(b,a));DR1(13,riD(sqrt(riL(a))));
+  DR2(11,mO(b,a));DR2(12,pow(b,a));
+  DR1(13,riD(sqrt(riL(a))));DR1(20,-a);
   DR2(70,riD(riL(b)+riL(a)));DR2(71,riD(riL(b)-riL(a)));
   DR2(72,riD(riL(b)*riL(a)));DR2(73,riD(dvD(riL(b),riL(a))));
   DR2(74,riD(mdD(riL(b),riL(a))));DR2(75,riD(powf(riL(b),riL(a))));
   DR1(80,eX(a));DR1(81,ouD(a));
   DR0(40,a=po();pu(a);pu(a););DR0(41,a=po();b=po();pu(b);pu(a););DR0(42,po(););DF(e("illegal special directive: 0x%llx.",d);)}
-L r(L a){if(a==-K)R getchar();ELIF(a==-2)R 0;ELIF(a==-3)Rk;ELIF(a==-4)R-K;ELIF(a==-5)R lj();ELIF(a==-6)R time(0);ELIF(a==-7)R MZ+sp;ELIF(a==-8)R EZ;ELIF(a==-9)R SZ;ELIF(a==-10)R MZ;ELIF(a==-11)R JZ;ELIF(a==-12)R iL();ELIF(a==-13){R aM(64);}if(a<0||a>=MZ)e("attempt to read from an illegal address: 0x%x.",a);R m[a];}
-V w(L a,L v){if(a==-K){putchar(v);R;}ELIF(a==-2){printf("%lld",v);R;}ELIF(a==-5){pj(v);R;}ELIF(a==-6){usleep(v);R;}ELIF(a==-7){sp=v-MZ;R;}ELIF(a==-8){fo(v);R;}ELIF(a==-9){if(v>=0)close(v);R;}ELIF(a==-10){fr(v);R;}ELIF(a==-11){fw(v);R;}ELIF(a==-12){fs(v);R;}ELIF(a==-13){SW(r(v))CS(0,w(v,aM(64));)CS(K,mF(v);)CS(2,mW(v);)CS(3,w(v,mR(v));)DF(w(v,-1);)R;}ELIF(a==-14){cP(v);R;}else if(a==-15){R;}if(a<0||a>=MZ)e("attempt to write (value = 0x%x) to an illegal address: 0x%x.",v,a);m[a]=v;}
+L r(L a){if(a<-1000){a=llabs(a);a-=1000;L i=a/64;L p=a%64;if(i<0||i>=mi||!mm[i])e("attempt to read an illegal memory bank: 0x%x.",i);if(p<0||p>=64)e("attempt to read an illegal memory bank: 0x%x (pointer: 0x%x).",i,p);R mm[i][p];}ELIF(a==-K)R getchar();ELIF(a==-2)R 0;ELIF(a==-3)Rk;ELIF(a==-4)R-K;ELIF(a==-5)R lj();ELIF(a==-6)R time(0);ELIF(a==-7)R MZ+sp;ELIF(a==-8)R EZ;ELIF(a==-9)R SZ;ELIF(a==-10)R MZ;ELIF(a==-11)R JZ;ELIF(a==-12)R iL();ELIF(a==-13)R aM(64);ELIF(a==-15)R 0;if(a<0||a>=MZ)e("attempt to read from an illegal address: 0x%x.",a);R m[a];}
+V w(L a,L v){if(a<-1000){a=llabs(a);a-=1000;L i=a/64;L p=a%64;if(i<0||i>=mi||!mm[i])e("attempt to write to an illegal memory bank: 0x%x.",i);if(p<0||p>=64)e("attempt to write to an illegal memory bank: 0x%x (pointer: 0x%x).",i,p);mm[i][p]=v;R;} else if(a==-K){putchar(v);R;}ELIF(a==-2){printf("%lld",v);R;}ELIF(a==-5){pj(v);R;}ELIF(a==-6){usleep(v);R;}ELIF(a==-7){sp=v-MZ;R;}ELIF(a==-8){fo(v);R;}ELIF(a==-9){if(v>=0)close(v);R;}ELIF(a==-10){fr(v);R;}ELIF(a==-11){fw(v);R;}ELIF(a==-12){fs(v);R;}ELIF(a==-13){SW(r(v))CS(0,w(v,aM(64));)CS(K,mF(v);)CS(2,mW(v);)CS(3,w(v,mR(v));)DF(w(v,-1);)R;}ELIF(a==-14){cP(v);R;}ELIF(a==-15){R;}if(a<0||a>=MZ)e("attempt to write (value = 0x%x) to an illegal address: 0x%x.",v,a);m[a]=v;}
 #define OP1(x,p)case x:a=g();if(s)break;{p}break
 #define OP2(x,p)case x:a=g();b=g();if(s)break;{p}break
 #define OP3(x,p)case x:a=g();b=g();c=g();if(s)break;{p}break
-enum{NOP,MVJ,SBLEZ,ABLEZ,SBLZ,BLES,NBNZ,DBNZ,SSLEZ,ASLEZ,IBNC,VBLZ,XBLZ,DSLZ,SSGT,MBNZ,MODBZ,AJA,LA,LD,IA,JMC,JW,PSH,PD,POP,SHLBNZ,SHRBNZ,NBZ,ANZ,ABGZ,SWP};
+enum{NOP,MVJ,SBLEZ,ABLEZ,SBLZ,BLES,NBNZ,DBNZ,SSLEZ,ASLEZ,IBNC,VBLZ,XBLZ,DSLZ,SSGT,MBNZ,MODBZ,AJA,LA,LD,IA,JMC,JW,PSH,PD,POP,SHLBNZ,SHRBNZ,NBZ,ANZ,ABGZ,SWP,STR};
 V ro(I s){L a,b,c,d,t;U o=g();lo=o;
 SW(o)CS(NOP,);OP3(MVJ,if(a!=b)w(b,r(a));j(c);); 
  OP3(SBLEZ,w(b,r(b)-r(a));if(r(b)<=0)j(c););
@@ -88,12 +89,12 @@ SW(o)CS(NOP,);OP3(MVJ,if(a!=b)w(b,r(a));j(c););
  OP2(SSLEZ,w(b,r(b)-r(a));if(r(b)<0)ro(K););
  OP2(ASLEZ,w(b,r(b)+r(a));if(r(b)<0)ro(K););
  OP3(IBNC,t=r(b);w(b,r(a)+K);if(r(b)==t)j(c););
- OP3(VBLZ,t=r(a);w(b,dI(r(b),t));if(r(b)<0)j(c););
+ OP3(VBLZ,w(b,dI(r(b),r(a)));if(r(b)<0)j(c););
  OP3(XBLZ,w(b,r(b)^r(a));if(r(b)>r(a))j(c););
  OP1(DSLZ,w(a,r(a)-K);if(r(a)<0)ro(K););
  OP2(SSGT,t=r(a)>r(b);w(b,r(b)-r(a));w(a,r(b));if(t)ro(K););
  OP3(MBNZ,if(a!=-3)w(b,r(b)*r(a));if(r(b))j(c););
- OP3(MODBZ,t=r(a);w(b,mO(r(b),t));if(!r(b))j(c););
+ OP3(MODBZ,w(b,mO(r(b),r(a)));if(!r(b))j(c););
  OP2(AJA,w(b,r(b)+r(a));j(r(b)););
  OP2(LA,w(b,r(r(a))););OP2(LD,w(b,a););OP1(IA,t=r(r(a));w(t,r(t)+K););
  OP1(JMC,j(m[0]*a););OP1(JW,pj(m[0]);j(a););
@@ -103,7 +104,7 @@ SW(o)CS(NOP,);OP3(MVJ,if(a!=b)w(b,r(a));j(c););
  OP2(NBZ,w(a,~r(a));if(!r(a))j(c););
  OP3(ANZ,w(b,r(b)&r(a));if(r(b))j(c););
  OP3(ABGZ,w(b,r(b)+r(a));if(r(b)>0)j(c););
- OP2(SWP,t=r(a);w(a,r(b));w(b,t););DF(e("unrecognized operation: 0x%x.",o);)}
+ OP2(SWP,t=r(a);w(a,r(b));w(b,t););OP2(STR,w(r(b),r(a)););DF(e("unrecognized operation: 0x%x.",o);)}
 V rn(){while(!ST)ro(0);}
 main(I c,S*as){I o;S f=0;
  while((o=getopt(c,as,"dDs:j:z:f:"))!=-K){

+ 72 - 49
langs/wm/wma.py

@@ -11,7 +11,8 @@ command: LABEL? operation
        | LABEL (arg|mixed)
        | LABEL
        | INCLUDE
-?operation: "nop" -> nop
+?operation: "org" INTEGER -> org
+          | "nop" -> nop
           | ("mvj"|"mj") arg arg arg -> mj          
           | "sblez" arg arg arg -> sjlez
           | "ablez" arg arg arg -> ajlez
@@ -33,7 +34,7 @@ command: LABEL? operation
           | "ld" arg arg -> ld
           | "ia" arg -> ia
           | "jmc" arg -> jmc
-          | ("jw"|"ja") arg -> ja
+          | ("jw"|"ja"|"call") arg -> ja
           | ("push"|"psh") arg -> psh
           | "pd" arg -> pd
           | "pop" arg -> pop
@@ -43,6 +44,7 @@ command: LABEL? operation
           | "anz" arg arg arg -> anz
           | "abgz" arg arg arg -> abgz
           | "swp" arg arg -> swp
+          | "str" arg arg -> str
           | "add" arg arg -> h_add
           | "sub" arg arg -> h_sub
           | "inc" arg -> h_inc
@@ -53,6 +55,8 @@ command: LABEL? operation
           | "out" arg -> h_out
           | "outn" arg -> h_outn
           | "in" arg -> h_in
+          | "dir" arg -> h_dir
+          | "ret" -> h_ret
 mixed: arg arg+
 ?arg: INTEGER
     | DOUBLE
@@ -61,21 +65,23 @@ mixed: arg arg+
     | OFFSET
     | LABELOFFSET
     | QMARK
+    | EMARK
     | NAMEOFFSET
     | NAME
     | rep
-rep: arg "*" COUNT
+rep: (INTEGER|DOUBLE|CHAR|CHARS|NAME|NAMEOFFSET) "*" COUNT
 COUNT: /[0-9]+/
 INTEGER: /-?[0-9]+/
 DOUBLE: /-?[0-9]+\.[0-9]+/
 CHAR: "'" /./ "'"
 CHARS: "\"" /[^"]*/ "\""
 QMARK: "?"
+EMARK: "!"
 OFFSET: "?" /(-|\+)[0-9]+/
-LABELOFFSET: "?" /(-|\+)[A-Za-z][a-zA-Z0-9_]*/ 
-NAMEOFFSET: /[A-Za-z][a-zA-Z0-9_]*(-|\+)[0-9]+/
-LABEL: /[A-Za-z][a-zA-Z0-9_]*:/
-NAME: /[A-Za-z][a-zA-Z0-9_]*/
+LABELOFFSET: "?" /(-|\+)[A-Za-z_][a-zA-Z0-9_]*/ 
+NAMEOFFSET: /[A-Za-z_][a-zA-Z0-9_]*(-|\+)[0-9]+/
+LABEL: /[A-Za-z_][a-zA-Z0-9_]*:/
+NAME: /[A-Za-z_][a-zA-Z0-9_]*/
 INCLUDE: "+" /.+/
 _NL: /\n+/
 IG: /[ \t\r]+/
@@ -86,11 +92,22 @@ COM: /#.*[^\n]/
 
 class WMA:
   def __init__(self):
+    self.origin = 1
+
     self.buffer = []
     self.size = 0
     
-    self.parser = lark.Lark(GRAMMAR)
+    self.parser = lark.Lark(GRAMMAR, parser='lalr')
     self.used_regs = []
+    self.labels = {}
+
+  def add_label(self, label):
+    if label in self.labels:
+      raise Exception(f"Duplicated label: {label}.")
+    elif label in ("PC", "IO", "Z", "O", "N", "J", "T", "SP", "EZ", "SZ", "MZ", "JZ", "W", "MM", "DR", "ZZ"):
+      raise Exception(f"Register override: {label}.")
+
+    self.labels[label] = len(self.buffer)
 
   def emit(self, *ops):
     self.buffer.extend(ops)
@@ -119,9 +136,11 @@ class WMA:
       for char in arg.value[1:-1]:
         self.emit(ord(char))
     elif arg.type == "QMARK":
-      self.emit(self.size+1)
+      self.emit(self.origin + self.size)
+    elif arg.type == "EMARK":
+      self.emit(self.origin + self.size + 1)
     elif arg.type == "OFFSET":
-      self.emit(self.size+int(arg.value[1:])+1)
+      self.emit(self.origin + self.size + int(arg.value[1:]))
     elif arg.type == "LABELOFFSET":
       self.emit((False, arg.value[2:], arg.value[1]))
     elif arg.type == "NAMEOFFSET":
@@ -131,7 +150,9 @@ class WMA:
 
       self.emit((False, n, '+' if '+' in arg.value else '-', int(o)))
     elif arg.type == "NAME":
-      if arg.value == "IO":
+      if arg.value == "PC":
+        self.emit(0)
+      elif arg.value == "IO":
         self.emit(-1)
       elif arg.value == "Z":
         self.emit(-2)
@@ -166,7 +187,9 @@ class WMA:
         self.emit((False, arg.value))
 
   def compile_operation(self, op):
-    if op.data == "nop":
+    if op.data == "org":
+      self.org = int(op.children[0].value)
+    elif op.data == "nop":
       self.emit(0)
     elif op.data == "mj":
       self.emit(1)
@@ -299,6 +322,10 @@ class WMA:
       self.emit(31)
       self.compile_arg(op.children[0])
       self.compile_arg(op.children[1])
+    elif op.data == "str":
+      self.emit(32)
+      self.compile_arg(op.children[0])
+      self.compile_arg(op.children[1])
     elif op.data == "h_add":
       self.emit(3)
       self.compile_arg(op.children[0])
@@ -320,10 +347,15 @@ class WMA:
       self.compile_arg(op.children[0])
       self.emit(self.size+2)
     elif op.data == "h_mov":
-      self.emit(1)
-      self.compile_arg(op.children[0])
-      self.compile_arg(op.children[1])
-      self.emit(self.size+2)
+      if type(op.children[0]) is lark.Token and op.children[0].type in ("INTEGER", "DOUBLE", "CHAR"):
+        self.emit(19)
+        self.compile_arg(op.children[0])
+        self.compile_arg(op.children[1])
+      else:
+        self.emit(1)
+        self.compile_arg(op.children[0])
+        self.compile_arg(op.children[1])
+        self.emit(self.size+2)
     elif op.data == "h_jmp":
       self.emit(1)
       self.emit(0)
@@ -349,28 +381,17 @@ class WMA:
       self.emit(-1)
       self.compile_arg(op.children[0])
       self.emit(self.size+2)
+    elif op.data == "h_dir":
+      self.emit(19)
+      self.compile_arg(op.children[0])
+      self.emit(-14)
+    elif op.data == "h_ret":
+      self.emit(1)
+      self.emit(-2)
+      self.emit(-2)
+      self.emit(-5)
 
   def compile_labels(self):
-    labels = {}
-
-    position = 0
-    while position < len(self.buffer):
-      this = self.buffer[position]
-
-      if type(this) is tuple and this[0]:
-        label = this[1]
-
-        if label in labels:
-          raise Exception(f"Duplicated label: {label}.")
-        elif label in ("IO", "Z", "O", "N", "J", "T", "SP", "EZ", "SZ", "MZ", "JZ", "W", "MM", "DR", "ZZ"):
-          raise Exception(f"Register override: {label}.")
-
-        self.buffer.pop(position)
-      
-        labels[label] = position + 1
-
-      position += 1
-
     position = 0
     while position < len(self.buffer):
       this = self.buffer[position]
@@ -378,15 +399,15 @@ class WMA:
       if type(this) is tuple and not this[0]:
         label = this[1]
 
-        if label not in labels:          
+        if label not in self.labels:          
           raise Exception(f"Undefined label/register: {label}.")
         
         if len(this) == 3:
-          self.buffer[position] = 1 + (position + labels[label] if this[2] == '+' else position - labels[label])
+          self.buffer[position] = self.origin + (position + self.labels[label] if this[2] == '+' else position - selfmlabels[label])
         elif len(this) == 4:
-          self.buffer[position] = labels[label] + this[3] if this[2] == '+' else labels[label] - this[3]
+          self.buffer[position] = self.origin + (self.labels[label] + this[3] if this[2] == '+' else self.labels[label] - this[3])
         else:
-          self.buffer[position] = labels[label]
+          self.buffer[position] = self.origin + self.labels[label]
 
       position += 1
 
@@ -409,18 +430,20 @@ class WMA:
       if len(command.children) == 2:
         label = command.children[0].value[:-1]
         
-        self.emit((True, label))
+        self.add_label(label)
 
-        if type(command.children[1]) is lark.Tree and command.children[1].data != "mixed":                
+        if type(command.children[1]) is lark.Tree and command.children[1].data not in ("mixed", "rep"):                
           self.compile_operation(command.children[1])
-        else:
+        else:          
           self.compile_arg(command.children[1])
-      else: 
+      else:
         if type(command.children[0]) is lark.Token:
           if command.children[0].type == "LABEL":
             label = command.children[0].value[:-1]
-        
-            self.emit((True, label)) 
+
+            self.add_label(label)
+
+            self.labels[label] = len(self.buffer)
           else:
             with open(command.children[0].value[1:], "r") as f:
               self.precompile(f.read())
@@ -429,10 +452,10 @@ class WMA:
 
   def compile(self, source):
     self.precompile(source)
-
-    for reg in "ABCDEFGHIXYK":
+    
+    for reg in "ABCDEFGHIXYKRS":
       if reg in self.used_regs:
-        self.buffer.append((True, reg))
+        self.add_label(reg)
         self.buffer.append(0)
 
     self.compile_labels()

+ 1239 - 0
langs/wm/wmc.py

@@ -0,0 +1,1239 @@
+import os
+import os.path
+import sys
+
+import lark
+
+GRAMMAR = r"""
+start: toplevel+
+?toplevel: include
+         | funcdef
+         | arrdec ";"
+         | vardec ";"
+         | varinit ";"
+         | arrinit ";"
+         | asm ";"
+
+include: "#" "include" FILENAME 
+FILENAME: "<" /.+/ ">"
+funcdef: NAME "(" params ")" block
+varinit: NAME
+arrinit: NAME "[" INTEGER "]"
+vardec: NAME "=" expr
+arrdec: NAME "[" expr "]" ("=" expr)?
+
+params:
+      | NAME ("," NAME)*
+
+block: "{" op* "}"
+
+asm: "asm" "(" STRING+ ")"
+
+?op: block
+   | label
+   | goto ";"
+   | vardec ";"
+   | arrdec ";"
+   | varinit ";"
+   | arrinit ";"
+   | inc ";"
+   | dec ";"
+   | rinc ";"
+   | rdec ";"
+   | asm ";"
+   | funcall ";"
+   | if
+   | while
+   | for
+   | return ";"
+label: NAME ":"
+goto: "goto" NAME
+if: "if" "(" expr ")" op ("else" op)?
+while: "while" "(" expr ")" op
+for: "for" "(" vardec ";" expr ";" (inc|dec|vardec|funcall) ")" op
+return: "return" expr
+inc: NAME "++"
+dec: NAME "--"
+rinc: "++" NAME
+rdec: "--" NAME 
+
+funcall: NAME "(" args ")"
+args:
+    | [expr ("," expr)*]
+
+?expr: op1
+?op1: op2
+    | vardec
+    | op1 "==" op1 -> equals
+    | op1 "!=" op1 -> not_equals
+    | op1 "+" op2 -> plus
+    | op1 "-" op2 -> minus
+?op2: op3
+    | op2 "*" op2 -> times
+    | op2 "/" op3 -> divide
+    | op2 "%" op3 -> modulo
+    | op2 "<" op3 -> less
+    | op2 ">" op3 -> greater
+    | op2 "<=" op3 -> less_or_equals
+    | op2 ">=" op3 -> greater_or_equals
+?op3: op4
+    | op3 "**" op4 -> raise
+?op4: op5
+    | op4 "||" op4 -> or
+    | op4 "&&" op4 -> and
+    | "!" atom -> not
+    | "*" atom -> deref
+?op5: atom
+    | op5 "[" op1 "]" -> index
+?atom: "(" op1 ")"
+     | NAME
+     | INTEGER
+     | FLOAT
+     | CHAR
+     | STRING
+     | "-" atom -> negate
+     | array
+     | funcall
+     | inc
+     | dec
+     | rinc
+     | rdec
+array: "{" atom ("," atom)* "}"
+
+NAME: /[A-Za-z_][a-zA-Z0-9_]*/
+INTEGER: /[0-9]+/
+FLOAT: /[0-9]+\.[0-9]+/
+CHAR: /'(.|(\\.))'/
+_STRING_INNER: /(.|\n)*?/
+_STRING_ESC_INNER: _STRING_INNER /(?<!\\)(\\\\)*?/ 
+STRING: "\"" _STRING_ESC_INNER "\""
+
+IG: /[ \t\r\n]+/
+COM: /\/\*(.|\!)*\*\//
+%ignore IG
+%ignore COM
+"""
+
+def parse_escape(s):
+  return bytes(s, "utf-8").decode("unicode_escape")
+
+class Buffer:
+  def __init__(self, *init):
+    self.buffer = list(init)
+
+  def emit(self, asm, *args):
+    if type(asm) is list:
+      self.buffer.extend(asm)
+    elif type(asm) is Buffer:
+      self.buffer.extend(asm.buffer)
+    else:
+      self.buffer.append(asm.format(*args))
+
+  def generate(self):
+    return "\n".join(self.buffer)
+
+  def __add__(self, other):
+    if type(other) is Buffer:
+      return Buffer(
+        *self.buffer + other.buffer
+      )
+
+    raise TypeError
+
+class Scope:
+  def __init__(self):
+    self.scopes = []
+    self.ndx = 0
+
+    self.names = set()
+
+  def new(self):
+    self.scopes.append((self.ndx, {}, {}))
+    self.ndx += 1
+
+  def leave(self):
+    self.scopes.pop()
+
+  def add_label(self, name):
+    renamed = f"__{self.scopes[-1][0]}l_{name}"
+
+    self.scopes[-1][2][name] = renamed
+
+    return renamed
+
+  def get_label(self, name):
+    if name not in self.scopes[-1][2]:
+      raise Exception(f"Undeclared label: '{name}`.")
+
+    return self.scopes[-1][2][name]
+
+  def is_local(self, name):
+    return name in self.scopes[-1][1]
+
+  def insert(self, name):
+    renamed = f"__{self.scopes[-1][0]}_{name}"
+
+    self.scopes[-1][1][name] = renamed
+
+    self.names.add(renamed)
+
+    return renamed
+
+  def find(self, name):
+    for _, scope, _ in self.scopes[::-1]:
+      if name in scope:
+        return scope[name]
+
+    raise Exception(f"Undeclared identifier: '{name}`.")
+
+  def __contains__(self, name):
+    for _, scope, _ in self.scopes[::-1]:
+      if name in scope:
+        return True
+
+    return False
+
+  def __getitem__(self, name):
+    if name in self:
+      return self.find(name)
+
+    return self.insert(name)
+
+  def __iter__(self):
+    for _, scope, _ in self.scopes[::-1]:
+      for name in scope:
+        yield (name, scope[name])
+
+  @property
+  def is_toplevel(self):
+    return self.scopes[-1][0] == 0
+
+class Func:
+  def __init__(self, argc, params):
+    self.argc = argc
+    self.params = params
+
+class WMC:
+  def __init__(self):
+    self.funcs = {}
+    self.used_symbols = {}
+    self.where = "toplevel"
+
+    self.scope = Scope()
+    self.scope.new()
+
+    self.compiled_funcs = {}
+
+    self.arrays_buffer = Buffer()
+    self.init_buffer = Buffer()
+    self.buffer = Buffer(
+      "jw __main",
+      "pop Y",
+      "mov 80 _dirBuf",
+      "mov Y _dirBuf+1",
+      "dir _dirBuf",
+      "hlt",
+      "_dirBuf:0*4",
+    )
+
+    self.label_ndx = 0
+
+    self.included_files = set()
+
+    self.parser = lark.Lark(GRAMMAR)
+
+  def record_usage(self, name):
+    if name in self.used_symbols:
+      self.used_symbols[name].add(self.where)
+    else:
+      self.used_symbols[name] = set([self.where])
+
+  def is_used(self, name):
+    if name in ("main", "toplevel"):
+      return True
+
+    if name not in self.used_symbols:
+      return False
+
+    for other in self.used_symbols[name]:
+      if other == name:
+        continue
+
+      if self.is_used(other):
+        return True
+
+    return False
+
+  def make_label(self):
+    name = f"__{self.label_ndx}l"
+
+    self.label_ndx += 1
+
+    return name
+
+  def make_array(self, value):
+    name = self.make_label()
+
+    self.arrays_buffer.emit(
+      "{}:{}",
+      name, value
+    )
+
+    return name
+
+  def compile_literal(self, node):
+    if type(node) is lark.Token:
+      if node.type == "NAME":        
+        value = self.scope.find(node.value)
+
+        self.record_usage(value)
+
+        return value
+      elif node.type in ("INTEGER", "FLOAT"):
+        return node.value
+      elif node.type == "CHAR":
+        return str(ord(parse_escape(node.value[1:-1])))
+      elif node.type == "STRING":
+        value = parse_escape(node.value[1:-1])
+        value = map(ord, value)
+        value = map(str, value)
+        value = " ".join(value)
+        value += " 0"
+
+        return value
+    elif node.data == "array":
+      value = []
+      for child in node.children:
+        value.append(
+          self.compile_literal(child)
+        )
+
+      return " ".join(value)
+
+    raise Exception(f"Not implemented: {node}")
+
+  def compile_unary_expr(self, node, *ops):
+    buffer = Buffer()
+
+    buffer.emit(
+      self.compile_expr(
+        node.children[0]
+      )
+    )
+
+    for op in ops:
+      buffer.emit(op)
+
+    return buffer
+
+  def compile_binary_expr(self, node):
+    buffer = Buffer()
+
+    buffer.emit(
+      self.compile_expr(
+        node.children[0]
+      )
+    )
+    buffer.emit('push Y')
+
+    buffer.emit(
+      self.compile_expr(
+        node.children[1]
+      )
+    )
+    buffer.emit('push Y')
+
+    buffer.emit('pop X')
+    buffer.emit('pop Y')
+
+    return buffer
+
+  def compile_compare_expr(self, node, *ops, true='1', false='0'):
+    buffer = Buffer()
+
+    ret_label = self.make_label()
+    exit_label = self.make_label()
+
+    buffer.emit(
+      self.compile_binary_expr(
+        node        
+      )
+    )
+
+    for op in ops:
+      buffer.emit(
+        op,
+        ret_label
+      )
+
+    buffer.emit(
+      "mov {} Y",
+      false
+    )
+    buffer.emit(
+      "jmp {}",
+      exit_label
+    )
+
+    buffer.emit(
+      "{}:",
+      ret_label
+    )
+    buffer.emit(
+      "mov {} Y",
+      true
+    )      
+
+    buffer.emit(
+      "{}:",
+      exit_label
+    )
+
+    return buffer
+
+  def compile_expr(self, node):
+    buffer = Buffer()
+
+    if type(node) is lark.Token:
+      if node.type == "NAME":
+        buffer.emit(
+          "mov {} Y",
+          self.compile_literal(node)
+        )
+      elif node.type in ("INTEGER", "FLOAT"):
+        buffer.emit(
+          "mov {} Y",
+          self.compile_literal(node)
+        )
+      elif node.type == "CHAR":
+        buffer.emit(
+          "mov {} Y",
+          self.compile_literal(node)
+        )
+      elif node.type == "STRING":
+        buffer.emit(
+          "ld {} Y",
+          self.make_array(
+            self.compile_literal(node)
+          )
+        )
+      else:
+        raise Exception(f"Not implemented: {node}")
+    elif node.data == "equals":
+      buffer.emit(
+        self.compile_compare_expr(
+          node,
+          "sblez X Y !",
+          "nbnz Y {}",
+          true='1',
+          false='0'
+        )
+      )
+    elif node.data == "not_equals":
+      buffer.emit(
+        self.compile_compare_expr(
+          node,
+          "sblez X Y !",
+          "nbnz Y {}",
+          true='0',
+          false='1'
+        )
+      )
+    elif node.data == "plus":
+      buffer.emit(
+        self.compile_binary_expr(
+          node        
+        )
+      )
+
+      buffer.emit(
+        "ablez X Y !"
+      )
+    elif node.data == "minus":
+      buffer.emit(
+        self.compile_binary_expr(
+          node        
+        )
+      )
+
+      buffer.emit(
+        "sblez X Y !"
+      )
+    elif node.data == "times":
+      buffer.emit(
+        self.compile_binary_expr(
+          node        
+        )
+      )
+
+      buffer.emit(
+        "mbnz X Y !"
+      )
+    elif node.data == "divide":
+      buffer.emit(
+        self.compile_binary_expr(
+          node        
+        )
+      )
+
+      buffer.emit(
+        "vblz X Y !"
+      )
+    elif node.data == "modulo":
+      buffer.emit(
+        self.compile_binary_expr(
+          node        
+        )
+      )
+
+      buffer.emit(
+        "modbz X Y !"
+      )
+    elif node.data == "raise":
+      buffer.emit(
+        self.compile_binary_expr(
+          node        
+        )
+      )
+
+      buffer.emit(
+        "mov 12 _dirBuf"
+      )
+      buffer.emit(
+        "mov X _dirBuf+1"
+      )
+      buffer.emit(
+        "mov Y _dirBuf+2"
+      )
+      buffer.emit(
+        "dir _dirBuf"
+      )
+      buffer.emit(
+        "mov _dirBuf+2 Y"
+      )
+    elif node.data == "or":
+      true_label = self.make_label()
+      exit_label = self.make_label()
+
+      buffer.emit(
+        self.compile_expr(
+          node.children[0]
+        )
+      )
+
+      buffer.emit(
+        "nbnz Y !"
+      )
+      buffer.emit(
+        "nbnz Y {}",
+        true_label
+      )
+
+      buffer.emit(
+        self.compile_expr(
+        node.children[1]
+        )
+      )
+
+      buffer.emit(
+        "jmp {}",
+        exit_label
+      )
+      buffer.emit(
+        "{}:",
+        true_label
+      )
+      buffer.emit(
+        "mov 1 Y"
+      )
+      buffer.emit(
+        "{}:",
+        exit_label
+      )
+    elif node.data == "and":
+      false_label = self.make_label()
+      exit_label = self.make_label()
+
+      buffer.emit(
+        self.compile_expr(
+          node.children[0]
+        )
+      )
+
+      buffer.emit(
+        "nbnz Y {}",
+        false_label
+      )
+
+      buffer.emit(
+        self.compile_expr(
+        node.children[1]
+        )
+      )
+
+      buffer.emit(
+        "jmp {}",
+        exit_label
+      )
+      buffer.emit(
+        "{}:",
+        false_label
+      )
+      buffer.emit(
+        "mov 0 Y"
+      )
+      buffer.emit(
+        "{}:",
+        exit_label
+      )
+    elif node.data == "less":
+      buffer.emit(
+        self.compile_compare_expr(
+          node,
+          "inc Y",
+          "sblez X Y {}"
+        )
+      )
+    elif node.data == "greater":
+      buffer.emit(
+        self.compile_compare_expr(
+          node,
+          "dec Y",
+          "sblez X Y {}",
+          true='0',
+          false='1'
+        )
+      )
+    elif node.data == "less_or_equals":
+      buffer.emit(
+        self.compile_compare_expr(
+          node,
+          "sblez X Y {}"
+        )
+      )
+    elif node.data == "greater_or_equals":
+      buffer.emit(
+        self.compile_compare_expr(
+          node,
+          "inc Y",
+          "sblez X Y {}",
+          true='0',
+          false='1'
+        )
+      )
+    elif node.data == "not":
+      buffer.emit(
+        self.compile_unary_expr(
+          node,
+          "nbnz Y !"
+        )
+      )
+    elif node.data == "deref":
+      buffer.emit(
+        self.compile_unary_expr(
+          node,
+          "la Y Y"
+        )
+      )
+    elif node.data == "index":
+      buffer.emit(
+        self.compile_binary_expr(
+          node        
+        )
+      )
+
+      buffer.emit(
+        "ablez X Y !"
+      )
+      buffer.emit(
+        "la Y Y"
+      )
+    elif node.data == "negate":
+      buffer.emit(
+        self.compile_unary_expr(
+          node,
+          "mov Y X",
+          "sblez X Y !",
+          "sblez X Y !",
+        )
+      )
+    elif node.data == "array":
+      buffer.emit(
+        "ld {} Y",
+        self.make_array(
+          self.compile_literal(node)
+        )
+      )   
+    elif node.data == "inc":
+      name = self.scope[node.children[0].value]
+
+      self.record_usage(name)
+
+      buffer.emit(
+        "mov {} Y",
+        name
+      )
+      buffer.emit(
+        "inc {}",
+        name
+      )     
+    elif node.data == "dec":
+      name = self.scope[node.children[0].value]
+
+      self.record_usage(name)
+
+      buffer.emit(
+        "mov {} Y"
+      )
+      buffer.emit(
+        "dec {}",
+        name
+      )     
+    elif node.data == "rinc":
+      name = self.scope[node.children[0].value]
+
+      self.record_usage(name)
+
+      buffer.emit(
+        "inc {}",
+        name
+      )  
+      buffer.emit(
+        "mov {} Y",
+        name
+      )  
+    elif node.data == "rdec":
+      name = self.scope[node.children[0].value]
+
+      self.record_usage(name)
+
+      buffer.emit(
+        "dec {}",
+        name
+      )  
+      buffer.emit(
+        "mov {} Y"
+      )
+    elif node.data == "funcall":
+      buffer.emit(
+        self.compile_funcall(node, dest='Y')
+      )
+    else:
+      raise Exception(f"Not implemented: {node}")
+
+    return buffer
+
+  def compile_funcall(self, node, dest='Y'):
+    buffer = Buffer()
+
+    name = node.children[0].value
+
+    if name not in self.funcs:
+      raise Exception(f"Call to an undeclared function: '{name}`.")
+
+    if self.funcs[name].argc != len(node.children[1].children):
+      raise Exception(f"Function '{name}` expects {self.funcs[name].argc} arguments, but got {node.children[1].children}.")
+
+    for arg in node.children[1].children[::-1]:
+      buffer.emit(
+        self.compile_expr(arg)
+      )
+      buffer.emit(
+        "push Y"
+      )
+
+    buffer.emit(
+      "jw __{}",
+      name
+    )
+    buffer.emit(
+      "pop {}",
+      dest
+    )
+
+    self.record_usage(name)
+
+    return buffer
+
+  def compile_asm(self, node):
+    buffer = Buffer()
+
+    table = {}
+    for name, renamed in self.scope:
+      table[name] = renamed
+
+    for child in node.children:
+      value = parse_escape(child.value[1:-1])
+
+      for name in table:
+        if f"{{{name}}}" in value:
+          self.record_usage(table[name])
+
+      try:
+        value = value.format(
+          **table
+        )
+      except:
+        raise Exception("Malformed asm directive.")
+
+      buffer.emit(value)
+
+    return buffer
+
+  def compile_arrdec(self, node):
+    buffer = Buffer()
+
+    name = node.children[0].value
+
+    if name in self.scope and len(node.children) == 3: # Index assignment.
+      name = self.scope.find(name)
+
+      self.record_usage(name)
+
+      buffer.emit(
+        self.compile_expr(
+          node.children[1]
+        )
+      )
+
+      buffer.emit(
+        "mov {} X",
+        name
+      )
+      buffer.emit(
+        "ablez X Y !"
+      )
+      buffer.emit(
+        "push Y"
+      )
+
+      buffer.emit(
+        self.compile_expr(
+          node.children[2]
+        )
+      )
+
+      buffer.emit(
+        "pop X"
+      )
+      buffer.emit(
+        "str Y X"
+      )
+
+      return buffer
+
+    if not self.scope.is_toplevel and self.scope.is_local(name):
+      raise Exception(f"Duplicated declaration of a local variable: '{node.children[0].value}`.")
+
+    name = self.scope[name]
+
+    self.record_usage(name)
+
+    count = int(node.children[1].value)
+
+    if count <= 0:
+      raise Exception(f"Illegal array declaration '{node.children[0].value}`: array size should be >0, but it is {count}.")
+
+    if len(node.children) == 3:
+      value = self.compile_literal(node.children[2])
+      size = len(value.split(" ")) # Dirty.
+
+      if size < count:
+        value += f" 0*{count-size}"
+      elif size != count:
+        raise Exception(f"Illegal array declaration '{node.children[0].value}`: value size is {size}, but expected {count}.")
+
+      buffer.emit(
+        "ld {} Y",
+        self.make_array(value)
+      )
+    else:
+      buffer.emit(
+        "ld {} Y",
+        self.make_array(f"0*{count}")
+      )
+
+    buffer.emit(
+      "mov Y {}",
+      name
+    )
+
+    return buffer
+
+  def compile_op(self, node):
+    buffer = Buffer()
+
+    if node.data == "block":
+      buffer.emit(
+        self.compile_block(
+          node
+        )
+      )
+    elif node.data == "label":
+      buffer.emit(
+        "{}:",
+        self.scope.get_label(node.children[0].value)
+      )
+    elif node.data == "goto":
+      buffer.emit(
+        "jmp {}",
+        self.scope.get_label(node.children[0].value)
+      )
+    elif node.data == "varinit":
+      name = node.children[0].value
+
+      if self.scope.is_local(name):
+        raise Exception(f"Duplicated declaration of a local variable: '{name}`.")
+        
+      self.scope.insert(name)
+    elif node.data == "vardec":
+      name = self.scope[node.children[0].value]
+
+      self.record_usage(name)
+
+      buffer.emit(
+        self.compile_expr(node.children[1])
+      )
+      buffer.emit(
+        "mov Y {}",
+        name
+      )
+    elif node.data in ("arrdec", "arrinit"):
+      buffer.emit(
+        self.compile_arrdec(node)
+      )
+    elif node.data in ("inc", "rinc"):
+      name = self.scope[node.children[0].value]
+
+      self.record_usage(name)
+
+      buffer.emit(
+        "inc {}",
+        name
+      )
+    elif node.data == ("dec", "rdec"):
+      name = self.scope[node.children[0].value]
+
+      self.record_usage(name)
+
+      buffer.emit(
+        "dec {}",
+        name
+      )
+    elif node.data == "funcall":
+      buffer.emit(
+        self.compile_funcall(node, dest='ZZ')
+      )
+    elif node.data == "return":
+      buffer.emit(
+        self.compile_expr(
+          node.children[0]
+        )
+      )
+
+      buffer.emit("push Y")
+      buffer.emit("ret")
+    elif node.data == "asm":
+      buffer.emit(
+        self.compile_asm(node)
+      )
+    elif node.data == "if":
+      else_label = self.make_label()
+      exit_label = self.make_label()
+
+      buffer.emit(
+        self.compile_expr(
+          node.children[0]
+        )
+      )
+
+      buffer.emit(
+        "nbnz Y {}",
+        else_label
+      )
+
+      buffer.emit(
+        self.compile_op(
+          node.children[1]
+        )
+      )
+
+      buffer.emit(
+        "jmp {}",
+        exit_label
+      )
+      buffer.emit(
+        "{}:",
+        else_label
+      )
+
+      if len(node.children) == 3:
+        buffer.emit(
+          self.compile_op(
+            node.children[2]
+          )
+        )
+
+      buffer.emit(
+        "{}:",
+        exit_label
+      )
+    elif node.data == "while":
+      loop_label = self.make_label()
+      exit_label = self.make_label()
+
+      buffer.emit(
+        "{}:",
+        loop_label
+      )
+
+      buffer.emit(
+        self.compile_expr(
+          node.children[0]
+        )
+      )
+
+      buffer.emit(
+        "nbnz Y {}",
+        exit_label
+      )
+
+      buffer.emit(
+        self.compile_op(
+          node.children[1]
+        )
+      )
+
+      buffer.emit(
+        "jmp {}",
+        loop_label
+      )
+      buffer.emit(
+        "{}:",
+        exit_label
+      )
+    elif node.data == "for":
+      loop_label = self.make_label()
+      exit_label = self.make_label()
+
+      self.scope.new()
+
+      buffer.emit(
+        self.compile_op(
+          node.children[0]
+        )
+      )
+
+      buffer.emit(
+        "{}:",
+        loop_label
+      )
+
+      buffer.emit(
+        self.compile_expr(
+          node.children[1]
+        )
+      )
+
+      buffer.emit(
+        "nbnz Y {}",
+        exit_label
+      )
+
+      buffer.emit(
+        self.compile_op(
+          node.children[3]
+        )
+      )
+
+      buffer.emit(
+        self.compile_op(
+          node.children[2]
+        )
+      )
+
+      self.scope.leave()
+
+      buffer.emit(
+        "jmp {}",
+        loop_label
+      )
+      buffer.emit(
+        "{}:",
+        exit_label
+      )
+    else:
+      raise Exception(f"Not implemented: {node}")
+
+    return buffer
+
+  def collect_labels(self, node):
+    for child in node.children:
+      if child.data == "label":
+        self.scope.add_label(child.children[0].value)       
+
+  def compile_block(self, node, *prepend_names, scope=True):
+    if scope:
+      self.scope.new()
+
+      for name in prepend_names:
+        self.scope.insert(name)
+
+    buffer = Buffer()
+
+    self.collect_labels(node)
+
+    for child in node.children:
+      buffer.emit(
+        self.compile_op(child)
+      )
+
+    if scope:
+      self.scope.leave()
+
+    return buffer
+
+  def compile_toplevel(self, node):
+    buffer = Buffer()
+
+    if node.data == "funcdef":
+      name = node.children[0].value
+      params = self.funcs[name].params
+
+      buffer.emit("__{}:", name)
+
+      for param in params:
+        buffer.emit(
+          "pop __{}_{}",
+          self.scope.ndx, param
+        )
+
+      self.where = name
+
+      buffer.emit(
+        self.compile_block(
+          node.children[2],
+          *params
+        )
+      )
+
+      buffer.emit(
+        "push Z"
+      )
+      buffer.emit(
+        "ret"
+      )
+    elif node.data == "vardec":
+      name = node.children[0].value
+
+      self.init_buffer.emit(
+        self.compile_expr(node.children[1])
+      )
+      self.init_buffer.emit(
+        "mov Y __0_{}",
+        name
+      )
+
+      self.record_usage(f"__0__{name}")
+    elif node.data in ("arrdec", "arrinit"):
+      self.init_buffer.emit(
+        self.compile_arrdec(node)
+      )
+    elif node.data == "asm":
+      buffer.emit(
+        self.compile_asm(node)
+      )
+    elif node.data == "include":
+      filename = node.children[0].value[1:-1]
+
+      if not os.path.isfile(filename):
+        filename = os.path.join(os.getenv("WC_I"), filename)
+
+      if not os.path.isfile(filename):
+        raise Exception(f"No such file: '{os.path.basename(filename)}`.")
+
+      if filename not in self.included_files:
+        with open(filename, "r") as f:
+          self.compile_program(f.read())
+
+        self.included_files.add(filename)
+    elif node.data == "varinit":
+      pass
+    else:
+      raise Exception(f"Not implemented: {node}")
+
+    return buffer
+
+  def collect_toplevel(self, ast):
+    for node in ast.children:
+      if node.data == "funcdef":
+        name = node.children[0].value
+
+        if name in self.funcs:
+          raise Exception(f"Duplicated function declaration: '{name}`.")
+
+        self.funcs[name] = Func(
+          len(node.children[1].children),
+          tuple(map(lambda t: t.value, node.children[1].children))
+        )
+      elif node.data in ("varinit", "vardec", "arrinit", "arrdec"):
+        name = node.children[0].value
+
+        if name in self.scope:
+          raise Exception(f"Duplicated top-level variable declaration: '{name}`.")
+
+        self.scope.insert(name) # Because we're at the top-level rn.
+
+  def compile_program(self, text):
+    ast = self.parser.parse(text)
+
+    #print(ast.pretty())
+
+    self.collect_toplevel(ast)
+
+    for node in ast.children:
+      buffer = self.compile_toplevel(node)
+
+      if node.data == "funcdef":
+        self.compiled_funcs[node.children[0].value] = buffer
+      else:
+        self.buffer.emit(buffer)
+
+  def compile(self, text):
+    self.compile_program(text)
+
+    for name in self.compiled_funcs:
+      if self.is_used(name):
+        self.buffer.emit(self.compiled_funcs[name])
+
+        for param in self.funcs[name].params:
+          self.record_usage(param)
+
+    self.buffer = self.init_buffer + self.buffer + self.arrays_buffer
+
+    for name in self.scope.names:
+      if self.is_used(name):
+        self.buffer.emit(
+          "{}:0", name
+        )
+
+    if "main" not in self.funcs:
+      raise Exception("Missing 'main` function.")
+
+    return self.buffer.generate() + "\n"
+
+wmc = WMC()
+
+try:
+  if len(sys.argv) == 3:
+    with open(sys.argv[1], "r") as fin:
+      with open(sys.argv[2], "w") as fout:
+        fout.write(wmc.compile(fin.read()))
+  else:
+    sys.stdout.write(wmc.compile(sys.stdin.read()))
+except Exception as e:
+  #__import__('traceback').print_exc()
+
+  print(e)
+
+  sys.exit(1)