import org.simantics.scl.compiler.constants.SCLConstructor;
import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
+import org.simantics.scl.compiler.elaboration.contexts.TranslationContext.ExistentialFrame;
import org.simantics.scl.compiler.elaboration.expressions.records.FieldAssignment;
import org.simantics.scl.compiler.elaboration.modules.SCLValue;
import org.simantics.scl.compiler.environment.AmbiguousNameException;
import org.simantics.scl.compiler.errors.Locations;
-import org.simantics.scl.compiler.internal.parsing.Token;
import gnu.trove.map.hash.THashMap;
public class ERecord extends ASTExpression {
- Token constructor;
- FieldAssignment[] fields;
+ public final EVar constructor;
+ public final FieldAssignment[] fields;
- public ERecord(Token constructor, FieldAssignment[] fields) {
+ public ERecord(EVar constructor, FieldAssignment[] fields) {
this.constructor = constructor;
this.fields = fields;
}
public Expression resolve(TranslationContext context, boolean asPattern) {
SCLValue constructorValue;
try {
- constructorValue = context.getEnvironment().getLocalNamespace().getValue(constructor.text);
+ constructorValue = context.getEnvironment().getLocalNamespace().getValue(constructor.name);
} catch (AmbiguousNameException e) {
context.getErrorLog().log(constructor.location, e.getMessage());
return new EError(constructor.location);
}
if(constructorValue == null) {
- context.getErrorLog().log(constructor.location, "Couldn't resolve the record constructor " + constructor.text + ".");
+ context.getErrorLog().log(constructor.location, "Couldn't resolve the record constructor " + constructor.name + ".");
return new EError(constructor.location);
}
if(!(constructorValue.getValue() instanceof SCLConstructor)) {
- context.getErrorLog().log(constructor.location, "Value " + constructor.text + " is not a record constructor.");
+ context.getErrorLog().log(constructor.location, "Value " + constructor.name + " is not a record constructor.");
return new EError(constructor.location);
}
String[] fieldNames = ((SCLConstructor)constructorValue.getValue()).recordFieldNames;
if(fieldNames == null) {
- context.getErrorLog().log(constructor.location, "Value " + constructor.text + " is not a record constructor.");
+ context.getErrorLog().log(constructor.location, "Value " + constructor.name + " is not a record constructor.");
return new EError(constructor.location);
}
+ Expression[] parameters = translateFieldsToFunctionParameters(context, fields, fieldNames);
+ if(parameters == null)
+ return new EError(location);
+ if(asPattern)
+ for(int i=0;i<parameters.length;++i) {
+ Expression parameter = parameters[i];
+ if(parameter == null)
+ parameters[i] = Expressions.blank(null);
+ else
+ parameters[i] = parameter.resolveAsPattern(context);
+ }
+ else {
+ boolean error = false;
+ for(int i=0;i<parameters.length;++i) {
+ Expression parameter = parameters[i];
+ if(parameter == null) {
+ ExistentialFrame frame = context.getCurrentExistentialFrame();
+ if(frame == null || frame.disallowNewExistentials) {
+ context.getErrorLog().log(location, "Field " + fieldNames[i] + " not defined.");
+ error = true;
+ }
+ else
+ parameters[i] = frame.createBlank(location);
+ }
+ else
+ parameters[i] = parameter.resolve(context);
+ }
+ if(error)
+ return new EError(location);
+ }
+ EApply result = new EApply(new EConstant(constructorValue), parameters);
+ result.setLocationDeep(location);
+ return result;
+ }
+
+ public static Expression[] translateFieldsToFunctionParameters(TranslationContext context, FieldAssignment[] fields, String[] fieldNames) {
THashMap<String,FieldAssignment> recordMap = new THashMap<String,FieldAssignment>(fields.length);
+ boolean error = false;
for(FieldAssignment field : fields) {
if(field.value == null) {
+ String actualName = field.name;
+ if(actualName.charAt(0) == '?')
+ actualName = actualName.substring(1);
String bestMatch = null;
int bestMatchLength = 0;
for(int i=0;i<fieldNames.length;++i) {
String fieldName = fieldNames[i];
- if(field.name.startsWith(fieldName) && fieldName.length() > bestMatchLength) {
+ if(actualName.startsWith(fieldName) && fieldName.length() > bestMatchLength) {
bestMatch = fieldName;
bestMatchLength = fieldName.length();
}
}
if(bestMatch == null) {
context.getErrorLog().log(field.location, "Invalid shorthand field " + field.name + " is defined twice.");
- return new EError(location);
+ error = true;
}
field.value = new EVar(field.location, field.name);
field.name = bestMatch;
}
if(recordMap.put(field.name, field) != null) {
context.getErrorLog().log(field.location, "Field " + field.name + " is defined more than once.");
- return new EError(location);
+ error = true;
}
}
+ if(error)
+ return null;
Expression[] parameters = new Expression[fieldNames.length];
- boolean error = false;
for(int i=0;i<fieldNames.length;++i) {
FieldAssignment assignment = recordMap.remove(fieldNames[i]);
- if(assignment == null) {
- if(asPattern) {
- parameters[i] = Expressions.blank(null);
- }
- else {
- context.getErrorLog().log(location, "Field " + fieldNames[i] + " not defined.");
- error = true;
- }
- }
- else
- parameters[i] = asPattern
- ? assignment.value.resolveAsPattern(context)
- : assignment.value.resolve(context);
+ if(assignment != null)
+ parameters[i] = assignment.value;
}
if(!recordMap.isEmpty()) {
for(FieldAssignment field : recordMap.values())
context.getErrorLog().log(field.location, "Field " + field.name + " is not defined in the constructor.");
- error = true;
- }
- if(error)
- return new EError(location);
- else {
- EApply result = new EApply(new EConstant(constructorValue), parameters);
- result.setLocationDeep(location);
- return result;
+ return null;
}
+ return parameters;
}
@Override