Polished build process. Added CONS special form.

This commit is contained in:
Jonathan Bernard 2009-11-24 11:55:02 -06:00
parent e6a608ee2b
commit 2c0e932f89
14 changed files with 281 additions and 56 deletions

View File

@ -1,28 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project name="Common Build Versioning" > <project name="Common Build Versioning" >
<property name="cbv-basedir" value="nbproject" /> <property name="cbv-basedir" value="${basedir}" />
<property name="cbv-property-file" value="project.properties"/>
<target name="-cbv-init"> <target name="-cbv-init">
<property file="${cbv-basedir}/project.versioning.properties" /> <property file="${cbv-basedir}/${cbv-property-file}" />
</target> </target>
<target name="-pre-set-version" /> <target name="-pre-set-version" />
<target name="-do-set-version"> <target name="-do-set-version">
<input message="Current version is ${application.version}. Enter new version: " addproperty="new-version"/> <input message="Current version is ${application.version}. Enter new version: " addproperty="new-version"/>
<propertyfile file="${cbv-basedir}/project.versioning.properties"> <propertyfile file="${cbv-basedir}/${cbv-property-file}">
<entry key="application.version" value="${new-version}" /> <entry key="application.version" value="${new-version}" />
<entry key="build.number" value="0" /> <entry key="build.number" value="0" />
</propertyfile> </propertyfile>
<property file="${cbv-basedir}/project.versioning.properties" /> <property file="${cbv-basedir}/${cbv-property-file}" />
</target> </target>
<target name="-post-set-version" /> <target name="-post-set-version" />
<target name="set-version" depends="-cbv-init,-pre-set-version,-do-set-version,-post-set-version"/> <target name="set-version" depends="-cbv-init,-pre-set-version,-do-set-version,-post-set-version"/>
<target name="increment-build-number"> <target name="increment-build-number">
<propertyfile file="${cbv-basedir}/project.versioning.properties"> <propertyfile file="${cbv-basedir}/${cbv-property-file}">
<entry key="build.number" operation="+" type="int" default="0"/> <entry key="build.number" operation="+" type="int" default="0"/>
</propertyfile> </propertyfile>
<property file="${cbv-basedir}/project.versioning.properties" /> <property file="${cbv-basedir}/${cbv-property-file}" />
</target> </target>
</project> </project>

View File

@ -12,7 +12,7 @@
<!-- This path represents all run-time dependancies --> <!-- This path represents all run-time dependancies -->
<path id="java.path"> <path id="java.path">
<path refid="javac.path"/> <path refid="javac.path"/>
<pathelement path="${build.dir}"/> <pathelement path="${build.classes.dir}"/>
</path> </path>
<target name="init"> <target name="init">
@ -35,20 +35,38 @@
<target name="compile" depends="compile-parser"> <target name="compile" depends="compile-parser">
<!-- Make build directory if it does not exist --> <!-- Make build directory if it does not exist -->
<mkdir dir="${build.dir}"/> <mkdir dir="${build.classes.dir}"/>
<!-- Compile all sources to build directory --> <!-- Compile all sources to build directory -->
<javac <javac
srcdir="${src.dir}" srcdir="${src.dir}"
destdir="${build.dir}" destdir="${build.classes.dir}"
classpathref="javac.path" classpathref="javac.path"
debug="on" debug="on"
debuglevel="source,vars,lines"/> debuglevel="source,vars,lines"/>
</target> </target>
<target name="dist" depends="init,compile"> <target name="build" depends="compile,increment-build-number">
<mkdir dir="${dist.dir}"/> <jar
basedir="${build.classes.dir}"
destfile="${build.jar}">
<manifest>
<attribute
name="Main-Class"
value="edu.utexas.cs345.jdblisp.LISPRuntime"/>
<attribute
name="Built-By"
value="Jonathan Bernard (jdbernard@gmail.com)"/>
</manifest>
</jar>
</target>
<target name="dist" depends="build">
<mkdir dir="${dist.dir}/lib"/>
<move file="${build.jar}" tofile="${dist.jar}" />
<copy todir="${dist.dir}/lib">
<fileset dir="${lib.dir}"/>
</copy>
</target> </target>
</project> </project>

View File

@ -1,7 +1,12 @@
src.dir=src #Tue Nov 24 11:54:14 CST 2009
build.dir=build build.dir=build
src.dir=src
grammar.output.dir=${src.dir}/edu/utexas/cs345/jdblisp/parser
build.jar=${build.dir}/JCLisp-${application.version}.${build.number}.jar
build.number=6
dist.dir=dist dist.dir=dist
dist.jar=${dist.dir}/JCLisp-${application.version}.jar
lib.dir=lib lib.dir=lib
grammar.file=${src.dir}/edu/utexas/cs345/jdblisp/Parser.jj grammar.file=${src.dir}/edu/utexas/cs345/jdblisp/Parser.jj
grammar.output.dir=${src.dir}/edu/utexas/cs345/jdblisp/parser build.classes.dir=${build.dir}/classes
project.version=0.1.0 application.version=0.1.0

