First import of Factor 0.60

cvs first-cvs-commit
Slava Pestov 2004-07-16 06:26:21 +00:00
parent ce55daaf6d
commit c8654c207d
312 changed files with 37480 additions and 0 deletions

BIN
Factor.jar Normal file

Binary file not shown.

1
Factor.manifest Normal file
View File

@ -0,0 +1 @@
Main-Class: factor.listener.FactorDesktop

55
README.SRC.txt Normal file
View File

@ -0,0 +1,55 @@
FACTOR
This source archive contains sources for two distinct
bodies of code -- a Factor interpreter written in Java,
and a Factor interpreter written in C. The C interpreter
is a more recent development than the Java interpreter.
They both share a large body of library code written in
Factor.
Java interpreter
----------------
The Java interpreter includes a slick GUI with hyperlinked
inspection of source code, as well as stack effect checking.
build.xml - Ant buildfile for Java interpreter.
factor/ - source code for Factor interpreter written in Java.
org/objectweb/asm/ - helper library for Java interpreter.
Factor.jar - compiled, stand-alone Java interpreter
C interpreter
-------------
The C interpreter is a minimal implementation, with the goal
of achieving the highest possible flexibility/lines of code
ratio. It runs faster than the Java interpreter, and uses
far less memory.
native/ - source code for Factor interpreter written in C.
native/build.sh - build script for C interpreter.
native/f - compiled C interpreter - needs image to run
native/factor.image - cross-compiler output
library/ - Factor code shared between Java and C backends
library/platform/jvm - Java interpreter-specific code
library/platform/native - C interpreter-specific code
Notes on the C interpreter
--------------------------
At the moment it assumes little endian byte order, 32-bit
words. This pretty much means x86.
Very soon I will add image input and output in both byte
orders - this will allow Factor to run on powerpc and
sparc.
Moving to 64-bits would require a few changes in the image
cross-compiler, namely in the way it packs strings.
Not everything has been implemented yet. However, a lot
already works. Compare the output of this in the C and
Java interpreters to see how they differ:
"vocabularies" get describe

72
TODO.FACTOR.txt Normal file
View File

@ -0,0 +1,72 @@
contains ==> contains?
.s: needs nreverse
{...} vectors
better .s
+ native:
- partition, sort
- describe-word
- need hashtable inspection too
- clone-sbuf
- parsing should be parsing
- inspector:
sort
partition
- vocab inspecting ==> worddef>list, assumes . on a list works
+ interactive:
- words. should output in a nicer form
- inspector links when describe called without object path
- 'cascading' styles
+ math:
- FactorLib.equal() not very good
- IN: format base: work with all types of numbers
+ listener:
- link style lingers
- back space then type: input style gone
- fedit broken with listener
- press enter in the middle of a line
- print-numbered-list
- new-listener shouldn't suspend continuation in current listener
+ compiler:
- tail call optimization broken again
- don't compile inline words
- recursive words with code after ifte
- less unnecessary args to auxiliary methods
- inlining tail-recursive immediates
- direct stack access leaks memory on stack
- unnecessary local allocation: max is instance var, but several methods
get compiled.
+ misc:
- should i -i inf -inf be parsing words?
- fix multithreading
- errors at startup not handled properly
- namespace clone drops static var bindings
- ditch map
- ditch expand
- try/catch
- set-object-path
- more readable traces
+ httpd:
- httpd: don't flush so much
- log with date
- log user agent
- add a socket timeout
- if a directory is requested and URL does not end with /, redirect
- return more header fields, like Content-Length, Last-Modified, and so on
- HEAD request
- basic authentication, using httpdAuth function from a config file

59
build.xml Normal file
View File

@ -0,0 +1,59 @@
<?xml version="1.0"?>
<!--
Lets get high!
-->
<project name="SmokedOut" default="dist" basedir=".">
<target name="compile">
<javac
srcdir="."
destdir="."
deprecation="on"
includeJavaRuntime="yes"
debug="true"
optimize="true"
>
<include name="**/*.java"/>
</javac>
</target>
<target name="dist" depends="compile">
<jar
jarfile="Factor.jar"
manifest="Factor.manifest"
compress="true"
>
<fileset dir=".">
<include name="factor/*.java"/>
<include name="factor/**/*.java"/>
<include name="factor/*.class"/>
<include name="factor/*.factor"/>
<include name="factor/*.fasl"/>
<include name="factor/**/*.factor"/>
<include name="factor/**/*.class"/>
<include name="library/**/*.factor"/>
<include name="org/**/*.java"/>
<include name="org/**/*.class"/>
<include name="*.factor"/>
<include name="*.lsa"/>
<include name="Factor.manifest"/>
</fileset>
</jar>
</target>
<target name="clean" description="Clean old stuff.">
<delete>
<fileset dir="." includes="**/*.class"/>
<fileset dir="." includes="**/*.fasl"/>
<fileset dir="." includes="**/*~" defaultexcludes="no"/>
<fileset dir="." includes="**/#*#" defaultexcludes="no"/>
<fileset dir="." includes="**/*.rej"/>
<fileset dir="." includes="**/*.orig"/>
<fileset dir="." includes="**/.*.swp"/>
<fileset dir="." includes="**/.#*"/>
<fileset dir="." includes="**/.new*"/>
<fileset dir="." includes="**/.directory"/>
</delete>
</target>
</project>

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
!a;7777;7777

View File

@ -0,0 +1 @@
!a;7528;7528

View File

@ -0,0 +1 @@
!a;10514;10514

View File

@ -0,0 +1 @@
!a;4651;4651

Binary file not shown.

Binary file not shown.

Binary file not shown.

322
factor/Cons.java Normal file
View File

@ -0,0 +1,322 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
/**
* Used to build up linked lists.
*/
public class Cons implements PublicCloneable, FactorExternalizable
{
public static int COUNT;
public Object car;
public Object cdr;
//{{{ Cons constructor
public Cons(Object car, Object cdr)
{
this.car = car;
this.cdr = cdr;
COUNT++;
} //}}}
//{{{ car() method
public Object car(Class clas) throws Exception
{
return FactorJava.convertToJavaType(car,clas);
} //}}}
//{{{ cdr() method
public Object cdr(Class clas) throws Exception
{
return FactorJava.convertToJavaType(cdr,clas);
} //}}}
//{{{ next() method
public Cons next()
{
return (Cons)cdr;
} //}}}
//{{{ get() method
public Object get(int index)
{
return _get(index).car;
} //}}}
//{{{ _get() method
public Cons _get(int index)
{
Cons iter = this;
while(index != 0)
{
iter = (Cons)iter.cdr;
index--;
}
return iter;
} //}}}
//{{{ contains() method
public boolean contains(Object obj)
{
Cons iter = this;
while(iter != null)
{
if(FactorLib.objectsEqual(obj,iter.car))
return true;
iter = iter.next();
}
return false;
} //}}}
//{{{ contains() method
public static boolean contains(Cons list, Object obj)
{
if(list == null)
return false;
else
return list.contains(obj);
} //}}}
//{{{ length() method
public int length()
{
int size = 0;
Cons iter = this;
while(iter != null)
{
iter = (Cons)iter.cdr;
size++;
}
return size;
} //}}}
//{{{ nappend() method
public static Cons nappend(Cons l1, Cons l2)
{
if(l1 == null)
return l2;
if(l2 == null)
return l1;
Cons last = l1;
while(last.cdr != null)
last = last.next();
last.cdr = l2;
return l1;
} //}}}
//{{{ reverse() method
public static Cons reverse(Cons list)
{
Cons reversed = null;
while(list != null)
{
reversed = new Cons(list.car,reversed);
list = list.next();
}
return reversed;
} //}}}
//{{{ assoc() method
public static Object assoc(Cons assoc, Object key)
{
if(assoc == null)
return null;
else
{
Cons first = (Cons)assoc.car;
if(FactorLib.equal(first.car,key))
return first.cdr;
else
return assoc(assoc.next(),key);
}
} //}}}
//{{{ unparseDB() method
/**
* Returns elementsToDBString() enclosed with [ and ].
*/
public String unparseDB()
{
return "[ " + elementsToString(true) + " ]";
} //}}}
//{{{ elementsToString() method
/**
* Returns a whitespace separated string of the unparseObject() of each
* item.
*/
public String elementsToString()
{
return elementsToString(false);
} //}}}
//{{{ elementsToString() method
/**
* Returns a whitespace separated string of the unparseObject() of each
* item.
*/
public String elementsToString(boolean dbUnparse)
{
StringBuffer buf = new StringBuffer();
Cons iter = this;
while(iter != null)
{
buf.append(dbUnparse ?
FactorReader.unparseDBObject(iter.car)
: FactorReader.unparseObject(iter.car));
if(iter.cdr instanceof Cons)
{
buf.append(' ');
iter = (Cons)iter.cdr;
continue;
}
else if(iter.cdr == null)
break;
else
{
buf.append(" | ");
buf.append(dbUnparse ?
FactorReader.unparseDBObject(iter.cdr)
: FactorReader.unparseObject(iter.cdr));
iter = null;
}
}
return buf.toString();
} //}}}
//{{{ toString() method
/**
* Returns elementsToString() enclosed with [ and ].
*/
public String toString()
{
return "[ " + elementsToString(false) + " ]";
} //}}}
//{{{ toArray() method
/**
* Note that unlike Java list toArray(), the given array must already
* be the right size.
*/
public Object[] toArray(Object[] returnValue)
{
int i = 0;
Cons iter = this;
while(iter != null)
{
returnValue[i++] = iter.car;
iter = iter.next();
}
return returnValue;
} //}}}
//{{{ fromArray() method
public static Cons fromArray(Object[] array)
{
if(array == null || array.length == 0)
return null;
else
{
Cons first = new Cons(array[0],null);
Cons last = first;
for(int i = 1; i < array.length; i++)
{
Cons cons = new Cons(array[i],null);
last.cdr = cons;
last = cons;
}
return first;
}
} //}}}
//{{{ equals() method
public boolean equals(Object o)
{
if(o instanceof Cons)
{
Cons l = (Cons)o;
return FactorLib.objectsEqual(car,l.car)
&& FactorLib.objectsEqual(cdr,l.cdr);
}
else
return false;
} //}}}
//{{{ hashCode() method
public int hashCode()
{
if(car == null)
return 0;
else
return car.hashCode();
} //}}}
//{{{ clone() method
public Object clone()
{
if(cdr instanceof Cons)
return new Cons(car,((Cons)cdr).clone());
else
return new Cons(car,cdr);
} //}}}
//{{{ deepClone() method
public static Cons deepClone(Cons list)
{
if(list == null)
return null;
Object ccar;
if(list.car instanceof PublicCloneable)
ccar = ((PublicCloneable)list.car).clone();
else
ccar = list.car;
if(list.cdr instanceof Cons)
{
return new Cons(ccar,deepClone(list.next()));
}
else if(list.cdr == null)
{
return new Cons(ccar,null);
}
else
{
Object ccdr;
if(list.cdr instanceof PublicCloneable)
ccdr = ((PublicCloneable)list.cdr).clone();
else
ccdr = list.cdr;
return new Cons(ccar,ccdr);
}
} //}}}
}

189
factor/FactorArray.java Normal file
View File

@ -0,0 +1,189 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
/**
* Factor is a stack-based language.
* @author Slava Pestov
*/
public class FactorArray implements PublicCloneable
{
public Object[] stack;
public int top;
//{{{ FactorArray constructor
public FactorArray()
{
stack = new Object[64];
} //}}}
//{{{ FactorArray constructor
public FactorArray(int size)
{
stack = new Object[size];
} //}}}
//{{{ FactorArray constructor
public FactorArray(Cons list)
{
this();
if(list != null)
{
ensurePush(list.length());
while(list != null)
{
push(list.car);
list = list.next();
}
}
} //}}}
//{{{ FactorArray constructor
public FactorArray(Object[] stack, int top)
{
this.stack = stack;
this.top = top;
} //}}}
//{{{ pop() method
public Object pop(Class clas) throws Exception
{
return FactorJava.convertToJavaType(pop(),clas);
} //}}}
//{{{ pop() method
public Object pop() throws FactorStackException
{
ensurePop(1);
Object returnValue = stack[--top];
stack[top] = null;
return returnValue;
} //}}}
//{{{ peek() method
public Object peek() throws FactorStackException
{
ensurePop(1);
return stack[top - 1];
} //}}}
//{{{ ensurePop() method
public void ensurePop(int amount) throws FactorStackException
{
if(amount > top)
throw new FactorStackException(amount);
} //}}}
//{{{ push() method
public void push(Object o)
{
ensurePush(1);
stack[top++] = o;
} //}}}
//{{{ pushAll() method
public void pushAll(Object[] array)
{
ensurePush(array.length);
System.arraycopy(array,0,stack,top,array.length);
top += array.length;
} //}}}
//{{{ ensureCapacity() method
private void ensureCapacity(int index)
{
if(index >= stack.length)
{
Object[] newStack = new Object[index * 2 + 1];
System.arraycopy(stack,0,newStack,0,top);
stack = newStack;
}
} //}}}
//{{{ ensurePush() method
public void ensurePush(int amount)
{
ensureCapacity(top + amount);
} //}}}
//{{{ get() method
public Object get(int index)
{
return stack[index];
} //}}}
//{{{ set() method
public void set(Object value, int index)
{
ensureCapacity(index);
if(index >= top)
{
for(int i = top; i < index; i++)
stack[i] = null;
top = index + 1;
}
stack[index] = value;
} //}}}
//{{{ getCapacity() method
public int getCapacity()
{
return stack.length;
} //}}}
//{{{ toList() method
public Cons toList()
{
Cons first = null, last = null;
for(int i = 0; i < top; i++)
{
Cons cons = new Cons(stack[i],null);
if(first == null)
first = cons;
else
last.cdr = cons;
last = cons;
}
return first;
} //}}}
//{{{ clone() method
public Object clone()
{
if(stack == null)
return new FactorArray();
else
{
return new FactorArray(
FactorLib.cloneArray(stack),top);
}
} //}}}
}

View File

@ -0,0 +1,367 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import factor.compiler.*;
import factor.db.Workspace;
import factor.db.PersistenceException;
import java.lang.reflect.*;
import java.io.FileOutputStream;
import java.util.*;
import org.objectweb.asm.*;
/**
* : name ... ;
*/
public class FactorCompoundDefinition extends FactorWordDefinition
{
private static int compileCount;
public Cons definition;
private Cons endOfDocs;
//{{{ FactorCompoundDefinition constructor
/**
* A new definition.
*/
public FactorCompoundDefinition(FactorWord word, Cons definition,
FactorInterpreter interp) throws PersistenceException
{
super(word,interp.workspace);
fromList(definition,interp);
if(interp.workspace != null)
interp.workspace.put(this);
} //}}}
//{{{ FactorCompoundDefinition constructor
/**
* A blank definition, about to be unpickled.
*/
public FactorCompoundDefinition(Workspace workspace, long id)
{
super(workspace,id);
} //}}}
//{{{ eval() method
public void eval(FactorInterpreter interp)
throws Exception
{
lazyInit(interp);
interp.call(endOfDocs);
} //}}}
//{{{ getStackEffect() method
public void getStackEffect(RecursiveState recursiveCheck,
FactorCompiler compiler) throws Exception
{
lazyInit(compiler.interp);
RecursiveForm rec = recursiveCheck.get(word);
if(rec.active)
{
if(rec.baseCase == null)
throw new FactorCompilerException("Indeterminate recursive call");
compiler.apply(StackEffect.decompose(rec.effect,rec.baseCase));
}
else
{
compiler.compile(endOfDocs,null,recursiveCheck);
}
} //}}}
//{{{ getClassName() method
private static String getClassName(String name)
{
return FactorJava.getSanitizedName(name)
+ "_" + (compileCount++);
} //}}}
//{{{ compile() method
/**
* Compile the given word, returning a new word definition.
*/
FactorWordDefinition compile(FactorInterpreter interp,
RecursiveState recursiveCheck) throws Exception
{
lazyInit(interp);
// Each word has its own class loader
FactorClassLoader loader = new FactorClassLoader(
interp.workspace);
StackEffect effect = getStackEffect(interp);
if(effect.inR != 0 || effect.outR != 0)
throw new FactorCompilerException("Compiled code cannot manipulate call stack frames");
String className = getClassName(word.name);
ClassWriter cw = new ClassWriter(true);
cw.visit(ACC_PUBLIC, className,
"factor/compiler/CompiledDefinition",
null, null);
compileConstructor(cw,className);
FactorCompiler compiler = compileEval(interp,cw,loader,
className,effect,recursiveCheck);
// Generate auxiliary methods
compiler.generateAuxiliary(cw);
// Generate fields for storing literals and
// word references
compiler.generateFields(cw);
compileToList(interp,compiler,cw);
compileGetStackEffect(cw,effect);
// gets the bytecode of the class, and loads it
// dynamically
byte[] code = cw.toByteArray();
if(interp.dump)
{
FileOutputStream fos = new FileOutputStream(
className + ".class");
try
{
fos.write(code);
}
finally
{
fos.close();
}
}
String javaClassName = className.replace('/','.');
word.setCompiledInfo(compiler.loader,javaClassName);
Class compiledWordClass = loader.addClass(
javaClassName,code,0,code.length);
return CompiledDefinition.create(interp,word,compiledWordClass);
} //}}}
//{{{ compileConstructor() method
private void compileConstructor(ClassVisitor cw, String className)
{
// creates a MethodWriter for the constructor
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC,
"<init>",
"(Lfactor/FactorWord;)V",
null, null);
// pushes the 'this' variable
mw.visitVarInsn(ALOAD, 0);
// pushes the word parameter
mw.visitVarInsn(ALOAD, 1);
// invokes the super class constructor
mw.visitMethodInsn(INVOKESPECIAL,
"factor/compiler/CompiledDefinition", "<init>",
"(Lfactor/FactorWord;)V");
mw.visitInsn(RETURN);
mw.visitMaxs(0,0);
} //}}}
//{{{ compileToList() method
private void compileToList(FactorInterpreter interp,
FactorCompiler compiler, ClassVisitor cw)
{
// creates a MethodWriter for the toList() method
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC,
"toList",
"(Lfactor/FactorInterpreter;)Lfactor/Cons;",
null, null);
// push unparsed string representation of this word and parse it
compiler.generateParse(mw,toList(interp),1);
mw.visitTypeInsn(CHECKCAST,"factor/Cons");
mw.visitInsn(ARETURN);
mw.visitMaxs(0,0);
} //}}}
//{{{ compileGetStackEffect() method
private void compileGetStackEffect(ClassVisitor cw, StackEffect effect)
{
// creates a MethodWriter for the getStackEffect() method
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC,
"getStackEffect",
"(Lfactor/compiler/RecursiveState;"
+ "Lfactor/compiler/FactorCompiler;)V",
null, null);
mw.visitVarInsn(ALOAD,2);
mw.visitTypeInsn(NEW,"factor/compiler/StackEffect");
mw.visitInsn(DUP);
mw.visitLdcInsn(new Integer(effect.inD));
mw.visitLdcInsn(new Integer(effect.outD));
mw.visitLdcInsn(new Integer(effect.inR));
mw.visitLdcInsn(new Integer(effect.outR));
mw.visitMethodInsn(INVOKESPECIAL,"factor/compiler/StackEffect",
"<init>","(IIII)V");
mw.visitMethodInsn(INVOKEVIRTUAL,"factor/compiler/FactorCompiler",
"apply","(Lfactor/compiler/StackEffect;)V");
mw.visitInsn(RETURN);
mw.visitMaxs(0,0);
} //}}}
//{{{ compileEval() method
/**
* Write the definition of the eval() method in the compiled word.
* Local 0 -- this
* Local 1 -- interpreter
*/
protected FactorCompiler compileEval(FactorInterpreter interp,
ClassWriter cw, FactorClassLoader loader,
String className, StackEffect effect,
RecursiveState recursiveCheck)
throws Exception
{
cw.visitField(ACC_PRIVATE | ACC_STATIC, "initialized", "Z",
null, null);
// creates a MethodWriter for the 'eval' method
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC,
"eval", "(Lfactor/FactorInterpreter;)V",
null, null);
// eval() method calls core
mw.visitVarInsn(ALOAD,1);
compileDataStackToJVMStack(effect,mw);
mw.visitMethodInsn(INVOKESTATIC,className,"core",
effect.getCorePrototype());
compileJVMStackToDataStack(effect,mw);
mw.visitInsn(RETURN);
mw.visitMaxs(0,0);
// generate core
FactorCompiler compiler = new FactorCompiler(interp,word,
className,loader);
compiler.init(1,effect.inD,effect.inR,"core");
compiler.compileCore(endOfDocs,cw,effect,recursiveCheck);
return compiler;
} //}}}
//{{{ compileDataStackToJVMStack() method
private void compileDataStackToJVMStack(StackEffect effect,
CodeVisitor mw)
{
if(effect.inD != 0)
{
mw.visitVarInsn(ALOAD,1);
mw.visitFieldInsn(GETFIELD,
"factor/FactorInterpreter", "datastack",
"Lfactor/FactorArray;");
// ensure the stack has enough elements
mw.visitInsn(DUP);
mw.visitIntInsn(BIPUSH,effect.inD);
mw.visitMethodInsn(INVOKEVIRTUAL,
"factor/FactorArray", "ensurePop",
"(I)V");
// datastack.stack -> 2
mw.visitInsn(DUP);
mw.visitFieldInsn(GETFIELD,
"factor/FactorArray", "stack",
"[Ljava/lang/Object;");
mw.visitVarInsn(ASTORE,2);
// datastack.top-args.length -> 3
mw.visitInsn(DUP);
mw.visitFieldInsn(GETFIELD,
"factor/FactorArray", "top",
"I");
mw.visitIntInsn(BIPUSH,effect.inD);
mw.visitInsn(ISUB);
// datastack.top -= args.length
mw.visitInsn(DUP_X1);
mw.visitFieldInsn(PUTFIELD,
"factor/FactorArray", "top",
"I");
mw.visitVarInsn(ISTORE,3);
for(int i = 0; i < effect.inD; i++)
{
mw.visitVarInsn(ALOAD,2);
mw.visitVarInsn(ILOAD,3);
mw.visitInsn(AALOAD);
if(i != effect.inD - 1)
mw.visitIincInsn(3,1);
}
}
} //}}}
//{{{ compileJVMStackToDataStack() method
private void compileJVMStackToDataStack(StackEffect effect,
CodeVisitor mw)
{
if(effect.outD == 1)
{
// ( datastack )
mw.visitVarInsn(ALOAD,1);
mw.visitFieldInsn(GETFIELD,
"factor/FactorInterpreter", "datastack",
"Lfactor/FactorArray;");
mw.visitInsn(SWAP);
mw.visitMethodInsn(INVOKEVIRTUAL,
"factor/FactorArray", "push",
"(Ljava/lang/Object;)V");
}
} //}}}
//{{{ fromList() method
public void fromList(Cons definition, FactorInterpreter interp)
{
this.definition = definition;
if(definition == null)
endOfDocs = null;
else
{
endOfDocs = definition;
while(endOfDocs != null
&& endOfDocs.car instanceof FactorDocComment)
endOfDocs = endOfDocs.next();
}
} //}}}
//{{{ toList() method
public Cons toList(FactorInterpreter interp)
{
return definition;
} //}}}
}

View File

@ -0,0 +1,347 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import factor.compiler.*;
import factor.db.Workspace;
import factor.db.PersistenceException;
import java.lang.reflect.*;
import java.io.FileOutputStream;
import java.util.*;
import org.objectweb.asm.*;
/**
* : name ... ;
*/
public class FactorCompoundDefinition extends FactorWordDefinition
{
private static int compileCount;
public Cons definition;
private Cons endOfDocs;
//{{{ FactorCompoundDefinition constructor
/**
* A new definition.
*/
public FactorCompoundDefinition(FactorWord word, Cons definition,
FactorInterpreter interp) throws PersistenceException
{
super(word,interp.workspace);
fromList(definition,interp);
if(interp.workspace != null)
interp.workspace.put(this);
} //}}}
//{{{ FactorCompoundDefinition constructor
/**
* A blank definition, about to be unpickled.
*/
public FactorCompoundDefinition(Workspace workspace, long id)
{
super(workspace,id);
} //}}}
//{{{ eval() method
public void eval(FactorInterpreter interp)
throws Exception
{
interp.call(word,endOfDocs);
} //}}}
//{{{ getClassName() method
private static String getClassName(String name)
{
return FactorJava.getSanitizedName(name)
+ "_" + (compileCount++);
} //}}}
//{{{ compile() method
/**
* Compile the given word, returning a new word definition.
*/
FactorWordDefinition compile(FactorInterpreter interp,
RecursiveState recursiveCheck) throws Exception
{
// Each word has its own class loader
FactorClassLoader loader = new FactorClassLoader(
interp.workspace);
StackEffect effect = getStackEffect(interp,
new RecursiveState());
if(effect.inR != 0 || effect.outR != 0)
throw new FactorCompilerException("Compiled code cannot manipulate call stack frames");
String className = getClassName(word.name);
ClassWriter cw = new ClassWriter(true);
cw.visit(ACC_PUBLIC, className,
"factor/compiler/CompiledDefinition",
null, null);
compileConstructor(cw,className);
FactorCompiler compiler = compileEval(interp,cw,loader,
className,effect,recursiveCheck);
// Generate auxiliary methods
compiler.generateAuxiliary(cw);
// Generate fields for storing literals and
// word references
compiler.generateFields(cw);
compileToList(interp,compiler,cw);
compileGetStackEffect(cw,effect);
// gets the bytecode of the class, and loads it
// dynamically
byte[] code = cw.toByteArray();
if(interp.dump)
{
FileOutputStream fos = new FileOutputStream(
className + ".class");
try
{
fos.write(code);
}
finally
{
fos.close();
}
}
String javaClassName = className.replace('/','.');
word.setCompiledInfo(compiler.loader,javaClassName);
Class compiledWordClass = loader.addClass(
javaClassName,code,0,code.length);
return CompiledDefinition.create(interp,word,compiledWordClass);
} //}}}
//{{{ compileConstructor() method
private void compileConstructor(ClassVisitor cw, String className)
{
// creates a MethodWriter for the constructor
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC,
"<init>",
"(Lfactor/FactorWord;)V",
null, null);
// pushes the 'this' variable
mw.visitVarInsn(ALOAD, 0);
// pushes the word parameter
mw.visitVarInsn(ALOAD, 1);
// invokes the super class constructor
mw.visitMethodInsn(INVOKESPECIAL,
"factor/compiler/CompiledDefinition", "<init>",
"(Lfactor/FactorWord;)V");
mw.visitInsn(RETURN);
mw.visitMaxs(0,0);
} //}}}
//{{{ compileToList() method
private void compileToList(FactorInterpreter interp,
FactorCompiler compiler, ClassVisitor cw)
{
// creates a MethodWriter for the toList() method
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC,
"toList",
"(Lfactor/FactorInterpreter;)Lfactor/Cons;",
null, null);
// push unparsed string representation of this word and parse it
compiler.generateParse(mw,toList(interp),1);
mw.visitTypeInsn(CHECKCAST,"factor/Cons");
mw.visitInsn(ARETURN);
mw.visitMaxs(0,0);
} //}}}
//{{{ compileGetStackEffect() method
private void compileGetStackEffect(ClassVisitor cw, StackEffect effect)
{
// creates a MethodWriter for the getStackEffect() method
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC,
"getStackEffect",
"(Lfactor/compiler/RecursiveState;"
+ "Lfactor/compiler/FactorCompiler;)V",
null, null);
mw.visitVarInsn(ALOAD,2);
mw.visitTypeInsn(NEW,"factor/compiler/StackEffect");
mw.visitInsn(DUP);
mw.visitLdcInsn(new Integer(effect.inD));
mw.visitLdcInsn(new Integer(effect.outD));
mw.visitLdcInsn(new Integer(effect.inR));
mw.visitLdcInsn(new Integer(effect.outR));
mw.visitMethodInsn(INVOKESPECIAL,"factor/compiler/StackEffect",
"<init>","(IIII)V");
mw.visitMethodInsn(INVOKEVIRTUAL,"factor/compiler/FactorCompiler",
"apply","(Lfactor/compiler/StackEffect;)V");
mw.visitInsn(RETURN);
mw.visitMaxs(0,0);
} //}}}
//{{{ compileEval() method
/**
* Write the definition of the eval() method in the compiled word.
* Local 0 -- this
* Local 1 -- interpreter
*/
protected FactorCompiler compileEval(FactorInterpreter interp,
ClassWriter cw, FactorClassLoader loader,
String className, StackEffect effect,
RecursiveState recursiveCheck)
throws Exception
{
cw.visitField(ACC_PRIVATE | ACC_STATIC, "initialized", "Z",
null, null);
// creates a MethodWriter for the 'eval' method
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC,
"eval", "(Lfactor/FactorInterpreter;)V",
null, null);
// eval() method calls core
mw.visitVarInsn(ALOAD,1);
compileDataStackToJVMStack(effect,mw);
mw.visitMethodInsn(INVOKESTATIC,className,"core",
effect.getCorePrototype());
compileJVMStackToDataStack(effect,mw);
mw.visitInsn(RETURN);
mw.visitMaxs(0,0);
// generate core
FactorCompiler compiler = new FactorCompiler(interp,word,
className,loader);
compiler.init(1,effect.inD,effect.inR,"core");
compiler.compileCore(endOfDocs,cw,effect,recursiveCheck);
return compiler;
} //}}}
//{{{ compileDataStackToJVMStack() method
private void compileDataStackToJVMStack(StackEffect effect,
CodeVisitor mw)
{
if(effect.inD != 0)
{
mw.visitVarInsn(ALOAD,1);
mw.visitFieldInsn(GETFIELD,
"factor/FactorInterpreter", "datastack",
"Lfactor/FactorArrayStack;");
// ensure the stack has enough elements
mw.visitInsn(DUP);
mw.visitIntInsn(BIPUSH,effect.inD);
mw.visitMethodInsn(INVOKEVIRTUAL,
"factor/FactorArrayStack", "ensurePop",
"(I)V");
// datastack.stack -> 2
mw.visitInsn(DUP);
mw.visitFieldInsn(GETFIELD,
"factor/FactorArrayStack", "stack",
"[Ljava/lang/Object;");
mw.visitVarInsn(ASTORE,2);
// datastack.top-args.length -> 3
mw.visitInsn(DUP);
mw.visitFieldInsn(GETFIELD,
"factor/FactorArrayStack", "top",
"I");
mw.visitIntInsn(BIPUSH,effect.inD);
mw.visitInsn(ISUB);
// datastack.top -= args.length
mw.visitInsn(DUP_X1);
mw.visitFieldInsn(PUTFIELD,
"factor/FactorArrayStack", "top",
"I");
mw.visitVarInsn(ISTORE,3);
for(int i = 0; i < effect.inD; i++)
{
mw.visitVarInsn(ALOAD,2);
mw.visitVarInsn(ILOAD,3);
mw.visitInsn(AALOAD);
if(i != effect.inD - 1)
mw.visitIincInsn(3,1);
}
}
} //}}}
//{{{ compileJVMStackToDataStack() method
private void compileJVMStackToDataStack(StackEffect effect,
CodeVisitor mw)
{
if(effect.outD == 1)
{
// ( datastack )
mw.visitVarInsn(ALOAD,1);
mw.visitFieldInsn(GETFIELD,
"factor/FactorInterpreter", "datastack",
"Lfactor/FactorArrayStack;");
mw.visitInsn(SWAP);
mw.visitMethodInsn(INVOKEVIRTUAL,
"factor/FactorArrayStack", "push",
"(Ljava/lang/Object;)V");
}
} //}}}
//{{{ fromList() method
public void fromList(Cons definition, FactorInterpreter interp)
{
this.definition = definition;
if(definition == null)
endOfDocs = null;
else
{
endOfDocs = definition;
while(endOfDocs != null
&& endOfDocs.car instanceof FactorDocComment)
endOfDocs = endOfDocs.next();
}
} //}}}
//{{{ toList() method
public Cons toList(FactorInterpreter interp)
{
return definition;
} //}}}
}

View File

@ -0,0 +1,59 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import java.io.IOException;
public class FactorDocComment implements FactorExternalizable
{
private String msg;
private boolean stack;
public FactorDocComment(String msg, boolean stack)
{
if(stack)
msg = msg.trim();
this.msg = msg;
this.stack = stack;
}
public String toString()
{
if(stack)
return "( " + msg + " )\n";
else
return "#!" + msg + "\n";
}
public boolean isStackComment()
{
return stack;
}
}

View File

@ -0,0 +1,38 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
public class FactorDomainException extends FactorRuntimeException
{
public FactorDomainException(Object arg, Class clas)
{
super("Cannot turn " + arg + " into " + clas);
}
}

View File

@ -0,0 +1,59 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import java.lang.reflect.InvocationTargetException;
public class FactorException extends Exception
{
public FactorException()
{
super();
}
public FactorException(String str)
{
super(str);
}
public FactorException(String str, Throwable t)
{
super(str,unwrap(t));
}
private static Throwable unwrap(Throwable t)
{
if(t instanceof InvocationTargetException)
return ((InvocationTargetException)t)
.getTargetException();
else
return t;
}
}

View File

@ -0,0 +1,39 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
/**
* Called if the object has a string representation that is understood
* by the parser.
*/
public interface FactorExternalizable
{
String toString();
}

View File

@ -0,0 +1,757 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import factor.db.*;
import factor.parser.*;
import factor.primitives.*;
import java.io.*;
public class FactorInterpreter implements FactorObject, Runnable
{
public static final String VERSION = "0.60.5";
// we need to call two words (boot and break) from the kernel
// vocabulary
private static final String KERNEL_VOCAB = "kernel";
// command line arguments are stored here.
public Cons args;
// boot.factor sets these.
public boolean interactive = true;
public boolean errorFlag = false;
public Throwable error;
public boolean dump = false;
public boolean verboseCompile = false;
public boolean firstTime = false;
public boolean mini = false;
public Cons callframe;
public FactorArray callstack = new FactorArray();
public FactorArray datastack = new FactorArray();
public FactorArray namestack = new FactorArray();
/**
* Maps vocabulary names to vocabularies.
*/
public FactorNamespace vocabularies;
/**
* Vocabulary search path for interactive parser.
*/
public Cons use;
/**
* Vocabulary to define new words in.
*/
public String in;
/**
* Kernel vocabulary. Re-created on each startup, contains
* primitives and parsing words.
*/
public FactorNamespace builtins;
/**
* Most recently defined word.
*/
public FactorWord last;
/**
* Persistent store, maybe null.
*/
public Workspace workspace;
public FactorNamespace global;
private FactorNamespace interpNamespace;
private Cons compiledExceptions;
//{{{ main() method
public static void main(String[] args) throws Exception
{
FactorInterpreter interp = new FactorInterpreter();
interp.init(args,null);
interp.run();
if(interp.workspace != null)
interp.workspace.close();
} //}}}
//{{{ init() method
public void init(FactorInterpreter interp) throws Exception
{
this.args = interp.args;
this.interactive = interp.interactive;
this.dump = interp.dump;
this.verboseCompile = interp.verboseCompile;
this.firstTime = firstTime;
this.callframe = interp.callframe;
this.callstack = (FactorArray)interp.callstack.clone();
this.datastack = (FactorArray)interp.datastack.clone();
this.namestack = (FactorArray)interp.namestack.clone();
this.vocabularies = interp.vocabularies;
this.use = interp.use;
this.in = interp.in;
this.builtins = interp.builtins;
this.last = interp.last;
this.workspace = interp.workspace;
this.global = interp.global;
} //}}}
//{{{ init() method
public void init(String[] args, Object root) throws Exception
{
for(int i = 0; i < args.length; i++)
{
String arg = args[i];
if(arg.equals("-no-db"))
{
workspace = null;
args[i] = null;
}
else if(arg.equals("-db"))
{
if(workspace != null)
workspace.close();
workspace = new Workspace(
new BTreeStore(new File("factor.db"),
(byte)64,false),false,this);
}
else if(arg.startsWith("-db:"))
{
if(workspace != null)
workspace.close();
workspace = parseDBSpec(arg.substring(4));
args[i] = null;
}
// this switch forces a first time init
else if(arg.equals("-no-first-time"))
{
firstTime = false;
args[i] = null;
}
else if(arg.equals("-first-time"))
{
firstTime = true;
args[i] = null;
}
// this switch forces minimal libraries to be loaded
else if(arg.equals("-no-mini"))
{
mini = false;
args[i] = null;
}
else if(arg.equals("-mini"))
{
mini = true;
args[i] = null;
}
}
this.args = Cons.fromArray(args);
vocabularies = new Table(workspace);
initBuiltinDictionary();
initNamespace(root);
topLevel();
runBootstrap();
} //}}}
//{{{ parseDBSpec() method
private Workspace parseDBSpec(String db) throws Exception
{
int index = db.indexOf(':');
String className = db.substring(0,index);
String arg = db.substring(index + 1);
boolean readOnly;
if(arg.startsWith("ro:"))
{
readOnly = true;
arg = arg.substring(3);
}
else
readOnly = false;
Class[] consArgClasses = new Class[] { String.class };
Object[] consArgs = new Object[] { arg };
return new Workspace((Store)Class.forName(className)
.getConstructor(consArgClasses)
.newInstance(consArgs),readOnly,this);
} //}}}
//{{{ initBuiltinDictionary() method
private void initBuiltinDictionary() throws Exception
{
builtins = new Table(null);
vocabularies.setVariable("builtins",builtins);
in = "builtins";
use = new Cons(in,null);
// parsing words
FactorWord lineComment = define("builtins","!");
lineComment.parsing = new LineComment(lineComment,false,null);
FactorWord stackComment = define("builtins","(");
stackComment.parsing = new StackComment(stackComment,null);
FactorWord str = define("builtins","\"");
str.parsing = new StringLiteral(str,true,null);
FactorWord t = define("builtins","t");
t.parsing = new T(t,null);
FactorWord f = define("builtins","f");
f.parsing = new F(f,null);
FactorWord bra = define("builtins","[");
bra.parsing = new Bra(bra,null);
FactorWord ket = define("builtins","]");
ket.parsing = new Ket(bra,ket,null);
FactorWord bar = define("builtins","|");
bar.parsing = new Bar(bar,null);
FactorWord def = define("builtins",":");
def.parsing = new Def(def,null);
def.getNamespace().setVariable("doc-comments",Boolean.TRUE);
FactorWord ine = define("builtins",";");
ine.parsing = new Ine(def,ine,null);
FactorWord shuffle = define("builtins","~<<");
shuffle.parsing = new Shuffle(shuffle,">>~",null);
FactorWord noParsing = define("builtins","POSTPONE:");
noParsing.parsing = new NoParsing(noParsing,null);
// #X
FactorWord dispatch = define("builtins","#");
dispatch.parsing = new Dispatch(dispatch,null);
FactorWord getPersistentObject = define("builtins","#O");
getPersistentObject.parsing = new GetPersistentObject(
getPersistentObject,null);
FactorWord ch = define("builtins","#\\");
ch.parsing = new CharLiteral(ch,null);
FactorWord raw = define("builtins","#\"");
raw.parsing = new StringLiteral(raw,false,null);
FactorWord complex = define("builtins","#{");
complex.parsing = new ComplexLiteral(complex,"}",null);
FactorWord docComment = define("builtins","#!");
docComment.parsing = new LineComment(docComment,true,null);
FactorWord unreadable = define("builtins","#<");
unreadable.parsing = new Unreadable(unreadable,null);
// #: is not handled with a special dispatch. instead, when
// a word starting with #: is passed to intern(), it creates
// a new symbol
FactorWord passthru = define("builtins","#:");
passthru.parsing = new PassThrough(passthru,null);
// vocabulary parsing words
FactorWord defer = define("builtins","DEFER:");
defer.parsing = new Defer(defer,null);
FactorWord in = define("builtins","IN:");
in.parsing = new In(in,null);
FactorWord use = define("builtins","USE:");
use.parsing = new Use(use,null);
FactorWord interpreterGet = define("builtins","interpreter");
interpreterGet.def = new InterpreterGet(interpreterGet,null);
interpreterGet.inline = true;
// reading numbers with another base
FactorWord bin = define("builtins","BIN:");
bin.parsing = new Base(defer,null,2);
FactorWord oct = define("builtins","OCT:");
oct.parsing = new Base(defer,null,8);
FactorWord hex = define("builtins","HEX:");
hex.parsing = new Base(defer,null,16);
// primitives used by 'expand' and 'map'
FactorWord restack = define("builtins","restack");
restack.def = new Restack(restack,null);
FactorWord unstack = define("builtins","unstack");
unstack.def = new Unstack(unstack,null);
// reflection primitives
FactorWord jinvoke = define("builtins","jinvoke");
jinvoke.def = new JInvoke(jinvoke,null,false);
jinvoke.inline = true;
FactorWord jinvokeStatic = define("builtins","jinvoke-static");
jinvokeStatic.def = new JInvoke(jinvokeStatic,null,true);
jinvokeStatic.inline = true;
FactorWord jnew = define("builtins","jnew");
jnew.def = new JNew(jnew,null);
jnew.inline = true;
FactorWord jvarGet = define("builtins","jvar-get");
jvarGet.def = new JVarGet(jvarGet,null);
jvarGet.inline = true;
FactorWord jvarGetStatic = define("builtins","jvar-static-get");
jvarGetStatic.def = new JVarGetStatic(jvarGetStatic,null);
jvarGetStatic.inline = true;
FactorWord jvarSet = define("builtins","jvar-set");
jvarSet.def = new JVarSet(jvarSet,null);
jvarSet.inline = true;
FactorWord jvarSetStatic = define("builtins","jvar-static-set");
jvarSetStatic.def = new JVarSetStatic(jvarSetStatic,null);
jvarSetStatic.inline = true;
FactorWord coerce = define("builtins","coerce");
coerce.def = new Coerce(coerce,null);
coerce.inline = true;
// definition
FactorWord define = define("builtins","define");
define.def = new Define(define,null);
// combinators
FactorWord execute = define("builtins","execute");
execute.def = new Execute(execute,null);
FactorWord call = define("builtins","call");
call.def = new Call(call,null);
call.inline = true;
FactorWord ifte = define("builtins","ifte");
ifte.def = new Ifte(ifte,null);
ifte.inline = true;
} //}}}
//{{{ initNamespace() method
private void initNamespace(Object root) throws Exception
{
if(workspace == null)
global = new FactorNamespace(null,root);
else
global = workspace.getRoot();
global.setVariable("interpreter",this);
global.setVariable("error-flag",
new FactorNamespace.VarBinding(
getClass().getField("errorFlag"),
this));
global.setVariable("verbose-compile",
new FactorNamespace.VarBinding(
getClass().getField("verboseCompile"),
this));
global.setVariable("global",
new FactorNamespace.VarBinding(
getClass().getField("global"),
this));
FactorNamespace newVocabs;
try
{
Object obj = global.getVariable("vocabularies");
if(!(obj instanceof FactorNamespace))
newVocabs = new Table(workspace);
else
newVocabs = (FactorNamespace)obj;
}
catch(Exception e)
{
System.err.println("Vocabularies table corrupt: " + e);
newVocabs = new Table(workspace);
}
vocabularies = newVocabs;
global.setVariable("vocabularies",
new FactorNamespace.VarBinding(
getClass().getField("vocabularies"),
this));
// Shouldn't have to do this twice!
initBuiltinDictionary();
String[] boundFields = {
"args",
"dump",
"interactive",
"builtins",
"in",
"last",
"use"
};
for(int i = 0; i < boundFields.length; i++)
{
String name = boundFields[i];
global.setVariable(name,
new FactorNamespace.VarBinding(
getClass().getField(name),
this));
}
} //}}}
//{{{ getNamespace() method
public FactorNamespace getNamespace()
throws Exception
{
if(interpNamespace == null)
interpNamespace = new FactorNamespace(this);
return interpNamespace;
} //}}}
//{{{ runBootstrap() method
private void runBootstrap() throws Exception
{
if(workspace == null || workspace.isFirstTime()
|| firstTime)
{
if(workspace != null)
workspace.setFirstTime(false);
String initFile = "/library/platform/jvm/boot.factor";
FactorReader parser = new FactorReader(
initFile,
new BufferedReader(
new InputStreamReader(
getClass().getResourceAsStream(
initFile))),
this);
call(parser.parse());
}
else
eval(searchVocabulary(KERNEL_VOCAB,"boot"));
//XXX messy
run();
if(errorFlag)
run();
run();
} //}}}
//{{{ run() method
/**
* Runs the top-level loop until there is no more code to execute.
*/
public void run()
{
for(;;)
{
try
{
if(callframe == null)
{
if(callstack.top == 0)
break;
callframe = (Cons)callstack.pop();
continue;
}
Object eval = callframe.car;
callframe = callframe.next();
eval(eval);
}
catch(Throwable e)
{
if(handleError(e))
return;
}
}
callframe = null;
} //}}}
//{{{ handleError() method
private boolean handleError(Throwable e)
{
if(errorFlag)
{
System.err.println("Exception inside"
+ " error handler:");
e.printStackTrace();
System.err.println("Original exception:");
error.printStackTrace();
System.err.println("Factor datastack:");
System.err.println(datastack.toList());
System.err.println("Factor callstack:");
System.err.println(callstack.toList());
topLevel();
return true;
}
else
{
errorFlag = true;
error = FactorJava.unwrapException(e);
datastack.push(error);
try
{
eval(searchVocabulary(KERNEL_VOCAB,"break"));
return false;
}
catch(Throwable e2)
{
System.err.println("Exception when calling break:");
e.printStackTrace();
System.err.println("Factor callstack:");
System.err.println(callstack);
topLevel();
return true;
}
}
} //}}}
//{{{ createCompiledCallframe() method
private Cons createCompiledCallframe(FactorWord word)
{
return new Cons(new FactorWord(null,"#<compiled>"),word);
} //}}}
//{{{ compiledException() method
/**
* Called by compiled words to give the user a meaningful call stack
* trace in the case of an exception.
*/
public void compiledException(FactorWord word, Throwable t)
{
compiledExceptions = new Cons(createCompiledCallframe(word),
this.compiledExceptions);
} //}}}
//{{{ call() method
/**
* Pushes the given list of code onto the callstack.
*/
public void call(Cons code)
{
// tail call optimization
if(callframe != null)
callstack.push(callframe);
callframe = code;
} //}}}
//{{{ eval() method
/**
* Evaluates a word.
*/
public void eval(Object obj) throws Exception
{
if(obj instanceof FactorWord)
{
try
{
FactorWordDefinition d = ((FactorWord)obj).def;
if(d == null)
{
throw new FactorUndefinedWordException(
((FactorWord)obj).name);
}
else
d.eval(this);
}
catch(Exception e)
{
callstack.push(callframe);
callframe = createCompiledCallframe(
(FactorWord)obj);
while(compiledExceptions != null)
{
callstack.push(compiledExceptions.car);
compiledExceptions = compiledExceptions
.next();
}
throw e;
}
}
else
datastack.push(obj);
} //}}}
//{{{ getVariable() method
/**
* Return the value of a variable, by searching the namestack
* in order.
*/
public Object getVariable(String name) throws Exception
{
for(int i = namestack.top - 1; i >= 0; i--)
{
FactorNamespace namespace = FactorJava.toNamespace(
namestack.stack[i]);
if(namespace.isDefined(name))
return namespace.getVariable(name);
}
return null;
} //}}}
//{{{ isUninterned() method
/**
* Words whose name begin with #: but are not #: themselves are not
* in any vocabulary.
*/
public static boolean isUninterned(String name)
{
return (name.startsWith("#:") && name.length() > 2);
} //}}}
//{{{ getVocabulary() method
public FactorNamespace getVocabulary(String name)
throws Exception
{
Object value = vocabularies.getVariable(name);
if(value instanceof FactorNamespace)
return (FactorNamespace)value;
else
return null;
} //}}}
//{{{ defineVocabulary() method
public void defineVocabulary(String name)
throws Exception
{
Object value = vocabularies.getVariable(name);
if(value == null)
{
value = new Table(workspace);
vocabularies.setVariable(name,value);
}
} //}}}
//{{{ searchVocabulary() method
/**
* Search in the given vocabulary for the given word.
*/
public FactorWord searchVocabulary(String vname, String name)
{
if(isUninterned(name))
return new FactorWord(null,name);
try
{
FactorNamespace v = getVocabulary(vname);
if(v != null)
{
Object word = v.getVariable(name);
if(word instanceof FactorWord)
return (FactorWord)word;
else
return null;
}
else
return null;
}
catch(Exception e)
{
// should not happen!
throw new RuntimeException(e);
}
} //}}}
//{{{ searchVocabulary() method
/**
* Search through the given vocabulary list for the given word.
*/
public FactorWord searchVocabulary(Cons vocabulary, String name)
{
if(isUninterned(name))
return new FactorWord(null,name);
while(vocabulary != null)
{
FactorWord word = searchVocabulary(
(String)vocabulary.car,name);
if(word != null)
return word;
vocabulary = vocabulary.next();
}
return null;
} //}}}
//{{{ define() method
/**
* Define a word in the given vocabulary if it doesn't exist already.
*/
public FactorWord define(String vocabulary, String name)
{
if(isUninterned(name))
return new FactorWord(null,name);
try
{
FactorNamespace v = getVocabulary(vocabulary);
if(v == null)
{
v = new Table(workspace);
vocabularies.setVariable(vocabulary,v);
}
Object value = v.getVariable(name);
if(value instanceof FactorWord)
return (FactorWord)value;
else
{
Workspace workspace;
if(v instanceof PersistentObject)
{
workspace = ((PersistentObject)v)
.getWorkspace();
}
else
workspace = null;
// save to same workspace as vocabulary,
// or no workspace if vocabulary is builtins
FactorWord word = new FactorWord(workspace,
vocabulary,name,null);
v.setVariable(name,word);
return word;
}
}
catch(Exception e)
{
// should not happen!
throw new RuntimeException(e);
}
} //}}}
//{{{ topLevel() method
/**
* Returns the parser to the top level context.
*/
public void topLevel()
{
callstack.top = 0;
datastack.top = 0;
namestack.top = 0;
namestack.push(global);
callframe = null;
} //}}}
}

643
factor/FactorJava.java Normal file
View File

@ -0,0 +1,643 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import factor.compiler.FactorCompiler;
import factor.math.*;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.*;
import java.math.BigInteger;
/**
* A few methods for converting between Java types at runtime.
* Note that the compiler incorporates calls to some of these methods in
* generated bytecode.
*/
public class FactorJava
{
public static final Class[] EMPTY_ARRAY = new Class[0];
//{{{ getSanitizedName() method
public static String getSanitizedName(String name)
{
StringBuffer sanitizedName = new StringBuffer();
for(int i = 0; i < name.length(); i++)
{
char ch = name.charAt(i);
if(!Character.isJavaIdentifierStart(ch))
sanitizedName.append("_");
else
sanitizedName.append(ch);
}
return sanitizedName.toString();
} //}}}
//{{{ classNameToClassList() method
public static Class[] classNameToClassList(Cons classes)
throws Exception
{
if(classes == null)
return EMPTY_ARRAY;
Class[] _classes = new Class[classes.length()];
int i = 0;
while(classes != null)
{
Object car = classes.car;
if(car instanceof Cons)
{
Cons classSpec = (Cons)car;
if(classSpec.cdr != null)
{
throw new FactorRuntimeException(
"Bad class spec: " + car);
}
Class clazz = (Class)classSpec.car(Class.class);
if(clazz.isPrimitive())
{
_classes[i] = getClass("["
+ javaClassToVMClass(clazz));
}
else
{
_classes[i] = getClass("[L"
+ clazz.getName() + ";");
}
}
else
_classes[i] = (Class)classes.car(Class.class);
i++;
classes = classes.next();
}
return _classes;
} //}}}
//{{{ toNumber() method
public static Number toNumber(Object arg)
throws FactorDomainException
{
if(arg instanceof Number)
return (Number)arg;
else if(arg instanceof Character)
return new Integer((int)((Character)arg).charValue());
else if(arg instanceof String)
{
Number num = NumberParser.parseNumber((String)arg,10);
if(num != null)
return num;
}
throw new FactorDomainException(arg,Number.class);
} //}}}
//{{{ toString() method
public static String toString(Object arg)
{
if(arg == null)
return null;
else if(arg instanceof String)
return (String)arg;
else
return String.valueOf(arg);
} //}}}
//{{{ toCharSequence() method
public static CharSequence toCharSequence(Object arg)
{
if(arg instanceof CharSequence)
return ((CharSequence)arg);
else
return toString(arg);
} //}}}
//{{{ toBoolean() method
public static boolean toBoolean(Object arg)
{
if(Boolean.FALSE.equals(arg) || arg == null)
return false;
else
return true;
} //}}}
//{{{ toByte() method
public static byte toByte(Object arg)
throws FactorDomainException
{
if(arg instanceof Number && !(arg instanceof Complex))
return ((Number)arg).byteValue();
else if(arg instanceof Character)
return (byte)(((Character)arg).charValue());
else if(arg instanceof String)
return Byte.parseByte((String)arg);
else
throw new FactorDomainException(arg,byte.class);
} //}}}
//{{{ toShort() method
public static short toShort(Object arg)
throws FactorDomainException
{
if(arg instanceof Number && !(arg instanceof Complex))
return ((Number)arg).shortValue();
else if(arg instanceof Character)
return (short)(((Character)arg).charValue());
else if(arg instanceof String)
return Short.parseShort((String)arg);
else
throw new FactorDomainException(arg,short.class);
} //}}}
//{{{ toChar() method
public static char toChar(Object arg)
throws FactorDomainException
{
if(arg instanceof Character)
return ((Character)arg).charValue();
else if(arg == null)
return '\0';
else if(arg instanceof String)
{
String s = (String)arg;
if(s.length() != 1)
throw new FactorDomainException(arg,char.class);
return s.charAt(0);
}
else if(arg instanceof Number)
{
return (char)((Number)arg).intValue();
}
else
throw new FactorDomainException(arg,char.class);
} //}}}
//{{{ toInt() method
public static int toInt(Object arg)
throws FactorDomainException
{
if(arg instanceof Number && !(arg instanceof Complex))
return ((Number)arg).intValue();
else if(arg instanceof Character)
return (int)(((Character)arg).charValue());
else if(arg instanceof String)
return Integer.parseInt((String)arg);
else
throw new FactorDomainException(arg,int.class);
} //}}}
//{{{ toLong() method
public static long toLong(Object arg)
throws FactorDomainException
{
if(arg instanceof Number && !(arg instanceof Complex))
return ((Number)arg).longValue();
else if(arg instanceof Character)
return (long)(((Character)arg).charValue());
else if(arg instanceof String)
return Long.parseLong((String)arg);
else
throw new FactorDomainException(arg,long.class);
} //}}}
//{{{ toFloat() method
public static float toFloat(Object arg)
throws FactorDomainException
{
if(arg instanceof Number && !(arg instanceof Complex))
return ((Number)arg).floatValue();
else if(arg instanceof Character)
return (float)(((Character)arg).charValue());
else if(arg instanceof String)
return Float.parseFloat((String)arg);
else
throw new FactorDomainException(arg,float.class);
} //}}}
//{{{ toDouble() method
public static double toDouble(Object arg)
throws FactorDomainException
{
if(arg instanceof Number && !(arg instanceof Complex))
return ((Number)arg).doubleValue();
else if(arg instanceof Character)
return (double)(((Character)arg).charValue());
else if(arg instanceof String)
return Double.parseDouble((String)arg);
else
throw new FactorDomainException(arg,double.class);
} //}}}
//{{{ toBigInteger() method
public static BigInteger toBigInteger(Object arg)
throws FactorDomainException
{
if(arg instanceof BigInteger)
return (BigInteger)arg;
else if(arg instanceof Character)
{
return BigInteger.valueOf(
(long)(((Character)arg).charValue()));
}
else if(arg instanceof Number)
return BigInteger.valueOf(((Number)arg).longValue());
else
throw new FactorDomainException(arg,BigInteger.class);
} //}}}
//{{{ toClass() method
public static Class toClass(Object arg)
throws Exception
{
if(arg instanceof Class)
return (Class)arg;
else
{
return getClass((String)
convertToJavaType(arg,String.class));
}
} //}}}
//{{{ toNamespace() method
public static FactorNamespace toNamespace(Object obj) throws Exception
{
if(obj instanceof FactorNamespace)
return (FactorNamespace)obj;
else if(obj instanceof FactorObject)
{
FactorNamespace ns = ((FactorObject)obj)
.getNamespace();
if(ns == null)
throw new FactorRuntimeException(
obj + " has a null"
+ " namespace");
return ns;
}
else
{
throw new FactorDomainException(obj,
FactorObject.class);
}
} //}}}
//{{{ toBooleanArray() method
public static boolean[] toBooleanArray(Object arg)
throws FactorDomainException
{
if(arg == null)
return new boolean[0];
else if(arg instanceof Cons)
arg = toArray(arg,Object[].class);
try
{
boolean[] returnValue = new boolean[
Array.getLength(arg)];
for(int i = 0; i < returnValue.length; i++)
{
returnValue[i] = toBoolean(
Array.get(arg,i));
}
return returnValue;
}
catch(IllegalArgumentException e)
{
throw new FactorDomainException(arg,boolean[].class);
}
} //}}}
//{{{ toByteArray() method
public static byte[] toByteArray(Object arg)
throws FactorDomainException
{
if(arg == null)
return new byte[0];
else if(arg instanceof Cons)
arg = toArray(arg,Object[].class);
if(arg.getClass().isArray())
{
try
{
byte[] returnValue = new byte[
Array.getLength(arg)];
for(int i = 0; i < returnValue.length; i++)
{
returnValue[i] = toByte(
Array.get(arg,i));
}
return returnValue;
}
catch(IllegalArgumentException e)
{
throw new FactorDomainException(arg,byte[].class);
}
}
else
{
try
{
return String.valueOf(arg).getBytes("UTF8");
}
catch(UnsupportedEncodingException e)
{
throw new RuntimeException(e);
}
}
} //}}}
//{{{ toArray() method
public static Object[] toArray(Object arg)
throws FactorDomainException
{
return toArray(arg,Object[].class);
} //}}}
//{{{ toArray() method
public static Object[] toArray(Object arg, Class clas)
throws FactorDomainException
{
if(arg == null)
{
return (Object[])Array.newInstance(
clas.getComponentType(),0);
}
else if(arg instanceof Cons)
{
Cons list = (Cons)arg;
Object[] array = (Object[])
Array.newInstance(
clas.getComponentType(),
list.length());
list.toArray(array);
return array;
}
else if(arg.getClass().isArray())
{
if(arg.getClass() == clas)
return (Object[])arg;
else
{
Object[] _arg = (Object[])arg;
Object[] array = (Object[])
Array.newInstance(
clas.getComponentType(),
_arg.length);
System.arraycopy(arg,0,array,0,
_arg.length);
return array;
}
}
else
throw new FactorDomainException(arg,Object[].class);
} //}}}
//{{{ convertToJavaType() method
public static Object convertToJavaType(Object arg, Class clas)
throws Exception
{
if(clas.isPrimitive())
clas = getBoxingType(clas);
if(clas == Object.class)
return arg;
else if(clas == Number.class)
{
return toNumber(arg);
}
else if(clas == String.class)
{
return toString(arg);
}
else if(clas == CharSequence.class)
{
return toCharSequence(arg);
}
else if(clas == Boolean.class)
{
if(arg instanceof Boolean)
return arg;
else
return Boolean.valueOf(toBoolean(arg));
}
else if(clas == Byte.class)
{
if(arg instanceof Byte)
return arg;
else
return new Byte(toByte(arg));
}
else if(clas == Character.class)
{
if(arg instanceof Character)
return arg;
return new Character(toChar(arg));
}
else if(clas == Integer.class)
{
if(arg instanceof Integer)
return arg;
else
return new Integer(toInt(arg));
}
else if(clas == Long.class)
{
if(arg instanceof Long)
return arg;
else
return new Long(toLong(arg));
}
else if(clas == Float.class)
{
if(arg instanceof Float)
return arg;
else
return new Float(toFloat(arg));
}
else if(clas == Double.class)
{
if(arg instanceof Double)
return arg;
else
return new Double(toDouble(arg));
}
else if(clas == BigInteger.class)
{
if(arg instanceof BigInteger)
return arg;
else
return toBigInteger(arg);
}
else if(clas == FactorNamespace.class)
{
if(arg instanceof FactorNamespace)
return arg;
else
return toNamespace(arg);
}
else if(clas == Class.class)
{
return toClass(arg);
}
else if(clas.isArray())
{
Class comp = clas.getComponentType();
if(!comp.isPrimitive())
return toArray(arg,clas);
else if(comp == boolean.class)
return toBooleanArray(arg);
else if(comp == byte.class)
return toByteArray(arg);
}
if(arg != null && !clas.isInstance(arg))
throw new FactorDomainException(arg,clas);
else
return arg;
} //}}}
//{{{ fromBoolean() method
public static Object fromBoolean(boolean b)
{
return (b ? Boolean.TRUE : null);
} //}}}
//{{{ convertFromJavaType() method
public static Object convertFromJavaType(Object arg)
{
if(Boolean.FALSE.equals(arg))
return null;
else
return arg;
} //}}}
//{{{ javaClassToVMClass() method
public static String javaClassToVMClass(Class clazz)
{
String name = clazz.getName();
if(clazz.isArray())
return clazz.getName().replace('.','/');
else if(name.equals("boolean"))
return "Z";
else if(name.equals("byte"))
return "B";
else if(name.equals("char"))
return "C";
else if(name.equals("double"))
return "D";
else if(name.equals("float"))
return "F";
else if(name.equals("int"))
return "I";
else if(name.equals("long"))
return "J";
else if(name.equals("short"))
return "S";
else if(name.equals("void"))
return "V";
else
return "L" + clazz.getName().replace('.','/') + ";";
} //}}}
//{{{ getBoxingType() method
public static Class getBoxingType(Class clazz)
{
if(clazz == Boolean.TYPE)
return Boolean.class;
else if(clazz == Byte.TYPE)
return Byte.class;
else if(clazz == Character.TYPE)
return Character.class;
else if(clazz == Double.TYPE)
return Double.class;
else if(clazz == Float.TYPE)
return Float.class;
else if(clazz == Integer.TYPE)
return Integer.class;
else if(clazz == Long.TYPE)
return Long.class;
else if(clazz == Short.TYPE)
return Short.class;
else
return null;
} //}}}
//{{{ javaSignatureToVMSignature() method
public static String javaSignatureToVMSignature(Class[] args,
Class returnType)
{
StringBuffer buf = new StringBuffer("(");
for(int i = 0; i < args.length; i++)
{
buf.append(javaClassToVMClass(args[i]));
}
buf.append(")");
buf.append(javaClassToVMClass(returnType));
return buf.toString();
} //}}}
//{{{ getClass() method
public static Class getClass(String name) throws ClassNotFoundException
{
if(name.equals("boolean"))
return Boolean.TYPE;
else if(name.equals("byte"))
return Byte.TYPE;
else if(name.equals("char"))
return Character.TYPE;
else if(name.equals("double"))
return Double.TYPE;
else if(name.equals("float"))
return Float.TYPE;
else if(name.equals("int"))
return Integer.TYPE;
else if(name.equals("long"))
return Long.TYPE;
else if(name.equals("short"))
return Short.TYPE;
else
return Class.forName(name);
} //}}}
//{{{ unwrapException() method
public static Throwable unwrapException(Throwable e)
{
if(e instanceof InvocationTargetException)
{
return unwrapException(
((InvocationTargetException)e)
.getTargetException());
}
else if(e.getCause() != null)
return unwrapException(e.getCause());
else
return e;
} //}}}
}

272
factor/FactorLib.java Normal file
View File

@ -0,0 +1,272 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import factor.math.*;
import java.io.*;
/**
* A few useful words.
*/
public class FactorLib
{
//{{{ branch3() method
public static Object branch3(float x, float y,
Object o1, Object o2, Object o3)
{
if(x > y)
return o1;
else if(x == y)
return o2;
else
return o3;
} //}}}
//{{{ cloneArray() method
public static Object[] cloneArray(Object[] array)
{
Object[] newArray = new Object[array.length];
System.arraycopy(array,0,newArray,0,array.length);
return newArray;
} //}}}
//{{{ deepCloneArray() method
public static Object[] deepCloneArray(Object[] array)
{
Object[] newArray = new Object[array.length];
for(int i = 0; i < array.length; i++)
{
Object o = array[i];
if(o instanceof PublicCloneable)
newArray[i] = ((PublicCloneable)o).clone();
else
newArray[i] = o;
}
return newArray;
} //}}}
//{{{ error() method
public static void error(Object obj) throws Throwable
{
if(obj instanceof Throwable)
throw (Throwable)obj;
else
throw new FactorRuntimeException(String.valueOf(obj));
} //}}}
//{{{ eq() method
public static boolean eq(Object o1, Object o2)
{
return o1 == o2;
} //}}}
//{{{ equal() method
public static boolean equal(Object o1, Object o2)
{
if(o1 == null)
return o2 == null;
else if((o1 instanceof Number && !(o1 instanceof FactorNumber))
&&
(o2 instanceof Number && !(o2 instanceof FactorNumber))
&&
o1.getClass() != o2.getClass())
{
// to compare different types of numbers, cast to a
// double first
return ((Number)o1).doubleValue()
== ((Number)o2).doubleValue();
}
else if(o1 instanceof Number
&& o2 instanceof String)
{
try
{
return Double.parseDouble((String)o2)
== ((Number)o1).doubleValue();
}
catch(NumberFormatException nf)
{
return false;
}
}
else if(o1 instanceof String
&& o2 instanceof Number)
{
try
{
return Double.parseDouble((String)o1)
== ((Number)o2).doubleValue();
}
catch(NumberFormatException nf)
{
return false;
}
}
else if(o1 instanceof String
&& o2 instanceof Character)
{
return o1.equals(o2.toString());
}
else if(o1 instanceof Character
&& o2 instanceof String)
{
return o1.toString().equals(o2);
}
else if(o1 instanceof Cons
&& o2 instanceof Cons)
{
Cons c1 = (Cons)o1;
Cons c2 = (Cons)o2;
return equal(c1.car,c2.car) && equal(c1.cdr,c2.cdr);
}
else
return o1.equals(o2);
} //}}}
//{{{ exec() method
public static int exec(String[] args) throws Exception
{
int exitCode = -1;
try
{
Process process = Runtime.getRuntime().exec(args);
process.getInputStream().close();
process.getOutputStream().close();
process.getErrorStream().close();
exitCode = process.waitFor();
}
catch(Exception e)
{
e.printStackTrace();
// this needs to be handled better
/* stack.push(MiscUtilities.throwableToString(e));
Console.print(stack,namespace); */
}
return exitCode;
} //}}}
//{{{ objectsEqual() method
/**
* Returns if two objects are equal. This correctly handles null
* pointers, as opposed to calling <code>o1.equals(o2)</code>.
*/
public static boolean objectsEqual(Object o1, Object o2)
{
if(o1 == null)
{
if(o2 == null)
return true;
else
return false;
}
else if(o2 == null)
return false;
else
return o1.equals(o2);
} //}}}
//{{{ copy() method
/**
* Copies the contents of an input stream to an output stream.
*/
public static void copy(InputStream in, OutputStream out)
throws IOException
{
byte[] buf = new byte[4096];
int count;
for(;;)
{
count = in.read(buf,0,buf.length);
if(count == -1 || count == 0)
break;
out.write(buf,0,count);
}
in.close();
out.close();
} //}}}
//{{{ readLine() method
/**
* Reads a line of text from the given input stream.
*/
public static String readLine(InputStream in) throws IOException
{
StringBuffer buf = new StringBuffer();
int b;
while((b = in.read()) != -1)
{
if(b == '\r')
{
if(in.markSupported()/* && in.available() >= 1 */)
{
in.mark(1);
b = in.read();
if(b != '\n')
in.reset();
}
break;
}
else if(b == '\n')
break;
buf.append((char)b);
}
return buf.toString();
} //}}}
//{{{ readCount() method
public static String readCount(int count, InputStream in)
throws IOException
{
byte[] bytes = new byte[count];
int offset = 0;
int read = 0;
while((read = in.read(bytes,offset,count - offset)) > 0)
offset += read;
return new String(bytes,"ASCII");
} //}}}
//{{{ readCount() method
public static String readCount(int count, Reader in)
throws IOException
{
char[] chars = new char[count];
int offset = 0;
int read = 0;
while((read = in.read(chars,offset,count - offset)) > 0)
offset += read;
return new String(chars);
} //}}}
}

445
factor/FactorNamespace.java Normal file
View File

@ -0,0 +1,445 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.TreeMap;
import java.util.Iterator;
import java.util.Map;
/**
* A namespace is a list of name/value bindings. A namespace can optionally
* have a bound object, in which case every public field of the
* object will be accessible through the namespace. Additionally, static fields
* from arbitrary classes can be imported into the namespace.
*/
public class FactorNamespace implements PublicCloneable, FactorObject
{
private static FactorWord NULL = new FactorWord(null,"#<represent-f>");
private static FactorWord CHECK_PARENT = new FactorWord(null,"#<check-parent>");
public Object obj;
protected Map words;
private Class constraint;
//{{{ createConstrainedNamespace() method
/**
* Used for dictionary.
*/
public static FactorNamespace createConstrainedNamespace(
Class constraint) throws Exception
{
FactorNamespace namespace = new FactorNamespace(null,null);
namespace.constraint = constraint;
return namespace;
} //}}}
//{{{ FactorNamespace constructor
public FactorNamespace()
{
this.words = new TreeMap();
} //}}}
//{{{ FactorNamespace constructor
public FactorNamespace(Object obj) throws Exception
{
this(null,obj);
} //}}}
//{{{ FactorNamespace constructor
/**
* Cloning constructor.
*/
public FactorNamespace(Map words, Object obj)
throws Exception
{
this.words = new TreeMap();
// used by clone()
if(words != null)
{
Iterator iter = words.entrySet().iterator();
while(iter.hasNext())
{
Map.Entry entry = (Map.Entry)iter.next();
Object key = entry.getKey();
Object value = entry.getValue();
if(!(value instanceof VarBinding))
this.words.put(key,value);
}
}
this.obj = obj;
} //}}}
//{{{ getNamespace() method
public FactorNamespace getNamespace()
{
return this;
} //}}}
//{{{ getThis() method
/**
* Returns the object bound to this namespace, or null.
*/
public Object getThis()
{
return obj;
} //}}}
//{{{ importVars() method
/**
* Defines a variable bound to a Java field.
*/
public synchronized void importVars(String clazz, Cons vars)
throws Exception
{
Class clas = Class.forName(clazz);
while(vars != null)
{
String field = (String)vars.car(String.class);
vars = vars.next();
String word = (String)vars.car(String.class);
setVariable(word,new VarBinding(
clas.getField(field),
null));
vars = vars.next();
}
} //}}}
//{{{ isDefined() method
public synchronized boolean isDefined(String name) throws Exception
{
Object o = words.get(name);
if(o instanceof VarBinding)
return true;
else if(o == NULL)
return true;
else if(o == CHECK_PARENT)
return false;
else if(o == null)
{
// lazily instantiate object field binding
if(obj == null)
return false;
else
{
lazyFieldInit(name);
return isDefined(name);
}
}
else
return true;
} //}}}
//{{{ getVariable() method
public synchronized Object getVariable(String name) throws Exception
{
Object o = words.get(name);
if(o instanceof VarBinding)
return ((VarBinding)o).get();
else if(o == NULL)
return null;
else if(o == CHECK_PARENT)
{
// we know this is not a field binding
return null;
}
else if(o == null)
{
// lazily instantiate object field binding
if(obj == null)
return null;
else
{
lazyFieldInit(name);
return getVariable(name);
}
}
else
return o;
} //}}}
//{{{ setVariable() method
public synchronized void setVariable(String name, Object value)
throws Exception
{
if(name == null)
throw new NullPointerException();
Object o = words.get(name);
if(o instanceof VarBinding && !(value instanceof VarBinding))
((VarBinding)o).set(value);
else if(o == null)
{
// lazily instantiate object field binding
if(obj == null)
{
if(value == null)
words.put(name,NULL);
else
words.put(name,value);
}
else
{
lazyFieldInit(name);
setVariable(name,value);
}
}
else if(value == null)
words.put(name,NULL);
else
{
if(constraint != null)
{
if(!constraint.isAssignableFrom(
value.getClass()))
{
throw new FactorRuntimeException(
"Can only store "
+ constraint
+ " in " + this);
}
}
words.put(name,value);
}
} //}}}
//{{{ lazyFieldInit() method
protected void lazyFieldInit(String name)
{
try
{
Field f = obj.getClass().getField(name);
if(!Modifier.isStatic(f.getModifiers()))
{
words.put(name,new VarBinding(f,obj));
return;
}
}
catch(Exception e)
{
}
// not a field, don't check again
words.put(name,CHECK_PARENT);
} //}}}
//{{{ initAllFields() method
private void initAllFields()
{
if(obj != null)
{
try
{
Field[] fields = obj.getClass().getFields();
for(int i = 0; i < fields.length; i++)
{
Field f = fields[i];
if(Modifier.isStatic(f.getModifiers()))
continue;
words.put(f.getName(),
new VarBinding(f,obj));
}
}
catch(Exception e)
{
}
}
} //}}}
//{{{ toVarList() method
/**
* Returns a list of variable names.
*/
public synchronized Cons toVarList()
{
initAllFields();
Cons first = null;
Cons last = null;
Iterator iter = words.keySet().iterator();
while(iter.hasNext())
{
String key = (String)iter.next();
if(words.get(key) != CHECK_PARENT)
{
Cons cons = new Cons(key,null);
if(first == null)
first = last = cons;
else
{
last.cdr = cons;
last = cons;
}
}
}
return first;
} //}}}
//{{{ toValueList() method
/**
* Returns a list of variable values.
*/
public synchronized Cons toValueList() throws Exception
{
initAllFields();
Cons first = null;
Cons last = null;
Cons vars = toVarList();
while(vars != null)
{
String key = (String)vars.car;
Cons cons = new Cons(getVariable(key),null);
if(first == null)
first = last = cons;
else
{
last.cdr = cons;
last = cons;
}
vars = vars.next();
}
return first;
} //}}}
//{{{ toVarValueList() method
/**
* Returns a list of pairs of variable names, and their values.
*/
public synchronized Cons toVarValueList() throws Exception
{
initAllFields();
Cons first = null;
Cons last = null;
Cons vars = toVarList();
while(vars != null)
{
String key = (String)vars.car;
Object value = getVariable(key);
Cons cons = new Cons(new Cons(key,value),null);
if(first == null)
first = last = cons;
else
{
last.cdr = cons;
last = cons;
}
vars = vars.next();
}
return first;
} //}}}
//{{{ VarBinding class
/**
* This is messy.
*/
public static class VarBinding
{
private Field field;
private Object instance;
public VarBinding(Field field, Object instance)
throws FactorRuntimeException
{
this.field = field;
this.instance = instance;
}
public Object get() throws Exception
{
return FactorJava.convertFromJavaType(
field.get(instance));
}
public void set(Object value) throws Exception
{
field.set(instance,FactorJava.convertToJavaType(
value,field.getType()));
}
} //}}}
//{{{ toString() method
public String toString()
{
initAllFields();
String str = getClass().getName() + ", " + words.size()
+ " items";
if(obj == null)
return str;
else
return str + ", bound: " + obj;
} //}}}
//{{{ clone() method
public FactorNamespace clone(Object rebind)
{
if(rebind.getClass() != obj.getClass())
throw new RuntimeException("Cannot rebind to different type");
try
{
return new FactorNamespace(words,rebind);
}
catch(Exception e)
{
throw new InternalError();
}
} //}}}
//{{{ clone() method
public Object clone()
{
if(obj != null)
throw new RuntimeException("Cannot clone namespace that's bound to an object");
try
{
return new FactorNamespace(new TreeMap(words),null);
}
catch(Exception e)
{
throw new InternalError();
}
} //}}}
}

