Variable.java
package Hoot.Runtime.Values;
import java.util.*;
import java.math.BigDecimal;
import static org.apache.commons.lang3.StringUtils.*;
import Hoot.Runtime.Names.Global;
import Hoot.Runtime.Names.TypeName;
import Hoot.Runtime.Behaviors.Typified;
import Hoot.Runtime.Behaviors.Scope;
import Hoot.Runtime.Notes.*;
import Hoot.Runtime.Emissions.Item;
import Hoot.Runtime.Emissions.Emission;
import Hoot.Runtime.Emissions.NamedItem;
import static Hoot.Runtime.Names.Primitive.*;
import static Hoot.Runtime.Emissions.Emission.*;
import static Hoot.Runtime.Names.TypeName.RootType;
import static Hoot.Runtime.Names.TypeName.EmptyType;
import static Hoot.Runtime.Names.Keyword.*;
import static Hoot.Runtime.Functions.Utils.*;
import static Hoot.Runtime.Values.Exit.*;
/**
* A variable, including its name, type, and (optional) initial value.
*
* @author nik <nikboyd@sonic.net>
* @see "Copyright 1999,2021 Nikolas S Boyd."
* @see "Permission is granted to copy this work provided this copyright statement is retained in all copies."
*/
public class Variable extends Operand implements ValueSource {
public Variable(NamedItem scope) { super(scope); }
protected Variable(NamedItem scope, String name, DetailedType type) { this(scope); name(name); notedType(type); }
// @Override public void clean() { resolveType(); }
protected boolean definesValue = false;
public boolean definesValue() { return this.definesValue; }
protected void makeDefined() { this.definesValue = true; }
public Variable makeAssignment() { return defineLocal(); }
public Variable defineMember() { defineMember(facialScope()); return this; }
private void defineMember(Scope s) { if (!s.hasLocal(this.name())) scopeLocal(s); }
public Variable defineLocal() { defineLocal(containerScope()); return this; }
private void defineLocal(Scope s) { if (!isDefined()) scopeLocal(s); }
private void scopeLocal(Scope s) {
s.addLocal(this);
makeDefined();
reportLocal(s);
}
static final String[] KnownNames = { "f0", "exitID", };
static final String LocalReport = "defined %s in %s";
private void reportLocal(Scope s) {
if (!wrap(KnownNames).contains(name())) {
whisper(format(LocalReport, name(), s.description()));
}
}
public static Variable from(Item item) { return nullOr(v -> (Variable)v, item.variableContainer()); }
public static Variable from(Scope scope, String name, DetailedType type) { return new Variable(scope, name, type); }
public static Variable exitFrom(Scope context, Operand value) {
return named(FrameId, FrameIdType, context).withValue(value).makeForExiting(); }
public static Variable frame(Scope context, Operand value) {
return frameFrom(context).withValue(value).makeForExiting(); }
private static Variable frameFrom(Scope context) {
return named(Frame.name(0), Frame.className(), context); }
public static Variable named(String name, DetailedType type, Operand value) {
return from(Scope.current(), name, type).withValue(value).resolveType(); }
public static Variable named(String name, DetailedType type) {
// for arguments only, infer type from name if needed. --nik
return from(Scope.current(), name, hasAny(type) ? type : DetailedType.from(TypeName.inferFrom(name))); }
private static Variable named(String name, String type, Scope container) {
return from(container, name, DetailedType.with(Global.named(type))); }
public Variable withErasure() {
Variable result = new Variable((NamedItem)container());
result.notedType(DetailedType.RootType);
result.names.addAll(this.names);
return result;
}
protected final List<String> names = emptyList(String.class);
private void name(String aName) { names.add(aName); }
@Override public boolean isEmpty() { return names.isEmpty(); }
@Override public String name() { return isEmpty() ? Empty : TypeName.with(names).fullName(); }
protected static final String NameReport = "%s = %s: %s";
@Override public String description() {
return format(NameReport, getClass().getSimpleName(), name(), resolvedTypeName()); }
DetailedType notedType;
public DetailedType notedType() { return this.notedType; }
public boolean hasTypeNote() { return notedType() != null; }
private void notedType(DetailedType notedType) { if (hasAny(notedType)) this.notedType = notedType; }
public String type() { return typeResolver().typeName(); }
public Typified typeFace() { return typeResolver().findType(); }
public boolean valueNeedsCast() { return hasValue() && !type().equals(operandValue().resolvedTypeName()); }
@Override public TypeName typeResolver() {
if (this.hasTypeNote()) return this.notedType().toTypeName();
if (referencesMember()) return referencedMember().typeResolver();
if (referencesLocal()) {
Variable ref = referencedLocal(); // same ref without note?
return (ref == this) ? EmptyType : ref.typeResolver();
}
return EmptyType;
}
// @SuppressWarnings("unchecked")
// @Override public Object value() { return operandValue(); }
protected Operand value = null;
public Operand operandValue() { return this.value; }
public void value(Operand aValue) { value = aValue.inside(this); }
public boolean noValue() { return hasNone(operandValue()); }
public boolean hasValue() { return hasOne(operandValue()); }
@Override public boolean containsExit() { return hasValue() && operandValue().exitsMethod(); }
public Variable withValue(Operand value) { if (hasAny(value)) { value(value); value.makeAssigned(); } return this; }
public String defaultValue() {
String result = Null;
if (isPrimitiveType(type())) {
result = 0 + Empty; // as text
if (Boolean.toLowerCase().equals(type())) {
result = False.toLowerCase();
}
}
return result;
}
protected boolean forExiting = false;
public boolean isForExiting() { return forExiting; }
public Variable makeForExiting() { this.forExiting = true; return this; }
@Override public boolean isVariable() { return true; }
@Override public boolean isTransient() { return notes().isTransient(); }
@Override public boolean isPrimitive() { return notes().isPrimitive(); }
public Variable withNotes(List<Note> notes) { noteAll(notes); return this; }
@Override public void makeTransient() { notes().makeTransient(); }
public boolean needsAccess() { return !notes().hasAccess(); }
public boolean isMember() { return containerScope().isFacial(); }
public boolean isDefined() { return referencesMember() || referencesLocal(); }
public String scopeDescription() { return isMember() ? "member" : "local"; }
public boolean needsTypeResolved() { return !isMember() && !isDefined() && !hasTypeNote(); }
protected Variable resolveType() {
if (hasValue()) {
operandValue().clean();
if (needsTypeResolved()) {
TypeName type = operandValue().typeResolver();
if (!type.isUnknown() && !type.isRootType()) {
notedType(DetailedType.from(type));
}
}
}
return this; }
protected String comment = Empty;
public void comment(String aString) { comment = defaultIfEmpty(aString, Empty); }
public boolean hasComment() { return comment.length() > 0; }
@Override public String comment() { // remove the surrounding quotes if needed
return (comment.isEmpty() ? null : comment.substring(1, comment.length() - 1)); }
public boolean referencesStacked() { return falseOr(v -> v.isStacked(), referencedLocal()); }
public boolean referencesLocal() { return matchAny(blockScopes(), s -> s.hasLocal(name())); }
public boolean referencesMember() { return matchAny(faceHeritage(), f -> f.resolves(this)); }
public Variable referencedLocal() {
return nullOr(s -> s.localNamed(name()), findFirst(blockScopes(), bs -> bs.hasLocal(name()))); }
public Variable referencedMember() {
return nullOr(s -> s.localNamed(name()), findFirst(faceHeritage(), fs -> fs.resolves(this))); }
public List<Typified> faceHeritage() {
List<Typified> results = emptyList(Typified.class);
results.add((Typified)facialScope());
results.addAll(facialScope().simpleHeritage());
return results; }
public Emission variableNotes() { return emitSequence(notes().variableNotesOnlyDecor()); }
public Emission argumentNotes() { return emitSequence(notes().argumentNotesOnlyDecor()); }
public Emission emitNotedType() {
if (hasTypeNote()) return notedType().emitCode(true);
if (definesValue()) return emitItem(localVar());
if (isDefined()) return null;
return emitItem(localVar());
}
public Emission emitType(boolean wantsBases) { // used by members
if (hasTypeNote()) return notedType().emitCode(wantsBases);
if (definesValue()) emitItem(localVar());
if (isDefined()) return null;
return emitItem(localVar());
}
public Emission emitCast() { return emitCast(emitType(false), emitItem(name())); }
public Emission emitType() { return emitType(true) ; }
public Emission emitArgumentType() {
return hasTypeNote() ? notedType().makeArgued(true).emitCode(false) : emitItem(RootType().fullName()) ; }
@Override public Emission emitOperand() {
if (this.hasValue()) {
if (this.isStacked()) return this.hasTypeNote() ? emitSequence(emitSimply(), emitBinding()) : emitBoundValue();
if (this.referencesStacked()) return emitBoundValue();
if (operandValue().hasCascades()) return emitValue();
}
return emitSimply();
}
@Override public Emission emitItem() {
if (notes().isProperty()) return emitProperty();
return this.isTransient() ? emitTransientLocal() : emitVariable(); }
public Emission emitBinding() { return emitStackedBind(name(), name()); }
public Emission emitBoundValue() { return emitStackedBind(name(), emitValue()); }
public Emission emitErasedArgument() { return emitArgument(fullName(), RootType().shortName(), true); }
public Emission emitBlockArgument(int index, int level) { return emitBlockArgument(name(), emitArgumentType(), index, level); }
public Emission emitArgument() { return emitNamedArgument(name(), emitArgumentType(), argumentNotes()); }
public Emission emitArgument(boolean useFinal) { return emitArgument(fullName(), typeResolver().typeName(), useFinal); }
public Emission emitProperty() {
return emitProperty(name(), emitNotedType(), containerScope().name(), emitValue(), variableNotes(), comment()) ; }
static final String NoComment = null;
public Emission emitLocal() { return emitVariable(name(), emitNotedType(), emitValue(), NoValue, NoComment); }
public Emission emitSimply() { return emitVariable(name(), emitNotedType(), emitValue(), NoValue, NoComment); }
public Emission emitVariable() { return emitVariable(name(), emitNotedType(), emitValue(), variableNotes(), comment()); }
public Emission emitTransientLocal() { return emitTransient(name(), emitNotedType(), emitValueOrDefault(), valueNeedsCast()); }
public Emission emitValueOrDefault() { return hasValue() ? emitValue() : emitItem(defaultValue()); }
public Emission emitValue() { return nullOr(v -> isPrimitive() ? v.emitPrimitive() : v.emitOperand(), operandValue()); }
// @Override public boolean needsLocalResolution() { return false; }
// @Override public Scope resolvingScope() { return containerScope(); }
// public void name(Tree node) {
// comment(commentFrom(node));
// name(tokenFrom(node).getText());
// }
// @Override public Class resolvedType() {
// String typeName = type();
// if (typeName.endsWith("[]")) {
// return typeNamed(typeName);
// }
// Class type = Primitive.getPrimitiveType(type());
// if (type != null) {
// return type;
// }
// if (typeName.startsWith(RootJava)) {
// return typeNamed(typeName);
// }
// Typified typeFace = Library.findFace(typeName);
// return (typeFace == null ? RootClass() : typeFace.primitiveClass());
// }
static final String JDK_8 = "1.8";
static final String JDK_11 = "11";
static final BigDecimal MinimumVersion = new BigDecimal(JDK_8);
static final BigDecimal LocalVarSupport = new BigDecimal(JDK_11);
static final String JavaSpecVersion = systemValue("java.specification.version");
static final BigDecimal JavaVersion = new BigDecimal(JavaSpecVersion);
public static String javaVersion() { return JavaSpecVersion; }
public static String javaCompatibility() { return (JavaVersion.compareTo(LocalVarSupport) < 0)? JDK_8 : JDK_11; }
private boolean supportsLocalVars() { return JDK_11.equals(javaCompatibility()); }
private String localVar() { return supportsLocalVars() ? LocalVariable : null; }
static final String LocalVariable = "var";
} // Variable