init
This commit is contained in:
32
wcmtools/s2/danga/s2/Backend.java
Executable file
32
wcmtools/s2/danga/s2/Backend.java
Executable file
@@ -0,0 +1,32 @@
|
||||
package danga.s2;
|
||||
|
||||
public abstract class Backend {
|
||||
|
||||
Layer layer;
|
||||
|
||||
public abstract void output (Output o);
|
||||
|
||||
public static String quoteString (String s)
|
||||
{
|
||||
int len = s.length();
|
||||
StringBuffer sb = new StringBuffer(len + 20);
|
||||
sb.append("\"");
|
||||
sb.append(quoteStringInner(s));
|
||||
sb.append("\"");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String quoteStringInner (String s)
|
||||
{
|
||||
int len = s.length();
|
||||
StringBuffer sb = new StringBuffer(len + 20);
|
||||
for (int i=0; i<len; i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c=='\\' || c=='$' || c=='"')
|
||||
sb.append('\\');
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
};
|
||||
69
wcmtools/s2/danga/s2/BackendHTML.java
Executable file
69
wcmtools/s2/danga/s2/BackendHTML.java
Executable file
@@ -0,0 +1,69 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
public class BackendHTML extends Backend {
|
||||
|
||||
public final static boolean addBreaks = false;
|
||||
public final static String CommentColor = new String("#008000");
|
||||
public final static String IdentColor = new String("#000000");
|
||||
public final static String KeywordColor = new String("#0000FF");
|
||||
public final static String StringColor = new String("#008080");
|
||||
public final static String PunctColor = new String("#000000");
|
||||
public final static String BracketColor = new String("#800080");
|
||||
public final static String TypeColor = new String("#000080");
|
||||
public final static String VarColor = new String("#000000");
|
||||
public final static String IntegerColor = new String("#000000");
|
||||
|
||||
public BackendHTML (Layer l) {
|
||||
layer = l;
|
||||
}
|
||||
|
||||
public void output (Output o) {
|
||||
String layername = (s2compile.topLayerName == null ?
|
||||
"untitled layer" :
|
||||
s2compile.topLayerName);
|
||||
|
||||
o.write("<html><head><title>Source for "+layername+"</title>\n");
|
||||
o.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n");
|
||||
o.write("<style type=\"text/css\">\n");
|
||||
o.write("body { background: #ffffff none; color: #000000; }\n");
|
||||
o.write(".c { background: #ffffff none; color: "+CommentColor+"; }\n");
|
||||
o.write(".i { background: #ffffff none; color: "+IdentColor+"; }\n");
|
||||
o.write(".k { background: #ffffff none; color: "+KeywordColor+"; }\n");
|
||||
o.write(".s { background: #ffffff none; color: "+StringColor+"; }\n");
|
||||
o.write(".p { background: #ffffff none; color: "+PunctColor+"; }\n");
|
||||
o.write(".b { background: #ffffff none; color: "+BracketColor+"; }\n");
|
||||
o.write(".t { background: #ffffff none; color: "+TypeColor+"; }\n");
|
||||
o.write(".v { background: #ffffff none; color: "+VarColor+"; }\n");
|
||||
o.write(".n { background: #ffffff none; color: "+IntegerColor+"; }\n");
|
||||
o.write("</style></head>\n<body>\n<pre>");
|
||||
LinkedList nodes = layer.getNodes();
|
||||
ListIterator li = nodes.listIterator();
|
||||
while (li.hasNext()) {
|
||||
Node n = (Node) li.next();
|
||||
n.asHTML(o);
|
||||
}
|
||||
o.write("</pre></body></html>"); o.newline();
|
||||
}
|
||||
|
||||
public static String quoteHTML (String s)
|
||||
{
|
||||
int len = s.length();
|
||||
StringBuffer sb = new StringBuffer(len + len / 10);
|
||||
for (int i=0; i<len; i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c=='<')
|
||||
sb.append("<");
|
||||
else if (c=='>')
|
||||
sb.append(">");
|
||||
else if (c=='&')
|
||||
sb.append("&");
|
||||
else
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
};
|
||||
67
wcmtools/s2/danga/s2/BackendPerl.java
Executable file
67
wcmtools/s2/danga/s2/BackendPerl.java
Executable file
@@ -0,0 +1,67 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
public class BackendPerl extends Backend {
|
||||
|
||||
int layerID;
|
||||
|
||||
public BackendPerl (Layer l, int layerID) {
|
||||
layer = l;
|
||||
this.layerID = layerID;
|
||||
}
|
||||
|
||||
public int getLayerID () { return layerID; }
|
||||
public String getLayerIDString () {
|
||||
return (new Integer(layerID)).toString();
|
||||
}
|
||||
|
||||
public void output (Output o) {
|
||||
Indenter io = new Indenter(o, 4);
|
||||
io.writeln("#!/usr/bin/perl");
|
||||
io.writeln("# auto-generated Perl code from input S2 code");
|
||||
io.writeln("package S2;");
|
||||
io.writeln("use strict;");
|
||||
io.writeln("use constant VTABLE => 0;");
|
||||
io.writeln("use constant STATIC => 1;");
|
||||
io.writeln("use constant PROPS => 2;");
|
||||
io.writeln("register_layer("+layerID+");");
|
||||
LinkedList nodes = layer.getNodes();
|
||||
ListIterator li = nodes.listIterator();
|
||||
while (li.hasNext()) {
|
||||
Node n = (Node) li.next();
|
||||
n.asPerl(this, io);
|
||||
}
|
||||
io.writeln("1;");
|
||||
io.writeln("# end.");
|
||||
}
|
||||
|
||||
public static String quoteString (String s)
|
||||
{
|
||||
int len = s.length();
|
||||
StringBuffer sb = new StringBuffer(len + 20);
|
||||
sb.append("\"");
|
||||
sb.append(quoteStringInner(s));
|
||||
sb.append("\"");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String quoteStringInner (String s)
|
||||
{
|
||||
int len = s.length();
|
||||
StringBuffer sb = new StringBuffer(len + 20);
|
||||
for (int i=0; i<len; i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c=='\n') {
|
||||
sb.append("\\n");
|
||||
} else {
|
||||
if (c=='\\' || c=='$' || c=='"' || c=='@')
|
||||
sb.append('\\');
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
};
|
||||
29
wcmtools/s2/danga/s2/BackendS2.java
Executable file
29
wcmtools/s2/danga/s2/BackendS2.java
Executable file
@@ -0,0 +1,29 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
public class BackendS2 extends Backend {
|
||||
|
||||
private final static boolean MANY_QUOTES = false;
|
||||
|
||||
public BackendS2 (Layer l) {
|
||||
layer = l;
|
||||
}
|
||||
|
||||
public void output (Output o)
|
||||
{
|
||||
Indenter io = new Indenter(o, 4);
|
||||
io.writeln("# auto-generated S2 code from input S2 code");
|
||||
LinkedList nodes = layer.getNodes();
|
||||
ListIterator li = nodes.listIterator();
|
||||
while (li.hasNext()) {
|
||||
Node n = (Node) li.next();
|
||||
n.asS2(io);
|
||||
}
|
||||
}
|
||||
|
||||
public static void LParen (Indenter o) { if (MANY_QUOTES) o.write("("); }
|
||||
public static void RParen (Indenter o) { if (MANY_QUOTES) o.write(")"); }
|
||||
|
||||
};
|
||||
19
wcmtools/s2/danga/s2/BufferedIndenter.java
Executable file
19
wcmtools/s2/danga/s2/BufferedIndenter.java
Executable file
@@ -0,0 +1,19 @@
|
||||
package danga.s2;
|
||||
|
||||
// NOTE: wrote this, used it for awhile, then decided not to use it.
|
||||
// it works, but it'll probably bit rot.
|
||||
|
||||
public class BufferedIndenter extends Indenter
|
||||
{
|
||||
public BufferedIndenter (Indenter i) {
|
||||
depth = i.depth;
|
||||
tabsize = i.tabsize;
|
||||
makeSpaces();
|
||||
o = new OutputStringBuffer();
|
||||
}
|
||||
|
||||
public void writeTo (Indenter i) {
|
||||
OutputStringBuffer osb = (OutputStringBuffer) o;
|
||||
osb.writeTo(i);
|
||||
}
|
||||
}
|
||||
367
wcmtools/s2/danga/s2/Checker.java
Executable file
367
wcmtools/s2/danga/s2/Checker.java
Executable file
@@ -0,0 +1,367 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.ListIterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Set;
|
||||
import java.util.Collections;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class Checker
|
||||
{
|
||||
// combined (all layers)
|
||||
private Hashtable classes; // class name -> NodeClass
|
||||
private Hashtable props; // property name -> Type
|
||||
private Hashtable funcs; // FuncID -> return type
|
||||
private Hashtable funcBuiltin; // FuncID -> Boolean (is builtin)
|
||||
private LinkedList localblocks; // NodeStmtBlock scopes .. last is deepest (closest)
|
||||
private Type returnType;
|
||||
private String funcClass; // current function class
|
||||
private Hashtable derclass; // classname -> LinkedList<classname>
|
||||
private boolean inFunction; // checking in a function now?
|
||||
|
||||
// per-layer
|
||||
private Hashtable funcDist; // FuncID -> [ distance, NodeFunction ]
|
||||
private Hashtable funcIDs; // NodeFunction -> Set<FuncID>
|
||||
private boolean hitFunction; // true once a function has been declared/defined
|
||||
|
||||
// per function
|
||||
private int funcNum = 0;
|
||||
private Hashtable funcNums; // FuncID -> Integer(funcnum)
|
||||
private LinkedList funcNames; // Strings
|
||||
|
||||
public Checker ()
|
||||
{
|
||||
classes = new Hashtable();
|
||||
props = new Hashtable();
|
||||
funcs = new Hashtable();
|
||||
funcBuiltin = new Hashtable();
|
||||
derclass = new Hashtable();
|
||||
localblocks = new LinkedList();
|
||||
}
|
||||
|
||||
// class functions
|
||||
public void addClass (String name, NodeClass nc) {
|
||||
classes.put(name, nc);
|
||||
|
||||
// make sure that the list of classes that derive from this
|
||||
// one exists.
|
||||
if (derclass.get(name) == null) {
|
||||
derclass.put(name, new LinkedList());
|
||||
}
|
||||
|
||||
// and if this class derives from another, add ourselves
|
||||
// to that list.
|
||||
String parent = nc.getParentName();
|
||||
if (parent != null) {
|
||||
LinkedList l = (LinkedList) derclass.get(parent);
|
||||
l.add(name);
|
||||
}
|
||||
}
|
||||
public NodeClass getClass (String name) {
|
||||
if (name == null) return null;
|
||||
return (NodeClass) classes.get(name);
|
||||
}
|
||||
|
||||
public String getParentClassName (String name) {
|
||||
NodeClass nc = getClass(name);
|
||||
if (nc == null) return null;
|
||||
return nc.getParentName();
|
||||
}
|
||||
|
||||
public boolean isValidType (Type t) {
|
||||
if (t == null) return false;
|
||||
if (t.isPrimitive()) return true;
|
||||
if (getClass(t.baseType()) != null) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// property functions
|
||||
public void addProperty (String name, Type t) {
|
||||
props.put(name, t);
|
||||
}
|
||||
public Type propertyType (String name) {
|
||||
return (Type) props.get(name);
|
||||
}
|
||||
|
||||
// return type of null means no return type.
|
||||
public void setReturnType (Type t) {
|
||||
returnType = t;
|
||||
}
|
||||
public Type getReturnType () {
|
||||
return returnType;
|
||||
}
|
||||
|
||||
// function functions
|
||||
public void addFunction (String funcid, Type t, boolean builtin)
|
||||
throws Exception
|
||||
{
|
||||
// make sure function doesn't mask a lower one with a different type
|
||||
Type existing = functionType(funcid);
|
||||
if (existing != null && ! existing.equals(t)) {
|
||||
throw new Exception("Can't override function '" + funcid + "' with new "+
|
||||
"return type.");
|
||||
}
|
||||
funcs.put(funcid, t);
|
||||
funcBuiltin.put(funcid, new Boolean(builtin));
|
||||
}
|
||||
public Type functionType (String funcid) {
|
||||
return (Type) funcs.get(funcid);
|
||||
}
|
||||
public boolean isFuncBuiltin (String funcid) {
|
||||
Boolean b = (Boolean)funcBuiltin.get(funcid);
|
||||
return b == null ? false : b.booleanValue();
|
||||
}
|
||||
|
||||
// returns true if there's a string -> t class constructor
|
||||
public boolean isStringCtor (Type t) {
|
||||
if (t == null) return false;
|
||||
if (! t.isSimple()) return false;
|
||||
String cname = t.baseType();
|
||||
String ctorid = cname+"::"+cname+"(string)";
|
||||
Type rt = functionType(ctorid);
|
||||
if (rt == null || ! rt.isSimple() || ! rt.baseType().equals(cname) ||
|
||||
! isFuncBuiltin(ctorid))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// setting/getting the current function class we're in
|
||||
public void setCurrentFunctionClass (String f) {
|
||||
funcClass = f;
|
||||
}
|
||||
public String getCurrentFunctionClass () {
|
||||
return funcClass;
|
||||
}
|
||||
|
||||
// setting/getting whether in a function now
|
||||
public void setInFunction (boolean in) {
|
||||
inFunction = in;
|
||||
}
|
||||
public boolean getInFunction () {
|
||||
return inFunction;
|
||||
}
|
||||
|
||||
// variable lookup
|
||||
public void pushLocalBlock (NodeStmtBlock nb) {
|
||||
localblocks.addLast(nb);
|
||||
}
|
||||
public void popLocalBlock () {
|
||||
localblocks.removeLast();
|
||||
}
|
||||
public NodeStmtBlock getLocalScope () {
|
||||
if (localblocks.size() == 0)
|
||||
return null;
|
||||
return (NodeStmtBlock) localblocks.getLast();
|
||||
}
|
||||
|
||||
public Type localType (String local)
|
||||
{
|
||||
if (localblocks.size() == 0)
|
||||
return null;
|
||||
|
||||
ListIterator li = localblocks.listIterator(localblocks.size());
|
||||
while (li.hasPrevious()) {
|
||||
NodeStmtBlock nb = (NodeStmtBlock) li.previous();
|
||||
Type t = nb.getLocalVar(local);
|
||||
if (t != null)
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public Type memberType (String clas, String member)
|
||||
{
|
||||
NodeClass nc = getClass(clas);
|
||||
if (nc == null) return null;
|
||||
return nc.getMemberType(member);
|
||||
}
|
||||
|
||||
public void setHitFunction (boolean b) {
|
||||
hitFunction = b;
|
||||
}
|
||||
public boolean getHitFunction () {
|
||||
return hitFunction;
|
||||
}
|
||||
|
||||
public boolean hasDerClasses (String clas) {
|
||||
LinkedList l = (LinkedList) derclass.get(clas);
|
||||
return l.size() > 0;
|
||||
}
|
||||
|
||||
public ListIterator getDerClassesIter (String clas) {
|
||||
LinkedList l = (LinkedList) derclass.get(clas);
|
||||
return l.listIterator();
|
||||
}
|
||||
|
||||
public void setFuncDistance (String funcID, DerItem df)
|
||||
{
|
||||
//System.err.println("setFuncDistance(\""+funcID+"\", "+df+")");
|
||||
|
||||
DerItem existing = (DerItem) funcDist.get(funcID);
|
||||
if (existing == null || df.dist < existing.dist) {
|
||||
funcDist.put(funcID, df);
|
||||
|
||||
///// keep the funcIDs hashes -> FuncID set up-to-date
|
||||
// removing the existing funcID from the old set first
|
||||
if (existing != null) {
|
||||
Set oldset = (Set) funcIDs.get(existing.nf);
|
||||
oldset.remove(funcID);
|
||||
}
|
||||
|
||||
// first, make sure the set exists
|
||||
Set idset = (Set) funcIDs.get(df.nf);
|
||||
if (idset == null) {
|
||||
idset = Collections.synchronizedSortedSet(new TreeSet());
|
||||
funcIDs.put(df.nf, idset);
|
||||
}
|
||||
|
||||
// now, insert this funcID for this function.
|
||||
idset.add(funcID);
|
||||
}
|
||||
}
|
||||
|
||||
public Iterator getFuncIDsIter (NodeFunction nf)
|
||||
{
|
||||
Set s = (Set) funcIDs.get(nf);
|
||||
if (s == null) {
|
||||
System.err.println("WARN: no funcids for nf="+nf);
|
||||
return null;
|
||||
}
|
||||
return s.iterator();
|
||||
}
|
||||
|
||||
// per function
|
||||
public void resetFunctionNums () {
|
||||
funcNum = 0;
|
||||
funcNums = new Hashtable();
|
||||
funcNames = new LinkedList();
|
||||
}
|
||||
public int functionNum (String funcID) {
|
||||
Integer num = (Integer) funcNums.get(funcID);
|
||||
if (num == null) {
|
||||
num = new Integer(++funcNum);
|
||||
funcNums.put(funcID, num);
|
||||
funcNames.add(funcID);
|
||||
}
|
||||
return num.intValue();
|
||||
}
|
||||
public Hashtable getFuncNums () {
|
||||
return funcNums;
|
||||
}
|
||||
public LinkedList getFuncNames () {
|
||||
return funcNames;
|
||||
}
|
||||
|
||||
// check if type 't' is a subclass of 'w'
|
||||
public boolean typeIsa (Type t, Type w)
|
||||
{
|
||||
if (! Type.sameMods(t, w))
|
||||
return false;
|
||||
|
||||
String is = t.baseType();
|
||||
String parent = w.baseType();
|
||||
while (is != null) {
|
||||
if (is.equals(parent))
|
||||
return true;
|
||||
|
||||
NodeClass nc = getClass(is);
|
||||
is = nc != null ? nc.getParentName() : null;
|
||||
};
|
||||
return false ;
|
||||
}
|
||||
|
||||
// check to see if a class or parents has a
|
||||
// "toString()" method
|
||||
public boolean classHasToString (String clas) {
|
||||
Type et = functionType(clas+"::toString()");
|
||||
if (et != null && et.equals(Type.STRING))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// check to see if a class or parents has an
|
||||
// "as_string" string member
|
||||
public boolean classHasAsString (String clas) {
|
||||
Type et = memberType(clas, "as_string");
|
||||
if (et != null && et.equals(Type.STRING))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------
|
||||
|
||||
public void checkLayer (Layer lay) throws Exception
|
||||
{
|
||||
// initialize layer-specific data structures
|
||||
funcDist = new Hashtable();
|
||||
funcIDs = new Hashtable();
|
||||
hitFunction = false;
|
||||
|
||||
// check to see that they declared the layer type, and that
|
||||
// it isn't bogus.
|
||||
{
|
||||
// what the S2 source says the layer is
|
||||
String dtype = lay.getDeclaredType();
|
||||
if (dtype == null)
|
||||
throw new Exception("Layer type not declared.");
|
||||
|
||||
// what type s2compile thinks it is
|
||||
String type = lay.getType();
|
||||
|
||||
if (! dtype.equals(type)) {
|
||||
throw new Exception("Layer is declared " + dtype +
|
||||
" but expecting a "+type+" layer.");
|
||||
}
|
||||
|
||||
// now that we've validated their type is okay
|
||||
lay.setType(dtype);
|
||||
}
|
||||
|
||||
LinkedList nodes = lay.getNodes();
|
||||
ListIterator li = nodes.listIterator();
|
||||
while (li.hasNext()) {
|
||||
Node n = (Node) li.next();
|
||||
n.check(lay, this);
|
||||
}
|
||||
|
||||
if (lay.getType().equals("core")) {
|
||||
String mv = lay.getLayerInfo("majorversion");
|
||||
if (mv == null) {
|
||||
throw new Exception("Core layers must declare 'majorversion' layerinfo.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// static stuff
|
||||
|
||||
// returns the signature of a function and its arguments, in the form
|
||||
// of: Classname::funcName(String,UserPage,int)
|
||||
public static String functionID (String clas, String func, Object o)
|
||||
{
|
||||
StringBuffer sb = new StringBuffer(70);
|
||||
|
||||
if (clas != null) {
|
||||
sb.append(clas); sb.append("::");
|
||||
}
|
||||
sb.append(func);
|
||||
sb.append("(");
|
||||
|
||||
// where Object can be a NodeFormals or FIXME: other stuff
|
||||
if (o == null) {
|
||||
// do nothing.
|
||||
} else if (o instanceof NodeFormals) {
|
||||
NodeFormals nf = (NodeFormals) o;
|
||||
sb.append(nf.typeList());
|
||||
} else if (o instanceof String) {
|
||||
String s = (String) o;
|
||||
sb.append(s);
|
||||
} else {
|
||||
sb.append("[-----]");
|
||||
}
|
||||
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
22
wcmtools/s2/danga/s2/DerItem.java
Executable file
22
wcmtools/s2/danga/s2/DerItem.java
Executable file
@@ -0,0 +1,22 @@
|
||||
package danga.s2;
|
||||
|
||||
public class DerItem
|
||||
{
|
||||
public int dist;
|
||||
public NodeClass nc;
|
||||
public NodeFunction nf;
|
||||
|
||||
public DerItem (NodeClass nc, int dist) {
|
||||
this.dist = dist;
|
||||
this.nc = nc;
|
||||
}
|
||||
|
||||
public DerItem (NodeFunction nf, int dist) {
|
||||
this.dist = dist;
|
||||
this.nf = nf;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return (nc != null ? nc.toString() : nf.toString()) + "-@" + dist;
|
||||
}
|
||||
}
|
||||
24
wcmtools/s2/danga/s2/FilePos.java
Executable file
24
wcmtools/s2/danga/s2/FilePos.java
Executable file
@@ -0,0 +1,24 @@
|
||||
package danga.s2;
|
||||
|
||||
public class FilePos implements Cloneable
|
||||
{
|
||||
public int line;
|
||||
public int col;
|
||||
|
||||
public FilePos (int l, int c) {
|
||||
line = l;
|
||||
col = c;
|
||||
}
|
||||
|
||||
public Object clone () {
|
||||
return new FilePos(line, col);
|
||||
}
|
||||
|
||||
public String locationString () {
|
||||
return ("line " + line + ", column " + col);
|
||||
}
|
||||
public String toString () {
|
||||
return locationString();
|
||||
}
|
||||
|
||||
}
|
||||
49
wcmtools/s2/danga/s2/Indenter.java
Executable file
49
wcmtools/s2/danga/s2/Indenter.java
Executable file
@@ -0,0 +1,49 @@
|
||||
package danga.s2;
|
||||
|
||||
public class Indenter
|
||||
{
|
||||
int depth = 0;
|
||||
int tabsize = 4;
|
||||
Output o;
|
||||
|
||||
String spaces;
|
||||
|
||||
public Indenter () {
|
||||
o = new OutputConsole();
|
||||
}
|
||||
|
||||
public Indenter (Output o, int tabsize) {
|
||||
this.tabsize = tabsize;
|
||||
this.o = o;
|
||||
makeSpaces();
|
||||
}
|
||||
|
||||
public void write (String s) { o.write(s); }
|
||||
public void writeln (String s) { o.writeln(s); }
|
||||
|
||||
public void write (int i) { o.write(i); }
|
||||
public void writeln (int i) { o.writeln(i); }
|
||||
|
||||
public void tabwrite (String s) { doTab(); o.write(s); }
|
||||
public void tabwriteln (String s) { doTab(); o.writeln(s); }
|
||||
|
||||
public void newline () { o.newline(); }
|
||||
|
||||
public void tabIn () { depth++; makeSpaces(); }
|
||||
public void tabOut () { depth--; makeSpaces(); }
|
||||
|
||||
protected void makeSpaces () {
|
||||
int tsize = depth * tabsize;
|
||||
char[] spaces = new char[tsize];
|
||||
for (int i=0; i<tsize; i++) {
|
||||
spaces[i] = ' ';
|
||||
}
|
||||
this.spaces = new String(spaces);
|
||||
}
|
||||
|
||||
public void doTab () {
|
||||
o.write(spaces);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
81
wcmtools/s2/danga/s2/Layer.java
Executable file
81
wcmtools/s2/danga/s2/Layer.java
Executable file
@@ -0,0 +1,81 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Enumeration;
|
||||
|
||||
public class Layer
|
||||
{
|
||||
String type;
|
||||
String declaredType;
|
||||
LinkedList nodes = new LinkedList();
|
||||
Hashtable layerinfo = new Hashtable();
|
||||
|
||||
public Layer (Tokenizer toker, String type) throws Exception
|
||||
{
|
||||
this.type = type;
|
||||
|
||||
Node n;
|
||||
Token t;
|
||||
|
||||
while ((t=toker.peek()) != null) {
|
||||
if (NodeUnnecessary.canStart(toker)) {
|
||||
nodes.add(NodeUnnecessary.parse(toker));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (NodeLayerInfo.canStart(toker)) {
|
||||
NodeLayerInfo nli = (NodeLayerInfo) NodeLayerInfo.parse(toker);
|
||||
nodes.add(nli);
|
||||
|
||||
// Remember the 'type' layerinfo value for checking later:
|
||||
if (nli.getKey().equals("type")) {
|
||||
declaredType = nli.getValue();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (NodeSet.canStart(toker)) {
|
||||
nodes.add(NodeSet.parse(toker));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (NodeProperty.canStart(toker)) {
|
||||
nodes.add(NodeProperty.parse(toker));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (NodeFunction.canStart(toker)) {
|
||||
nodes.add(NodeFunction.parse(toker, false));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (NodeClass.canStart(toker)) {
|
||||
nodes.add(NodeClass.parse(toker));
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new Exception("Unknown token encountered while parsing layer: "+
|
||||
t.toString());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void setLayerInfo (String key, String val) {
|
||||
layerinfo.put(key, val);
|
||||
}
|
||||
public String getLayerInfo (String key) {
|
||||
return (String) layerinfo.get(key);
|
||||
}
|
||||
public Enumeration getLayerInfoKeys () { return layerinfo.keys(); }
|
||||
public String getType () { return type; }
|
||||
public String getDeclaredType () { return declaredType; }
|
||||
public void setType (String newtype) { type = newtype; }
|
||||
public String toString () { return type; }
|
||||
public LinkedList getNodes() { return nodes; }
|
||||
public boolean isCoreOrLayout () {
|
||||
return (type.equals("core") || type.equals("layout"));
|
||||
}
|
||||
|
||||
}
|
||||
222
wcmtools/s2/danga/s2/Node.java
Executable file
222
wcmtools/s2/danga/s2/Node.java
Executable file
@@ -0,0 +1,222 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
public abstract class Node
|
||||
{
|
||||
protected FilePos startPos;
|
||||
protected LinkedList tokenlist = new LinkedList ();
|
||||
|
||||
public void setStart (Token t) {
|
||||
startPos = (FilePos) t.getFilePos().clone();
|
||||
}
|
||||
|
||||
public void setStart (FilePos p) {
|
||||
startPos = (FilePos) p.clone();
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception {
|
||||
System.err.println("FIXME: check not implemented for " + this.toString());
|
||||
}
|
||||
|
||||
public void asHTML (Output o) {
|
||||
ListIterator li = tokenlist.listIterator(0);
|
||||
while (li.hasNext()) {
|
||||
Object el = li.next();
|
||||
if (el instanceof Token) {
|
||||
Token t = (Token) el;
|
||||
t.asHTML(o);
|
||||
} else if (el instanceof Node) {
|
||||
Node n = (Node) el;
|
||||
n.asHTML(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o) {
|
||||
o.tabwriteln("###Node::asS2###");
|
||||
return;
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o) {
|
||||
o.tabwriteln("###"+this+"::asPerl###");
|
||||
|
||||
/*
|
||||
ListIterator li = tokenlist.listIterator(0);
|
||||
while (li.hasNext()) {
|
||||
Object el = li.next();
|
||||
if (el instanceof Token) {
|
||||
Token t = (Token) el;
|
||||
t.asPerl(o);
|
||||
} else if (el instanceof Node) {
|
||||
Node n = (Node) el;
|
||||
n.asPerl(o);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public void setTokenList (LinkedList newlist) {
|
||||
tokenlist = newlist;
|
||||
}
|
||||
|
||||
public void addNode (Node subnode) {
|
||||
tokenlist.add(subnode);
|
||||
}
|
||||
|
||||
public void addToken (Token t) {
|
||||
tokenlist.add(t);
|
||||
}
|
||||
|
||||
public Token eatToken (Tokenizer toker, boolean ignoreSpace) throws Exception {
|
||||
Token t = toker.getToken();
|
||||
tokenlist.add(t);
|
||||
if (ignoreSpace) skipWhite(toker);
|
||||
return t;
|
||||
}
|
||||
|
||||
public Token eatToken (Tokenizer toker) throws Exception {
|
||||
return eatToken(toker, true);
|
||||
}
|
||||
|
||||
public Token requireToken (Tokenizer toker, Token t) throws Exception {
|
||||
return requireToken(toker, t, true);
|
||||
}
|
||||
|
||||
public Token requireToken (Tokenizer toker, Token t, boolean ignoreSpace)
|
||||
throws Exception
|
||||
{
|
||||
if (ignoreSpace) skipWhite(toker);
|
||||
|
||||
Token next = toker.getToken();
|
||||
if (next == null) {
|
||||
throw new Exception("Unexpected end of file found");
|
||||
}
|
||||
if (! next.equals(t)) {
|
||||
System.err.println("Expecting: " + t.toString());
|
||||
System.err.println("Got: " + next.toString());
|
||||
throw new Exception("Unexpected token found at " + toker.locationString());
|
||||
}
|
||||
tokenlist.add(next);
|
||||
|
||||
if (ignoreSpace) skipWhite(toker);
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
public TokenStringLiteral
|
||||
getStringLiteral (Tokenizer toker)
|
||||
throws Exception
|
||||
{
|
||||
return getStringLiteral(toker, true);
|
||||
}
|
||||
|
||||
public TokenStringLiteral
|
||||
getStringLiteral (Tokenizer toker, boolean ignoreSpace)
|
||||
throws Exception
|
||||
{
|
||||
if (ignoreSpace) skipWhite(toker);
|
||||
|
||||
if (! (toker.peek() instanceof TokenStringLiteral)) {
|
||||
throw new Exception("Expected string literal");
|
||||
}
|
||||
tokenlist.add(toker.peek());
|
||||
return (TokenStringLiteral) toker.getToken();
|
||||
}
|
||||
|
||||
public TokenIdent getIdent (Tokenizer toker) throws Exception {
|
||||
return getIdent(toker, true, true);
|
||||
}
|
||||
|
||||
public TokenIdent getIdent (Tokenizer toker, boolean addToList) throws Exception {
|
||||
return getIdent(toker, addToList, true);
|
||||
}
|
||||
|
||||
|
||||
public TokenIdent getIdent (Tokenizer toker,
|
||||
boolean addToList,
|
||||
boolean ignoreSpace) throws Exception
|
||||
{
|
||||
Token id = toker.peek();
|
||||
if (! (id instanceof TokenIdent)) {
|
||||
throw new Exception("Expected identifer at " + toker.locationString());
|
||||
}
|
||||
if (addToList) {
|
||||
eatToken(toker, ignoreSpace);
|
||||
}
|
||||
return (TokenIdent) id;
|
||||
}
|
||||
|
||||
|
||||
public void skipWhite (Tokenizer toker) throws Exception {
|
||||
Token next;
|
||||
while ((next=toker.peek()) != null) {
|
||||
if (next.isNecessary()) {
|
||||
return;
|
||||
}
|
||||
tokenlist.add(toker.getToken());
|
||||
}
|
||||
}
|
||||
|
||||
public FilePos getFilePos ()
|
||||
{
|
||||
// most nodes should set their position
|
||||
if (startPos != null)
|
||||
return startPos;
|
||||
|
||||
// if the node didn't record its position, try to figure it out
|
||||
// from where the first token is at
|
||||
ListIterator li = tokenlist.listIterator(0);
|
||||
if (li.hasNext()) {
|
||||
Object el = li.next();
|
||||
|
||||
// usually tokenlist is tokens, but can also be nodes:
|
||||
if (el instanceof Node) {
|
||||
Node eln = (Node) el;
|
||||
return eln.getFilePos();
|
||||
}
|
||||
|
||||
Token elt = (Token) el;
|
||||
return elt.getFilePos();
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static void dbg (String s) {
|
||||
System.err.println(s);
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
throw new Exception("FIXME: getType(ck) not implemented in "+this);
|
||||
}
|
||||
|
||||
public Type getType (Checker ck, Type wanted) throws Exception
|
||||
{
|
||||
return getType(ck);
|
||||
}
|
||||
|
||||
// kinda a crappy part to put this, perhaps. but all expr
|
||||
// nodes don't inherit from NodeExpr. maybe they should?
|
||||
public boolean isLValue ()
|
||||
{
|
||||
// hack: only NodeTerms inside NodeExprs can be true
|
||||
if (this instanceof NodeExpr) {
|
||||
NodeExpr ne = (NodeExpr) this;
|
||||
Node n = ne.getExpr();
|
||||
if (n instanceof NodeTerm) {
|
||||
NodeTerm nt = (NodeTerm) n;
|
||||
return nt.isLValue();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean makeAsString(Checker ck)
|
||||
{
|
||||
System.err.println("Node::makeAsString() on "+this);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
106
wcmtools/s2/danga/s2/NodeArguments.java
Executable file
106
wcmtools/s2/danga/s2/NodeArguments.java
Executable file
@@ -0,0 +1,106 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
public class NodeArguments extends Node
|
||||
{
|
||||
public LinkedList args = new LinkedList();
|
||||
|
||||
public static NodeArguments makeEmptyArgs ()
|
||||
{
|
||||
NodeArguments n = new NodeArguments();
|
||||
n.args = new LinkedList();
|
||||
return n;
|
||||
}
|
||||
|
||||
public void addArg (NodeExpr ne) {
|
||||
args.add(ne);
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeArguments n = new NodeArguments();
|
||||
|
||||
n.setStart(n.requireToken(toker, TokenPunct.LPAREN));
|
||||
boolean loop = true;
|
||||
while (loop) {
|
||||
Token tp = toker.peek();
|
||||
if (tp.equals(TokenPunct.RPAREN)) {
|
||||
n.eatToken(toker);
|
||||
loop = false;
|
||||
} else {
|
||||
Node expr = NodeExpr.parse(toker);
|
||||
n.args.add(expr);
|
||||
n.addNode(expr);
|
||||
|
||||
if (toker.peek().equals(TokenPunct.COMMA)) {
|
||||
n.eatToken(toker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.write("(");
|
||||
|
||||
ListIterator li = args.listIterator(0);
|
||||
boolean didFirst = false;
|
||||
while (li.hasNext()) {
|
||||
Node n = (Node) li.next();
|
||||
if (didFirst) {
|
||||
o.write(", ");
|
||||
} else {
|
||||
didFirst = true;
|
||||
}
|
||||
n.asS2(o);
|
||||
}
|
||||
o.write(")");
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o) {
|
||||
asPerl(bp, o, true);
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o, boolean doCurlies)
|
||||
{
|
||||
if (doCurlies)
|
||||
o.write("(");
|
||||
|
||||
ListIterator li = args.listIterator(0);
|
||||
boolean didFirst = false;
|
||||
while (li.hasNext()) {
|
||||
Node n = (Node) li.next();
|
||||
if (didFirst) {
|
||||
o.write(", ");
|
||||
} else {
|
||||
didFirst = true;
|
||||
}
|
||||
n.asPerl(bp, o);
|
||||
}
|
||||
|
||||
if (doCurlies)
|
||||
o.write(")");
|
||||
}
|
||||
|
||||
public String typeList (Checker ck) throws Exception
|
||||
{
|
||||
StringBuffer sb = new StringBuffer(50);
|
||||
if (args.size() == 0) return sb.toString();
|
||||
|
||||
ListIterator li = args.listIterator();
|
||||
boolean first = true;
|
||||
while (li.hasNext()) {
|
||||
NodeExpr n = (NodeExpr) li.next();
|
||||
if (! first) sb.append(",");
|
||||
first = false;
|
||||
sb.append(n.getType(ck).toString());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
};
|
||||
161
wcmtools/s2/danga/s2/NodeArrayLiteral.java
Executable file
161
wcmtools/s2/danga/s2/NodeArrayLiteral.java
Executable file
@@ -0,0 +1,161 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
// [ <NodeExpr>? (, <NodeExpr>)* ,? ]
|
||||
// { (<NodeExpr> => <NodeExpr> ,)* }
|
||||
|
||||
public class NodeArrayLiteral extends NodeExpr
|
||||
{
|
||||
boolean isHash = false;
|
||||
boolean isArray = false;
|
||||
|
||||
LinkedList keys = new LinkedList();
|
||||
LinkedList vals = new LinkedList();
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return (toker.peek().equals(TokenPunct.LBRACK) ||
|
||||
toker.peek().equals(TokenPunct.LBRACE));
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeArrayLiteral nal = new NodeArrayLiteral();
|
||||
|
||||
Token t = toker.peek();
|
||||
if (t.equals(TokenPunct.LBRACK)) {
|
||||
nal.isArray = true;
|
||||
nal.setStart(nal.requireToken(toker, TokenPunct.LBRACK));
|
||||
} else {
|
||||
nal.isHash = true;
|
||||
nal.setStart(nal.requireToken(toker, TokenPunct.LBRACE));
|
||||
}
|
||||
|
||||
boolean need_comma = false;
|
||||
while (true) {
|
||||
t = toker.peek();
|
||||
|
||||
// find the ends
|
||||
if (nal.isArray && t.equals(TokenPunct.RBRACK)) {
|
||||
nal.requireToken(toker, TokenPunct.RBRACK);
|
||||
return nal;
|
||||
}
|
||||
if (nal.isHash && t.equals(TokenPunct.RBRACE)) {
|
||||
nal.requireToken(toker, TokenPunct.RBRACE);
|
||||
return nal;
|
||||
}
|
||||
|
||||
if (need_comma) {
|
||||
throw new Exception("Expecting comma at "+toker.getPos());
|
||||
}
|
||||
|
||||
if (nal.isArray) {
|
||||
NodeExpr ne = (NodeExpr) NodeExpr.parse(toker);
|
||||
nal.vals.add(ne);
|
||||
nal.addNode(ne);
|
||||
}
|
||||
if (nal.isHash) {
|
||||
NodeExpr ne = (NodeExpr) NodeExpr.parse(toker);
|
||||
nal.keys.add(ne);
|
||||
nal.addNode(ne);
|
||||
|
||||
nal.requireToken(toker, TokenPunct.HASSOC);
|
||||
|
||||
ne = (NodeExpr) NodeExpr.parse(toker);
|
||||
nal.vals.add(ne);
|
||||
nal.addNode(ne);
|
||||
}
|
||||
|
||||
need_comma = true;
|
||||
if (toker.peek().equals(TokenPunct.COMMA)) {
|
||||
nal.requireToken(toker, TokenPunct.COMMA);
|
||||
need_comma = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.writeln(isArray ? "[" : "{");
|
||||
o.tabIn();
|
||||
ListIterator liv = vals.listIterator();
|
||||
ListIterator lik = keys.listIterator();
|
||||
Node n;
|
||||
while (liv.hasNext()) {
|
||||
o.tabwrite("");
|
||||
if (isHash) {
|
||||
n = (Node) lik.next();
|
||||
n.asS2(o);
|
||||
o.write(" => ");
|
||||
}
|
||||
n = (Node) liv.next();
|
||||
n.asS2(o);
|
||||
o.writeln(",");
|
||||
}
|
||||
o.tabOut();
|
||||
o.tabwrite(isArray ? "]" : "}");
|
||||
}
|
||||
|
||||
public Type getType (Checker ck, Type wanted) throws Exception
|
||||
{
|
||||
// in case of empty array [] or hash {}, the type is what they wanted,
|
||||
// if they wanted something, otherwise void[] or void{}
|
||||
Type t;
|
||||
if (vals.size() == 0) {
|
||||
if (wanted != null) return wanted;
|
||||
t = new Type("void");
|
||||
if (isArray) t.makeArrayOf();
|
||||
if (isHash) t.makeHashOf();
|
||||
return t;
|
||||
}
|
||||
|
||||
ListIterator liv = vals.listIterator();
|
||||
ListIterator lik = keys.listIterator();
|
||||
|
||||
t = (Type) ((Node) liv.next()).getType(ck).clone();
|
||||
while (liv.hasNext()) {
|
||||
Node n = (Node) liv.next();
|
||||
Type next = n.getType(ck);
|
||||
if (! t.equals(next)) {
|
||||
throw new Exception("Array literal with inconsistent types: "+
|
||||
"starts with "+t+", but then has "+next+" at "+
|
||||
n.getFilePos());
|
||||
}
|
||||
}
|
||||
|
||||
if (isArray) t.makeArrayOf();
|
||||
if (isHash) t.makeHashOf();
|
||||
return t;
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
return getType(ck, null);
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
o.writeln(isArray ? "[" : "{");
|
||||
o.tabIn();
|
||||
ListIterator liv = vals.listIterator();
|
||||
ListIterator lik = keys.listIterator();
|
||||
Node n;
|
||||
while (liv.hasNext()) {
|
||||
o.tabwrite("");
|
||||
if (isHash) {
|
||||
n = (Node) lik.next();
|
||||
n.asPerl(bp, o);
|
||||
o.write(" => ");
|
||||
}
|
||||
n = (Node) liv.next();
|
||||
n.asPerl(bp, o);
|
||||
o.writeln(",");
|
||||
}
|
||||
o.tabOut();
|
||||
o.tabwrite(isArray ? "]" : "}");
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
87
wcmtools/s2/danga/s2/NodeAssignExpr.java
Executable file
87
wcmtools/s2/danga/s2/NodeAssignExpr.java
Executable file
@@ -0,0 +1,87 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeAssignExpr extends Node
|
||||
{
|
||||
Node lhs;
|
||||
TokenPunct op;
|
||||
Node rhs;
|
||||
|
||||
boolean builtin;
|
||||
String baseType;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return NodeCondExpr.canStart(toker);
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeAssignExpr n = new NodeAssignExpr();
|
||||
|
||||
n.lhs = NodeCondExpr.parse(toker);
|
||||
n.addNode(n.lhs);
|
||||
|
||||
if (toker.peek().equals(TokenPunct.ASSIGN)) {
|
||||
n.op = (TokenPunct) toker.peek();
|
||||
n.eatToken(toker);
|
||||
n.skipWhite(toker);
|
||||
} else {
|
||||
return n.lhs;
|
||||
}
|
||||
|
||||
n.rhs = NodeAssignExpr.parse(toker);
|
||||
n.addNode(n.rhs);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public Type getType (Checker ck, Type wanted) throws Exception
|
||||
{
|
||||
Type lt = lhs.getType(ck, wanted);
|
||||
Type rt = rhs.getType(ck, lt);
|
||||
|
||||
if (lt.isReadOnly()) {
|
||||
throw new Exception("Left-hand side of assignment at "+getFilePos()+
|
||||
" is a read-only value.");
|
||||
}
|
||||
|
||||
if (! (lhs instanceof NodeTerm) ||
|
||||
! lhs.isLValue()) {
|
||||
throw new Exception("Left-hand side of assignment at "+getFilePos()+
|
||||
" must be an lvalue.");
|
||||
}
|
||||
|
||||
if (ck.typeIsa(rt, lt))
|
||||
return lt;
|
||||
|
||||
// types don't match, but maybe class for left hand side has
|
||||
// a constructor which takes a string.
|
||||
if (rt.equals(Type.STRING) && ck.isStringCtor(lt)) {
|
||||
rt = rhs.getType(ck, lt);
|
||||
if (lt.equals(rt)) return lt;
|
||||
}
|
||||
|
||||
throw new Exception("Can't assign type "+rt+" to "+lt+" at "+
|
||||
getFilePos());
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
lhs.asS2(o);
|
||||
if (op != null) {
|
||||
o.write(" = ");
|
||||
rhs.asS2(o);
|
||||
}
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
lhs.asPerl(bp, o);
|
||||
if (op != null) {
|
||||
o.write(" = ");
|
||||
rhs.asPerl(bp, o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
296
wcmtools/s2/danga/s2/NodeClass.java
Executable file
296
wcmtools/s2/danga/s2/NodeClass.java
Executable file
@@ -0,0 +1,296 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Hashtable;
|
||||
|
||||
public class NodeClass extends Node
|
||||
{
|
||||
TokenIdent name;
|
||||
TokenIdent parentName;
|
||||
|
||||
String docstring;
|
||||
|
||||
LinkedList vars = new LinkedList(); // NodeNamedType
|
||||
Hashtable varType = new Hashtable(); // token String -> Type
|
||||
|
||||
LinkedList functions = new LinkedList(); // NodeFunction
|
||||
Hashtable funcType = new Hashtable(); // funcID String -> Type
|
||||
|
||||
NodeClass parentClass; // Not set until check() is run
|
||||
|
||||
// this is kinda ugly, keeping a reference to the checker for use
|
||||
// later, but there's only ever one checker, so it's okay.
|
||||
Checker ck;
|
||||
|
||||
public String getParentName() {
|
||||
if (parentName == null) return null;
|
||||
return parentName.getIdent();
|
||||
}
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception {
|
||||
if (toker.peek().equals(TokenKeyword.CLASS))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeClass n = new NodeClass();
|
||||
|
||||
n.setStart(n.requireToken(toker, TokenKeyword.CLASS));
|
||||
|
||||
n.name = n.getIdent(toker);
|
||||
|
||||
if (toker.peek().equals(TokenKeyword.EXTENDS)) {
|
||||
n.eatToken(toker);
|
||||
n.parentName = n.getIdent(toker);
|
||||
}
|
||||
|
||||
// docstring
|
||||
if (toker.peek() instanceof TokenStringLiteral) {
|
||||
TokenStringLiteral t = (TokenStringLiteral) n.eatToken(toker);
|
||||
n.docstring = t.getString();
|
||||
}
|
||||
|
||||
n.requireToken(toker, TokenPunct.LBRACE);
|
||||
|
||||
while (toker.peek() != null && toker.peek() instanceof TokenKeyword) {
|
||||
if (toker.peek().equals(TokenKeyword.VAR)) {
|
||||
NodeClassVarDecl ncvd = (NodeClassVarDecl) NodeClassVarDecl.parse(toker);
|
||||
n.vars.add(ncvd);
|
||||
n.addNode(ncvd);
|
||||
} else if (toker.peek().equals(TokenKeyword.FUNCTION)) {
|
||||
NodeFunction nm = (NodeFunction) NodeFunction.parse(toker, true);
|
||||
n.functions.add(nm);
|
||||
n.addNode(nm);
|
||||
}
|
||||
}
|
||||
|
||||
n.requireToken(toker, TokenPunct.RBRACE);
|
||||
return n;
|
||||
}
|
||||
|
||||
public String getName () {
|
||||
return name.getIdent();
|
||||
}
|
||||
public Type getFunctionType (String funcID) {
|
||||
Type t = (Type) funcType.get(funcID);
|
||||
if (t != null) return t;
|
||||
if (parentClass != null)
|
||||
return parentClass.getFunctionType(funcID);
|
||||
return null;
|
||||
}
|
||||
public NodeClass getFunctionDeclClass (String funcID) {
|
||||
Type t = (Type) funcType.get(funcID);
|
||||
if (t != null) return this;
|
||||
if (parentClass != null)
|
||||
return parentClass.getFunctionDeclClass(funcID);
|
||||
return null;
|
||||
}
|
||||
|
||||
public Type getMemberType (String mem) {
|
||||
Type t = (Type) varType.get(mem);
|
||||
if (t != null) return t;
|
||||
if (parentClass != null) {
|
||||
return parentClass.getMemberType(mem);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// returns LinkedList<DerClass> from the current class down to
|
||||
// all children classes.
|
||||
public LinkedList getDerClasses () {
|
||||
return getDerClasses(null, 0);
|
||||
}
|
||||
|
||||
private LinkedList getDerClasses (LinkedList l, int depth) {
|
||||
if (l == null) l = new LinkedList();
|
||||
l.add(new DerItem(this, depth));
|
||||
ListIterator li = ck.getDerClassesIter(getName());
|
||||
while (li.hasNext()) {
|
||||
String cname = (String) li.next();
|
||||
NodeClass c = ck.getClass(cname);
|
||||
c.getDerClasses(l, depth+1);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
// returns the class/parent-class the named member variable was
|
||||
// defined in.
|
||||
public NodeClass getMemberDeclClass (String mem) {
|
||||
Type t = (Type) varType.get(mem);
|
||||
if (t != null) return this;
|
||||
if (parentClass != null) {
|
||||
return parentClass.getMemberDeclClass(mem);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception
|
||||
{
|
||||
ListIterator li;
|
||||
this.ck = ck;
|
||||
|
||||
// can't declare classes inside of a layer if functions
|
||||
// have already been declared or defined.
|
||||
if (ck.getHitFunction()) {
|
||||
throw new Exception("Can't declare a class inside a layer "+
|
||||
"file after functions have been defined at "+
|
||||
getFilePos());
|
||||
}
|
||||
|
||||
// if this is an extended class, make sure parent class exists
|
||||
parentClass = null;
|
||||
if (parentName != null) {
|
||||
String pname = parentName.getIdent();
|
||||
parentClass = ck.getClass(pname);
|
||||
if (parentClass == null) {
|
||||
throw new Exception("Can't extend non-existent class '"+
|
||||
pname+"' at "+getFilePos());
|
||||
}
|
||||
}
|
||||
|
||||
// make sure the class isn't already defined.
|
||||
String cname = name.getIdent();
|
||||
if (ck.getClass(cname) != null) {
|
||||
throw new Exception("Can't redeclare class '"+cname+"' at "+
|
||||
getFilePos());
|
||||
}
|
||||
|
||||
// register all var and function declarations in hash & check for both
|
||||
// duplicates and masking of parent class's declarations
|
||||
|
||||
// register self. this needs to be done before checking member
|
||||
// variables so we can have members of our own type.
|
||||
ck.addClass(cname, this);
|
||||
|
||||
// member vars
|
||||
for (li = vars.listIterator(); li.hasNext(); ) {
|
||||
NodeClassVarDecl nnt = (NodeClassVarDecl) li.next();
|
||||
boolean readonly = nnt.isReadOnly();
|
||||
String vn = nnt.getName();
|
||||
Type vt = nnt.getType();
|
||||
Type et = getMemberType(vn);
|
||||
if (et != null) {
|
||||
NodeClass oc = getMemberDeclClass(vn);
|
||||
throw new Exception("Can't declare the variable '"+vn+"' "+
|
||||
"as '"+vt+"' in class '"+cname+"' at "+
|
||||
nnt.getFilePos()+" because it's "+
|
||||
"already defined in class '"+oc.getName()+"' as "+
|
||||
"type '"+et+"'.");
|
||||
}
|
||||
|
||||
// check to see if type exists
|
||||
if (ck.isValidType(vt) != true) {
|
||||
throw new Exception("Can't declare member variable '"+vn+"' "+
|
||||
"as unknown type '"+vt+"' in class '"+cname+"' at "+
|
||||
nnt.getFilePos());
|
||||
}
|
||||
|
||||
vt.setReadOnly(readonly);
|
||||
varType.put(vn, vt); // register member variable
|
||||
}
|
||||
|
||||
// all parent class functions need to be inherited:
|
||||
registerFunctions(ck, cname);
|
||||
}
|
||||
|
||||
private void registerFunctions (Checker ck, String clas) throws Exception
|
||||
{
|
||||
// register parent's functions first.
|
||||
if (parentClass != null)
|
||||
parentClass.registerFunctions(ck, clas);
|
||||
|
||||
// now do our own
|
||||
for (ListIterator li = functions.listIterator(); li.hasNext(); ) {
|
||||
NodeFunction nf = (NodeFunction) li.next();
|
||||
Type rettype = nf.getReturnType();
|
||||
nf.registerFunction(ck, clas);
|
||||
}
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
ListIterator li;
|
||||
|
||||
o.tabwrite("class " + name.getIdent() + " ");
|
||||
if (parentName != null) {
|
||||
o.write("extends " + parentName.getIdent() + " ");
|
||||
}
|
||||
o.writeln("{");
|
||||
o.tabIn();
|
||||
|
||||
// vars
|
||||
for (li = vars.listIterator(0); li.hasNext(); ) {
|
||||
NodeClassVarDecl vd = (NodeClassVarDecl) li.next();
|
||||
vd.asS2(o);
|
||||
}
|
||||
|
||||
// functions
|
||||
for (li = functions.listIterator(0); li.hasNext(); ) {
|
||||
NodeFunction nf = (NodeFunction) li.next();
|
||||
nf.asS2(o);
|
||||
}
|
||||
|
||||
o.tabOut();
|
||||
o.writeln("}");
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o) {
|
||||
o.tabwriteln("register_class(" + bp.getLayerIDString() +
|
||||
", " + bp.quoteString(name.getIdent()) + ", {");
|
||||
o.tabIn();
|
||||
if (parentName != null) {
|
||||
o.tabwriteln("'parent' => " + bp.quoteString(parentName.getIdent()) + ",");
|
||||
}
|
||||
if (docstring != null) {
|
||||
o.tabwriteln("'docstring' => " + bp.quoteString(docstring) + ",");
|
||||
}
|
||||
|
||||
// vars
|
||||
o.tabwriteln("'vars' => {");
|
||||
o.tabIn();
|
||||
for (ListIterator li = vars.listIterator(); li.hasNext(); ) {
|
||||
NodeClassVarDecl nnt = (NodeClassVarDecl) li.next();
|
||||
String vn = nnt.getName();
|
||||
Type vt = nnt.getType();
|
||||
Type et = getMemberType(vn);
|
||||
o.tabwrite(bp.quoteString(vn) + " => { 'type' => " + bp.quoteString(vt.toString()));
|
||||
if (vt.isReadOnly()) {
|
||||
o.write(", 'readonly' => 1");
|
||||
}
|
||||
if (nnt.getDocString() != null) {
|
||||
o.write(", 'docstring' => " + bp.quoteString(nnt.getDocString()));
|
||||
}
|
||||
o.writeln(" },");
|
||||
}
|
||||
o.tabOut();
|
||||
o.tabwriteln("},");
|
||||
|
||||
// methods
|
||||
o.tabwriteln("'funcs' => {");
|
||||
o.tabIn();
|
||||
for (ListIterator li = functions.listIterator(); li.hasNext(); ) {
|
||||
NodeFunction nf = (NodeFunction) li.next();
|
||||
String name = nf.getName();
|
||||
NodeFormals nfo = nf.getFormals();
|
||||
Type rt = nf.getReturnType();
|
||||
o.tabwrite(bp.quoteString(name + ((nfo != null) ? nfo.toString() : "()"))
|
||||
+ " => { 'returntype' => "
|
||||
+ bp.quoteString(rt.toString()));
|
||||
if (nf.getDocString() != null) {
|
||||
o.write(", 'docstring' => " + bp.quoteString(nf.getDocString()));
|
||||
}
|
||||
o.writeln(" },");
|
||||
}
|
||||
o.tabOut();
|
||||
o.tabwriteln("},");
|
||||
|
||||
|
||||
o.tabOut();
|
||||
o.tabwriteln("});");
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
80
wcmtools/s2/danga/s2/NodeClassVarDecl.java
Executable file
80
wcmtools/s2/danga/s2/NodeClassVarDecl.java
Executable file
@@ -0,0 +1,80 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeClassVarDecl extends Node
|
||||
{
|
||||
public Type type;
|
||||
public NodeType typenode;
|
||||
public String name;
|
||||
public String docstring;
|
||||
|
||||
boolean readonly = false;
|
||||
|
||||
public Type getType () {
|
||||
return type;
|
||||
}
|
||||
public String getName () {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDocString () {
|
||||
return docstring;
|
||||
}
|
||||
|
||||
public boolean isReadOnly () {
|
||||
return readonly;
|
||||
}
|
||||
|
||||
public NodeClassVarDecl () {
|
||||
}
|
||||
|
||||
public NodeClassVarDecl (String name, Type type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeClassVarDecl n = new NodeClassVarDecl();
|
||||
|
||||
n.setStart(n.requireToken(toker, TokenKeyword.VAR));
|
||||
|
||||
if (toker.peek() == TokenKeyword.READONLY) {
|
||||
n.readonly = true;
|
||||
n.eatToken(toker);
|
||||
}
|
||||
|
||||
n.typenode = (NodeType) NodeType.parse(toker);
|
||||
n.type = n.typenode.getType();
|
||||
n.addNode(n.typenode);
|
||||
|
||||
n.name = n.getIdent(toker).getIdent();
|
||||
|
||||
// docstring
|
||||
if (toker.peek() instanceof TokenStringLiteral) {
|
||||
TokenStringLiteral t = (TokenStringLiteral) n.eatToken(toker);
|
||||
n.docstring = t.getString();
|
||||
}
|
||||
|
||||
n.requireToken(toker, TokenPunct.SCOLON);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.tabwrite("var ");
|
||||
if (readonly) o.write("readonly ");
|
||||
typenode.asS2(o);
|
||||
o.write(" " + name);
|
||||
if (docstring != null) {
|
||||
o.write(BackendPerl.quoteString(" " + docstring));
|
||||
}
|
||||
o.writeln(";");
|
||||
}
|
||||
|
||||
public String asString ()
|
||||
{
|
||||
return type.toString() + " " + name;
|
||||
}
|
||||
|
||||
};
|
||||
78
wcmtools/s2/danga/s2/NodeCondExpr.java
Executable file
78
wcmtools/s2/danga/s2/NodeCondExpr.java
Executable file
@@ -0,0 +1,78 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeCondExpr extends Node
|
||||
{
|
||||
Node test_expr;
|
||||
Node true_expr;
|
||||
Node false_expr;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return NodeRange.canStart(toker);
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeCondExpr n = new NodeCondExpr();
|
||||
|
||||
n.test_expr = NodeRange.parse(toker);
|
||||
n.addNode(n.test_expr);
|
||||
|
||||
if (toker.peek().equals(TokenPunct.QMARK)) {
|
||||
n.eatToken(toker);
|
||||
n.skipWhite(toker);
|
||||
} else {
|
||||
return n.test_expr;
|
||||
}
|
||||
|
||||
n.true_expr = NodeRange.parse(toker);
|
||||
n.addNode(n.true_expr);
|
||||
n.requireToken(toker, TokenPunct.COLON);
|
||||
|
||||
n.false_expr = NodeRange.parse(toker);
|
||||
n.addNode(n.false_expr);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
Type ctype = test_expr.getType(ck);
|
||||
if (! ctype.isBoolable()) {
|
||||
throw new Exception("Conditional expression not a boolean at "+
|
||||
getFilePos());
|
||||
}
|
||||
Type lt = true_expr.getType(ck);
|
||||
Type rt = false_expr.getType(ck);
|
||||
if (! lt.equals(rt)) {
|
||||
throw new Exception("Types must match in condition expression at "+
|
||||
getFilePos());
|
||||
}
|
||||
return lt;
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
test_expr.asS2(o);
|
||||
if (true_expr != null) {
|
||||
o.write(" ? ");
|
||||
true_expr.asS2(o);
|
||||
o.write(" : ");
|
||||
false_expr.asS2(o);
|
||||
}
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
test_expr.asPerl(bp, o);
|
||||
if (true_expr != null) {
|
||||
o.write(" ? ");
|
||||
true_expr.asPerl(bp, o);
|
||||
o.write(" : ");
|
||||
false_expr.asPerl(bp, o);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
52
wcmtools/s2/danga/s2/NodeDeleteStmt.java
Executable file
52
wcmtools/s2/danga/s2/NodeDeleteStmt.java
Executable file
@@ -0,0 +1,52 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeDeleteStmt extends Node
|
||||
{
|
||||
NodeVarRef var;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (toker.peek().equals(TokenKeyword.DELETE))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeDeleteStmt n = new NodeDeleteStmt();
|
||||
Token t = toker.peek();
|
||||
|
||||
n.requireToken(toker, TokenKeyword.DELETE);
|
||||
n.addNode(n.var = (NodeVarRef) NodeVarRef.parse(toker));
|
||||
n.requireToken(toker, TokenPunct.SCOLON);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception
|
||||
{
|
||||
// type check the innards, but we don't care what type it
|
||||
// actually is.
|
||||
var.getType(ck);
|
||||
|
||||
// but it must be a hash reference
|
||||
if (! var.isHashElement()) {
|
||||
throw new Exception("Delete statement argument is not a hash at "+
|
||||
var.getFilePos());
|
||||
}
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.tabwrite("delete ");
|
||||
var.asS2(o);
|
||||
o.writeln(";");
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
o.tabwrite("delete ");
|
||||
var.asPerl(bp, o);
|
||||
o.writeln(";");
|
||||
}
|
||||
};
|
||||
85
wcmtools/s2/danga/s2/NodeEqExpr.java
Executable file
85
wcmtools/s2/danga/s2/NodeEqExpr.java
Executable file
@@ -0,0 +1,85 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeEqExpr extends Node
|
||||
{
|
||||
Node lhs;
|
||||
TokenPunct op;
|
||||
Node rhs;
|
||||
|
||||
// use this for the backend to decide which add operator to use
|
||||
private Type myType;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return NodeRelExpr.canStart(toker);
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeEqExpr n = new NodeEqExpr();
|
||||
|
||||
n.lhs = NodeRelExpr.parse(toker);
|
||||
n.addNode(n.lhs);
|
||||
|
||||
Token t = toker.peek();
|
||||
|
||||
if (t.equals(TokenPunct.EQ) || t.equals(TokenPunct.NE)) {
|
||||
n.op = (TokenPunct) t;
|
||||
n.eatToken(toker);
|
||||
n.skipWhite(toker);
|
||||
} else {
|
||||
return n.lhs;
|
||||
}
|
||||
|
||||
n.rhs = NodeRelExpr.parse(toker);
|
||||
n.addNode(n.rhs);
|
||||
n.skipWhite(toker);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
Type lt = lhs.getType(ck);
|
||||
Type rt = rhs.getType(ck);
|
||||
if (! lt.equals(rt))
|
||||
throw new Exception("The types of the left and right hand side of "+
|
||||
"equality test expression don't match at "+getFilePos());
|
||||
myType = lt;
|
||||
if (lt.equals(Type.BOOL) || lt.equals(Type.STRING) || lt.equals(Type.INT)) {
|
||||
return Type.BOOL;
|
||||
}
|
||||
throw new Exception ("Only bool, string, and int types can be tested for "+
|
||||
"equality at "+getFilePos());
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
lhs.asS2(o);
|
||||
if (op != null) {
|
||||
o.write(" " + op.getPunct() + " ");
|
||||
rhs.asS2(o);
|
||||
}
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
lhs.asPerl(bp, o);
|
||||
if (op != null) {
|
||||
if (op.equals(TokenPunct.EQ)) {
|
||||
if (myType.equals(Type.STRING))
|
||||
o.write(" eq ");
|
||||
else
|
||||
o.write(" == ");
|
||||
} else {
|
||||
if (myType.equals(Type.STRING))
|
||||
o.write(" ne ");
|
||||
else
|
||||
o.write(" != ");
|
||||
}
|
||||
rhs.asPerl(bp, o);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
49
wcmtools/s2/danga/s2/NodeExpr.java
Executable file
49
wcmtools/s2/danga/s2/NodeExpr.java
Executable file
@@ -0,0 +1,49 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeExpr extends Node
|
||||
{
|
||||
Node expr;
|
||||
|
||||
public NodeExpr () { }
|
||||
|
||||
public NodeExpr (Node n) {
|
||||
expr = n;
|
||||
}
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return NodeAssignExpr.canStart(toker);
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeExpr n = new NodeExpr();
|
||||
n.expr = NodeAssignExpr.parse(toker);
|
||||
n.addNode(n.expr);
|
||||
return n; // Note: always return a NodeExpr here
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
expr.asS2(o);
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
expr.asPerl(bp, o);
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
return expr.getType(ck, null);
|
||||
}
|
||||
|
||||
public Type getType (Checker ck, Type wanted) throws Exception
|
||||
{
|
||||
return expr.getType(ck, wanted);
|
||||
}
|
||||
|
||||
public Node getExpr () {
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
42
wcmtools/s2/danga/s2/NodeExprStmt.java
Executable file
42
wcmtools/s2/danga/s2/NodeExprStmt.java
Executable file
@@ -0,0 +1,42 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeExprStmt extends Node
|
||||
{
|
||||
NodeExpr expr;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return NodeExpr.canStart(toker);
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeExprStmt n = new NodeExprStmt();
|
||||
|
||||
n.addNode(n.expr = (NodeExpr) NodeExpr.parse(toker));
|
||||
n.requireToken(toker, TokenPunct.SCOLON);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception
|
||||
{
|
||||
Type t = expr.getType(ck); // checks the type
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.doTab();
|
||||
expr.asS2(o);
|
||||
o.writeln(";");
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
o.doTab();
|
||||
expr.asPerl(bp, o);
|
||||
o.writeln(";");
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
136
wcmtools/s2/danga/s2/NodeForeachStmt.java
Executable file
136
wcmtools/s2/danga/s2/NodeForeachStmt.java
Executable file
@@ -0,0 +1,136 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeForeachStmt extends Node
|
||||
{
|
||||
NodeExpr listexpr;
|
||||
NodeStmtBlock stmts;
|
||||
NodeVarDecl vardecl;
|
||||
NodeVarRef varref;
|
||||
boolean isHash; // otherwise it's an array or a string
|
||||
boolean isString;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (toker.peek().equals(TokenKeyword.FOREACH))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeForeachStmt n = new NodeForeachStmt();
|
||||
|
||||
n.requireToken(toker, TokenKeyword.FOREACH);
|
||||
|
||||
if (NodeVarDecl.canStart(toker)) {
|
||||
n.addNode(n.vardecl = (NodeVarDecl) NodeVarDecl.parse(toker));
|
||||
} else {
|
||||
n.addNode(n.varref = (NodeVarRef) NodeVarRef.parse(toker));
|
||||
}
|
||||
|
||||
// expression in parenthesis representing an array to iterate over:
|
||||
n.requireToken(toker, TokenPunct.LPAREN);
|
||||
n.addNode(n.listexpr = (NodeExpr) NodeExpr.parse(toker));
|
||||
n.requireToken(toker, TokenPunct.RPAREN);
|
||||
|
||||
// and what to do on each element
|
||||
n.addNode(n.stmts = (NodeStmtBlock) NodeStmtBlock.parse(toker));
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception
|
||||
{
|
||||
Type ltype = listexpr.getType(ck);
|
||||
isHash = false;
|
||||
|
||||
if (ltype.isHashOf()) {
|
||||
isHash = true;
|
||||
} else if (ltype.equals(Type.STRING)) { // Iterate over characters in a string
|
||||
isString = true;
|
||||
} else if (! ltype.isArrayOf()) {
|
||||
throw new Exception("Must use an array, hash or string in foreach statement at "+
|
||||
listexpr.getFilePos());
|
||||
}
|
||||
|
||||
Type itype = null;
|
||||
if (vardecl != null) {
|
||||
vardecl.populateScope(stmts);
|
||||
itype = vardecl.getType();
|
||||
}
|
||||
if (varref != null) {
|
||||
itype = varref.getType(ck);
|
||||
}
|
||||
|
||||
if (isHash) {
|
||||
// then iter type must be a string or int
|
||||
if (! itype.equals(Type.STRING) && ! itype.equals(Type.INT)) {
|
||||
throw new Exception("Foreach iteration variable must be a "+
|
||||
"string or int when interating over the keys "+
|
||||
"in a hash at "+getFilePos());
|
||||
}
|
||||
} else if (isString) {
|
||||
if (! itype.equals(Type.STRING)) {
|
||||
throw new Exception("Foreach iteration variable must be a "+
|
||||
"string when interating over the characters "+
|
||||
"in a string at "+getFilePos());
|
||||
}
|
||||
} else {
|
||||
// iter type must be the same as the list type minus
|
||||
// the final array ref
|
||||
|
||||
// figure out the desired type
|
||||
Type dtype = (Type) ltype.clone();
|
||||
dtype.removeMod();
|
||||
|
||||
if (! dtype.equals(itype)) {
|
||||
throw new Exception("Foreach iteration variable is of type "+
|
||||
itype+", not the expected type of "+dtype+" at "+
|
||||
getFilePos());
|
||||
}
|
||||
}
|
||||
|
||||
ck.pushLocalBlock(stmts);
|
||||
stmts.check(l, ck);
|
||||
ck.popLocalBlock();
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.tabwrite("foreach ");
|
||||
if (vardecl != null)
|
||||
vardecl.asS2(o);
|
||||
if (varref != null)
|
||||
varref.asS2(o);
|
||||
o.write(" (");
|
||||
listexpr.asS2(o);
|
||||
o.write(") ");
|
||||
stmts.asS2(o);
|
||||
o.newline();
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
o.tabwrite("foreach ");
|
||||
if (vardecl != null)
|
||||
vardecl.asPerl(bp, o);
|
||||
if (varref != null)
|
||||
varref.asPerl(bp, o);
|
||||
if (isHash) {
|
||||
o.write(" (keys %{");
|
||||
} else if (isString) {
|
||||
o.write(" (S2::get_characters(");
|
||||
} else {
|
||||
o.write(" (@{");
|
||||
}
|
||||
listexpr.asPerl(bp, o);
|
||||
if (isString) {
|
||||
o.write(")) ");
|
||||
} else {
|
||||
o.write("}) ");
|
||||
}
|
||||
stmts.asPerl(bp, o);
|
||||
o.newline();
|
||||
}
|
||||
|
||||
};
|
||||
148
wcmtools/s2/danga/s2/NodeFormals.java
Executable file
148
wcmtools/s2/danga/s2/NodeFormals.java
Executable file
@@ -0,0 +1,148 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Hashtable;
|
||||
|
||||
public class NodeFormals extends Node
|
||||
{
|
||||
public LinkedList listFormals = new LinkedList(); // NodeNamedType
|
||||
|
||||
public NodeFormals () { }
|
||||
public NodeFormals (LinkedList formals) {
|
||||
listFormals = formals;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeFormals n = new NodeFormals();
|
||||
int count = 0;
|
||||
|
||||
n.requireToken(toker, TokenPunct.LPAREN);
|
||||
while (toker.peek() != null && ! toker.peek().equals(TokenPunct.RPAREN)) {
|
||||
if (count > 0) {
|
||||
n.requireToken(toker, TokenPunct.COMMA);
|
||||
}
|
||||
n.skipWhite(toker);
|
||||
|
||||
NodeNamedType nf = (NodeNamedType) NodeNamedType.parse(toker);
|
||||
n.listFormals.add(nf);
|
||||
n.tokenlist.add(nf);
|
||||
|
||||
n.skipWhite(toker);
|
||||
count++;
|
||||
}
|
||||
n.requireToken(toker, TokenPunct.RPAREN);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception
|
||||
{
|
||||
Hashtable h = new Hashtable();
|
||||
ListIterator li = listFormals.listIterator();
|
||||
while (li.hasNext()) {
|
||||
NodeNamedType nt = (NodeNamedType) li.next();
|
||||
String name = nt.getName();
|
||||
if (h.get(name) != null)
|
||||
throw new Exception("Duplicate argument named '" + name + "' at "+
|
||||
nt.getFilePos());
|
||||
h.put(name, name);
|
||||
|
||||
Type t = nt.getType();
|
||||
if (! ck.isValidType(t))
|
||||
throw new Exception("Unknown type '" + t + "' at "+nt.getFilePos());
|
||||
}
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o) {
|
||||
if (listFormals.size() == 0) return; // no empty parens necessary in S2
|
||||
o.write(toString());
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
StringBuffer sb = new StringBuffer("(");
|
||||
ListIterator li = listFormals.listIterator();
|
||||
boolean first = true;
|
||||
while (li.hasNext()) {
|
||||
NodeNamedType nf = (NodeNamedType) li.next();
|
||||
if (! first) {
|
||||
sb.append(", ");
|
||||
}
|
||||
first = false;
|
||||
|
||||
sb.append(nf.toString());
|
||||
}
|
||||
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// returns a ListIterator returning variations of this NodeFormal
|
||||
// object using derived classes as well.
|
||||
public static ListIterator variationIterator (NodeFormals nf, Checker ck)
|
||||
{
|
||||
LinkedList l = new LinkedList();
|
||||
if (nf == null) {
|
||||
l.add(new NodeFormals(new LinkedList()));
|
||||
} else {
|
||||
nf.getVariations(ck, l, new LinkedList(), 0);
|
||||
}
|
||||
return l.listIterator();
|
||||
}
|
||||
|
||||
private void getVariations (Checker ck,
|
||||
LinkedList vars,
|
||||
LinkedList temp,
|
||||
int col)
|
||||
{
|
||||
if (col == listFormals.size()) {
|
||||
vars.add(new NodeFormals(temp));
|
||||
return;
|
||||
}
|
||||
|
||||
NodeNamedType nt = (NodeNamedType) listFormals.get(col);
|
||||
Type t = nt.getType();
|
||||
|
||||
for (ListIterator li = t.subTypesIter(ck); li.hasNext(); ) {
|
||||
t = (Type) li.next();
|
||||
LinkedList newtemp = (LinkedList) temp.clone();
|
||||
newtemp.add(new NodeNamedType(nt.getName(), t));
|
||||
getVariations(ck, vars, newtemp, col+1);
|
||||
}
|
||||
}
|
||||
|
||||
public String typeList ()
|
||||
{
|
||||
StringBuffer sb = new StringBuffer(50);
|
||||
if (listFormals.size() == 0) return sb.toString();
|
||||
|
||||
ListIterator li = listFormals.listIterator();
|
||||
boolean first = true;
|
||||
while (li.hasNext()) {
|
||||
NodeNamedType nt = (NodeNamedType) li.next();
|
||||
if (! first) sb.append(",");
|
||||
first = false;
|
||||
|
||||
sb.append(nt.getType().toString());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// adds all these variables to the stmtblock's symbol table
|
||||
public void populateScope (NodeStmtBlock nb)
|
||||
{
|
||||
if (listFormals.size() == 0) return;
|
||||
|
||||
ListIterator li = listFormals.listIterator();
|
||||
while (li.hasNext()) {
|
||||
NodeNamedType nt = (NodeNamedType) li.next();
|
||||
nb.addLocalVar(nt.getName(), nt.getType());
|
||||
}
|
||||
}
|
||||
|
||||
public ListIterator iterator() {
|
||||
return listFormals.listIterator();
|
||||
}
|
||||
};
|
||||
364
wcmtools/s2/danga/s2/NodeFunction.java
Executable file
364
wcmtools/s2/danga/s2/NodeFunction.java
Executable file
@@ -0,0 +1,364 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Iterator;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Enumeration;
|
||||
|
||||
public class NodeFunction extends Node
|
||||
{
|
||||
TokenIdent classname;
|
||||
TokenIdent name;
|
||||
NodeType rettype;
|
||||
NodeFormals formals;
|
||||
NodeStmtBlock stmts;
|
||||
boolean builtin = false;
|
||||
boolean isCtor = false;
|
||||
LinkedList funcNames = null;
|
||||
|
||||
Checker ck;
|
||||
|
||||
String docstring;
|
||||
|
||||
public String getDocString () {
|
||||
return docstring;
|
||||
}
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (toker.peek().equals(TokenKeyword.FUNCTION))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
return parse(toker, false);
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker, boolean isDecl) throws Exception
|
||||
{
|
||||
NodeFunction n = new NodeFunction();
|
||||
|
||||
// get the function keyword
|
||||
n.setStart(n.requireToken(toker, TokenKeyword.FUNCTION));
|
||||
|
||||
// is the builtin keyword on?
|
||||
if (toker.peek().equals(TokenKeyword.BUILTIN)) {
|
||||
n.builtin = true;
|
||||
n.eatToken(toker);
|
||||
}
|
||||
|
||||
// and the class name or function name (if no class)
|
||||
n.name = n.getIdent(toker);
|
||||
|
||||
// check for a double colon
|
||||
if (toker.peek().equals(TokenPunct.DCOLON)) {
|
||||
// so last ident was the class name
|
||||
n.classname = n.name;
|
||||
n.eatToken(toker);
|
||||
n.name = n.getIdent(toker);
|
||||
}
|
||||
|
||||
// Argument list is optional.
|
||||
if (toker.peek().equals(TokenPunct.LPAREN)) {
|
||||
n.addNode(n.formals = (NodeFormals) NodeFormals.parse(toker));
|
||||
}
|
||||
|
||||
// return type is optional too.
|
||||
if (toker.peek().equals(TokenPunct.COLON)) {
|
||||
n.requireToken(toker, TokenPunct.COLON);
|
||||
n.addNode(n.rettype = (NodeType) NodeType.parse(toker));
|
||||
}
|
||||
|
||||
// docstring
|
||||
if (toker.peek() instanceof TokenStringLiteral) {
|
||||
TokenStringLiteral t = (TokenStringLiteral) n.eatToken(toker);
|
||||
n.docstring = t.getString();
|
||||
}
|
||||
|
||||
// if inside a class declaration, only a declaration now.
|
||||
if (isDecl || n.builtin) {
|
||||
n.requireToken(toker, TokenPunct.SCOLON);
|
||||
return n;
|
||||
}
|
||||
|
||||
// otherwise, parsing the function definition.
|
||||
n.stmts = (NodeStmtBlock) NodeStmtBlock.parse(toker);
|
||||
n.addNode(n.stmts);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception
|
||||
{
|
||||
// keep a reference to the checker for later
|
||||
this.ck = ck;
|
||||
ck.setInFunction(true);
|
||||
|
||||
// reset the functionID -> local funcNum mappings
|
||||
ck.resetFunctionNums();
|
||||
|
||||
// only core and layout layers can define functions
|
||||
if (! l.isCoreOrLayout()) {
|
||||
throw new Exception("Only core and layout layers can define new functions.");
|
||||
}
|
||||
|
||||
// tell the checker we've seen a function now so it knows
|
||||
// later to complain if it then sees a new class declaration.
|
||||
// (builtin functions are okay)
|
||||
if (! builtin)
|
||||
ck.setHitFunction(true);
|
||||
|
||||
String cname = className();
|
||||
String funcID = Checker.functionID(cname, name.getIdent(), formals);
|
||||
Type t = getReturnType();
|
||||
|
||||
if (cname != null && cname.equals(name.getIdent())) {
|
||||
isCtor = true;
|
||||
}
|
||||
|
||||
// if this function is global, no declaration is done, but if
|
||||
// this is class-scoped, we must check the class exists and
|
||||
// that it declares this function.
|
||||
if (cname != null) {
|
||||
NodeClass nc = ck.getClass(cname);
|
||||
if (nc == null) {
|
||||
throw new Exception("Can't declare function "+funcID+" for "+
|
||||
"non-existent class '"+cname+"' at "+
|
||||
getFilePos());
|
||||
}
|
||||
|
||||
Type et = ck.functionType(funcID);
|
||||
if (et == null) {
|
||||
throw new Exception("Can't define undeclared object function "+funcID+" at "+
|
||||
getFilePos());
|
||||
}
|
||||
|
||||
// find & register all the derivative names by which this function
|
||||
// could be called.
|
||||
ListIterator li = nc.getDerClasses().listIterator();
|
||||
while (li.hasNext()) {
|
||||
DerItem dc = (DerItem) li.next();
|
||||
NodeClass c = dc.nc;
|
||||
|
||||
ListIterator fi = NodeFormals.variationIterator(formals, ck);
|
||||
while (fi.hasNext()) {
|
||||
NodeFormals fv = (NodeFormals) fi.next();
|
||||
String derFuncID = Checker.functionID(c.getName(), getName(), fv);
|
||||
ck.setFuncDistance(derFuncID, new DerItem(this, dc.dist));
|
||||
ck.addFunction(derFuncID, t, builtin);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// non-class function. register all variations of the formals.
|
||||
ListIterator fi = NodeFormals.variationIterator(formals, ck);
|
||||
while (fi.hasNext()) {
|
||||
NodeFormals fv = (NodeFormals) fi.next();
|
||||
String derFuncID = Checker.functionID(cname, getName(), fv);
|
||||
ck.setFuncDistance(derFuncID, new DerItem(this, 0));
|
||||
ck.addFunction(derFuncID, t, builtin);
|
||||
}
|
||||
}
|
||||
|
||||
// check the formals
|
||||
if (formals != null)
|
||||
formals.check(l, ck);
|
||||
|
||||
// check the statement block
|
||||
if (stmts != null) {
|
||||
// prepare stmts to be checked
|
||||
stmts.setReturnType(t);
|
||||
|
||||
// make sure $this is accessible in a class method
|
||||
// FIXME: not in static functions, once we have static functions
|
||||
if (cname != null) {
|
||||
stmts.addLocalVar("this", new Type(cname));
|
||||
} else {
|
||||
stmts.addLocalVar("this", Type.VOID); // prevent its use
|
||||
}
|
||||
|
||||
// make sure $this is accessible in a class method
|
||||
// that has a parent.
|
||||
String pname = ck.getParentClassName(cname);
|
||||
if (pname != null) {
|
||||
stmts.addLocalVar("super", new Type(pname));
|
||||
} else {
|
||||
stmts.addLocalVar("super", Type.VOID); // prevent its use
|
||||
}
|
||||
|
||||
if (formals != null)
|
||||
formals.populateScope(stmts);
|
||||
|
||||
ck.setCurrentFunctionClass(cname); // for $.member lookups
|
||||
ck.pushLocalBlock(stmts);
|
||||
stmts.check(l, ck);
|
||||
ck.popLocalBlock();
|
||||
}
|
||||
|
||||
// remember the funcID -> local funcNum mappings for the backend
|
||||
funcNames = ck.getFuncNames();
|
||||
}
|
||||
|
||||
// called by NodeClass
|
||||
public void registerFunction (Checker ck, String cname)
|
||||
throws Exception
|
||||
{
|
||||
String funcID = Checker.functionID(cname, getName(), formals);
|
||||
Type et = ck.functionType(funcID);
|
||||
Type rt = getReturnType();
|
||||
|
||||
// check that function is either currently undefined or
|
||||
// defined with the same type, otherwise complain
|
||||
if (et == null || et.equals(rt)) {
|
||||
ck.addFunction(funcID, rt, builtin); // Register
|
||||
} else {
|
||||
throw new Exception("Can't redefine function '"+getName()+"' with return "+
|
||||
"type of '"+rt+"' at "+getFilePos()+" masking "+
|
||||
"earlier definition of type '"+et+"'.");
|
||||
}
|
||||
}
|
||||
|
||||
public NodeFormals getFormals () {
|
||||
return formals;
|
||||
}
|
||||
|
||||
public String getName () {
|
||||
return name.getIdent();
|
||||
}
|
||||
|
||||
public Type getReturnType () {
|
||||
return (rettype != null ? rettype.getType() : Type.VOID);
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.tabwrite("function " + totalName());
|
||||
if (formals != null) {
|
||||
o.write(" ");
|
||||
formals.asS2(o);
|
||||
}
|
||||
if (rettype != null) {
|
||||
o.write(" : ");
|
||||
rettype.asS2(o);
|
||||
}
|
||||
if (stmts != null) {
|
||||
o.write(" ");
|
||||
stmts.asS2(o);
|
||||
o.newline();
|
||||
} else {
|
||||
o.writeln(";");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
if (classname == null) {
|
||||
o.tabwrite("register_global_function(" +
|
||||
bp.getLayerIDString() + "," +
|
||||
bp.quoteString(name.getIdent() + (formals != null ? formals.toString() : "()")) + "," +
|
||||
bp.quoteString(getReturnType().toString()));
|
||||
if (docstring != null)
|
||||
o.write(", " + bp.quoteString(docstring));
|
||||
o.writeln(");");
|
||||
}
|
||||
|
||||
if (builtin) return;
|
||||
|
||||
o.tabwrite("register_function(" + bp.getLayerIDString() +
|
||||
", [");
|
||||
|
||||
// declare all the names by which this function would be called:
|
||||
// its base name, then all derivative classes which aren't already
|
||||
// used.
|
||||
Iterator i = ck.getFuncIDsIter(this);
|
||||
while (i.hasNext()) {
|
||||
String funcID = (String) i.next();
|
||||
o.write(bp.quoteString(funcID) + ", ");
|
||||
}
|
||||
|
||||
o.writeln("], sub {");
|
||||
o.tabIn();
|
||||
|
||||
// the first time register_function is run, it'll find the
|
||||
// funcNames for this session and save those in a list and then
|
||||
// return the sub which is a closure and will have fast access
|
||||
// to that num -> num hash. (benchmarking showed two
|
||||
// hashlookups on ints was faster than one on strings)
|
||||
|
||||
if (funcNames.size() > 0) {
|
||||
o.tabwriteln("my @_l2g_func = ( undef, ");
|
||||
o.tabIn();
|
||||
ListIterator li = funcNames.listIterator();
|
||||
while (li.hasNext()) {
|
||||
String id = (String) li.next();
|
||||
o.tabwriteln("get_func_num(" +
|
||||
BackendPerl.quoteString(id) + "),");
|
||||
}
|
||||
o.tabOut();
|
||||
o.tabwriteln(");");
|
||||
}
|
||||
|
||||
// now, return the closure
|
||||
o.tabwriteln("return sub {");
|
||||
o.tabIn();
|
||||
|
||||
// setup function argument/ locals
|
||||
o.tabwrite("my ($_ctx");
|
||||
if (classname != null && ! isCtor) {
|
||||
o.write(", $this");
|
||||
}
|
||||
if (formals != null) {
|
||||
ListIterator li = formals.iterator();
|
||||
while (li.hasNext()) {
|
||||
NodeNamedType nt = (NodeNamedType) li.next();
|
||||
o.write(", $"+nt.getName());
|
||||
}
|
||||
}
|
||||
o.writeln(") = @_;");
|
||||
// end function locals
|
||||
|
||||
stmts.asPerl(bp, o, false);
|
||||
o.tabOut();
|
||||
o.tabwriteln("};");
|
||||
|
||||
// end the outer sub
|
||||
o.tabOut();
|
||||
o.tabwriteln("});");
|
||||
}
|
||||
|
||||
public String toString ()
|
||||
{
|
||||
return (className() + "...");
|
||||
}
|
||||
|
||||
public boolean isBuiltin () {
|
||||
return builtin;
|
||||
}
|
||||
|
||||
//-----------------------------
|
||||
|
||||
private String className ()
|
||||
{
|
||||
if (classname != null)
|
||||
return classname.getIdent();
|
||||
return null;
|
||||
}
|
||||
|
||||
private String totalName ()
|
||||
{
|
||||
StringBuffer sb = new StringBuffer(50);
|
||||
|
||||
String clas = className();
|
||||
if (clas != null) {
|
||||
sb.append(clas);
|
||||
sb.append("::");
|
||||
}
|
||||
sb.append(name.getIdent());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
};
|
||||
169
wcmtools/s2/danga/s2/NodeIfStmt.java
Executable file
169
wcmtools/s2/danga/s2/NodeIfStmt.java
Executable file
@@ -0,0 +1,169 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
public class NodeIfStmt extends Node
|
||||
{
|
||||
NodeExpr expr;
|
||||
NodeStmtBlock thenblock;
|
||||
NodeStmtBlock elseblock;
|
||||
LinkedList elseifexprs;
|
||||
LinkedList elseifblocks;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (toker.peek().equals(TokenKeyword.IF))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeIfStmt n = new NodeIfStmt();
|
||||
n.elseifblocks = new LinkedList();
|
||||
n.elseifexprs = new LinkedList();
|
||||
|
||||
n.setStart(n.requireToken(toker, TokenKeyword.IF));
|
||||
n.requireToken(toker, TokenPunct.LPAREN);
|
||||
n.addNode(n.expr = (NodeExpr) NodeExpr.parse(toker));
|
||||
n.requireToken(toker, TokenPunct.RPAREN);
|
||||
n.addNode(n.thenblock = (NodeStmtBlock) NodeStmtBlock.parse(toker));
|
||||
|
||||
while (toker.peek().equals(TokenKeyword.ELSEIF)) {
|
||||
n.eatToken(toker);
|
||||
|
||||
// get the expression.
|
||||
n.requireToken(toker, TokenPunct.LPAREN);
|
||||
Node expr = NodeExpr.parse(toker);
|
||||
n.addNode(expr);
|
||||
n.requireToken(toker, TokenPunct.RPAREN);
|
||||
n.elseifexprs.add(expr);
|
||||
|
||||
// and the block
|
||||
Node nie = NodeStmtBlock.parse(toker);
|
||||
n.addNode(nie);
|
||||
n.elseifblocks.add(nie);
|
||||
}
|
||||
|
||||
if (toker.peek().equals(TokenKeyword.ELSE)) {
|
||||
n.eatToken(toker);
|
||||
n.addNode(n.elseblock = (NodeStmtBlock) NodeStmtBlock.parse(toker));
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
// returns true if and only if the 'then' stmtblock ends in a
|
||||
// return statement, the 'else' stmtblock is non-null and ends
|
||||
// in a return statement, and any elseif stmtblocks end in a return
|
||||
// statement.
|
||||
public boolean willReturn ()
|
||||
{
|
||||
// there must be an else block.
|
||||
if (elseblock == null) return false;
|
||||
|
||||
// both the 'then' and 'else' blocks must return
|
||||
if (! thenblock.willReturn()) return false;
|
||||
if (! elseblock.willReturn()) return false;
|
||||
|
||||
// if there are elseif blocks, all those must return
|
||||
ListIterator li = elseifblocks.listIterator();
|
||||
while (li.hasNext()) {
|
||||
NodeStmtBlock sb = (NodeStmtBlock) li.next();
|
||||
if (! sb.willReturn()) return false;
|
||||
}
|
||||
|
||||
// else, it does return.
|
||||
return true;
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception
|
||||
{
|
||||
Type t = expr.getType(ck);
|
||||
if (! t.equals(Type.BOOL) && ! t.equals(Type.INT)) {
|
||||
throw new Exception("Non-boolean if test at "+getFilePos());
|
||||
}
|
||||
thenblock.check(l, ck);
|
||||
|
||||
ListIterator li;
|
||||
|
||||
li = elseifexprs.listIterator();
|
||||
while (li.hasNext()) {
|
||||
NodeExpr ne = (NodeExpr) li.next();
|
||||
t = ne.getType(ck);
|
||||
if (! t.equals(Type.BOOL) && ! t.equals(Type.INT))
|
||||
throw new Exception("Non-boolean elseif test at "+ne.getFilePos());
|
||||
}
|
||||
|
||||
li = elseifblocks.listIterator();
|
||||
while (li.hasNext()) {
|
||||
NodeStmtBlock sb = (NodeStmtBlock) li.next();
|
||||
sb.check(l, ck);
|
||||
}
|
||||
|
||||
if (elseblock != null)
|
||||
elseblock.check(l, ck);
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
// if
|
||||
o.tabwrite("if (");
|
||||
expr.asS2(o);
|
||||
o.write(") ");
|
||||
thenblock.asS2(o);
|
||||
|
||||
// else-if
|
||||
ListIterator li = elseifexprs.listIterator(0);
|
||||
ListIterator lib = elseifblocks.listIterator(0);
|
||||
while (li.hasNext()) {
|
||||
NodeExpr expr = (NodeExpr) li.next();
|
||||
NodeStmtBlock block = (NodeStmtBlock) lib.next();
|
||||
|
||||
o.write(" elseif (");
|
||||
expr.asS2(o);
|
||||
o.write(") ");
|
||||
block.asS2(o);
|
||||
}
|
||||
|
||||
|
||||
// else
|
||||
if (elseblock != null) {
|
||||
o.write(" else ");
|
||||
elseblock.asS2(o);
|
||||
}
|
||||
o.newline();
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
// if
|
||||
o.tabwrite("if (");
|
||||
expr.asPerl(bp, o);
|
||||
o.write(") ");
|
||||
thenblock.asPerl(bp, o);
|
||||
|
||||
// else-if
|
||||
ListIterator li = elseifexprs.listIterator(0);
|
||||
ListIterator lib = elseifblocks.listIterator(0);
|
||||
while (li.hasNext()) {
|
||||
NodeExpr expr = (NodeExpr) li.next();
|
||||
NodeStmtBlock block = (NodeStmtBlock) lib.next();
|
||||
o.write(" elsif (");
|
||||
expr.asPerl(bp, o);
|
||||
o.write(") ");
|
||||
block.asPerl(bp, o);
|
||||
}
|
||||
|
||||
|
||||
// else
|
||||
if (elseblock != null) {
|
||||
o.write(" else ");
|
||||
elseblock.asPerl(bp, o);
|
||||
}
|
||||
o.newline();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
70
wcmtools/s2/danga/s2/NodeIncExpr.java
Executable file
70
wcmtools/s2/danga/s2/NodeIncExpr.java
Executable file
@@ -0,0 +1,70 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeIncExpr extends Node
|
||||
{
|
||||
Node expr;
|
||||
TokenPunct op;
|
||||
boolean bPre = false;
|
||||
boolean bPost = false;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return (toker.peek().equals(TokenPunct.INC) ||
|
||||
toker.peek().equals(TokenPunct.DEC) ||
|
||||
NodeTerm.canStart(toker));
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeIncExpr n = new NodeIncExpr();
|
||||
|
||||
if (toker.peek().equals(TokenPunct.INC) ||
|
||||
toker.peek().equals(TokenPunct.DEC)) {
|
||||
n.bPre = true;
|
||||
n.op = (TokenPunct) toker.peek();
|
||||
n.setStart(n.eatToken(toker));
|
||||
n.skipWhite(toker);
|
||||
}
|
||||
|
||||
Node expr = NodeTerm.parse(toker);
|
||||
|
||||
if (toker.peek().equals(TokenPunct.INC) ||
|
||||
toker.peek().equals(TokenPunct.DEC)) {
|
||||
if (n.bPre) throw new Exception("Unexpected -- or ++");
|
||||
n.bPost = true;
|
||||
n.op = (TokenPunct) toker.peek();
|
||||
n.eatToken(toker);
|
||||
n.skipWhite(toker);
|
||||
}
|
||||
|
||||
if (n.bPre || n.bPost) {
|
||||
n.expr = expr;
|
||||
return n;
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
if (! expr.isLValue()) {
|
||||
throw new Exception("Post/pre-increment must operate on lvalue at "+
|
||||
expr.getFilePos());
|
||||
}
|
||||
return expr.getType(ck);
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
if (bPre) { o.write(op.getPunct()); }
|
||||
expr.asS2(o);
|
||||
if (bPost) { o.write(op.getPunct()); }
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
if (bPre) { o.write(op.getPunct()); }
|
||||
expr.asPerl(bp, o);
|
||||
if (bPost) { o.write(op.getPunct()); }
|
||||
}
|
||||
|
||||
}
|
||||
64
wcmtools/s2/danga/s2/NodeLayerInfo.java
Executable file
64
wcmtools/s2/danga/s2/NodeLayerInfo.java
Executable file
@@ -0,0 +1,64 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class NodeLayerInfo extends Node
|
||||
{
|
||||
String key;
|
||||
String val;
|
||||
|
||||
public String getKey () { return key; }
|
||||
public String getValue () { return val; }
|
||||
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (toker.peek().equals(TokenKeyword.LAYERINFO))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeLayerInfo n = new NodeLayerInfo();
|
||||
|
||||
NodeText nkey, nval;
|
||||
|
||||
n.requireToken(toker, TokenKeyword.LAYERINFO);
|
||||
n.addNode(nkey = (NodeText) NodeText.parse(toker));
|
||||
n.requireToken(toker, TokenPunct.ASSIGN);
|
||||
n.addNode(nval = (NodeText) NodeText.parse(toker));
|
||||
n.requireToken(toker, TokenPunct.SCOLON);
|
||||
|
||||
n.key = nkey.getText();
|
||||
n.val = nval.getText();
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.tabwrite("layerinfo ");
|
||||
o.write(Backend.quoteString(key));
|
||||
o.write(" = ");
|
||||
o.write(Backend.quoteString(val));
|
||||
o.writeln(";");
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
o.tabwriteln("set_layer_info("+
|
||||
bp.getLayerIDString() + "," +
|
||||
bp.quoteString(key) + "," +
|
||||
bp.quoteString(val) + ");");
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception
|
||||
{
|
||||
l.setLayerInfo(key, val);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
60
wcmtools/s2/danga/s2/NodeLogAndExpr.java
Executable file
60
wcmtools/s2/danga/s2/NodeLogAndExpr.java
Executable file
@@ -0,0 +1,60 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeLogAndExpr extends Node
|
||||
{
|
||||
Node lhs;
|
||||
Node rhs;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return NodeEqExpr.canStart(toker);
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeLogAndExpr n = new NodeLogAndExpr();
|
||||
|
||||
n.lhs = NodeEqExpr.parse(toker);
|
||||
n.addNode(n.lhs);
|
||||
|
||||
Token t = toker.peek();
|
||||
if (t.equals(TokenKeyword.AND)) {
|
||||
n.eatToken(toker);
|
||||
} else {
|
||||
return n.lhs;
|
||||
}
|
||||
|
||||
n.rhs = NodeEqExpr.parse(toker);
|
||||
n.addNode(n.rhs);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
Type lt = lhs.getType(ck);
|
||||
Type rt = rhs.getType(ck);
|
||||
if (! lt.equals(rt) || ! lt.isBoolable())
|
||||
throw new Exception("The left and right side of the 'and' expression must "+
|
||||
"both be of either type bool or int at "+getFilePos());
|
||||
return lt;
|
||||
}
|
||||
|
||||
|
||||
public void asS2 (Indenter o) {
|
||||
lhs.asS2(o);
|
||||
if (rhs != null) {
|
||||
o.write(" and ");
|
||||
rhs.asS2(o);
|
||||
}
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o) {
|
||||
lhs.asPerl(bp, o);
|
||||
if (rhs != null) {
|
||||
o.write(" && ");
|
||||
rhs.asPerl(bp, o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
61
wcmtools/s2/danga/s2/NodeLogOrExpr.java
Executable file
61
wcmtools/s2/danga/s2/NodeLogOrExpr.java
Executable file
@@ -0,0 +1,61 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeLogOrExpr extends Node
|
||||
{
|
||||
Node lhs;
|
||||
TokenKeyword op;
|
||||
Node rhs;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return NodeLogAndExpr.canStart(toker);
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeLogOrExpr n = new NodeLogOrExpr();
|
||||
|
||||
n.lhs = NodeLogAndExpr.parse(toker);
|
||||
n.addNode(n.lhs);
|
||||
|
||||
Token t = toker.peek();
|
||||
|
||||
if (t.equals(TokenKeyword.OR) || t.equals(TokenKeyword.XOR)) {
|
||||
n.op = (TokenKeyword) t;
|
||||
n.eatToken(toker);
|
||||
} else {
|
||||
return n.lhs;
|
||||
}
|
||||
|
||||
n.rhs = NodeLogOrExpr.parse(toker);
|
||||
n.addNode(n.rhs);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
Type lt = lhs.getType(ck);
|
||||
Type rt = rhs.getType(ck);
|
||||
if (! lt.equals(rt) || ! lt.isBoolable())
|
||||
throw new Exception("The left and right side of the 'or' expression must "+
|
||||
"both be of either type bool or int at "+getFilePos());
|
||||
return lt;
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o) {
|
||||
lhs.asS2(o);
|
||||
if (rhs != null) {
|
||||
o.write(" " + op.getIdent() + " ");
|
||||
rhs.asS2(o);
|
||||
}
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o) {
|
||||
lhs.asPerl(bp, o);
|
||||
if (rhs != null) {
|
||||
o.write(" || ");
|
||||
rhs.asPerl(bp, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
48
wcmtools/s2/danga/s2/NodeNamedType.java
Executable file
48
wcmtools/s2/danga/s2/NodeNamedType.java
Executable file
@@ -0,0 +1,48 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeNamedType extends Node
|
||||
{
|
||||
public Type type;
|
||||
public NodeType typenode;
|
||||
public String name;
|
||||
|
||||
public Type getType () {
|
||||
return type;
|
||||
}
|
||||
public String getName () {
|
||||
return name;
|
||||
}
|
||||
|
||||
public NodeNamedType () {
|
||||
}
|
||||
|
||||
public NodeNamedType (String name, Type type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeNamedType n = new NodeNamedType();
|
||||
|
||||
n.typenode = (NodeType) NodeType.parse(toker);
|
||||
n.type = n.typenode.getType();
|
||||
|
||||
n.addNode(n.typenode);
|
||||
n.name = n.getIdent(toker).getIdent();
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
typenode.asS2(o);
|
||||
o.write(" " + name);
|
||||
}
|
||||
|
||||
public String toString () // was asString
|
||||
{
|
||||
return type.toString() + " " + name;
|
||||
}
|
||||
|
||||
};
|
||||
66
wcmtools/s2/danga/s2/NodePrintStmt.java
Executable file
66
wcmtools/s2/danga/s2/NodePrintStmt.java
Executable file
@@ -0,0 +1,66 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodePrintStmt extends Node
|
||||
{
|
||||
NodeExpr expr;
|
||||
boolean doNewline = false;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (toker.peek().equals(TokenKeyword.PRINT) ||
|
||||
toker.peek().equals(TokenKeyword.PRINTLN) ||
|
||||
toker.peek() instanceof TokenStringLiteral)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodePrintStmt n = new NodePrintStmt();
|
||||
Token t = toker.peek();
|
||||
|
||||
if (t.equals(TokenKeyword.PRINT)) {
|
||||
n.setStart(n.eatToken(toker));
|
||||
}
|
||||
if (t.equals(TokenKeyword.PRINTLN)) {
|
||||
n.setStart(n.eatToken(toker));
|
||||
n.doNewline = true;
|
||||
}
|
||||
|
||||
n.addNode(n.expr = (NodeExpr) NodeExpr.parse(toker));
|
||||
n.requireToken(toker, TokenPunct.SCOLON);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception
|
||||
{
|
||||
Type t = expr.getType(ck);
|
||||
if (t.equals(Type.INT) || t.equals(Type.STRING)) {
|
||||
return;
|
||||
}
|
||||
throw new Exception("Print statement must print an expression of type "
|
||||
+"int or string, not "+t+" at "+expr.getFilePos());
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
if (doNewline)
|
||||
o.tabwrite("println ");
|
||||
else
|
||||
o.tabwrite("print ");
|
||||
expr.asS2(o);
|
||||
o.writeln(";");
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
o.tabwrite("pout(");
|
||||
expr.asPerl(bp, o);
|
||||
if (doNewline) {
|
||||
o.write(" . \"\\n\"");
|
||||
}
|
||||
o.writeln(");");
|
||||
}
|
||||
|
||||
};
|
||||
89
wcmtools/s2/danga/s2/NodeProduct.java
Executable file
89
wcmtools/s2/danga/s2/NodeProduct.java
Executable file
@@ -0,0 +1,89 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeProduct extends Node
|
||||
{
|
||||
Node lhs;
|
||||
TokenPunct op;
|
||||
Node rhs;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return NodeUnaryExpr.canStart(toker);
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
Node lhs = NodeUnaryExpr.parse(toker);
|
||||
|
||||
while (toker.peek().equals(TokenPunct.MULT) ||
|
||||
toker.peek().equals(TokenPunct.DIV) ||
|
||||
toker.peek().equals(TokenPunct.MOD)) {
|
||||
lhs = parseAnother(toker, lhs);
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
|
||||
private static Node parseAnother (Tokenizer toker, Node lhs) throws Exception
|
||||
{
|
||||
NodeProduct n = new NodeProduct();
|
||||
|
||||
n.lhs = lhs;
|
||||
n.addNode(n.lhs);
|
||||
|
||||
n.op = (TokenPunct) toker.peek();
|
||||
n.eatToken(toker);
|
||||
n.skipWhite(toker);
|
||||
|
||||
n.rhs = NodeUnaryExpr.parse(toker);
|
||||
n.addNode(n.rhs);
|
||||
n.skipWhite(toker);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
Type lt = lhs.getType(ck);
|
||||
Type rt = rhs.getType(ck);
|
||||
if (! rt.equals(Type.INT)) {
|
||||
throw new Exception("Right hand side of " + op.getPunct() + " operator is not an integer at "+
|
||||
rhs.getFilePos());
|
||||
}
|
||||
if (! lt.equals(Type.INT)) {
|
||||
throw new Exception("Left hand side of " + op.getPunct() + " operator is not an integer at "+
|
||||
lhs.getFilePos());
|
||||
}
|
||||
|
||||
return Type.INT;
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
BackendS2.LParen(o);
|
||||
lhs.asS2(o);
|
||||
if (op != null) {
|
||||
o.write(" " + op.getPunct() + " ");
|
||||
rhs.asS2(o);
|
||||
}
|
||||
BackendS2.RParen(o);
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
if (op == TokenPunct.DIV)
|
||||
o.write("int(");
|
||||
lhs.asPerl(bp, o);
|
||||
if (op != null) {
|
||||
if (op == TokenPunct.MULT)
|
||||
o.write(" * ");
|
||||
else if (op == TokenPunct.DIV)
|
||||
o.write(" / ");
|
||||
else if (op == TokenPunct.MOD)
|
||||
o.write(" % ");
|
||||
rhs.asPerl(bp, o);
|
||||
if (op == TokenPunct.DIV)
|
||||
o.write(")");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
189
wcmtools/s2/danga/s2/NodeProperty.java
Executable file
189
wcmtools/s2/danga/s2/NodeProperty.java
Executable file
@@ -0,0 +1,189 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
public class NodeProperty extends Node
|
||||
{
|
||||
NodeNamedType nt;
|
||||
LinkedList pairs;
|
||||
boolean builtin = false, use = false, hide = false;
|
||||
String uhName; // if use or hide, then this is property to use/hide
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (toker.peek().equals(TokenKeyword.PROPERTY))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeProperty n = new NodeProperty();
|
||||
n.pairs = new LinkedList();
|
||||
|
||||
n.setStart(n.requireToken(toker, TokenKeyword.PROPERTY));
|
||||
|
||||
if (toker.peek().equals(TokenKeyword.BUILTIN)) {
|
||||
n.builtin = true;
|
||||
n.eatToken(toker);
|
||||
}
|
||||
|
||||
// parse the use/hide case
|
||||
if (toker.peek() instanceof TokenIdent) {
|
||||
String ident = ((TokenIdent) toker.peek()).getIdent();
|
||||
if (ident.equals("use") || ident.equals("hide")) {
|
||||
if (ident.equals("use")) n.use = true;
|
||||
if (ident.equals("hide")) n.hide = true;
|
||||
n.eatToken(toker);
|
||||
|
||||
Token t = toker.peek();
|
||||
if (! (t instanceof TokenIdent)) {
|
||||
throw new Exception("Expecting identifer after "+ident+" at "+t.getFilePos());
|
||||
}
|
||||
n.uhName = ((TokenIdent) toker.peek()).getIdent();
|
||||
n.eatToken(toker);
|
||||
n.requireToken(toker, TokenPunct.SCOLON);
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
n.addNode(n.nt = (NodeNamedType) NodeNamedType.parse(toker));
|
||||
|
||||
Token t = toker.peek();
|
||||
if (t.equals(TokenPunct.SCOLON)) {
|
||||
n.eatToken(toker);
|
||||
return n;
|
||||
}
|
||||
|
||||
n.requireToken(toker, TokenPunct.LBRACE);
|
||||
while (NodePropertyPair.canStart(toker)) {
|
||||
Node pair = NodePropertyPair.parse(toker);
|
||||
n.tokenlist.add(pair);
|
||||
n.pairs.add(pair);
|
||||
}
|
||||
n.requireToken(toker, TokenPunct.RBRACE);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception
|
||||
{
|
||||
if (use) {
|
||||
if (! l.getType().equals("layout")) {
|
||||
throw new Exception("Can't declare property usage in non-layout layer at"
|
||||
+ getFilePos());
|
||||
}
|
||||
if (ck.propertyType(uhName) == null) {
|
||||
throw new Exception("Can't declare usage of non-existent property at"
|
||||
+ getFilePos());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (hide) {
|
||||
if (ck.propertyType(uhName) == null) {
|
||||
throw new Exception("Can't hide non-existent property at"
|
||||
+ getFilePos());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
String name = nt.getName();
|
||||
Type type = nt.getType();
|
||||
|
||||
if (l.getType().equals("i18n")) {
|
||||
// FIXME: as a special case, allow an i18n layer to
|
||||
// to override the 'des' property of a property, so
|
||||
// that stuff can be translated
|
||||
return;
|
||||
}
|
||||
|
||||
// only core and layout layers can define properties
|
||||
if (! l.isCoreOrLayout()) {
|
||||
throw new Exception("Only core and layout layers can define new properties.");
|
||||
}
|
||||
|
||||
// make sure they aren't overriding a property from a lower layer
|
||||
Type existing = ck.propertyType(name);
|
||||
if (existing != null && ! type.equals(existing)) {
|
||||
throw new Exception("Can't override property '" + name +
|
||||
"' at " + getFilePos() + " of type "+existing+
|
||||
" with new type "+type+".");
|
||||
}
|
||||
|
||||
String basetype = type.baseType();
|
||||
if (! Type.isPrimitive(basetype) && ck.getClass(basetype) == null) {
|
||||
throw new Exception("Can't define a property of an unknown class "+
|
||||
"at "+nt.getFilePos());
|
||||
}
|
||||
|
||||
// all is well, so register this property with its type
|
||||
ck.addProperty(name, type);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.tabwrite("property ");
|
||||
if (builtin) { o.write("builtin "); }
|
||||
if (use || hide) {
|
||||
if (use) o.write("use ");
|
||||
if (hide) o.write("hide ");
|
||||
o.write(uhName);
|
||||
o.writeln(";");
|
||||
return;
|
||||
}
|
||||
nt.asS2(o);
|
||||
if (pairs.size() > 0) {
|
||||
o.writeln(" {");
|
||||
o.tabIn();
|
||||
ListIterator li = pairs.listIterator(0);
|
||||
while (li.hasNext()) {
|
||||
NodePropertyPair pp = (NodePropertyPair) li.next();
|
||||
pp.asS2(o);
|
||||
}
|
||||
o.tabOut();
|
||||
o.writeln("}");
|
||||
} else {
|
||||
o.writeln(";");
|
||||
}
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
if (use) {
|
||||
o.tabwriteln("register_property_use("+
|
||||
bp.getLayerIDString() + "," +
|
||||
bp.quoteString(uhName) + ");");
|
||||
return;
|
||||
}
|
||||
|
||||
if (hide) {
|
||||
o.tabwriteln("register_property_hide("+
|
||||
bp.getLayerIDString() + "," +
|
||||
bp.quoteString(uhName) + ");");
|
||||
return;
|
||||
}
|
||||
|
||||
o.tabwriteln("register_property("+
|
||||
bp.getLayerIDString() + "," +
|
||||
bp.quoteString(nt.getName()) + ",{");
|
||||
o.tabIn();
|
||||
o.tabwriteln("\"type\"=>" +
|
||||
bp.quoteString(nt.getType().toString())+",");
|
||||
|
||||
ListIterator li = pairs.listIterator();
|
||||
|
||||
while (li.hasNext()) {
|
||||
NodePropertyPair pp = (NodePropertyPair) li.next();
|
||||
o.tabwriteln(bp.quoteString(pp.getKey()) + "=>" +
|
||||
bp.quoteString(pp.getVal()) + ",");
|
||||
|
||||
}
|
||||
o.tabOut();
|
||||
o.writeln("});");
|
||||
}
|
||||
|
||||
};
|
||||
40
wcmtools/s2/danga/s2/NodePropertyPair.java
Executable file
40
wcmtools/s2/danga/s2/NodePropertyPair.java
Executable file
@@ -0,0 +1,40 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodePropertyPair extends Node
|
||||
{
|
||||
NodeText key;
|
||||
NodeText val;
|
||||
|
||||
public String getKey () { return key.getText(); }
|
||||
public String getVal () { return val.getText(); }
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (NodeText.canStart(toker))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodePropertyPair n = new NodePropertyPair();
|
||||
|
||||
n.addNode(n.key = (NodeText) NodeText.parse(toker));
|
||||
n.requireToken(toker, TokenPunct.ASSIGN);
|
||||
n.addNode(n.val = (NodeText) NodeText.parse(toker));
|
||||
n.requireToken(toker, TokenPunct.SCOLON);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.doTab();
|
||||
key.asS2(o);
|
||||
o.write(" = ");
|
||||
val.asS2(o);
|
||||
o.writeln(";");
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
77
wcmtools/s2/danga/s2/NodeRange.java
Executable file
77
wcmtools/s2/danga/s2/NodeRange.java
Executable file
@@ -0,0 +1,77 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeRange extends Node
|
||||
{
|
||||
Node lhs;
|
||||
Node rhs;
|
||||
|
||||
public NodeRange() {
|
||||
}
|
||||
public NodeRange(Node start, Node end) {
|
||||
this.lhs = start;
|
||||
this.rhs = end;
|
||||
}
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return NodeLogOrExpr.canStart(toker);
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeRange n = new NodeRange();
|
||||
|
||||
n.lhs = NodeLogOrExpr.parse(toker);
|
||||
n.addNode(n.lhs);
|
||||
|
||||
if (toker.peek().equals(TokenPunct.DOTDOT)) {
|
||||
n.eatToken(toker);
|
||||
n.skipWhite(toker);
|
||||
} else {
|
||||
return n.lhs;
|
||||
}
|
||||
|
||||
n.rhs = NodeLogOrExpr.parse(toker);
|
||||
n.addNode(n.rhs);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
return getType(ck, null);
|
||||
}
|
||||
|
||||
public Type getType (Checker ck, Type wanted) throws Exception
|
||||
{
|
||||
Type lt = lhs.getType(ck, wanted);
|
||||
Type rt = rhs.getType(ck, wanted);
|
||||
|
||||
if (! lt.equals(Type.INT)) {
|
||||
throw new Exception("Left operand of '..' range operator is not int at "+lhs.getFilePos());
|
||||
}
|
||||
if (! rt.equals(Type.INT)) {
|
||||
throw new Exception("Right operand of '..' range operator is not int at "+rhs.getFilePos());
|
||||
}
|
||||
Type ret = new Type("int");
|
||||
ret.makeArrayOf();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
lhs.asS2(o);
|
||||
o.write(" .. ");
|
||||
rhs.asS2(o);
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
o.write("[");
|
||||
lhs.asPerl(bp, o);
|
||||
o.write(" .. ");
|
||||
rhs.asPerl(bp, o);
|
||||
o.write("]");
|
||||
}
|
||||
|
||||
}
|
||||
93
wcmtools/s2/danga/s2/NodeRelExpr.java
Executable file
93
wcmtools/s2/danga/s2/NodeRelExpr.java
Executable file
@@ -0,0 +1,93 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeRelExpr extends Node
|
||||
{
|
||||
Node lhs;
|
||||
TokenPunct op;
|
||||
Node rhs;
|
||||
|
||||
private Type myType; // for backend later
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return NodeSum.canStart(toker);
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeRelExpr n = new NodeRelExpr();
|
||||
|
||||
n.lhs = NodeSum.parse(toker);
|
||||
n.addNode(n.lhs);
|
||||
|
||||
Token t = toker.peek();
|
||||
|
||||
if (t.equals(TokenPunct.LT) || t.equals(TokenPunct.LTE) ||
|
||||
t.equals(TokenPunct.GT) || t.equals(TokenPunct.GTE)) {
|
||||
n.op = (TokenPunct) t;
|
||||
n.eatToken(toker);
|
||||
n.skipWhite(toker);
|
||||
} else {
|
||||
return n.lhs;
|
||||
}
|
||||
|
||||
n.rhs = NodeSum.parse(toker);
|
||||
n.skipWhite(toker);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
Type lt = lhs.getType(ck);
|
||||
Type rt = rhs.getType(ck);
|
||||
if (! lt.equals(rt))
|
||||
throw new Exception("The types of the left and right hand side of "+
|
||||
"comparision test expression don't match at "+getFilePos());
|
||||
if (lt.equals(Type.STRING) || lt.equals(Type.INT)) {
|
||||
myType = lt;
|
||||
return Type.BOOL;
|
||||
}
|
||||
throw new Exception ("Only string and int types can be compared at "+
|
||||
getFilePos());
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
lhs.asS2(o);
|
||||
if (op != null) {
|
||||
o.write(" " + op.getPunct() + " ");
|
||||
rhs.asS2(o);
|
||||
}
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
lhs.asPerl(bp, o);
|
||||
if (op != null) {
|
||||
if (op.equals(TokenPunct.LT)) {
|
||||
if (myType.equals(Type.STRING))
|
||||
o.write(" lt ");
|
||||
else
|
||||
o.write(" < ");
|
||||
} else if (op.equals(TokenPunct.LTE)) {
|
||||
if (myType.equals(Type.STRING))
|
||||
o.write(" le ");
|
||||
else
|
||||
o.write(" <= ");
|
||||
} else if (op.equals(TokenPunct.GT)) {
|
||||
if (myType.equals(Type.STRING))
|
||||
o.write(" gt ");
|
||||
else
|
||||
o.write(" > ");
|
||||
} else if (op.equals(TokenPunct.GTE)) {
|
||||
if (myType.equals(Type.STRING))
|
||||
o.write(" ge ");
|
||||
else
|
||||
o.write(" >= ");
|
||||
}
|
||||
rhs.asPerl(bp, o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
63
wcmtools/s2/danga/s2/NodeReturnStmt.java
Executable file
63
wcmtools/s2/danga/s2/NodeReturnStmt.java
Executable file
@@ -0,0 +1,63 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeReturnStmt extends Node
|
||||
{
|
||||
NodeExpr expr;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (toker.peek().equals(TokenKeyword.RETURN))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeReturnStmt n = new NodeReturnStmt();
|
||||
|
||||
n.setStart(n.requireToken(toker, TokenKeyword.RETURN));
|
||||
|
||||
// optional return expression
|
||||
if (NodeExpr.canStart(toker)) {
|
||||
n.addNode(n.expr = (NodeExpr) NodeExpr.parse(toker));
|
||||
}
|
||||
|
||||
n.requireToken(toker, TokenPunct.SCOLON);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception
|
||||
{
|
||||
Type exptype = ck.getReturnType();
|
||||
Type rettype = expr != null ? expr.getType(ck) : Type.VOID;
|
||||
|
||||
if (! ck.typeIsa(rettype, exptype)) {
|
||||
throw new Exception("Return type of "+rettype+" at "+
|
||||
getFilePos()+" doesn't match expected type of "+
|
||||
exptype+" for this function.");
|
||||
}
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.tabwrite("return");
|
||||
if (expr != null) {
|
||||
o.write(" ");
|
||||
expr.asS2(o);
|
||||
}
|
||||
o.writeln(";");
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
o.tabwrite("return");
|
||||
if (expr != null) {
|
||||
o.write(" ");
|
||||
expr.asPerl(bp, o);
|
||||
}
|
||||
o.writeln(";");
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
90
wcmtools/s2/danga/s2/NodeSet.java
Executable file
90
wcmtools/s2/danga/s2/NodeSet.java
Executable file
@@ -0,0 +1,90 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeSet extends Node
|
||||
{
|
||||
String key;
|
||||
NodeExpr value;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (toker.peek().equals(TokenKeyword.SET))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeText nkey;
|
||||
NodeSet ns = new NodeSet();
|
||||
|
||||
ns.setStart(ns.requireToken(toker, TokenKeyword.SET));
|
||||
|
||||
nkey = (NodeText) NodeText.parse(toker);
|
||||
ns.addNode(nkey);
|
||||
|
||||
ns.requireToken(toker, TokenPunct.ASSIGN);
|
||||
|
||||
ns.value = (NodeExpr) NodeExpr.parse(toker);
|
||||
ns.addNode(ns.value);
|
||||
|
||||
ns.requireToken(toker, TokenPunct.SCOLON);
|
||||
|
||||
ns.key = nkey.getText();
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.tabwrite("set ");
|
||||
o.write(Backend.quoteString(key));
|
||||
o.write(" = ");
|
||||
value.asS2(o);
|
||||
o.writeln(";");
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception
|
||||
{
|
||||
Type ltype = ck.propertyType(key);
|
||||
|
||||
ck.setInFunction(false);
|
||||
|
||||
// check to see that the thing we're setting exists
|
||||
if (ltype == null) {
|
||||
throw new Exception("Can't set non-existent property '" + key + "' at " +
|
||||
getFilePos());
|
||||
}
|
||||
|
||||
Type rtype = value.getType(ck, ltype);
|
||||
|
||||
if (! ltype.equals(rtype)) {
|
||||
throw new Exception("Property value is of wrong type at "+getFilePos());
|
||||
}
|
||||
|
||||
// simple case... assigning a primitive
|
||||
if (ltype.isPrimitive()) {
|
||||
// TODO: check that value.isLiteral()
|
||||
// TODO: check value's type matches
|
||||
return;
|
||||
}
|
||||
|
||||
Type base = new Type(ltype.baseType());
|
||||
if (base.isPrimitive()) {
|
||||
return;
|
||||
} else if (ck.getClass(ltype.baseType()) == null) {
|
||||
throw new Exception("Can't set property of unknown type at "+
|
||||
getFilePos());
|
||||
}
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
o.tabwrite("register_set("+
|
||||
bp.getLayerIDString() + "," +
|
||||
bp.quoteString(key) + ",");
|
||||
value.asPerl(bp, o);
|
||||
o.writeln(");");
|
||||
return;
|
||||
}
|
||||
|
||||
};
|
||||
48
wcmtools/s2/danga/s2/NodeStmt.java
Executable file
48
wcmtools/s2/danga/s2/NodeStmt.java
Executable file
@@ -0,0 +1,48 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeStmt extends Node
|
||||
{
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (NodePrintStmt.canStart(toker) ||
|
||||
NodeIfStmt.canStart(toker) ||
|
||||
NodeReturnStmt.canStart(toker) ||
|
||||
NodeDeleteStmt.canStart(toker) ||
|
||||
NodeForeachStmt.canStart(toker) ||
|
||||
NodeVarDeclStmt.canStart(toker) ||
|
||||
NodeExprStmt.canStart(toker) ||
|
||||
false)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (NodePrintStmt.canStart(toker))
|
||||
return NodePrintStmt.parse(toker);
|
||||
|
||||
if (NodeIfStmt.canStart(toker))
|
||||
return NodeIfStmt.parse(toker);
|
||||
|
||||
if (NodeReturnStmt.canStart(toker))
|
||||
return NodeReturnStmt.parse(toker);
|
||||
|
||||
if (NodeDeleteStmt.canStart(toker))
|
||||
return NodeDeleteStmt.parse(toker);
|
||||
|
||||
if (NodeForeachStmt.canStart(toker))
|
||||
return NodeForeachStmt.parse(toker);
|
||||
|
||||
if (NodeVarDeclStmt.canStart(toker))
|
||||
return NodeVarDeclStmt.parse(toker);
|
||||
|
||||
// important that this is last:
|
||||
//(otherwise idents would be seen as function calls)
|
||||
if (NodeExprStmt.canStart(toker))
|
||||
return NodeExprStmt.parse(toker);
|
||||
|
||||
throw new Exception("don't know how to parse this type of statement");
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
152
wcmtools/s2/danga/s2/NodeStmtBlock.java
Executable file
152
wcmtools/s2/danga/s2/NodeStmtBlock.java
Executable file
@@ -0,0 +1,152 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Hashtable;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class NodeStmtBlock extends Node
|
||||
{
|
||||
protected LinkedList stmtlist = new LinkedList ();
|
||||
protected Type returnType;
|
||||
|
||||
protected Hashtable localvars = new Hashtable (); // String -> Type
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeStmtBlock ns = new NodeStmtBlock();
|
||||
ns.setStart(ns.requireToken(toker, TokenPunct.LBRACE));
|
||||
boolean loop = true;
|
||||
boolean closed = false;
|
||||
|
||||
do {
|
||||
ns.skipWhite(toker);
|
||||
Token p = toker.peek();
|
||||
|
||||
if (p == null) {
|
||||
loop = false;
|
||||
} else if (p.equals(TokenPunct.RBRACE)) {
|
||||
ns.eatToken(toker);
|
||||
loop = false;
|
||||
closed = true;
|
||||
|
||||
}
|
||||
else if (NodeStmt.canStart(toker)) {
|
||||
Node s = NodeStmt.parse(toker);
|
||||
ns.stmtlist.add(s);
|
||||
ns.addNode(s);
|
||||
}
|
||||
else {
|
||||
throw new Exception("Unexpected token at " + toker.locationString() +
|
||||
" while parsing statement block: " + p.toString());
|
||||
}
|
||||
|
||||
}
|
||||
while (loop);
|
||||
|
||||
if (! closed)
|
||||
throw new Exception("Didn't find closing brace in statement block");
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
public void addLocalVar (String v, Type t) {
|
||||
localvars.put(v, t);
|
||||
}
|
||||
public Type getLocalVar (String v) {
|
||||
return (Type) localvars.get(v);
|
||||
}
|
||||
|
||||
public void setReturnType (Type t) {
|
||||
returnType = t;
|
||||
}
|
||||
|
||||
public boolean willReturn ()
|
||||
{
|
||||
Node ns;
|
||||
|
||||
// find the last statement in the block, if one exists
|
||||
try {
|
||||
ns = (Node) stmtlist.getLast();
|
||||
} catch (NoSuchElementException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ns instanceof NodeReturnStmt) {
|
||||
// a return statement obviously returns
|
||||
return true;
|
||||
} else if (ns instanceof NodeIfStmt) {
|
||||
// and if statement at the end of a function returns
|
||||
// if all paths return, so ask the ifstatement
|
||||
NodeIfStmt ni = (NodeIfStmt) ns;
|
||||
return ni.willReturn();
|
||||
} else {
|
||||
// all other types of statements don't return
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception
|
||||
{
|
||||
ListIterator li = stmtlist.listIterator();
|
||||
|
||||
// set the return type for any returnstmts that need it.
|
||||
// NOTE: the returnType is non-null if and only if it's
|
||||
// attached to a function.
|
||||
if (returnType != null) {
|
||||
ck.setReturnType(returnType);
|
||||
}
|
||||
|
||||
while (li.hasNext()) {
|
||||
Node ns = (Node) li.next();
|
||||
ns.check(l, ck);
|
||||
|
||||
if (! li.hasNext() &&
|
||||
returnType != null &&
|
||||
! returnType.equals(Type.VOID) &&
|
||||
! willReturn()) {
|
||||
throw new Exception("Statement block at "+getFilePos()+
|
||||
" isn't guaranteed to return type "+
|
||||
returnType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o) {
|
||||
o.writeln("{");
|
||||
o.tabIn();
|
||||
ListIterator li = stmtlist.listIterator();
|
||||
while (li.hasNext()) {
|
||||
Node ns = (Node) li.next();
|
||||
ns.asS2(o);
|
||||
}
|
||||
o.tabOut();
|
||||
o.tabwrite("}");
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
asPerl(bp, o, true);
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o, boolean doCurlies)
|
||||
{
|
||||
if (doCurlies) {
|
||||
o.writeln("{");
|
||||
o.tabIn();
|
||||
}
|
||||
|
||||
ListIterator li = stmtlist.listIterator();
|
||||
while (li.hasNext()) {
|
||||
Node n = (Node) li.next();
|
||||
n.asPerl(bp, o);
|
||||
}
|
||||
|
||||
if (doCurlies) {
|
||||
o.tabOut();
|
||||
o.tabwrite("}");
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
119
wcmtools/s2/danga/s2/NodeSum.java
Executable file
119
wcmtools/s2/danga/s2/NodeSum.java
Executable file
@@ -0,0 +1,119 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeSum extends Node
|
||||
{
|
||||
Node lhs;
|
||||
TokenPunct op;
|
||||
Node rhs;
|
||||
|
||||
// use this for the backend to decide which add operator to use
|
||||
private Type myType;
|
||||
|
||||
public NodeSum () {
|
||||
}
|
||||
|
||||
public NodeSum (Node lhs, TokenPunct op, Node rhs)
|
||||
{
|
||||
this.lhs = lhs;
|
||||
this.op = op;
|
||||
this.rhs = rhs;
|
||||
}
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return NodeProduct.canStart(toker);
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
Node lhs = NodeProduct.parse(toker);
|
||||
lhs.skipWhite(toker);
|
||||
|
||||
while(toker.peek().equals(TokenPunct.PLUS) ||
|
||||
toker.peek().equals(TokenPunct.MINUS)) {
|
||||
lhs = parseAnother(toker, lhs);
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
|
||||
private static Node parseAnother (Tokenizer toker, Node lhs) throws Exception
|
||||
{
|
||||
NodeSum n = new NodeSum();
|
||||
|
||||
n.lhs = lhs;
|
||||
n.addNode(n.lhs);
|
||||
|
||||
n.op = (TokenPunct) toker.peek();
|
||||
n.eatToken(toker);
|
||||
n.skipWhite(toker);
|
||||
|
||||
n.rhs = NodeProduct.parse(toker);
|
||||
n.addNode(n.rhs);
|
||||
n.skipWhite(toker);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
return getType(ck, null);
|
||||
}
|
||||
|
||||
public Type getType (Checker ck, Type wanted) throws Exception
|
||||
{
|
||||
Type lt = lhs.getType(ck, wanted);
|
||||
Type rt = rhs.getType(ck, wanted);
|
||||
if (! (lt.equals(Type.INT) || lt.equals(Type.STRING))) {
|
||||
if (lhs.makeAsString(ck))
|
||||
lt = Type.STRING;
|
||||
else
|
||||
throw new Exception("Left hand side of + operator is "+lt+", not a string or "+
|
||||
"integer at "+lhs.getFilePos());
|
||||
}
|
||||
if (! (rt.equals(Type.INT) || rt.equals(Type.STRING))) {
|
||||
if (rhs.makeAsString(ck))
|
||||
rt = Type.STRING;
|
||||
else
|
||||
throw new Exception("Right hand side of + operator is "+rt+", not a string or "+
|
||||
"integer at "+rhs.getFilePos());
|
||||
}
|
||||
// can't subtract strings
|
||||
if (op == TokenPunct.MINUS && (lt.equals(Type.STRING) ||
|
||||
rt.equals(Type.STRING))) {
|
||||
throw new Exception("Can't subtract strings at "+rhs.getFilePos());
|
||||
}
|
||||
|
||||
// all summations involving a string on either side are promoted
|
||||
// to a concatenation
|
||||
if (lt.equals(Type.STRING) || rt.equals(Type.STRING)) {
|
||||
return (myType = Type.STRING);
|
||||
}
|
||||
return (myType = Type.INT);
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
BackendS2.LParen(o);
|
||||
lhs.asS2(o);
|
||||
if (op != null) {
|
||||
o.write(" " + op.getPunct() + " ");
|
||||
rhs.asS2(o);
|
||||
}
|
||||
BackendS2.RParen(o);
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
lhs.asPerl(bp, o);
|
||||
if (op != null) {
|
||||
if (myType == Type.STRING)
|
||||
o.write(" . ");
|
||||
else if (op == TokenPunct.PLUS)
|
||||
o.write(" + ");
|
||||
else if (op == TokenPunct.MINUS)
|
||||
o.write(" - ");
|
||||
rhs.asPerl(bp, o);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
717
wcmtools/s2/danga/s2/NodeTerm.java
Executable file
717
wcmtools/s2/danga/s2/NodeTerm.java
Executable file
@@ -0,0 +1,717 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
public class NodeTerm extends NodeExpr
|
||||
{
|
||||
int type = 0;
|
||||
|
||||
public final static int INTEGER = 1;
|
||||
TokenIntegerLiteral tokInt;
|
||||
|
||||
public final static int STRING = 2;
|
||||
TokenStringLiteral tokStr;
|
||||
Node nodeString;
|
||||
String ctorclass; // if not null, then we're not a string, but calling a class ctor with a string
|
||||
|
||||
public final static int BOOL = 3;
|
||||
boolean boolValue;
|
||||
|
||||
public final static int VARREF = 4;
|
||||
NodeVarRef var;
|
||||
|
||||
public final static int SUBEXPR = 5;
|
||||
public final static int DEFINEDTEST = 6;
|
||||
public final static int SIZEFUNC = 7;
|
||||
public final static int REVERSEFUNC = 8;
|
||||
public final static int ISNULLFUNC = 12;
|
||||
NodeExpr subExpr;
|
||||
Type subType; // for backend, set by getType()
|
||||
|
||||
public final static int NEW = 9;
|
||||
public final static int NEWNULL = 13; // Like NEW, but sets object to be null
|
||||
TokenIdent newClass;
|
||||
|
||||
public final static int FUNCCALL = 10;
|
||||
public final static int METHCALL = 11;
|
||||
int derefLine; // keep track of where we saw the deref token
|
||||
TokenIdent funcIdent;
|
||||
String funcClass; // null or classname of the method call
|
||||
NodeArguments funcArgs;
|
||||
boolean funcBuiltin; // is this function call a builtin?
|
||||
// (if so, don't use vtable)
|
||||
boolean parentMethod; // is this a method call on a super class? if so,
|
||||
// can't optimize method call since instance class won't
|
||||
// necessarily be known until run-time (without a lot of analysis)
|
||||
|
||||
String funcID; // used by backend; set after getType()
|
||||
String funcID_noclass;
|
||||
int funcNum; // used by perl backend for function vtables
|
||||
|
||||
public final static int ARRAY = 14;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
Token t = toker.peek();
|
||||
if (t instanceof TokenIntegerLiteral ||
|
||||
t instanceof TokenStringLiteral ||
|
||||
t instanceof TokenIdent ||
|
||||
t.equals(TokenPunct.LPAREN) ||
|
||||
t.equals(TokenPunct.DOLLAR) ||
|
||||
t.equals(TokenKeyword.DEFINED) ||
|
||||
t.equals(TokenKeyword.TRUE) ||
|
||||
t.equals(TokenKeyword.FALSE) ||
|
||||
t.equals(TokenKeyword.NEW) ||
|
||||
t.equals(TokenKeyword.SIZE) ||
|
||||
t.equals(TokenKeyword.REVERSE) ||
|
||||
t.equals(TokenKeyword.ISNULL) ||
|
||||
t.equals(TokenKeyword.NULL) ||
|
||||
t.equals(TokenPunct.LBRACK) ||
|
||||
t.equals(TokenPunct.LBRACE)
|
||||
)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
return getType(ck, null);
|
||||
}
|
||||
|
||||
public Type getType (Checker ck, Type wanted) throws Exception
|
||||
{
|
||||
if (type == INTEGER) return Type.INT;
|
||||
if (type == STRING) {
|
||||
if (nodeString != null) {
|
||||
return nodeString.getType(ck, Type.STRING);
|
||||
}
|
||||
if (ck.isStringCtor(wanted)) {
|
||||
ctorclass = wanted.baseType();
|
||||
return wanted;
|
||||
}
|
||||
return Type.STRING;
|
||||
}
|
||||
if (type == SUBEXPR) return subExpr.getType(ck);
|
||||
if (type == BOOL) return Type.BOOL;
|
||||
|
||||
if (type == DEFINEDTEST) {
|
||||
System.err.println("FIXME: check type of defined expression");
|
||||
return Type.BOOL;
|
||||
}
|
||||
if (type == SIZEFUNC) {
|
||||
subType = subExpr.getType(ck);
|
||||
if (subType.equals(Type.STRING))
|
||||
return Type.INT;
|
||||
if (subType.isArrayOf())
|
||||
return Type.INT;
|
||||
// complain
|
||||
throw new Exception("Can't use size on expression that's "+
|
||||
"not a string or array at "+getFilePos());
|
||||
|
||||
}
|
||||
if (type == REVERSEFUNC) {
|
||||
subType = subExpr.getType(ck);
|
||||
|
||||
// reverse a string
|
||||
if (subType.equals(Type.STRING))
|
||||
return Type.STRING;
|
||||
|
||||
// reverse an array
|
||||
if (subType.isArrayOf())
|
||||
return subType;
|
||||
|
||||
// complain
|
||||
throw new Exception("Can't use reverse on expression that's "+
|
||||
"not a string or array at "+getFilePos());
|
||||
}
|
||||
|
||||
if (type == ISNULLFUNC) {
|
||||
subType = subExpr.getType(ck);
|
||||
if (subExpr instanceof NodeTerm) {
|
||||
NodeTerm nt = (NodeTerm) subExpr;
|
||||
if (nt.type != VARREF && nt.type != FUNCCALL && nt.type != METHCALL)
|
||||
throw new Exception("isnull must only be used on an object variable, "+
|
||||
"function call or method call at "+getFilePos());
|
||||
} else {
|
||||
throw new Exception("isnull must only be used on an object variable, "+
|
||||
"function call or method call at "+getFilePos());
|
||||
}
|
||||
|
||||
// can't be used on arrays and hashes
|
||||
if (subType.isArrayOf() || subType.isHashOf())
|
||||
throw new Exception("Can't use isnull on an array or hash at "+getFilePos());
|
||||
|
||||
// not primitive types either
|
||||
if (subType.equals(Type.BOOL) || subType.equals(Type.STRING) || subType.equals(Type.INT))
|
||||
throw new Exception("Can't use isnull on primitive types at "+getFilePos());
|
||||
|
||||
// nor void
|
||||
if (subType.equals(Type.VOID))
|
||||
throw new Exception("Can't use isnull on a void value at "+getFilePos());
|
||||
|
||||
return Type.BOOL;
|
||||
}
|
||||
|
||||
if (type == NEW || type == NEWNULL) {
|
||||
String clas = newClass.getIdent();
|
||||
NodeClass nc = ck.getClass(clas);
|
||||
if (nc == null) {
|
||||
throw new Exception("Can't instantiate unknown class at "+
|
||||
getFilePos());
|
||||
}
|
||||
return new Type(clas);
|
||||
}
|
||||
|
||||
if (type == VARREF) {
|
||||
if (! ck.getInFunction()) {
|
||||
throw new Exception("Can't reference a variable outside of a function at "+getFilePos());
|
||||
}
|
||||
return var.getType(ck, wanted);
|
||||
}
|
||||
|
||||
if (type == METHCALL || type == FUNCCALL) {
|
||||
if (! ck.getInFunction()) {
|
||||
throw new Exception("Can't call a function or method outside of a function at "+getFilePos());
|
||||
}
|
||||
|
||||
// find the classname of the variable the method was being called on
|
||||
if (type == METHCALL) {
|
||||
Type vartype = var.getType(ck);
|
||||
if (! vartype.isSimple()) {
|
||||
throw new Exception("Cannot call a method on an array or hash of "+
|
||||
"objects at "+getFilePos());
|
||||
}
|
||||
funcClass = vartype.toString();
|
||||
|
||||
NodeClass methClass = ck.getClass(funcClass);
|
||||
if (methClass == null) {
|
||||
throw new Exception("Can't call a method on an instance of an "+
|
||||
"undefined class at "+getFilePos());
|
||||
}
|
||||
|
||||
if (ck.hasDerClasses(funcClass))
|
||||
parentMethod = true;
|
||||
}
|
||||
|
||||
funcID = Checker.functionID(funcClass, funcIdent.getIdent(),
|
||||
funcArgs.typeList(ck));
|
||||
funcBuiltin = ck.isFuncBuiltin(funcID);
|
||||
|
||||
// and remember the funcID without a class for use later when
|
||||
// we have to generate a funcID at run-time by concatenating
|
||||
// this to the end of a $instance->{'_type'}
|
||||
funcID_noclass = Checker.functionID(null, funcIdent.getIdent(),
|
||||
funcArgs.typeList(ck));
|
||||
|
||||
Type t = ck.functionType(funcID);
|
||||
if (! funcBuiltin)
|
||||
funcNum = ck.functionNum(funcID);
|
||||
if (t == null) {
|
||||
throw new Exception("Unknown function "+funcID+" at "+
|
||||
funcIdent.getFilePos());
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
if (type == ARRAY) {
|
||||
return subExpr.getType(ck, wanted);
|
||||
}
|
||||
|
||||
throw new Exception("ERROR: unknown NodeTerm type at "+getFilePos());
|
||||
}
|
||||
|
||||
public boolean isLValue ()
|
||||
{
|
||||
if (type == VARREF) return true;
|
||||
if (type == SUBEXPR) {
|
||||
return subExpr.isLValue();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean makeAsString(Checker ck)
|
||||
{
|
||||
if (type == VARREF) {
|
||||
try {
|
||||
Type t = var.getType(ck);
|
||||
if (t.isSimple()) {
|
||||
String bt = t.baseType();
|
||||
|
||||
// class has .toString() method
|
||||
if (ck.classHasToString(bt)) {
|
||||
// let's change this VARREF into a METHCALL!
|
||||
// warning: ugly hacks ahead...
|
||||
type = METHCALL;
|
||||
funcIdent = new TokenIdent("toString");
|
||||
funcClass = bt;
|
||||
funcArgs = (NodeArguments)
|
||||
NodeArguments.makeEmptyArgs();
|
||||
funcID_noclass = "toString()";
|
||||
funcID = bt + "::" + funcID_noclass;
|
||||
funcBuiltin = ck.isFuncBuiltin(funcID);
|
||||
funcNum = ck.functionNum(funcID);
|
||||
if (ck.hasDerClasses(funcClass))
|
||||
parentMethod = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// class has $.as_string string member
|
||||
if (ck.classHasAsString(bt)) {
|
||||
var.useAsString();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeTerm nt = new NodeTerm();
|
||||
|
||||
Token t = toker.peek();
|
||||
|
||||
// integer literal
|
||||
if (t instanceof TokenIntegerLiteral) {
|
||||
nt.type = NodeTerm.INTEGER;
|
||||
nt.tokInt = (TokenIntegerLiteral) nt.eatToken(toker);
|
||||
return nt;
|
||||
}
|
||||
|
||||
// boolean literal
|
||||
if (t.equals(TokenKeyword.TRUE) || t.equals(TokenKeyword.FALSE)) {
|
||||
nt.type = NodeTerm.BOOL;
|
||||
nt.boolValue = t.equals(TokenKeyword.TRUE);
|
||||
nt.eatToken(toker);
|
||||
return nt;
|
||||
}
|
||||
|
||||
// string literal
|
||||
if (t instanceof TokenStringLiteral) {
|
||||
TokenStringLiteral ts = (TokenStringLiteral) t;
|
||||
int ql = ts.getQuotesLeft();
|
||||
int qr = ts.getQuotesRight();
|
||||
|
||||
if (qr != 0) {
|
||||
// whole string literal
|
||||
nt.type = NodeTerm.STRING;
|
||||
nt.tokStr = (TokenStringLiteral) nt.eatToken(toker);
|
||||
nt.setStart(nt.tokStr);
|
||||
return nt;
|
||||
}
|
||||
|
||||
// interpolated string literal (turn into a subexpr)
|
||||
LinkedList toklist = new LinkedList();
|
||||
|
||||
nt.type = NodeTerm.STRING;
|
||||
nt.tokStr = (TokenStringLiteral) nt.eatToken(toker);
|
||||
toklist.add(nt.tokStr.clone()); // cloned before it's changed.
|
||||
nt.tokStr.setQuotesRight(ql);
|
||||
Node lhs = nt;
|
||||
FilePos filepos = (FilePos) nt.tokStr.getFilePos();
|
||||
|
||||
boolean loop = true;
|
||||
while (loop) {
|
||||
Node rhs = null;
|
||||
Token tok = toker.peek();
|
||||
if (tok instanceof TokenStringLiteral) {
|
||||
rhs = new NodeTerm();
|
||||
NodeTerm rhsnt = (NodeTerm) rhs;
|
||||
ts = (TokenStringLiteral) tok;
|
||||
rhsnt.type = NodeTerm.STRING;
|
||||
rhsnt.tokStr = (TokenStringLiteral) rhsnt.eatToken(toker);
|
||||
toklist.add(rhsnt.tokStr.clone()); // cloned before it's changed.
|
||||
|
||||
if (ts.getQuotesRight() == ql) {
|
||||
loop = false;
|
||||
}
|
||||
ts.setQuotesRight(ql);
|
||||
ts.setQuotesLeft(ql);
|
||||
}
|
||||
else if (tok.equals(TokenPunct.DOLLAR)) {
|
||||
rhs = NodeTerm.parse(toker);
|
||||
toklist.add(rhs);
|
||||
}
|
||||
else {
|
||||
throw new Exception("Error parsing "+
|
||||
"interpolated string.");
|
||||
}
|
||||
|
||||
// don't make a sum out of a blank string on either side
|
||||
boolean join = true;
|
||||
if (lhs instanceof NodeTerm) {
|
||||
NodeTerm lhst = (NodeTerm) lhs;
|
||||
if (lhst.type == STRING &&
|
||||
lhst.tokStr.getString().length() == 0) {
|
||||
lhs = rhs;
|
||||
join = false;
|
||||
}
|
||||
}
|
||||
if (rhs instanceof NodeTerm) {
|
||||
NodeTerm rhst = (NodeTerm) rhs;
|
||||
if (rhst.type == STRING &&
|
||||
rhst.tokStr.getString().length() == 0) {
|
||||
join = false;
|
||||
}
|
||||
}
|
||||
if (join) {
|
||||
lhs = new NodeSum(lhs, TokenPunct.PLUS, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
lhs.setTokenList(toklist);
|
||||
lhs.setStart(filepos);
|
||||
|
||||
NodeTerm rnt = new NodeTerm();
|
||||
rnt.type = NodeTerm.STRING;
|
||||
rnt.nodeString = lhs;
|
||||
rnt.addNode(lhs);
|
||||
return rnt;
|
||||
}
|
||||
|
||||
// Sub-expression (in parenthesis)
|
||||
if (t.equals(TokenPunct.LPAREN)) {
|
||||
nt.type = NodeTerm.SUBEXPR;
|
||||
nt.setStart(nt.eatToken(toker));
|
||||
|
||||
nt.subExpr = (NodeExpr) NodeExpr.parse(toker);
|
||||
nt.addNode(nt.subExpr);
|
||||
|
||||
nt.requireToken(toker, TokenPunct.RPAREN);
|
||||
return nt;
|
||||
}
|
||||
|
||||
// defined test
|
||||
if (t.equals(TokenKeyword.DEFINED)) {
|
||||
nt.type = NodeTerm.DEFINEDTEST;
|
||||
nt.eatToken(toker);
|
||||
nt.subExpr = (NodeTerm) NodeTerm.parse(toker);
|
||||
nt.addNode(nt.subExpr);
|
||||
return nt;
|
||||
}
|
||||
|
||||
// reverse function
|
||||
if (t.equals(TokenKeyword.REVERSE)) {
|
||||
nt.type = NodeTerm.REVERSEFUNC;
|
||||
nt.eatToken(toker);
|
||||
nt.subExpr = (NodeTerm) NodeTerm.parse(toker);
|
||||
nt.addNode(nt.subExpr);
|
||||
return nt;
|
||||
}
|
||||
|
||||
// size function
|
||||
if (t.equals(TokenKeyword.SIZE)) {
|
||||
nt.type = NodeTerm.SIZEFUNC;
|
||||
nt.eatToken(toker);
|
||||
nt.subExpr = (NodeTerm) NodeTerm.parse(toker);
|
||||
nt.addNode(nt.subExpr);
|
||||
return nt;
|
||||
}
|
||||
|
||||
// isnull function
|
||||
if (t.equals(TokenKeyword.ISNULL)) {
|
||||
nt.type = NodeTerm.ISNULLFUNC;
|
||||
nt.eatToken(toker);
|
||||
nt.subExpr = (NodeTerm) NodeTerm.parse(toker);
|
||||
nt.addNode(nt.subExpr);
|
||||
return nt;
|
||||
}
|
||||
|
||||
// new and null
|
||||
if (t.equals(TokenKeyword.NEW) || t.equals(TokenKeyword.NULL)) {
|
||||
nt.type = (t.equals(TokenKeyword.NEW) ? NodeTerm.NEW : NodeTerm.NEWNULL);
|
||||
nt.eatToken(toker);
|
||||
nt.newClass = nt.getIdent(toker);
|
||||
return nt;
|
||||
}
|
||||
|
||||
// VarRef
|
||||
if (t.equals(TokenPunct.DOLLAR)) {
|
||||
nt.type = VARREF;
|
||||
nt.var = (NodeVarRef) NodeVarRef.parse(toker);
|
||||
nt.addNode(nt.var);
|
||||
|
||||
// check for -> after, like: $object->method(arg1, arg2, ...)
|
||||
if (toker.peek().equals(TokenPunct.DEREF)) {
|
||||
nt.derefLine = toker.peek().getFilePos().line;
|
||||
|
||||
nt.eatToken(toker);
|
||||
nt.type = METHCALL;
|
||||
// don't return... parsing continues below.
|
||||
} else {
|
||||
return nt;
|
||||
}
|
||||
}
|
||||
|
||||
// function/method call
|
||||
if (t instanceof TokenIdent || nt.type == METHCALL) {
|
||||
if (nt.type != METHCALL) nt.type = FUNCCALL;
|
||||
|
||||
nt.funcIdent = nt.getIdent(toker);
|
||||
nt.funcArgs = (NodeArguments) NodeArguments.parse(toker);
|
||||
nt.addNode(nt.funcArgs);
|
||||
return nt;
|
||||
}
|
||||
|
||||
// array/hash literal
|
||||
if (NodeArrayLiteral.canStart(toker)) {
|
||||
nt.type = ARRAY;
|
||||
nt.subExpr = (NodeExpr) NodeArrayLiteral.parse(toker);
|
||||
nt.addNode(nt.subExpr);
|
||||
return nt;
|
||||
}
|
||||
|
||||
throw new Exception("Can't finish parsing NodeTerm at " +
|
||||
toker.locationString() +
|
||||
", toker.peek() = " + t.toString());
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
if (type == INTEGER) {
|
||||
tokInt.asS2(o);
|
||||
return;
|
||||
}
|
||||
if (type == STRING) {
|
||||
if (nodeString != null) {
|
||||
nodeString.asS2(o);
|
||||
return;
|
||||
}
|
||||
tokStr.asS2(o);
|
||||
return;
|
||||
}
|
||||
if (type == BOOL) {
|
||||
if (boolValue)
|
||||
o.write("true");
|
||||
else
|
||||
o.write("false");
|
||||
return;
|
||||
}
|
||||
if (type == SUBEXPR) {
|
||||
o.write("(");
|
||||
subExpr.asS2(o);
|
||||
o.write(")");
|
||||
return;
|
||||
}
|
||||
if (type == NEW) {
|
||||
o.write("new ");
|
||||
o.write(newClass.getIdent());
|
||||
return;
|
||||
}
|
||||
if (type == NEWNULL) {
|
||||
o.write("null ");
|
||||
o.write(newClass.getIdent());
|
||||
return;
|
||||
}
|
||||
if (type == DEFINEDTEST) {
|
||||
o.write("defined ");
|
||||
subExpr.asS2(o);
|
||||
return;
|
||||
}
|
||||
if (type == SIZEFUNC) {
|
||||
o.write("size ");
|
||||
subExpr.asS2(o);
|
||||
return;
|
||||
}
|
||||
if (type == REVERSEFUNC) {
|
||||
o.write("reverse ");
|
||||
subExpr.asS2(o);
|
||||
return;
|
||||
}
|
||||
if (type == ISNULLFUNC) {
|
||||
o.write("isnull ");
|
||||
subExpr.asS2(o);
|
||||
return;
|
||||
}
|
||||
if (type == VARREF || type == METHCALL) {
|
||||
var.asS2(o);
|
||||
}
|
||||
if (type == METHCALL) {
|
||||
o.write("->");
|
||||
}
|
||||
if (type == METHCALL || type == FUNCCALL) {
|
||||
o.write(funcIdent.getIdent());
|
||||
funcArgs.asS2(o);
|
||||
}
|
||||
if (type == VARREF || type == METHCALL || type == FUNCCALL)
|
||||
return;
|
||||
if (type == ARRAY) {
|
||||
subExpr.asS2(o);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
if (type == INTEGER) {
|
||||
tokInt.asPerl(bp, o);
|
||||
return;
|
||||
}
|
||||
if (type == STRING) {
|
||||
if (nodeString != null) {
|
||||
o.write("(");
|
||||
nodeString.asPerl(bp, o);
|
||||
o.write(")");
|
||||
return;
|
||||
}
|
||||
if (ctorclass != null)
|
||||
o.write("S2::Builtin::"+ctorclass+"__"+ctorclass+"(");
|
||||
tokStr.asPerl(bp, o);
|
||||
if (ctorclass != null)
|
||||
o.write(")");
|
||||
return;
|
||||
}
|
||||
if (type == BOOL) {
|
||||
if (boolValue)
|
||||
o.write("1");
|
||||
else
|
||||
o.write("0");
|
||||
return;
|
||||
}
|
||||
if (type == SUBEXPR) {
|
||||
o.write("(");
|
||||
subExpr.asPerl(bp, o);
|
||||
o.write(")");
|
||||
return;
|
||||
}
|
||||
if (type == ARRAY) {
|
||||
subExpr.asPerl(bp, o);
|
||||
return;
|
||||
}
|
||||
if (type == NEW) {
|
||||
o.write("{'_type'=>" +
|
||||
bp.quoteString(newClass.getIdent())+
|
||||
"}");
|
||||
return;
|
||||
}
|
||||
if (type == NEWNULL) {
|
||||
o.write("{'_type'=>" +
|
||||
bp.quoteString(newClass.getIdent())+
|
||||
", '_isnull'=>1}");
|
||||
return;
|
||||
}
|
||||
if (type == DEFINEDTEST) {
|
||||
o.write("defined(");
|
||||
subExpr.asPerl(bp, o);
|
||||
o.write(")");
|
||||
return;
|
||||
}
|
||||
if (type == REVERSEFUNC) {
|
||||
if (subType.isArrayOf()) {
|
||||
o.write("[reverse(");
|
||||
o.write("@{");
|
||||
subExpr.asPerl(bp, o);
|
||||
o.write("})");
|
||||
o.write("]");
|
||||
} else if (subType.equals(Type.STRING)) {
|
||||
o.write("reverse(");
|
||||
subExpr.asPerl(bp, o);
|
||||
o.write(")");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (type == SIZEFUNC) {
|
||||
if (subType.equals(Type.STRING)) {
|
||||
o.write("length(");
|
||||
subExpr.asPerl(bp, o);
|
||||
o.write(")");
|
||||
}
|
||||
else if (subType.isArrayOf()) {
|
||||
o.write("scalar(@{");
|
||||
subExpr.asPerl(bp, o);
|
||||
o.write("})");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (type == ISNULLFUNC) {
|
||||
o.write("(ref ");
|
||||
subExpr.asPerl(bp, o);
|
||||
o.write(" ne \"HASH\" || ");
|
||||
subExpr.asPerl(bp, o);
|
||||
o.write("->{'_isnull'})");
|
||||
return;
|
||||
}
|
||||
if (type == VARREF) {
|
||||
var.asPerl(bp, o);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == FUNCCALL || type == METHCALL) {
|
||||
|
||||
boolean funcDumped = false;
|
||||
|
||||
// builtin functions can be optimized.
|
||||
if (funcBuiltin) {
|
||||
// these built-in functions can be inlined.
|
||||
if (funcID.equals("string(int)")) {
|
||||
funcArgs.asPerl(bp, o, false);
|
||||
return;
|
||||
}
|
||||
if (funcID.equals("int(string)")) {
|
||||
// cast from string to int by adding zero to it
|
||||
o.write("(0+");
|
||||
funcArgs.asPerl(bp, o, false);
|
||||
o.write(")");
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise, call the builtin function (avoid a layer
|
||||
// of indirection), unless it's for a class that has
|
||||
// children (won't know until run-time which class to call)
|
||||
if(funcClass == null || (funcClass != null && ! parentMethod)) {
|
||||
o.write("S2::Builtin::");
|
||||
if (funcClass != null) {
|
||||
o.write(funcClass + "__");
|
||||
}
|
||||
o.write(funcIdent.getIdent());
|
||||
funcDumped = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (funcDumped == false) {
|
||||
if (type == METHCALL && ! funcClass.equals("string")) {
|
||||
o.write("$_ctx->[VTABLE]->{get_object_func_num(");
|
||||
o.write(bp.quoteString(funcClass));
|
||||
o.write(",");
|
||||
var.asPerl(bp, o);
|
||||
o.write(",");
|
||||
o.write(bp.quoteString(funcID_noclass));
|
||||
o.write(",");
|
||||
o.write(bp.getLayerID());
|
||||
o.write(",");
|
||||
o.write(derefLine);
|
||||
if (var.isSuper()) {
|
||||
o.write(",1");
|
||||
}
|
||||
o.write(")}->");
|
||||
} else if (type == METHCALL) {
|
||||
o.write("$_ctx->[VTABLE]->{get_func_num(");
|
||||
o.write(bp.quoteString(funcID));
|
||||
o.write(")}->");
|
||||
} else {
|
||||
o.write("$_ctx->[VTABLE]->{$_l2g_func["+funcNum+"]}->");
|
||||
}
|
||||
}
|
||||
|
||||
o.write("($_ctx, ");
|
||||
|
||||
// this pointer
|
||||
if (type == METHCALL) {
|
||||
var.asPerl(bp, o);
|
||||
o.write(", ");
|
||||
}
|
||||
|
||||
funcArgs.asPerl(bp, o, false);
|
||||
|
||||
o.write(")");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
62
wcmtools/s2/danga/s2/NodeText.java
Executable file
62
wcmtools/s2/danga/s2/NodeText.java
Executable file
@@ -0,0 +1,62 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeText extends Node
|
||||
{
|
||||
String text;
|
||||
|
||||
public String getText () {
|
||||
return text;
|
||||
}
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
Token t = toker.peek();
|
||||
|
||||
if (t instanceof TokenIdent ||
|
||||
t instanceof TokenIntegerLiteral ||
|
||||
t instanceof TokenStringLiteral)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeText nt = new NodeText();
|
||||
|
||||
nt.skipWhite(toker);
|
||||
Token t = toker.peek();
|
||||
|
||||
if (t instanceof TokenIdent ||
|
||||
t instanceof TokenIntegerLiteral ||
|
||||
t instanceof TokenStringLiteral) {
|
||||
|
||||
if (t instanceof TokenIdent) {
|
||||
TokenIdent ti = (TokenIdent) t;
|
||||
nt.text = ti.getIdent();
|
||||
ti.setType(TokenIdent.STRING);
|
||||
}
|
||||
if (t instanceof TokenIntegerLiteral) {
|
||||
int iv = ((TokenIntegerLiteral)t).val;
|
||||
nt.text = (new Integer(iv)).toString();
|
||||
}
|
||||
if (t instanceof TokenStringLiteral) {
|
||||
TokenStringLiteral ts = (TokenStringLiteral) t;
|
||||
//FIXME: check for unclosed side.
|
||||
nt.text = ts.text;
|
||||
}
|
||||
|
||||
nt.eatToken(toker);
|
||||
} else {
|
||||
throw new Exception("Expecting text (integer, string, or identifer)");
|
||||
}
|
||||
|
||||
return nt;
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.write(Backend.quoteString(text));
|
||||
}
|
||||
|
||||
};
|
||||
45
wcmtools/s2/danga/s2/NodeType.java
Executable file
45
wcmtools/s2/danga/s2/NodeType.java
Executable file
@@ -0,0 +1,45 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeType extends Node
|
||||
{
|
||||
private Type type;
|
||||
|
||||
public Type getType () { return type; }
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeType n = new NodeType();
|
||||
|
||||
TokenIdent base = (TokenIdent) n.getIdent(toker, true, false);
|
||||
base.setType(TokenIdent.TYPE);
|
||||
|
||||
n.type = new Type(base.getIdent());
|
||||
while (toker.peek().equals(TokenPunct.LBRACK) ||
|
||||
toker.peek().equals(TokenPunct.LBRACE)) {
|
||||
|
||||
Token t = toker.peek();
|
||||
|
||||
n.eatToken(toker, false);
|
||||
if (t.equals(TokenPunct.LBRACK)) {
|
||||
n.requireToken(toker, TokenPunct.RBRACK, false);
|
||||
n.type.makeArrayOf();
|
||||
}
|
||||
if (t.equals(TokenPunct.LBRACE)) {
|
||||
n.requireToken(toker, TokenPunct.RBRACE, false);
|
||||
n.type.makeHashOf();
|
||||
}
|
||||
}
|
||||
|
||||
// If the type was a simple type, we have to remove whitespace,
|
||||
// since we explictly said not to above.
|
||||
n.skipWhite(toker);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.write(type.toString());
|
||||
}
|
||||
|
||||
};
|
||||
71
wcmtools/s2/danga/s2/NodeUnaryExpr.java
Executable file
71
wcmtools/s2/danga/s2/NodeUnaryExpr.java
Executable file
@@ -0,0 +1,71 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeUnaryExpr extends Node
|
||||
{
|
||||
boolean bNot = false;
|
||||
boolean bNegative = false;
|
||||
Node expr;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return (toker.peek().equals(TokenPunct.MINUS) ||
|
||||
toker.peek().equals(TokenKeyword.NOT) ||
|
||||
NodeIncExpr.canStart(toker));
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeUnaryExpr n = new NodeUnaryExpr();
|
||||
|
||||
if (toker.peek().equals(TokenPunct.MINUS)) {
|
||||
n.bNegative = true;
|
||||
n.eatToken(toker);
|
||||
n.skipWhite(toker);
|
||||
} else if (toker.peek().equals(TokenKeyword.NOT)) {
|
||||
n.bNot = true;
|
||||
n.eatToken(toker);
|
||||
n.skipWhite(toker);
|
||||
}
|
||||
|
||||
Node expr = NodeIncExpr.parse(toker);
|
||||
|
||||
if (n.bNegative || n.bNot) {
|
||||
n.expr = expr;
|
||||
n.addNode(n.expr);
|
||||
return n;
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
Type t = expr.getType(ck);
|
||||
if (bNegative) {
|
||||
if (! t.equals(Type.INT))
|
||||
throw new Exception("Can't use unary minus on non-integer. Type = "+t);
|
||||
return Type.INT;
|
||||
}
|
||||
if (bNot) {
|
||||
if (! t.equals(Type.BOOL))
|
||||
throw new Exception("Can't use NOT operator on non-boolean. Type = "+t);
|
||||
return Type.BOOL;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
if (bNot) o.write("! ");
|
||||
if (bNegative) o.write("-");
|
||||
expr.asPerl(bp, o);
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
if (bNot) o.write("not ");
|
||||
if (bNegative) o.write("-");
|
||||
expr.asS2(o);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
31
wcmtools/s2/danga/s2/NodeUnnecessary.java
Executable file
31
wcmtools/s2/danga/s2/NodeUnnecessary.java
Executable file
@@ -0,0 +1,31 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeUnnecessary extends Node
|
||||
{
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
Node n = new NodeUnnecessary();
|
||||
n.skipWhite(toker);
|
||||
return n;
|
||||
}
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (toker.peek().isNecessary() == false)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o) {
|
||||
// do nothing when making the canonical S2
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o) {
|
||||
// do nothing when doing the perl output
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception {
|
||||
// nothing can be wrong with whitespace and comments
|
||||
}
|
||||
|
||||
};
|
||||
55
wcmtools/s2/danga/s2/NodeVarDecl.java
Executable file
55
wcmtools/s2/danga/s2/NodeVarDecl.java
Executable file
@@ -0,0 +1,55 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeVarDecl extends Node
|
||||
{
|
||||
NodeNamedType nt;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (toker.peek().equals(TokenKeyword.VAR))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeVarDecl n = new NodeVarDecl();
|
||||
|
||||
n.setStart(n.requireToken(toker, TokenKeyword.VAR));
|
||||
n.addNode(n.nt = (NodeNamedType) NodeNamedType.parse(toker));
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public Type getType () {
|
||||
return nt.getType();
|
||||
}
|
||||
|
||||
public String getName () {
|
||||
return nt.getName();
|
||||
}
|
||||
|
||||
public void populateScope (NodeStmtBlock nb) throws Exception
|
||||
{
|
||||
String name = nt.getName();
|
||||
Type et = nb.getLocalVar(name);
|
||||
if (et == null) {
|
||||
nb.addLocalVar(name, nt.getType());
|
||||
return;
|
||||
}
|
||||
throw new Exception("Can't mask local variable '"+name+"' at "+getFilePos());
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o) {
|
||||
// Note: no tabbing, as this may be in a foreach. nodes using
|
||||
// VarDecl nodes must do their own tabbing (NodeClass, NodeVarDeclStmt)
|
||||
o.write("var ");
|
||||
nt.asS2(o);
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o) {
|
||||
// Note: no tabbing, as this may be in a foreach. nodes using
|
||||
// VarDecl nodes must do their own tabbing (NodeClass, NodeVarDeclStmt)
|
||||
o.write("my $" + nt.getName());
|
||||
}
|
||||
}
|
||||
91
wcmtools/s2/danga/s2/NodeVarDeclStmt.java
Executable file
91
wcmtools/s2/danga/s2/NodeVarDeclStmt.java
Executable file
@@ -0,0 +1,91 @@
|
||||
package danga.s2;
|
||||
|
||||
public class NodeVarDeclStmt extends Node
|
||||
{
|
||||
NodeVarDecl nvd;
|
||||
NodeExpr expr;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
return NodeVarDecl.canStart(toker);
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer toker) throws Exception
|
||||
{
|
||||
NodeVarDeclStmt n = new NodeVarDeclStmt();
|
||||
|
||||
n.addNode(n.nvd = (NodeVarDecl) NodeVarDecl.parse(toker));
|
||||
|
||||
if (toker.peek().equals(TokenPunct.ASSIGN)) {
|
||||
n.eatToken(toker);
|
||||
n.expr = (NodeExpr) NodeExpr.parse(toker);
|
||||
n.addNode(n.expr);
|
||||
}
|
||||
n.requireToken(toker, TokenPunct.SCOLON);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void check (Layer l, Checker ck) throws Exception
|
||||
{
|
||||
nvd.populateScope(ck.getLocalScope());
|
||||
|
||||
// check that the variable type is a known class
|
||||
Type t = nvd.getType();
|
||||
String bt = t.baseType();
|
||||
|
||||
if (! Type.isPrimitive(bt) &&
|
||||
ck.getClass(bt) == null) {
|
||||
throw new Exception("Unknown type or class '"+bt+"' at "+
|
||||
nvd.getFilePos());
|
||||
}
|
||||
|
||||
if (expr != null) {
|
||||
Type et = expr.getType(ck, t);
|
||||
if (! ck.typeIsa(et, t)) {
|
||||
throw new Exception("Can't initialize variable '"+nvd.getName()+"' "+
|
||||
"of type "+t+" with expression of type "+
|
||||
et+" at "+expr.getFilePos());
|
||||
}
|
||||
}
|
||||
|
||||
// can't be named $_ctx (conflicts with perl backend)
|
||||
String vname = nvd.getName();
|
||||
if (vname.equals("_ctx")) {
|
||||
throw new Exception("Reserved variable name '_ctx' in use at "+
|
||||
getFilePos());
|
||||
}
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o) {
|
||||
o.doTab();
|
||||
nvd.asS2(o);
|
||||
if (expr != null) {
|
||||
o.write(" = ");
|
||||
expr.asS2(o);
|
||||
}
|
||||
o.writeln(";");
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o) {
|
||||
o.doTab();
|
||||
nvd.asPerl(bp, o);
|
||||
|
||||
if (expr != null) {
|
||||
o.write(" = ");
|
||||
expr.asPerl(bp, o);
|
||||
} else {
|
||||
Type t = nvd.getType();
|
||||
if (t.equals(Type.STRING)) {
|
||||
o.write(" = \"\"");
|
||||
} else if (t.equals(Type.INT) || t.equals(Type.BOOL)) {
|
||||
o.write(" = 0");
|
||||
}
|
||||
}
|
||||
//else {
|
||||
// o.write(" = { '_type' => " + bp.quoteString(t.toString()) + "}");
|
||||
//}
|
||||
|
||||
o.writeln(";");
|
||||
}
|
||||
}
|
||||
356
wcmtools/s2/danga/s2/NodeVarRef.java
Executable file
356
wcmtools/s2/danga/s2/NodeVarRef.java
Executable file
@@ -0,0 +1,356 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
public class NodeVarRef extends Node
|
||||
{
|
||||
public final static int LOCAL = 1;
|
||||
public final static int OBJECT = 2;
|
||||
public final static int PROPERTY = 3;
|
||||
|
||||
class Deref {
|
||||
public char type;
|
||||
public NodeExpr expr;
|
||||
}
|
||||
|
||||
class VarLevel {
|
||||
public String var;
|
||||
public LinkedList derefs;
|
||||
}
|
||||
|
||||
LinkedList levels;
|
||||
|
||||
boolean braced;
|
||||
int type = LOCAL;
|
||||
|
||||
boolean useAsString = false;
|
||||
|
||||
public static boolean canStart (Tokenizer toker) throws Exception
|
||||
{
|
||||
if (toker.peek().equals(TokenPunct.DOLLAR))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Node parse (Tokenizer tokermain) throws Exception
|
||||
{
|
||||
// voo-doo so tokenizer won't continue parsing a string
|
||||
// if we're in a string and trying to parse interesting things
|
||||
// involved in a VarRef:
|
||||
|
||||
NodeVarRef n = new NodeVarRef();
|
||||
n.levels = new LinkedList();
|
||||
|
||||
n.setStart(n.requireToken(tokermain, TokenPunct.DOLLAR, false));
|
||||
|
||||
Tokenizer toker = tokermain.inString == 0 ?
|
||||
tokermain : tokermain.getVarTokenizer();
|
||||
|
||||
if (toker.peekChar() == '{') {
|
||||
n.requireToken(toker, TokenPunct.LBRACE, false);
|
||||
n.braced = true;
|
||||
} else {
|
||||
n.braced = false;
|
||||
}
|
||||
|
||||
if (toker.peekChar() == '.') {
|
||||
n.requireToken(toker, TokenPunct.DOT, false);
|
||||
n.type = OBJECT;
|
||||
} else if (toker.peekChar() == '*') {
|
||||
n.requireToken(toker, TokenPunct.MULT, false);
|
||||
n.type = PROPERTY;
|
||||
}
|
||||
|
||||
boolean requireDot = false;
|
||||
|
||||
// only peeking at characters, not tokens, otherwise
|
||||
// we could force tokens could be created in the wrong
|
||||
// context.
|
||||
while (TokenIdent.canStart(toker) ||
|
||||
toker.peekChar() == '.') {
|
||||
|
||||
if (requireDot) {
|
||||
n.requireToken(toker, TokenPunct.DOT, false);
|
||||
} else {
|
||||
requireDot = true;
|
||||
}
|
||||
|
||||
TokenIdent ident = (TokenIdent) n.getIdent(toker, true, false);
|
||||
|
||||
VarLevel vl = n.new VarLevel();
|
||||
vl.var = ident.getIdent();
|
||||
vl.derefs = new LinkedList();
|
||||
|
||||
// more preventing of token peeking:
|
||||
while (toker.peekChar() == '[' ||
|
||||
toker.peekChar() == '{') {
|
||||
|
||||
Deref dr = n.new Deref();
|
||||
Token t = n.eatToken(toker, false);
|
||||
|
||||
if (t.equals(TokenPunct.LBRACK)) {
|
||||
dr.type = '[';
|
||||
n.addNode(dr.expr = (NodeExpr) NodeExpr.parse(toker));
|
||||
n.requireToken(toker, TokenPunct.RBRACK, false);
|
||||
} else if (t.equals(TokenPunct.LBRACE)) {
|
||||
dr.type = '{';
|
||||
n.addNode(dr.expr = (NodeExpr) NodeExpr.parse(toker));
|
||||
n.requireToken(toker, TokenPunct.RBRACE, false);
|
||||
} else {
|
||||
throw new Exception("shouldn't get here");
|
||||
}
|
||||
|
||||
vl.derefs.add(dr);
|
||||
}
|
||||
|
||||
n.levels.add(vl);
|
||||
} // end while
|
||||
|
||||
// did we parse just $ ?
|
||||
if (n.levels.size() == 0) {
|
||||
throw new Exception("Malformed variable reference at "+
|
||||
n.getFilePos());
|
||||
}
|
||||
|
||||
if (n.braced) {
|
||||
// false argument necessary to prevent peeking at token
|
||||
// stream while it's in the interpolated variable parsing state,
|
||||
// else the string text following the variable would be
|
||||
// treated as if it were outside the string.
|
||||
n.requireToken(toker, TokenPunct.RBRACE, false);
|
||||
}
|
||||
|
||||
// now we must skip white space that requireToken above would've
|
||||
// done had we not told it not to, but not if the main tokenizer
|
||||
// is in a quoted string
|
||||
if (tokermain.inString == 0) {
|
||||
n.skipWhite(toker);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
// if told by NodeTerm.java, add another varlevel to point to
|
||||
// this object's $.as_string
|
||||
public void useAsString ()
|
||||
{
|
||||
VarLevel vl = new VarLevel();
|
||||
vl.var = "as_string";
|
||||
vl.derefs = new LinkedList(); // empty
|
||||
levels.add(vl);
|
||||
}
|
||||
|
||||
public boolean isHashElement ()
|
||||
{
|
||||
if (type != OBJECT && type != LOCAL)
|
||||
return false;
|
||||
|
||||
// need to get the last deref of the last varlevel
|
||||
if (levels.size() == 0)
|
||||
return false;
|
||||
VarLevel l = (VarLevel) levels.getLast();
|
||||
if (l.derefs.size() == 0)
|
||||
return false;
|
||||
Deref d = (Deref) l.derefs.getLast();
|
||||
return d.type == '{';
|
||||
}
|
||||
|
||||
public Type getType (Checker ck, Type wanted) throws Exception
|
||||
{
|
||||
Type t = getType(ck);
|
||||
if (wanted == null) return t;
|
||||
if (! wanted.equals(Type.STRING)) return t;
|
||||
|
||||
String type = t.toString();
|
||||
if (ck.classHasAsString(type)) {
|
||||
useAsString = true;
|
||||
return Type.STRING;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
public Type getType (Checker ck) throws Exception
|
||||
{
|
||||
// must have at least reference something.
|
||||
if (levels.size() == 0) return null;
|
||||
|
||||
ListIterator levi = levels.listIterator();
|
||||
VarLevel lev = (VarLevel) levi.next();
|
||||
|
||||
Type vart = null;
|
||||
|
||||
// properties
|
||||
if (type == PROPERTY) {
|
||||
vart = ck.propertyType(lev.var);
|
||||
if (vart == null)
|
||||
throw new Exception("Unknown property at "+getFilePos());
|
||||
vart = (Type) vart.clone();
|
||||
}
|
||||
|
||||
// local variables.
|
||||
if (type == LOCAL) {
|
||||
vart = (Type) ck.localType(lev.var);
|
||||
if (vart == null) {
|
||||
throw new Exception("Unknown local variable $"+lev.var+" at "+
|
||||
getFilePos());
|
||||
}
|
||||
}
|
||||
|
||||
// properties & locals
|
||||
if (type == PROPERTY || type == LOCAL) {
|
||||
vart = (Type) vart.clone(); // since we'll be modifying it
|
||||
|
||||
// dereference [] and {} stuff
|
||||
doDerefs(ck, lev.derefs, vart);
|
||||
|
||||
// if no more levels, return now. otherwise deferencing
|
||||
// happens below.
|
||||
if (! levi.hasNext()) {
|
||||
return vart;
|
||||
} else {
|
||||
lev = (VarLevel) levi.next();
|
||||
}
|
||||
}
|
||||
|
||||
// initialize the name of the current object
|
||||
if (type == OBJECT) {
|
||||
String curclass = ck.getCurrentFunctionClass();
|
||||
if (curclass == null) {
|
||||
throw new Exception("Can't reference member variable in non-method "+
|
||||
"function at "+getFilePos());
|
||||
}
|
||||
vart = new Type(curclass);
|
||||
}
|
||||
|
||||
while (lev != null) {
|
||||
NodeClass nc = ck.getClass(vart.toString());
|
||||
if (nc == null)
|
||||
throw new Exception("Can't use members of undefined class "+vart+" at "+getFilePos());
|
||||
vart = nc.getMemberType(lev.var);
|
||||
if (vart == null) {
|
||||
throw new Exception("Can't find member '"+lev.var+"' in '"+nc.getName()+"'");
|
||||
}
|
||||
vart = (Type) vart.clone();
|
||||
|
||||
// dereference [] and {} stuff
|
||||
doDerefs(ck, lev.derefs, vart);
|
||||
|
||||
lev = levi.hasNext() ? (VarLevel) levi.next() : null;
|
||||
}
|
||||
|
||||
return vart;
|
||||
}
|
||||
|
||||
private void doDerefs (Checker ck, LinkedList derefs, Type vart) throws Exception
|
||||
{
|
||||
// remove [] and {} references
|
||||
ListIterator lm = derefs.listIterator();
|
||||
while (lm.hasNext()) {
|
||||
Deref d = (Deref) lm.next();
|
||||
Type et = d.expr.getType(ck);
|
||||
if (d.type == '{') {
|
||||
if (! vart.isHashOf()) {
|
||||
throw new Exception("Can't dereference a non-hash as a hash at "+
|
||||
getFilePos());
|
||||
}
|
||||
if (! (et.equals(Type.STRING) || et.equals(Type.INT))) {
|
||||
throw new Exception("Must deference a hash with a string or "+
|
||||
"int, not a "+et+" at "+getFilePos());
|
||||
}
|
||||
vart.removeMod(); // not a hash anymore
|
||||
} else if (d.type == '[') {
|
||||
if (! vart.isArrayOf()) {
|
||||
throw new Exception("Can't dereference a non-array as an array at "+
|
||||
getFilePos());
|
||||
}
|
||||
if (! et.equals(Type.INT)) {
|
||||
throw new Exception("Must deference an array with an integer, "+
|
||||
"not a "+et+" at "+getFilePos());
|
||||
}
|
||||
vart.removeMod(); // not an array anymore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String typeChar () {
|
||||
if (type == OBJECT) return ".";
|
||||
if (type == PROPERTY) return "*";
|
||||
return "";
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.write("$" + typeChar());
|
||||
ListIterator li = levels.listIterator(0);
|
||||
boolean first = true;
|
||||
while (li.hasNext()) {
|
||||
VarLevel lev = (VarLevel) li.next();
|
||||
if (! first) o.write("."); else first = false;
|
||||
o.write(lev.var);
|
||||
|
||||
ListIterator dli = lev.derefs.listIterator(0);
|
||||
while (dli.hasNext()) {
|
||||
Deref d = (Deref) dli.next();
|
||||
if (d.type == '[') { o.write("["); }
|
||||
if (d.type == '{') { o.write("{"); }
|
||||
d.expr.asS2(o);
|
||||
if (d.type == '[') { o.write("]"); }
|
||||
if (d.type == '{') { o.write("}"); }
|
||||
}
|
||||
} // end levels
|
||||
}
|
||||
|
||||
// is this variable $super ?
|
||||
public boolean isSuper ()
|
||||
{
|
||||
if (type != LOCAL) return false;
|
||||
if (levels.size() > 1) return false;
|
||||
VarLevel v = (VarLevel) levels.getFirst();
|
||||
return (v.var.equals("super") &&
|
||||
v.derefs.size() == 0);
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
ListIterator li = levels.listIterator();
|
||||
boolean first = true;
|
||||
|
||||
if (type == LOCAL) {
|
||||
o.write("$");
|
||||
} else if (type == OBJECT) {
|
||||
o.write("$this");
|
||||
} else if (type == PROPERTY) {
|
||||
o.write("$_ctx->[PROPS]");
|
||||
first = false;
|
||||
}
|
||||
while (li.hasNext()) {
|
||||
VarLevel lev = (VarLevel) li.next();
|
||||
if (! first || type == OBJECT) {
|
||||
o.write("->{'" + lev.var + "'}");
|
||||
} else {
|
||||
String v = lev.var;
|
||||
if (first && type == LOCAL && lev.var.equals("super"))
|
||||
v = "this";
|
||||
o.write(v);
|
||||
first = false;
|
||||
}
|
||||
|
||||
ListIterator dli = lev.derefs.listIterator(0);
|
||||
while (dli.hasNext()) {
|
||||
o.write("->");
|
||||
Deref d = (Deref) dli.next();
|
||||
if (d.type == '[') { o.write("["); }
|
||||
if (d.type == '{') { o.write("{"); }
|
||||
d.expr.asPerl(bp, o);
|
||||
if (d.type == '[') { o.write("]"); }
|
||||
if (d.type == '{') { o.write("}"); }
|
||||
}
|
||||
} // end levels
|
||||
|
||||
if (useAsString) {
|
||||
o.write("->{'as_string'}");
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
22
wcmtools/s2/danga/s2/Output.java
Executable file
22
wcmtools/s2/danga/s2/Output.java
Executable file
@@ -0,0 +1,22 @@
|
||||
package danga.s2;
|
||||
|
||||
/* if I knew java.io.* more, I'd probably not write this. */
|
||||
|
||||
public abstract class Output {
|
||||
|
||||
public abstract void write (String s);
|
||||
public void write (int i) {
|
||||
write(new Integer(i).toString());
|
||||
}
|
||||
public void writeln (int i) {
|
||||
write(new Integer(i).toString());
|
||||
newline();
|
||||
}
|
||||
public void writeln (String s) {
|
||||
write(s);
|
||||
newline();
|
||||
}
|
||||
public abstract void newline ();
|
||||
|
||||
public void flush () { }
|
||||
}
|
||||
38
wcmtools/s2/danga/s2/OutputConsole.java
Executable file
38
wcmtools/s2/danga/s2/OutputConsole.java
Executable file
@@ -0,0 +1,38 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
public class OutputConsole extends Output {
|
||||
|
||||
BufferedWriter out;
|
||||
|
||||
public OutputConsole () {
|
||||
try {
|
||||
out = new BufferedWriter(new OutputStreamWriter(System.out, "UTF-8"),
|
||||
4096);
|
||||
} catch (Exception e) {
|
||||
System.err.println("ERROR: Java installation doesn't support UTF-8?");
|
||||
}
|
||||
}
|
||||
|
||||
public void write (String s) {
|
||||
try {
|
||||
out.write(s);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error: "+e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void newline () {
|
||||
write("\n");
|
||||
}
|
||||
|
||||
public void flush () {
|
||||
try { out.flush(); }
|
||||
catch (Exception e) {
|
||||
System.err.println("UTF-8 output flush failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
26
wcmtools/s2/danga/s2/OutputStringBuffer.java
Executable file
26
wcmtools/s2/danga/s2/OutputStringBuffer.java
Executable file
@@ -0,0 +1,26 @@
|
||||
package danga.s2;
|
||||
|
||||
/* if I knew java.io.* more, I'd probably not write this. */
|
||||
|
||||
public class OutputStringBuffer extends Output {
|
||||
|
||||
StringBuffer sb;
|
||||
public OutputStringBuffer () {
|
||||
sb = new StringBuffer();
|
||||
}
|
||||
|
||||
public void write (String s) {
|
||||
if (s == null) {
|
||||
System.err.println("\n write s==null");
|
||||
}
|
||||
sb.append(s);
|
||||
}
|
||||
|
||||
public void newline () {
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
public void writeTo (Indenter i) {
|
||||
i.write(sb.toString());
|
||||
}
|
||||
}
|
||||
99
wcmtools/s2/danga/s2/Scanner.java
Executable file
99
wcmtools/s2/danga/s2/Scanner.java
Executable file
@@ -0,0 +1,99 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.BufferedReader;
|
||||
|
||||
public class Scanner
|
||||
{
|
||||
public int line;
|
||||
public int col;
|
||||
|
||||
BufferedReader is;
|
||||
|
||||
boolean havePeeked;
|
||||
char peekedChar;
|
||||
boolean fakeNewline = false;
|
||||
|
||||
boolean bogusInput = false;
|
||||
|
||||
public Scanner (InputStream is)
|
||||
{
|
||||
try {
|
||||
this.is = new BufferedReader(new InputStreamReader(is, "UTF-8"), 4096);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Scanner Error: "+e);
|
||||
}
|
||||
line = 1;
|
||||
col = 1;
|
||||
havePeeked = false;
|
||||
}
|
||||
|
||||
public boolean getBogusFlag() {
|
||||
return bogusInput;
|
||||
}
|
||||
|
||||
public char peek ()
|
||||
{
|
||||
if (havePeeked) {
|
||||
return peekedChar;
|
||||
}
|
||||
|
||||
havePeeked = true;
|
||||
try {
|
||||
peekedChar = (char) is.read();
|
||||
if (peekedChar == (char) 0) peekedChar = (char) -1;
|
||||
} catch (Exception e) {
|
||||
bogusInput = true;
|
||||
peekedChar = (char) -1;
|
||||
}
|
||||
|
||||
return peekedChar;
|
||||
}
|
||||
|
||||
// get a char, or -1 if the end of the stream is hit.
|
||||
public char getChar ()
|
||||
{
|
||||
char ch = peek();
|
||||
havePeeked = false;
|
||||
|
||||
if (ch == '\n') {
|
||||
if (fakeNewline) {
|
||||
fakeNewline = false;
|
||||
} else {
|
||||
line++;
|
||||
col = 0;
|
||||
}
|
||||
}
|
||||
if (ch == '\t')
|
||||
col += 4; // stupid assumption.
|
||||
else
|
||||
col++;
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
public void forceNextChar (char ch)
|
||||
{
|
||||
if (ch == '\n') fakeNewline = true;
|
||||
havePeeked = true;
|
||||
peekedChar = ch;
|
||||
}
|
||||
|
||||
public String locationString ()
|
||||
{
|
||||
return ("line " + line + ", column " + col + ".");
|
||||
}
|
||||
|
||||
// get a char that isn't an end of file.
|
||||
public char getRealChar () throws Exception
|
||||
{
|
||||
char ch = getChar();
|
||||
if (ch == -1) {
|
||||
throw new Exception("Unexpected end of file!");
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
24
wcmtools/s2/danga/s2/Token.java
Executable file
24
wcmtools/s2/danga/s2/Token.java
Executable file
@@ -0,0 +1,24 @@
|
||||
package danga.s2;
|
||||
|
||||
public abstract class Token {
|
||||
|
||||
public FilePos pos;
|
||||
|
||||
public FilePos getFilePos () {
|
||||
return pos;
|
||||
}
|
||||
|
||||
public boolean isNecessary() {
|
||||
return true;
|
||||
}
|
||||
public abstract String toString();
|
||||
public abstract void asHTML (Output o);
|
||||
|
||||
public void asS2 (Indenter o) {
|
||||
o.write("##Token::asS2##");
|
||||
}
|
||||
public void asPerl (BackendPerl bp, Indenter o) {
|
||||
o.write("##Token::asPerl##");
|
||||
}
|
||||
|
||||
}
|
||||
35
wcmtools/s2/danga/s2/TokenComment.java
Executable file
35
wcmtools/s2/danga/s2/TokenComment.java
Executable file
@@ -0,0 +1,35 @@
|
||||
package danga.s2;
|
||||
|
||||
class TokenComment extends Token {
|
||||
|
||||
String com;
|
||||
|
||||
public boolean isNecessary() { return false; }
|
||||
|
||||
public TokenComment (String com) {
|
||||
this.com = com;
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return com;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return ("[TokenComment]");
|
||||
}
|
||||
|
||||
public static Token scan (Tokenizer t)
|
||||
{
|
||||
StringBuffer tbuf = new StringBuffer(80);
|
||||
|
||||
while ((t.peekChar() != '\n')) {
|
||||
tbuf.append(t.getChar());
|
||||
}
|
||||
return new TokenComment(tbuf.toString());
|
||||
}
|
||||
|
||||
public void asHTML (Output o)
|
||||
{
|
||||
o.write("<span class=\"c\">" + com + "</span>");
|
||||
}
|
||||
}
|
||||
82
wcmtools/s2/danga/s2/TokenIdent.java
Executable file
82
wcmtools/s2/danga/s2/TokenIdent.java
Executable file
@@ -0,0 +1,82 @@
|
||||
package danga.s2;
|
||||
|
||||
class TokenIdent extends Token {
|
||||
|
||||
public final static int DEFAULT = 0;
|
||||
public final static int TYPE = 1;
|
||||
public final static int STRING = 2;
|
||||
|
||||
int type;
|
||||
String ident;
|
||||
|
||||
// dummy constructor for subclass
|
||||
public TokenIdent () {
|
||||
this.ident = null;
|
||||
}
|
||||
|
||||
public TokenIdent (String ident) {
|
||||
this.ident = ident;
|
||||
}
|
||||
|
||||
public String getIdent() {
|
||||
return ident;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return ("[TokenIdent] = " + ident);
|
||||
}
|
||||
|
||||
public void setType (int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public static boolean canStart (Tokenizer t)
|
||||
{
|
||||
char nextchar = t.peekChar();
|
||||
if ((nextchar >= 'a' && nextchar <= 'z') ||
|
||||
(nextchar >= 'A' && nextchar <= 'Z') ||
|
||||
(nextchar == '_'))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Token scan (Tokenizer t)
|
||||
{
|
||||
StringBuffer tbuf = new StringBuffer(15);
|
||||
|
||||
while ((t.peekChar() >= 'a' && t.peekChar() <= 'z') ||
|
||||
(t.peekChar() >= 'A' && t.peekChar() <= 'Z') ||
|
||||
(t.peekChar() >= '0' && t.peekChar() <= '9') ||
|
||||
(t.peekChar() == '_'))
|
||||
{
|
||||
|
||||
tbuf.append(t.getChar());
|
||||
}
|
||||
|
||||
String token = tbuf.toString();
|
||||
Token kwtok = TokenKeyword.tokenFromString(token);
|
||||
Token ret = (kwtok != null) ? kwtok : new TokenIdent(token);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void asHTML (Output o)
|
||||
{
|
||||
String c = BackendHTML.IdentColor;
|
||||
if (type == TYPE) c = BackendHTML.TypeColor;
|
||||
else if (type == STRING) c = BackendHTML.StringColor;
|
||||
|
||||
// FIXME: TODO: Don't hardcode internal types, intelligently recognise
|
||||
// places where types and class references occur and
|
||||
// make them class="t"
|
||||
if (ident.equals("int") || ident.equals("void") ||
|
||||
ident.equals("string") || ident.equals("bool")) {
|
||||
|
||||
o.write("<span class=\"t\">" + ident + "</span>");
|
||||
} else {
|
||||
o.write("<span class=\"i\">" + ident + "</span>");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
46
wcmtools/s2/danga/s2/TokenIntegerLiteral.java
Executable file
46
wcmtools/s2/danga/s2/TokenIntegerLiteral.java
Executable file
@@ -0,0 +1,46 @@
|
||||
package danga.s2;
|
||||
|
||||
class TokenIntegerLiteral extends Token {
|
||||
|
||||
int val;
|
||||
|
||||
public TokenIntegerLiteral (int val) {
|
||||
this.val = val;
|
||||
}
|
||||
|
||||
public int getInteger() {
|
||||
return val;
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o) {
|
||||
o.write((new Integer(val)).toString());
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o) {
|
||||
asS2(o); // same as S2 (just an integer)
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer ret = new StringBuffer("[TokenIntegerLiteral] = ");
|
||||
ret.append((new Integer(val)).toString());
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
public static Token scan (Tokenizer t)
|
||||
{
|
||||
StringBuffer tbuf = new StringBuffer(15);
|
||||
|
||||
while (t.peekChar() >= '0' && t.peekChar() <= '9') {
|
||||
tbuf.append(t.getChar());
|
||||
}
|
||||
|
||||
return new TokenIntegerLiteral((new Integer(tbuf.toString())).intValue());
|
||||
}
|
||||
|
||||
public void asHTML (Output o)
|
||||
{
|
||||
o.write("<span class=\"n\">" +
|
||||
val + "</span>");
|
||||
}
|
||||
|
||||
}
|
||||
70
wcmtools/s2/danga/s2/TokenKeyword.java
Executable file
70
wcmtools/s2/danga/s2/TokenKeyword.java
Executable file
@@ -0,0 +1,70 @@
|
||||
package danga.s2;
|
||||
|
||||
class TokenKeyword extends TokenIdent {
|
||||
|
||||
public final static TokenKeyword CLASS = new TokenKeyword("class");
|
||||
public final static TokenKeyword ELSE = new TokenKeyword("else");
|
||||
public final static TokenKeyword ELSEIF = new TokenKeyword("elseif");
|
||||
public final static TokenKeyword FUNCTION = new TokenKeyword("function");
|
||||
public final static TokenKeyword IF = new TokenKeyword("if");
|
||||
public final static TokenKeyword BUILTIN = new TokenKeyword("builtin");
|
||||
public final static TokenKeyword PROPERTY = new TokenKeyword("property");
|
||||
public final static TokenKeyword SET = new TokenKeyword("set");
|
||||
public final static TokenKeyword STATIC = new TokenKeyword("static");
|
||||
public final static TokenKeyword VAR = new TokenKeyword("var");
|
||||
public final static TokenKeyword WHILE = new TokenKeyword("while");
|
||||
public final static TokenKeyword FOREACH = new TokenKeyword("foreach");
|
||||
public final static TokenKeyword PRINT = new TokenKeyword("print");
|
||||
public final static TokenKeyword PRINTLN = new TokenKeyword("println");
|
||||
public final static TokenKeyword NOT = new TokenKeyword("not");
|
||||
public final static TokenKeyword AND = new TokenKeyword("and");
|
||||
public final static TokenKeyword OR = new TokenKeyword("or");
|
||||
public final static TokenKeyword XOR = new TokenKeyword("xor");
|
||||
public final static TokenKeyword LAYERINFO = new TokenKeyword("layerinfo");
|
||||
public final static TokenKeyword EXTENDS = new TokenKeyword("extends");
|
||||
public final static TokenKeyword RETURN = new TokenKeyword("return");
|
||||
public final static TokenKeyword DELETE = new TokenKeyword("delete");
|
||||
public final static TokenKeyword DEFINED = new TokenKeyword("defined");
|
||||
public final static TokenKeyword NEW = new TokenKeyword("new");
|
||||
public final static TokenKeyword TRUE = new TokenKeyword("true");
|
||||
public final static TokenKeyword FALSE = new TokenKeyword("false");
|
||||
public final static TokenKeyword REVERSE = new TokenKeyword("reverse");
|
||||
public final static TokenKeyword SIZE = new TokenKeyword("size");
|
||||
public final static TokenKeyword ISNULL = new TokenKeyword("isnull");
|
||||
public final static TokenKeyword NULL = new TokenKeyword("null");
|
||||
public final static TokenKeyword READONLY = new TokenKeyword("readonly");
|
||||
|
||||
static TokenKeyword[] keywords = {
|
||||
CLASS, ELSE, ELSEIF, FUNCTION, IF, BUILTIN, PROPERTY, SET,
|
||||
STATIC, VAR, WHILE, FOREACH, PRINT, PRINTLN, NEW, TRUE, FALSE,
|
||||
NOT, AND, OR, XOR, LAYERINFO, EXTENDS, RETURN, DELETE, DEFINED,
|
||||
REVERSE, SIZE, ISNULL, NULL, READONLY
|
||||
};
|
||||
|
||||
public TokenKeyword () {
|
||||
this.ident = null;
|
||||
}
|
||||
|
||||
public TokenKeyword (String ident) {
|
||||
this.ident = ident;
|
||||
}
|
||||
|
||||
public static TokenKeyword tokenFromString (String ident) {
|
||||
// FIXME: this is O(n). do binary search or use hash
|
||||
for (int i=0; i<keywords.length; i++) {
|
||||
if (keywords[i].ident.equals(ident))
|
||||
return keywords[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return ("[TokenKeyword] = " + ident);
|
||||
}
|
||||
|
||||
public void asHTML (Output o)
|
||||
{
|
||||
o.write("<span class=\"k\">" + ident + "</span>");
|
||||
}
|
||||
|
||||
}
|
||||
247
wcmtools/s2/danga/s2/TokenPunct.java
Executable file
247
wcmtools/s2/danga/s2/TokenPunct.java
Executable file
@@ -0,0 +1,247 @@
|
||||
package danga.s2;
|
||||
|
||||
class TokenPunct extends Token {
|
||||
|
||||
String punct;
|
||||
public TokenPunct (String punct) {
|
||||
this.punct = punct;
|
||||
}
|
||||
|
||||
public String getPunct() {
|
||||
return punct;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return ("[TokenPunct] = " + punct);
|
||||
}
|
||||
|
||||
public static final TokenPunct LTE = new TokenPunct("<=");
|
||||
public static final TokenPunct LT = new TokenPunct("<");
|
||||
public static final TokenPunct GTE = new TokenPunct(">=");
|
||||
public static final TokenPunct GT = new TokenPunct(">");
|
||||
public static final TokenPunct EQ = new TokenPunct("==");
|
||||
public static final TokenPunct NE = new TokenPunct("!=");
|
||||
public static final TokenPunct ASSIGN = new TokenPunct("=");
|
||||
public static final TokenPunct INC = new TokenPunct("++");
|
||||
public static final TokenPunct PLUS = new TokenPunct("+");
|
||||
public static final TokenPunct DEC = new TokenPunct("--");
|
||||
public static final TokenPunct MINUS = new TokenPunct("-");
|
||||
public static final TokenPunct DEREF = new TokenPunct("->");
|
||||
public static final TokenPunct SCOLON = new TokenPunct(";");
|
||||
public static final TokenPunct COLON = new TokenPunct(":");
|
||||
public static final TokenPunct DCOLON = new TokenPunct("::");
|
||||
public static final TokenPunct LOGAND = new TokenPunct("&&");
|
||||
public static final TokenPunct BITAND = new TokenPunct("&");
|
||||
public static final TokenPunct LOGOR = new TokenPunct("||");
|
||||
public static final TokenPunct BITOR = new TokenPunct("|");
|
||||
public static final TokenPunct MULT = new TokenPunct("*");
|
||||
public static final TokenPunct DIV = new TokenPunct("/");
|
||||
public static final TokenPunct MOD = new TokenPunct("%");
|
||||
public static final TokenPunct NOT = new TokenPunct("!");
|
||||
public static final TokenPunct DOT = new TokenPunct(".");
|
||||
public static final TokenPunct DOTDOT = new TokenPunct("..");
|
||||
public static final TokenPunct LBRACE = new TokenPunct("{");
|
||||
public static final TokenPunct RBRACE = new TokenPunct("}");
|
||||
public static final TokenPunct LBRACK = new TokenPunct("[");
|
||||
public static final TokenPunct RBRACK = new TokenPunct("]");
|
||||
public static final TokenPunct LPAREN = new TokenPunct("(");
|
||||
public static final TokenPunct RPAREN = new TokenPunct(")");
|
||||
public static final TokenPunct COMMA = new TokenPunct(",");
|
||||
public static final TokenPunct QMARK = new TokenPunct("?");
|
||||
public static final TokenPunct DOLLAR = new TokenPunct("$");
|
||||
public static final TokenPunct HASSOC = new TokenPunct("=>");
|
||||
|
||||
public static Token scan (Tokenizer t)
|
||||
{
|
||||
if (t.peekChar() == '$') {
|
||||
t.getChar();
|
||||
return TokenPunct.DOLLAR;
|
||||
}
|
||||
|
||||
if (t.peekChar() == '<') {
|
||||
t.getChar();
|
||||
if (t.peekChar() == '=') {
|
||||
t.getChar();
|
||||
return TokenPunct.LTE;
|
||||
} else {
|
||||
return TokenPunct.LT;
|
||||
}
|
||||
}
|
||||
|
||||
if (t.peekChar() == '>') {
|
||||
t.getChar();
|
||||
if (t.peekChar() == '=') {
|
||||
t.getChar();
|
||||
return TokenPunct.GTE;
|
||||
} else {
|
||||
return TokenPunct.GT;
|
||||
}
|
||||
}
|
||||
|
||||
if (t.peekChar() == '=') {
|
||||
t.getChar();
|
||||
if (t.peekChar() == '=') {
|
||||
t.getChar();
|
||||
return TokenPunct.EQ;
|
||||
} else if (t.peekChar() == '>') {
|
||||
t.getChar();
|
||||
return TokenPunct.HASSOC;
|
||||
} else {
|
||||
return TokenPunct.ASSIGN;
|
||||
}
|
||||
}
|
||||
|
||||
if (t.peekChar() == '+') {
|
||||
t.getChar();
|
||||
if (t.peekChar() == '+') {
|
||||
t.getChar();
|
||||
return TokenPunct.INC;
|
||||
} else {
|
||||
return TokenPunct.PLUS;
|
||||
}
|
||||
}
|
||||
|
||||
if (t.peekChar() == '-') {
|
||||
t.getChar();
|
||||
if (t.peekChar() == '-') {
|
||||
t.getChar();
|
||||
return TokenPunct.DEC;
|
||||
} else if (t.peekChar() == '>') {
|
||||
t.getChar();
|
||||
return TokenPunct.DEREF;
|
||||
} else {
|
||||
return TokenPunct.MINUS;
|
||||
}
|
||||
}
|
||||
|
||||
if (t.peekChar() == ';') {
|
||||
t.getChar();
|
||||
return TokenPunct.SCOLON;
|
||||
}
|
||||
|
||||
if (t.peekChar() == ':') {
|
||||
t.getChar();
|
||||
if (t.peekChar() == ':') {
|
||||
t.getChar();
|
||||
return TokenPunct.DCOLON;
|
||||
} else {
|
||||
return TokenPunct.COLON;
|
||||
}
|
||||
}
|
||||
|
||||
if (t.peekChar() == '&') {
|
||||
t.getChar();
|
||||
if (t.peekChar() == '&') {
|
||||
t.getChar();
|
||||
return TokenPunct.LOGAND;
|
||||
} else {
|
||||
return TokenPunct.BITAND;
|
||||
}
|
||||
}
|
||||
|
||||
if (t.peekChar() == '|') {
|
||||
t.getChar();
|
||||
if (t.peekChar() == '|') {
|
||||
t.getChar();
|
||||
return TokenPunct.LOGOR;
|
||||
} else {
|
||||
return TokenPunct.BITOR;
|
||||
}
|
||||
}
|
||||
|
||||
if (t.peekChar() == '*') {
|
||||
t.getChar();
|
||||
return TokenPunct.MULT;
|
||||
}
|
||||
|
||||
if (t.peekChar() == '/') {
|
||||
t.getChar();
|
||||
return TokenPunct.DIV;
|
||||
}
|
||||
if (t.peekChar() == '%') {
|
||||
t.getChar();
|
||||
return TokenPunct.MOD;
|
||||
}
|
||||
|
||||
if (t.peekChar() == '!') {
|
||||
t.getChar();
|
||||
if (t.peekChar() == '=') {
|
||||
t.getChar();
|
||||
return TokenPunct.NE;
|
||||
} else {
|
||||
return TokenPunct.NOT;
|
||||
}
|
||||
}
|
||||
|
||||
if (t.peekChar() == '{') {
|
||||
t.getChar();
|
||||
return TokenPunct.LBRACE;
|
||||
}
|
||||
|
||||
if (t.peekChar() == '}') {
|
||||
t.getChar();
|
||||
return TokenPunct.RBRACE;
|
||||
}
|
||||
|
||||
if (t.peekChar() == '[') {
|
||||
t.getChar();
|
||||
return TokenPunct.LBRACK;
|
||||
}
|
||||
|
||||
if (t.peekChar() == ']') {
|
||||
t.getChar();
|
||||
return TokenPunct.RBRACK;
|
||||
}
|
||||
|
||||
if (t.peekChar() == '(') {
|
||||
t.getChar();
|
||||
return TokenPunct.LPAREN;
|
||||
}
|
||||
|
||||
if (t.peekChar() == ')') {
|
||||
t.getChar();
|
||||
return TokenPunct.RPAREN;
|
||||
}
|
||||
|
||||
if (t.peekChar() == '.') {
|
||||
t.getChar();
|
||||
if (t.peekChar() == '.') {
|
||||
t.getChar();
|
||||
return TokenPunct.DOTDOT;
|
||||
}
|
||||
return TokenPunct.DOT;
|
||||
}
|
||||
|
||||
if (t.peekChar() == ',') {
|
||||
t.getChar();
|
||||
return TokenPunct.COMMA;
|
||||
}
|
||||
|
||||
if (t.peekChar() == '?') {
|
||||
t.getChar();
|
||||
return TokenPunct.QMARK;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void asHTML (Output o)
|
||||
{
|
||||
if (punct.equals("[") || punct.equals("]") ||
|
||||
punct.equals("(") || punct.equals(")") ||
|
||||
punct.equals("{") || punct.equals("}")) {
|
||||
|
||||
o.write("<span class=\"b\">" + punct + "</span>");
|
||||
} else {
|
||||
o.write("<span class=\"p\">" + punct + "</span>");
|
||||
}
|
||||
}
|
||||
|
||||
public void asS2 (Output o)
|
||||
{
|
||||
o.write(punct);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
159
wcmtools/s2/danga/s2/TokenStringLiteral.java
Executable file
159
wcmtools/s2/danga/s2/TokenStringLiteral.java
Executable file
@@ -0,0 +1,159 @@
|
||||
package danga.s2;
|
||||
|
||||
class TokenStringLiteral extends Token {
|
||||
|
||||
int quotesLeft;
|
||||
int quotesRight;
|
||||
String text;
|
||||
String source;
|
||||
|
||||
public int getQuotesLeft () { return quotesLeft; }
|
||||
public int getQuotesRight () { return quotesRight; }
|
||||
public void setQuotesLeft (int c) { quotesLeft = c; }
|
||||
public void setQuotesRight (int c) { quotesRight = c; }
|
||||
|
||||
public Object clone () {
|
||||
return new TokenStringLiteral(text, source, quotesLeft, quotesRight);
|
||||
}
|
||||
|
||||
public TokenStringLiteral (String text) {
|
||||
this(text, text, 1, 1);
|
||||
|
||||
}
|
||||
public TokenStringLiteral (String text, int quotesLeft, int quotesRight) {
|
||||
this(text, text, quotesLeft, quotesRight);
|
||||
}
|
||||
|
||||
public TokenStringLiteral (String text, String source, int quotesLeft, int quotesRight) {
|
||||
this.text = text;
|
||||
this.source = source;
|
||||
this.quotesLeft = quotesLeft;
|
||||
this.quotesRight = quotesRight;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer ret = new StringBuffer("[TokenStringLiteral] = ");
|
||||
if (quotesLeft == 0) { ret.append("("); }
|
||||
else if (quotesLeft == 1) { ret.append("<"); }
|
||||
else if (quotesLeft == 3) { ret.append("<<"); }
|
||||
ret.append(text);
|
||||
if (quotesRight == 0) { ret.append(")"); }
|
||||
else if (quotesRight == 1) { ret.append(">"); }
|
||||
else if (quotesRight == 3) { ret.append(">>"); }
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
public void asHTML (Output o)
|
||||
{
|
||||
StringBuffer ret = new StringBuffer();
|
||||
ret.append(makeQuotes(quotesLeft));
|
||||
ret.append(source);
|
||||
ret.append(makeQuotes(quotesRight));
|
||||
o.write("<span class=\"s\">" + BackendHTML.quoteHTML(ret.toString()) + "</span>");
|
||||
}
|
||||
|
||||
public static Token scan (Tokenizer t) throws Exception
|
||||
{
|
||||
boolean inTriple = false;
|
||||
boolean continued = false;
|
||||
|
||||
FilePos pos = t.getPos();
|
||||
|
||||
if (t.inString == 0) {
|
||||
// see if this is a triple quoted string,
|
||||
// like python. if so, don't need to escape quotes
|
||||
t.getRealChar(); // 1
|
||||
if (t.peekChar() == '"') {
|
||||
t.getChar(); // 2
|
||||
if (t.peekChar() == '"') {
|
||||
t.getRealChar(); // 3
|
||||
inTriple = true;
|
||||
} else {
|
||||
t.inString = 0;
|
||||
return new TokenStringLiteral("", 1, 1);
|
||||
}
|
||||
}
|
||||
} else if (t.inString == 3) {
|
||||
continued = true;
|
||||
inTriple = true;
|
||||
} else if (t.inString == 1) {
|
||||
continued = true;
|
||||
}
|
||||
|
||||
StringBuffer tbuf = new StringBuffer(inTriple ? 500 : 80); // escaped
|
||||
StringBuffer sbuf = new StringBuffer(inTriple ? 500 : 80); // source
|
||||
|
||||
while (true) {
|
||||
char peekchar = t.peekChar();
|
||||
if (peekchar == (char)-1) {
|
||||
throw new Exception("Run-away string. Check for unbalanced quote types on string literals.");
|
||||
} else if (peekchar == '"') {
|
||||
if (! inTriple) {
|
||||
t.getChar();
|
||||
t.inString = 0;
|
||||
return new TokenStringLiteral(tbuf.toString(), sbuf.toString(), continued ? 0 : 1, 1);
|
||||
} else {
|
||||
t.getChar(); // 1
|
||||
if (t.peekChar() == '"') {
|
||||
t.getChar(); // 2
|
||||
if (t.peekChar() == '"') {
|
||||
t.getChar(); // 3
|
||||
t.inString = 0;
|
||||
return new TokenStringLiteral(tbuf.toString(), sbuf.toString(), continued ? 0 : 3, 3);
|
||||
} else {
|
||||
tbuf.append('"'); tbuf.append('"');
|
||||
sbuf.append('"'); sbuf.append('"');
|
||||
}
|
||||
} else {
|
||||
tbuf.append('"');
|
||||
sbuf.append('"');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (t.peekChar() == '$') {
|
||||
t.inString = inTriple ? 3 : 1;
|
||||
return new TokenStringLiteral(tbuf.toString(), sbuf.toString(),
|
||||
continued ? 0 : (inTriple ? 3 : 1),
|
||||
0);
|
||||
}
|
||||
|
||||
if (t.peekChar() == '\\') {
|
||||
sbuf.append(t.getRealChar()); // skip the backslash. next thing will be literal.
|
||||
sbuf.append(t.peekChar());
|
||||
if (t.peekChar() == 'n') {
|
||||
t.forceNextChar('\n');
|
||||
}
|
||||
tbuf.append(t.getRealChar());
|
||||
} else {
|
||||
char c = t.getRealChar();
|
||||
tbuf.append(c);
|
||||
sbuf.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void asS2 (Indenter o)
|
||||
{
|
||||
o.write(makeQuotes(quotesLeft));
|
||||
o.write(Backend.quoteStringInner(text));
|
||||
o.write(makeQuotes(quotesRight));
|
||||
}
|
||||
|
||||
public void asPerl (BackendPerl bp, Indenter o)
|
||||
{
|
||||
o.write(bp.quoteString(text));
|
||||
}
|
||||
|
||||
private String makeQuotes(int i) {
|
||||
if (i == 0) return "";
|
||||
if (i == 1) return "\"";
|
||||
if (i == 3) return "\"\"\"";
|
||||
return "XXX";
|
||||
}
|
||||
}
|
||||
47
wcmtools/s2/danga/s2/TokenWhitespace.java
Executable file
47
wcmtools/s2/danga/s2/TokenWhitespace.java
Executable file
@@ -0,0 +1,47 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
class TokenWhitespace extends Token {
|
||||
|
||||
String ws;
|
||||
|
||||
public boolean isNecessary() { return false; }
|
||||
|
||||
public TokenWhitespace (String ws) {
|
||||
this.ws = ws;
|
||||
}
|
||||
|
||||
public String getWhitespace() {
|
||||
return ws;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return ("[TokenWhitespace]");
|
||||
}
|
||||
|
||||
public static Token scan (Tokenizer t)
|
||||
{
|
||||
StringBuffer tbuf = new StringBuffer(200);
|
||||
|
||||
while ((t.peekChar() == ' ' || t.peekChar() == '\t' ||
|
||||
t.peekChar() == '\n' || t.peekChar() == '\r')) {
|
||||
tbuf.append(t.getChar());
|
||||
}
|
||||
|
||||
return new TokenWhitespace(tbuf.toString());
|
||||
}
|
||||
|
||||
public void asHTML (Output o) {
|
||||
StringTokenizer st = new StringTokenizer(ws, "\n", true);
|
||||
while (st.hasMoreTokens()) {
|
||||
String s = st.nextToken();
|
||||
if (s.equals("\n") && BackendHTML.addBreaks) {
|
||||
o.write("<br />\n");
|
||||
} else {
|
||||
o.write(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
161
wcmtools/s2/danga/s2/Tokenizer.java
Executable file
161
wcmtools/s2/danga/s2/Tokenizer.java
Executable file
@@ -0,0 +1,161 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
public class Tokenizer
|
||||
{
|
||||
Token peekedToken;
|
||||
Tokenizer masterTokenizer;
|
||||
Scanner sc;
|
||||
|
||||
// these are public for the Tokens to access when scanning: (no 'friend' classes in Java. :/)
|
||||
public int inString; // can be 0, 1, or 3. (string types of none, normal, triple quote)
|
||||
|
||||
boolean varToker = false;
|
||||
|
||||
public Tokenizer (InputStream is)
|
||||
{
|
||||
if (is != null) sc = new Scanner(is);
|
||||
inString = 0;
|
||||
peekedToken = null;
|
||||
masterTokenizer = this;
|
||||
}
|
||||
|
||||
public Tokenizer getVarTokenizer() throws Exception
|
||||
{
|
||||
Tokenizer vt = new Tokenizer(null); // null unimportant--- setting scanner later
|
||||
vt.inString = 0; // note: we probably _are_ in a string.
|
||||
vt.varToker = true;
|
||||
|
||||
// clone everything else:
|
||||
vt.masterTokenizer = masterTokenizer;
|
||||
vt.sc = sc;
|
||||
|
||||
// but don't clone this...
|
||||
if (peekedToken != null) {
|
||||
throw new Exception("Request to instantiate sub-tokenizer failed because "+
|
||||
"master tokenizer has a peeked token loaded already.");
|
||||
}
|
||||
|
||||
return vt;
|
||||
}
|
||||
|
||||
public void release () throws Exception
|
||||
{
|
||||
if (peekedToken != null) {
|
||||
throw new Exception("Sub-tokenizer had a peeked token when releasing.");
|
||||
}
|
||||
}
|
||||
|
||||
public Token peek () throws Exception
|
||||
{
|
||||
if (peekedToken == null) {
|
||||
peekedToken = getToken();
|
||||
}
|
||||
return peekedToken;
|
||||
}
|
||||
|
||||
public Token getToken () throws Exception
|
||||
{
|
||||
if (peekedToken != null) {
|
||||
Token t = peekedToken;
|
||||
peekedToken = null;
|
||||
return t;
|
||||
}
|
||||
|
||||
char nextChar = sc.peek();
|
||||
|
||||
if (nextChar == (char) -1) {
|
||||
if (sc.getBogusFlag())
|
||||
throw new Exception("Malformed source encoding. (should be UTF-8)");
|
||||
return null;
|
||||
}
|
||||
|
||||
FilePos pos = getPos();
|
||||
Token nxtoken = makeToken();
|
||||
nxtoken.pos = pos;
|
||||
|
||||
return nxtoken;
|
||||
}
|
||||
|
||||
public FilePos getPos () {
|
||||
return new FilePos(sc.line, sc.col);
|
||||
}
|
||||
|
||||
private Token makeToken () throws Exception
|
||||
{
|
||||
char nextChar = sc.peek();
|
||||
|
||||
// this has to be before the string literal parsing.
|
||||
if (nextChar == '$') {
|
||||
return TokenPunct.scan(this);
|
||||
}
|
||||
|
||||
if (inString != 0) {
|
||||
return TokenStringLiteral.scan(this);
|
||||
}
|
||||
|
||||
if (nextChar == ' ' || nextChar == '\t' ||
|
||||
nextChar == '\n' || nextChar == '\r') {
|
||||
return TokenWhitespace.scan(this);
|
||||
}
|
||||
|
||||
if (TokenIdent.canStart(this)) {
|
||||
return TokenIdent.scan(this);
|
||||
}
|
||||
|
||||
if (nextChar >= '0' && nextChar <= '9') {
|
||||
return TokenIntegerLiteral.scan(this);
|
||||
}
|
||||
|
||||
if (nextChar == '<' || nextChar == '>' ||
|
||||
nextChar == '=' || nextChar == '!' ||
|
||||
nextChar == ';' || nextChar == ':' ||
|
||||
nextChar == '+' || nextChar == '-' ||
|
||||
nextChar == '*' || nextChar == '/' ||
|
||||
nextChar == '&' || nextChar == '|' ||
|
||||
nextChar == '{' || nextChar == '}' ||
|
||||
nextChar == '[' || nextChar == ']' ||
|
||||
nextChar == '(' || nextChar == ')' ||
|
||||
nextChar == '.' || nextChar == ',' ||
|
||||
nextChar == '?' || nextChar == '%' ||
|
||||
false) {
|
||||
return TokenPunct.scan(this);
|
||||
}
|
||||
|
||||
if (nextChar == '#') {
|
||||
return TokenComment.scan(this);
|
||||
}
|
||||
|
||||
if (nextChar == '"') {
|
||||
return TokenStringLiteral.scan(this);
|
||||
}
|
||||
|
||||
throw new Exception("Parse error! Unknown character '" + nextChar +
|
||||
"' (" + (new Integer((int)nextChar)).toString() + ") encountered at " +
|
||||
locationString());
|
||||
}
|
||||
|
||||
public String locationString () {
|
||||
return sc.locationString();
|
||||
}
|
||||
|
||||
public char peekChar () {
|
||||
return sc.peek();
|
||||
}
|
||||
|
||||
public char getChar () {
|
||||
return sc.getChar();
|
||||
}
|
||||
|
||||
public char getRealChar () throws Exception {
|
||||
return sc.getRealChar();
|
||||
}
|
||||
|
||||
public void forceNextChar (char ch) {
|
||||
sc.forceNextChar(ch);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
152
wcmtools/s2/danga/s2/Type.java
Executable file
152
wcmtools/s2/danga/s2/Type.java
Executable file
@@ -0,0 +1,152 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
public class Type implements Cloneable
|
||||
{
|
||||
public final static Type VOID = new Type("void");
|
||||
public final static Type STRING = new Type("string");
|
||||
public final static Type INT = new Type("int");
|
||||
public final static Type BOOL = new Type("bool");
|
||||
|
||||
protected LinkedList typeMods; // stores "[]" and "{}" information
|
||||
protected String baseType;
|
||||
|
||||
protected boolean readOnly = false;
|
||||
|
||||
public Object clone () {
|
||||
Type nt = new Type (baseType);
|
||||
|
||||
// GCJBUG: this doesn't work in gcj
|
||||
// nt.typeMods = (LinkedList) typeMods.clone();
|
||||
|
||||
// GCJBUG: this works:
|
||||
nt.typeMods = new LinkedList();
|
||||
for (ListIterator li = typeMods.listIterator(); li.hasNext(); ) {
|
||||
nt.typeMods.add(li.next());
|
||||
}
|
||||
nt.readOnly = readOnly;
|
||||
|
||||
return nt;
|
||||
}
|
||||
|
||||
public Type (String baseType) {
|
||||
this.baseType = baseType;
|
||||
typeMods = new LinkedList();
|
||||
}
|
||||
|
||||
// return true if the type is an INT or BOOL (something
|
||||
// that can be interpretted in a boolean context)
|
||||
public boolean isBoolable ()
|
||||
{
|
||||
return (equals(BOOL) || equals(INT));
|
||||
}
|
||||
|
||||
// return a vector of all the sub-types this could be:
|
||||
// if this is a A[][], would return A[][], B[][], C[][]
|
||||
public ListIterator subTypesIter (Checker ck)
|
||||
{
|
||||
LinkedList l = new LinkedList();
|
||||
NodeClass nc = ck.getClass(baseType);
|
||||
if (nc == null) {
|
||||
// no sub-classes. just return our type.
|
||||
l.add(this);
|
||||
return l.listIterator();
|
||||
}
|
||||
ListIterator di = nc.getDerClasses().listIterator();
|
||||
while (di.hasNext()) {
|
||||
// add a copy of this type to the list, but with
|
||||
// the derivative class type.
|
||||
DerItem der = (DerItem) di.next();
|
||||
String c = der.nc.getName();
|
||||
Type newt = (Type) clone();
|
||||
newt.baseType = c;
|
||||
l.add(newt);
|
||||
}
|
||||
return l.listIterator();
|
||||
}
|
||||
|
||||
public boolean equals (Object o) {
|
||||
if (! (o instanceof Type)) return false;
|
||||
Type ot = (Type) o;
|
||||
if (ot.baseType.equals(baseType) &&
|
||||
ot.typeMods.equals(typeMods))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean sameMods (Type a, Type b)
|
||||
{
|
||||
if (a.typeMods.equals(b.typeMods))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void makeArrayOf () {
|
||||
typeMods.addLast("[]");
|
||||
}
|
||||
|
||||
public void makeHashOf () {
|
||||
typeMods.addLast("{}");
|
||||
}
|
||||
|
||||
public void removeMod () {
|
||||
try {
|
||||
typeMods.removeLast();
|
||||
} catch (Exception e) { }
|
||||
}
|
||||
|
||||
// return true if it's not a hashof or arrayof
|
||||
public boolean isSimple () {
|
||||
return (typeMods.size() == 0);
|
||||
}
|
||||
|
||||
private boolean isThing (String s) {
|
||||
try {
|
||||
Object o = typeMods.removeLast();
|
||||
typeMods.addLast(o);
|
||||
return o.equals(s);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHashOf () { return isThing("{}"); }
|
||||
public boolean isArrayOf () { return isThing("[]"); }
|
||||
|
||||
public String baseType ()
|
||||
{
|
||||
return baseType;
|
||||
}
|
||||
|
||||
public String toString ()
|
||||
{
|
||||
StringBuffer sb = new StringBuffer(baseType);
|
||||
ListIterator li = typeMods.listIterator(0);
|
||||
while (li.hasNext()) {
|
||||
String s = (String) li.next();
|
||||
sb.append(s);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static boolean isPrimitive (String bt)
|
||||
{
|
||||
Type t = new Type(bt);
|
||||
return (t.equals(STRING) ||
|
||||
t.equals(INT) ||
|
||||
t.equals(BOOL));
|
||||
}
|
||||
|
||||
public boolean isPrimitive () {
|
||||
return (this.equals(STRING) ||
|
||||
this.equals(INT) ||
|
||||
this.equals(BOOL));
|
||||
|
||||
}
|
||||
|
||||
public boolean isReadOnly () { return readOnly; }
|
||||
public void setReadOnly (boolean b) { readOnly = b; }
|
||||
|
||||
}
|
||||
151
wcmtools/s2/danga/s2/s2compile.java
Executable file
151
wcmtools/s2/danga/s2/s2compile.java
Executable file
@@ -0,0 +1,151 @@
|
||||
package danga.s2;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.File;
|
||||
import java.util.Hashtable;
|
||||
|
||||
class s2compile
|
||||
{
|
||||
|
||||
public static String topLayerName;
|
||||
public static String topLayerType;
|
||||
|
||||
public static void main (String[] args) throws Exception
|
||||
{
|
||||
if (args.length < 3 || args.length % 2 != 1) { usage(); return; }
|
||||
Hashtable hargs = new Hashtable();
|
||||
int i = 0;
|
||||
while (args[i].startsWith("-") && i != args.length-1) {
|
||||
int offset = args[i].startsWith("--") ? 2 : 1;
|
||||
String key = args[i].substring(offset, args[i].length());
|
||||
String val = args[i+1];
|
||||
i += 2;
|
||||
hargs.put(key, val);
|
||||
}
|
||||
if (i != args.length - 1) { usage(); return; }
|
||||
String filename = args[i];
|
||||
|
||||
String format = (String) hargs.get("output");
|
||||
if (format == null) {
|
||||
System.err.println("No output format specified");
|
||||
return;
|
||||
}
|
||||
|
||||
// Tokens output format requires no validation.
|
||||
if (format.equals("tokens")) {
|
||||
Tokenizer toker = new Tokenizer(getInputStream(filename));
|
||||
try {
|
||||
Token tok;
|
||||
while ((tok = toker.getToken()) != null) {
|
||||
System.out.println(tok.toString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.toString());
|
||||
return;
|
||||
}
|
||||
System.out.println("end.");
|
||||
return;
|
||||
}
|
||||
|
||||
String layertype = null;
|
||||
Checker ck = null;
|
||||
Layer layerMain;
|
||||
|
||||
// TODO: respect cmdline option to pre-load serialized checker to
|
||||
// avoid having to make one by reparsing source of core[+layout]
|
||||
|
||||
if (format.equals("html") || format.equals("s2")) {
|
||||
ck = null;
|
||||
} else {
|
||||
ck = new Checker();
|
||||
layertype = (String) hargs.get("layertype");
|
||||
if (layertype == null) {
|
||||
System.err.println("Unspecified layertype.");
|
||||
return;
|
||||
} else if (layertype.equals("core")) {
|
||||
// nothing.
|
||||
}
|
||||
else if (layertype.equals("i18nc") || layertype.equals("layout")) {
|
||||
makeLayer((String) hargs.get("core"), "core", ck);
|
||||
}
|
||||
else if (layertype.equals("theme") || layertype.equals("i18n") ||
|
||||
layertype.equals("user")) {
|
||||
makeLayer((String) hargs.get("core"), "core", ck);
|
||||
makeLayer((String) hargs.get("layout"), "layout", ck);
|
||||
}
|
||||
else {
|
||||
System.err.println("Invalid layertype.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
layerMain = makeLayer(filename, layertype, ck);
|
||||
|
||||
topLayerName = layerMain.getLayerInfo("name");
|
||||
topLayerType = layerMain.type;
|
||||
|
||||
Output o = new OutputConsole();
|
||||
Backend be = null;
|
||||
|
||||
if (format.equals("html"))
|
||||
be = new BackendHTML(layerMain);
|
||||
|
||||
if (format.equals("s2"))
|
||||
be = new BackendS2(layerMain);
|
||||
|
||||
if (format.equals("perl")) {
|
||||
int layerid = 0;
|
||||
try {
|
||||
layerid = Integer.parseInt((String) hargs.get("layerid"));
|
||||
} catch (Exception e) {
|
||||
System.err.println("Unspecified -layerid <n>");
|
||||
return;
|
||||
}
|
||||
be = new BackendPerl(layerMain, layerid);
|
||||
}
|
||||
|
||||
if (be == null) {
|
||||
System.err.println("No backend found for '" + format + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
be.output(o);
|
||||
o.flush();
|
||||
return;
|
||||
}
|
||||
|
||||
private static int usage () {
|
||||
System.err.println("Usage: ");
|
||||
System.err.println(" s2compile [opts]* <file>\n");
|
||||
System.err.println("Options:");
|
||||
System.err.println(" --output <format> One of: perl, html, s2, tokens");
|
||||
System.err.println(" --layerid <int> For perl output format only");
|
||||
System.err.println(" --layertype <type> One of: core, i18nc, layout, theme, i18n, user");
|
||||
System.err.println(" --core <filename> Core S2 file, if layertype after core");
|
||||
System.err.println(" --layout <filename> Layout S2 file, if compiling layer after layout");
|
||||
System.err.println("\nAny file args can be '-' to read from STDIN, ending with ^D");
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static InputStream getInputStream (String filename) throws Exception {
|
||||
if (filename.equals("-"))
|
||||
return System.in;
|
||||
return new FileInputStream(new File(filename));
|
||||
}
|
||||
|
||||
public static Layer makeLayer (String filename, String type, Checker ck) throws Exception
|
||||
{
|
||||
if (filename == null) {
|
||||
throw new Exception("Undefined filename for "+type+" layer.");
|
||||
}
|
||||
|
||||
Tokenizer toker = new Tokenizer(getInputStream(filename));
|
||||
Layer s2l = new Layer(toker, type);
|
||||
// now check the layer, since it must have parsed fine (otherwise
|
||||
// the Layer constructor would have thrown an exception
|
||||
if (ck != null)
|
||||
ck.checkLayer(s2l);
|
||||
return s2l;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user