All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.unibo.tuprolog.ui.gui.TuPrologIDEModelImpl.kt Maven / Gradle / Ivy

There is a newer version: 1.0.4
Show newest version
package it.unibo.tuprolog.ui.gui

import it.unibo.tuprolog.core.Struct
import it.unibo.tuprolog.core.exception.TuPrologException
import it.unibo.tuprolog.core.operators.OperatorSet
import it.unibo.tuprolog.core.parsing.ParseException
import it.unibo.tuprolog.core.parsing.parseAsStruct
import it.unibo.tuprolog.solve.MutableSolver
import it.unibo.tuprolog.solve.Solution
import it.unibo.tuprolog.solve.SolveOptions
import it.unibo.tuprolog.solve.Solver
import it.unibo.tuprolog.solve.channel.InputChannel
import it.unibo.tuprolog.solve.channel.OutputChannel
import it.unibo.tuprolog.solve.exception.Warning
import it.unibo.tuprolog.solve.flags.TrackVariables
import it.unibo.tuprolog.solve.library.Runtime
import it.unibo.tuprolog.solve.libs.io.IOLib
import it.unibo.tuprolog.solve.libs.oop.OOPLib
import it.unibo.tuprolog.theory.Theory
import it.unibo.tuprolog.theory.parsing.parseAsTheory
import it.unibo.tuprolog.ui.gui.TuPrologIDEModel.State
import it.unibo.tuprolog.utils.Cached
import org.reactfx.EventSource
import java.io.File
import java.util.EnumSet
import java.util.concurrent.ExecutorService

