Value fun = this.op.typecheck(s);
if (fun instanceof FunType) {
FunType funtype = (FunType) fun;
// TypeChecker.self.uncalled.remove(funtype);
Scope funScope = new Scope(funtype.env);
List<Name> params = funtype.fun.params;
// set default values for parameters
if (funtype.properties != null) {
Declare.mergeType(funtype.properties, funScope);
}
if (!args.positional.isEmpty() && args.keywords.isEmpty()) {
// positional
if (args.positional.size() != params.size()) {
Util.abort(this.op,
"calling function with wrong number of arguments. expected: " + params.size()
+ " actual: " + args.positional.size());
}
for (int i = 0; i < args.positional.size(); i++) {
Value value = args.positional.get(i).typecheck(s);
Value expected = funScope.lookup(params.get(i).id);
if (!Type.subtype(value, expected, false)) {
Util.abort(args.positional.get(i), "type error. expected: " + expected + ", actual: " + value);
}
funScope.putValue(params.get(i).id, value);
}
} else {
// keywords
Set<String> seen = new HashSet<>();
// try to bind all arguments
for (Name param : params) {
Node actual = args.keywords.get(param.id);
if (actual != null) {
seen.add(param.id);
Value value = actual.typecheck(funScope);
Value expected = funScope.lookup(param.id);
if (!Type.subtype(value, expected, false)) {
Util.abort(actual, "type error. expected: " + expected + ", actual: " + value);
}
funScope.putValue(param.id, value);
} else {
Util.abort(this, "argument not supplied for: " + param);
return Value.VOID;
}
}
// detect extra arguments
List<String> extra = new ArrayList<>();
for (String id : args.keywords.keySet()) {
if (!seen.contains(id)) {
extra.add(id);
}
}
if (!extra.isEmpty()) {
Util.abort(this, "extra keyword arguments: " + extra);
return Value.VOID;
}
}
Object retType = funtype.properties.lookupPropertyLocal(Constants.RETURN_ARROW, "type");
if (retType != null) {
if (retType instanceof Node) {
// evaluate the return type because it might be (typeof x)
return ((Node) retType).typecheck(funScope);
} else {
Util.abort("illegal return type: " + retType);
return null;
}
} else {
if (TypeChecker.self.callStack.contains(fun)) {
Util.abort(op, "You must specify return type for recursive functions: " + op);
return null;
}
TypeChecker.self.callStack.add((FunType) fun);
Value actual = funtype.fun.body.typecheck(funScope);
TypeChecker.self.callStack.remove(fun);
return actual;
}
} else if (fun instanceof RecordType) {
RecordType template = (RecordType) fun;
Scope values = new Scope();
// set default values for fields
Declare.mergeDefault(template.properties, values);
// set actual values, overwrite defaults if any
for (Map.Entry<String, Node> e : args.keywords.entrySet()) {
if (!template.properties.keySet().contains(e.getKey())) {
Util.abort(this, "extra keyword argument: " + e.getKey());
}
Value actual = args.keywords.get(e.getKey()).typecheck(s);
Value expected = template.properties.lookupLocalType(e.getKey());
if (!Type.subtype(actual, expected, false)) {
Util.abort(this, "type error. expected: " + expected + ", actual: " + actual);
}
values.putValue(e.getKey(), e.getValue().typecheck(s));
}
// check uninitialized fields
for (String field : template.properties.keySet()) {
if (values.lookupLocal(field) == null) {
Util.abort(this, "field is not initialized: " + field);
}
}
// instantiate