// objlib.h -- header file for simple O-O runtime.
#define TAG_NIL 0
#define TAG_INT 1
#define TAG_STR 2
void *int_new(int v);
void *str_new(char *s);
typedef struct {
  int tag, nmethods; void* (**funcs)(); char* *names; int *nargs;
} ty_base;
extern ty_base nil_val;
typedef struct {
  int tag, nmethods; void* (**funcs)(); char* *names; int *nargs;
  int val;
} ty_int;
extern ty_int int_1_val, int_0_val;
typedef struct {
  int tag, nmethods; void* (**funcs)(); char* *names; int *nargs;
  char *str;
} ty_str;
void *send0(void *p, char *name);
void *send1(void *p, char *name, void *q);
int tst(void *p);
// objlib.c --- runtime for simple O-O language.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "objlib.h"
void *nil_to_i(void *p) {
  return &int_0_val;
}
void *nil_to_s(void *p) {
  return str_new("nil");
}
void *nil_print(void *p) {
  printf("nil"); return p;
}
void *nil_println(void *p) {
  printf("nil\n"); return p;
}
char *nil_names[] = {"to_i", "to_s", "print", "println"};
int nil_nargs[] = {0,0,0,0};
void* (*nil_funcs[])() = {nil_to_i, nil_to_s, nil_print, nil_println};

ty_base nil_val = {
  TAG_NIL, sizeof(nil_nargs)/sizeof(int), nil_funcs, nil_names, nil_nargs
};
void *str_to_s(void *p) {
  return p;
}
void *str_to_i(void *p) {
  return int_new(atoi(((ty_str*)p)->str));
}
void *str_print(void *p) {
  printf("%s", ((ty_str*)p)->str); return p;
}
void *str_println(void *p) {
  printf("%s\n", ((ty_str*)p)->str); return p;
}
void *str_add(void *p, void *q) {
  char buf[100];
  sprintf(buf, "%s%s", ((ty_str*)p)->str, ((ty_str*)send0(q, "to_s"))->str);
  return str_new(buf);
}
void *str_prompt(void *p) {
  char buf[100];
  printf(((ty_str*)p)->str); fgets(buf, 100, stdin);
  buf[strlen(buf)-1] = '\0'; return str_new(buf);
}
char *str_names[] = {"to_i", "to_s", "print", "println", "add", "prompt"};
int str_nargs[] = {0,0,0,0,1,0};
void* (*str_funcs[])() = {str_to_i, str_to_s, str_print, str_println,
  str_add, str_prompt};
void *str_new(char *s) {
  ty_str *p = (ty_str*)malloc(sizeof(ty_str));
  p->tag = TAG_STR; p->nmethods = sizeof(str_nargs)/sizeof(int);
  p->funcs = str_funcs; p->names = str_names; p->nargs = str_nargs;
  p->str = (char*)malloc(strlen(s)+1); strcpy(p->str, s);
  return (void*)p;
}
void *int_to_i(void *p) {
  return p;
}
void *int_to_s(void *p) {
  char buf[100];
  sprintf(buf, "%d", ((ty_int*)p)->val); return str_new(buf);
}
void *int_print(void *p) {
  printf("%d", ((ty_int*)p)->val); return p;
}
void *int_println(void *p) {
  printf("%d\n", ((ty_int*)p)->val); return p;
}
#define icalc(name, exp) \
void *name(void *p, void *q) {\
  int x = ((ty_int*)p)->val, y = ((ty_int*)send0(q, "to_i"))->val; \
  return int_new(exp); }
icalc(int_add, x+y)
icalc(int_sub, x-y)
icalc(int_mul, x*y)
icalc(int_div, x/y)
icalc(int_mod, x%y)
#define icmp(name, cmp) \
void *name(void *p, void *q) {\
  int x = ((ty_int*)p)->val, y = ((ty_int*)send0(q, "to_i"))->val; \
  return (cmp) ?  &int_1_val : &int_0_val; }