@Suppress("UnsafeCallOnNullableType", "TooManyFunctions")
internal class TuPrologIDEModelImpl(
    override val executor: ExecutorService,
    var customizer: ((MutableSolver) -> MutableSolver)? = { it },
) : TuPrologIDEModel {
    companion object {
        const val DEFAULT_TIMEOUT: Long = 5000
    }

    private data class FileContent(var text: String, var changed: Boolean = true) {
        fun text(text: String) {
            if (text != this.text) changed = true
            this.text = text
        }

        fun text(): String = text.also { changed = false }
    }

    private var tempFiles = 0

    private val files = mutableMapOf()

    private var solutions: Iterator? = null

    private var solutionCount = 0

    private var lastGoal: Struct? = null

    private var stdin: String = ""

    override var query: String = ""

    override var currentFile: File? = null
        set(value) {
            if (field != null && field != value) {
                files[field]?.changed = true
            }
            field = value
        }

    override var solveOptions: SolveOptions = SolveOptions.DEFAULT.setTimeout(DEFAULT_TIMEOUT)
        set(value) {
            val changed = field != value
            field = value
            if (changed) {
                onSolveOptionsChanged.push(value)
            }
        }

    override fun newFile(): File =
        File.createTempFile("untitled-${++tempFiles}-", ".pl").also {
            onFileCreated.push(it)
            loadFile(it)
        }

    override fun loadFile(file: File) {
        val text = file.readText()
        setFile(file, text)
        onFileLoaded.push(file to text)
        selectFile(file)
    }

    override fun saveFile(file: File) {
        file.writeText(getFile(file))
    }

    override fun selectFile(file: File) {
        currentFile = file
        onFileSelected.push(file)
    }

    override fun getFile(file: File): String {
        return files[file]!!.text
    }

    override fun setFile(
        file: File,
        theory: String,
    ) {
        if (file in files) {
            files[file]?.text(theory)
        } else {
            files[file] = FileContent(theory, true)
        }
    }

    override fun renameFile(
        file: File,
        newFile: File,
    ) {
        files[newFile] = files[file]!!
        files -= file
    }

    override fun setCurrentFile(theory: String) {
        setFile(currentFile!!, theory)
    }

    override fun setStdin(content: String) {
        ensuringStateIs(State.IDLE) {
            stdin = content
            solver.invalidate()
            // solver.regenerate()
        }
    }

    override fun quit() {
        onQuit.push(Unit)
    }

    @Volatile
    override var state: State = State.IDLE
        private set

    override fun customizeSolver(customizer: (MutableSolver) -> MutableSolver) {
        this.customizer = customizer
        this.solver.invalidate()
        this.solver.regenerate()
    }

    private inline fun  ensuringStateIs(
        state: State,
        vararg states: State,
        action: () -> T,
    ): T {
        val admissibleStates = EnumSet.of(state, *states)
        if (this.state in admissibleStates) {
            return action()
        } else {
            error("Invalid state $state: should be among $admissibleStates")
        }
    }

    private val solver =
        Cached.of {
            var newSolver =
                Solver.prolog.newBuilder()
                    .runtime(Runtime.of(OOPLib, IOLib))
                    .flag(TrackVariables) { ON }
                    .standardInput(InputChannel.of(stdin))
                    .standardOutput(OutputChannel.of { onStdoutPrinted.push(it) })
                    .standardError(OutputChannel.of { onStderrPrinted.push(it) })
                    .warnings(OutputChannel.of { onWarning.push(it) })
                    .buildMutable()
            if (this.customizer != null) {
                newSolver = this.customizer!!(newSolver)
            }
            newSolver.also {
                onNewSolver.push(SolverEvent(Unit, it))
            }
        }

    override fun closeFile(file: File) {
        files -= file
        onFileClosed.push(file)
    }

    override fun reset() {
        ensuringStateIs(State.IDLE, State.SOLUTION) {
            if (state == State.SOLUTION) {
                stop()
            }
            solver.invalidate()
            solver.regenerate()
            onReset.push(SolverEvent(Unit, solver.value))
            try {
                loadCurrentFileAsStaticKB(onlyIfChanged = false)
            } catch (e: SyntaxException) {
                onError.push(e)
            }
        }
    }

    override fun solve() {
        solveImpl {
            state = State.COMPUTING
            nextImpl()
        }
    }

    override fun solveAll() {
        solveImpl {
            state = State.COMPUTING
            onResolutionStarted.push(SolverEvent(++solutionCount, solver.value))
            nextAllImpl()
        }
    }

    private fun solveImpl(continuation: () -> Unit) {
        ensuringStateIs(State.IDLE, State.SOLUTION) {
            try {
                solutions = newResolution()
                solutionCount = 0
                onNewQuery.push(SolverEvent(lastGoal!!, solver.value))
                continuation()
            } catch (e: SyntaxException) {
                onError.push(e)
            }
        }
    }

    private fun newResolution(): Iterator {
        loadCurrentFileAsStaticKB()
        solver.value.let {
            lastGoal = parseQueryAsStruct(it.operators)
            return it.solve(lastGoal!!, solveOptions).iterator()
        }
    }

    private fun parseQueryAsStruct(operators: OperatorSet): Struct {
        try {
            return query.parseAsStruct(operators)
        } catch (e: ParseException) {
            throw SyntaxException.InQuerySyntaxError(query, e)
        }
    }

    private fun loadCurrentFileAsStaticKB(onlyIfChanged: Boolean = true) {
        currentFile?.let {
            loadCurrentFileAsStaticKB(solver.value, it, files[it], onlyIfChanged)
        }
    }

    private fun loadCurrentFileAsStaticKB(
        solver: MutableSolver,
        file: File,
        content: FileContent?,
        onlyIfChanged: Boolean,
    ) {
        if (onlyIfChanged && content?.changed == true) {
            return
        }
        try {
            val theory =
                content?.text()?.parseAsTheory(solver.operators)
                    ?: Theory.empty(solver.unificator)
            solver.resetDynamicKb()
            solver.loadStaticKb(theory)
            onNewStaticKb.push(SolverEvent(Unit, solver))
        } catch (e: ParseException) {
            content?.changed = true
            throw SyntaxException.InTheorySyntaxError(file, e)
        }
    }

    private fun nextImpl() {
        executor.execute {
            onResolutionStarted.push(SolverEvent(++solutionCount, solver.value))
            val sol = solutions!!.next()
            onResolutionOver.push(SolverEvent(solutionCount, solver.value))
            onNewSolution.push(SolverEvent(sol, solver.value))
            state =
                if (!sol.isYes || !solutions!!.hasNext()) {
                    onQueryOver.push(SolverEvent(sol.query, solver.value))
                    State.IDLE
                } else {
                    State.SOLUTION
                }
        }
    }

    private fun nextAllImpl() {
        executor.submit {
            val sol = solutions!!.next()
//            onResolutionOver.push(solutionCount)
            solutionCount++
            onNewSolution.push(SolverEvent(sol, solver.value))
            if (!solutions!!.hasNext() || state != State.COMPUTING) {
                onResolutionOver.push(SolverEvent(solutionCount, solver.value))
                onQueryOver.push(SolverEvent(sol.query, solver.value))
                state = State.IDLE
            } else {
                nextAllImpl()
            }
        }
    }

    override fun next() {
        ensuringStateIs(State.SOLUTION) {
            state = State.COMPUTING
            nextImpl()
        }
    }

    override fun nextAll() {
        ensuringStateIs(State.SOLUTION) {
            state = State.COMPUTING
            onResolutionStarted.push(SolverEvent(solutionCount, solver.value))
            nextAllImpl()
        }
    }

    override fun stop() {
        ensuringStateIs(State.SOLUTION) {
            state = State.IDLE
            onQueryOver.push(SolverEvent(lastGoal!!, solver.value))
        }
    }

    override val onQuit: EventSource = EventSource()

    override val onReset: EventSource> = EventSource()

    override val onSolveOptionsChanged: EventSource = EventSource()

    override val onFileSelected: EventSource = EventSource()

    override val onFileCreated: EventSource = EventSource()

    override val onFileLoaded: EventSource> = EventSource()

    override val onFileClosed: EventSource = EventSource()

    override val onQueryChanged: EventSource = EventSource()

    override val onNewQuery: EventSource> = EventSource()

    override val onNewSolver: EventSource> = EventSource()

    override val onNewStaticKb: EventSource> = EventSource()

    override val onResolutionStarted: EventSource> = EventSource()

    override val onNewSolution: EventSource> = EventSource()

    override val onResolutionOver: EventSource> = EventSource()

    override val onQueryOver: EventSource> = EventSource()

    override val onStdoutPrinted: EventSource = EventSource()

    override val onStderrPrinted: EventSource = EventSource()

    override val onWarning: EventSource = EventSource()

    override val onError: EventSource = EventSource()
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy