Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions runtime/src/main/java/dev/ionfusion/fusion/AbstractLetForm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright Ion Fusion contributors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package dev.ionfusion.fusion;

import static dev.ionfusion.fusion.SyntaxSymbol.ensureUniqueIdentifiers;

import dev.ionfusion.runtime.base.FusionException;
import java.util.ArrayList;

abstract class AbstractLetForm
extends SyntacticForm
{
private final boolean myBindingsAreMultiValue;
private final boolean myBindingsAreRecursive;

protected AbstractLetForm(boolean bindingsAreMultiValue,
boolean bindingsAreRecursive)
{
myBindingsAreMultiValue = bindingsAreMultiValue;
myBindingsAreRecursive = bindingsAreRecursive;
}


@Override
SyntaxValue<?> expand(Expander expander, Environment outerEnv, SyntaxSexp stx)
throws FusionException
{
final Evaluator eval = expander.getEvaluator();

SyntaxChecker check = check(eval, stx);
check.arityAtLeast(3);

SyntaxChecker checkBindings = check.subformSeq("sequence of bindings", 1);
SyntaxSequence<?> bindingPairs = checkBindings.form();

var binders = collectBinders(eval, checkBindings, bindingPairs);
ensureUniqueIdentifiers(eval, binders, stx);

var localEnv = LocalEnvironment.forExpansion(outerEnv, binders);

var subforms = stx.extract(eval);
subforms[1] = expandBindingPairs(expander, outerEnv, localEnv, bindingPairs);

// TODO #67 Should allow internal definitions
for (int i = 2; i < subforms.length; i++)
{
var bodyForm = localEnv.applyContext(subforms[i]);
subforms[i] = expander.expandExpression(localEnv, bodyForm);
}

return stx.copyReplacingChildren(eval, subforms);
}


SyntaxSymbol[] collectBinders(Evaluator eval,
SyntaxChecker checkPairs,
SyntaxSequence<?> bindingPairs)
throws FusionException
{
int pairCount = bindingPairs.size(eval);

var binders = new ArrayList<SyntaxSymbol>(pairCount);
for (int i = 0; i < pairCount; i++)
{
var checkPair = checkPairs.subformSexp("binding pair", i);
checkPair.arityExact(2);

if (myBindingsAreMultiValue)
{
var checkIds = checkPair.subformSexp("binding names", 0);
int size = checkIds.form().size(eval);
for (int j = 0; j < size; j++)
{
var binder = checkIds.requiredIdentifier("bound name", j);
binders.add(binder);
}
}
else
{
var binder = checkPair.requiredIdentifier("bound name", 0);
binders.add(binder);
}
}

return binders.toArray(SyntaxSymbol.EMPTY_ARRAY);
}


SyntaxSequence<?> expandBindingPairs(Expander expander,
Environment outerEnv,
LocalEnvironment localEnv,
SyntaxSequence<?> bindingPairs)
throws FusionException
{
var eval = expander.getEvaluator();

var pairs = bindingPairs.extract(eval);
for (int i = 0; i < pairs.length; i++)
{
var bindingPair = (SyntaxSexp) bindingPairs.get(eval, i);

var binders = bindingPair.get(eval, 0);
var expr = bindingPair.get(eval, 1);

// Binders always get the local context so they resolve properly.
binders = localEnv.applyContext(binders);

// Value expressions only get the local context for recursive forms.
if (myBindingsAreRecursive)
{
expr = localEnv.applyContext(expr);
}

expr = expander.expandExpression(outerEnv, expr);

pairs[i] = bindingPair.copyReplacingChildren(eval, binders, expr);
}

return bindingPairs.copyReplacingChildren(eval, pairs);
}
}
56 changes: 13 additions & 43 deletions runtime/src/main/java/dev/ionfusion/fusion/LambdaForm.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,61 +25,31 @@ SyntaxValue<?> expand(Expander expander, Environment env, SyntaxSexp stx)
SyntaxChecker check = check(eval, stx);
int arity = check.arityAtLeast(3);

var children = stx.extract(eval);
var subforms = stx.extract(eval);

int bodyStart;
var maybeDoc = children[2];
if (isString(eval, maybeDoc.unwrap(eval)) && arity > 3)
{
bodyStart = 3;
}
else
{
bodyStart = 2;
}
var maybeDoc = subforms[2];
int bodyStart = ((isString(eval, maybeDoc.unwrap(eval)) && arity > 3) ? 3 : 2);

var formals = children[1];
var args = flattenFormals(eval, check, formals);
var binders = collectBinders(eval, check, subforms[1]);
ensureUniqueIdentifiers(eval, binders, stx);

LocalEnvironment localEnv = null;
if (args.length != 0)
{
ensureUniqueIdentifiers(eval, args, stx);
env = localEnv = LocalEnvironment.forExpansion(env, args);
}
var localEnv = LocalEnvironment.forExpansion(env, binders);

// Prepare the bound names so they resolve to their own binding.
for (int i = 0; i < args.length; i++)
{
args[i] = (SyntaxSymbol) args[i].addWrap(localEnv.getWrap());
}

if (formals instanceof SyntaxSymbol)
{
children[1] = args[0];
}
else
{
children[1] = ((SyntaxSexp) formals).copyReplacingChildren(eval, args);
}
subforms[1] = localEnv.applyContext(subforms[1]);

// TODO #67 Should allow internal definitions
for (int i = bodyStart; i < children.length; i++)
for (int i = bodyStart; i < subforms.length; i++)
{
SyntaxValue<?> bodyForm = children[i];
if (localEnv != null)
{
bodyForm = bodyForm.addWrap(localEnv.getWrap());
}
bodyForm = expander.expandExpression(env, bodyForm);
children[i] = bodyForm;
var bodyForm = localEnv.applyContext(subforms[i]);
subforms[i] = expander.expandExpression(localEnv, bodyForm);
}

return stx.copyReplacingChildren(eval, children);
return stx.copyReplacingChildren(eval, subforms);
}


private static SyntaxSymbol[] flattenFormals(Evaluator eval,
private static SyntaxSymbol[] collectBinders(Evaluator eval,
SyntaxChecker lambdaCheck,
SyntaxValue<?> formals)
throws FusionException
Expand Down Expand Up @@ -262,7 +232,7 @@ Object doApply(Evaluator eval, Object[] args)
{
checkArityExact(eval, 0, args);

// No local store is created to wrap myEnclosure!
// No local bindings, so no local store.
return eval.bounceTailForm(myEnclosure, myBody);
}
}
Expand Down
109 changes: 3 additions & 106 deletions runtime/src/main/java/dev/ionfusion/fusion/LetValuesForm.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,120 +7,17 @@
import static dev.ionfusion.fusion.FusionSexp.unsafePairHead;
import static dev.ionfusion.fusion.FusionSexp.unsafePairTail;
import static dev.ionfusion.fusion.ResultFailure.makeResultError;
import static dev.ionfusion.fusion.SyntaxSymbol.ensureUniqueIdentifiers;

import dev.ionfusion.commons.resources.ResourcePosition;
import dev.ionfusion.runtime.base.FusionException;
import java.util.ArrayList;

final class LetValuesForm
extends SyntacticForm
extends AbstractLetForm
{
@Override
SyntaxValue expand(Expander expander, Environment env, SyntaxSexp stx)
throws FusionException
LetValuesForm()
{
final Evaluator eval = expander.getEvaluator();

SyntaxChecker check = check(eval, stx);
final int letExprSize = check.arityAtLeast(3);

SyntaxChecker checkBindings =
check.subformSeq("sequence of bindings", 1);
SyntaxSequence bindingForms = checkBindings.form();

final int numBindingForms = bindingForms.size(eval);

// Gather the bound names
ArrayList<SyntaxSymbol> boundNameList =
new ArrayList<>(numBindingForms);
for (int i = 0; i < numBindingForms; i++)
{
SyntaxChecker checkPair =
checkBindings.subformSexp("binding pair", i);
checkPair.arityExact(2);

SyntaxChecker checkBoundNames =
checkPair.subformSexp("binding name sequence", 0);

int size = checkBoundNames.form().size(eval);
for (int j = 0; j < size; j++)
{
SyntaxSymbol name =
checkBoundNames.requiredIdentifier("binding name", j);
boundNameList.add(name);
}
}


SyntaxSymbol[] boundNames;
Environment bodyEnv;
SyntaxWrap localWrap;

final int bindingCount = boundNameList.size();
if (bindingCount == 0)
{
boundNames = null;
bodyEnv = env;
localWrap = null;
}
else
{
boundNames = boundNameList.toArray(SyntaxSymbol.EMPTY_ARRAY);
ensureUniqueIdentifiers(eval, boundNames, stx);
var localEnv = LocalEnvironment.forExpansion(env, boundNames);
localWrap = localEnv.getWrap();
bodyEnv = localEnv;
}

// Expand the bound-value expressions
SyntaxValue[] expandedForms = new SyntaxValue[numBindingForms];
int bindingPos = 0;
for (int i = 0; i < numBindingForms; i++)
{
// Already type- and arity-checked this above
SyntaxSexp binding = (SyntaxSexp) bindingForms.get(eval, i);

SyntaxSexp names = (SyntaxSexp) binding.get(eval, 0);
SyntaxValue[] wrappedNames = names.extract(eval);
int size = names.size(eval);
for (int j = 0; j < size; j++)
{
// Wrap the bound names so they resolve to their own binding.
SyntaxSymbol name = boundNames[bindingPos];
assert name == wrappedNames[j];

wrappedNames[j] = name.addWrap(localWrap);
bindingPos++;
}
names = names.copyReplacingChildren(eval, wrappedNames);

SyntaxValue boundExpr = binding.get(eval, 1);
boundExpr = expander.expandExpression(env, boundExpr);
expandedForms[i] =
binding.copyReplacingChildren(eval, names, boundExpr);
}
assert bindingPos == bindingCount;

bindingForms = bindingForms.copyReplacingChildren(eval, expandedForms);

expandedForms = new SyntaxValue[letExprSize];
expandedForms[0] = stx.get(eval, 0);
expandedForms[1] = bindingForms;

// TODO Should allow internal definitions
// https://github.com/ion-fusion/fusion-java/issues/67
for (int i = 2; i < letExprSize; i++)
{
SyntaxValue subform = stx.get(eval, i);
if (localWrap != null)
{
subform = subform.addWrap(localWrap);
}
expandedForms[i] = expander.expandExpression(bodyEnv, subform);
}

return stx.copyReplacingChildren(eval, expandedForms);
super(/* multivalue? */ true, /* recursive? */ false);
}


Expand Down
Loading
Loading