35
factor/FactorObject.java Normal file
View File

@ -0,0 +1,35 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
public interface FactorObject
{
FactorNamespace getNamespace() throws Exception;
}

View File

@ -0,0 +1,65 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
public class FactorParseException extends FactorException
{
public FactorParseException(
String filename,
int lineno,
String str)
{
super(filename + ":" + lineno + ": " + str);
}
public FactorParseException(
String filename,
int lineno,
String line,
int position,
String str)
{
super(filename + ":" + lineno + ": " + str
+ "\n" + getDetailMessage(line,position));
}
private static String getDetailMessage(String line, int position)
{
if(line == null)
return "#<at end of file>";
StringBuffer buf = new StringBuffer(line);
buf.append('\n');
for(int i = 0; i < position; i++)
buf.append(' ');
buf.append('^');
return buf.toString();
}
}

View File

@ -0,0 +1,76 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import factor.db.*;
import java.io.IOException;
/**
* A parsing word definition.
*/
public abstract class FactorParsingDefinition extends SimplePersistentObject
{
public static final String ENCODING = "UTF8";
public FactorWord word;
//{{{ FactorParsingDefinition constructor
/**
* A new definition.
*/
public FactorParsingDefinition(FactorWord word, Workspace workspace)
throws Exception
{
this(workspace,workspace == null
? 0L : workspace.nextID());
this.word = word;
if(workspace != null && id != 0L)
workspace.put(this);
} //}}}
//{{{ FactorParsingDefinition constructor
/**
* A blank definition, about to be unpickled.
*/
public FactorParsingDefinition(Workspace workspace, long id)
throws Exception
{
super(workspace,id);
} //}}}
public abstract void eval(FactorInterpreter interp, FactorReader reader)
throws Exception;
//{{{ toString() method
public String toString()
{
return getClass().getName() + ": " + word;
} //}}}
}

View File

@ -0,0 +1,67 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import factor.compiler.*;
import factor.db.*;
import java.util.Set;
/**
* All primitive words extend this.
*/
public abstract class FactorPrimitiveDefinition extends FactorWordDefinition
{
//{{{ FactorPrimitiveDefinition constructor
/**
* A new definition.
*/
public FactorPrimitiveDefinition(FactorWord word, Workspace workspace)
throws Exception
{
super(word,workspace);
if(workspace != null)
workspace.put(this);
} //}}}
//{{{ FactorPrimitiveDefinition constructor
/**
* A blank definition, about to be unpickled.
*/
public FactorPrimitiveDefinition(Workspace workspace, long id)
throws Exception
{
super(workspace,id);
} //}}}
//{{{ fromList() method
public void fromList(Cons cons, FactorInterpreter interp)
{
} //}}}
}

617
factor/FactorReader.java Normal file
View File

@ -0,0 +1,617 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import factor.db.PersistentIgnore;
import factor.db.PersistentObject;
import java.io.*;
import java.util.*;
/**
* Use a FactorScanner to read words, and dispatch to parsing words in order
* to build a parse tree.
*/
public class FactorReader
{
public static final Cons DEFAULT_USE = new Cons("builtins",
new Cons("scratchpad",null));
public static final String DEFAULT_IN = "scratchpad";
public static final ReadTable DEFAULT_READTABLE;
//{{{ Class initializer
static
{
DEFAULT_READTABLE = new ReadTable();
DEFAULT_READTABLE.setCharacterType('\t',ReadTable.WHITESPACE);
DEFAULT_READTABLE.setCharacterType('\n',ReadTable.WHITESPACE);
// ^L
DEFAULT_READTABLE.setCharacterType((char)12,ReadTable.WHITESPACE);
DEFAULT_READTABLE.setCharacterType('\r',ReadTable.WHITESPACE);
DEFAULT_READTABLE.setCharacterType(' ',ReadTable.WHITESPACE);
DEFAULT_READTABLE.setCharacterType('!',ReadTable.CONSTITUENT);
DEFAULT_READTABLE.setCharacterType('"',ReadTable.DISPATCH);
DEFAULT_READTABLE.setCharacterType('#',ReadTable.DISPATCH);
DEFAULT_READTABLE.setCharacterRange('$','[',ReadTable.CONSTITUENT);
DEFAULT_READTABLE.setCharacterType('\\',ReadTable.SINGLE_ESCAPE);
DEFAULT_READTABLE.setCharacterRange(']','~',ReadTable.CONSTITUENT);
DEFAULT_READTABLE.setCharacterType('!',ReadTable.DISPATCH);
DEFAULT_READTABLE.setCharacterType('(',ReadTable.CONSTITUENT);
} //}}}
private FactorInterpreter interp;
private FactorScanner scanner;
private Cons states;
/**
* Top level of parse tree.
*/
private FactorWord toplevel = new FactorWord(null,"#<EOF>");
private boolean alwaysDocComments;
// if interactive, use interp.use & interp.in instead of below two
private boolean interactive;
private Cons use;
private String in;
private int base = 10;
//{{{ parseObject() method
/**
* Parse the given string. It must be a single literal object.
* The object is returned.
*/
public static Object parseObject(String input, FactorInterpreter interp)
throws Exception
{
try
{
FactorReader parser = new FactorReader(
"parseObject()",
new BufferedReader(new StringReader(input)),
true,false,interp);
Cons parsed = parser.parse();
if(parsed.cdr != null)
{
// not a single literal
throw new FactorParseException("parseObject()",
1,"Not a literal: " + input);
}
return parsed.car;
}
catch(IOException io)
{
// can't happen!
throw new FactorParseException("parseObject()",1,
io.toString());
}
} //}}}
//{{{ getUnreadableString() method
public static String getUnreadableString(String str)
{
return "#<" + str + ">";
} //}}}
//{{{ charsToEscapes() method
public static String charsToEscapes(String str)
{
StringBuffer buf = new StringBuffer();
for(int i = 0; i < str.length(); i++)
{
char ch = str.charAt(i);
switch(ch)
{
case 27: // ASCII ESC
buf.append("\\e");
break;
case '\n':
buf.append("\\n");
break;
case '\r':
buf.append("\\r");
break;
case '\t':
buf.append("\\t");
break;
case '"':
buf.append("\\\"");
break;
case '\\':
buf.append("\\\\");
break;
case '\0':
buf.append("\\0");
break;
default:
if(DEFAULT_READTABLE.getCharacterType(ch)
== ReadTable.INVALID)
{
buf.append("\\u");
String hex = Integer.toString(ch,16);
buf.append("0000".substring(hex.length()));
buf.append(hex);
}
else
buf.append(ch);
}
}
return buf.toString();
} //}}}
//{{{ getVocabularyDeclaration() method
/**
* Return a string of USE: declarations for the given object.
*/
public static String getVocabularyDeclaration(Object obj)
{
StringBuffer buf = new StringBuffer();
Set vocabs = getAllVocabularies(obj);
Iterator iter = vocabs.iterator();
while(iter.hasNext())
{
String name = (String)iter.next();
buf.append("USE: ").append(name).append('\n');
}
return buf.toString();
} //}}}
//{{{ getAllVocabularies() method
/**
* Return a set of all vocabularies referenced in the given quotation.
*/
public static Set getAllVocabularies(Object obj)
{
Set set = new TreeSet();
getAllVocabularies(obj,set);
return set;
} //}}}
//{{{ getAllVocabularies() method
/**
* Return a set of all vocabularies referenced in the given quotation.
*/
private static void getAllVocabularies(Object obj, Set set)
{
if(obj instanceof FactorWord)
{
String vocab = ((FactorWord)obj).vocabulary;
if(vocab != null)
set.add(vocab);
}
else if(obj instanceof Cons)
{
Cons quotation = (Cons)obj;
while(quotation != null)
{
getAllVocabularies(quotation.car,set);
if(quotation.car instanceof Cons)
getAllVocabularies((Cons)quotation.car,set);
if(quotation.cdr instanceof Cons)
quotation = quotation.next();
else
{
getAllVocabularies(quotation.cdr,set);
return;
}
}
}
} //}}}
//{{{ unparseDBObject() method
public static String unparseDBObject(Object obj)
{
if(obj instanceof PersistentIgnore)
return "f";
else if(obj instanceof PersistentObject)
{
PersistentObject pobj = (PersistentObject)obj;
if(pobj.getWorkspace() != null
&& pobj.getID() != 0L)
{
return "#O" + pobj.getID();
}
else
return unparseObject(obj);
}
else if(obj instanceof Cons)
return ((Cons)obj).unparseDB();
else
return unparseObject(obj);
} //}}}
//{{{ unparseObject() method
public static String unparseObject(Object obj)
{
// this is for string representations of lists and stacks
if(obj == null || obj.equals(Boolean.FALSE))
return "f";
else if(obj.equals(Boolean.TRUE))
return "t";
else if(obj instanceof FactorWord)
{
FactorWord word = (FactorWord)obj;
return (word.parsing != null ? "POSTPONE: " : "")
+ word.toString();
}
else if(obj instanceof String)
return '"' + charsToEscapes((String)obj) + '"';
else if(obj instanceof Number
|| obj instanceof FactorExternalizable)
return obj.toString();
else if(obj instanceof Character)
return "\"" + charsToEscapes(obj.toString()) + "\"";
else
return getUnreadableString(obj.toString());
} //}}}
//{{{ FactorReader constructor
public FactorReader(
String filename,
BufferedReader in,
FactorInterpreter interp)
{
this(filename,in,false,false,interp);
} //}}}
//{{{ FactorReader constructor
public FactorReader(
String filename,
BufferedReader in,
boolean alwaysDocComments,
boolean interactive,
FactorInterpreter interp)
{
this.interp = interp;
scanner = new FactorScanner(filename,in);
scanner.setReadTable(DEFAULT_READTABLE);
pushState(toplevel,null);
this.alwaysDocComments = alwaysDocComments;
this.interactive = interactive;
this.in = DEFAULT_IN;
this.use = DEFAULT_USE;
} //}}}
//{{{ getScanner() method
public FactorScanner getScanner()
{
return scanner;
} //}}}
//{{{ getIn() method
public String getIn()
{
if(interactive)
return interp.in;
else
return in;
} //}}}
//{{{ setIn() method
public void setIn(String in) throws Exception
{
if(interactive)
interp.in = in;
else
this.in = in;
if(interp.getVocabulary(in) == null)
interp.defineVocabulary(in);
} //}}}
//{{{ getUse() method
public Cons getUse()
{
if(interactive)
return interp.use;
else
return use;
} //}}}
//{{{ setUse() method
public void setUse(Cons use)
{
if(interactive)
interp.use = use;
else
this.use = use;
} //}}}
//{{{ addUse() method
public void addUse(String name) throws Exception
{
if(interp.getVocabulary(name) == null)
error("Undefined vocabulary: " + name);
Cons use = getUse();
if(!Cons.contains(use,name))
use = new Cons(name,use);
setUse(use);
} //}}}
//{{{ parse() method
/**
* Keeps parsing the input stream until EOF, and returns the
* parse tree.
*/
public Cons parse() throws Exception
{
for(;;)
{
if(next())
{
// eof.
return popState(toplevel,toplevel).first;
}
}
} //}}}
//{{{ intern() method
public FactorWord intern(String name, boolean define)
throws Exception
{
if(define)
return interp.define(getIn(),name);
else
{
FactorWord word = interp.searchVocabulary(
getUse(),name);
if(word == null)
error("Undefined: " + name);
return word;
}
} //}}}
//{{{ nextWord() method
/**
* Read a word from the scanner and intern it. Returns null on EOF.
*/
public FactorWord nextWord(boolean define) throws Exception
{
Object next = next(true,false);
if(next == FactorScanner.EOF)
{
scanner.error("Unexpected EOF");
// can't happen
return null;
}
else if(next instanceof Number)
{
scanner.error("Unexpected " + next);
// can't happen
return null;
}
else
return intern((String)next,define);
} //}}}
//{{{ next() method
public Object next(
boolean readNumbers,
boolean start)
throws IOException, FactorParseException
{
return scanner.next(readNumbers,start,base);
} //}}}
//{{{ nextNonEOF() method
public Object nextNonEOF(
boolean readNumbers,
boolean start)
throws IOException, FactorParseException
{
return scanner.nextNonEOF(readNumbers,start,base);
} //}}}
//{{{ next() method
/**
* Read the next word and take some kind of action.
* Returns true if EOF, false otherwise.
*/
private boolean next() throws Exception
{
Object next = next(true,true);
if(next == FactorScanner.EOF)
return true;
else if(next instanceof String)
{
FactorWord word = intern((String)next,
!getCurrentState().warnUndefined);
if(word.parsing != null)
{
word.parsing.eval(interp,this);
return false;
}
append(word);
return false;
}
else // its a number.
{
append(next);
return false;
}
} //}}}
//{{{ pushExclusiveState() method
/**
* An exclusive state can only happen at the top level.
* For example, : ... ; definitions cannot be nested so they
* are exclusive.
*
* @param args Parsing words can use this to store arbitrary info
*/
public void pushExclusiveState(FactorWord start, Object args)
throws FactorParseException
{
if(getCurrentState().start != toplevel)
scanner.error(start + " cannot be nested");
pushState(start,args);
} //}}}
//{{{ pushState() method
/**
* Push a parser state, for example reading of a list.
*/
public void pushState(FactorWord start, Object args)
{
states = new Cons(new ParseState(start,args),states);
} //}}}
//{{{ popState() method
/**
* Pop a parser state, throw exception if it doesn't match the
* parameter.
*/
public ParseState popState(FactorWord start, FactorWord end)
throws FactorParseException
{
ParseState state = getCurrentState();
if(state.start != start)
{
scanner.error(end + " does not close " + state.start);
}
states = states.next();
return state;
} //}}}
//{{{ getCurrentState() method
public ParseState getCurrentState()
{
return (ParseState)states.car;
} //}}}
//{{{ append() method
/**
* Append the given object to the current parse tree node.
*/
public void append(Object obj) throws FactorParseException
{
getCurrentState().append(obj);
} //}}}
//{{{ bar() method
/**
* Sets the current parser state's cdr to the given object.
*/
public void bar() throws FactorParseException
{
getCurrentState().bar();
} //}}}
//{{{ error() method
public void error(String msg) throws FactorParseException
{
scanner.error(msg);
} //}}}
//{{{ ParseState class
public class ParseState
{
public FactorWord start;
public Object arg;
public Cons first;
public Cons last;
public boolean warnUndefined;
private boolean comma;
private boolean docComment;
ParseState(FactorWord start, Object arg)
{
warnUndefined = true;
this.start = start;
this.arg = arg;
try
{
this.docComment
= (start.getNamespace()
.getVariable("doc-comments")
!= null);
}
catch(Exception e)
{
throw new RuntimeException(e);
}
}
void append(Object obj) throws FactorParseException
{
boolean docComment = (this.docComment
|| alwaysDocComments);
// In a doc comment context, first object is always
// a word, then followed by doc comments, then followed
// by code.
if(docComment && !(obj instanceof FactorDocComment)
&& first != null)
{
this.docComment = false;
}
else if(!docComment && obj instanceof FactorDocComment)
{
//scanner.error("Documentation comment not allowed here");
return;
}
if(comma)
{
if(last.cdr != null)
scanner.error("Only one token allowed after ,");
last.cdr = obj;
}
else
{
Cons next = new Cons(obj,null);
if(first == null)
first = next;
else
last.cdr = next;
last = next;
}
}
void bar() throws FactorParseException
{
if(last.cdr != null)
{
// We already read [ a | b
// no more can be appended to this state.
scanner.error("Only one token allowed after |");
}
comma = true;
}
} //}}}
}

View File

@ -0,0 +1,43 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
public class FactorRuntimeException extends FactorException
{
public FactorRuntimeException(String str)
{
super(str);
}
public FactorRuntimeException(String str, Throwable t)
{
super(str,t);
}
}

324
factor/FactorScanner.java Normal file
View File

@ -0,0 +1,324 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import factor.math.NumberParser;
import java.io.*;
/**
* Splits an input stream into words.
*/
public class FactorScanner
{
/**
* Special object returned on EOF.
*/
public static final Object EOF = new Object();
private String filename;
private BufferedReader in;
/**
* Line number being parsed, for error reporting.
*/
private int lineNo = 0;
/**
* The line currently being parsed.
*/
private String line;
/**
* Position within line being parsed.
*/
private int position = 0;
private ReadTable readtable;
/**
* The current word.
*/
private StringBuffer buf;
//{{{ FactorScanner constructor
public FactorScanner(String filename, BufferedReader in)
{
this.filename = filename;
this.in = in;
buf = new StringBuffer();
} //}}}
//{{{ getReadTable() method
public ReadTable getReadTable()
{
return readtable;
} //}}}
//{{{ setReadTable() method
public void setReadTable(ReadTable readtable)
{
this.readtable = readtable;
} //}}}
//{{{ nextLine() method
private void nextLine() throws IOException
{
lineNo++;
line = in.readLine();
position = 0;
if(line != null && line.length() == 0)
nextLine();
} //}}}
//{{{ next() method
/**
* @param readNumbers If true, will return either a Number or a
* String. Otherwise, only Strings are returned.
* @param start If true, dispatches will be handled by their parsing
* word, otherwise dispatches are ignored.
* @param base The number base -- not that if this is not equal to
* 10, floats cannot be read
*/
public Object next(
boolean readNumbers,
boolean start,
int base)
throws IOException, FactorParseException
{
if(line == null || position == line.length())
nextLine();
if(line == null)
return EOF;
for(;;)
{
if(position == line.length())
{
// EOL
if(buf.length() != 0)
return word(readNumbers,base);
nextLine();
if(line == null)
return EOF;
}
char ch = line.charAt(position++);
int type = readtable.getCharacterType(ch);
switch(type)
{
case ReadTable.INVALID:
error("Invalid character in input: " + ch);
break;
case ReadTable.WHITESPACE:
if(buf.length() != 0)
return word(readNumbers,base);
break;
case ReadTable.DISPATCH:
// note that s" is read as the word s", no
// dispatch on "
if(buf.length() == 0 && start)
{
buf.append(ch);
return word(readNumbers,base);
}
case ReadTable.CONSTITUENT:
buf.append(ch);
break;
case ReadTable.SINGLE_ESCAPE:
buf.append(escape());
break;
}
}
} //}}}
//{{{ nextNonEOF() method
public Object nextNonEOF(
boolean readNumbers,
boolean start,
int base)
throws IOException, FactorParseException
{
Object next = next(readNumbers,start,base);
if(next == EOF)
error("Unexpected EOF");
return next;
} //}}}
//{{{ readUntil() method
public String readUntil(char start, char end, boolean escapesAllowed)
throws IOException, FactorParseException
{
buf.setLength(0);
for(;;)
{
if(position == line.length())
error("Expected " + end + " before EOL");
if(line == null)
error("Expected " + end + " before EOF");
char ch = line.charAt(position++);
if(ch == end)
break;
int type = readtable.getCharacterType(ch);
if(escapesAllowed && type == ReadTable.SINGLE_ESCAPE)
buf.append(escape());
else
buf.append(ch);
}
String returnValue = buf.toString();
buf.setLength(0);
return returnValue;
} //}}}
//{{{ readUntilEOL() method
public String readUntilEOL() throws IOException
{
buf.setLength(0);
while(position < line.length())
buf.append(line.charAt(position++));
String returnValue = buf.toString();
buf.setLength(0);
return returnValue;
} //}}}
//{{{ readNonEOF() method
public char readNonEOF() throws FactorParseException, IOException
{
if(position == line.length())
error("Unexpected EOL");
if(line == null)
error("Unexpected EOF");
return line.charAt(position++);
} //}}}
//{{{ readNonEOFEscaped() method
public char readNonEOFEscaped() throws FactorParseException, IOException
{
char next = readNonEOF();
if(readtable.getCharacterType(next) == ReadTable.SINGLE_ESCAPE)
return escape();
else
return next;
} //}}}
//{{{ atEndOfWord() method
public boolean atEndOfWord() throws IOException
{
if(position == line.length())
return true;
if(line == null)
return true;
char next = line.charAt(position);
int type = readtable.getCharacterType(next);
return type == ReadTable.WHITESPACE;
} //}}}
//{{{ escape() method
private char escape() throws FactorParseException
{
char ch = line.charAt(position++);
switch(ch)
{
case 'e':
// ASCII ESC
return (char)27;
case 'n':
return '\n';
case 'r':
return '\r';
case 't':
return '\t';
case '\\':
return '\\';
case '"':
return '"';
case 's':
case ' ':
return ' ';
case '0':
return '\0';
case 'u':
if(line.length() - position < 4)
error("Unexpected EOL");
String hex = line.substring(position,position + 4);
position += 4;
try
{
return (char)Integer.parseInt(hex,16);
}
catch(NumberFormatException e)
{
error("Invalid \\u escape: " + hex);
}
return '\0';
default:
error("Unknown escape: " + ch);
// can't happen
return '\0';
}
} //}}}
//{{{ word() method
private Object word(boolean readNumbers, int base)
{
String name = buf.toString();
buf.setLength(0);
if(readNumbers)
{
Number n = NumberParser.parseNumber(name, base);
if(n != null)
return n;
}
return name;
} //}}}
//{{{ error() method
public void error(String msg) throws FactorParseException
{
throw new FactorParseException(
filename,lineNo,line,position - 1,msg);
} //}}}
}

View File

@ -0,0 +1,408 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import factor.compiler.*;
import factor.db.Workspace;
import java.util.*;
import org.objectweb.asm.*;
/**
* ~<< name ... -- >>~
*/
public class FactorShuffleDefinition extends FactorWordDefinition
{
public static final int FROM_R_MASK = (1<<15);
public static final int TO_R_MASK = (1<<16);
public static final int SPECIFIER = FROM_R_MASK | TO_R_MASK;
/**
* Elements to consume from stacks.
*/
private int consumeD;
private int consumeR;
/**
* Permutation for elements on stack.
*/
private int[] shuffleD;
private int shuffleDstart;
private int shuffleDlength;
private int[] shuffleR;
private int shuffleRstart;
private int shuffleRlength;
//{{{ FactorShuffleDefinition constructor
public FactorShuffleDefinition(Workspace workspace, long id)
{
super(workspace,id);
} //}}}
//{{{ FactorShuffleDefinition constructor
public FactorShuffleDefinition(FactorWord word, Cons definition,
FactorInterpreter interp) throws FactorException
{
super(word,interp.workspace);
fromList(definition,interp);
if(interp.workspace != null)
interp.workspace.put(this);
} //}}}
//{{{ FactorShuffleDefinition constructor
public FactorShuffleDefinition(FactorWord word,
int consumeD, int consumeR,
int[] shuffleD, int shuffleDlength,
int[] shuffleR, int shuffleRlength)
{
super(word);
this.consumeD = consumeD;
this.consumeR = consumeR;
this.shuffleD = shuffleD;
this.shuffleDlength = shuffleDlength;
this.shuffleR = shuffleR;
this.shuffleRlength = shuffleRlength;
init();
} //}}}
//{{{ init() method
private void init()
{
if(this.shuffleD != null && this.shuffleDlength == 0)
this.shuffleD = null;
if(this.shuffleR != null && this.shuffleRlength == 0)
this.shuffleR = null;
if(this.shuffleD != null)
{
for(int i = 0; i < shuffleDlength; i++)
{
if(shuffleD[i] == i)
shuffleDstart++;
else
break;
}
}
if(this.shuffleR != null)
{
for(int i = 0; i < shuffleRlength; i++)
{
if(shuffleR[i] == (i | FROM_R_MASK))
shuffleRstart++;
else
break;
}
}
} //}}}
//{{{ getStackEffect() method
public void getStackEffect(RecursiveState recursiveCheck,
FactorCompiler compiler) throws FactorStackException
{
compileCallTo(null,compiler,recursiveCheck);
} //}}}
//{{{ compile() method
/**
* Compile the given word, returning a new word definition.
*/
FactorWordDefinition compile(FactorInterpreter interp,
RecursiveState recursiveCheck) throws Exception
{
return this;
} //}}}
//{{{ compileCallTo() method
public void compileCallTo(CodeVisitor mw, FactorCompiler compiler,
RecursiveState recursiveCheck) throws FactorStackException
{
compiler.ensure(compiler.datastack,consumeD);
compiler.ensure(compiler.callstack,consumeR);
eval(compiler.interp,compiler.datastack,compiler.callstack);
} //}}}
//{{{ eval() method
public void eval(FactorInterpreter interp)
throws FactorStackException
{
eval(interp,interp.datastack,interp.callstack);
} //}}}
//{{{ eval() method
public void eval(FactorInterpreter interp,
FactorArray datastack,
FactorArray callstack)
throws FactorStackException
{
lazyInit(interp);
if(datastack.top < consumeD)
throw new FactorStackException(consumeD);
if(callstack.top < consumeR)
throw new FactorStackException(consumeR);
Object[] temporaryD;
if(shuffleD != null)
{
temporaryD = new Object[shuffleDlength];
shuffle(datastack,callstack,datastack,consumeD,consumeR,
shuffleD,temporaryD);
}
else
temporaryD = null;
Object[] temporaryR;
if(shuffleR != null)
{
temporaryR = new Object[shuffleRlength];
shuffle(datastack,callstack,callstack,consumeD,consumeR,
shuffleR,temporaryR);
}
else
temporaryR = null;
datastack.top -= consumeD;
if(temporaryD != null)
datastack.pushAll(temporaryD);
callstack.top -= consumeR;
if(temporaryR != null)
callstack.pushAll(temporaryR);
} //}}}
//{{{ shuffle() method
private void shuffle(
FactorArray datastack,
FactorArray callstack,
FactorArray stack,
int consumeD,
int consumeR,
int[] shuffle,
Object[] temporary)
throws FactorStackException
{
for(int i = 0; i < temporary.length; i++)
{
Object[] array;
int top;
int index = shuffle[i];
int consume;
if((index & FROM_R_MASK) == FROM_R_MASK)
{
array = callstack.stack;
top = callstack.top;
index = (index & ~FROM_R_MASK);
consume = consumeR;
}
else
{
array = datastack.stack;
top = datastack.top;
consume = consumeD;
}
temporary[i] = array[top - consume + index];
}
} //}}}
//{{{ fromList() method
public void fromList(Cons definition, FactorInterpreter interp)
throws FactorRuntimeException
{
String f = "--";
// 0 in consume map is last consumed, n is first consumed.
HashMap consumeMap = new HashMap();
while(definition != null)
{
Object next = definition.car;
if(f.equals(next.toString()))
{
definition = definition.next();
break;
}
else if(next instanceof String
|| next instanceof Character)
{
String name = next.toString();
int counter;
if(name.startsWith("r:"))
{
counter = (FactorShuffleDefinition
.FROM_R_MASK
| consumeR++);
name = name.substring(2);
}
else
counter = consumeD++;
Object existing = consumeMap.put(name,
new Integer(counter));
if(existing != null)
throw new FactorRuntimeException(
word + ": appears twice in shuffle LHS: "
+ next);
}
else if(!(next instanceof FactorDocComment))
{
throw new FactorRuntimeException(
word + ": unexpected "
+ FactorReader.unparseObject(
next));
}
definition = definition.next();
}
int consume = consumeMap.size();
if(definition != null)
{
int[] shuffle = new int[definition.length()];
int i = 0;
while(definition != null)
{
if(definition.car instanceof String
|| definition.car instanceof Character)
{
String name = definition.car.toString();
boolean r = (name.startsWith("r:"));
if(r)
name = name.substring(2);
Integer _index = (Integer)
consumeMap.get(name);
if(_index == null)
{
throw new FactorRuntimeException(word +
": does not appear in shuffle LHS: "
+ definition.car);
}
int index = _index.intValue();
if(r)
{
shuffleRlength++;
shuffle[i++] = (index
| FactorShuffleDefinition
.TO_R_MASK);
}
else
{
shuffleDlength++;
shuffle[i++] = index;
}
}
else
{
throw new FactorRuntimeException(
word + ": unexpected "
+ FactorReader.unparseObject(
definition.car));
}
definition = definition.next();
}
shuffleD = new int[shuffleDlength];
shuffleR = new int[shuffleRlength];
int j = 0, k = 0;
for(i = 0; i < shuffle.length; i++)
{
int index = shuffle[i];
if((index & FactorShuffleDefinition.TO_R_MASK)
== FactorShuffleDefinition.TO_R_MASK)
{
index = (index
& ~FactorShuffleDefinition.TO_R_MASK);
shuffleR[j++] = index;
}
else
shuffleD[k++] = index;
}
}
init();
} //}}}
//{{{ toList() method
public Cons toList(FactorInterpreter interp)
{
Cons list = null;
for(int i = 0; i < consumeD; i++)
list = new Cons(String.valueOf((char)('A' + i)),list);
for(int i = 0; i < consumeR; i++)
list = new Cons("r:" + (char)('A' + consumeD + i),list);
list = new Cons("--",list);
if(shuffleD != null)
{
for(int i = 0; i < shuffleDlength; i++)
{
int index = shuffleD[i];
if((index & FROM_R_MASK) == FROM_R_MASK)
{
index &= ~FROM_R_MASK;
index += consumeD;
}
list = new Cons(String.valueOf(
(char)('A' + index)),
list);
}
}
if(shuffleR != null)
{
for(int i = 0; i < shuffleRlength; i++)
{
int index = shuffleR[i];
if((index & FROM_R_MASK) == FROM_R_MASK)
{
index &= ~FROM_R_MASK;
index += consumeD;
}
list = new Cons(
"r:" + (char)('A' + index),
list);
}
}
return Cons.reverse(list);
} //}}}
}

View File

@ -0,0 +1,38 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
public class FactorStackException extends FactorRuntimeException
{
public FactorStackException(int expected)
{
super("Expected " + expected);
}
}

View File

@ -0,0 +1,38 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
public class FactorUndefinedWordException extends FactorRuntimeException
{
public FactorUndefinedWordException(String word)
{
super(word + " not defined");
}
}

233
factor/FactorWord.java Normal file
View File

@ -0,0 +1,233 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import factor.compiler.*;
import factor.db.*;
import java.util.*;
/**
* An internalized symbol.
*/
public class FactorWord extends SimplePersistentObject
implements FactorExternalizable
{
private static int gensymCount = 0;
public String vocabulary;
public String name;
/**
* Interpreted/compiled word definition.
*/
public FactorWordDefinition def;
/**
* Parsing word definition.
*/
public FactorParsingDefinition parsing;
/**
* Is this word referenced from a compiled word?
*/
public boolean compileRef;
/**
* Should this word be inlined when compiling?
*/
public boolean inline;
/**
* Raise an error if an attempt is made to compile this word?
*/
public boolean interpretOnly;
/**
* Only compiled words have this.
*/
public FactorClassLoader loader;
public String className;
//{{{ FactorWord constructor
/**
* Do not use this constructor unless you're writing a packages
* implementation or something. Use an FactorDictionary's
* intern() method instead.
*/
public FactorWord(Workspace workspace, long id) throws Exception
{
super(workspace,id);
} //}}}
//{{{ FactorWord constructor
/**
* Do not use this constructor unless you're writing a packages
* implementation or something. Use an FactorDictionary's
* intern() method instead.
*/
public FactorWord(Workspace workspace, String vocabulary, String name,
FactorWordDefinition def) throws Exception
{
this(workspace,workspace == null ? 0L : workspace.nextID());
this.vocabulary = vocabulary;
this.name = name;
this.def = def;
if(workspace != null && id != 0L)
workspace.put(this);
} //}}}
//{{{ FactorWord constructor
/**
* Do not use this constructor unless you're writing a packages
* implementation or something. Use an FactorDictionary's
* intern() method instead.
*/
public FactorWord(String vocabulary, String name)
{
this.vocabulary = vocabulary;
this.name = name;
} //}}}
//{{{ gensym() method
/**
* Returns an un-internalized word with a unique name.
*/
public synchronized static FactorWord gensym()
{
return new FactorWord(null,"#:GENSYM:" + (gensymCount++));
} //}}}
//{{{ define() method
public synchronized void define(FactorWordDefinition def)
throws PersistenceException
{
if(compileRef)
{
System.err.println("WARNING: " + this
+ " is used in one or more compiled words; old definition will remain until full recompile");
}
else if(this.def != null)
System.err.println("WARNING: redefining " + this);
this.def = def;
loader = null;
className = null;
if(workspace != null && id != 0L)
workspace.put(this);
} //}}}
//{{{ setCompiledInfo() method
synchronized void setCompiledInfo(FactorClassLoader loader,
String className) throws PersistenceException
{
this.loader = loader;
this.className = className;
if(workspace != null && id != 0L)
workspace.put(this);
} //}}}
//{{{ compile() method
public synchronized void compile(FactorInterpreter interp)
{
RecursiveState recursiveCheck = new RecursiveState();
recursiveCheck.add(this,new StackEffect(),null,null,null);
compile(interp,recursiveCheck);
recursiveCheck.remove(this);
} //}}}
//{{{ unpickle() method
/**
* Each persistent object can set its state to that in a byte array.
*/
public synchronized void unpickle(byte[] bytes, int offset,
FactorInterpreter interp) throws PersistenceException
{
super.unpickle(bytes,offset);
if(loader != null && className != null)
{
try
{
def = CompiledDefinition.create(interp,
this,loader.loadClass(className));
}
catch(Throwable e)
{
System.err.println("WARNING: cannot unpickle compiled definition");
e.printStackTrace();
}
}
} //}}}
//{{{ compile() method
public synchronized void compile(FactorInterpreter interp,
RecursiveState recursiveCheck)
{
if(def == null)
return;
if(interpretOnly)
{
if(interp.verboseCompile)
System.err.println(this + " is interpret-only");
return;
}
//if(def.compileFailed)
// return;
if(interp.verboseCompile)
System.err.println("Compiling " + this);
try
{
def = def.compile(interp,recursiveCheck);
}
catch(Throwable t)
{
def.compileFailed = true;
if(interp.verboseCompile)
{
System.err.println("WARNING: cannot compile " + this);
t.printStackTrace();
}
}
} //}}}
//{{{ toString() method
public String toString()
{
return name == null ? "#<unnamed>"
: FactorReader.charsToEscapes(name);
} //}}}
}

View File

@ -0,0 +1,472 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import factor.db.*;
import factor.compiler.*;
import java.io.*;
import java.util.*;
import org.objectweb.asm.*;
/**
* A word definition.
*
* The pickled form is an unparsed list. The car of the list is the word,
* the cdr is toList().
*/
public abstract class FactorWordDefinition
implements Constants, PersistentObject
{
public static final String ENCODING = "UTF8";
private String unparsed;
private boolean initialized;
private Workspace workspace;
private long id;
protected FactorWord word;
public boolean compileFailed;
//{{{ FactorWordDefinition constructor
/**
* A new definition.
*/
public FactorWordDefinition(FactorWord word, Workspace workspace)
{
this(workspace,workspace == null
? 0L : workspace.nextID());
this.word = word;
initialized = true;
} //}}}
//{{{ FactorWordDefinition constructor
/**
* A blank definition, about to be unpickled.
*/
public FactorWordDefinition(Workspace workspace, long id)
{
this.workspace = workspace;
this.id = id;
} //}}}
//{{{ FactorWordDefinition constructor
/**
* A definition that is not saved in the current workspace.
*/
public FactorWordDefinition(FactorWord word)
{
this.word = word;
initialized = true;
} //}}}
public abstract void eval(FactorInterpreter interp)
throws Exception;
//{{{ getWord() method
public FactorWord getWord(FactorInterpreter interp)
{
lazyInit(interp);
return word;
} //}}}
//{{{ fromList() method
public void fromList(Cons cons, FactorInterpreter interp)
throws FactorRuntimeException, PersistenceException
{
throw new PersistenceException("Cannot unpickle " + this);
} //}}}
//{{{ toList() method
public Cons toList(FactorInterpreter interp)
{
return new Cons(new FactorWord(null,getClass().getName()),null);
} //}}}
//{{{ getStackEffect() method
public final StackEffect getStackEffect(FactorInterpreter interp)
throws Exception
{
return getStackEffect(new RecursiveState(),interp);
} //}}}
//{{{ getStackEffect() method
public final StackEffect getStackEffect(RecursiveState recursiveCheck,
FactorInterpreter interp) throws Exception
{
FactorCompiler compiler = new FactorCompiler(interp);
lazyInit(interp);
recursiveCheck.add(word,new StackEffect(),null,null,null);
getStackEffect(recursiveCheck,compiler);
recursiveCheck.remove(word);
return compiler.getStackEffect();
} //}}}
//{{{ getStackEffect() method
public void getStackEffect(RecursiveState recursiveCheck,
FactorCompiler compiler) throws Exception
{
lazyInit(compiler.interp);
throw new FactorCompilerException("Cannot deduce stack effect of " + word);
} //}}}
//{{{ compile() method
FactorWordDefinition compile(FactorInterpreter interp,
RecursiveState recursiveCheck) throws Exception
{
return this;
} //}}}
//{{{ compileCallTo() method
/**
* Compile a call to this word. Returns maximum JVM stack use.
*/
public void compileCallTo(CodeVisitor mw, FactorCompiler compiler,
RecursiveState recursiveCheck) throws Exception
{
lazyInit(compiler.interp);
// normal word
String defclass;
String defmethod;
StackEffect effect;
FactorClassLoader loader;
RecursiveForm rec = recursiveCheck.get(word);
if(rec != null && rec.active)
{
if(compiler.interp.verboseCompile)
System.err.println("Recursive call to " + rec);
effect = StackEffect.decompose(rec.effect,rec.baseCase);
// are we recursing back on a form inside the current
// method?
RecursiveForm last = recursiveCheck.last();
if(recursiveCheck.allTails(rec)
&& last.className.equals(rec.className)
&& last.method.equals(rec.method))
{
if(compiler.interp.verboseCompile)
System.err.println(word + " is tail recursive");
// GOTO instad of INVOKEVIRTUAL; ie a loop!
compiler.normalizeStacks(mw);
mw.visitJumpInsn(GOTO,rec.label);
compiler.apply(effect);
return;
}
/* recursive method call! */
defclass = rec.className;
defmethod = rec.method;
loader = rec.loader;
if(!defclass.equals(compiler.className))
compiler.loader.addDependency(defclass,loader);
}
// not a recursive call but we're still not compiled
// its a bug in the compiler.
else if(this instanceof FactorCompoundDefinition)
{
throw new FactorCompilerException("You are an idiot!");
}
// inlining?
else if(word.inline)
{
compileImmediate(mw,compiler,recursiveCheck);
return;
}
/* ordinary method call! */
else
{
defclass = getClass().getName().replace('.','/');
defmethod = "core";
effect = getStackEffect(compiler.interp);
ClassLoader l = getClass().getClassLoader();
if(l instanceof FactorClassLoader)
{
loader = (FactorClassLoader)l;
compiler.loader.addDependency(getClass().getName(),loader);
}
else
loader = null;
}
mw.visitVarInsn(ALOAD,0);
compiler.generateArgs(mw,effect.inD,effect.inR,null);
String signature = effect.getCorePrototype();
mw.visitMethodInsn(INVOKESTATIC,defclass,defmethod,signature);
compiler.generateReturn(mw,effect.outD,effect.outR);
} //}}}
//{{{ compileNonRecursiveImmediate() method
/**
* Non-recursive immediate words are inlined.
*/
protected void compileNonRecursiveImmediate(CodeVisitor mw,
FactorCompiler compiler,
RecursiveState recursiveCheck,
StackEffect immediateEffect) throws Exception
{
Cons definition = toList(compiler.getInterpreter());
Cons endOfDocs = definition;
while(endOfDocs != null
&& endOfDocs.car instanceof FactorDocComment)
endOfDocs = endOfDocs.next();
compiler.compile(endOfDocs,mw,recursiveCheck);
} //}}}
//{{{ compileRecursiveImmediate() method
/**
* Recursive immediate words are compiled to an auxiliary method
* inside the compiled class definition.
*
* This must be done so that recursion has something to jump to.
*/
protected void compileRecursiveImmediate(CodeVisitor mw,
FactorCompiler compiler,
RecursiveState recursiveCheck,
StackEffect immediateEffect) throws Exception
{
lazyInit(compiler.interp);
Cons definition = toList(compiler.getInterpreter());
Cons endOfDocs = definition;
while(endOfDocs != null
&& endOfDocs.car instanceof FactorDocComment)
endOfDocs = endOfDocs.next();
String method = compiler.auxiliary(word,
endOfDocs,immediateEffect,recursiveCheck);
mw.visitVarInsn(ALOAD,0);
compiler.generateArgs(mw,immediateEffect.inD,
immediateEffect.inR,null);
String signature = immediateEffect.getCorePrototype();
mw.visitMethodInsn(INVOKESTATIC,compiler.className,
method,signature);
compiler.generateReturn(mw,
immediateEffect.outD,
immediateEffect.outR);
} //}}}
//{{{ compileImmediate() method
/**
* Compile a call to this word. Returns maximum JVM stack use.
*/
public void compileImmediate(CodeVisitor mw, FactorCompiler compiler,
RecursiveState recursiveCheck) throws Exception
{
Cons definition = toList(compiler.getInterpreter());
if(definition == null)
return;
Cons endOfDocs = definition;
while(endOfDocs != null
&& endOfDocs.car instanceof FactorDocComment)
endOfDocs = endOfDocs.next();
// determine stack effect of this instantiation, and if its
// recursive.
FactorArray savedDatastack = (FactorArray)
compiler.datastack.clone();
FactorArray savedCallstack = (FactorArray)
compiler.callstack.clone();
StackEffect savedEffect = compiler.getStackEffect();
RecursiveState _recursiveCheck = (RecursiveState)
recursiveCheck.clone();
_recursiveCheck.last().effect = compiler.getStackEffect();
getStackEffect(_recursiveCheck,compiler);
boolean recursive = (_recursiveCheck.last().baseCase != null);
StackEffect effect = compiler.getStackEffect();
StackEffect immediateEffect = StackEffect.decompose(
savedEffect,compiler.getStackEffect());
// restore previous state.
FactorArray afterDatastack = (FactorArray)
compiler.datastack.clone();
FactorArray afterCallstack = (FactorArray)
compiler.callstack.clone();
compiler.datastack = (FactorArray)savedDatastack.clone();
compiler.callstack = (FactorArray)savedCallstack.clone();
compiler.effect = savedEffect;
if(!recursive)
{
// not recursive; inline.
compileNonRecursiveImmediate(mw,compiler,recursiveCheck,
immediateEffect);
}
else
{
// recursive; must generate auxiliary method.
compileRecursiveImmediate(mw,compiler,recursiveCheck,
immediateEffect);
mergeStacks(savedDatastack,afterDatastack,compiler.datastack);
mergeStacks(savedCallstack,afterCallstack,compiler.callstack);
}
} //}}}
//{{{ mergeStacks() method
private void mergeStacks(FactorArray s1, FactorArray s2,
FactorArray into)
{
for(int i = 0; i < s2.top; i++)
{
if(s1.top <= i)
break;
if(FactorLib.objectsEqual(s1.stack[i],
s2.stack[i]))
{
into.stack[i] = s1.stack[i];
}
}
} //}}}
//{{{ getWorkspace() method
/**
* Each persistent object is stored in one workspace only.
*/
public Workspace getWorkspace()
{
return workspace;
} //}}}
//{{{ getID() method
/**
* Each persistent object has an associated ID.
*/
public long getID()
{
return id;
} //}}}
//{{{ lazyInit() method
public synchronized void lazyInit(FactorInterpreter interp)
{
if(initialized)
return;
initialized = true;
try
{
Cons pickle = (Cons)FactorReader.parseObject(
unparsed,interp);
word = (FactorWord)pickle.car;
//System.err.println(word + " unpickled");
fromList(pickle.next(),interp);
}
catch(Exception e)
{
// should not happen with byte array stream
throw new RuntimeException("Unexpected error",e);
}
} //}}}
//{{{ pickle() method
/**
* Each persistent object can turn itself into a byte array.
*/
public byte[] pickle()
throws PersistenceException
{
try
{
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
Cons pickle = new Cons(word,toList(
workspace.getInterpreter()));
bytes.write((FactorReader.getVocabularyDeclaration(pickle)
+ FactorReader.unparseDBObject(pickle))
.getBytes(ENCODING));
return bytes.toByteArray();
}
catch(Exception e)
{
// should not happen with byte array stream
throw new PersistenceException("Unexpected error",e);
}
} //}}}
//{{{ unpickle() method
/**
* Each persistent object can set its state to that in a byte array.
*/
public void unpickle(byte[] bytes, int offset)
throws PersistenceException
{
try
{
unparsed = new String(bytes,offset,
bytes.length - offset,ENCODING);
}
catch(Exception e)
{
// should not happen with byte array stream
throw new PersistenceException("Unexpected error",e);
}
} //}}}
//{{{ getReferences() method
public Cons getReferences()
{
return toList(workspace.getInterpreter());
} //}}}
//{{{ toString() method
public String toString()
{
return getClass().getName() + ": " + word;
} //}}}
}

View File

@ -0,0 +1,444 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
import factor.db.*;
import factor.compiler.*;
import java.io.*;
import java.util.*;
import org.objectweb.asm.*;
/**
* A word definition.
*
* The pickled form is an unparsed list. The car of the list is the word,
* the cdr is toList().
*/
public abstract class FactorWordDefinition
implements Constants, PersistentObject
{
public static final String ENCODING = "UTF8";
private Workspace workspace;
private long id;
public FactorWord word;
public boolean compileFailed;
//{{{ FactorWordDefinition constructor
/**
* A new definition.
*/
public FactorWordDefinition(FactorWord word, Workspace workspace)
{
this(workspace,workspace == null
? 0L : workspace.nextID());
this.word = word;
} //}}}
//{{{ FactorWordDefinition constructor
/**
* A blank definition, about to be unpickled.
*/
public FactorWordDefinition(Workspace workspace, long id)
{
this.workspace = workspace;
this.id = id;
} //}}}
//{{{ FactorWordDefinition constructor
/**
* A definition that is not saved in the current workspace.
*/
public FactorWordDefinition(FactorWord word)
{
this.word = word;
} //}}}
public abstract void eval(FactorInterpreter interp)
throws Exception;
//{{{ fromList() method
public void fromList(Cons cons, FactorInterpreter interp)
throws FactorRuntimeException, PersistenceException
{
throw new PersistenceException("Cannot unpickle " + this);
} //}}}
//{{{ toList() method
public Cons toList(FactorInterpreter interp)
{
return new Cons(new FactorWord(null,getClass().getName()),null);
} //}}}
//{{{ getStackEffect() method
public final StackEffect getStackEffect(FactorInterpreter interp)
throws Exception
{
return getStackEffect(interp,new RecursiveState());
} //}}}
//{{{ getStackEffect() method
public final StackEffect getStackEffect(FactorInterpreter interp,
RecursiveState recursiveCheck)
throws Exception
{
FactorCompiler compiler = new FactorCompiler(interp);
recursiveCheck.add(word,new StackEffect(),null,null,null);
compileCallTo(null,compiler,recursiveCheck);
recursiveCheck.remove(word);
return compiler.getStackEffect();
} //}}}
/*
//{{{ getStackEffect() method
public void getStackEffect(RecursiveState recursiveCheck,
FactorCompiler compiler) throws Exception
{
compileCallTo(null,compiler,recursiveCheck);
} //}}}
*/
//{{{ compile() method
FactorWordDefinition compile(FactorInterpreter interp,
RecursiveState recursiveCheck) throws Exception
{
return this;
} //}}}
//{{{ getDefinition() method
protected Cons getDefinition(FactorInterpreter interp)
throws FactorCompilerException
{
Cons definition = toList(interp);
while(definition != null
&& definition.car instanceof FactorDocComment)
definition = definition.next();
return definition;
} //}}}
//{{{ compileCallTo() method
/**
* Compile a call to this word. Returns maximum JVM stack use.
*/
public void compileCallTo(CodeVisitor mw, FactorCompiler compiler,
RecursiveState recursiveCheck) throws Exception
{
// normal word
String defclass;
String defmethod;
StackEffect effect;
FactorClassLoader loader;
RecursiveForm rec = recursiveCheck.get(word);
if(rec != null && rec.active)
{
if(compiler.interp.verboseCompile)
System.err.println("Recursive call to " + rec);
effect = StackEffect.decompose(rec.effect,rec.baseCase);
// are we recursing back on a form inside the current
// method?
RecursiveForm last = recursiveCheck.last();
if(mw != null
&& recursiveCheck.allTails(rec)
&& last.className.equals(rec.className)
&& last.method.equals(rec.method))
{
if(compiler.interp.verboseCompile)
System.err.println(word + " is tail recursive");
// GOTO instad of INVOKEVIRTUAL; ie a loop!
compiler.normalizeStacks(mw);
mw.visitJumpInsn(GOTO,rec.label);
compiler.apply(effect);
return;
}
/* recursive method call! */
defclass = rec.className;
defmethod = rec.method;
loader = rec.loader;
if(mw != null && !defclass.equals(compiler.className))
compiler.loader.addDependency(defclass,loader);
}
else if(mw == null)
{
Cons definition = getDefinition(compiler.interp);
compiler.getStackEffect(definition,recursiveCheck);
return;
}
// not a recursive call but we're still not compiled
// its a bug in the compiler.
else if(this instanceof FactorCompoundDefinition)
{
throw new FactorCompilerException("You are an idiot!");
}
/* ordinary method call! */
else
{
defclass = getClass().getName().replace('.','/');
defmethod = "core";
effect = getStackEffect(compiler.interp,
new RecursiveState());
ClassLoader l = getClass().getClassLoader();
if(l instanceof FactorClassLoader)
{
loader = (FactorClassLoader)l;
compiler.loader.addDependency(
getClass().getName(),loader);
}
else
loader = null;
}
if(mw == null)
compiler.apply(effect);
else
{
mw.visitVarInsn(ALOAD,0);
compiler.generateArgs(mw,effect.inD,effect.inR,null);
String signature = effect.getCorePrototype();
mw.visitMethodInsn(INVOKESTATIC,defclass,defmethod,signature);
compiler.generateReturn(mw,effect.outD,effect.outR);
}
} //}}}
//{{{ compileNonRecursiveImmediate() method
/**
* Non-recursive immediate words are inlined.
*/
protected void compileNonRecursiveImmediate(CodeVisitor mw,
FactorCompiler compiler,
RecursiveState recursiveCheck,
StackEffect immediateEffect) throws Exception
{
Cons definition = toList(compiler.getInterpreter());
Cons endOfDocs = definition;
while(endOfDocs != null
&& endOfDocs.car instanceof FactorDocComment)
endOfDocs = endOfDocs.next();
compiler.compile(endOfDocs,mw,recursiveCheck);
} //}}}
//{{{ compileRecursiveImmediate() method
/**
* Recursive immediate words are compiled to an auxiliary method
* inside the compiled class definition.
*
* This must be done so that recursion has something to jump to.
*/
protected void compileRecursiveImmediate(CodeVisitor mw,
FactorCompiler compiler,
RecursiveState recursiveCheck,
StackEffect immediateEffect) throws Exception
{
Cons definition = toList(compiler.getInterpreter());
Cons endOfDocs = definition;
while(endOfDocs != null
&& endOfDocs.car instanceof FactorDocComment)
endOfDocs = endOfDocs.next();
String method = compiler.auxiliary(word,
endOfDocs,immediateEffect,recursiveCheck);
mw.visitVarInsn(ALOAD,0);
compiler.generateArgs(mw,immediateEffect.inD,
immediateEffect.inR,null);
String signature = immediateEffect.getCorePrototype();
mw.visitMethodInsn(INVOKESTATIC,compiler.className,
method,signature);
compiler.generateReturn(mw,
immediateEffect.outD,
immediateEffect.outR);
} //}}}
//{{{ compileImmediate() method
/**
* Compile a call to this word. Returns maximum JVM stack use.
*/
public void compileImmediate(CodeVisitor mw, FactorCompiler compiler,
RecursiveState recursiveCheck) throws Exception
{
Cons definition = getDefinition(compiler.interp);
if(mw == null)
{
compiler.compile(definition,null,recursiveCheck);
return;
}
// determine stack effect of this instantiation, and if its
// recursive.
FactorArrayStack savedDatastack = (FactorArrayStack)
compiler.datastack.clone();
FactorCallStack savedCallstack = (FactorCallStack)
compiler.callstack.clone();
StackEffect savedEffect = compiler.getStackEffect();
RecursiveState _recursiveCheck = (RecursiveState)
recursiveCheck.clone();
_recursiveCheck.last().effect = compiler.getStackEffect();
compileImmediate(null,compiler,_recursiveCheck);
boolean recursive = (_recursiveCheck.last().baseCase != null);
StackEffect effect = compiler.getStackEffect();
StackEffect immediateEffect = StackEffect.decompose(
savedEffect,compiler.getStackEffect());
// restore previous state.
FactorArrayStack afterDatastack = (FactorArrayStack)
compiler.datastack.clone();
FactorCallStack afterCallstack = (FactorCallStack)
compiler.callstack.clone();
compiler.datastack = (FactorArrayStack)savedDatastack.clone();
compiler.callstack = (FactorCallStack)savedCallstack.clone();
compiler.effect = savedEffect;
if(!recursive)
{
// not recursive; inline.
compileNonRecursiveImmediate(mw,compiler,recursiveCheck,
immediateEffect);
}
else
{
// recursive; must generate auxiliary method.
compileRecursiveImmediate(mw,compiler,recursiveCheck,
immediateEffect);
mergeStacks(savedDatastack,afterDatastack,compiler.datastack);
mergeStacks(savedCallstack,afterCallstack,compiler.callstack);
}
} //}}}
//{{{ mergeStacks() method
private void mergeStacks(FactorArrayStack s1, FactorArrayStack s2,
FactorArrayStack into)
{
for(int i = 0; i < s2.top; i++)
{
if(s1.top <= i)
break;
if(FactorLib.objectsEqual(s1.stack[i],
s2.stack[i]))
{
into.stack[i] = s1.stack[i];
}
}
} //}}}
//{{{ getWorkspace() method
/**
* Each persistent object is stored in one workspace only.
*/
public Workspace getWorkspace()
{
return workspace;
} //}}}
//{{{ getID() method
/**
* Each persistent object has an associated ID.
*/
public long getID()
{
return id;
} //}}}
//{{{ pickle() method
/**
* Each persistent object can turn itself into a byte array.
*/
public byte[] pickle(FactorInterpreter interp)
throws PersistenceException
{
try
{
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
Cons pickle = new Cons(word,toList(interp));
bytes.write((FactorReader.getVocabularyDeclaration(pickle)
+ FactorReader.unparseDBObject(pickle))
.getBytes(ENCODING));
return bytes.toByteArray();
}
catch(Exception e)
{
// should not happen with byte array stream
throw new PersistenceException("Unexpected error",e);
}
} //}}}
//{{{ unpickle() method
/**
* Each persistent object can set its state to that in a byte array.
*/
public void unpickle(byte[] bytes, int offset, FactorInterpreter interp)
throws PersistenceException
{
try
{
String unparsed = new String(bytes,offset,
bytes.length - offset,ENCODING);
Cons pickle = (Cons)FactorReader.parseObject(unparsed,
interp);
word = (FactorWord)pickle.car;
fromList(pickle.next(),interp);
}
catch(Exception e)
{
// should not happen with byte array stream
throw new PersistenceException("Unexpected error",e);
}
} //}}}
//{{{ toString() method
public String toString()
{
return getClass().getName() + ": " + word;
} //}}}
}

View File

@ -0,0 +1,35 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
public interface PublicCloneable extends Cloneable
{
Object clone();
}

85
factor/ReadTable.java Normal file
View File

@ -0,0 +1,85 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor;
/**
* Specifies how an input stream is to be split into words.
*/
public class ReadTable
{
/**
* Invalid character.
*/
public static final int INVALID = 0;
/**
* Word break character.
*/
public static final int WHITESPACE = 1;
/**
* Word character. Entire words are read at once.
*/
public static final int CONSTITUENT = 2;
/**
* A single character to dispatch on.
*/
public static final int DISPATCH = 3;
/**
* Escape the next character.
*/
public static final int SINGLE_ESCAPE = 4;
private int[] chars = new int[256];
//{{{ getCharacterType() method
public int getCharacterType(char ch)
{
if(ch >= 256)
return INVALID;
else
return chars[ch];
} //}}}
//{{{ setCharacterType() method
public void setCharacterType(char ch, int type)
{
chars[ch] = type;
} //}}}
//{{{ setCharacterRange() method
public void setCharacterRange(char ch1, char ch2, int type)
{
for(int i = ch1; i <= ch2; i++)
chars[i] = type;
} //}}}
}

Binary file not shown.

View File

@ -0,0 +1,105 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
import org.objectweb.asm.*;
class AuxiliaryQuotation
{
private String method;
private FactorArray datastack;
private FactorArray callstack;
private Cons code;
private StackEffect effect;
private FactorWord word;
private RecursiveState recursiveCheck;
//{{{ AuxiliaryQuotation constructor
AuxiliaryQuotation(String method,
FactorArray datastack,
FactorArray callstack,
Cons code,
StackEffect effect,
FactorWord word,
FactorCompiler compiler,
RecursiveState recursiveCheck)
throws Exception
{
this.method = method;
this.datastack = datastack;
this.callstack = callstack;
this.code = code;
this.effect = effect;
this.word = word;
this.recursiveCheck = new RecursiveState(recursiveCheck);
System.arraycopy(datastack.stack,datastack.top - effect.inD,
datastack.stack,0,effect.inD);
for(int i = 0; i < effect.inD; i++)
{
int index = datastack.top - effect.inD + i;
FlowObject flow = (FlowObject)datastack.stack[index];
datastack.stack[index] = flow.munge(
1,index,compiler,recursiveCheck);
}
System.arraycopy(callstack.stack,callstack.top - effect.inR,
callstack.stack,0,effect.inD);
for(int i = 0; i < effect.inR; i++)
{
int index = callstack.top - effect.inR + i;
FlowObject flow = (FlowObject)callstack.stack[index];
callstack.stack[index] = flow.munge(
1 + effect.inD,index,compiler,recursiveCheck);
}
} //}}}
//{{{ compile() method
void compile(FactorCompiler compiler, ClassWriter cw)
throws Exception
{
// generate core
compiler.init(1,effect.inD,effect.inR,method);
compiler.datastack = datastack;
compiler.callstack = callstack;
//compiler.produce(compiler.datastack,effect.inD);
// important: this.recursiveCheck due to
// lexically-scoped recursion issues
compiler.compileMethod(code,cw,method,effect,word,
new RecursiveState(this.recursiveCheck),false);
} //}}}
//{{{ toString() method
public String toString()
{
return method + effect;
} //}}}
}

View File

@ -0,0 +1,64 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
import java.lang.reflect.*;
import java.util.Map;
import org.objectweb.asm.*;
/**
* Compiled colon definition.
*/
public abstract class CompiledDefinition
extends FactorWordDefinition
implements factor.db.PersistentIgnore
{
//{{{ CompiledDefinition constructor
public CompiledDefinition(FactorWord word)
{
super(word);
} //}}}
//{{{ create() method
public static CompiledDefinition create(FactorInterpreter interp,
FactorWord word, Class compiledWordClass)
throws Exception
{
Constructor constructor = compiledWordClass
.getConstructor(
new Class[] {
FactorWord.class
});
return (CompiledDefinition)constructor.newInstance(
new Object[] { word });
} //}}}
}

View File

@ -0,0 +1,128 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
import java.lang.reflect.*;
import java.util.*;
import org.objectweb.asm.*;
public class CompiledList extends FlowObject
{
protected Cons quotation;
protected RecursiveState recursiveCheck;
//{{{ CompiledList constructor
CompiledList(Cons quotation, FactorCompiler compiler,
RecursiveState recursiveCheck)
{
super(compiler,recursiveCheck.lastCallable());
this.quotation = quotation;
// clone it
this.recursiveCheck = new RecursiveState(
recursiveCheck);
expectedType = Cons.class;
} //}}}
//{{{ pop() method
public void pop(CodeVisitor mw)
{
mw.visitFieldInsn(GETSTATIC,compiler.className,
compiler.literal(quotation),
"Ljava/lang/Object;");
} //}}}
//{{{ munge() method
/**
* Munging transforms the flow object such that it is now stored in
* a local variable and hence can be mutated by compiled code, however
* its original compileCallTo() semantics remain (for example, if it is
* a quotation).
*/
FlowObject munge(int base, int index,
FactorCompiler compiler,
RecursiveState recursiveCheck)
throws Exception
{
return new CompiledListResult(index + base,
quotation,compiler,
this.recursiveCheck);
} //}}}
//{{{ getLiteral() method
Object getLiteral()
{
return quotation;
} //}}}
//{{{ compileCallTo()
/**
* Write code for evaluating this. Returns maximum JVM stack
* usage.
*/
public void compileCallTo(CodeVisitor mw,
RecursiveState recursiveCheck)
throws Exception
{
RecursiveForm last = this.recursiveCheck.last();
FactorWord word = FactorWord.gensym();
try
{
recursiveCheck.add(word,new StackEffect(),
last.className,last.loader,last.method,last);
recursiveCheck.last().callable = false;
compiler.compile(quotation,mw,recursiveCheck);
}
finally
{
recursiveCheck.remove(word);
}
} //}}}
//{{{ equals() method
public boolean equals(Object o)
{
if(o instanceof CompiledList)
{
CompiledList c = (CompiledList)o;
return FactorLib.objectsEqual(c.quotation,quotation);
}
else if(o instanceof Null)
return quotation == null;
else
return false;
} //}}}
//{{{ clone() method
public Object clone()
{
return new CompiledList(quotation,compiler,recursiveCheck);
} //}}}
}

View File

@ -0,0 +1,81 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
import java.lang.reflect.*;
import java.util.*;
import org.objectweb.asm.*;
public class CompiledListResult extends CompiledList implements Constants
{
private int local;
//{{{ CompiledListResult constructor
CompiledListResult(int local, Cons quotation,
FactorCompiler compiler,
RecursiveState recursiveCheck)
{
super(quotation,compiler,recursiveCheck);
this.local = local;
} //}}}
//{{{ push() method
public void push(CodeVisitor mw)
throws Exception
{
mw.visitVarInsn(ASTORE,local);
} //}}}
//{{{ pop() method
public void pop(CodeVisitor mw)
{
mw.visitVarInsn(ALOAD,local);
} //}}}
//{{{ getLocal() method
public int getLocal()
{
return local;
} //}}}
//{{{ usingLocal() method
boolean usingLocal(int local)
{
return (this.local == local);
} //}}}
//{{{ clone() method
public Object clone()
{
return new CompiledListResult(local,quotation,compiler,
recursiveCheck);
} //}}}
}

View File

@ -0,0 +1,231 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
public class CompilerState
{
public FactorArray datastack;
public FactorArray callstack;
public StackEffect effect;
public Cons inDtypes;
public Cons inRtypes;
//{{{ CompilerState constructor
public CompilerState(FactorCompiler compiler)
{
datastack = (FactorArray)compiler.datastack.clone();
callstack = (FactorArray)compiler.callstack.clone();
effect = (StackEffect)compiler.getStackEffect();
inDtypes = cloneTypespec(compiler,compiler.inDtypes);
inRtypes = cloneTypespec(compiler,compiler.inRtypes);
} //}}}
//{{{ restore() method
public void restore(FactorCompiler compiler)
{
compiler.datastack = (FactorArray)datastack.clone();
compiler.callstack = (FactorArray)callstack.clone();
compiler.effect = (StackEffect)effect.clone();
compiler.inDtypes = cloneTypespec(compiler,inDtypes);
compiler.inRtypes = cloneTypespec(compiler,inRtypes);
} //}}}
//{{{ findResult() method
private static Result findResult(Result result,
FactorArray stack)
{
for(int i = 0; i < stack.top; i++)
{
if(stack.stack[i] instanceof Result)
{
Result r = (Result)stack.stack[i];
if(r.getLocal() == result.getLocal())
return r;
}
}
return null;
} //}}}
//{{{ cloneResult() method
private static Result cloneResult(Result result,
FactorArray datastack,
FactorArray callstack)
{
Result r = findResult(result,datastack);
if(r != null)
return r;
r = findResult(result,callstack);
if(r != null)
return r;
return (Result)result.clone();
} //}}}
//{{{ cloneTypespec() method
private static Cons cloneTypespec(FactorCompiler compiler, Cons spec)
{
Cons newSpec = null;
while(spec != null)
{
newSpec = new Cons(cloneResult((Result)spec.car,
compiler.datastack,compiler.callstack),
newSpec);
spec = spec.next();
}
return Cons.reverse(newSpec);
} //}}}
//{{{ commonAncestor() method
private static Class commonAncestor(Class ca, Class cb)
{
if(ca.isAssignableFrom(cb))
return ca;
else if(cb.isAssignableFrom(ca))
return cb;
else if(ca.isInterface() || cb.isInterface())
return Object.class;
else
return commonAncestor(ca.getSuperclass(),cb);
} //}}}
//{{{ unifyTypes() method
private static Cons unifyTypes(FactorCompiler compiler,
RecursiveState recursiveCheck,
Cons a, Cons b)
{
Cons cons = null;
for(;;)
{
if(a == null && b == null)
return Cons.reverse(cons);
int la, lb;
Class ca, cb;
if(a == null)
{
la = -1;
ca = Object.class;
}
else
{
FlowObject fa = (FlowObject)a.car;
la = fa.getLocal();
ca = fa.getType();
a = a.next();
}
if(b == null)
{
lb = -1;
cb = Object.class;
}
else
{
FlowObject fb = (FlowObject)b.car;
lb = fb.getLocal();
//if(la != -1 && la != lb)
// System.err.println("? " + a + "," + b);
cb = fb.getType();
b = b.next();
}
//System.err.println("Common ancestor of " + ca + " and " + cb + " is " + commonAncestor(ca,cb));
cons = new Cons(
new Result(la,compiler,recursiveCheck.last(),
commonAncestor(ca,cb)),cons);
}
} //}}}
//{{{ mergeStacks() method
private static void mergeStacks(FactorCompiler compiler,
RecursiveState recursiveCheck,
FactorArray s1, FactorArray s2,
FactorArray into)
{
for(int i = Math.min(s1.top,s2.top) - 1; i >= 0; i--)
{
FlowObject fa = (FlowObject)s1.stack[s1.top - i - 1];
FlowObject fb = (FlowObject)s2.stack[s2.top - i - 1];
into.stack[into.top - i - 1]
= new Result(i,compiler,recursiveCheck.last(),
commonAncestor(fa.getType(),fb.getType()));
}
} //}}}
//{{{ unifyStates() method
public static void unifyStates(
FactorCompiler compiler,
RecursiveState recursiveCheck,
FlowObject t, FlowObject f,
CompilerState a, CompilerState b)
throws Exception
{
StackEffect te = a.effect;
StackEffect fe = b.effect;
// we can only balance out a conditional if
// both sides leave the same amount of elements
// on the stack.
// eg, 1/1 -vs- 2/2 is ok, 3/1 -vs- 4/2 is ok,
// but 1/2 -vs- 2/1 is not.
if(te.outD - te.inD != fe.outD - fe.inD
|| te.outR - te.inR != fe.outR - fe.inR)
{
throw new FactorCompilerException(
"Stack effect of " + t + " " + a.effect + " is inconsistent with " + f + " " + b.effect
+ "\nRecursive state:\n"
+ recursiveCheck);
}
compiler.ensure(compiler.datastack,Math.max(te.outD,fe.outD));
compiler.ensure(compiler.callstack,Math.max(te.outR,fe.outR));
// replace results from the f branch with
// dummy values so that subsequent code
// doesn't assume these values always
// result from this
mergeStacks(compiler,recursiveCheck,a.datastack,
b.datastack,compiler.datastack);
mergeStacks(compiler,recursiveCheck,a.callstack,
b.callstack,compiler.callstack);
compiler.inDtypes = unifyTypes(compiler,recursiveCheck,
a.inDtypes,b.inDtypes);
compiler.inRtypes = unifyTypes(compiler,recursiveCheck,
a.inRtypes,b.inRtypes);
} //}}}
}

View File

@ -0,0 +1,67 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
import java.lang.reflect.*;
import java.util.*;
import org.objectweb.asm.*;
public class ConstantPoolString extends FlowObject
{
private String str;
//{{{ ConstantPoolString constructor
ConstantPoolString(String str, FactorCompiler compiler,
RecursiveForm word)
{
super(compiler,word);
this.str = str;
expectedType = String.class;
} //}}}
//{{{ pop() method
public void pop(CodeVisitor mw)
{
mw.visitLdcInsn(str);
} //}}}
//{{{ getLiteral() method
Object getLiteral()
{
return str;
} //}}}
//{{{ clone() method
public Object clone()
{
return new Null(compiler,word);
} //}}}
}

View File

@ -0,0 +1,188 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.Cons;
import factor.FactorInterpreter;
import factor.db.*;
import java.util.*;
/**
* Each word class has a class loader.
*
* When compiling a word; add each dependent word to new class loader's
* delegates map.
*/
public class FactorClassLoader extends ClassLoader implements PersistentObject
{
private Workspace workspace;
private long id;
private Table table;
//{{{ FactorClassLoader constructor
public FactorClassLoader(Workspace workspace, long id)
throws Exception
{
this.workspace = workspace;
this.id = id;
table = new Table(null,workspace,0L);
if(workspace != null && id != 0L)
workspace.put(this);
} //}}}
//{{{ FactorClassLoader constructor
public FactorClassLoader(Workspace workspace) throws Exception
{
this(workspace,workspace == null ? 0L : workspace.nextID());
} //}}}
//{{{ addDependency() method
public void addDependency(String name, FactorClassLoader loader)
{
try
{
table.setVariable(name,loader);
if(workspace != null && id != 0L)
workspace.put(this);
}
catch(Exception e)
{
throw new RuntimeException(e);
}
} //}}}
//{{{ getWorkspace() method
/**
* Each persistent object is stored in one workspace only.
*/
public Workspace getWorkspace()
{
return workspace;
} //}}}
//{{{ getID() method
/**
* Each persistent object has an associated ID.
*/
public long getID()
{
return id;
} //}}}
//{{{ pickle() method
/**
* Each persistent object can turn itself into a byte array.
*/
public byte[] pickle()
throws PersistenceException
{
return table.pickle();
} //}}}
//{{{ unpickle() method
/**
* Each persistent object can set its state to that in a byte array.
*/
public void unpickle(byte[] bytes, int offset)
throws PersistenceException
{
table.unpickle(bytes,offset);
} //}}}
//{{{ addClass() method
public Class addClass(String name, byte[] code, int off, int len)
{
try
{
table.setVariable(name,
new PersistentBinary(workspace,code));
}
catch(Exception e)
{
throw new RuntimeException(e);
}
return defineClass(name,code,off,len);
} //}}}
//{{{ loadClass() method
public synchronized Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
Class c = findLoadedClass(name);
if(c != null)
{
if(resolve)
resolveClass(c);
return c;
}
// See if another known class loader has this
try
{
Object obj = table.getVariable(name);
if(obj instanceof FactorClassLoader)
{
return ((FactorClassLoader)obj)
.loadClass(name,resolve);
}
else if(obj instanceof PersistentBinary)
{
byte[] bytes = ((PersistentBinary)obj)
.getBytes();
c = defineClass(
name,bytes,0,bytes.length);
if(resolve)
resolveClass(c);
return c;
}
else if(obj != null)
{
System.err.println("WARNING: unknown object in class loader table for " + this + ": " + obj);
}
return super.loadClass(name,resolve);
}
catch(ClassNotFoundException e)
{
throw e;
}
catch(Exception e)
{
throw new RuntimeException(e);
}
} //}}}
//{{{ getReferences() method
public Cons getReferences()
{
return table.getReferences();
} //}}}
}

