Embedded CAL

2 views
Skip to first unread message

Andrew Eisenberg

unread,
Sep 12, 2007, 4:25:22 PM9/12/07
to cal_la...@googlegroups.com
Hi everyone,

I'd like to introduce Embedded CAL, the work I did this summer.
Embedded CAL allows you to add CAL expressions and modules directly
into the Java editor. The main goal of Embedded CAL is to make it as
easy as possible to switch back and forth between Java and CAL, thus
lowering the barrier of entry to CAL, and providing more opportunities
to use CAL in small places as part of a larger Java application.
Click here if you are interested reading more about Embedded CAL:
http://groups.google.com/group/cal_language/web/embedded-cal

I will be updating this page periodically and notifying this group as
I make big changes.

I spent three and a half months as a summer student working with the
Quark team. I had a great time and I learned quite a bit. And so, I
appreciate all the help that the Quark team has given me with this
project.

thank you,
Andrew Eisenberg

Message has been deleted
Message has been deleted

Steve Harris

unread,
Sep 12, 2007, 4:46:12 PM9/12/07
to CAL Language Discussion
Andrew,
that looks *really* nice, that's how integrated language editing
should look!
I'll definitely be interested in giving it a try after it's released.


Tom Davies

unread,
Sep 12, 2007, 9:50:27 PM9/12/07
to CAL Language Discussion
That looks very cool, Andrew. What does the Java source code look
like?

I've been playing with Java/CAL integration using Javassist to replace
annotated methods with calls to CAL functions, so you can say:
// Start.java
...
@Cal(workspace = "myworkspace.cws", module = "TDavies.Start")
public class Start {

@Cal(outputPolicy=CalOutputPolicy.ITERATOR)
private Iterator<Integer> randomList() {
return null;
}
... other java methods...
}

and have the randomList() method replace at class load time with a
call to:
// Start.cal
...
randomList :: [Int];
public randomList = map (\n -> n + 1) $ randomBoundedInts initialSeed
10;


Andrew Eisenberg

unread,
Sep 13, 2007, 1:31:00 AM9/13/07
to cal_la...@googlegroups.com
Thanks.

The underlying Java is just a method invocation to the embedded CAL runtime (this library must be on the classpath at runtime). The arguments of the method invocation store all of the attributes of the embedded editor.  There are quite a few of them, but some of the arguments are only needed at edit-time (eg- the height and width of the box).

(view this in fixed width font if possible)

/* <-- */RunQuark.evaluateExpression(
  "Bus \"#215\"",           // the actual CAL expression
  new InputTuple[] {  },    // list of arguments and input policies
  null,                     // output policy --- null means inferred
  "TransitTripDemo",        // the module that this runs in (inferred)
  25, 88,                   // height and width of the box
  true,                     // true if no syntax errors, false if errors
  false                     // show the expanded view of the editor. 
                            // (this last one is explained in the video)
) /* --> */



The beginning and end delimiters are important.  They signify to the editor that what comes between should look like an embedded editor.  Because embedded editors are stored as method invocations, there is considerable flexibility in where they can be placed.

If things go well, it should be unimportant what is actually behind the embedded editor, but things don't always go well and sometimes it is necessary to peak behind the scenes.

That's interesting what you are doing with Javassist.  What do the pre-processors look like?  How do you pass in arguments?  Are the entry points generated and then cached, or are they re-created for each entry?

For now, Embedded CAL does the simplest thing possible and regenerates entry points each time an embedded editor is executed, but it really should be cached.

--a

Tom Davies

unread,
Sep 13, 2007, 5:09:17 PM9/13/07
to CAL Language Discussion

On Sep 13, 3:31 pm, "Andrew Eisenberg" <a...@cs.ubc.ca> wrote:
[snip]

Thanks for the details Andrew.

> That's interesting what you are doing with Javassist. What do the
> pre-processors look like? How do you pass in arguments? Are the entry
> points generated and then cached, or are they re-created for each entry?

On Sep 13, 3:31 pm, "Andrew Eisenberg" <a...@cs.ubc.ca> wrote:

> That's interesting what you are doing with Javassist. What do the
> pre-processors look like? How do you pass in arguments? Are the entry
> points generated and then cached, or are they re-created for each entry?

The class which does the code changes looks like this:

package tdavies.cal;

