package scotch.compiler.intermediate;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;
import static scotch.compiler.intermediate.Intermediates.data;
import static scotch.compiler.intermediate.Intermediates.module;
import static scotch.compiler.intermediate.Intermediates.root;
import static scotch.compiler.intermediate.Intermediates.value;
import static scotch.compiler.syntax.reference.DefinitionReference.dataRef;
import static scotch.compiler.syntax.reference.DefinitionReference.moduleRef;
import static scotch.compiler.syntax.reference.DefinitionReference.rootRef;
import static scotch.compiler.syntax.reference.DefinitionReference.valueRef;
import static scotch.compiler.text.TextUtil.quote;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import com.google.common.collect.ImmutableList;
import scotch.compiler.error.CompileException;
import scotch.compiler.syntax.Scoped;
import scotch.compiler.syntax.definition.DefinitionGraph;
import scotch.compiler.syntax.reference.DefinitionReference;
import scotch.compiler.syntax.reference.InstanceReference;
import scotch.compiler.syntax.reference.ValueReference;
import scotch.compiler.syntax.scope.Scope;
import scotch.compiler.syntax.type.Types;
import scotch.symbol.FieldSignature;
import scotch.symbol.MethodSignature;
import scotch.symbol.Symbol;
import scotch.symbol.descriptor.DataConstructorDescriptor;
import scotch.compiler.syntax.type.Type;
public class IntermediateGenerator {
private final DefinitionGraph graph;
private final List<IntermediateDefinition> definitions;
private final Deque<Scope> scopes;
private final List<String> references;
private IntermediateModule currentModule;
public IntermediateGenerator(DefinitionGraph graph) {
this.graph = graph;
this.definitions = new ArrayList<>();
this.scopes = new ArrayDeque<>();
this.references = new ArrayList<>();
}
public void addArgument(String name) {
references.remove(name);
}
public List<String> capture() {
return ImmutableList.copyOf(references);
}
public IntermediateValue constantReference(Symbol symbol, Symbol dataType, FieldSignature constantField) {
return Intermediates.constantReference(symbol, dataType, constantField);
}
public IntermediateConstructor createConstructor(Symbol symbol, String className, MethodSignature methodSignature, List<IntermediateValue> arguments) {
return Intermediates.constructor(symbol, className, methodSignature, arguments);
}
public Optional<DefinitionReference> defineData(Symbol symbol, List<Type> parameters, List<IntermediateConstructorDefinition> constructors) {
definitions.add(data(symbol, parameters, constructors));
return Optional.of(dataRef(symbol));
}
public DefinitionReference defineMembers(List<DefinitionReference> definitions) {
currentModule = currentModule.append(definitions);
return currentModule.getReference();
}
public DefinitionReference defineModule(String symbol, Runnable importScopeRunner) {
currentModule = module(symbol, emptyList());
importScopeRunner.run();
definitions.add(currentModule);
currentModule = null;
return moduleRef(symbol);
}
public DefinitionReference defineRoot(List<DefinitionReference> references) {
definitions.add(root(references));
return rootRef();
}
public DefinitionReference defineValue(Symbol symbol, Type type, IntermediateValue body) {
definitions.add(value(symbol, type, body));
return valueRef(symbol);
}
public IntermediateGraph generateIntermediateCode() {
if (graph.hasErrors()) {
throw new CompileException(graph.getErrors());
} else {
graph.getDefinition(rootRef()).get().generateIntermediateCode(this);
return new IntermediateGraph(definitions);
}
}
public Optional<DefinitionReference> generateIntermediateCode(DefinitionReference reference) {
return graph.getDefinition(reference).flatMap(definition -> definition.generateIntermediateCode(this));
}
public DataConstructorDescriptor getDataConstructor(Symbol constructor) {
return scope().getDataConstructor(constructor)
.orElseThrow(() -> new IllegalStateException("Constructor " + quote(constructor) + " not found"));
}
public String getFieldMethod(Symbol symbol, String field) {
DataConstructorDescriptor constructor = scope().getDataConstructor(symbol).get();
return constructor.getField(field).get().getMethodName();
}
public MethodSignature instanceGetter(InstanceReference reference) {
return scope().getTypeInstance(
reference.getClassReference(),
reference.getModuleReference(),
reference.getParameters().stream()
.map(parameter -> parameter.copy(() -> scope().reserveType().toDescriptor()))
.map(Types::toType)
.collect(toList())
).get().getInstanceGetter();
}
public void reference(String name) {
if (!references.contains(name)) {
references.add(name);
}
}
public <T extends Scoped> Optional<DefinitionReference> scoped(T scoped, Supplier<DefinitionReference> runnable) {
scopes.push(graph.getScope(scoped.getReference()));
try {
return Optional.of(runnable.get());
} finally {
scopes.pop();
}
}
public MethodSignature valueSignature(ValueReference reference) {
return scope().getValueSignature(reference.getSymbol()).get();
}
private Scope scope() {
return scopes.peek();
}
}