View File

@ -0,0 +1,899 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
import java.lang.reflect.*;
import java.util.*;
import org.objectweb.asm.*;
public class FactorCompiler implements Constants
{
public final FactorInterpreter interp;
public final FactorWord word;
public final String className;
public final FactorClassLoader loader;
public String method;
private int base;
private int allotD;
private int allotR;
public FactorArray datastack;
public FactorArray callstack;
private int literalCount;
private Map literals;
public StackEffect effect;
/**
* getStackEffect() turns these into arrays and places them in the
* returned object.
*/
public Cons inDtypes, inRtypes;
private Cons aux;
private int auxCount;
//{{{ FactorCompiler constructor
/**
* For balancing.
*/
public FactorCompiler(FactorInterpreter interp)
{
this(interp,null,null,null);
init(0,0,0,null);
} //}}}
//{{{ FactorCompiler constructor
/**
* For compiling.
*/
public FactorCompiler(FactorInterpreter interp,
FactorWord word, String className,
FactorClassLoader loader)
{
this.interp = interp;
this.word = word;
this.className = className;
this.loader = loader;
literals = new HashMap();
datastack = new FactorArray();
callstack = new FactorArray();
} //}}}
//{{{ getInterpreter() method
public FactorInterpreter getInterpreter()
{
return interp;
} //}}}
//{{{ init() method
public void init(int base, int allotD, int allotR, String method)
{
effect = new StackEffect();
this.base = base;
datastack.top = 0;
callstack.top = 0;
for(int i = 0; i < allotD; i++)
{
Result r = new Result(base + i,this,null,
Object.class,true);
datastack.push(r);
inDtypes = new Cons(r,inDtypes);
}
for(int i = 0; i < allotR; i++)
{
Result r = new Result(base + allotD + i,this,null,
Object.class,true);
callstack.push(r);
inRtypes = new Cons(r,inRtypes);
}
this.allotD = allotD;
this.allotR = allotR;
effect.inD = allotD;
effect.inR = allotR;
this.method = method;
} //}}}
//{{{ getAllotedEffect() method
public StackEffect getAllotedEffect()
{
return new StackEffect(allotD,allotR,0,0);
} //}}}
//{{{ ensure() method
public void ensure(FactorArray stack, Class type)
{
if(stack.top == 0)
{
Result r = new Result(allocate(),this,null,type);
if(stack == datastack)
{
inDtypes = new Cons(r,inDtypes);
effect.inD++;
}
else if(stack == callstack)
{
inRtypes = new Cons(r,inRtypes);
effect.inR++;
}
stack.push(r);
}
} //}}}
//{{{ ensure() method
/**
* Ensure stack has at least 'count' elements.
* Eg, if count is 4 and stack is A B,
* stack will become RESULT RESULT A B.
* Used when deducing stack effects.
*/
public void ensure(FactorArray stack, Class[] types)
{
int top = stack.top;
if(top < types.length)
{
Cons typespec = null;
if(stack == datastack)
effect.inD += (types.length - top);
else if(stack == callstack)
effect.inR += (types.length - top);
stack.ensurePush(types.length - top);
System.arraycopy(stack.stack,0,stack.stack,
types.length - top,top);
for(int i = 0; i < types.length - top; i++)
{
int local = allocate();
Result r = new Result(
local,this,null,types[i]);
stack.stack[i] = r;
typespec = new Cons(r,typespec);
}
stack.top = types.length;
if(stack == datastack)
inDtypes = Cons.nappend(inDtypes,typespec);
else if(stack == callstack)
inRtypes = Cons.nappend(inRtypes,typespec);
}
} //}}}
//{{{ ensure() method
/**
* Ensure stack has at least 'count' elements.
* Eg, if count is 4 and stack is A B,
* stack will become RESULT RESULT A B.
* Used when deducing stack effects.
*/
public void ensure(FactorArray stack, int count)
{
Class[] types = new Class[count];
for(int i = 0; i < types.length; i++)
types[i] = Object.class;
ensure(stack,types);
} //}}}
//{{{ consume() method
public void consume(FactorArray stack, int count)
{
ensure(stack,count);
stack.top -= count;
} //}}}
//{{{ produce() method
public void produce(FactorArray stack, int count)
{
for(int i = 0; i < count; i++)
{
int local = allocate();
stack.push(new Result(local,this,null,Object.class));
}
} //}}}
//{{{ apply() method
public void apply(StackEffect se)
{
consume(datastack,se.inD);
produce(datastack,se.outD);
consume(callstack,se.inR);
produce(callstack,se.outR);
} //}}}
//{{{ getTypeSpec() method
private Class[] getTypeSpec(Cons list)
{
if(list == null)
return new Class[0];
int length = list.length();
Class[] typespec = new Class[length];
int i = 0;
while(list != null)
{
typespec[length - i - 1]
= ((FlowObject)list.car).getType();
i++;
list = list.next();
}
return typespec;
} //}}}
//{{{ getStackEffect() method
public StackEffect getStackEffect()
{
effect.inDtypes = getTypeSpec(inDtypes);
effect.outD = datastack.top;
effect.outDtypes = new Class[datastack.top];
for(int i = 0; i < datastack.top; i++)
{
effect.outDtypes[i] = ((FlowObject)datastack.stack[i])
.getType();
}
effect.inRtypes = getTypeSpec(inRtypes);
effect.outR = callstack.top;
effect.outRtypes = new Class[callstack.top];
for(int i = 0; i < callstack.top; i++)
{
effect.outRtypes[i] = ((FlowObject)callstack.stack[i])
.getType();
}
return (StackEffect)effect.clone();
} //}}}
//{{{ compileCore() method
public void compileCore(Cons definition, ClassWriter cw,
StackEffect effect, RecursiveState recursiveCheck)
throws Exception
{
RecursiveForm last = recursiveCheck.last();
last.method = "core";
last.className = className;
last.loader = loader;
compileMethod(definition,cw,"core",effect,word,recursiveCheck,true);
} //}}}
//{{{ compileFieldInit() method
private void compileFieldInit(CodeVisitor mw, Label start)
{
mw.visitFieldInsn(GETSTATIC,className,"initialized","Z");
mw.visitJumpInsn(IFNE,start);
mw.visitInsn(ICONST_1);
mw.visitFieldInsn(PUTSTATIC,className,"initialized","Z");
mw.visitVarInsn(ALOAD,0);
mw.visitMethodInsn(INVOKESTATIC,className,"setFields",
"(Lfactor/FactorInterpreter;)V");
} //}}}
//{{{ compileReturn() method
/**
* Once the word finishes executing, any return values need to be
* passed up.
*/
private void compileReturn(CodeVisitor mw, Label end,
StackEffect effect) throws Exception
{
// special case where return value is passed on
// JVM operand stack
// note: in each branch, must visit end label before RETURN!
if(effect.outD == 0 && effect.outR == 0)
{
mw.visitLabel(end);
mw.visitInsn(RETURN);
}
else if(effect.outD == 1 && effect.outR == 0)
{
pop(datastack,mw,Object.class);
mw.visitLabel(end);
mw.visitInsn(ARETURN);
}
else
{
// store datastack in a local
mw.visitVarInsn(ALOAD,0);
mw.visitFieldInsn(GETFIELD,
"factor/FactorInterpreter",
"datastack",
"Lfactor/FactorArray;");
int datastackLocal = allocate();
mw.visitVarInsn(ASTORE,datastackLocal);
for(int i = 0; i < datastack.top; i++)
{
mw.visitVarInsn(ALOAD,datastackLocal);
((FlowObject)datastack.stack[i])
.pop(mw,Object.class);
mw.visitMethodInsn(INVOKEVIRTUAL,
"factor/FactorArray",
"push",
"(Ljava/lang/Object;)V");
}
datastack.top = 0;
// store callstack in a local
mw.visitVarInsn(ALOAD,0);
mw.visitFieldInsn(GETFIELD,
"factor/FactorInterpreter",
"callstack",
"Lfactor/FactorArray;");
int callstackLocal = allocate();
mw.visitVarInsn(ASTORE,callstackLocal);
for(int i = 0; i < callstack.top; i++)
{
mw.visitVarInsn(ALOAD,callstackLocal);
((FlowObject)callstack.stack[i])
.pop(mw,Object.class);
mw.visitMethodInsn(INVOKEVIRTUAL,
"factor/FactorArray",
"push",
"(Ljava/lang/Object;)V");
}
callstack.top = 0;
mw.visitLabel(end);
mw.visitInsn(RETURN);
}
} //}}}
//{{{ compileMethod() method
/**
* Compiles a method.
*/
public void compileMethod(Cons definition, ClassWriter cw,
String methodName, StackEffect effect, FactorWord word,
RecursiveState recursiveCheck, boolean fieldInit)
throws Exception
{
String signature = effect.getCorePrototype();
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC | ACC_STATIC,
methodName,signature,null,null);
Label start = recursiveCheck.get(word).label;
if(fieldInit)
compileFieldInit(mw,start);
mw.visitLabel(start);
compile(definition,mw,recursiveCheck);
Label end = new Label();
compileReturn(mw,end,effect);
compileExceptionHandler(mw,start,end,word);
mw.visitMaxs(0,0);
} //}}}
//{{{ compileExceptionHandler() method
private void compileExceptionHandler(CodeVisitor mw,
Label start, Label end, FactorWord word)
{
// otherwise no code can throw exception etc
if(start.getOffset() != end.getOffset())
{
// Now compile exception handler.
Label target = new Label();
mw.visitLabel(target);
mw.visitVarInsn(ASTORE,1);
mw.visitVarInsn(ALOAD,0);
mw.visitFieldInsn(GETSTATIC,className,literal(word),
"Ljava/lang/Object;");
mw.visitTypeInsn(CHECKCAST,"factor/FactorWord");
mw.visitVarInsn(ALOAD,1);
mw.visitMethodInsn(INVOKEVIRTUAL,"factor/FactorInterpreter",
"compiledException",
"(Lfactor/FactorWord;Ljava/lang/Throwable;)V");
mw.visitVarInsn(ALOAD,1);
mw.visitInsn(ATHROW);
mw.visitTryCatchBlock(start,end,target,"java/lang/Throwable");
}
} //}}}
//{{{ compile() method
/**
* Compiles a quotation.
*/
public void compile(Cons definition, CodeVisitor mw,
RecursiveState recursiveCheck) throws Exception
{
while(definition != null)
{
Object obj = definition.car;
if(obj instanceof FactorWord)
{
compileWord((FactorWord)obj,mw,recursiveCheck,
definition.cdr == null);
}
else
pushLiteral(obj,recursiveCheck);
definition = definition.next();
}
} //}}}
//{{{ compileWord() method
private void compileWord(FactorWord w, CodeVisitor mw,
RecursiveState recursiveCheck,
boolean tail) throws Exception
{
if(tail && interp != null && interp.verboseCompile)
System.err.println("Tail: " + recursiveCheck.last());
recursiveCheck.last().tail = tail;
RecursiveForm rec = recursiveCheck.get(w);
try
{
boolean recursiveCall;
if(rec == null)
{
recursiveCall = false;
recursiveCheck.add(w,
new StackEffect(),
className,loader,
"core");
recursiveCheck.last().tail
= (w.def instanceof FactorPrimitiveDefinition);
}
else
{
recursiveCall = true;
rec.active = true;
}
compileCallTo(w,mw,recursiveCheck,recursiveCall);
}
finally
{
if(rec == null)
recursiveCheck.remove(w);
else
{
rec.active = false;
rec.tail = false;
}
}
} //}}}
//{{{ compileCallTo() method
private void compileCallTo(FactorWord w, CodeVisitor mw,
RecursiveState recursiveCheck,
boolean recursiveCall) throws Exception
{
if(w.def == null)
throw new FactorUndefinedWordException(w.name);
FactorWordDefinition d = w.def;
if(mw == null)
{
recursiveCheck.last().effect = getStackEffect();
d.getStackEffect(recursiveCheck,this);
return;
}
if(!recursiveCall)
{
if(w.inline)
{
recursiveCheck.last().effect = getStackEffect();
d.compileImmediate(mw,this,recursiveCheck);
return;
}
else if(d instanceof FactorCompoundDefinition)
{
w.compile(interp,recursiveCheck);
if(d == w.def)
{
throw new FactorCompilerException(word + " depends on " + w + " which cannot be compiled");
}
d = w.def;
}
}
d.compileCallTo(mw,this,recursiveCheck);
} //}}}
//{{{ push() method
/**
* Generates code for pushing the top of the JVM stack onto the
* data stack. Also generates code for converting this value to
* the given type.
*/
public void push(FactorArray stack, CodeVisitor mw, Class type)
throws Exception
{
int local = allocate();
Result r = new Result(local,this,null,type);
stack.push(r);
r.push(mw,type);
} //}}}
//{{{ pushLiteral() method
public void pushLiteral(Object literal, RecursiveState recursiveCheck)
{
RecursiveForm word = recursiveCheck.last();
if(literal == null)
datastack.push(new Null(this,word));
else if(literal instanceof Cons)
{
datastack.push(new CompiledList((Cons)literal,this,
recursiveCheck));
}
else if(literal instanceof String)
{
datastack.push(new ConstantPoolString((String)literal,
this,word));
}
else
datastack.push(new Literal(literal,this, word));
} //}}}
//{{{ pop() method
/**
* Generates code for popping the top of the data stack onto
* the JVM stack. Also generates code for converting this value to
* the given type.
*/
public void pop(FactorArray stack, CodeVisitor mw, Class type)
throws Exception
{
FlowObject obj = (FlowObject)datastack.pop();
obj.pop(mw,type);
} //}}}
//{{{ popLiteral() method
/**
* Pops a literal off the datastack or throws an exception.
*/
public Object popLiteral() throws FactorException
{
FlowObject obj = (FlowObject)datastack.pop();
return obj.getLiteral();
} //}}}
//{{{ allocate() method
/**
* Allocate a local variable.
*/
public int allocate()
{
// inefficient!
int i = base;
for(;;)
{
if(allocate(i,datastack) && allocate(i,callstack))
return i;
else
i++;
}
} //}}}
//{{{ allocate() method
/**
* Return true if not in use, false if in use.
*/
private boolean allocate(int local, FactorArray stack)
{
for(int i = 0; i < stack.top; i++)
{
FlowObject obj = (FlowObject)stack.stack[i];
if(obj.usingLocal(local))
return false;
}
return true;
} //}}}
//{{{ literal() method
public String literal(Object obj)
{
Integer i = (Integer)literals.get(obj);
int literal;
if(i == null)
{
literal = literalCount++;
literals.put(obj,new Integer(literal));
}
else
literal = i.intValue();
return "literal_" + literal;
} //}}}
//{{{ auxiliary() method
public String auxiliary(FactorWord word, Cons code, StackEffect effect,
RecursiveState recursiveCheck) throws Exception
{
FactorArray savedDatastack = (FactorArray)
datastack.clone();
FactorArray savedCallstack = (FactorArray)
callstack.clone();
String method = "aux_" + FactorJava.getSanitizedName(word.name)
+ "_" + (auxCount++);
recursiveCheck.last().method = method;
aux = new Cons(new AuxiliaryQuotation(
method,savedDatastack,savedCallstack,
code,effect,word,this,recursiveCheck),aux);
return method;
} //}}}
//{{{ generateAuxiliary() method
public void generateAuxiliary(ClassWriter cw) throws Exception
{
while(aux != null)
{
AuxiliaryQuotation q = (AuxiliaryQuotation)aux.car;
// order of these two important, in case
// compilation of q adds more quotations to aux list
aux = aux.next();
q.compile(this,cw);
}
} //}}}
//{{{ normalizeStacks() method
public void normalizeStacks(CodeVisitor mw)
throws Exception
{
int datastackTop = datastack.top;
datastack.top = 0;
int callstackTop = callstack.top;
callstack.top = 0;
localsToStack(callstack,callstackTop,mw);
localsToStack(datastack,datastackTop,mw);
stackToLocals(datastack,datastackTop,mw);
stackToLocals(callstack,callstackTop,mw);
} //}}}
//{{{ localsToStack() method
private void localsToStack(FactorArray stack, int top,
CodeVisitor mw) throws Exception
{
for(int i = top - 1; i >= 0; i--)
{
FlowObject obj = (FlowObject)stack.stack[i];
obj.pop(mw,Object.class);
}
} //}}}
//{{{ stackToLocals() method
private void stackToLocals(FactorArray stack, int top,
CodeVisitor mw) throws Exception
{
for(int i = 0; i < top; i++)
push(stack,mw,Object.class);
} //}}}
//{{{ generateArgs() method
/**
* Generate instructions for copying arguments from the allocated
* local variables to the JVM stack, doing type conversion in the
* process.
*/
public void generateArgs(CodeVisitor mw, int inD, int inR, Class[] args)
throws Exception
{
for(int i = 0; i < inD; i++)
{
FlowObject obj = (FlowObject)datastack.stack[
datastack.top - inD + i];
obj.pop(mw,args == null ? Object.class : args[i]);
}
datastack.top -= inD;
for(int i = 0; i < inR; i++)
{
FlowObject obj = (FlowObject)callstack.stack[
callstack.top - inR + i];
obj.pop(mw,args == null ? Object.class : args[i]);
}
callstack.top -= inR;
} //}}}
//{{{ generateReturn() method
public void generateReturn(CodeVisitor mw, int outD, int outR)
throws Exception
{
if(outD == 0 && outR == 0)
{
// do nothing
}
else if(outD == 1 && outR == 0)
{
push(datastack,mw,Object.class);
}
else
{
// transfer from data stack to JVM locals
// allocate the appropriate number of locals
if(outD != 0)
{
produce(datastack,outD);
// store the datastack instance somewhere
mw.visitVarInsn(ALOAD,0);
mw.visitFieldInsn(GETFIELD,
"factor/FactorInterpreter",
"datastack",
"Lfactor/FactorArray;");
int datastackLocal = allocate();
mw.visitVarInsn(ASTORE,datastackLocal);
// put all elements from the real datastack
// into locals
for(int i = 0; i < outD; i++)
{
mw.visitVarInsn(ALOAD,datastackLocal);
mw.visitMethodInsn(INVOKEVIRTUAL,
"factor/FactorArray",
"pop",
"()Ljava/lang/Object;");
Result destination = (Result)
datastack.stack[
datastack.top - i - 1];
destination.push(mw,Object.class);
}
}
if(outR != 0)
{
produce(callstack,outR);
mw.visitVarInsn(ALOAD,0);
mw.visitFieldInsn(GETFIELD,
"factor/FactorInterpreter",
"callstack",
"Lfactor/FactorArray;");
int callstackLocal = allocate();
mw.visitVarInsn(ASTORE,callstackLocal);
// put all elements from the real callstack
// into locals
for(int i = 0; i < outR; i++)
{
mw.visitVarInsn(ALOAD,callstackLocal);
mw.visitMethodInsn(INVOKEVIRTUAL,
"factor/FactorArray",
"pop",
"()Ljava/lang/Object;");
Result destination = (Result)
callstack.stack[
callstack.top - i - 1];
destination.push(mw,Object.class);
}
}
}
} //}}}
//{{{ generateFields() method
public void generateFields(ClassWriter cw)
throws Exception
{
for(int i = 0; i < literalCount; i++)
{
cw.visitField(ACC_PRIVATE | ACC_STATIC,"literal_" + i,
"Ljava/lang/Object;",null,null);
}
CodeVisitor mw = cw.visitMethod(ACC_PRIVATE | ACC_STATIC,
"setFields","(Lfactor/FactorInterpreter;)V",null,null);
Iterator entries = literals.entrySet().iterator();
while(entries.hasNext())
{
Map.Entry entry = (Map.Entry)entries.next();
Object literal = entry.getKey();
int index = ((Integer)entry.getValue()).intValue();
generateParse(mw,literal,0);
mw.visitFieldInsn(PUTSTATIC,
className,
"literal_" + index,
"Ljava/lang/Object;");
}
mw.visitInsn(RETURN);
mw.visitMaxs(0,0);
} //}}}
//{{{ generateParse() method
public void generateParse(CodeVisitor mw, Object obj, int interpLocal)
{
mw.visitLdcInsn(FactorReader.getVocabularyDeclaration(obj)
+ FactorReader.unparseObject(obj));
mw.visitVarInsn(ALOAD,interpLocal);
mw.visitMethodInsn(INVOKESTATIC,
"factor/FactorReader",
"parseObject",
"(Ljava/lang/String;Lfactor/FactorInterpreter;)"
+ "Ljava/lang/Object;");
} //}}}
//{{{ getStackEffectOrNull() method
public StackEffect getStackEffectOrNull(FactorWordDefinition def)
{
try
{
return def.getStackEffect(interp);
}
catch(Exception e)
{
//System.err.println("WARNING: " + e);
//System.err.println(def);
return null;
}
} //}}}
}

View File

@ -0,0 +1,983 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
import java.lang.reflect.*;
import java.util.*;
import org.objectweb.asm.*;
public class FactorCompiler implements Constants
{
public final FactorInterpreter interp;
public final FactorWord word;
public final String className;
public final FactorClassLoader loader;
public String method;
private int base;
private int allotD;
private int allotR;
public FactorArrayStack datastack;
public FactorCallStack callstack;
private int literalCount;
private Map literals;
public StackEffect effect;
/**
* getStackEffect() turns these into arrays and places them in the
* returned object.
*/
private Cons inDtypes, inRtypes;
private Cons aux;
private int auxCount;
//{{{ FactorCompiler constructor
/**
* For balancing.
*/
public FactorCompiler(FactorInterpreter interp)
{
this(interp,null,null,null);
init(0,0,0,null);
} //}}}
//{{{ FactorCompiler constructor
/**
* For compiling.
*/
public FactorCompiler(FactorInterpreter interp,
FactorWord word, String className,
FactorClassLoader loader)
{
this.interp = interp;
this.word = word;
this.className = className;
this.loader = loader;
literals = new HashMap();
datastack = new FactorArrayStack();
callstack = new FactorCallStack();
} //}}}
//{{{ getInterpreter() method
public FactorInterpreter getInterpreter()
{
return interp;
} //}}}
//{{{ init() method
public void init(int base, int allotD, int allotR, String method)
{
effect = new StackEffect();
this.base = base;
datastack.top = 0;
callstack.top = 0;
for(int i = 0; i < allotD; i++)
{
Result r = new Result(base + i,this,null,
Object.class);
datastack.push(r);
inDtypes = new Cons(r,inDtypes);
}
for(int i = 0; i < allotR; i++)
{
Result r = new Result(base + allotD + i,this,null,
Object.class);
callstack.push(r);
inRtypes = new Cons(r,inRtypes);
}
this.allotD = allotD;
this.allotR = allotR;
effect.inD = allotD;
effect.inR = allotR;
this.method = method;
} //}}}
//{{{ getAllotedEffect() method
public StackEffect getAllotedEffect()
{
return new StackEffect(allotD,allotR,0,0);
} //}}}
//{{{ ensure() method
public void ensure(FactorArrayStack stack, Class type)
{
if(stack.top == 0)
{
Result r = new Result(allocate(),this,null,type);
if(stack == datastack)
{
inDtypes = new Cons(r,inDtypes);
effect.inD++;
}
else if(stack == callstack)
{
inRtypes = new Cons(r,inRtypes);
effect.inR++;
}
stack.push(r);
}
} //}}}
//{{{ ensure() method
/**
* Ensure stack has at least 'count' elements.
* Eg, if count is 4 and stack is A B,
* stack will become RESULT RESULT A B.
* Used when deducing stack effects.
*/
public void ensure(FactorArrayStack stack, int count)
{
Class[] types = new Class[count];
for(int i = 0; i < types.length; i++)
types[i] = Object.class;
ensure(stack,types);
} //}}}
//{{{ ensure() method
/**
* Ensure stack has at least 'count' elements.
* Eg, if count is 4 and stack is A B,
* stack will become RESULT RESULT A B.
* Used when deducing stack effects.
*/
public void ensure(FactorArrayStack stack, Class[] types)
{
int top = stack.top;
if(top < types.length)
{
Cons typespec = null;
if(stack == datastack)
effect.inD += (types.length - top);
else if(stack == callstack)
effect.inR += (types.length - top);
stack.ensurePush(types.length - top);
System.arraycopy(stack.stack,0,stack.stack,
types.length - top,top);
for(int i = 0; i < types.length - top; i++)
{
int local = allocate();
Result r = new Result(
local,this,null,types[i]);
stack.stack[i] = r;
typespec = new Cons(r,typespec);
}
stack.top = types.length;
if(stack == datastack)
inDtypes = Cons.nappend(inDtypes,typespec);
else if(stack == callstack)
inRtypes = Cons.nappend(inRtypes,typespec);
}
} //}}}
//{{{ consume() method
public void consume(FactorArrayStack stack, int count)
{
ensure(stack,count);
stack.top -= count;
} //}}}
//{{{ produce() method
public void produce(FactorArrayStack stack, int count)
{
for(int i = 0; i < count; i++)
{
int local = allocate();
stack.push(new Result(local,this,null,Object.class));
}
} //}}}
//{{{ apply() method
public void apply(StackEffect se)
{
consume(datastack,se.inD);
produce(datastack,se.outD);
consume(callstack,se.inR);
produce(callstack,se.outR);
} //}}}
//{{{ getTypeSpec() method
private Class[] getTypeSpec(Cons list)
{
if(list == null)
return new Class[0];
int length = list.length();
Class[] typespec = new Class[length];
int i = 0;
while(list != null)
{
typespec[length - i - 1]
= ((FlowObject)list.car).getType();
i++;
list = list.next();
}
return typespec;
} //}}}
//{{{ getStackEffect() method
public StackEffect getStackEffect()
{
effect.inDtypes = getTypeSpec(inDtypes);
effect.outD = datastack.top;
effect.outDtypes = new Class[datastack.top];
for(int i = 0; i < datastack.top; i++)
{
effect.outDtypes[i] = ((FlowObject)datastack.stack[i])
.getType();
}
effect.inRtypes = getTypeSpec(inRtypes);
effect.outR = callstack.top;
effect.outRtypes = new Class[callstack.top];
for(int i = 0; i < callstack.top; i++)
{
effect.outRtypes[i] = ((FlowObject)callstack.stack[i])
.getType();
}
return (StackEffect)effect.clone();
} //}}}
//{{{ getStackEffect() method
public void getStackEffect(Cons definition,
RecursiveState recursiveCheck)
throws Exception
{
while(definition != null)
{
Object obj = definition.car;
if(obj instanceof FactorWord)
getStackEffectOfWord((FactorWord)obj,recursiveCheck);
else
pushLiteral(obj,recursiveCheck);
definition = definition.next();
}
} //}}}
//{{{ getStackEffectOfWord() method
private void getStackEffectOfWord(FactorWord word,
RecursiveState recursiveCheck)
throws Exception
{
RecursiveForm rec = recursiveCheck.get(word);
try
{
boolean recursiveCall;
if(rec == null)
{
recursiveCall = false;
recursiveCheck.add(word,
getStackEffect(),
className,loader,
"core");
/* recursiveCheck.last().tail
= (word.def instanceof FactorPrimitiveDefinition); */
}
else
{
recursiveCall = true;
rec.active = true;
}/*
if(rec == null)
recursiveCheck.add(word,getStackEffect(),null,null,null);
else
rec.active = true; */
compileCallTo(word,null,recursiveCheck,false);
}
finally
{
if(rec == null)
recursiveCheck.remove(word);
else
{
rec.active = false;
rec.tail = false;
}
}
} //}}}
//{{{ compileCore() method
public void compileCore(Cons definition, ClassWriter cw,
StackEffect effect, RecursiveState recursiveCheck)
throws Exception
{
RecursiveForm last = recursiveCheck.last();
last.method = "core";
last.className = className;
last.loader = loader;
compileMethod(definition,cw,"core",effect,word,recursiveCheck,true);
} //}}}
//{{{ compileFieldInit() method
private void compileFieldInit(CodeVisitor mw, Label start)
{
mw.visitFieldInsn(GETSTATIC,className,"initialized","Z");
mw.visitJumpInsn(IFNE,start);
mw.visitInsn(ICONST_1);
mw.visitFieldInsn(PUTSTATIC,className,"initialized","Z");
mw.visitVarInsn(ALOAD,0);
mw.visitMethodInsn(INVOKESTATIC,className,"setFields",
"(Lfactor/FactorInterpreter;)V");
} //}}}
//{{{ compileReturn() method
/**
* Once the word finishes executing, any return values need to be
* passed up.
*/
private void compileReturn(CodeVisitor mw, Label end,
StackEffect effect) throws Exception
{
// special case where return value is passed on
// JVM operand stack
// note: in each branch, must visit end label before RETURN!
if(effect.outD == 0 && effect.outR == 0)
{
mw.visitLabel(end);
mw.visitInsn(RETURN);
}
else if(effect.outD == 1 && effect.outR == 0)
{
pop(datastack,mw,Object.class);
mw.visitLabel(end);
mw.visitInsn(ARETURN);
}
else
{
// store datastack in a local
mw.visitVarInsn(ALOAD,0);
mw.visitFieldInsn(GETFIELD,
"factor/FactorInterpreter",
"datastack",
"Lfactor/FactorArrayStack;");
int datastackLocal = allocate();
mw.visitVarInsn(ASTORE,datastackLocal);
for(int i = 0; i < datastack.top; i++)
{
mw.visitVarInsn(ALOAD,datastackLocal);
((FlowObject)datastack.stack[i])
.pop(mw,Object.class);
mw.visitMethodInsn(INVOKEVIRTUAL,
"factor/FactorArrayStack",
"push",
"(Ljava/lang/Object;)V");
}
datastack.top = 0;
// store callstack in a local
mw.visitVarInsn(ALOAD,0);
mw.visitFieldInsn(GETFIELD,
"factor/FactorInterpreter",
"callstack",
"Lfactor/FactorCallStack;");
int callstackLocal = allocate();
mw.visitVarInsn(ASTORE,callstackLocal);
for(int i = 0; i < callstack.top; i++)
{
mw.visitVarInsn(ALOAD,callstackLocal);
((FlowObject)callstack.stack[i])
.pop(mw,Object.class);
mw.visitMethodInsn(INVOKEVIRTUAL,
"factor/FactorCallStack",
"push",
"(Ljava/lang/Object;)V");
}
callstack.top = 0;
mw.visitLabel(end);
mw.visitInsn(RETURN);
}
} //}}}
//{{{ compileMethod() method
/**
* Compiles a method.
*/
public void compileMethod(Cons definition, ClassWriter cw,
String methodName, StackEffect effect, FactorWord word,
RecursiveState recursiveCheck, boolean fieldInit)
throws Exception
{
String signature = effect.getCorePrototype();
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC | ACC_STATIC,
methodName,signature,null,null);
Label start = recursiveCheck.get(word).label;
if(fieldInit)
compileFieldInit(mw,start);
mw.visitLabel(start);
compile(definition,mw,recursiveCheck);
Label end = new Label();
compileReturn(mw,end,effect);
compileExceptionHandler(mw,start,end,word);
mw.visitMaxs(0,0);
} //}}}
//{{{ compileExceptionHandler() method
private void compileExceptionHandler(CodeVisitor mw,
Label start, Label end, FactorWord word)
{
// otherwise no code can throw exception etc
if(start.getOffset() != end.getOffset())
{
// Now compile exception handler.
Label target = new Label();
mw.visitLabel(target);
mw.visitVarInsn(ASTORE,1);
mw.visitVarInsn(ALOAD,0);
mw.visitFieldInsn(GETSTATIC,className,literal(word),
"Ljava/lang/Object;");
mw.visitTypeInsn(CHECKCAST,"factor/FactorWord");
mw.visitVarInsn(ALOAD,1);
mw.visitMethodInsn(INVOKEVIRTUAL,"factor/FactorInterpreter",
"compiledException",
"(Lfactor/FactorWord;Ljava/lang/Throwable;)V");
mw.visitVarInsn(ALOAD,1);
mw.visitInsn(ATHROW);
mw.visitTryCatchBlock(start,end,target,"java/lang/Throwable");
}
} //}}}
//{{{ compile() method
/**
* Compiles a quotation.
*/
public void compile(Cons definition, CodeVisitor mw,
RecursiveState recursiveCheck) throws Exception
{
while(definition != null)
{
Object obj = definition.car;
if(obj instanceof FactorWord)
{
compileWord((FactorWord)obj,mw,recursiveCheck,
definition.cdr == null);
}
else
pushLiteral(obj,recursiveCheck);
definition = definition.next();
}
} //}}}
//{{{ compileWord() method
private void compileWord(FactorWord w, CodeVisitor mw,
RecursiveState recursiveCheck,
boolean tail) throws Exception
{
if(tail && interp.verboseCompile)
System.err.println("Tail: " + recursiveCheck.last());
recursiveCheck.last().tail = tail;
RecursiveForm rec = recursiveCheck.get(w);
try
{
boolean recursiveCall;
if(rec == null)
{
recursiveCall = false;
recursiveCheck.add(w,
new StackEffect(),
className,loader,
"core");
recursiveCheck.last().tail
= (w.def instanceof FactorPrimitiveDefinition);
}
else
{
recursiveCall = true;
rec.active = true;
}
compileCallTo(w,mw,recursiveCheck,recursiveCall);
}
finally
{
if(rec == null)
recursiveCheck.remove(w);
else
{
rec.active = false;
rec.tail = false;
}
}
} //}}}
//{{{ compileCallTo() method
private void compileCallTo(FactorWord w, CodeVisitor mw,
RecursiveState recursiveCheck,
boolean recursiveCall) throws Exception
{
if(w.def == null)
throw new FactorUndefinedWordException(w);
FactorWordDefinition d = w.def;
if(!recursiveCall)
{
StackEffect effect = getStackEffectOrNull(d);
if(w.inline)
{
d.compileImmediate(mw,this,recursiveCheck);
return;
}
else if(d instanceof FactorCompoundDefinition
&& mw != null)
{
w.compile(interp,recursiveCheck);
if(d == w.def)
{
throw new FactorCompilerException(word + " depends on " + w + " which cannot be compiled");
}
d = w.def;
}
w.compileRef = true;
}
d.compileCallTo(mw,this,recursiveCheck);
} //}}}
//{{{ push() method
/**
* Generates code for pushing the top of the JVM stack onto the
* data stack. Also generates code for converting this value to
* the given type.
*/
public void push(FactorArrayStack stack, CodeVisitor mw, Class type)
throws Exception
{
int local = allocate();
Result r = new Result(local,this,null,type);
stack.push(r);
r.push(mw,type);
} //}}}
//{{{ pushLiteral() method
public void pushLiteral(Object literal, RecursiveState recursiveCheck)
{
if(literal == null)
datastack.push(new Null(this,recursiveCheck));
else if(literal instanceof Cons)
{
datastack.push(new CompiledList((Cons)literal,this,
recursiveCheck));
}
else if(literal instanceof String)
{
datastack.push(new ConstantPoolString((String)literal,
this,recursiveCheck));
}
else
{
datastack.push(new Literal(literal,this,
recursiveCheck));
}
} //}}}
//{{{ pop() method
/**
* Generates code for popping the top of the data stack onto
* the JVM stack. Also generates code for converting this value to
* the given type.
*/
public void pop(FactorArrayStack stack, CodeVisitor mw, Class type)
throws Exception
{
FlowObject obj = (FlowObject)datastack.pop();
obj.pop(mw,type);
} //}}}
//{{{ popLiteral() method
/**
* Pops a literal off the datastack or throws an exception.
*/
public Object popLiteral() throws FactorException
{
FlowObject obj = (FlowObject)datastack.pop();
return obj.getLiteral();
} //}}}
//{{{ allocate() method
/**
* Allocate a local variable.
*/
public int allocate()
{
// inefficient!
int i = base;
for(;;)
{
if(allocate(i,datastack) && allocate(i,callstack))
return i;
else
i++;
}
} //}}}
//{{{ allocate() method
/**
* Return true if not in use, false if in use.
*/
private boolean allocate(int local, FactorArrayStack stack)
{
for(int i = 0; i < stack.top; i++)
{
FlowObject obj = (FlowObject)stack.stack[i];
if(obj.usingLocal(local))
return false;
}
return true;
} //}}}
//{{{ literal() method
public String literal(Object obj)
{
Integer i = (Integer)literals.get(obj);
int literal;
if(i == null)
{
literal = literalCount++;
literals.put(obj,new Integer(literal));
}
else
literal = i.intValue();
return "literal_" + literal;
} //}}}
//{{{ auxiliary() method
public String auxiliary(FactorWord word, Cons code, StackEffect effect,
RecursiveState recursiveCheck) throws Exception
{
FactorArrayStack savedDatastack = (FactorArrayStack)
datastack.clone();
FactorCallStack savedCallstack = (FactorCallStack)
callstack.clone();
String method = "aux_" + FactorJava.getSanitizedName(word.name)
+ "_" + (auxCount++);
recursiveCheck.last().method = method;
aux = new Cons(new AuxiliaryQuotation(
method,savedDatastack,savedCallstack,
code,effect,word,this,recursiveCheck),aux);
return method;
} //}}}
//{{{ generateAuxiliary() method
public void generateAuxiliary(ClassWriter cw) throws Exception
{
while(aux != null)
{
AuxiliaryQuotation q = (AuxiliaryQuotation)aux.car;
// order of these two important, in case
// compilation of q adds more quotations to aux list
aux = aux.next();
q.compile(this,cw);
}
} //}}}
//{{{ normalizeStacks() method
public void normalizeStacks(CodeVisitor mw)
throws Exception
{
int datastackTop = datastack.top;
datastack.top = 0;
int callstackTop = callstack.top;
callstack.top = 0;
localsToStack(callstack,callstackTop,mw);
localsToStack(datastack,datastackTop,mw);
stackToLocals(datastack,datastackTop,mw);
stackToLocals(callstack,callstackTop,mw);
} //}}}
//{{{ localsToStack() method
private void localsToStack(FactorArrayStack stack, int top,
CodeVisitor mw)
{
for(int i = top - 1; i >= 0; i--)
{
FlowObject obj = (FlowObject)stack.stack[i];
obj.pop(mw);
}
} //}}}
//{{{ stackToLocals() method
private void stackToLocals(FactorArrayStack stack, int top,
CodeVisitor mw) throws Exception
{
for(int i = 0; i < top; i++)
push(stack,mw,Object.class);
} //}}}
//{{{ generateArgs() method
/**
* Generate instructions for copying arguments from the allocated
* local variables to the JVM stack, doing type conversion in the
* process.
*/
public void generateArgs(CodeVisitor mw, int inD, int inR, Class[] args)
throws Exception
{
for(int i = 0; i < inD; i++)
{
FlowObject obj = (FlowObject)datastack.stack[
datastack.top - inD + i];
obj.pop(mw,args == null ? Object.class : args[i]);
}
datastack.top -= inD;
for(int i = 0; i < inR; i++)
{
FlowObject obj = (FlowObject)callstack.stack[
callstack.top - inR + i];
obj.pop(mw,args == null ? Object.class : args[i]);
}
callstack.top -= inR;
} //}}}
//{{{ generateReturn() method
public void generateReturn(CodeVisitor mw, int outD, int outR)
throws Exception
{
if(outD == 0 && outR == 0)
{
// do nothing
}
else if(outD == 1 && outR == 0)
{
push(datastack,mw,Object.class);
}
else
{
// transfer from data stack to JVM locals
// allocate the appropriate number of locals
if(outD != 0)
{
produce(datastack,outD);
// store the datastack instance somewhere
mw.visitVarInsn(ALOAD,0);
mw.visitFieldInsn(GETFIELD,
"factor/FactorInterpreter",
"datastack",
"Lfactor/FactorArrayStack;");
int datastackLocal = allocate();
mw.visitVarInsn(ASTORE,datastackLocal);
// put all elements from the real datastack
// into locals
for(int i = 0; i < outD; i++)
{
mw.visitVarInsn(ALOAD,datastackLocal);
mw.visitMethodInsn(INVOKEVIRTUAL,
"factor/FactorArrayStack",
"pop",
"()Ljava/lang/Object;");
Result destination = (Result)
datastack.stack[
datastack.top - i - 1];
destination.push(mw,Object.class);
}
}
if(outR != 0)
{
produce(callstack,outR);
mw.visitVarInsn(ALOAD,0);
mw.visitFieldInsn(GETFIELD,
"factor/FactorInterpreter",
"callstack",
"Lfactor/FactorCallStack;");
int callstackLocal = allocate();
mw.visitVarInsn(ASTORE,callstackLocal);
// put all elements from the real callstack
// into locals
for(int i = 0; i < outR; i++)
{
mw.visitVarInsn(ALOAD,callstackLocal);
mw.visitMethodInsn(INVOKEVIRTUAL,
"factor/FactorCallStack",
"pop",
"()Ljava/lang/Object;");
Result destination = (Result)
callstack.stack[
callstack.top - i - 1];
destination.push(mw,Object.class);
}
}
}
} //}}}
//{{{ generateFields() method
public void generateFields(ClassWriter cw)
throws Exception
{
for(int i = 0; i < literalCount; i++)
{
cw.visitField(ACC_PRIVATE | ACC_STATIC,"literal_" + i,
"Ljava/lang/Object;",null,null);
}
CodeVisitor mw = cw.visitMethod(ACC_PRIVATE | ACC_STATIC,
"setFields","(Lfactor/FactorInterpreter;)V",null,null);
Iterator entries = literals.entrySet().iterator();
while(entries.hasNext())
{
Map.Entry entry = (Map.Entry)entries.next();
Object literal = entry.getKey();
int index = ((Integer)entry.getValue()).intValue();
generateParse(mw,literal,0);
mw.visitFieldInsn(PUTSTATIC,
className,
"literal_" + index,
"Ljava/lang/Object;");
}
mw.visitInsn(RETURN);
mw.visitMaxs(0,0);
} //}}}
//{{{ generateParse() method
public void generateParse(CodeVisitor mw, Object obj, int interpLocal)
{
mw.visitLdcInsn(FactorReader.getVocabularyDeclaration(obj)
+ FactorReader.unparseObject(obj));
mw.visitVarInsn(ALOAD,interpLocal);
mw.visitMethodInsn(INVOKESTATIC,
"factor/FactorReader",
"parseObject",
"(Ljava/lang/String;Lfactor/FactorInterpreter;)"
+ "Ljava/lang/Object;");
} //}}}
//{{{ getStackEffectOrNull() method
public StackEffect getStackEffectOrNull(FactorWordDefinition def)
{
try
{
return def.getStackEffect(interp,
new RecursiveState());
}
catch(Exception e)
{
//System.err.println("WARNING: " + e);
//System.err.println(def);
return null;
}
} //}}}
//{{{ getStackEffectOrNull() method
public StackEffect getStackEffectOrNull(FlowObject obj,
RecursiveState recursiveCheck,
boolean decompose)
{
try
{
obj.getStackEffect(recursiveCheck);
StackEffect effect = getStackEffect();
if(decompose)
{
effect = StackEffect.decompose(
recursiveCheck.last().effect,
effect);
}
return effect;
}
catch(Exception e)
{
//System.err.println("WARNING: " + e);
//System.err.println(obj);
return null;
}
} //}}}
}

