![JAR search and dependency download from the Maven repository](/logo.png)
convex.core.lang.Compiler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of convex-core Show documentation
Show all versions of convex-core Show documentation
Convex core libraries and common utilities
The newest version!
package convex.core.lang;
import java.util.Map;
import convex.core.Constants;
import convex.core.ErrorCodes;
import convex.core.data.ABlobLike;
import convex.core.data.ACell;
import convex.core.data.ADataStructure;
import convex.core.data.AHashMap;
import convex.core.data.AList;
import convex.core.data.AMap;
import convex.core.data.ASequence;
import convex.core.data.ASet;
import convex.core.data.AString;
import convex.core.data.AVector;
import convex.core.data.Address;
import convex.core.data.Cells;
import convex.core.data.Keywords;
import convex.core.data.List;
import convex.core.data.MapEntry;
import convex.core.data.Maps;
import convex.core.data.Sets;
import convex.core.data.Symbol;
import convex.core.data.Syntax;
import convex.core.data.Vectors;
import convex.core.data.prim.CVMBool;
import convex.core.data.prim.CVMLong;
import convex.core.data.type.Types;
import convex.core.lang.Context.CompilerState;
import convex.core.lang.impl.AClosure;
import convex.core.lang.impl.CoreFn;
import convex.core.lang.impl.MultiFn;
import convex.core.lang.ops.Cond;
import convex.core.lang.ops.Constant;
import convex.core.lang.ops.Def;
import convex.core.lang.ops.Do;
import convex.core.lang.ops.Invoke;
import convex.core.lang.ops.Lambda;
import convex.core.lang.ops.Let;
import convex.core.lang.ops.Local;
import convex.core.lang.ops.Lookup;
import convex.core.lang.ops.Query;
import convex.core.lang.ops.Special;
import convex.core.lang.ops.Try;
/**
* Compiler class responsible for transforming forms (code as data) into an
* Op tree for execution.
*
* Phases in complete evaluation:
*
* - Expansion (form -> AST)
* - Compile (AST -> op)
* - Execute (op -> result)
*
*
* Expanded form follows certain rules: - No remaining macros / expanders in
* leading list positions
*
* TODO: consider including typechecking in expansion phase as per:
* http://www.ccs.neu.edu/home/stchang/pubs/ckg-popl2017.pdf
*
* "A language that doesn't affect the way you think about programming is not
* worth knowing." ― Alan Perlis
*/
public class Compiler {
/**
* Expands and compiles a form. Equivalent to running expansion followed by
* compile. Should not be used directly, intended for use via
* Context.expandCompile(...)
*
* @param form A form, either raw or wrapped in a Syntax Object
* @param context Compilation context
* @return Context with compiled op as result
*/
static Context expandCompile(ACell form, Context context) {
// expand phase starts with initial expander
AFn ex = INITIAL_EXPANDER;
// Use initial expander both as current and continuation expander
// call expand via context to get correct depth and exception handling
final Context ctx = context.invoke(ex, form,ex);
if (ctx.isExceptional()) return ctx;
ACell c=ctx.getResult();
return ctx.compile(c);
}
/**
* Compiles a single form. Should not be used directly, intended for use via
* Context.compile(...)
*
* Updates context with result, juice consumed
*
* @param expandedForm A fully expanded form expressed as a Syntax Object
* @param context
* @return Context with compiled Op as result
*/
@SuppressWarnings("unchecked")
static Context compile(ACell form, Context context) {
if (form==null) return compileConstant(context,null);
if (form instanceof Symbol) {
return compileSymbol((Symbol) form, context);
}
if (form instanceof ADataStructure) {
if (form instanceof AList) return compileList((AList) form, context);
if (form instanceof AVector) return compileVector((AVector) form, context);
if (form instanceof AMap) return compileMap((AMap) form, context);
if (form instanceof ASet) return compileSet((ASet) form, context);
return context.withCompileError("Unexpected data structure: "+form.getClass());
}
if (form instanceof ABlobLike) {
ABlobLike> bf=(ABlobLike>)form;
if (bf.count()==0) {
if (bf instanceof AString) return context.withResult(Juice.COMPILE_CONSTANT, Constant.EMPTY_STRING);
}
return compileConstant(context, form);
}
if (form instanceof Syntax) return compileSyntax((Syntax) form, context);
if (form instanceof CVMBool) {
return compileBoolean(context, (CVMBool)form);
}
if (form instanceof AOp) {
// already compiled, just return directly
return context.withResult(Juice.COMPILE_CONSTANT, (AOp>)form);
}
// return as a constant literal, handles keywords, integers etc.
return compileConstant(context,form);
}
/**
* Compiles a sequence of forms, returning a vector of ops in the updated
* context. Equivalent to calling compile sequentially on each form.
*
* @param forms
* @param context
* @return Context with Vector of compiled ops as result
*/
static Context compileAll(ASequence forms, Context context) {
if (forms == null) return context.withResult(Vectors.empty()); // consider null as empty list
int n = forms.size();
AVector> obs = Vectors.empty();
for (int i = 0; i < n; i++) {
ACell form = forms.get(i);
context = context.compile(form);
if (context.isExceptional()) return context;
obs = obs.conj((AOp>) context.getResult());
}
return context.withResult(obs);
}
private static Context compileSyntax(Syntax s, Context context) {
context=compile(s.getValue(),context);
return context;
}
/**
* Compiles a Symbol as found in regular code
* @param sym Symbol
* @param context
* @return Context with compiled symbol lookup
*/
private static Context compileSymbol(Symbol sym, Context context) {
// First check for lexically defined Symbols
CompilerState cs=context.getCompilerState();
// First check if we hit a local declaration within the current compile context
if (cs!=null) {
CVMLong position=cs.getPosition(sym);
if (position!=null) {
Local> op=Local.create(position.longValue());
return context.withResult(Juice.COMPILE_LOOKUP,op);
}
}
// Next check for special values
int ch=sym.getName().charAt(0);
if (ch=='*') {
Special> maybeSpecial=Special.forSymbol(sym);
if (maybeSpecial!=null) {
return context.withResult(maybeSpecial);
}
} else if (ch=='#') {
ACell maybeCoreImplicit = Core.CORE_FORMS.get(sym);
if (maybeCoreImplicit!=null) {
return compileConstant(context,maybeCoreImplicit);
}
}
// Regular symbol lookup in environment
Address address=context.getAddress();
return compileEnvSymbol(address,sym,context);
}
private static Context compileEnvSymbol(Address address,Symbol sym, Context context) {
// Optional code for :static embedding
if (Constants.OPT_STATIC) {
// Get metadata for symbol.
AHashMap meta=context.lookupMeta(sym);
// If static, embed value directly as constant
if ((meta!=null)&&meta.get(Keywords.STATIC)==CVMBool.TRUE) {
ACell value=context.lookupValue(sym);
return context.withResult(Juice.COMPILE_LOOKUP,Constant.create(value));
}
}
// Check if the symbol references an existing declaration
context=context.lookupDefiningAddress(address, sym);
if (context.isExceptional()) return context; // could be juice error?
Address a=context.getResult();
if (a!=null) return context.withResult(Juice.COMPILE_LOOKUP,Lookup.create(Constant.of(a),sym));
// Finally revert to a lookup in the current address / environment
Lookup> lookUp=Lookup.create(Constant.of(address),sym);
return context.withResult(Juice.COMPILE_LOOKUP, lookUp);
}
private static Context compileSetBang(AList list, Context context) {
if (list.count()!=3) return context.withArityError("set! requires two arguments, a symbol and an expression");
// First position must be a Symbol in `(set! sym exp)`
ACell a1=list.get(1);
if (!(a1 instanceof Symbol)) return context.withError(ErrorCodes.SYNTAX,"set! requires a symbol as first argument");
Symbol sym=(Symbol)a1;
// Extract Expression
context=context.compile(list.get(2));
if (context.isExceptional()) return context;
AOp> exp=(AOp>) context.getResult();
// Check for local binding
CompilerState cs=context.getCompilerState();
CVMLong position=(cs==null)?null:context.getCompilerState().getPosition(sym);
if (position==null) {
// If not a local binding, create a Def Op iff definition already exists
if (context.getEnvironment().containsKey(sym)) {
Def> op = Def.create(sym, exp);
return context.withResult(Juice.COMPILE_NODE,op);
}
return context.withUndeclaredError(sym);
} else {
// Otherwise must be a Local binding, so use a Set op
AOp> op=convex.core.lang.ops.Set.create(position.longValue(), exp);
return context.withResult(Juice.COMPILE_NODE,op);
}
}
/**
* Compile a lookup of the form (lookup 'foo) or (lookup addr 'foo)
* @param list Lookup form
* @param context Compiler context
* @return Op performing Lookup
*/
@SuppressWarnings("unchecked")
private static Context compileLookup(AList list, Context context) {
long n=list.count();
if ((n<2)||(n>3)) return context.withArityError("lookup requires one or two arguments: an optional expression specifying an account and a Symbol");
AOp exp=null;
if (n==3) {
// second element of list should be an expression that evaluates to an Address
context=context.compile(list.get(1));
if (context.isExceptional()) return context;
exp=(AOp) context.getResult();
}
ACell a1=list.get(n-1);
if (!(a1 instanceof Symbol)) return context.withCompileError("lookup requires a Symbol as last argument");
Symbol sym=(Symbol)a1;
// Pick up possible static definitions in case of constant Address
if (Constants.OPT_STATIC&&(exp instanceof Constant)) {
Address address=RT.ensureAddress(((Constant)exp).getValue());
if (address==null) return context.withError(ErrorCodes.CAST,"lookup first expression must be an Address");
// Get metadata for symbol.
AHashMap meta=context.lookupMeta(address,sym);
// If static, embed value directly as constant
if ((meta!=null)&&meta.get(Keywords.STATIC)==CVMBool.TRUE) {
ACell value=context.lookupValue(address,sym);
return context.withResult(Juice.COMPILE_LOOKUP,Constant.create(value));
}
}
Lookup> op=Lookup.create(exp,sym);
return context.withResult(Juice.COMPILE_NODE,op);
}
/**
* Compiles a map of the form {a b, c d}
* @param form Form as a Map
* @param context
* @return Op producing the given map
*/
private static Context compileMap(AMap form, Context context) {
int n = form.size();
if (n==0) return context.withResult(Juice.COMPILE_CONSTANT, Constant.EMPTY_MAP);
ACell[] vs = new ACell[1 + n * 2];
vs[0] = Symbols.HASH_MAP;
for (int i = 0; i < n; i++) {
MapEntry me = form.entryAt(i);
vs[1 + i * 2] = me.getKey();
vs[1 + i * 2 + 1] = me.getValue();
}
return compileList(List.create(vs), context);
}
/**
* Compile a set literal of the form #{1 2} as (hash-set 1 2)
*/
private static Context compileSet(ASet form, Context context) {
if (form.isEmpty()) return context.withResult(Juice.COMPILE_CONSTANT, Constant.EMPTY_SET);
AVector vs = Vectors.empty();
for (ACell o : form) {
vs = vs.conj(o);
}
vs = vs.conj(Symbols.HASH_SET);
return compileList(List.reverse(vs), context);
}
// A vector literal needs to be compiled as (vector ....)
private static Context compileVector(AVector vec, Context context) {
int n = vec.size();
// Zero length vector fast path compiles to a constant
if (n == 0) return context.withResult(Juice.COMPILE_CONSTANT, Constant.EMPTY_VECTOR);
context = context.compileAll(vec);
if (context.isExceptional()) return context;
AVector> obs = context.getResult();
// return a 'vector' call - note function arg is a constant, we don't want to
// lookup on the 'vector' symbol
Constant fn = Constant.create(Core.VECTOR);
return context.withResult(Juice.COMPILE_NODE, Invoke.create(fn, obs));
}
private static Context compileConstant(Context context, ACell value) {
return context.withResult(Juice.COMPILE_CONSTANT, Constant.create(value));
}
private static Context compileBoolean(Context context, CVMBool value) {
return context.withResult(Juice.COMPILE_CONSTANT, Constant.forBoolean(value.booleanValue()));
}
/**
* Returns true if the form is a List starting with value equal to the
* the specified element
*
* @param element
* @param form
* @return True if form is a list starting with a Syntax Object wrapping the
* specified element, false otherwise.
*/
@SuppressWarnings("unchecked")
protected static boolean isListStarting(Symbol element, ACell form) {
if (!(form instanceof AList)) return false;
AList list = (AList) form;
if (list.count() == 0) return false;
ACell firstElement=list.get(0);
return Cells.equals(element, Syntax.unwrap(firstElement));
}
@SuppressWarnings("unchecked")
private static Context compileList(AList list, Context context) {
int n = list.size();
if (n == 0) return context.withResult(Juice.COMPILE_CONSTANT, Constant.EMPTY_LIST);
// first entry in list should be syntax
ACell first = list.get(0);
ACell head = Syntax.unwrap(first);
if (head instanceof Symbol) {
Symbol sym = (Symbol) head;
if (sym.equals(Symbols.DO)) return compileDo(list,context);
if (sym.equals(Symbols.LET)) return compileLet(list, context, false);
if (sym.equals(Symbols.LOOKUP)) return compileLookup(list, context);
if (sym.equals(Symbols.COND)) {
context = context.compileAll(list.next());
if (context.isExceptional()) return context;
Cond> op = Cond.create((AVector>) (context.getResult()));
return context.withResult(Juice.COMPILE_NODE, op);
}
if (sym.equals(Symbols.DEF)) return compileDef(list, context);
if (sym.equals(Symbols.FN)) return compileFn(list, context);
if (sym.equals(Symbols.QUOTE)) {
if (list.size() != 2) return context.withArityError("quote requires one argument.");
return compileConstant(context,list.get(1));
}
if (sym.equals(Symbols.QUASIQUOTE)) {
return context.withCompileError("unexpanded quasiquote in compiler. Remember to expand first!");
}
if (sym.equals(Symbols.UNQUOTE)) {
// execute the unquoted code directly to get a form to compile
if (list.size() != 2) return context.withArityError("unquote requires one argument.");
context = context.expandCompile(list.get(1));
if (context.isExceptional()) return context;
AOp> quotedOp = context.getResult();
Context rctx = context.execute(quotedOp);
if (rctx.isExceptional()) return rctx;
Syntax resultForm = Syntax.create(rctx.getResult());
// need to expand and compile here, since we just created a raw form
return expandCompile(resultForm, context);
}
if (sym.equals(Symbols.TRY)) return compileTry(list,context);
if (sym.equals(Symbols.QUERY)) {
context = context.compileAll(list.next());
if (context.isExceptional()) return context;
Query> op = Query.create((AVector>) context.getResult());
return context.withResult(Juice.COMPILE_NODE, op);
}
if (sym.equals(Symbols.LOOP)) return compileLet(list, context, true);
if (sym.equals(Symbols.SET_BANG)) return compileSetBang(list, context);
}
// must be a regular function call
context = context.compileAll(list);
if (context.isExceptional()) return context;
Invoke> op = Invoke.create((AVector>) context.getResult());
return context.withResult(Juice.COMPILE_NODE, op);
}
@SuppressWarnings("unchecked")
private static Context compileLet(ASequence list, Context context,
boolean isLoop) {
// list = (let [...] a b c ...)
int n=list.size();
if (n<2) return context.withSyntaxError(list.get(0) + " requires a binding form vector at minimum");
ACell bo = list.get(1);
if (!(bo instanceof AVector))
return context.withSyntaxError(list.get(0) + " requires a vector of binding forms but got: " + bo);
AVector bv = (AVector) bo;
int bn = bv.size();
if ((bn & 1) != 0) return context.withSyntaxError(
list.get(0) + " requires a binding vector with an even number of forms but got: " + bn);
AVector bindingForms = Vectors.empty();
AVector> ops = Vectors.empty();
for (int i = 0; i < bn; i += 2) {
// Get corresponding op
context = context.expandCompile(bv.get(i + 1));
if (context.isExceptional()) return context;
AOp op = (AOp) context.getResult();
ops = ops.conj(op);
// Get a binding form. Note binding happens *after* op
ACell bf = bv.get(i);
context=compileBinding(bf,context);
if (context.isExceptional()) return context;
bindingForms = bindingForms.conj(context.getResult());
}
int exs = n - 2; // expressions in let after binding vector
for (int i = 2; i < 2 + exs; i++) {
context = context.expandCompile(list.get(i));
if (context.isExceptional()) return context;
AOp op = (AOp) context.getResult();
ops = ops.conj(op);
}
AOp> op = Let.create(bindingForms, ops, isLoop);
return context.withResult(Juice.COMPILE_NODE, op);
}
/**
* Compiles a binding form. Updates the current CompilerState. Should save compiler state if used
* @param bindingForm
* @param context
* @return
*/
private static Context compileBinding(ACell bindingForm,Context context) {
CompilerState cs=context.getCompilerState();
if (cs==null) cs=CompilerState.EMPTY;
cs=updateBinding(bindingForm,cs);
if (cs==null) return context.withCompileError("Bad binding form");
context=context.withCompilerState(cs);
return context.withResult(bindingForm);
}
@SuppressWarnings("unchecked")
private static CompilerState updateBinding(ACell bindingForm,CompilerState cs) {
bindingForm=Syntax.unwrap(bindingForm);
if (bindingForm instanceof Symbol) {
Symbol sym=(Symbol)bindingForm;
if (!sym.equals(Symbols.UNDERSCORE)) {
cs=cs.define(sym, null); // TODO: metadata?
}
} else if (bindingForm instanceof AVector) {
AVector v=(AVector)bindingForm;
boolean foundAmpersand=false;
long vcount=v.count(); // count of binding form symbols (may include & etc.)
for (long i=0; i=(vcount-1)) return null; // trailing ampersand
foundAmpersand=true;
bf=v.get(i+1);
i++;
}
cs=updateBinding(bf,cs);
if (cs==null) return null;
}
} else {
cs=null;
}
return cs;
}
/**
* Compiles a lambda function form "(fn [...] ...)" to create a Lambda op.
*
* @param list
* @param context
* @return Context with compiled op as result.
*/
@SuppressWarnings("unchecked")
private static Context compileFn(AList list, Context context) {
// list.get(0) is presumably fn
int n = list.size();
if (n < 2) return context.withArityError("fn requires parameter vector and body in form: " + list);
// check if we have a vector, in which case we have a single function definition
ACell firstObject = Syntax.unwrap(list.get(1));
if (firstObject instanceof AVector) {
AVector paramsVector=(AVector) firstObject;
AList bodyList=list.drop(2);
return compileFnInstance(paramsVector,bodyList,context);
}
return compileMultiFn(list.drop(1),context);
}
@SuppressWarnings({ "unchecked"})
private static Context compileMultiFn(AList list, Context context) {
AVector> fns=Vectors.empty();
int num=list.size();
for (int i=0; i) o,context);
if (context.isExceptional()) return context;
AClosure compiledFn=((Lambda) context.getResult()).getFunction();
fns=fns.conj(compiledFn);
}
MultiFn> mf=MultiFn.create(fns);
Lambda> op = Lambda.create(mf);
return context.withResult(Juice.COMPILE_NODE, op);
}
/**
* Compiles a function instance function form "([...] ...)" to create a Lambda op.
*
* @param list
* @param context
* @return Context with compiled op as result.
*/
@SuppressWarnings("unchecked")
private static Context compileFnInstance(AList list, Context context) {
int n = list.size();
if (n < 1) return context.withArityError("fn requires parameter vector and body in form: " + list);
ACell firstObject = Syntax.unwrap(list.get(0));
if (firstObject instanceof AVector) {
AVector paramsVector=(AVector) firstObject;
AList bodyList=list.drop(1);
return compileFnInstance(paramsVector,bodyList,context);
}
return context.withSyntaxError("fn instance requires a vector of parameters but got form: " + list);
}
@SuppressWarnings("unchecked")
private static Context compileFnInstance(AVector paramsVector, AList bodyList,Context context) {
// need to save compiler state, since we are compiling bindings
CompilerState savedCompilerState=context.getCompilerState();
context=compileBinding(paramsVector,context);
if (context.isExceptional()) return context.withCompilerState(savedCompilerState); // restore before return
paramsVector=(AVector) context.getResult();
context = context.compileAll(bodyList);
if (context.isExceptional()) return context.withCompilerState(savedCompilerState); // restore before return
int n=bodyList.size();
AOp> body;
if (n == 0) {
// no body, so function just returns nil
body = Constant.nil();
} else if (n == 1) {
// one body element, so just unwrap from list
body = ((ASequence>) context.getResult()).get(0);
} else {
// wrap multiple expressions in implicit do
body = Do.create(((ASequence>) context.getResult()));
}
Lambda> op = Lambda.create(paramsVector, body);
context=context.withCompilerState(savedCompilerState);
return context.withResult(Juice.COMPILE_NODE, op);
}
private static Context compileDef(AList list, Context context) {
long n = list.count();
if (n < 2) return context.withCompileError("def requires a symbol as second argument");
if (n > 3) return context.withCompileError("Too many arguments to def");
ACell symArg=list.get(1);
{// check we are actually defining a symbol
ACell sym = Syntax.unwrapAll(symArg);
if (!(sym instanceof Symbol)) return context.withCompileError("def requires a Symbol as first argument but got: " + RT.getType(sym));
}
Def> op;
if (n==3) {
// We have a value, so need to compile the value generating op
ACell exp=list.get(2);
// move metadata from expression. TODO: do we need to expand this first?
if (exp instanceof Syntax) {
symArg=Syntax.create(symArg).mergeMeta(((Syntax)exp).getMeta());
exp=Syntax.unwrap(exp);
}
context = context.compile(exp);
if (context.isExceptional()) return context;
AOp> valOp=context.getResult();
op=Def.create(symArg, valOp);
} else {
op=Def.create(symArg);
}
return context.withResult(Juice.COMPILE_NODE, op);
}
// Compile do: note optimisation for small forms
private static Context compileDo(AList list, Context context){
list=list.next(); // advance past "do", might be nothing left....
if (list==null) return context.withResult(Juice.COMPILE_NODE,Constant.NULL);
context = context.compileAll(list);
if (context.isExceptional()) return context;
AVector> ops=context.getResult();
if (list.count()==1) return context.withResult(Juice.COMPILE_NODE, ops.get(0));
Do> op = Do.create(ops);
return context.withResult(Juice.COMPILE_NODE, op);
}
// Compile do: note optimisation for small forms
private static Context compileTry(AList list, Context context){
list=list.next(); // advance past "try", might be nothing left....
if (list==null) return context.withResult(Juice.COMPILE_NODE,Constant.NULL);
context = context.compileAll(list);
if (context.isExceptional()) return context;
AVector> ops=context.getResult();
if (list.count()==1) return context.withResult(Juice.COMPILE_NODE, ops.get(0));
Try> op = Try.create(ops);
return context.withResult(Juice.COMPILE_NODE, op);
}
/**
* Initial expander used for expansion of forms prior to compilation.
*
* Should work on both raw forms and syntax objects.
*
* Follows the "Expansion-Passing Style" approach of Dybvig, Friedman, and Haynes
*/
public static final CoreFn INITIAL_EXPANDER =new CoreFn(Symbols.STAR_INITIAL_EXPANDER,259) {
@SuppressWarnings("unchecked")
@Override
public Context invoke(Context context,ACell[] args ) {
if (args.length!=2) return context.withArityError(exactArityMessage(2, args.length));
ACell x = args[0];
AFn cont=RT.ensureFunction(args[1]);
if (cont==null) return context.withCastError(1, args,Types.FUNCTION);
// If x is a Syntax object, need to compile the datum
// TODO: check interactions with macros etc.
if (x instanceof Syntax) {
Syntax sx=(Syntax)x;
ACell[] nargs=args.clone();
nargs[0]=sx.getValue();
context=context.invoke(this, nargs);
if (context.isExceptional()) return context;
ACell expanded=context.getResult();
Syntax result=Syntax.mergeMeta(expanded,sx);
return context.withResult(Juice.EXPAND_CONSTANT, result);
}
// Check for data structures, which potentially need expansion
if (x instanceof ADataStructure) {
ACell form = x;
// First check for sequences. This covers most cases.
if (form instanceof ASequence) {
// first check for List containing an expander
if (form instanceof AList) {
AList listForm = (AList) form;
int n = listForm.size();
// consider length 0 lists as constant
if (n == 0) return context.withResult(Juice.EXPAND_CONSTANT, x);
// we need to check if the form itself starts with an expander
ACell first = Syntax.unwrap(listForm.get(0));
// check for macro / expander in initial position.
// Note that 'quote' is handled by this, via QUOTE_EXPANDER
AFn expander = context.lookupExpander(first);
if (expander!=null) {
return context.expand(expander,x, cont); // (exp x cont)
}
}
// need to recursively expand collection elements
// OK for vectors and lists
ASequence seq = (ASequence) form;
if (seq.isEmpty()) return context.withResult(Juice.EXPAND_CONSTANT, x);
long n=seq.count();
for (long i=0; i updated = Sets.empty();
for (ACell elem : ((ASet) form)) {
ctx = ctx.expand(cont, elem, cont);
if (ctx.isExceptional()) return ctx;
ACell newElement = ctx.getResult();
updated = updated.conj(newElement);
}
return ctx.withResult(Juice.EXPAND_SEQUENCE, updated);
}
if (form instanceof AMap) {
Context ctx = context;
AMap updated = Maps.empty();
for (Map.Entry me : ((AMap) form).entrySet()) {
// get new key
ctx = ctx.expand(cont,me.getKey(), cont);
if (ctx.isExceptional()) return ctx;
ACell newKey = ctx.getResult();
// get new value
ctx = ctx.expand(cont,me.getValue(), cont);
if (ctx.isExceptional()) return ctx;
ACell newValue = ctx.getResult();
updated = updated.assoc(newKey, newValue);
}
return ctx.withResult(Juice.EXPAND_SEQUENCE, updated);
}
}
// Return the value directly for anything else
return context.withResult(Juice.EXPAND_CONSTANT, x);
}
};
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy