/*
* JAME 6.2.1
* http://jame.sourceforge.net
*
* Copyright 2001, 2016 Andrea Medeghini
*
* This file is part of JAME.
*
* JAME is an application for creating fractals and other graphics artifacts.
*
* JAME is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JAME is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JAME. If not, see <http://www.gnu.org/licenses/>.
*
*/
package net.sf.jame.contextfree.parser;
import java.util.List;
import org.antlr.v4.runtime.Token;
class ASTUserFunction extends ASTExpression {
private ASTExpression arguments;
private ASTDefine definition;
private int nameIndex;
protected boolean isLet;
public ASTUserFunction(int nameIndex, ASTExpression arguments, ASTDefine definition, Token location) {
super(false, false, EExpType.NoType, location);
this.nameIndex = nameIndex;
this.definition = definition;
this.arguments = arguments;
isLet = false;
}
public ASTExpression getArguments() {
return arguments;
}
protected void setArguments(ASTExpression arguments) {
this.arguments = arguments;
}
public ASTDefine getDefinition() {
return definition;
}
public int getNameIndex() {
return nameIndex;
}
public boolean isLet() {
return isLet;
}
@Override
public int evaluate(double[] result, int length, RTI rti) {
if (type != EExpType.NumericType) {
error("Function does not evaluate to a number");
return -1;
}
if (result != null && length < definition.getTupleSize()) {
return -1;
}
if (result == null) {
return definition.getTupleSize();
}
if (rti == null) throw new DeferUntilRuntimeException();
if (rti.getRequestStop()/*TODO || Render.abortEverything*/) {
throw new CfdgException("Stopping");
}
StackType oldStackType = setupStack(rti);
definition.getExp().evaluate(result, length, rti);
cleanupStack(rti, oldStackType);
return definition.getTupleSize();
}
@Override
public void evaluate(Modification[] result, boolean shapeDest, RTI rti) {
if (type != EExpType.ModType) {
error("Function does not evaluate to an adjustment");
return;
}
if (rti == null) throw new DeferUntilRuntimeException();
if (rti.getRequestStop()/*TODO || Render.abortEverything*/) {
throw new CfdgException("Stopping");
}
StackType oldStackType = setupStack(rti);
definition.getExp().evaluate(result, shapeDest, rti);
cleanupStack(rti, oldStackType);
}
@Override
public void entropy(StringBuilder e) {
if (arguments != null) {
arguments.entropy(e);
}
e.append(definition.getName());
}
@Override
public ASTExpression simplify() {
if (arguments != null) {
if (arguments instanceof ASTCons) {
ASTCons c = (ASTCons)arguments;
for (ASTExpression e : c.getChildren()) {
e.simplify();
}
} else {
arguments.simplify();
}
}
return this;
}
@Override
public ASTExpression compile(ECompilePhase ph) {
switch (ph) {
case TypeCheck:
{
// Function calls and shape specifications are ambiguous at parse
// time so the parser always chooses a function call. During
// type check we may need to convert to a shape spec.
ASTDefine[] def = new ASTDefine[1];
@SuppressWarnings("unchecked")
List<ASTParameter>[] p = new List[1];
String name = Builder.currentBuilder().getTypeInfo(nameIndex, def, p);
if (def[0] != null && p[0] != null) {
error("Name matches both a function and a shape");
return null;
}
if (def[0] == null && p[0] == null) {
error("Name does not match shape name or function name");
return null;
}
if (def[0] != null) {
if (arguments != null) {
arguments.compile(ph);
}
definition = def[0];
ASTParameter.checkType(def[0].getParameters(), arguments, false);
isConstant = false;
isNatural = definition.isNatural();
type = definition.getExpType();
locality = arguments != null ? arguments.getLocality() : ELocality.PureLocal;
if (definition.getExp() != null && definition.getExp().getLocality() == ELocality.ImpureNonlocal && locality == ELocality.PureNonlocal) {
locality = ELocality.ImpureNonlocal;
}
return null;
}
ASTRuleSpecifier r = new ASTRuleSpecifier(nameIndex, name, arguments, null, location);
r.compile(ph);
return r;
}
case Simplify:
break;
default:
break;
}
return null;
}
@Override
public StackRule evalArgs(RTI rti, StackRule parent) {
if (type != EExpType.RuleType) {
error("Function does not evaluate to a shape");
return null;
}
if (rti == null) throw new DeferUntilRuntimeException();
if (rti.getRequestStop()/*TODO || Render.abortEverything*/) {
throw new CfdgException("Stopping");
}
StackType oldStackType = setupStack(rti);
StackRule ret = definition.getExp().evalArgs(rti, parent);
cleanupStack(rti, oldStackType);
return ret;
}
private StackType setupStack(RTI rti) {
StackType stackType = rti.getLogicalStackTop();
if (definition.getStackCount() > 0) {
int size = rti.getCFStack().size();
rti.getCFStack().get(size).evalArgs(rti, arguments, definition.getParameters(), isLet);
rti.setLogicalStackTop(new StackType(size));
}
return stackType;
}
private void cleanupStack(RTI rti, StackType stackType) {
if (definition.getStackCount() > 0) {
rti.setLogicalStackTop(stackType);
}
}
}