View File

@ -0,0 +1,45 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
public class FactorCompilerException extends FactorRuntimeException
{
public FactorCompilerException(String str)
{
super(str);
}
public FactorCompilerException(String str, Throwable t)
{
super(str,t);
}
}

View File

@ -0,0 +1,318 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
import java.lang.reflect.*;
import java.math.BigInteger;
import java.util.*;
import org.objectweb.asm.*;
public abstract class FlowObject
implements Constants, FactorExternalizable, PublicCloneable
{
protected FactorCompiler compiler;
protected RecursiveForm word;
protected Class expectedType;
//{{{ FlowObject constructor
FlowObject(FactorCompiler compiler,
RecursiveForm word)
{
this.compiler = compiler;
this.word = word;
expectedType = Object.class;
} //}}}
//{{{ push() method
protected void push(CodeVisitor mw)
throws Exception
{
throw new FactorCompilerException("Cannot push: " + this);
} //}}}
//{{{ push() method
public void push(CodeVisitor mw, Class type)
throws Exception
{
if(type == null)
throw new NullPointerException();
expectedType = (type.isPrimitive()
? FactorJava.getBoxingType(type)
: type);
if(mw != null)
{
generateToConversion(mw,type);
push(mw);
}
} //}}}
protected abstract void pop(CodeVisitor mw);
//{{{ pop() method
public void pop(CodeVisitor mw, Class type)
throws Exception
{
if(mw != null)
pop(mw);
Class actualType;
if(type.isPrimitive())
actualType = FactorJava.getBoxingType(type);
else
actualType = type;
// if we're looking for a subclass the expected type,
// specialize the expected type.
if(expectedType.isAssignableFrom(actualType))
expectedType = actualType;
// if we're looking for a superclass, that is ok too,
// eg we can generate every flow object as a
// java.lang.Object instance
else if(actualType.isAssignableFrom(expectedType))
/* do nothing */;
// otherwise, type error!
else
{
/* System.err.println(new TypeInferenceException(
this,expectedType,actualType)); */
}
if(mw != null)
generateFromConversion(mw,type);
} //}}}
//{{{ getConversionMethodName() method
/**
* Returns method name for converting an object to the given type.
* Only for primitives.
*/
private static String getConversionMethodName(Class type)
{
if(type.isPrimitive())
{
String name = type.getName();
return "to"
+ Character.toUpperCase(name.charAt(0))
+ name.substring(1);
}
else
return null;
} //}}}
//{{{ generateFromConversion() method
/**
* Unbox value at top of the stack.
*/
private static void generateFromConversion(CodeVisitor mw, Class type)
throws Exception
{
if(type == Object.class)
return;
String methodName = null;
if(type == Number.class)
methodName = "toNumber";
if(type == BigInteger.class)
methodName = "toBigInteger";
else if(type == String.class)
methodName = "toString";
else if(type == CharSequence.class)
methodName = "toCharSequence";
else if(type.isPrimitive())
methodName = getConversionMethodName(type);
else if(type == Class.class)
methodName = "toClass";
else if(type == FactorNamespace.class)
methodName = "toNamespace";
else if(type.isArray())
{
Class comp = type.getComponentType();
if(comp.isPrimitive())
{
methodName = getConversionMethodName(comp)
+ "Array";
}
else
methodName = "toArray";
}
if(methodName == null)
{
mw.visitTypeInsn(CHECKCAST,
type.getName()
.replace('.','/'));
}
else
{
mw.visitMethodInsn(INVOKESTATIC,
"factor/FactorJava",
methodName,
"(Ljava/lang/Object;)"
+ FactorJava.javaClassToVMClass(type));
}
} //}}}
//{{{ generateToConversionPre() method
/**
* Avoid having to deal with category 1/2 computational type
* distinction.
*/
public static void generateToConversionPre(CodeVisitor mw, Class type)
throws Exception
{
if(type == boolean.class)
return;
Class boxingType = FactorJava.getBoxingType(type);
if(boxingType != null)
{
String boxingName = boxingType.getName()
.replace('.','/');
mw.visitTypeInsn(NEW,boxingName);
mw.visitInsn(DUP);
}
} //}}}
//{{{ generateToConversion() method
/**
* Box return value, if needed.
*/
private static void generateToConversion(CodeVisitor mw, Class type)
throws Exception
{
if(type == boolean.class)
{
// this case is handled specially
mw.visitMethodInsn(INVOKESTATIC,
"factor/FactorJava",
"fromBoolean",
"(Z)Ljava/lang/Object;");
}
else
{
Class boxingType = FactorJava.getBoxingType(type);
if(boxingType != null)
{
String boxingName = boxingType.getName()
.replace('.','/');
mw.visitMethodInsn(INVOKESPECIAL,boxingName,
"<init>",
"(" + FactorJava.javaClassToVMClass(
type) + ")V");
}
}
} //}}}
//{{{ munge() method
/**
* Munging transforms the flow object such that it is now stored in
* a local variable and hence can be mutated by compiled code, however
* its original compileCallTo() semantics remain (for example, if it is
* a quotation).
*/
FlowObject munge(int base, int index,
FactorCompiler compiler,
RecursiveState recursiveCheck)
throws Exception
{
return new Result(index + base,compiler,recursiveCheck.last(),
expectedType);
} //}}}
//{{{ usingLocal() method
boolean usingLocal(int local)
{
return false;
} //}}}
//{{{ getLiteral() method
Object getLiteral()
throws FactorCompilerException
{
throw new FactorCompilerException("Cannot compile unless literal on stack: " + this);
} //}}}
//{{{ getLocal() method
/**
* @return -1 if the result is not store in a local variable.
*/
public int getLocal()
{
return -1;
} //}}}
//{{{ compileCallTo() method
/**
* Write code for evaluating this. Returns maximum JVM stack
* usage.
*/
public void compileCallTo(CodeVisitor mw, RecursiveState recursiveCheck)
throws Exception
{
throw new FactorCompilerException("Cannot compile call to non-literal quotation");
} //}}}
//{{{ getWord() method
/**
* Returns the word where this flow object originated from.
*/
public RecursiveForm getWord()
{
return word;
} //}}}
//{{{ getType() method
public Class getType()
{
return expectedType;
} //}}}
//{{{ toString() method
public String toString()
{
try
{
return FactorReader.unparseObject(getLiteral());
}
catch(Exception e)
{
throw new RuntimeException("Override toString() if your getLiteral() bombs!");
}
} //}}}
//{{{ clone() method
public Object clone()
{
// cannot be abstract, and cannot be left undefined!
throw new InternalError();
} //}}}
}

View File

@ -0,0 +1,80 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
import java.lang.reflect.*;
import java.util.*;
import org.objectweb.asm.*;
public class Literal extends FlowObject
{
private Object literal;
//{{{ Literal constructor
Literal(Object literal, FactorCompiler compiler,
RecursiveForm word)
{
super(compiler,word);
this.literal = literal;
expectedType = literal.getClass();
} //}}}
//{{{ pop() method
public void pop(CodeVisitor mw)
{
mw.visitFieldInsn(GETSTATIC,compiler.className,
compiler.literal(literal),
"Ljava/lang/Object;");
} //}}}
//{{{ getLiteral() method
Object getLiteral()
{
return literal;
} //}}}
//{{{ compileCallTo() method
/**
* Write code for evaluating this. Returns maximum JVM stack
* usage.
*/
public void compileCallTo(CodeVisitor mw, RecursiveState recursiveCheck)
throws Exception
{
throw new FactorCompilerException("Not a quotation: " + literal);
} //}}}
//{{{ clone() method
public Object clone()
{
return new Literal(literal,compiler,word);
} //}}}
}

99
factor/compiler/Null.java Normal file
View File

@ -0,0 +1,99 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
import java.lang.reflect.*;
import java.util.*;
import org.objectweb.asm.*;
public class Null extends FlowObject
{
//{{{ Null constructor
Null(FactorCompiler compiler, RecursiveForm word)
{
super(compiler,word);
} //}}}
//{{{ pop() Method
public void pop(CodeVisitor mw)
{
mw.visitInsn(ACONST_NULL);
} //}}}
//{{{ munge() method
/**
* Munging transforms the flow object such that it is now stored in
* a local variable and hence can be mutated by compiled code, however
* its original compileCallTo() semantics remain (for example, if it is
* a quotation).
*/
FlowObject munge(int base, int index,
FactorCompiler compiler,
RecursiveState recursiveCheck)
throws Exception
{
return new CompiledListResult(index + base,null,
compiler,recursiveCheck);
} //}}}
//{{{ getLiteral() method
Object getLiteral()
{
return null;
} //}}}
//{{{ compileCallTo() method
/**
* Write code for evaluating this. Returns maximum JVM stack
* usage.
*/
public void compileCallTo(CodeVisitor mw, RecursiveState recursiveCheck)
throws Exception
{
} //}}}
//{{{ equals() method
public boolean equals(Object o)
{
if(o instanceof Null)
return true;
else if(o instanceof CompiledList)
return ((CompiledList)o).getLiteral() == null;
else
return false;
} //}}}
//{{{ clone() method
public Object clone()
{
return new Null(compiler,word);
} //}}}
}

View File

@ -0,0 +1,129 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
import org.objectweb.asm.Label;
public class RecursiveForm implements PublicCloneable
{
/**
* Word represented by this form.
*/
public final FactorWord word;
/**
* The effect on entry into this form.
* (?) only for immediates
*/
public StackEffect effect;
/**
* Base case of recursive form. This is left-composed with the
* effect above.
*/
public StackEffect baseCase;
/**
* Is the word being compiled right now?
*/
public boolean active;
/**
* Name of class to call to recurse.
*/
public String className;
/**
* Class loader containing this definition.
*/
public FactorClassLoader loader;
/**
* Name of method to call to recurse.
*/
public String method;
/**
* Are we compiling the last factor in the word right now?
*/
public boolean tail;
/**
* A label to jump to the beginning of the definition.
*/
public Label label = new Label();
/**
* See RecursiveState.lastCallable().
*/
public boolean callable = true;
/**
* The containing recursive form, lexically.
*/
public RecursiveForm next;
public RecursiveForm(FactorWord word, StackEffect effect,
String className, FactorClassLoader loader,
String method)
{
this.word = word;
this.effect = effect;
this.className = className;
this.loader = loader;
this.method = method;
}
public RecursiveForm(RecursiveForm form)
{
this.word = form.word;
this.effect = form.effect;
this.baseCase = form.baseCase;
this.effect = form.effect;
this.className = form.className;
this.loader = form.loader;
this.method = form.method;
}
public String toString()
{
return word.toString() + ",base=" + baseCase
+ ",effect=" + effect
+ (active?",active":"")
+ (tail?",tail":"")
+ "; " + className + "." + method + "()";
}
public Object clone()
{
return new RecursiveForm(this);
}
}

View File

@ -0,0 +1,148 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
public class RecursiveState implements PublicCloneable
{
private Cons words;
//{{{ RecursiveState constructor
public RecursiveState()
{
} //}}}
//{{{ RecursiveState constructor
public RecursiveState(RecursiveState clone)
{
if(clone.words != null)
words = clone.words;
} //}}}
//{{{ add() method
public void add(FactorWord word, StackEffect effect,
String className, FactorClassLoader loader, String method)
{
add(word,effect,className,loader,method,
words == null ? null : last());
} //}}}
//{{{ add() method
public void add(FactorWord word, StackEffect effect,
String className, FactorClassLoader loader, String method,
RecursiveForm next)
{
if(get(word) != null)
{
throw new RuntimeException("Calling add() twice on " + word);
}
RecursiveForm newForm = new RecursiveForm(
word,effect,className,loader,method);
words = new Cons(newForm,words);
newForm.next = next;
} //}}}
//{{{ remove() method
public void remove(FactorWord word)
{
//System.err.println(this + ": removing " + word);
if(last().word != word)
throw new RuntimeException("Expected " + word + ", found " + last().word);
words = words.next();
} //}}}
//{{{ get() method
public RecursiveForm get(FactorWord word)
{
if(words != null)
{
RecursiveForm iter = last();
while(iter != null)
{
if(iter.word == word)
return iter;
iter = iter.next;
}
}
return null;
} //}}}
//{{{ last() method
public RecursiveForm last()
{
return (RecursiveForm)words.car;
} //}}}
//{{{ lastCallable() method
public RecursiveForm lastCallable()
{
RecursiveForm word = (RecursiveForm)words.car;
while(word != null)
{
if(word.callable)
return word;
word = word.next;
}
return null;
} //}}}
//{{{ allTails() method
/**
* Returns if all forms from the given form upward are at their tail,
* so that we can do a direct GOTO to the given form to recurse on it.
*/
public boolean allTails(RecursiveForm form)
{
Cons iter = words;
for(;;)
{
if(!((RecursiveForm)iter.car).tail)
return false;
if(iter.car == form)
return true;
iter = iter.next();
}
} //}}}
//{{{ toString() method
public String toString()
{
return FactorReader.unparseObject(words);
} //}}}
//{{{ clone() method
public Object clone()
{
return new RecursiveState(this);
} //}}}
}

101
factor/compiler/Result.java Normal file
View File

@ -0,0 +1,101 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
import java.util.*;
import org.objectweb.asm.*;
public class Result extends FlowObject
{
private int local;
private boolean input;
public Result(int local, FactorCompiler compiler,
RecursiveForm word, Class type)
{
this(local,compiler,word,type,false);
}
/**
* @param input An input result contains a parameter passed to a
* compiled word's core() method.
*/
public Result(int local, FactorCompiler compiler,
RecursiveForm word, Class type,
boolean input)
{
super(compiler,word);
this.local = local;
if(type == null)
throw new NullPointerException();
this.expectedType = (type.isPrimitive()
? FactorJava.getBoxingType(type)
: type);
this.input = input;
}
public void push(CodeVisitor mw)
throws Exception
{
mw.visitVarInsn(ASTORE,local);
}
public void pop(CodeVisitor mw)
{
mw.visitVarInsn(ALOAD,local);
}
public int getLocal()
{
return local;
}
boolean usingLocal(int local)
{
return (this.local == local);
}
public boolean isInput()
{
return input;
}
public String toString()
{
return expectedType.getName() + "#" + local;
}
//{{{ clone() method
public Object clone()
{
return new Result(local,compiler,word,expectedType);
} //}}}
}

View File

@ -0,0 +1,194 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
import java.util.*;
public class StackEffect implements PublicCloneable, FactorExternalizable
{
public int inD;
public Class[] inDtypes;
public int outD;
public Class[] outDtypes;
public int inR;
public Class[] inRtypes;
public int outR;
public Class[] outRtypes;
//{{{ StackEffect constructor
public StackEffect() {}
//}}}
//{{{ StackEffect constructor
public StackEffect(int inD, int outD, int inR, int outR)
{
this.inD = inD;
this.outD = outD;
this.inR = inR;
this.outR = outR;
} //}}}
//{{{ StackEffect constructor
public StackEffect(int inD, Class[] inDtypes,
int outD, Class[] outDtypes,
int inR, Class[] inRtypes,
int outR, Class[] outRtypes)
{
this.inD = inD;
this.inDtypes = inDtypes;
this.outD = outD;
this.outDtypes = outDtypes;
this.inR = inR;
this.inRtypes = inRtypes;
this.outR = outR;
this.outRtypes = outRtypes;
} //}}}
//{{{ compose() method
public static StackEffect compose(StackEffect first,
StackEffect second)
{
if(first == null || second == null)
return null;
int inD = first.inD + Math.max(0,second.inD - first.outD);
int outD = second.outD + Math.max(0,first.outD - second.inD);
int inR = first.inR + Math.max(0,second.inR - first.outR);
int outR = second.outR + Math.max(0,first.outR - second.inR);
return new StackEffect(inD,outD,inR,outR);
} //}}}
//{{{ decompose() method
/**
* Returns a stack effect E such that compose(first,E) == second.
*/
public static StackEffect decompose(StackEffect first,
StackEffect second)
{
if(first == null)
throw new NullPointerException("first == null");
if(second == null)
throw new NullPointerException("second == null");
if(second.inD < first.inD || second.inR < first.inR)
throw new IllegalArgumentException();
return new StackEffect(
first.outD + second.inD - first.inD,
second.outD,
first.outR + second.inR - first.inR,
second.outR);
} //}}}
//{{{ getCorePrototype() method
public String getCorePrototype()
{
StringBuffer signatureBuf = new StringBuffer(
"(Lfactor/FactorInterpreter;");
for(int i = 0; i < inD + inR; i++)
signatureBuf.append("Ljava/lang/Object;");
if(outD + outR != 1)
signatureBuf.append(")V");
else
signatureBuf.append(")Ljava/lang/Object;");
return signatureBuf.toString();
} //}}}
//{{{ equals() method
public boolean equals(Object o)
{
if(!(o instanceof StackEffect))
return false;
StackEffect effect = (StackEffect)o;
return effect.inD == inD
&& effect.outD == outD
&& effect.inR == inR
&& effect.outR == outR;
} //}}}
//{{{ hashCode() method
public int hashCode()
{
return inD + inR + outD + outR;
} //}}}
//{{{ typespec() method
private String typespec(int index, Class[] types)
{
if(types != null)
return types[index].getName();
else
return "X";
} //}}}
//{{{ toString() method
public String toString()
{
StringBuffer buf = new StringBuffer("( ");
for(int i = 0; i < inD; i++)
{
buf.append(typespec(i,inDtypes));
buf.append(' ');
}
for(int i = 0; i < inR; i++)
{
buf.append("r:");
buf.append(typespec(i,inRtypes));
buf.append(' ');
}
buf.append("--");
for(int i = 0; i < outD; i++)
{
buf.append(' ');
buf.append(typespec(i,outDtypes));
}
for(int i = 0; i < outR; i++)
{
buf.append(" r:");
buf.append(typespec(i,outRtypes));
}
buf.append(" )");
return buf.toString();
} //}}}
//{{{ clone() method
public Object clone()
{
return new StackEffect(inD,inDtypes,
outD,outDtypes,
inR,inRtypes,
outR,outRtypes);
} //}}}
}

View File

@ -0,0 +1,42 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.compiler;
import factor.*;
public class TypeInferenceException extends FactorCompilerException
{
public TypeInferenceException(Object obj, Class expected, Class got)
{
super(obj + "\n"
+ "Expected: " + expected + "\n"
+ "Got: " + got);
}
}

View File

@ -0,0 +1 @@
!a;7980;7980

View File

@ -0,0 +1,48 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.db;
public class BTreeException extends PersistenceException
{
public BTreeException()
{
super();
}
public BTreeException(String msg)
{
super(msg);
}
public BTreeException(String msg, Throwable t)
{
super(msg,t);
}
}

277
factor/db/BTreeNode.java Normal file
View File

@ -0,0 +1,277 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.db;
import java.io.*;
/**
* A B-tree index node.
*
* Format on disk is:
* - 1 byte: leaf flag for sanity check
* - 1 byte: num children
* - 8 * order bytes: keys
* - 8 * order bytes: pointers
*/
class BTreeNode
{
// these two are not saved to disk.
byte order;
long offset;
boolean leaf;
int children;
long[] keys;
/**
* In the nodes a the bottom of the tree, these are pointers inside
* the data file; otherwise they are pointers inside the index file.
*/
long[] pointers;
/**
* Set to true if the node changed and should be saved to disk.
*/
boolean dirty;
//{{{ getSize() method
/**
* Returns the size in bytes of a node with the given order.
*/
public static int getSize(int order)
{
return 2 + order * 16;
} //}}}
//{{{ BTreeNode constructor
BTreeNode(byte order, long offset)
{
this.order = order;
this.offset = offset;
this.keys = new long[order];
this.pointers = new long[order];
} //}}}
//{{{ unpackLong() method
private long unpackLong(byte[] data, int offset)
{
return (((long)data[offset + 0] << 56) +
((long)(data[offset + 1] & 255) << 48) +
((long)(data[offset + 2] & 255) << 40) +
((long)(data[offset + 3] & 255) << 32) +
((long)(data[offset + 4] & 255) << 24) +
((data[offset + 5] & 255) << 16) +
((data[offset + 6] & 255) << 8) +
((data[offset + 7] & 255) << 0));
} //}}}
//{{{ read() method
void read(RandomAccessFile in) throws IOException
{
in.seek(offset);
byte[] data = new byte[getSize(order)];
in.readFully(data);
int pos = 0;
leaf = (data[pos++] != 0);
children = data[pos++];
for(int i = 0; i < children; i++)
{
keys[i] = unpackLong(data,pos);
pos += 8;
}
pos += 8 * (order - children);
for(int i = 0; i < children; i++)
{
pointers[i] = unpackLong(data,pos);
pos += 8;
}
} //}}}
//{{{ packLong() method
private void packLong(long num, byte[] data, int offset)
{
data[offset + 0] = (byte)(num >>> 56);
data[offset + 1] = (byte)(num >>> 48);
data[offset + 2] = (byte)(num >>> 40);
data[offset + 3] = (byte)(num >>> 32);
data[offset + 4] = (byte)(num >>> 24);
data[offset + 5] = (byte)(num >>> 16);
data[offset + 6] = (byte)(num >>> 8);
data[offset + 7] = (byte)(num >>> 0);
} //}}}
//{{{ write() method
void write(RandomAccessFile out) throws IOException
{
byte[] data = new byte[getSize(order)];
int pos = 0;
data[pos++] = (byte)(leaf ? 1 : 0);
data[pos++] = (byte)children;
for(int i = 0; i < children; i++)
{
packLong(keys[i],data,pos);
pos += 8;
}
pos += 8 * (order - children);
for(int i = 0; i < children; i++)
{
packLong(pointers[i],data,pos);
pos += 8;
}
out.seek(offset);
out.write(data);
} //}}}
//{{{ add() method
/**
* @exception BTreeException on various errors that should not occur
*/
void add(long key, long pointer) throws BTreeException
{
if(BTreeStore.DEBUG)
{
System.err.println("add " + key + "=" + pointer + " to");
System.err.println(this);
}
if(children == order)
throw new BTreeException("Node full");
int position = lookupInternal(key);
if(keys[position] == key && position != children)
throw new BTreeException("Adding twice");
// shift the keys along
for(int i = children - 1; i >= position; i--)
{
keys[i + 1] = keys[i];
pointers[i + 1] = pointers[i];
}
keys[position] = key;
pointers[position] = pointer;
children++;
} //}}}
//{{{ lookupExternal() method
int lookupExternal(long key)
{
for(int i = 0; i < children; i++)
{
if(key == keys[i])
return i;
}
return -1;
} //}}}
//{{{ lookupInternal() method
int lookupInternal(long key)
{
for(int i = 0; i < children; i++)
{
if(key <= keys[i])
return i;
}
return children;
} //}}}
//{{{ updateHighest() method
void updateHighest(long key)
{
keys[children - 1] = key;
} //}}}
//{{{ split() method
int split(BTreeNode x, BTreeNode y, long key, boolean leaf)
{
x.leaf = leaf;
y.leaf = leaf;
int median = children / 2;
x.children = children - median;
y.children = median;
if(order % 2 == 0)
median--;
for(int i = 0; i < x.children; i++)
{
x.keys[i] = keys[i];
x.pointers[i] = pointers[i];
}
for(int i = 0; i < y.children; i++)
{
y.keys[i] = keys[x.children + i];
y.pointers[i] = pointers[x.children + i];
}
return median;
} //}}}
//{{{ toString() method
public String toString()
{
StringBuffer buf = new StringBuffer(leaf ? "#{ " : "{ ");
for(int i = 0; i < children; i++)
{
buf.append(keys[i]);
buf.append("=");
buf.append(pointers[i]);
buf.append(" ");
}
return buf.append("}").toString();
} //}}}
//{{{ dump() method
public void dump(int indent)
{
StringBuffer buf = new StringBuffer();
for(int i = 0; i < indent; i++)
buf.append(' ');
buf.append(toString());
System.err.println(buf);
} //}}}
}

653
factor/db/BTreeStore.java Normal file
View File