/**
* This class visits the annotations on a class and provides new
method bodies, as Javassist strings,
* for those methods which should be delegated to CAL functions.
*
* @author tomd
*
*/
public class CalAnnotationInterpreter {
private String classWorkspace;
private String classModule;
private boolean all;

/**
* The client must call this method before calling visitMethod.
*
* @param className a String naming this class.
* @param classAnnotation the Cal annotation on this class, may be
null.
*/
public void visitClass(String className, Cal classAnnotation) {
classWorkspace = null;
classModule = null;
all = false;

if (classAnnotation != null) {
if (!classAnnotation.workspace().equals("")) {
classWorkspace = classAnnotation.workspace();
}
if (!classAnnotation.module().equals("")) {
classModule = classAnnotation.module();
}
if (!classAnnotation.function().equals("")) {
throw new RuntimeException(
"Error: cannot specify a function name ("
+ classAnnotation.function()
+ ") at class level in class "
+ className);
}
all = classAnnotation.allMethods();
}
}

/**
* The client must call this method for each method on the class
whose annotations are being
* processed, even methods with no Cal annotation.
*
* @param methodName a String naming the method.
* @param methodAnnotation the Cal annotation for this method, may be
null.
* @return a String containing the replacement body for the method,
or null if the body should not be replaced.
*/
public String visitMethod(String methodName, Cal methodAnnotation) {
if (methodAnnotation != null || all) {
String workspace = classWorkspace;
String module = classModule;
String function = methodName;
if (methodAnnotation != null) {
if (!methodAnnotation.workspace().equals("")) {
workspace = methodAnnotation.workspace();
}
if (!methodAnnotation.module().equals("")) {
module = methodAnnotation.module();
}
if (!methodAnnotation.function().equals("")) {
function = methodAnnotation.function();
}
}
return "{ return ($r)tdavies.CalFunctionInvoker.runFunction(\""
+ workspace
+ "\", \""
+ module
+ "\", \""
+ function
+ "\", "
+ "tdavies.CalOutputPolicy." + (methodAnnotation == null ?
CalOutputPolicy.DEFAULT : methodAnnotation.outputPolicy()).toString()
+ ", $args); }";
}
else
{
return null;
}
}
}

Javassist can pass on the arguments to the function as an Object[].

I cache the EntryPointSpecs -- but perhaps I could cache more? And I
don't use ExecutionContexts.

The CalFunctionInvoker class referred to in the generated body above
looks like:

package tdavies.cal;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.openquark.cal.compiler.CompilerMessageLogger;
import org.openquark.cal.compiler.MessageLogger;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.compiler.io.EntryPointSpec;
import org.openquark.cal.compiler.io.OutputPolicy;
import org.openquark.cal.services.BasicCALServices;

/**
* This class is responsible for calling a CAL function, given its
workspace, module
* and name.
*
* @author tomd
*
*/
public class CalFunctionInvoker {
private static Map<String, BasicCALServices> workspaces = new
ConcurrentHashMap<String, BasicCALServices>();
//TODO: the key of the cache should include the workspace name
private static Map<QualifiedName, EntryPointSpec> entryPoints = new
ConcurrentHashMap<QualifiedName, EntryPointSpec>();

private static BasicCALServices getWorkspace(String name) {
BasicCALServices bcs = workspaces.get(name);
if (bcs == null) {
CompilerMessageLogger messageLogger = new MessageLogger();
bcs = BasicCALServices.makeCompiled(name, messageLogger);
if (bcs == null)
throw new RuntimeException(messageLogger.toString());
workspaces.put(name, bcs);
}
return bcs;
}

private static EntryPointSpec getEntryPointSpec(String module,
String function, OutputPolicy outputPolicy) {
QualifiedName qn = QualifiedName.make(module, function);
EntryPointSpec eps = entryPoints.get(qn);
if (eps == null) {
eps = EntryPointSpec.make(qn, null, outputPolicy);
entryPoints.put(qn, eps);
}
return eps;
}

public static Object runFunction(String workspace, String module,
String function, CalOutputPolicy outputPolicy, Object[] args) {
try {
return getWorkspace(workspace).runFunction(
getEntryPointSpec(module, function,outputPolicy.outputPolicy),
args);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

Tom

Reply all
Reply to author
Forward
0 new messages