icmp(int_gt, x>y)  
icmp(int_lt, x<y)  
icmp(int_ge, x>=y)  
icmp(int_le, x<=y)
icmp(int_eq, x==y)
icmp(int_ne, x!=y)
char *int_names[] = {"to_i", "to_s", "print", "println", "add", "sub", 
  "mul", "div", "mod", "gt", "lt", "ge", "le", "eq", "ne"};
int int_nargs[] = {0,0,0,0,1,1,1,1,1,1,1,1,1,1,1};
void* (*int_funcs[])() = {int_to_i, int_to_s, int_print, int_println,
  int_add, int_sub, int_mul, int_div, int_mod, int_gt, int_lt, 
  int_ge, int_le, int_eq, int_ne};
void *int_new(int v) {
  ty_int *p = (ty_int*)malloc(sizeof(ty_int));
  p->tag = TAG_INT; p->nmethods = sizeof(int_nargs)/sizeof(int);
  p->funcs = int_funcs; p->names = int_names; p->nargs = int_nargs;
  p->val = v;
  return (void*)p;
}
ty_int int_1_val = {
  TAG_INT, sizeof(int_nargs)/sizeof(int), int_funcs, int_names, int_nargs, 1
};
ty_int int_0_val = {
  TAG_INT, sizeof(int_nargs)/sizeof(int), int_funcs, int_names, int_nargs, 0
};
void *send0(void *p1, char *name) {
  ty_base *p = (ty_base*)p1;
  for(int i = 0; i < p->nmethods; ++i) {
    if(strcmp(p->names[i], name) == 0) {
      if(p->nargs[i] == 0) { return (p->funcs[i])(p1); }
      fprintf(stderr, "tag %d, name %s, wrong args: 0\n", p->tag, name);
      exit(1);
    }
  }
  fprintf(stderr, "tag %d, name %s, undefined method\n", p->tag, name);
  exit(1);
}
void *send1(void *p1, char *name, void *q1) {
  ty_base *p = (ty_base*)p1;
  for(int i = 0; i < p->nmethods; ++i) {
    if(strcmp(p->names[i], name) == 0) {
      if(p->nargs[i] == 1) { return (p->funcs[i])(p1, q1); }
      fprintf(stderr, "tag %d, name %s, wrong args: 1\n", p->tag, name);
      exit(1);
    }
  }
  fprintf(stderr, "tag %d, name %s, undefined method\n", p->tag, name);
  exit(1);
}
int tst(void *p1) {
  ty_base *p = (ty_base*)p1;
  if(p->tag == TAG_NIL) { return 0; }
  if(p->tag == TAG_INT) { return ((ty_int*)p)->val; }
  return 1;
}
// test1.c --- demonstration for objlib usage.
#include "objlib.h"

int main(void) {
  void* s = str_new("input an integer> ");
  void* n = send0(send0(s, "prompt"), "to_i");
  void* i = send1(n, "sub", &int_1_val);
  while(tst(send1(i, "gt", &int_1_val))) {
    if(tst(send1(send1(n, "mod", i), "eq", &int_0_val))) {
      send0(str_new("not a prime number."), "println");
      return 0;
    }
    i = send1(i, "sub", &int_1_val);
  }
  send0(str_new("a prime number."), "println");
  return 0;
}
Package samd1;

Helpers
  digit = ['0'..'9'] ;
  lcase = ['a'..'z'] ;
  ucase = ['A'..'Z'] ;
  letter = lcase | ucase | '_' ;
  graph = [' '..'~'] ;
  nodq = [graph - '"'] ;