@ -0,0 +1,653 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.db;
import java.io.*;
/**
* A store that puts all records inside a single file on disk, indexed
* by a B-tree.
*
* B-Tree index header:
*
* INDEX_MAGIC
* 8 bytes - offset of root
* 1 byte - order
* 1 byte - dirty - set when loading, cleared when closing
* 4 bytes - height
* 8 bytes - maximum key
*
* Each record in data file:
*
* 8 bytes - key
* 4 bytes - length
* ... data follows
*
* Records in index file - see BTreeNode
*/
public class BTreeStore implements Store
{
public static boolean DEBUG = false;
private static final int INDEX_MAGIC = 0xcabba4e4;
private static final int DATA_MAGIC = 0xdeadbeef;
private static final int DEFAULT_ORDER = 64;
private File indexFile;
private RandomAccessFile index;
private File dataFile;
private RandomAccessFile data;
// header has INDEX_MAGIC + these 3 values packed
private long rootPointer;
private byte order;
private byte dirty; // non-zero if dirty
private int height; // height of the tree
private long maximumKey = -1;
// root is always in memory
private BTreeNode root;
private BTreeNode[] lookup;
// next offset in the index file
private long nextOffset;
//{{{ BTreeStore() method
/**
* Constructor used by FactorInterpreter when parsing -db parameter.
*/
public BTreeStore(String spec)
throws IOException, PersistenceException
{
int index = spec.lastIndexOf(':');
byte order;
if(index == -1)
order = DEFAULT_ORDER;
else
{
order = Byte.parseByte(spec.substring(index + 1));
spec = spec.substring(0,index);
}
init(new File(spec),order,spec.startsWith("ro:"));
} //}}}
//{{{ BTreeStore() method
public BTreeStore(File dataFile, byte order, boolean readOnly)
throws IOException, PersistenceException
{
init(dataFile,order,readOnly);
} //}}}
//{{{ init() method
private void init(File dataFile, byte order, boolean readOnly)
throws IOException, PersistenceException
{
if(order <= 3)
throw new BTreeException("order must be > 3, < 127");
this.indexFile = new File(dataFile.getParent(),
dataFile.getName() + ".index");
this.dataFile = dataFile;
boolean indexFileExists = indexFile.exists();
boolean dataFileExists = dataFile.exists();
index = new RandomAccessFile(indexFile,
readOnly ? "r" : "rw");
data = new RandomAccessFile(dataFile,
readOnly ? "r" : "rw");
try
{
if(!indexFileExists || !dataFileExists)
{
this.order = order;
writeHeader();
nextOffset = index.length();
if(dataFileExists)
createIndex();
}
else
{
readHeader();
if(rootPointer != 0)
root = readNode(rootPointer);
}
}
catch(IOException e)
{
index.close();
data.close();
throw e;
}
catch(PersistenceException e)
{
index.close();
data.close();
throw e;
}
} //}}}
//{{{ writeHeader() method
private void writeHeader() throws IOException
{
index.seek(0);
index.writeInt(INDEX_MAGIC);
index.writeByte(dirty);
index.writeLong(rootPointer);
index.writeByte(order);
index.writeInt(height);
} //}}}
//{{{ readHeader() method
private void readHeader() throws IOException, PersistenceException
{
index.seek(0);
if(index.readInt() != INDEX_MAGIC)
throw new BTreeException("Bad magic number in index file");
dirty = index.readByte();
rootPointer = index.readLong();
order = index.readByte();
if(order < 3)
throw new BTreeException("Bad order");
height = index.readInt();
nextOffset = index.length();
} //}}}
//{{{ createIndex() method
private void createIndex() throws IOException, PersistenceException
{
System.err.println("Re-creating index...");
data.seek(0);
for(;;)
{
long offset = data.getFilePointer();
if(offset == data.length())
{
// we're done
break;
}
int magic = data.readInt();
if(magic != DATA_MAGIC)
throw new BTreeException(magic + " != " + DATA_MAGIC);
long key = data.readLong();
int length = data.readInt();
saveToIndex(key,offset);
data.skipBytes(length);
}
System.err.println("... done");
} //}}}
//{{{ readNode() method
private BTreeNode readNode(long offset) throws IOException
{
BTreeNode node = new BTreeNode(order,offset);
node.read(index);
return node;
} //}}}
//{{{ writeNode() method
private void writeNode(BTreeNode node)
throws IOException, BTreeException
{
if(DEBUG)
{
System.err.println("node.offset=" + node.offset
+ ",index.length()=" + index.length());
if(node.offset < index.length())
{
BTreeNode existing = readNode(node.offset);
if(existing.leaf != node.leaf
// if children is zero, empty space!
&& existing.children != 0)
{
throw new BTreeException("Overwriting "
+ existing + " with "
+ node);
}
}
}
node.dirty = false;
node.write(index);
} //}}}
//{{{ dump() method
private void dump(BTreeNode node, int indent)
throws IOException
{
node.dump(indent);
if(node.leaf)
return;
indent++;
for(int i = 0; i < node.children; i++)
dump(readNode(node.pointers[i]),indent);
} //}}}
//{{{ dump() method
private void dump()
throws IOException
{
System.err.println("<<<< dump");
if(root != null)
dump(root,0);
System.err.println(">>>>");
} //}}}
//{{{ checkOffset() method
private void checkOffset(long offset)
throws IOException, BTreeException
{
if(offset > nextOffset)
throw new BTreeException("Invalid pointer: " + offset + " > " + nextOffset);
} //}}}
//{{{ lookup() method
/**
* Look up the given key, traversing down the tree. Returns an array
* of all nodes, from the root down, that were traversed.
*
* Note that when adding nodes to the B-tree, I 'cheat' by splitting on
* the way down any nodes with order == children. While this ends up
* splitting a few more nodes than strictly necessary, it avoids a
* second traversal up the tree, and simplifies the code.
* @param add in add mode, nodes along the way are split if they would
* overflow.
* @param newMaximum if add is true *and* this is true, a new maximal
* node is being added, so the rightmost key in each node along the way
* needs to be updated.
*/
private void lookup(long key, boolean add, boolean newMaximum)
throws IOException, BTreeException
{
if(DEBUG)
System.err.println("HEIGHT = " + height);
if(lookup == null || lookup.length != height)
{
saveLookup();
lookup = new BTreeNode[height];
}
if(height != 0)
{
/*
if this is true, a new level has been added (ie, the
root was split). we return this value instead of
incrementing the levels variable directly, since the old
value of 'levels' is needed until the end of the method.
*/
boolean newLevel = false;
lookup[0] = root;
if(add)
{
if(possiblySplitAndUpdateMax(0,newMaximum,key))
newLevel = true;
}
for(int i = 1; i < height; i++)
{
if(DEBUG)
System.err.println("Level " + i);
BTreeNode node = lookup[i - 1];
if(node.leaf)
throw new BTreeException("A leaf: " + node);
int next = node.lookupInternal(key);
if(next == node.children)
next--;
// read this node, and split it if we need to.
long offset = node.pointers[next];
checkOffset(offset);
// is the node already loaded?
if(lookup[i] == null)
lookup[i] = readNode(offset);
else if(lookup[i].offset != offset)
{
if(lookup[i].dirty)
writeNode(lookup[i]);
lookup[i] = readNode(offset);
}
if(add)
{
if(possiblySplitAndUpdateMax(i,newMaximum,key))
newLevel = true;
}
}
// now that the above loop (indexed by 'levels') is
// done, we can increment the variable, and update the
// index header on disk.
if(newLevel)
{
height++;
writeHeader();
}
}
BTreeNode last = lookup[lookup.length - 1];
if(!last.leaf)
throw new BTreeException("Not a leaf: " + last);
if(DEBUG)
System.err.println("NOW height=" + height);
} //}}}
//{{{ nextOffset() method
private long nextOffset()
{
long ret = nextOffset;
nextOffset += BTreeNode.getSize(order);
return ret;
} //}}}
//{{{ possiblySplitAndUpdateMax() method
/**
* The most important method of the B-tree class.
* If the number of keys in the node is equal to the order, split the
* node, and update the maximum key if necessary.
*/
private boolean possiblySplitAndUpdateMax(
int level,
boolean newMaximum,
long key)
throws IOException, BTreeException
{
BTreeNode node = lookup[level];
long offset = node.offset;
// see comment in findLeaf() to see why this is needed.
boolean newLevel = false;
// will we split?
boolean split = (node.children == order);
if(split)
{
BTreeNode left = new BTreeNode(order,0);
BTreeNode right = new BTreeNode(order,0);
// split the node along the median into left and right
// side of median. store the left side in a new index
// record.
int median = node.split(left,right,key,
level == height - 1);
if(DEBUG)
{
System.err.println("Splitting " + node);
System.err.println("==> left = " + left);
System.err.println("==> right = " + right);
}
long medianKey = node.keys[median];
long highestInLeft = medianKey;
long leftOffset = nextOffset();
if(DEBUG)
System.err.println("leftOffset=" + leftOffset);
// the key we're adding might be in the left or right
// side of the split, so act accordingly.
if(key < medianKey)
{
if(DEBUG)
System.err.println("node=left");
node = left;
right.offset = offset;
writeNode(right);
offset = leftOffset;
}
else
{
if(DEBUG)
System.err.println("node=right");
left.offset = leftOffset;
writeNode(left);
node = right;
}
if(level == 0)
{
if(DEBUG)
System.err.println("ROOT SPLIT");
// we just split the root. create a new root
BTreeNode newRoot = new BTreeNode(order,
nextOffset());
checkOffset(leftOffset);
newRoot.add(highestInLeft,leftOffset);
checkOffset(rootPointer);
newRoot.add(maximumKey,rootPointer);
writeNode(newRoot);
rootPointer = newRoot.offset;
root = newRoot;
newLevel = true;
}
else
{
if(DEBUG)
System.err.println("NODE SPLIT");
// we just split a non-root node, update its
// parent.
BTreeNode parent = lookup[level - 1];
// note that this will never fail, since if the
// parent previously had order == numKeys, it
// will already have been split
checkOffset(leftOffset);
parent.add(highestInLeft,leftOffset);
parent.dirty = true;
}
node.dirty = true;
}
// is this key we're adding a new maximum?
if(newMaximum && level != height - 1)
{
node.dirty = true;
node.updateHighest(key);
}
// store node back in the 'nodes' array, after any changes have
// been made.
lookup[level] = node;
node.offset = offset;
return newLevel;
} //}}}
//{{{ exists() method
public boolean exists(long key) throws IOException, BTreeException
{
// empty tree?
if(height == 0)
return false;
lookup(key,false,false);
return lookup[height - 1].lookupExternal(key) != -1;
} //}}}
//{{{ loadFromData() method
/**
* Load a record from the data file.
*/
private byte[] loadFromData(long offset, long key)
throws IOException, BTreeException
{
data.seek(offset);
int magic = data.readInt();
if(magic != DATA_MAGIC)
throw new BTreeException(magic + " != " + DATA_MAGIC);
if(data.readLong() != key)
throw new BTreeException("Record " + key + " not stored at " + offset);
int length = data.readInt();
byte[] value = new byte[length];
data.readFully(value);
return value;
} //}}}
//{{{ loadFromStore() method
/**
* Load a record from the database with the given ID.
*/
public byte[] loadFromStore(long key)
throws IOException, PersistenceException
{
lookup(key,false,false);
BTreeNode last = lookup[height - 1];
int index = last.lookupExternal(key);
if(index == -1)
throw new NoSuchRecordException(key);
long offset = last.pointers[index];
return loadFromData(offset,key);
} //}}}
//{{{ saveToData() method
/**
* Append a record to the data file.
*/
private long saveToData(long key, byte[] value) throws IOException
{
long offset = data.length();
data.seek(offset);
data.writeInt(DATA_MAGIC);
data.writeLong(key);
data.writeInt(value.length);
data.write(value);
return offset;
} //}}}
//{{{ saveToIndex() method
private void saveToIndex(long key, long offset)
throws IOException, PersistenceException
{
if(DEBUG)
dump();
// Do we need to update the maximum keys as we go along?
boolean newMaximum = (key > maximumKey);
if(newMaximum)
maximumKey = key;
BTreeNode leaf;
if(rootPointer == 0)
{
rootPointer = nextOffset();
root = new BTreeNode(order,rootPointer);
root.leaf = true;
leaf = root;
height = 1;
}
else
{
int last = height - 1;
lookup(key,true,newMaximum);
leaf = lookup[last];
}
// add the node to the leaf, write the leaf back to disk.
int existing = leaf.lookupExternal(key);
if(existing == -1)
{
// new record
leaf.add(key,offset);
}
else
{
// updating existing record
leaf.pointers[existing] = offset;
}
leaf.dirty = true;
writeNode(leaf);
} //}}}
//{{{ saveToStore() method
/**
* Save a record to the database with the given ID.
*/
public void saveToStore(long key, byte[] value)
throws IOException, PersistenceException
{
long offset = saveToData(key,value);
saveToIndex(key,offset);
} //}}}
//{{{ saveLookup() method
/**
* Save all nodes in the lookup array.
*/
private void saveLookup() throws IOException, BTreeException
{
if(lookup == null)
return;
for(int i = 0; i < lookup.length; i++)
{
if(lookup[i].dirty)
writeNode(lookup[i]);
}
} //}}}
//{{{ close() method
/**
* Close the store.
*/
public void close() throws IOException, BTreeException
{
saveLookup();
index.close();
data.close();
} //}}}
}

119
factor/db/FileStore.java Normal file
View File

@ -0,0 +1,119 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.db;
import java.io.*;
/**
* A store that puts all records as files inside a directory.
*/
public class FileStore implements Store
{
private File directory;
//{{{ FileStore() method
public FileStore(String directory)
{
this(new File(directory));
} //}}}
//{{{ FileStore() method
public FileStore(File directory)
{
this.directory = directory;
directory.mkdirs();
} //}}}
//{{{ exists() method
public boolean exists(long id)
{
return new File(directory,String.valueOf(id)).exists();
} //}}}
//{{{ loadFromStore() method
/**
* Load a record from the database with the given ID.
*/
public byte[] loadFromStore(long key)
throws IOException, PersistenceException
{
if(!exists(key))
throw new NoSuchRecordException(key);
return readFile(new File(directory,String.valueOf(key)));
} //}}}
//{{{ readFile() method
private byte[] readFile(File file) throws IOException
{
DataInputStream in = new DataInputStream(
new FileInputStream(file));
byte[] buf = new byte[(int)file.length()];
try
{
in.readFully(buf);
}
finally
{
in.close();
}
return buf;
} //}}}
//{{{ saveToStore() method
/**
* Save a record to the database with the given ID.
*/
public void saveToStore(long key, byte[] value) throws IOException
{
writeFile(new File(directory,String.valueOf(key)),value);
} //}}}
//{{{ writeFile() method
private void writeFile(File file, byte[] content) throws IOException
{
FileOutputStream out = new FileOutputStream(file);
try
{
out.write(content,0,content.length);
}
finally
{
out.close();
}
} //}}}
//{{{ close() method
/**
* Close the store.
*/
public void close() throws IOException, PersistenceException
{
} //}}}
}

View File

@ -0,0 +1,38 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.db;
public class NoSuchRecordException extends PersistenceException
{
public NoSuchRecordException(long id)
{
super("No such record: " + id);
}
}

View File

@ -0,0 +1,50 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.db;
import factor.FactorException;
public class PersistenceException extends FactorException
{
public PersistenceException()
{
super();
}
public PersistenceException(String msg)
{
super(msg);
}
public PersistenceException(String msg, Throwable t)
{
super(msg,t);
}
}

View File

@ -0,0 +1,116 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.db;
import factor.Cons;
import factor.FactorInterpreter;
/**
* A simple wrapper around a byte array stored in the object database.
*/
public class PersistentBinary implements PersistentObject
{
private Workspace workspace;
private long id;
private byte[] bytes;
//{{{ PersistentBinary constructor
public PersistentBinary(Workspace workspace, byte[] bytes)
throws Exception
{
this(workspace,workspace == null ? 0L : workspace.nextID());
this.bytes = bytes;
} //}}}
//{{{ PersistentBinary constructor
public PersistentBinary(Workspace workspace, long id) throws Exception
{
this.workspace = workspace;
this.id = id;
if(workspace != null && id != 0L)
workspace.put(this);
} //}}}
//{{{ getBytes() method
public byte[] getBytes()
{
return bytes;
} //}}}
//{{{ getWorkspace() method
/**
* Each persistent object is stored in one workspace only.
*/
public Workspace getWorkspace()
{
return workspace;
} //}}}
//{{{ getID() method
/**
* Each persistent object has an associated ID.
*/
public long getID()
{
return id;
} //}}}
//{{{ pickle() method
/**
* Each persistent object can turn itself into a byte array.
*/
public byte[] pickle()
{
return bytes;
} //}}}
//{{{ unpickle() method
/**
* Each persistent object can set its state to that in a byte array.
*/
public void unpickle(byte[] bytes, int offset)
{
if(offset == 0)
this.bytes = bytes;
else
{
int len = bytes.length - offset;
this.bytes = new byte[len];
System.arraycopy(bytes,offset,this.bytes,0,len);
}
} //}}}
//{{{ getReferences() method
public Cons getReferences()
{
return null;
} //}}}
}

View File

@ -0,0 +1,38 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.db;
import factor.FactorInterpreter;
/**
* Any objects implementing this interface are written to the database
* as f.
*/
public interface PersistentIgnore {}

View File

@ -0,0 +1,63 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.db;
import factor.Cons;
import factor.FactorInterpreter;
public interface PersistentObject
{
/**
* Each persistent object is stored in one workspace only.
*/
public Workspace getWorkspace();
/**
* Each persistent object has an associated ID.
*/
public long getID();
/**
* Each persistent object can turn itself into a byte array.
*/
public byte[] pickle()
throws PersistenceException;
/**
* Each persistent object can set its state to that in a byte array.
*/
public void unpickle(byte[] bytes, int offset)
throws PersistenceException;
/**
* Used for storage compaction.
*/
public Cons getReferences();
}

View File

@ -0,0 +1,86 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.db;
import factor.FactorLib;
import java.io.*;
/**
* A store that reads records from the class path.
*/
public class ResourceStore implements Store
{
private String directory;
//{{{ ResourceStore() method
public ResourceStore(String directory)
{
this.directory = directory;
} //}}}
//{{{ exists() method
public boolean exists(long key)
{
return getClass().getResource(directory + key) != null;
} //}}}
//{{{ loadFromStore() method
/**
* Load a record from the database with the given ID.
*/
public byte[] loadFromStore(long key)
throws IOException, PersistenceException
{
if(!exists(key))
throw new NoSuchRecordException(key);
ByteArrayOutputStream out = new ByteArrayOutputStream();
FactorLib.copy(getClass().getResourceAsStream(
directory + key),out);
return out.toByteArray();
} //}}}
//{{{ saveToStore() method
/**
* Save a record to the database with the given ID.
*/
public void saveToStore(long key, byte[] value) throws IOException
{
throw new IOException("Read only store");
} //}}}
//{{{ close() method
/**
* Close the store.
*/
public void close()
{
} //}}}
}

View File

@ -0,0 +1,124 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.db;
import factor.compiler.*;
import factor.*;
import java.util.*;
/**
* An object that persists to a name/value list.
*/
public class SimplePersistentObject implements FactorObject,
PersistentObject
{
protected Workspace workspace;
protected long id;
protected Table table;
//{{{ SimplePersistentObject constructor
public SimplePersistentObject()
{
try
{
table = new Table(this);
}
catch(Exception e)
{
throw new RuntimeException(e);
}
} //}}}
//{{{ SimplePersistentObject constructor
public SimplePersistentObject(Workspace workspace, long id)
throws Exception
{
this.workspace = workspace;
this.id = id;
table = new Table(this,workspace,0L);
} //}}}
//{{{ SimplePersistentObject constructor
public SimplePersistentObject(Workspace workspace) throws Exception
{
this(workspace,workspace == null ? 0L : workspace.nextID());
} //}}}
//{{{ getWorkspace() method
/**
* Each persistent object is stored in one workspace only.
*/
public Workspace getWorkspace()
{
return workspace;
} //}}}
//{{{ getID() method
/**
* Each persistent object has an associated ID.
*/
public long getID()
{
return id;
} //}}}
//{{{ pickle() method
/**
* Each persistent object can turn itself into a byte array.
*/
public byte[] pickle()
throws PersistenceException
{
return table.pickle();
} //}}}
//{{{ unpickle() method
/**
* Each persistent object can set its state to that in a byte array.
*/
public void unpickle(byte[] bytes, int offset)
throws PersistenceException
{
table.unpickle(bytes,offset);
} //}}}
//{{{ getNamespace() method
public FactorNamespace getNamespace()
throws Exception
{
return table;
} //}}}
//{{{ getReferences() method
public Cons getReferences()
{
return table.getReferences();
} //}}}
}

59
factor/db/Store.java Normal file
View File

@ -0,0 +1,59 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.db;
import java.io.IOException;
/**
* The low-level interface to a persistent store.
* It can retreive and store string values, keyed by string identifiers.
*/
public interface Store
{
public boolean exists(long id)
throws IOException, PersistenceException;
/**
* Load a record from the database with the given ID.
*/
public byte[] loadFromStore(long key)
throws IOException, PersistenceException;
/**
* Save a record to the database with the given ID.
*/
public void saveToStore(long key, byte[] value)
throws IOException, PersistenceException;
/**
* Close the store.
*/
public void close() throws IOException, PersistenceException;
}

381
factor/db/Table.java Normal file
View File

@ -0,0 +1,381 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.db;
import factor.*;
import java.io.*;
import java.util.Map;
import java.util.TreeMap;
/**
* A table is a persistent namespace.
*
* The picked format is as follows:
*
* 4 bytes -- number of rows
* Each row:
* 4 bytes -- length of name
* x bytes -- name
* 4 bytes -- length of value
* x bytes -- unparsed value
* 1 byte -- newline
*
* All strings are stored as UTF8.
*/
public class Table extends FactorNamespace implements PersistentObject
{
public static boolean DEBUG = false;
public static final String ENCODING = "UTF8";
private Workspace workspace;
private long id;
//{{{ Table constructor
public Table()
{
} //}}}
//{{{ Table constructor
public Table(Workspace workspace) throws Exception
{
this(workspace,workspace == null ? 0L : workspace.nextID());
} //}}}
//{{{ Table constructor
public Table(Workspace workspace, long id) throws Exception
{
this.workspace = workspace;
this.id = id;
if(workspace != null && id != 0L)
workspace.put(this);
} //}}}
//{{{ Table constructor
public Table(Object obj) throws Exception
{
super(obj);
} //}}}
//{{{ Table constructor
public Table(Object obj, Workspace workspace) throws Exception
{
this(obj,workspace,workspace == null ? 0L : workspace.nextID());
} //}}}
//{{{ Table constructor
public Table(Object obj, Workspace workspace, long id) throws Exception
{
super(obj);
this.workspace = workspace;
this.id = id;
if(workspace != null && id != 0L)
workspace.put(this);
} //}}}
//{{{ Table constructor
/**
* Cloning constructor.
*/
public Table(Map words, Object obj, Workspace workspace)
throws Exception
{
super(words,obj);
this.workspace = workspace;
if(workspace != null)
{
this.id = workspace.nextID();
workspace.put(this);
}
} //}}}
//{{{ getWorkspace() method
/**
* Each persistent object is stored in one workspace only.
*/
public Workspace getWorkspace()
{
return workspace;
} //}}}
//{{{ getID() method
/**
* Each persistent object has an associated ID.
*/
public long getID()
{
return id;
} //}}}
//{{{ pickleValue() method
private synchronized String pickleValue(String name)
throws Exception
{
Object valueObj = words.get(name);
if(valueObj == null)
{
lazyFieldInit(name);
valueObj = words.get(name);
}
String value;
if(valueObj instanceof Pickled)
{
value = ((Pickled)valueObj)
.getUnparsed();
}
else
{
value = FactorReader.unparseDBObject(
getVariable(name));
}
return value;
} //}}}
//{{{ pickle() method
/**
* Each persistent object can turn itself into a byte array.
*/
public synchronized byte[] pickle()
throws PersistenceException
{
try
{
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(bytes);
Cons values = toVarList();
if(values == null)
out.writeInt(0);
else
out.writeInt(values.length());
while(values != null)
{
String name = (String)values.car;
out.writeInt(name.length());
byte[] nameBytes = name.getBytes(ENCODING);
out.write(nameBytes);
String value = pickleValue(name);
out.writeInt(value.length());
byte[] valueBytes = value.getBytes(ENCODING);
out.write(valueBytes);
out.write('\n');
values = values.next();
}
return bytes.toByteArray();
}
catch(Exception e)
{
// should not happen with byte array stream
throw new PersistenceException("Unexpected error",e);
}
} //}}}
//{{{ unpickle() method
/**
* Each persistent object can set its state to that in a byte array.
*/
public synchronized void unpickle(byte[] bytes, int offset)
throws PersistenceException
{
try
{
ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
bin.skip(offset);
DataInputStream in = new DataInputStream(bin);
int count = in.readInt();
for(int i = 0; i < count; i++)
{
int nameLength = in.readInt();
byte[] nameBytes = new byte[nameLength];
in.readFully(nameBytes);
String name = new String(nameBytes,ENCODING);
int valueLength = in.readInt();
byte[] valueBytes = new byte[valueLength];
in.readFully(valueBytes);
// skip newline at the end
in.readByte();
String value = new String(valueBytes,ENCODING);
Object obj = words.get(name);
if(obj == null)
{
lazyFieldInit(name);
obj = words.get(name);
}
if(obj instanceof VarBinding)
{
try
{
setVariable(name,FactorReader.parseObject(
value,workspace.getInterpreter()));
}
catch(Exception e)
{
//XXX: what to do here
System.err.println("Unexpected error when setting " + name + " to " + value);
e.printStackTrace();
}
}
else
{
// super becaue we don't want this to add the
// table to the save queue
super.setVariable(name,new Pickled(value));
}
}
}
catch(Exception e)
{
// should not happen with byte array stream
throw new PersistenceException("Unexpected error",e);
}
} //}}}
//{{{ getVariable() method
public synchronized Object getVariable(String name) throws Exception
{
Object value = super.getVariable(name);
if(value instanceof Pickled)
{
try
{
if(DEBUG)
System.err.println(this + ".getVariable(" + name + "): "
+ value);
value = FactorReader.parseObject(((Pickled)value)
.getUnparsed(),workspace.getInterpreter());
}
catch(Exception e)
{
throw new FactorRuntimeException("Table " + getID() + " has unreadable values",e);
}
// super becaue we don't want this to add the table
// to the save queue
super.setVariable(name,value);
}
return value;
} //}}}
//{{{ setVariable() method
public synchronized void setVariable(String name, Object value)
throws Exception
{
super.setVariable(name,value);
if(workspace != null && id != 0L)
workspace.put(this);
} //}}}
//{{{ clone() method
public FactorNamespace clone(Object rebind)
{
if(rebind.getClass() != obj.getClass())
throw new RuntimeException("Cannot rebind to different type");
try
{
return new Table(words,rebind,workspace);
}
catch(Exception e)
{
throw new InternalError();
}
} //}}}
//{{{ clone() method
public Object clone()
{
if(obj != null)
throw new RuntimeException("Cannot clone namespace that's bound to an object");
try
{
return new Table(new TreeMap(words),null,workspace);
}
catch(Exception e)
{
throw new InternalError();
}
} //}}}
//{{{ Pickled class
/**
* We lazily parse values in tables. An unparsed value is represented
* by an instance of this class.
*/
static class Pickled
{
private String unparsed;
Pickled(String unparsed)
{
this.unparsed = unparsed;
}
String getUnparsed()
{
return unparsed;
}
public String toString()
{
return unparsed;
}
} //}}}
//{{{ getReferences() method
public synchronized Cons getReferences()
{
return new Cons(getThis(),toVarList());
} //}}}
}

517
factor/db/Workspace.java Normal file
View File

@ -0,0 +1,517 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.db;
import factor.*;
import java.io.*;
import java.lang.ref.WeakReference;
import java.util.*;
/**
* A workspace is an orthogonal persistence store.
*
* Record format is:
* - 1 byte: name length
* - n bytes: class name
* - remainder: bytes passed to new instance unpickle()
*/
public class Workspace
{
/**
* A map of WeakReferences. All instances of this class are stored
* here.
*/
private static Map instances = new WeakHashMap();
private static WorkspaceSaveDaemon flushThread = new WorkspaceSaveDaemon();
public static boolean LOAD_DEBUG = false;
public static boolean SAVE_DEBUG = false;
/**
* The ID of the header record. A value never returned by
* nextID().
*/
public static final long HEADER = -1;
public static final String DB_VERSION = "1.0";
public static int flushInterval = 5000;
/**
* In a read-only workspace, changes are silently discarded on
* shutdown.
*/
private boolean readOnly;
private Store store;
private FactorInterpreter interp;
private HashSet saveQueue;
/**
* Floating objects are currently in-memory.
* This map maps IDs to WeakReferences.
*/
private Map floating;
/**
* Table containing important values. Always in memory.
*/
private Table header;
/**
* For resolving circular references, currently loading objects.
*/
private Map loading;
private boolean closed;
/**
* Track all IDs handed out with nextID(), and make sure they
* eventually reach the store.
*/
private Set pendingIDs = new TreeSet();
//{{{ Workspace constructor
public Workspace(Store store, boolean readOnly,
FactorInterpreter interp)
throws Exception
{
this.store = store;
this.readOnly = readOnly;
this.interp = interp;
floating = new HashMap();
saveQueue = new HashSet();
loading = new HashMap();
if(store.exists(HEADER))
header = (Table)get(HEADER);
else
{
header = new Table(this,HEADER);
initHeader();
}
instances.put(this,Boolean.TRUE);
} //}}}
//{{{ isFirstTime() method
public boolean isFirstTime()
{
try
{
return header.getVariable("first-time") != null;
}
catch(Exception e)
{
throw new RuntimeException(e);
}
} //}}}
//{{{ setFirstTime() method
public void setFirstTime(boolean firstTime)
{
try
{
header.setVariable("first-time",firstTime
? Boolean.TRUE : null);
}
catch(Exception e)
{
throw new RuntimeException(e);
}
} //}}}
//{{{ isReadOnly() method
/**
* In a 'read only' workspace, changes are silently discarded when
* the workspace is closed.
*/
public boolean isReadOnly()
{
return readOnly;
} //}}}
//{{{ initHeader() method
public void initHeader() throws Exception
{
header.setVariable("nextID",new Long(0));
header.setVariable("root",new Table(this));
header.setVariable("version",DB_VERSION);
header.setVariable("first-time",Boolean.TRUE);
} //}}}
//{{{ getRoot() method
/**
* Returns the workspace root.
*/
public Table getRoot()
{
try
{
return (Table)header.getVariable("root");
}
catch(Exception e)
{
throw new RuntimeException(e);
}
} //}}}
//{{{ getInterpreter() method
public FactorInterpreter getInterpreter()
{
return interp;
} //}}}
//{{{ nextID() method
public synchronized long nextID()// throws PersistenceException
{
try
{
long nextID = FactorJava.toLong(
header.getVariable("nextID"));
if(nextID == Long.MAX_VALUE)
throw new RuntimeException("FIXME!");
nextID++;
Long nextIDboxed = new Long(nextID);
header.setVariable("nextID",nextIDboxed);
pendingIDs.add(nextIDboxed);
return nextID;
}
catch(Exception e)
{
throw new RuntimeException(e);
}
} //}}}
//{{{ load() method
/**
* Load an object.
*/
private PersistentObject load(long id)
throws IOException, PersistenceException
{
PersistentObject circularRef = (PersistentObject)
loading.get(new Long(id));
if(circularRef != null)
return circularRef;
if(LOAD_DEBUG)
System.err.println("Loading from store: " + id);
byte[] data = store.loadFromStore(id);
byte len = data[0];
String className = new String(data,1,len,"ASCII");
// hack :-)
try
{
PersistentObject obj = (PersistentObject)
Class.forName(className)
.getConstructor(new Class[] {
Workspace.class, long.class
}).newInstance(new Object[] {
this, new Long(id)
});
loading.put(new Long(id),obj);
obj.unpickle(data,len + 1);
return obj;
}
catch(PersistenceException p)
{
throw p;
}
catch(Exception e)
{
throw new PersistenceException("Unexpected error",e);
}
finally
{
loading.remove(new Long(id));
}
} //}}}
//{{{ loadToCache() method
/**
* Load an object with given ID and store it in the floating map.
*/
private PersistentObject loadToCache(long id)
throws IOException, PersistenceException
{
PersistentObject obj = load(id);
WeakReference ref = new WeakReference(obj);
floating.put(new Long(id),ref);
return obj;
} //}}}
//{{{ get() method
/**
* If an object is already loaded, return that instance, otherwise
* load it.
*/
public synchronized PersistentObject get(long id)
throws IOException, PersistenceException
{
if(closed)
throw new PersistenceException();
WeakReference ref = (WeakReference)floating.get(new Long(id));
if(ref == null)
return loadToCache(id);
else
{
PersistentObject obj = (PersistentObject)ref.get();
if(obj == null)
return loadToCache(id);
else
{
if(LOAD_DEBUG)
System.err.println("Found cached: " + id);
return obj;
}
}
} //}}}
//{{{ addToCache() method
private void addToCache(PersistentObject obj)
throws PersistenceException
{
if(obj.getWorkspace() != this)
throw new PersistenceException("Object from another workspace");
Long id = new Long(obj.getID());
WeakReference ref = (WeakReference)floating.get(id);
if(ref == null)
floating.put(id,new WeakReference(obj));
else
{
Object referenced = ref.get();
if(referenced != obj)
throw new PersistenceException(referenced + " != " + obj);
}
} //}}}
//{{{ save() method
/**
* Store an object.
*/
private void save(PersistentObject obj)
throws IOException, PersistenceException
{
save(obj,store);
} //}}}
//{{{ save() method
/**
* Store an object.
*/
private void save(PersistentObject obj, Store store)
throws IOException, PersistenceException
{
if(SAVE_DEBUG)
System.err.println("Saving object " + obj.getID());
if(readOnly)
throw new RuntimeException();
pendingIDs.remove(new Long(obj.getID()));
ByteArrayOutputStream bout = new ByteArrayOutputStream();
String className = obj.getClass().getName();
bout.write((byte)className.length());
bout.write(className.getBytes("ASCII"));
bout.write(obj.pickle());
store.saveToStore(obj.getID(),bout.toByteArray());
} //}}}
//{{{ put() method
/**
* Add an object to the save queue.
*/
public synchronized void put(PersistentObject obj)
throws PersistenceException
{
if(closed)
throw new PersistenceException();
addToCache(obj);
saveQueue.add(obj);
} //}}}
//{{{ flush() method
/**
* Write all pending unsaved objects to the store.
*/
public synchronized void flush()
throws IOException, PersistenceException
{
if(closed || readOnly)
return;
Iterator iter = saveQueue.iterator();
while(iter.hasNext())
{
PersistentObject obj = (PersistentObject)iter.next();
save(obj);
iter.remove();
}
} //}}}
//{{{ compact() method
/**
* Write all referencable objects to the given store.
*/
private void compact(Store store)
throws IOException, PersistenceException
{
Set open = new HashSet();
Set closed = new HashSet();
for(;;)
{
if(open.isEmpty())
break;
Iterator iter = open.iterator();
Object obj = iter.next();
iter.remove();
closed.add(obj);
Cons references;
if(obj instanceof PersistentObject)
{
PersistentObject pobj = (PersistentObject)obj;
save(pobj,store);
references = pobj.getReferences();
}
else
references = (Cons)obj;
while(references != null)
{
Object ref = references.car;
if((references.car instanceof PersistentObject
|| references.car instanceof Cons)
&& !closed.contains(references.car))
{
open.add(references.car);
}
if(references.cdr instanceof Cons)
references = references.next();
else
{
if(references.cdr != null
&&
!closed.contains(references.car))
{
open.add(references.cdr);
}
break;
}
}
}
} //}}}
//{{{ close() method
/**
* Close the workspace.
*/
public synchronized void close()
throws IOException, PersistenceException
{
flush();
closed = true;
if(pendingIDs.size() != 0)
{
System.err.println("The following IDs did not get saved:");
System.err.println(pendingIDs);
}
store.close();
} //}}}
//{{{ finalize() method
protected void finalize() throws Throwable
{
super.finalize();
close();
} //}}}
//{{{ WorkspaceSaveDaemon class
static class WorkspaceSaveDaemon extends Thread
{
WorkspaceSaveDaemon()
{
setDaemon(true);
start();
}
public void run()
{
for(;;)
{
Iterator workspaces = instances.keySet().iterator();
while(workspaces.hasNext())
{
Workspace workspace = (Workspace)
workspaces.next();
try
{
workspace.flush();
}
catch(Exception e)
{
System.err.println("ERROR WHILE SAVING WORKSPACE.");
System.err.println("Workspace will be closed.");
synchronized(workspace)
{
workspace.closed = true;
}
e.printStackTrace();
}
}
try
{
Thread.sleep(flushInterval);
}
catch(InterruptedException e)
{
}
}
}
} //}}}
}

View File

@ -0,0 +1 @@
!a;3422;3422

View File

@ -0,0 +1,38 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.listener;
import factor.Cons;
import java.util.EventListener;
public interface EvalListener extends EventListener
{
public void eval(Cons code);
}

View File

@ -0,0 +1,180 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.listener;
import factor.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
public class FactorDesktop extends JFrame
{
private JTabbedPane tabs;
private FactorInterpreter interp;
private boolean standalone;
private Map listeners;
//{{{ main() method
public static void main(String[] args)
{
new FactorDesktop(args,true);
} //}}}
//{{{ FactorDesktop constructor
public FactorDesktop(String[] args, boolean standalone)
{
super("Factor");
tabs = new JTabbedPane();
this.standalone = standalone;
listeners = new HashMap();
getContentPane().add(BorderLayout.CENTER,tabs);
try
{
interp = new FactorInterpreter();
interp.interactive = false;
interp.init(args,null);
interp.global.setVariable("desktop",this);
}
catch(Exception e)
{
System.err.println("Failed to initialize interpreter:");
e.printStackTrace();
}
newListener();
setSize(640,480);
setDefaultCloseOperation(standalone
? EXIT_ON_CLOSE
: DISPOSE_ON_CLOSE);
show();
} //}}}
//{{{ newListener() method
public FactorListener newListener()
{
final FactorListener listener = new FactorListener();
listener.addEvalListener(new EvalHandler());
try
{
interp.call(new Cons(listener,
new Cons(interp.searchVocabulary(
"listener","new-listener-hook"),
null)));
interp.run();
}
catch(Exception e)
{
System.err.println("Failed to initialize listener:");
e.printStackTrace();
}
JScrollPane scroller = new JScrollPane(listener);
listeners.put(listener,scroller);
tabs.addTab("Listener",scroller);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
listener.requestFocus();
}
});
return listener;
} //}}}
//{{{ closeListener() method
public void closeListener(FactorListener listener)
{
// remove tab containing the listener
tabs.remove((Component)listeners.get(listener));
if(tabs.getTabCount() == 0)
{
if(standalone)
System.exit(0);
else
dispose();
}
} //}}}
//{{{ getInterpreter() method
public FactorInterpreter getInterpreter()
{
return interp;
} //}}}
//{{{ eval() method
public void eval(Cons cmd)
{
try
{
interp.call(cmd);
interp.run();
}
catch(Exception e)
{
System.err.println("Failed to eval " + cmd + ":");
e.printStackTrace();
}
} //}}}
//{{{ EvalHandler class
class EvalHandler implements EvalListener
{
public void eval(Cons cmd)
{
FactorDesktop.this.eval(cmd);
}
} //}}}
//{{{ EvalAction class
class EvalAction extends AbstractAction
{
private Cons code;
public EvalAction(String label, Cons code)
{
super(label);
this.code = code;
}
public void actionPerformed(ActionEvent evt)
{
FactorDesktop.this.eval(code);
}
} //}}}
}

View File

@ -0,0 +1,221 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.listener;
import factor.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
public class FactorListener extends JTextPane
{
private static final Cursor MoveCursor
= Cursor.getPredefinedCursor
(Cursor.HAND_CURSOR);
private static final Cursor DefaultCursor
= Cursor.getPredefinedCursor
(Cursor.TEXT_CURSOR);
private static final Cursor WaitCursor
= Cursor.getPredefinedCursor
(Cursor.WAIT_CURSOR);
public static final Object Link = new Object();
private EventListenerList listenerList;
private Cons readLineContinuation;
private int cmdStart = -1;
//{{{ FactorListener constructor
public FactorListener()
{
MouseHandler mouse = new MouseHandler();
addMouseListener(mouse);
addMouseMotionListener(mouse);
listenerList = new EventListenerList();
getInputMap().put(KeyStroke.getKeyStroke('\n'),
new EnterAction());
} //}}}
//{{{ insertWithAttrs() method
public void insertWithAttrs(String text, AttributeSet attrs)
throws BadLocationException
{
if(text == null)
throw new NullPointerException();
StyledDocument doc = (StyledDocument)getDocument();
int offset1 = doc.getLength();
doc.insertString(offset1,text,null);
int offset2 = offset1 + text.length();
doc.setCharacterAttributes(offset1,offset2,attrs,false);
setCaretPosition(offset2);
} //}}}
//{{{ readLine() method
public void readLine(Cons continuation)
throws BadLocationException
{
StyledDocument doc = (StyledDocument)getDocument();
setCursor(DefaultCursor);
this.readLineContinuation = continuation;
cmdStart = doc.getLength();
setCaretPosition(cmdStart);
/* doc.setCharacterAttributes(cmdStart,cmdStart,input,false);
setCharacterAttributes(input,false); */
} //}}}
//{{{ getLine() method
private String getLine() throws BadLocationException
{
StyledDocument doc = (StyledDocument)getDocument();
int length = doc.getLength();
if(cmdStart > length)
return "";
else
return doc.getText(cmdStart,length - cmdStart);
} //}}}
//{{{ addEvalListener() method
public void addEvalListener(EvalListener l)
{
listenerList.add(EvalListener.class,l);
} //}}}
//{{{ removeEvalListener() method
public void removeEvalListener(EvalListener l)
{
listenerList.remove(EvalListener.class,l);
} //}}}
//{{{ fireEvalEvent() method
private void fireEvalEvent(String code)
{
setCursor(WaitCursor);
Cons quot = new Cons(code,readLineContinuation);
//readLineContinuation = null;
Object[] listeners = listenerList.getListenerList();
for(int i = 0; i < listeners.length; i++)
{
if(listeners[i] == EvalListener.class)
{
EvalListener l = (EvalListener)listeners[i+1];
l.eval(quot);
}
}
} //}}}
//{{{ getLinkAt() method
private String getLinkAt(int pos)
{
StyledDocument doc = (StyledDocument)getDocument();
Element e = doc.getCharacterElement(pos);
AttributeSet a = e.getAttributes();
if(a == null)
return null;
else
return (String)a.getAttribute(Link);
} //}}}
//{{{ activateLink() method
private void activateLink(int pos)
{
String eval = getLinkAt(pos);
if(eval == null)
return;
try
{
StyledDocument doc = (StyledDocument)getDocument();
doc.insertString(doc.getLength(),eval + "\n",
getCharacterAttributes());
}
catch(BadLocationException ble)
{
ble.printStackTrace();
}
fireEvalEvent(eval);
} //}}}
//{{{ MouseHandler class
class MouseHandler extends MouseInputAdapter
{
public void mouseClicked(MouseEvent e)
{
JEditorPane editor = (JEditorPane) e.getSource();
Point pt = new Point(e.getX(), e.getY());
int pos = editor.viewToModel(pt);
if(pos >= 0)
activateLink(pos);
}
public void mouseMoved(MouseEvent e)
{
JEditorPane editor = (JEditorPane) e.getSource();
Point pt = new Point(e.getX(), e.getY());
int pos = editor.viewToModel(pt);
if(pos >= 0)
{
Cursor cursor;
if(getLinkAt(pos) != null)
cursor = MoveCursor;
else
cursor = DefaultCursor;
if(getCursor() != cursor)
setCursor(cursor);
}
}
} //}}}
//{{{ EnterAction class
class EnterAction extends AbstractAction
{
public void actionPerformed(ActionEvent evt)
{
try
{
fireEvalEvent(getLine());
}
catch(BadLocationException e)
{
e.printStackTrace();
}
}
} //}}}
}

287
factor/math/Complex.java Normal file
View File

@ -0,0 +1,287 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.math;
import factor.FactorLib;
public class Complex extends FactorNumber
{
public final Number real, imaginary;
//{{{ Complex constructor
/**
* Creates a new complex number.
*/
private Complex(Number real, Number imaginary)
{
this.real = real;
this.imaginary = imaginary;
} //}}}
//{{{ add() method
public Number add(Number num)
{
if(num instanceof Complex)
{
Complex c = (Complex)num;
return valueOf(
FactorMath.add(real,c.real),
FactorMath.add(imaginary,c.imaginary));
}
else
{
return valueOf(
FactorMath.add(real,num),
imaginary);
}
} //}}}
//{{{ subtract() method
/**
* this - num
*/
public Number subtract(Number num)
{
if(num instanceof Complex)
{
Complex c = (Complex)num;
return valueOf(
FactorMath.subtract(real,c.real),
FactorMath.subtract(imaginary,c.imaginary));
}
else
{
return valueOf(
FactorMath.subtract(real,num),
imaginary);
}
} //}}}
//{{{ _subtract() method
/**
* num - this
*/
public Number _subtract(Number num)
{
if(num instanceof Complex)
{
Complex c = (Complex)num;
return valueOf(
FactorMath.subtract(c.real,real),
FactorMath.subtract(c.imaginary,imaginary));
}
else
{
return valueOf(
FactorMath.subtract(num,real),
FactorMath.neg(imaginary));
}
} //}}}
//{{{ multiply() method
/**
* (a+bi)*(c+di) = ac - bd + (ad + bc)i
*/
public Number multiply(Number num)
{
if(num instanceof Complex)
{
Complex c = (Complex)num;
return valueOf(
FactorMath.subtract(
FactorMath.multiply(real,c.real),
FactorMath.multiply(imaginary,c.imaginary)),
FactorMath.add(
FactorMath.multiply(real,c.imaginary),
FactorMath.multiply(imaginary,c.real)));
}
else
{
return valueOf(
FactorMath.multiply(num,real),
FactorMath.multiply(num,imaginary));
}
} //}}}
//{{{ magnitude2() method
/**
* Magnitude squared.
*/
public Number magnitude2()
{
return FactorMath.add(FactorMath.multiply(real,real),
FactorMath.multiply(imaginary,imaginary));
} //}}}
//{{{ divide() method
/**
* a+bi (a+bi)*(c-di) ac + db + (cb - ad)i
* ---- = ------------- = --------------------
* c+di cc+dd cc + dd
*/
public Number divide(Number num)
{
if(num instanceof Complex)
{
Complex c = (Complex)num;
Number mag = c.magnitude2();
Number r = FactorMath.add(
FactorMath.multiply(real,c.real),
FactorMath.multiply(imaginary,c.imaginary));
Number i = FactorMath.subtract(
FactorMath.multiply(imaginary,c.real),
FactorMath.multiply(real,c.imaginary));
return valueOf(
FactorMath.divide(r,mag),
FactorMath.divide(i,mag));
}
else
{
return valueOf(FactorMath.divide(real,num),
FactorMath.divide(imaginary,num));
}
} //}}}
//{{{ _divide() method
/**
* num / this
*/
public Number _divide(Number num)
{
Number mag = magnitude2();
if(num instanceof Complex)
{
Complex c = (Complex)num;
Number r = FactorMath.add(
FactorMath.multiply(c.real,real),
FactorMath.multiply(c.imaginary,imaginary));
Number i = FactorMath.subtract(
FactorMath.multiply(c.imaginary,real),
FactorMath.multiply(c.real,imaginary));
return valueOf(
FactorMath.divide(r,mag),
FactorMath.divide(i,mag));
}
else
{
Number r = FactorMath.multiply(real,num);
Number i = FactorMath.neg(
FactorMath.multiply(imaginary,num));
return valueOf(FactorMath.divide(r,mag),
FactorMath.divide(i,mag));
}
} //}}}
//{{{ neg() method
public Number neg()
{
return new Complex(FactorMath.neg(real),
FactorMath.neg(imaginary));
} //}}}
//{{{ valueOf() method
public static Number valueOf(Number real, Number imaginary)
{
if(FactorMath.is0(imaginary))
return real;
else if(real instanceof Complex)
throw new ArithmeticException("Not a real: " + real);
if(imaginary instanceof Complex)
throw new ArithmeticException("Not a real: " + imaginary);
else
return new Complex(real,imaginary);
} //}}}
//{{{ intValue() method
public int intValue()
{
return real.intValue();
} //}}}
//{{{ longValue() method
public long longValue()
{
return real.longValue();
} //}}}
//{{{ floatValue() method
public float floatValue()
{
return real.floatValue();
} //}}}
//{{{ doubleValue() method
public double doubleValue()
{
return real.doubleValue();
} //}}}
//{{{ byteValue() method
public byte byteValue()
{
return real.byteValue();
} //}}}
//{{{ shortValue() method
public short shortValue()
{
return real.shortValue();
} //}}}
//{{{ toString() method
public String toString()
{
return "#{ " + real + " " + imaginary + " }";
} //}}}
//{{{ equals() method
public boolean equals(Object o)
{
if(o instanceof Complex)
{
Complex c = (Complex)o;
return FactorLib.equal(real,c.real)
&& FactorLib.equal(imaginary,c.imaginary);
}
else
return false;
} //}}}
//{{{ hashCode() method
public int hashCode()
{
return real.hashCode() ^ imaginary.hashCode();
} //}}}
}

618
factor/math/FactorMath.java Normal file
View File

@ -0,0 +1,618 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.math;
import java.math.*;
import java.util.Random;
/**
* Math-related words.
*/
public class FactorMath
{
private static Random random = new Random();
//{{{ add() method
public static Number add(Number x, Number y)
{
if(y instanceof FactorNumber)
return ((FactorNumber)y).add(x);
else if(x instanceof Integer)
{
int _x = x.intValue();
if(y instanceof Integer)
{
int _y = ((Integer)y).intValue();
long result = (long)_x + (long)_y;
return longToNumber(result);
}
else if(y instanceof BigInteger)
{
return BigInteger.valueOf(_x)
.add((BigInteger)y);
}
}
else if(x instanceof FactorNumber)
return ((FactorNumber)x).add(y);
else if(x instanceof BigInteger)
{
BigInteger _x = (BigInteger)x;
if(y instanceof Integer)
{
return _x.add(BigInteger.valueOf(
((Integer)y).intValue()));
}
else if(y instanceof BigInteger)
return _x.add((BigInteger)y);
}
return new Double(x.doubleValue() + y.doubleValue());
} //}}}
//{{{ and() method
public static Number and(Number x, Number y)
{
if(x instanceof BigInteger)
{
if(y instanceof BigInteger)
return ((BigInteger)x).and((BigInteger)y);
else
{
return ((BigInteger)x).and(BigInteger.valueOf(
y.longValue()));
}
}
else
{
if(y instanceof BigInteger)
{
return ((BigInteger)y).and(BigInteger.valueOf(
x.longValue()));
}
else
{
long and = x.longValue() & y.longValue();
return longToNumber(and);
}
}
} //}}}
//{{{ or() method
public static Number or(Number x, Number y)
{
if(x instanceof BigInteger)
{
if(y instanceof BigInteger)
return ((BigInteger)x).or((BigInteger)y);
else
{
return ((BigInteger)x).or(BigInteger.valueOf(
y.longValue()));
}
}
else
{
if(y instanceof BigInteger)
{
return ((BigInteger)y).or(BigInteger.valueOf(
x.longValue()));
}
else
{
long or = x.longValue() | y.longValue();
return longToNumber(or);
}
}
} //}}}
//{{{ xor() method
public static Number xor(Number x, Number y)
{
if(x instanceof BigInteger)
{
if(y instanceof BigInteger)
return ((BigInteger)x).xor((BigInteger)y);
else
{
return ((BigInteger)x).xor(BigInteger.valueOf(
y.longValue()));
}
}
else
{
if(y instanceof BigInteger)
{
return ((BigInteger)y).xor(BigInteger.valueOf(
x.longValue()));
}
else
{
long xor = x.longValue() ^ y.longValue();
return longToNumber(xor);
}
}
} //}}}
//{{{ not() method
public static Number not(Number x)
{
if(x instanceof BigInteger)
return ((BigInteger)x).not();
else
return longToNumber(~x.longValue());
} //}}}
//{{{ shiftLeft() method
public static Number shiftLeft(Number x, int by)
{
if(by < 0)
throw new ArithmeticException("Cannot shift by negative amount");
if(x instanceof BigInteger)
return ((BigInteger)x).shiftLeft(by);
else if(x instanceof Integer)
{
int ix = x.intValue();
if(by >= 32)
return BigInteger.valueOf(ix).shiftLeft(by);
else
return longToNumber(ix << by);
}
else
return BigInteger.valueOf(x.longValue()).shiftLeft(by);
} //}}}
//{{{ shiftRight() method
public static Number shiftRight(Number x, int by)
{
if(by < 0)
throw new ArithmeticException("Cannot shift by negative amount");
if(x instanceof BigInteger)
return ((BigInteger)x).shiftRight(by);
else
return longToNumber(x.longValue() >> by);
} //}}}
//{{{ shiftRightUnsigned() method
public static Number shiftRightUnsigned(Number x, int by)
{
if(by < 0)
throw new ArithmeticException("Cannot shift by negative amount");
if(x instanceof BigInteger)
throw new RuntimeException();
else
return longToNumber(x.longValue() >>> by);
} //}}}
//{{{ _divide() method
/**
* Truncating division.
*/
public static Number _divide(Number x, Number y)
{
if(x instanceof Integer)
{
int _x = x.intValue();
if(y instanceof Integer)
{
int _y = y.intValue();
return new Integer(_x / _y);
}
else if(y instanceof BigInteger)
{
return BigInteger.valueOf(_x)
.divide((BigInteger)y);
}
}
else if(x instanceof BigInteger)
{
BigInteger _x = (BigInteger)x;
if(y instanceof Integer)
{
return _x.divide(BigInteger.valueOf(
y.intValue()));
}
else if(y instanceof BigInteger)
return _x.divide((BigInteger)y);
}
return new Double(x.doubleValue() / y.doubleValue());
} //}}}
//{{{ divide() method
public static Number divide(Number x, Number y)
{
if(
(x instanceof Integer
|| x instanceof BigInteger)
&&
(y instanceof Integer
|| y instanceof BigInteger))
{
return Ratio.valueOf(x,y);
}
else if(x instanceof FactorNumber)
return ((FactorNumber)x).divide(y);
else if(y instanceof FactorNumber)
return ((FactorNumber)y)._divide(x);
return new Double(x.doubleValue() / y.doubleValue());
} //}}}
//{{{ mod() method
public static Number mod(Number x, Number y)
{
if(x instanceof BigInteger)
{
if(y instanceof BigInteger)
return ((BigInteger)x).mod((BigInteger)y);
else
{
return ((BigInteger)x).mod(
BigInteger.valueOf(y.longValue()));
}
}
else if(y instanceof BigInteger)
{
return BigInteger.valueOf(x.longValue())
.mod((BigInteger)y);
}
else
return new Integer(x.intValue()%y.intValue());
} //}}}
//{{{ longToNumber() method
private static Number longToNumber(long x)
{
if(x < Integer.MIN_VALUE || x > Integer.MAX_VALUE)
return BigInteger.valueOf(x);
else
return new Integer((int)x);
} //}}}
//{{{ gcd() method
public static long gcd(long x, long y)
{
if(x < 0)
x = -x;
if(y < 0)
y = -y;
if(x > y)
{
long t = x;
x = y;
y = t;
}
for(;;)
{
if(x == 0)
return y;
long t = y % x;
y = x;
x = t;
}
} //}}}
//{{{ gcd() method
public static Number gcd(Number x, Number y)
{
if(x instanceof BigInteger)
{
BigInteger _x = (BigInteger)x;
if(y instanceof BigInteger)
return _x.gcd((BigInteger)y);
else
{
return _x.gcd(BigInteger.valueOf(
y.longValue()));
}
}
else
{
long _x = x.longValue();
if(y instanceof BigInteger)
{
return ((BigInteger)y).gcd(
BigInteger.valueOf(_x));
}
else
return longToNumber(gcd(_x,y.longValue()));
}
} //}}}
//{{{ is0() method
public static boolean is0(Number x)
{
if(x instanceof BigInteger)
return x.equals(BigInteger.ZERO);
else if(x instanceof Integer)
return x.intValue() == 0;
else
return x.floatValue() == 0.0f;
} //}}}
//{{{ is1() method
public static boolean is1(Number x)
{
if(x instanceof BigInteger)
return x.equals(BigInteger.ONE);
else if(x instanceof Integer)
return x.intValue() == 1;
else
return x.floatValue() == 1.0f;
} //}}}
//{{{ compare() method
/**
* -1: x < y
* 0: x = y
* 1: x > y
*/
public static int compare(Number x, Number y)
{
if(x instanceof Complex || y instanceof Complex)
throw new ArithmeticException("Complex numbers are not comparable");
if(x instanceof Ratio)
return ((Ratio)x).compare(y);
else if(y instanceof Ratio)
return -((Ratio)y).compare(x);
else if(x instanceof BigInteger)
{
if(y instanceof BigInteger)
{
return ((BigInteger)x).compareTo(
(BigInteger)y);
}
else if(y instanceof Integer || y instanceof Long)
{
return ((BigInteger)x).compareTo(
BigInteger.valueOf(y.longValue()));
}
}
else if(y instanceof BigInteger)
{
if(x instanceof Integer || x instanceof Long)
{
return BigInteger.valueOf(x.longValue())
.compareTo((BigInteger)y);
}
}
else if(x instanceof Integer || x instanceof Long)
{
if(y instanceof Integer || y instanceof Long)
{
return sgn(x.longValue() - y.longValue());
}
}
return sgn(x.doubleValue() - y.doubleValue());
} //}}}
//{{{ greater() method
public static boolean greater(Number x, Number y)
{
return compare(x,y) > 0;
} //}}}
//{{{ greaterEqual() method
public static boolean greaterEqual(Number x, Number y)
{
return compare(x,y) >= 0;
} //}}}
//{{{ less() method
public static boolean less(Number x, Number y)
{
return compare(x,y) < 0;
} //}}}
//{{{ lessEqual() method
public static boolean lessEqual(Number x, Number y)
{
return compare(x,y) <= 0;
} //}}}
//{{{ multiply() method
public static Number multiply(Number x, Number y)
{
if(y instanceof FactorNumber)
return ((FactorNumber)y).multiply(x);
else if(x instanceof Integer)
{
int _x = x.intValue();
if(y instanceof Integer)
{
int _y = y.intValue();
long result = (long)_x * (long)_y;
return longToNumber(result);
}
else if(y instanceof BigInteger)
{
return BigInteger.valueOf(_x)
.multiply((BigInteger)y);
}
}
else if(x instanceof FactorNumber)
return ((FactorNumber)x).multiply(y);
else if(x instanceof BigInteger)
{
BigInteger _x = (BigInteger)x;
if(y instanceof Integer)
{
return _x.multiply(BigInteger.valueOf(
y.intValue()));
}
else if(y instanceof BigInteger)
return _x.multiply((BigInteger)y);
}
return new Double(x.doubleValue() * y.doubleValue());
} //}}}
//{{{ neg() method
public static Number neg(Number x)
{
if(x instanceof Integer)
return new Integer(-x.intValue());
else if(x instanceof BigInteger)
return ((BigInteger)x).negate();
else if(x instanceof FactorNumber)
return ((FactorNumber)x).neg();
else if(x instanceof Float)
return new Float(-x.floatValue());
else
return new Double(-x.doubleValue());
} //}}}
//{{{ randomAngle() method
public static float randomAngle()
{
return (float)Math.PI * randomInt(0,360) / 180;
} //}}}
//{{{ randomBoolean() method
public static boolean randomBoolean()
{
return random.nextBoolean();
} //}}}
//{{{ randomInt() method
public static int randomInt(int min, int max)
{
if(min == max)
return min;
return min + random.nextInt(max - min + 1);
} //}}}
//{{{ randomFloat() method
public static float randomFloat(int min, int max, float scale)
{
return randomInt(min,max) / scale;
} //}}}
//{{{ sgn() method
public static int sgn(float num)
{
if(num < 0.0f)
return -1;
else if(num == 0.0f)
return 0;
else
return 1;
} //}}}
//{{{ sgn() method
public static int sgn(double num)
{
if(num < 0.0)
return -1;
else if(num == 0.0)
return 0;
else
return 1;
} //}}}
//{{{ sgn() method
public static int sgn(long num)
{
if(num < 0)
return -1;
else if(num == 0)
return 0;
else
return 1;
} //}}}
//{{{ sgn() method
public static int sgn(Number num)
{
if(num instanceof Ratio)
return sgn(((Ratio)num).numerator);
else if(num instanceof BigInteger)
return ((BigInteger)num).signum();
else if(num instanceof Integer || num instanceof Long)
return sgn(num.longValue());
else if(num instanceof Float)
return sgn(num.floatValue());
else //if(num instanceof Double)
return sgn(num.doubleValue());
} //}}}
//{{{ subtract() method
public static Number subtract(Number x, Number y)
{
if(y instanceof FactorNumber)
return ((FactorNumber)y)._subtract(x);
else if(x instanceof Integer)
{
int _x = x.intValue();
if(y instanceof Integer)
{
int _y = y.intValue();
long result = (long)_x - (long)_y;
return longToNumber(result);
}
else if(y instanceof BigInteger)
{
return BigInteger.valueOf(_x)
.subtract((BigInteger)y);
}
}
else if(x instanceof FactorNumber)
return ((FactorNumber)x).subtract(y);
else if(x instanceof BigInteger)
{
BigInteger _x = (BigInteger)x;
if(y instanceof Integer)
{
return _x.subtract(BigInteger.valueOf(
((Integer)y).intValue()));
}
else if(y instanceof BigInteger)
return _x.subtract((BigInteger)y);
}
return new Double(x.doubleValue() - y.doubleValue());
} //}}}
}

View File

@ -0,0 +1,62 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.math;
import factor.*;
public abstract class FactorNumber extends Number
implements FactorExternalizable
{
public abstract Number add(Number num);
/**
* this - num
*/
public abstract Number subtract(Number num);
/**
* num - this
*/
public abstract Number _subtract(Number num);
public abstract Number multiply(Number num);
/**
* this / num
*/
public abstract Number divide(Number num);
/**
* num / this
*/
public abstract Number _divide(Number num);
public abstract Number neg();
}

View File

@ -0,0 +1,157 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.math;
import java.io.*;
import java.math.BigInteger;
/**
* A class for turning strings into numbers.
*/
public class NumberParser
{
//{{{ parseInteger() method
private static Number parseInteger(String word, int base)
{
try
{
return Integer.valueOf(word,base);
}
catch(NumberFormatException e)
{
return new BigInteger(word,base);
}
} //}}}
//{{{ parseNumber() method
/**
* If the given string is a number, convert it to a Number instance,
* otherwise return null.
*/
public static Number parseNumber(String word, int base)
{
if(word == null)
return null;
boolean number = false;
boolean floating = false;
boolean exponent = false;
int ratio = -1;
for(int i = 0; i < word.length(); i++)
{
char ch = word.charAt(i);
if(ch == '-')
{
if((i != 0 && Character.toLowerCase(
word.charAt(i - 1))
!= 'e') || word.length() == 1)
{
number = false;
break;
}
}
else if(Character.isDigit(ch))
number = true;
else if(base == 16
&& ((ch >= 'a' && ch <= 'f')
|| (ch >= 'A' && ch <= 'F')))
{
number = true;
}
else if((ch == 'e' || ch == 'E')
&& word.length() != 1)
{
if(exponent || ratio != -1)
{
number = false;
break;
}
else
exponent = true;
}
else if(ch == '.' && word.length() != 1)
{
if(floating || ratio != -1)
{
number = false;
break;
}
else
floating = true;
}
else if(ch == '/')
{
if(floating || ratio != -1)
{
number = false;
break;
}
else
ratio = i;
}
else
{
number = false;
break;
}
}
if(number)
{
if(ratio == 0 || ratio == word.length() - 1)
return null;
else if(floating || exponent)
{
if(ratio != -1)
return null;
else
{
if(base != 10)
return null;
else
return new Double(word);
}
}
else if(ratio != -1)
{
String numerator = word.substring(0,ratio);
String denominator = word.substring(ratio + 1);
return Ratio.valueOf(
parseInteger(numerator,base),
parseInteger(denominator,base));
}
else
return parseInteger(word,base);
}
return null;
} //}}}
}

344
factor/math/Ratio.java Normal file
View File

@ -0,0 +1,344 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2003, 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.math;
import factor.FactorLib;
public class Ratio extends FactorNumber
{
public final Number numerator, denominator;
//{{{ Ratio constructor
/**
* Creates a new ratio.
*/
private Ratio(Number numerator, Number denominator)
{
this.numerator = numerator;
this.denominator = denominator;
} //}}}
//{{{ add() method
public Number add(Number num)
{
if(num instanceof Ratio)
{
// a c ad + bc
// - + - = -------
// b d bd
Ratio r = (Ratio)num;
return valueOf(
FactorMath.add(
FactorMath.multiply(numerator,r.denominator),
FactorMath.multiply(denominator,r.numerator)),
FactorMath.multiply(denominator,r.denominator));
}
else if(num instanceof Complex)
return ((Complex)num).add(this);
else if(num instanceof Float
|| num instanceof Double)
{
return new Double(doubleValue() + num.doubleValue());
}
else
{
return valueOf(
FactorMath.add(numerator,
FactorMath.multiply(denominator,num)),
denominator);
}
} //}}}
//{{{ subtract() method
/**
* this - num
*/
public Number subtract(Number num)
{
if(num instanceof Ratio)
{
// a c ad - bc
// - - - = -------
// b d bd
Ratio r = (Ratio)num;
return valueOf(
FactorMath.subtract(
FactorMath.multiply(numerator,r.denominator),
FactorMath.multiply(denominator,r.numerator)),
FactorMath.multiply(denominator,r.denominator));
}
else if(num instanceof Complex)
return ((Complex)num)._subtract(this);
else if(num instanceof Float
|| num instanceof Double)
{
return new Double(doubleValue() - num.doubleValue());
}
else
{
return valueOf(
FactorMath.subtract(numerator,
FactorMath.multiply(denominator,num)),
denominator);
}
} //}}}
//{{{ _subtract() method
/**
* num - this
*/
public Number _subtract(Number num)
{
if(num instanceof Ratio)
{
// a c bc - ad
// - - - = -------
// b d bd
Ratio r = (Ratio)num;
return valueOf(
FactorMath.subtract(
FactorMath.multiply(denominator,r.numerator),
FactorMath.multiply(numerator,r.denominator)),
FactorMath.multiply(denominator,r.denominator));
}
else if(num instanceof Complex)
return ((Complex)num).subtract(this);
else if(num instanceof Float
|| num instanceof Double)
{
return new Double(num.doubleValue() - doubleValue());
}
else
{
return valueOf(
FactorMath.subtract(FactorMath.multiply(denominator,num),
numerator),
denominator);
}
} //}}}
//{{{ multiply() method
public Number multiply(Number num)
{
if(num instanceof Ratio)
{
// a c ac
// - * - = --
// b d bd
Ratio r = (Ratio)num;
return valueOf(
FactorMath.multiply(numerator,r.numerator),
FactorMath.multiply(denominator,r.denominator));
}
else if(num instanceof Complex)
return ((Complex)num).multiply(this);
else if(num instanceof Float
|| num instanceof Double)
{
return new Double(doubleValue() * num.doubleValue());
}
else
{
return valueOf(
FactorMath.multiply(numerator,num),
denominator);
}
} //}}}
//{{{ divide() method
/**
* this / num
*/
public Number divide(Number num)
{
if(num instanceof Ratio)
{
// a c ad
// - / - = --
// b d bc
Ratio r = (Ratio)num;
return valueOf(
FactorMath.multiply(numerator,r.denominator),
FactorMath.multiply(denominator,r.numerator));
}
else if(num instanceof Complex)
return ((Complex)num)._divide(this);
else if(num instanceof Float
|| num instanceof Double)
{
return new Double(doubleValue() / num.doubleValue());
}
else
{
return valueOf(numerator,
FactorMath.multiply(denominator,num));
}
} //}}}
//{{{ _divide() method
/**
* num / this
*/
public Number _divide(Number num)
{
if(num instanceof Ratio)
{
// c a cb
// - / - = --
// d b da
Ratio r = (Ratio)num;
return valueOf(
FactorMath.multiply(denominator,r.numerator),
FactorMath.multiply(numerator,r.denominator));
}
else if(num instanceof Complex)
return ((Complex)num).divide(this);
else if(num instanceof Float
|| num instanceof Double)
{
return new Double(num.doubleValue() / doubleValue());
}
else
{
return valueOf(
FactorMath.multiply(denominator,num),
numerator);
}
} //}}}
//{{{ valueOf() method
public static Number valueOf(Number numerator, Number denominator)
{
if(FactorMath.sgn(denominator) == 0)
throw new ArithmeticException("/ by zero");
if(FactorMath.sgn(denominator) == -1)
{
numerator = FactorMath.neg(numerator);
denominator = FactorMath.neg(denominator);
}
Number gcd = FactorMath.gcd(numerator,denominator);
if(!FactorMath.is1(gcd))
{
numerator = FactorMath._divide(numerator,gcd);
denominator = FactorMath._divide(denominator,gcd);
}
if(FactorMath.is1(denominator))
return numerator;
else
return new Ratio(numerator,denominator);
} //}}}
//{{{ neg() method
public Number neg()
{
return new Ratio(FactorMath.neg(numerator),denominator);
} //}}}
//{{{ compare() method
public int compare(Number num)
{
if(num instanceof Ratio)
{
Ratio r = (Ratio)num;
return FactorMath.compare(
FactorMath.multiply(numerator,r.denominator),
FactorMath.multiply(r.numerator,denominator));
}
else
{
return FactorMath.compare(
numerator,
FactorMath.multiply(num,denominator));
}
} //}}}
//{{{ intValue() method
public int intValue()
{
return (int)doubleValue();
} //}}}
//{{{ longValue() method
public long longValue()
{
return (long)doubleValue();
} //}}}
//{{{ floatValue() method
public float floatValue()
{
return (float)doubleValue();
} //}}}
//{{{ doubleValue() method
public double doubleValue()
{
return numerator.doubleValue() / denominator.doubleValue();
} //}}}
//{{{ byteValue() method
public byte byteValue()
{
return (byte)doubleValue();
} //}}}
//{{{ shortValue() method
public short shortValue()
{
return (short)doubleValue();
} //}}}
//{{{ toString() method
public String toString()
{
return numerator + "/" + denominator;
} //}}}
//{{{ equals() method
public boolean equals(Object o)
{
if(o instanceof Ratio)
{
Ratio r = (Ratio)o;
return FactorLib.equal(numerator,r.numerator)
&& FactorLib.equal(denominator,r.denominator);
}
else
return false;
} //}}}
//{{{ hashCode() method
public int hashCode()
{
return numerator.hashCode() ^ denominator.hashCode();
} //}}}
}

55
factor/parser/Bar.java Normal file
View File

@ -0,0 +1,55 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
public class Bar extends FactorParsingDefinition
{
//{{{ Bar constructor
/**
* A new definition.
*/
public Bar(FactorWord word, Workspace workspace)
throws Exception
{
super(word,workspace);
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
throws Exception
{
FactorReader.ParseState state = reader.getCurrentState();
if(state.start != reader.intern("[",false))
reader.error("| only allowed inside [ ... ]");
reader.bar();
}
}

57
factor/parser/Base.java Normal file
View File

@ -0,0 +1,57 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
public class Base extends FactorParsingDefinition
{
private int base;
//{{{ Base constructor
/**
* A new definition.
*/
public Base(FactorWord word, Workspace workspace, int base)
throws Exception
{
super(word,workspace);
this.base = base;
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
throws Exception
{
Number num = FactorJava.toNumber(
reader.getScanner().next(true,false,base));
reader.append(num);
}
}

51
factor/parser/Bra.java Normal file
View File

@ -0,0 +1,51 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
public class Bra extends FactorParsingDefinition
{
//{{{ Bra constructor
/**
* A new definition.
*/
public Bra(FactorWord word, Workspace workspace)
throws Exception
{
super(word,workspace);
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
{
reader.pushState(word,null);
}
}

View File

@ -0,0 +1,65 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
import java.io.IOException;
public class CharLiteral extends FactorParsingDefinition
{
//{{{ CharLiteral constructor
/**
* A new definition.
*/
public CharLiteral(FactorWord word, Workspace workspace)
throws Exception
{
super(word,workspace);
} //}}}
//{{{ CharLiteral constructor
/**
* A blank definition, about to be unpickled.
*/
public CharLiteral(Workspace workspace, long id)
throws Exception
{
super(workspace,id);
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
throws IOException, FactorParseException
{
reader.append(new Character(
reader.getScanner()
.readNonEOFEscaped()));
}
}

View File

@ -0,0 +1,68 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
import factor.math.*;
public class ComplexLiteral extends FactorParsingDefinition
{
private String end;
//{{{ ComplexLiteral constructor
/**
* A new definition.
*/
public ComplexLiteral(FactorWord word,
String end, Workspace workspace)
throws Exception
{
super(word,workspace);
this.end = end;
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
throws Exception
{
// Read two numbers
Number real = FactorJava.toNumber(
reader.nextNonEOF(true,false));
Number imaginary = FactorJava.toNumber(
reader.nextNonEOF(true,false));
// Read the end
String end = (String)reader.nextNonEOF(false,false);
if(!end.equals(this.end))
reader.error("Expected " + this.end);
reader.append(Complex.valueOf(real,imaginary));
}
}

54
factor/parser/Def.java Normal file
View File

@ -0,0 +1,54 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
public class Def extends FactorParsingDefinition
{
//{{{ Def constructor
/**
* A new definition.
*/
public Def(FactorWord word, Workspace workspace)
throws Exception
{
super(word,workspace);
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
throws Exception
{
// Read the word name
FactorWord newWord = reader.nextWord(true);
reader.pushExclusiveState(word,newWord);
}
}

52
factor/parser/Defer.java Normal file
View File

@ -0,0 +1,52 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
public class Defer extends FactorParsingDefinition
{
//{{{ Defer constructor
/**
* A new definition.
*/
public Defer(FactorWord word, Workspace workspace)
throws Exception
{
super(word,workspace);
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
throws Exception
{
reader.nextWord(true);
}
}

View File

@ -0,0 +1,62 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
import java.io.IOException;
public class Dispatch extends FactorParsingDefinition
{
//{{{ Dispatch constructor
/**
* A new definition.
*/
public Dispatch(FactorWord word, Workspace workspace)
throws Exception
{
super(word,workspace);
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
throws Exception
{
char next = reader.getScanner().readNonEOF();
String dispatch = word.name + next;
FactorWord dispatchWord = reader.intern(dispatch,false);
if(dispatchWord.parsing != null)
dispatchWord.parsing.eval(interp,reader);
else
{
reader.error("Unknown dispatch: "
+ dispatch);
}
}
}

52
factor/parser/F.java Normal file
View File

@ -0,0 +1,52 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
public class F extends FactorParsingDefinition
{
//{{{ F constructor
/**
* A new definition.
*/
public F(FactorWord word, Workspace workspace)
throws Exception
{
super(word,workspace);
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
throws FactorParseException
{
reader.append(null);
}
}

View File

@ -0,0 +1,58 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
import java.io.IOException;
public class GetPersistentObject extends FactorParsingDefinition
{
//{{{ GetPersistentObject constructor
/**
* A new definition.
*/
public GetPersistentObject(FactorWord word, Workspace workspace)
throws Exception
{
super(word,workspace);
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
throws Exception
{
long id = FactorJava.toLong(reader.next(true,false));
if(interp.workspace == null)
reader.getScanner().error("No workspace");
else
reader.append(interp.workspace.get(id));
}
}

56
factor/parser/In.java Normal file
View File

@ -0,0 +1,56 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
public class In extends FactorParsingDefinition
{
//{{{ In constructor
/**
* A new definition.
*/
public In(FactorWord word, Workspace workspace)
throws Exception
{
super(word,workspace);
} //}}}
//{{{ eval() method
public void eval(FactorInterpreter interp, FactorReader reader)
throws Exception
{
String next = (String)reader.next(false,false);
reader.setIn(next);
reader.addUse(next);
} //}}}
}

61
factor/parser/Ine.java Normal file
View File

@ -0,0 +1,61 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
public class Ine extends FactorParsingDefinition
{
public FactorWord start;
//{{{ Ine constructor
/**
* A new definition.
*/
public Ine(FactorWord start, FactorWord end, Workspace workspace)
throws Exception
{
super(end,workspace);
this.start = start;
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
throws Exception
{
FactorReader.ParseState state = reader.popState(start,word);
FactorWord w = (FactorWord)state.arg;
reader.append(w.vocabulary);
reader.append(w.name);
reader.append(new FactorCompoundDefinition(
w,state.first,interp));
reader.append(reader.intern("define",false));
}
}

55
factor/parser/Ket.java Normal file
View File

@ -0,0 +1,55 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
public class Ket extends FactorParsingDefinition
{
public FactorWord start;
//{{{ Ket constructor
/**
* A new definition.
*/
public Ket(FactorWord start, FactorWord end, Workspace workspace)
throws Exception
{
super(end,workspace);
this.start = start;
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
throws FactorParseException
{
reader.append(reader.popState(start,word).first);
}
}

View File

@ -0,0 +1,58 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
import java.io.IOException;
public class LineComment extends FactorParsingDefinition
{
public boolean doc;
//{{{ LineComment constructor
/**
* A new definition.
*/
public LineComment(FactorWord word, boolean doc, Workspace workspace)
throws Exception
{
super(word,workspace);
this.doc = doc;
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
throws IOException, FactorParseException
{
String comment = reader.getScanner().readUntilEOL();
if(doc)
reader.append(new FactorDocComment(comment,false));
}
}

View File

@ -0,0 +1,53 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
import java.io.IOException;
public class NoParsing extends FactorParsingDefinition
{
//{{{ NoParsing constructor
/**
* A new definition.
*/
public NoParsing(FactorWord word, Workspace workspace)
throws Exception
{
super(word,workspace);
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
throws Exception
{
reader.append(reader.nextWord(false));
}
}

View File

@ -0,0 +1,54 @@
/* :folding=explicit:collapseFolds=1: */
/*
* $Id$
*
* Copyright (C) 2004 Slava Pestov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package factor.parser;
import factor.*;
import factor.db.*;
import java.io.IOException;
public class PassThrough extends FactorParsingDefinition
{
//{{{ PassThrough constructor
/**
* A new definition.
*/
public PassThrough(FactorWord word, Workspace workspace)
throws Exception
{
super(word,workspace);
} //}}}
public void eval(FactorInterpreter interp, FactorReader reader)
throws Exception
{
reader.append(reader.intern(word.name
+ reader.next(false,false),false));
}
}

Some files were not shown because too many files have changed in this diff Show More