View File

@ -0,0 +1,46 @@
package edu.utexas.cs345.jdblisp;
/**
* Cons
* @author Jonathan Bernard (jdbernard@gmail.com)
*/
public class Cons extends Seq {
public final SExp cdr;
public Cons(SExp car, SExp cdr) {
super(car, (cdr == null ||
cdr == SExp.NIL ||
!(cdr instanceof Seq) ?
null : (Seq) cdr));
this.cdr = (cdr == SExp.NIL ? null : cdr);
}
public Cons(SExp car, List list) {
super(car, list.seq);
this.cdr = list.seq;
}
@Override
public String display(String offset) {
if (this.cdr == super.cdr)
return super.display(offset);
StringBuilder sb = new StringBuilder();
sb.append(offset);
sb.append("Cons: \n");
sb.append(car.display(offset + " "));
if (cdr != null) sb.append(cdr.display(offset));
return sb.toString();
}
@Override
public String toString() {
if (this.cdr == super.cdr) return super.toString();
else return car.toString() + " . " + cdr.toString();
}
}

View File

@ -94,7 +94,7 @@ public class HelpTopic {
} }
// any left over, it will fit on one line // any left over, it will fit on one line
if (i - lineStartIdx > 1) { if (i - lineStartIdx > 0) {
String lastLine = message.substring(lineStartIdx); String lastLine = message.substring(lineStartIdx);
printer.print(lastLine); printer.print(lastLine);
curLineLength += lastLine.length(); curLineLength += lastLine.length();

View File

@ -30,7 +30,12 @@ public class List implements SExp {
// look up in the symbol table // look up in the symbol table
FormEntry functionEntry = table.lookupFunction((Symbol) seq.car); FormEntry functionEntry = table.lookupFunction((Symbol) seq.car);
// call function // throw an eror if it is not defined
if (functionEntry == null)
throw new LispException("Undefined function "
+ ((Symbol) seq.car).name);
// call function if it is
return functionEntry.call(table, seq.cdr); return functionEntry.call(table, seq.cdr);
} }

View File

@ -35,6 +35,7 @@ public class Seq implements SExp {
return 1 + cdr.length(); return 1 + cdr.length();
} }
@Override
public String display(String offset) { public String display(String offset) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(offset); sb.append(offset);

View File

@ -222,6 +222,41 @@ public abstract class SpecialFormEntry implements FormEntry {
} }
}; };
// ----
// CONS
// ----
SpecialFormEntry CONS = new SpecialFormEntry(
environment,
new FormHelpTopic("CONS", "create a cons",
"(cons <object-1> <object-2>) => <cons>",
"Creates a fresh cons, the car of which is object-1 and the "
+ "cdr of which is object-2.",
"object-1", "an object",
"object-2", "an object",
"cons", "a cons"))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
SExp object1, object2;
Cons cons;
if (arguments.length() != 2)
throw new InvalidArgumentQuantityException(2,
arguments.length());
// get the two objects
object1 = arguments.car.eval(symbolTable);
object2 = arguments.cdr.car.eval(symbolTable);
if (object2 instanceof List) cons = new Cons(object1, (List) object2);
else cons = new Cons(object1, object2);
return new List(cons);
}
};
// ----- // -----
// DEFUN // DEFUN
// ----- // -----
@ -326,17 +361,18 @@ public abstract class SpecialFormEntry implements FormEntry {
// second argument: initial value // second argument: initial value
arguments = arguments.cdr; arguments = arguments.cdr;
if (arguments != null) if (arguments != null) {
initValue = arguments.car.eval(symbolTable); initValue = arguments.car.eval(symbolTable);
// thrid argument: documentation // third argument: documentation
arguments = arguments.cdr; arguments = arguments.cdr;
if (arguments != null) { if (arguments != null) {
if (!(arguments.car instanceof Str)) if (!(arguments.car instanceof Str))
throw new TypeException(arguments.car, Str.class); throw new TypeException(arguments.car, Str.class);
helpinfo = new HelpTopic(name.name, "variable", helpinfo = new HelpTopic(name.toString(), "variable",
((Str) arguments.car).value); ((Str) arguments.car).value);
}
} }
symbolTable.bind(name, symbolTable.bind(name,
@ -421,6 +457,69 @@ public abstract class SpecialFormEntry implements FormEntry {
} }
}; };
// ----
// GETF
// ----
SpecialFormEntry GETF = new SpecialFormEntry(
environment,
new FormHelpTopic("GETF", "",
"(getf <plist> <indicator> [<default>]) => <value>",
"getf finds a property on the plist whose property indicator "
+ "is identical to indicator, and returns its "
+ "corresponding property value. If there are multiple "
+ "properties with that property indicator, getf uses the "
+ "first such property. If there is no property with that "
+ "property indicator, default is returned.",
"plist", "a property list.",
"indicator", "an object",
"default", "an object. The default is NIL",
"value", "an object"))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
SExp plistEval;
Seq plistSeq;
SExp indicator;
SExp retVal = SExp.NIL;
// check number of arguments
if (arguments.length() < 2)
throw new InvalidArgumentQuantityException(
"form requires at least 2 arguments.");
// first argument: property list
plistEval = arguments.car.eval(symbolTable);
if (!(plistEval instanceof List))
throw new TypeException(arguments.car, List.class);
plistSeq = ((List) plistEval).seq;
// second argument: indicator
arguments = arguments.cdr;
indicator = arguments.car.eval(symbolTable);
// third argument: default value
arguments = arguments.cdr;
if (arguments != null)
retVal = arguments.car.eval(symbolTable);
while(plistSeq != null) {
// check this value for equality
if (plistSeq.car.equals(indicator))
if (plistSeq.cdr != null)
return plistSeq.cdr.car;
// advance to the next pair (or terminate)
plistSeq = (plistSeq.cdr == null ? null : plistSeq.cdr.cdr);
}
return retVal;
}
};
// ---- // ----
// HELP // HELP
// ---- // ----
@ -429,48 +528,45 @@ public abstract class SpecialFormEntry implements FormEntry {
environment, environment,
new FormHelpTopic("HELP", new FormHelpTopic("HELP",
"Display online help information for a topic.", "Display online help information for a topic.",
"(help <topic>)", "(help [<topic>*])",
null, null,
"topic", "topic",
"either a string representing the topic to lookup or a symbol")) "either a string representing the topic to lookup or a symbol"))
{ {
public SExp call(SymbolTable symbolTable, Seq arguments) public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException { throws LispException {
HelpTopic topic = null; ArrayList<HelpTopic> topics = new ArrayList<HelpTopic>();
// no arguments: print help for HELP // no arguments: print help for HELP
if (arguments == null) if (arguments == null)
return this.call(symbolTable, return this.call(symbolTable,
new Seq(new Symbol("HELP"), null)); new Seq(new Symbol("HELP"), null));
// too many arguments while (arguments != null) {
if (arguments.length() > 1) // try to find the topic or function help
throw new InvalidArgumentQuantityException(1, if (arguments.car instanceof Str) {
arguments.length()); topics.add(HelpTopic.helpTopics.get(
((Str) arguments.car).value));
} else if (arguments.car instanceof Symbol) {
// try to find the topic or function help // lookup help for funtion
if (arguments.car instanceof Str) {
topic = HelpTopic.helpTopics.get(
((Str) arguments.car).value);
} else if (arguments.car instanceof Symbol) {
try {
FormEntry fe = symbolTable.lookupFunction( FormEntry fe = symbolTable.lookupFunction(
(Symbol) arguments.car); (Symbol) arguments.car);
topic = fe.helpinfo(); if (fe != null) topics.add(fe.helpinfo());
} catch (LispException le) { topic = null; }
// lookup help for variable
VariableEntry ve = symbolTable.lookupVariable(
(Symbol) arguments.car);
if (ve != null) topics.add(ve.helpinfo);
}
arguments = arguments.cdr;
} }
// no topic found for (HelpTopic topic : topics)
if (topic == null) { topic.print(environment.getOutputStream());
new PrintWriter(environment.getOutputStream(), true)
.println(
"No help information found for topic '"
+ arguments.car.toString() + "'.");
return SExp.NIL;
}
topic.print(environment.getOutputStream());
return SExp.NIL; return SExp.NIL;
} }
}; };
@ -866,7 +962,13 @@ public abstract class SpecialFormEntry implements FormEntry {
FormEntry fe = symbolTable.lookupFunction((Symbol) arguments.car); FormEntry fe = symbolTable.lookupFunction((Symbol) arguments.car);
if (fe instanceof FunctionEntry) ((FunctionEntry) fe).enableTrace(true); if (fe == null)
throw new UndefinedFunctionException(
((Symbol) arguments.car));
if (fe instanceof FunctionEntry)
((FunctionEntry) fe).enableTrace(true);
// TODO: else throw error // TODO: else throw error
return SExp.NIL; return SExp.NIL;
@ -877,9 +979,12 @@ public abstract class SpecialFormEntry implements FormEntry {
environment.globalSymbolTable.bind(new Symbol("/"), DIV); environment.globalSymbolTable.bind(new Symbol("/"), DIV);
environment.globalSymbolTable.bind(new Symbol("*"), MUL); environment.globalSymbolTable.bind(new Symbol("*"), MUL);
environment.globalSymbolTable.bind(new Symbol("+"), SUM); environment.globalSymbolTable.bind(new Symbol("+"), SUM);
environment.globalSymbolTable.bind(new Symbol("CONS"), CONS);
environment.globalSymbolTable.bind(new Symbol("DEFUN"), DEFUN); environment.globalSymbolTable.bind(new Symbol("DEFUN"), DEFUN);
environment.globalSymbolTable.bind(new Symbol("DEFPARAMETER"), DEFPARAMETER);
environment.globalSymbolTable.bind(new Symbol("DEFVAR"), DEFVAR); environment.globalSymbolTable.bind(new Symbol("DEFVAR"), DEFVAR);
environment.globalSymbolTable.bind(new Symbol("ENABLE-DEBUG-AST"), ENABLEDEBUGAST); environment.globalSymbolTable.bind(new Symbol("ENABLE-DEBUG-AST"), ENABLEDEBUGAST);
environment.globalSymbolTable.bind(new Symbol("GETF"), GETF);
environment.globalSymbolTable.bind(new Symbol("HELP"), HELP); environment.globalSymbolTable.bind(new Symbol("HELP"), HELP);
environment.globalSymbolTable.bind(new Symbol("IF"), IF); environment.globalSymbolTable.bind(new Symbol("IF"), IF);
environment.globalSymbolTable.bind(new Symbol("LET"), LET); environment.globalSymbolTable.bind(new Symbol("LET"), LET);

View File

@ -10,7 +10,12 @@ public class Symbol implements SExp {
/** {@inheritdoc}*/ /** {@inheritdoc}*/
public SExp eval(SymbolTable table) throws LispException { public SExp eval(SymbolTable table) throws LispException {
// lookup value in the symbol table
VariableEntry ve = table.lookupVariable(this); VariableEntry ve = table.lookupVariable(this);
// err if not defined
if (ve == null) throw new UndefinedVariableException(this);
return ve.value; return ve.value;
} }

View File

@ -81,8 +81,7 @@ public class SymbolTable {
if (fe != null) return fe; if (fe != null) return fe;
// did not find function, and there is no outer scope // did not find function, and there is no outer scope
if (enclosingTable == null) if (enclosingTable == null) return null;
throw new LispException("Undefined function " + s.toString());
// search outer scope // search outer scope
return enclosingTable.lookupFunction(s); return enclosingTable.lookupFunction(s);
@ -95,8 +94,7 @@ public class SymbolTable {
if (ve != null) return ve; if (ve != null) return ve;
// did not find variable entry and there is no outer scope // did not find variable entry and there is no outer scope
if (enclosingTable == null) if (enclosingTable == null) return null;
throw new LispException("Undefined variable " + s.toString());
// search outer scope // search outer scope
return enclosingTable.lookupVariable(s); return enclosingTable.lookupVariable(s);

View File

@ -0,0 +1,15 @@
package edu.utexas.cs345.jdblisp;
/**
* UndefinedFunctionException
* @author Jonathan Bernard (jdbernard@gmail.com)
*/
public class UndefinedFunctionException extends LispException {
Symbol symbol;
public UndefinedFunctionException(Symbol symbol) {
super("Undefined function: " + symbol.toString());
this.symbol = symbol;
}
}

View File

@ -0,0 +1,15 @@
package edu.utexas.cs345.jdblisp;
/**
* UndefinedVariableException
* @author Jonathan Bernard
*/
public class UndefinedVariableException extends LispException {
public final Symbol symbol;
public UndefinedVariableException(Symbol symbol) {
super("Undefined variable: " + symbol.toString());
this.symbol = symbol;
}
}

View File

@ -1,7 +1,10 @@
(defun make-cd (title artist rating ripped) (defun make-cd (title artist rating ripped)
(list title artist rating ripped)) (list :title title :artist artist :rating rating :ripped ripped))
(make-cd "Roses" "Kathy Mattea" 7 t)
(defvar *db* nil) (defvar *db* nil)
(defun add-record (cd) (setq *db* (cons cd *db*)))
(add-record (make-cd "Roses" "Kathy Mattea" 7 t))
(add-record (make-cd "Fly" "Dixie Chicks" 8 t))
(add-record (make-cd "Home" "Dixie Chicks" 9 t))

View File

@ -2,12 +2,20 @@ Outstanding
----------- -----------
- Implement FORMAT - Implement FORMAT
- Implement GETF
- Implement lambdas - Implement lambdas
- Implement LIST* - Implement LIST*
- Implement macros - Implement macros
- Implement packages - Implement packages
- Implement READ and PRINT - Implement READ and PRINT
- Implement SETF
- Redefine DEFUN as a macro - Redefine DEFUN as a macro
- Help for property list
- Help for keyword
- Help for symbol
- Help for sexp
- Help for number
- Help for string
Partially Done Partially Done
-------------- --------------