Tokens
  sconst = '"' (nodq | '\' graph)* '"' ;
  iconst = digit+ ;
  blank = (' '|13|10)+ ;
  klass = 'class' ;
  method = 'method' ;
  end = 'end' ;
  var = 'var' ;
  nil = 'nil' ;
  new = 'new' ;
  if = 'if' ;
  while = 'while' ;
  semi = ';' ;
  comma = ',' ;
  dot = '.' ;
  assign = '=' ;
  lbra = '{' ;
  rbra = '}' ;
  lpar = '(' ;
  rpar = ')' ;
  ident = letter (letter|digit)* ;

Ignored Tokens
  blank;

Productions
  prog = {class} cls prog
       | {empty}
       ;
  cls  = {one} klass ident vars meths end
       ;
  vars = {one} ident vars
       | {empty}
       ;
  meths = {one} meth meths
        | {empty}
        ;
  meth = {para} method ident lpar [para]:ident rpar lbra stlist rbra
       | {none} method ident lbra stlist rbra
       ;
  stlist = {stat} stlist stat
         | {empty}
         ;
  stat = {assign}  ident assign expr semi
       | {expr}    expr semi
       | {if}      if lpar expr rpar stat
       | {while}   while lpar expr rpar stat
       | {block}   lbra stlist rbra
       ;
  expr = {fact} fact
       | {send0} expr dot ident
       | {send1} expr dot ident lpar [arg]:expr rpar
       ;
  fact = {iconst} iconst
       | {sconst} sconst
       | {nil}    nil
       | {new}   new ident 
       | {ident}  ident
       | {one}    lpar expr rpar
       ;
package samd1;
import samd1.parser.*;
import samd1.lexer.*;
import samd1.node.*;
import java.io.*;
import java.util.*;

public class SamD1 {
  public static void main(String[] args) throws Exception {
    if(args.length != 3) {
      Log.pError("usage: SamD1 srcfile startclass startmethod.");
      System.exit(1);
    }
    Parser p = new Parser(new Lexer(new PushbackReader(
      new InputStreamReader(new FileInputStream(args[0]), "JISAutoDetect"),
        1024)));
    Start tree = p.parse();
    ObjSymtab st = new ObjSymtab();
    ObjChecker ck = new ObjChecker(st); tree.apply(ck); st.show();
    VarChecker vc = new VarChecker(st); tree.apply(vc); 
    if(Log.getError() > 0) { return; }
    PrintStream hp = new PrintStream("obj.h"); st.emithdr(hp); hp.close();
    PrintStream pr = new PrintStream("obj.c");
    pr.println("#include <stdlib.h>");
    pr.println("#include \"objlib.h\"");
    pr.println("#include \"obj.h\"");
    Generator gen = new Generator(st, pr); tree.apply(gen);
    st.emitbody(pr, args[1], args[2]); pr.close();
  }
}
class Log {
  public static int err = 0;
  public static void pError(String s) { System.out.println(s); ++err; }
  public static int getError() { return err; }
}
package samd1;
import java.util.*;
import java.io.*;

public class ObjSymtab {
  public static int newid = 4;
  public static int IVAR = 1, MVAR = 2, LVAR = 3, UNDEF = 0;
  public Map<String,ClassData> cmap = new TreeMap<String,ClassData>();
  public ClassData cur = null;
  String pnm = null, mnm = null;
  public void enterClass(String n) {
    if(!cmap.containsKey(n)) { cmap.put(n, new ClassData(n)); }
    cur = cmap.get(n);
  }
  public void addIvar(String n) {
    if(!cur.vset.contains(n)) { cur.vset.add(n); return; }
    Log.pError(cur.name + ": dupl var " + n);
  }
  public void addMethod(String m, String p) {
    if(cur.mmap.containsKey(m)) { Log.pError(cur.name+": dupl method "+m); }
    else { cur.mmap.put(m, p); }
  }
  public void addMethod(String m) {
    if(cur.mmap.containsKey(m)) { Log.pError(cur.name+": dupl method "+m); }
    else { cur.mmap.put(m, null); }
  }
  public void enterMethod(String m, String p) { mnm = m; pnm = p; }
  public void enterMethod(String m) { mnm = m; pnm = null; }
  public int vkind(String n) {
    if(pnm != null && pnm.equals(n)) { return LVAR; }
    if(mnm != null && mnm.equals(n)) { return MVAR; }
    if(cur.vset.contains(n)) { return IVAR; }
    return UNDEF;
  }
  public boolean isClass(String n) { return cmap.containsKey(n); }
  public void show() {
    for(String n: cmap.keySet()) { cmap.get(n).show(); }
  }
  public void emithdr(PrintStream pr) {
    for(String n: cmap.keySet()) { cmap.get(n).emithdr(pr); }
  }
  public void emitbody(PrintStream pr, String scls, String smeth) {
    ClassData c = cmap.get(scls);
    if(c == null || !c.mmap.containsKey(smeth) || c.mmap.get(smeth) != null) {
      Log.pError("invalid start class "+scls+" or method "+smeth); return;
    }
    for(String n: cmap.keySet()) { cmap.get(n).emitbody(pr); }
    pr.println("int main(void) {");
    pr.printf(" send0(%s_new(), \"%s\"); return 0; }\n", scls, smeth);
  }
  static class ClassData {
    public String name;
    public int id = newid++;
    public Map<String,String> mmap = new TreeMap<String,String>();
    public Set<String> vset = new TreeSet<String>();
    public ClassData(String n) { name = n; }
    public void show() {
      System.out.println(name + ":");
      for(String v: vset) { System.out.print(" " + v); }
      System.out.println();
      for(String k: mmap.keySet()) {
        String v = mmap.get(k);
        if(v == null) { System.out.printf(" %s()\n", k); }
        else          { System.out.printf(" %s(%s)\n", k, v); }
      }
    }
    public void emithdr(PrintStream pr) {
      pr.println("typedef struct {");
      pr.println(" int tag, nmethods; void* (**funcs)();");
      pr.println(" char* *names; int *nargs;");
      for(String v: vset) { pr.println(" void *_"+v+";"); }
      pr.println("} ty_"+name+";");
      pr.println("void *"+name+"_new();");
    }
    public void emitbody(PrintStream pr) {
      String n = name;
      pr.printf("char *%s_names[] = {", n);
      for(String m: mmap.keySet()) { pr.printf("\"%s\",", m); }
      pr.println("};");
      pr.printf("int %s_nargs[] = {", n);
      for(String m: mmap.keySet()) { pr.print(mmap.get(m) == null ? "0,":"1,"); }
      pr.println("};");
      pr.printf("void* (*%s_funcs[])() = {", n);
      for(String m: mmap.keySet()) { pr.printf("%s_%s,", n, m); }
      pr.println("};");
      pr.printf("void *%s_new() {\n", n);
      pr.printf(" ty_%s *p = (ty_%s*)malloc(sizeof(ty_%s));\n", n, n, n);
      pr.printf(" p->tag=%d; p->nmethods=sizeof(%s_names)/sizeof(int);\n", id, n);
      pr.printf(" p->funcs=%s_funcs; p->names=%s_names; p->nargs=%s_nargs;\n",
                  n, n, n);
      for(String v: vset) { pr.printf(" p->_%s=&nil_val;\n", v); }
      pr.println(" return (void*)p; }");
    }
  }
}
package samd1;
import samd1.analysis.*;
import samd1.node.*;

public class ObjChecker extends DepthFirstAdapter {
  ObjSymtab st;
  public ObjChecker(ObjSymtab st1) { st = st1; }
  @Override
  public void inAOneCls(AOneCls node) {
    st.enterClass(node.getIdent().getText());
  }
  @Override
  public void outAOneVars(AOneVars node) {
    st.addIvar(node.getIdent().getText());
  }
  @Override
  public void inAParaMeth(AParaMeth node) {
    st.addMethod(node.getIdent().getText(), node.getPara().getText());
  }
  @Override
  public void inANoneMeth(ANoneMeth node) {
    st.addMethod(node.getIdent().getText());
  }
}
package samd1;
import samd1.analysis.*;
import samd1.node.*;

public class VarChecker extends DepthFirstAdapter {
  ObjSymtab st;
  public VarChecker(ObjSymtab st1) { st = st1; }
  @Override
  public void inAOneCls(AOneCls node) {
    st.enterClass(node.getIdent().getText());
  }
  @Override
  public void inAParaMeth(AParaMeth node) {
    st.enterMethod(node.getIdent().getText(), node.getPara().getText());
  }
  @Override
  public void inANoneMeth(ANoneMeth node) {
    st.enterMethod(node.getIdent().getText());
  }
  @Override
  public void outAAssignStat(AAssignStat node) {
    if(st.vkind(node.getIdent().getText()) != ObjSymtab.UNDEF) { return; }
    Log.pError("undefined var: " + node.getIdent().getText());
  }
  @Override
  public void outAIdentFact(AIdentFact node) {
    if(st.vkind(node.getIdent().getText()) != ObjSymtab.UNDEF) { return; }
    Log.pError("undefined var: " + node.getIdent().getText());
  }
  @Override
  public void outANewFact(ANewFact node) {
    if(st.isClass(node.getIdent().getText())) { return; }
    Log.pError("undefined class in new: " + node.getIdent().getText());
  }
}
package samd1;
import samd1.analysis.*;
import samd1.node.*;
import java.io.*;

public class Generator extends DepthFirstAdapter {
  ObjSymtab st;
  PrintStream pr;
  String cname, mname, pname;
  public Generator(ObjSymtab s, PrintStream p) { st = s; pr = p; }
  @Override
  public void inAOneCls(AOneCls node) {
    cname = node.getIdent().getText(); st.enterClass(cname);
  }
  @Override
  public void inAParaMeth(AParaMeth node) {
    pname = node.getPara().getText(); mname = node.getIdent().getText();
    pr.printf("void *%s_%s(void *_self, void *%s) {\n", cname, mname, pname);
    pr.printf(" ty_%s *self = (ty_%s*)_self;\n", cname, cname);
    pr.printf(" void *%s = &nil_val;\n", mname);
  }
  @Override
  public void outAParaMeth(AParaMeth node) {
    pr.printf(" return %s; }\n", mname);
  }
  @Override
  public void inANoneMeth(ANoneMeth node) {
    pname = ""; mname = node.getIdent().getText();
    pr.printf("void *%s_%s(void *_self) {\n", cname, mname);
    pr.printf(" ty_%s *self = (ty_%s*)_self;\n", cname, cname);
    pr.printf(" void *%s = &nil_val;\n", mname);
  }
  @Override
  public void outANoneMeth(ANoneMeth node) {
    pr.printf(" return %s; }\n", mname);
  }
  @Override
  public void outAAssignStat(AAssignStat node) {
    String v = node.getIdent().getText(), e = (String)getOut(node.getExpr());
    if(st.vkind(v) == ObjSymtab.IVAR) { v = "self->_" + v; }
    pr.printf(" %s = %s;\n", v, e);
  }
  @Override
  public void outAExprStat(AExprStat node) {
    pr.printf(" %s;\n", ((String)getOut(node.getExpr())));
  }
  @Override
  public void caseAIfStat(AIfStat node) {
    node.getExpr().apply(this); String e = (String)getOut(node.getExpr());
    pr.printf(" if(tst(%s)) {\n", e);
    node.getStat().apply(this);
    pr.println(" }");
  }
  @Override
  public void caseAWhileStat(AWhileStat node) {
    node.getExpr().apply(this); String e = (String)getOut(node.getExpr());
    pr.printf(" while(tst(%s)) {\n", e);
    node.getStat().apply(this);
    pr.println(" }");
  }
  @Override
  public void outAFactExpr(AFactExpr node) {
    setOut(node, getOut(node.getFact()));
  }
  @Override
  public void outASend0Expr(ASend0Expr node) {
    setOut(node, String.format("send0(%s, \"%s\")",
            (String)getOut(node.getExpr()), node.getIdent().getText()));
  }
  @Override
  public void outASend1Expr(ASend1Expr node) {
    setOut(node, String.format("send1(%s, \"%s\", %s)",
            (String)getOut(node.getExpr()), node.getIdent().getText(),
            (String)getOut(node.getArg())));
  }
  @Override
  public void outAIconstFact(AIconstFact node) {
    setOut(node, String.format("int_new(%s)", node.getIconst().getText()));
  }
  @Override
  public void outASconstFact(ASconstFact node) {
    setOut(node, String.format("str_new(%s)", node.getSconst().getText()));
  }
  @Override
  public void outANilFact(ANilFact node) {
    setOut(node, "&nil_val");
  }
  @Override
  public void outANewFact(ANewFact node) {
    setOut(node, String.format("%s_new()", node.getIdent().getText()));
  }
  @Override
  public void outAIdentFact(AIdentFact node) {
    String v = node.getIdent().getText(); 
    if(st.vkind(v) == ObjSymtab.IVAR) { v = "self->_" + v; }
    setOut(node, v);
  }
  @Override
  public void outAOneFact(AOneFact node) {
    setOut(node, getOut(node.getExpr()));
  }
}
class main
  acc v
  method start {
    acc = new myclass;
    v = "enter number or '0'> ".prompt.to_i;
    while(v.ne(0)) {
      acc.put(v);
      v = "enter number or '0'> ".prompt.to_i;
    }
    "total = ".add(acc.get).println;
  }
end
class myclass
  mem
  method put(v) { mem = v.add(mem); }
  method get { get = mem; }
end
// obj.h --- header to be included for SamD1 compiler.
typedef struct {
 int tag, nmethods; void* (**funcs)();
 char* *names; int *nargs;
 void *_acc;
 void *_v;
} ty_main;
void *main_new();
typedef struct {
 int tag, nmethods; void* (**funcs)();
 char* *names; int *nargs;
 void *_mem;
} ty_myclass;
void *myclass_new();
#include <stdlib.h>
#include "objlib.h"
#include "obj.h"
void *main_start(void *_self) {
 ty_main *self = (ty_main*)_self;
 void *start = &nil_val;
 self->_acc = myclass_new();
 self->_v = send0(send0(str_new("enter number or '0'> "), "prompt"), "to_i");
 while(tst(send1(self->_v, "ne", int_new(0)))) {
 send1(self->_acc, "put", self->_v);
 self->_v = send0(send0(str_new("enter number or '0'> "), "prompt"), "to_i");
 }
 send0(send1(str_new("total = "), "add", send0(self->_acc, "get")), "println");
 return start; }
void *myclass_put(void *_self, void *v) {
 ty_myclass *self = (ty_myclass*)_self;
 void *put = &nil_val;
 self->_mem = send1(v, "add", self->_mem);
 return put; }
void *myclass_get(void *_self) {
 ty_myclass *self = (ty_myclass*)_self;
 void *get = &nil_val;
 get = self->_mem;
 return get; }
char *main_names[] = {"start",};
int main_nargs[] = {0,};
void* (*main_funcs[])() = {main_start,};
void *main_new() {
 ty_main *p = (ty_main*)malloc(sizeof(ty_main));
 p->tag=4; p->nmethods=sizeof(main_names)/sizeof(int);
 p->funcs=main_funcs; p->names=main_names; p->nargs=main_nargs;
 p->_acc=&nil_val;
 p->_v=&nil_val;
 return (void*)p; }
char *myclass_names[] = {"get","put",};
int myclass_nargs[] = {0,1,};
void* (*myclass_funcs[])() = {myclass_get,myclass_put,};
void *myclass_new() {
 ty_myclass *p = (ty_myclass*)malloc(sizeof(ty_myclass));
 p->tag=5; p->nmethods=sizeof(myclass_names)/sizeof(int);
 p->funcs=myclass_funcs; p->names=myclass_names; p->nargs=myclass_nargs;
 p->_mem=&nil_val;
 return (void*)p; }
int main(void) {
 send0(main_new(), "start"); return 0; }