package org.simantics.scl.compiler.source; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.Arrays; import org.simantics.scl.compiler.compilation.SCLCompiler; import org.simantics.scl.compiler.environment.EnvironmentFactoryImpl; import org.simantics.scl.compiler.errors.CompilationErrorFormatter; import org.simantics.scl.compiler.errors.Failable; import org.simantics.scl.compiler.errors.Failure; import org.simantics.scl.compiler.errors.Success; import org.simantics.scl.compiler.internal.codegen.types.JavaReferenceValidator; import org.simantics.scl.compiler.internal.codegen.types.JavaReferenceValidatorFactory; import org.simantics.scl.compiler.internal.codegen.types.RuntimeJavaReferenceValidator; import org.simantics.scl.compiler.module.ImportDeclaration; import org.simantics.scl.compiler.module.Module; import org.simantics.scl.compiler.module.options.ModuleCompilationOptions; import org.simantics.scl.compiler.module.repository.ModuleRepository; import org.simantics.scl.compiler.module.repository.UpdateListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class TextualModuleSource implements ModuleSource { private static final Logger LOGGER = LoggerFactory.getLogger(TextualModuleSource.class); public static final ImportDeclaration[] DEFAULT_IMPORTS = new ImportDeclaration[] { new ImportDeclaration("Builtin", ""), new ImportDeclaration("Prelude", "") }; private final String moduleName; private final double priority; public TextualModuleSource(String moduleName, double priority) { this.moduleName = moduleName; this.priority = priority; } public TextualModuleSource(String moduleName) { this(moduleName, 0.0); } @Override public ClassLoader getClassLoader() { return getClass().getClassLoader(); } protected Reader getSourceReader(UpdateListener listener) throws IOException { return new StringReader(getSourceText(listener)); } protected JavaReferenceValidator getJavaReferenceValidator() { return new RuntimeJavaReferenceValidator(getClassLoader()); } public String getSourceText(UpdateListener listener) throws IOException { Reader reader = getSourceReader(listener); char[] buffer = new char[4096]; int pos = 0; try { while(true) { int count = reader.read(buffer, pos, buffer.length-pos); if(count == -1) return new String(buffer, 0, pos); pos += count; if(pos == buffer.length) buffer = Arrays.copyOf(buffer, 2*buffer.length); } } finally { reader.close(); } } public ImportDeclaration[] getBuiltinImports(UpdateListener listener) { return DEFAULT_IMPORTS; } @Override public String getModuleName() { return moduleName; } @SuppressWarnings("unchecked") @Override public Failable compileModule(final ModuleRepository environment, final UpdateListener listener, ModuleCompilationOptions options) { SCLCompiler compiler = new SCLCompiler(options, getJavaReferenceValidatorFactory()); try { String source = getSourceText(listener); compiler.addSource(source); compiler.compile( new EnvironmentFactoryImpl( environment, getBuiltinImports(listener), listener), moduleName); if(compiler.getErrorLog().hasNoErrors()) return new Success(compiler.getModule()); else { if(options == null || !options.silent) LOGGER.error("While compiling " + getModuleName() + ":\n " + CompilationErrorFormatter.toString(getSourceReader(null), compiler.getErrorLog().getErrors()).replaceAll("\n", "\n ")); return new Failure(compiler.getErrorLog().getErrors()); } } catch (IOException e) { if(options == null || !options.silent) LOGGER.error("Compilation of module " + moduleName + " failed.", e); return new Failure(e); } } public JavaReferenceValidatorFactory getJavaReferenceValidatorFactory() { return new JavaReferenceValidatorFactory() { @Override public JavaReferenceValidator getJavaReferenceValidator(String context) { return (JavaReferenceValidator)TextualModuleSource.this.getJavaReferenceValidator(); } @Override public JavaReferenceValidator getDefaultJavaReferenceValidator() { return (JavaReferenceValidator)TextualModuleSource.this.getJavaReferenceValidator(); } }; } @Override public double getPriority() { return priority; } @Override public String toString() { return getClass().getSimpleName() + "(" + moduleName + ")"; } public boolean isUpdateable() { return false; } public void update(String newSourceText) { throw new UnsupportedOperationException(); } }