This repository has been archived on 2025-09-23. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
2025LogProg-project-GhentPr.../src/interpreter/Preprocessor.kt
2025-05-09 08:36:11 +02:00

189 lines
7.4 KiB
Kotlin

package interpreter
import io.Logger
import prolog.ast.arithmetic.Expression
import prolog.ast.arithmetic.Integer
import prolog.ast.lists.List as PList
import prolog.ast.logic.Clause
import prolog.ast.logic.Fact
import prolog.ast.logic.LogicOperand
import prolog.ast.logic.Rule
import prolog.ast.terms.*
import prolog.builtins.*
/**
* Preprocessor for Prolog
*
* This class preprocesses Prolog code and applies various transformations such as recognizing builtins.
*/
open class Preprocessor {
/**
* Preprocesses the input Prolog code.
*
* @param input The already parsed Prolog code as a list of clauses.
* @return The preprocessed Prolog code as a list of clauses.
*/
fun preprocess(input: List<Clause>): List<Clause> {
return input.map { preprocess(it) }
}
fun preprocess(input: Query): Query {
return Query(preprocess(input.query) as Goal)
}
private fun preprocess(clause: Clause): Clause {
return when (clause) {
is Fact -> {
Fact(preprocess(clause.head) as Head)
}
is Rule -> {
Rule(
preprocess(clause.head) as Head,
preprocess(clause.body as Term) as Body
)
}
else -> clause
}
}
protected open fun preprocess(term: Term, nested: Boolean = false): Term {
// TODO Remove hardcoding by storing the functors as constants in operators?
val prepped = when (term) {
Variable("_") -> AnonymousVariable.create()
is Atom, is Structure -> {
// Preprocess the arguments first to recognize builtins
val args = if (term is Structure) {
term.arguments.map { preprocess(it, nested = true) }
} else emptyList()
when (term.functor) {
// Analysis
Functor.of("functor/3") -> FunctorOp(args[0], args[1], args[2])
Functor.of("arg/3") -> Arg(args[0], args[1], args[2])
Functor.of("clause/2") -> ClauseOp(args[0] as Head, args[1] as Body)
Functor.of("atomic/1") -> AtomicOp(args[0])
Functor.of("compound/1") -> CompoundOp(args[0])
Functor.of("=../2") -> Univ(args[0], args[1])
// Arithmetic
Functor.of("inf/0") -> Integer(Int.MAX_VALUE)
Functor.of("=\\=/2") -> if (args.all { it is Expression }) {
EvaluatesToDifferent(args[0] as Expression, args[1] as Expression)
} else term
Functor.of("=:=/2") -> if (args.all { it is Expression }) {
EvaluatesTo(args[0] as Expression, args[1] as Expression)
} else term
Functor.of("is/2") -> if (args.all { it is Expression }) {
Is(args[0] as Expression, args[1] as Expression)
} else term
Functor.of("-/1") -> if (args.all { it is Expression }) Negate(args[0] as Expression) else term
Functor.of("+/1") -> if (args.all { it is Expression }) Positive(args[0] as Expression) else term
Functor.of("+/2") -> if (args.all { it is Expression }) {
Add(args[0] as Expression, args[1] as Expression)
} else term
Functor.of("-/2") -> if (args.all { it is Expression }) {
Subtract(args[0] as Expression, args[1] as Expression)
} else term
Functor.of("*/2") -> if (args.all { it is Expression }) {
Multiply(args[0] as Expression, args[1] as Expression)
} else term
Functor.of("//2") -> if (args.all { it is Expression }) {
Divide(args[0] as Expression, args[1] as Expression)
} else term
Functor.of("between/3") -> if (args.all { it is Expression }) {
Between(args[0] as Expression, args[1] as Expression, args[2] as Expression)
} else term
Functor.of("succ/2") -> if (args.all { it is Expression }) {
Successor(args[0] as Expression, args[1] as Expression)
} else term
// Control
Functor.of("fail/0") -> Fail
Functor.of("false/0") -> False
Functor.of("true/0") -> True
Functor.of("!/0") -> Cut()
Functor.of(",/2") -> Conjunction(args[0] as LogicOperand, args[1] as LogicOperand)
Functor.of(";/2") -> Disjunction(args[0] as LogicOperand, args[1] as LogicOperand)
Functor.of("\\+/1") -> Not(args[0] as Goal)
// Database
Functor.of("dynamic/1") -> Dynamic(Functor.of((args[0] as Atom).name))
Functor.of("retract/1") -> Retract(args[0])
Functor.of("retractall/1") -> RetractAll(args[0])
Functor.of("assert/1") -> {
if (args[0] is Rule) {
Assert(args[0] as Rule)
} else {
Assert(Fact(args[0] as Head))
}
}
Functor.of("asserta/1") -> {
if (args[0] is Rule) {
AssertA(args[0] as Rule)
} else {
AssertA(Fact(args[0] as Head))
}
}
Functor.of("assertz/1") -> {
if (args[0] is Rule) {
AssertZ(args[0] as Rule)
} else {
AssertZ(Fact(args[0] as Head))
}
}
// IO
Functor.of("write/1") -> Write(args[0])
Functor.of("nl/0") -> Nl
Functor.of("read/1") -> Read(args[0])
// Lists
Functor.of("member/2") -> Member(args[0], args[1] as PList)
// Meta
Functor.of("call/1") -> Call(args[0] as Goal)
Functor.of("ignore/1") -> Ignore(args[0] as Goal)
// Other
Functor.of("initialization/1") -> Initialization(args[0] as Goal)
Functor.of("forall/2") -> ForAll(args[0] as LogicOperand, args[1] as Goal)
// Unification
Functor.of("=/2") -> Unify(args[0], args[1])
Functor.of("\\=/2") -> NotUnify(args[0], args[1])
Functor.of("==/2") -> Equivalent(args[0], args[1])
Functor.of("\\==/2") -> NotEquivalent(args[0], args[1])
Functor.of(":-/2") -> Rule(args[0] as Head, args[1] as Body)
else -> {
if (term is Structure) term.arguments = args
term
}
}
}
else -> term
}
Logger.debug(
"Preprocessed term $term into $prepped (kind ${prepped::class.simpleName})",
!nested && (prepped != term || prepped::class != term::class)
)
return prepped
}
}