// 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; }