/* * 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.Iterator; import java.util.List; import org.antlr.v4.runtime.Token; class ASTRuleSpecifier extends ASTExpression { private int shapeType; private int argSize; private String entropy; private EArgSource argSource; private ASTExpression arguments; private StackRule simpleRule; private int stackIndex; private List<ASTParameter> typeSignature; private List<ASTParameter> parentSignature; public ASTRuleSpecifier(Token location) { super(false, false, EExpType.RuleType, location); this.shapeType = -1; this.argSize = 0; this.argSource = EArgSource.NoArgs; this.arguments = null; this.simpleRule = null; this.stackIndex = 0; this.typeSignature = null; this.parentSignature = null; } public ASTRuleSpecifier(int nameIndex, String name, ASTExpression arguments, List<ASTParameter> parent, Token location) { super(arguments == null || arguments.isConstant(), false, EExpType.RuleType, location); this.shapeType = nameIndex; this.entropy = name; this.argSource = EArgSource.DynamicArgs; this.arguments = arguments; this.typeSignature = null; this.parentSignature = parent; this.stackIndex = 0; this.simpleRule = null; if (parentSignature != null && parentSignature.isEmpty()) { parentSignature = null; } } public ASTRuleSpecifier(int nameIndex, String name, Token location) { super(false, false, EExpType.RuleType, location); this.shapeType = nameIndex; this.argSize = 0; this.entropy = name; this.argSource = EArgSource.StackArgs; this.arguments = null; this.simpleRule = null; this.stackIndex = 0; this.typeSignature = null; this.parentSignature = null; } public ASTRuleSpecifier(ASTRuleSpecifier spec, Token location) { super(spec.isConstant(), false, spec.getType(), location); this.argSize = spec.argSize; this.entropy = spec.entropy; this.argSource = spec.argSource; this.arguments = spec.arguments; this.simpleRule = spec.simpleRule; this.stackIndex = spec.stackIndex; this.typeSignature = spec.typeSignature; this.parentSignature = spec.parentSignature; spec.simpleRule = null; } public ASTRuleSpecifier(ASTExpression args, Token location) { super(false, false, EExpType.RuleType, location); this.shapeType = -1; this.argSize = 0; this.entropy = null; this.argSource = EArgSource.ShapeArgs; this.arguments = args; this.simpleRule = null; this.stackIndex = 0; this.typeSignature = null; this.parentSignature = null; } public String getEntropy() { return entropy; } public int getShapeType() { return shapeType; } public void setShapeType(int shapeType) { this.shapeType = shapeType; } public int getArgSize() { return argSize; } public EArgSource getArgSource() { return argSource; } public ASTExpression getArguments() { return arguments; } public StackRule getSimpleRule() { return simpleRule; } public int getStackIndex() { return stackIndex; } public List<ASTParameter> getTypeSignature() { return typeSignature; } public void setTypeSignature(List<ASTParameter> typeSignature) { this.typeSignature = typeSignature; } public void setArgSouce(EArgSource argSource) { this.argSource = argSource; } public List<ASTParameter> getParentSignature() { return parentSignature; } public void setParentSignature(List<ASTParameter> parentSignature) { this.parentSignature = parentSignature; } @Override public StackRule evalArgs(RTI rti, StackRule parent) { switch (argSource) { case NoArgs: case SimpleArgs: { return simpleRule; } case StackArgs: { StackType stackItem = rti.stackItem(stackIndex); stackItem.getRule().retain(rti); return stackItem.getRule(); } case ParentArgs: { if (shapeType != parent.getRuleName()) { // Child shape is different from parent, even though parameters are reused, // and we can't finesse it in ASTreplacement::traverse(). Just // copy the parameters with the correct shape type. StackRule ret = new StackRule(parent); ret.setRuleName(shapeType); return ret; } } case SimpleParentArgs: { parent.retain(rti); return parent; } case DynamicArgs: { StackRule ret = new StackRule(shapeType, argSize, typeSignature); ret.evalArgs(rti, arguments, parent); return ret; } case ShapeArgs: { return arguments.evalArgs(rti, parent); } default: { assert(false); return null; } } } @Override public int evaluate(double[] result, int length, RTI rti) { throw new RuntimeException("Improper evaluation of a rule specifier"); } @Override public void entropy(StringBuilder e) { e.append(entropy); } @Override public ASTExpression simplify() { if (arguments != null) { if (arguments instanceof ASTCons) { ASTCons c = (ASTCons)arguments; for (ASTExpression carg : c.getChildren()) { carg.simplify(); } } else { arguments.simplify(); } } if (argSource == EArgSource.StackArgs) { boolean isGlobal = false; ASTParameter bound = Builder.currentBuilder().findExpression(shapeType, isGlobal); if (bound.getType() != EExpType.RuleType) { return this; } if (bound.getStackIndex() == -1) { if (bound.getDefinition().getExp() instanceof ASTRuleSpecifier) { ASTRuleSpecifier r = (ASTRuleSpecifier)bound.getDefinition().getExp(); // The source ASTruleSpec must already be type-checked // because it is lexically earlier shapeType = r.getShapeType(); argSize = r.getArgSize(); argSource = r.getArgSource(); arguments = null; simpleRule = r.getSimpleRule(); typeSignature = r.getTypeSignature(); parentSignature = r.getParentSignature(); isConstant = true; locality = ELocality.PureLocal; } else { error("Error processing shape variable."); } } } return this; } @Override public ASTExpression compile(ECompilePhase ph) { if (arguments != null) { arguments.compile(ph); } switch (ph) { case TypeCheck: { switch (argSource) { case ShapeArgs: { if (arguments.getType() == EExpType.RuleType) { error("Expression does not return a shape"); } isConstant = true; locality = arguments.getLocality(); StringBuilder ent = new StringBuilder(); arguments.entropy(ent); entropy = ent.toString(); return null; } case SimpleParentArgs: { isConstant = true; locality = ELocality.PureLocal; return null; } case StackArgs: { boolean isGlobal = false; ASTParameter bound = Builder.currentBuilder().findExpression(shapeType, isGlobal); if (bound.getType() != EExpType.RuleType) { error("Shape name does not bind to a rule variable"); error(bound.getLocation() + " this is what it binds to"); } if (bound.getStackIndex() == -1) { if (bound.getDefinition() == null || bound.getDefinition().getExp() == null) { error("Error processing shape variable."); return null; } if (bound.getDefinition().getExp() instanceof ASTRuleSpecifier) { ASTRuleSpecifier r = (ASTRuleSpecifier)bound.getDefinition().getExp(); grab(r); locality = ELocality.PureLocal; } else { error("Error processing shape variable."); } } else { stackIndex = bound.getStackIndex() - (isGlobal ? 0 : Builder.currentBuilder().getLocalStackDepth()); isConstant = false; locality = bound.getLocality(); } if (arguments != null && arguments.getType() != EExpType.NoType) { error("Cannot bind parameters twice"); } return null; } case NoArgs: { isConstant = true; locality = ELocality.PureLocal; return null; } case ParentArgs: case SimpleArgs: assert(false); break; case DynamicArgs: { ASTDefine[] func = new ASTDefine[1]; List<ASTParameter>[] signature = new List[1]; String name = Builder.currentBuilder().getTypeInfo(shapeType, func, signature); typeSignature = signature[0]; if (typeSignature != null && typeSignature.isEmpty()) { typeSignature = null; } if (func[0] != null) { if (func[0].getExpType() == EExpType.RuleType) { argSource = EArgSource.ShapeArgs; arguments = new ASTUserFunction(shapeType, arguments, func[0], location); arguments.compile(ph); isConstant = false; locality = arguments.getLocality(); } else { error("Function does not return a shape"); } if (arguments != null) { StringBuilder ent = new StringBuilder(); arguments.entropy(ent); entropy = ent.toString(); } return null; } boolean isGlobal = false; ASTParameter bound = Builder.currentBuilder().findExpression(shapeType, isGlobal); if (bound != null && bound.getType() == EExpType.RuleType) { argSource = EArgSource.StackArgs; compile(ph); return null; } if (arguments != null && arguments.getType() == EExpType.ReuseType) { argSource = EArgSource.ParentArgs; if (typeSignature != parentSignature) { Iterator<ASTParameter> paramIt = typeSignature.iterator(); Iterator<ASTParameter> parentIt = parentSignature.iterator(); ASTParameter param = null; ASTParameter parent = null; while (paramIt.hasNext() && parentIt.hasNext()) { param = paramIt.next(); parent = parentIt.next(); if (param != parent) { error("Parameter reuse only allowed when type signature is identical."); error(param.getLocation() + " target shape parameter type"); error(parent.getLocation() + " does not equal source shape parameter type"); break; } } if (!paramIt.hasNext() && parentIt.hasNext()) { error("Source shape has more parameters than target shape."); error(parent.getLocation() + " extra source parameters start here"); } if (paramIt.hasNext() && !parentIt.hasNext()) { error("Target shape has more parameters than source shape."); error(param.getLocation() + " extra target parameters start here"); } } isConstant = true; locality = ELocality.PureLocal; return null; } argSize = ASTParameter.checkType(typeSignature, arguments, true); if (argSize < 0) { argSource = EArgSource.NoArgs; return null; } if (arguments != null && arguments.getType() != EExpType.NoType) { if (arguments.isConstant()) { simpleRule = evalArgs(null, null); argSource = EArgSource.SimpleArgs; Builder.currentBuilder().storeParams(simpleRule); isConstant = true; locality = ELocality.PureLocal; } else { isConstant = false; locality = arguments.getLocality(); } StringBuilder ent = new StringBuilder(); arguments.entropy(ent); entropy = ent.toString(); } else { argSource = EArgSource.NoArgs; simpleRule = new StackRule(shapeType, 0, typeSignature); isConstant = true; locality = ELocality.PureLocal; Builder.currentBuilder().storeParams(simpleRule); } } break; default: break; } } break; case Simplify: break; default: break; } return null; } public void grab(ASTRuleSpecifier src) { isConstant = true; shapeType = src.getShapeType(); argSize = 0; argSource = src.getArgSource(); arguments = null; simpleRule = src.getSimpleRule(); stackIndex = 0; typeSignature = src.getTypeSignature(); parentSignature = src.getParentSignature(); } }