%! Author = tdpeuter %! Date = 27/03/2025 \documentclass[11pt,a4paper]{article} \usepackage{amsmath} \usepackage[dutch]{babel} % Nederlands taal \usepackage[style=apa]{biblatex} % Bronnen \usepackage{enumitem} % Aanpasbare lijsten \usepackage[margin=1in]{geometry} % Sane marges \usepackage{hyperref} % Hyperlinks \usepackage{minted} % Syntax highlighting \usepackage{multicol} % Meerdere kolommen \usepackage{url} % Beter geformatteerde URLs \addbibresource{bibliography.bib} \title{Ghent Prolog} \author{Tibo De Peuter} \date{\today} \begin{document} \maketitle \abstract{ % KERN: Wat is Ghent Prolog? Ghent Prolog is een command-line Prolog interpreter geschreven in Kotlin. Het biedt een subset van de functionaliteit van SWI-Prolog, waaronder database operaties en meta abstracties. } \section{Overzicht}\label{sec:overzicht} % KERN: Programmeertaal, libraries, etc. Ghent Prolog werd geschreven in Kotlin en maakt gebruik van zowel object-gerichte als functionele concepten. Er werden twee bibliotheken gebruikt: \begin{itemize} \item \href{https://github.com/h0tk3y/better-parse}{\texttt{better-parse}}, voor het parsen van een gegeven grammatica, \item \href{https://github.com/xenomachina/kotlin-argparser}{\texttt{kotlin-argparser}}, voor het gebruik van command-line argumenten. \end{itemize} % KERN: Hoe ziet Ghent Prolog er abstract uit? De interpreter bestaat uit een aantal fases, die zo onafhankelijk mogelijk zijn om de implementatie modulair en uitbreidbaar te maken. \begin{enumerate} \item Lexing and parsing \item Preprocessor \item Programma en read-eval-print loop (REPL) \end{enumerate} \section{Lexing, parsing en preprocessing}\label{sec:lexing-parsing-preprocessing} Traditioneel zijn de lexer en parser aparte componenten. Om tijd te besparen werd echter voor het lexen en parsen van Prolog broncode en REPL-invoer gebruik gemaakt van de \texttt{better-parse} bibliotheek. De parser maakt gebruik van een vereenvoudigde Prolog grammatica, gebaseerd op \href{https://github.com/antlr/grammars-v4/blob/master/prolog/prolog.g4}{de ANTLR Prolog grammatica}, \href{https://sicstus.sics.se/sicstus/docs/3.7.1/html/sicstus_45.html#SEC370}{SICStus Prolog Full Prolog Syntax} en \href{https://github.com/simonkrenger/ch.bfh.bti7064.w2013.PrologParser/blob/2c06e5a221c1cc51ba766304250749a7f0caed8c/doc/prolog-bnf-grammar.txt}{simonkrenger/PrologParser's BNF}. Omdat de lexer en parser niet op maat gemaakt zijn, worden ze bewust zo eenvoudig mogelijk gehouden. Hun verantwoordelijkheid is het omzetten van inputtekst naar een basis Abstract Syntax Tree (AST), die enkel uit generieke Prolog termen bestaat. Daarna worden de termen in de AST omgezet naar specifieke klassen die de ingebouwde operatoren voorstellen. \section{Programma en REPL}\label{sec:programma-repl} % KERN: Datastructuur Ghent Prolog maakt gebruik van functie-oproepen streams om het backtracking mechanisme te implementeren. Bijzondere logica met betrekking tot \textit{choicepoints} en foutenafhandeling maakt gebruik van de Kotlin \texttt{Result} klasse. Om de resultaten van unificatie en evaluatie lazy te verwerken, wordt er gebruik gemaakt van Kotlin \texttt{Sequence}s (\textit{lazy streams}). Meer specifiek wordt er gebruik gemaakt van de volgende types: \begin{minted}{kotlin} typealias Substitutions = Map typealias Answer = Result typealias Answers = Sequence \end{minted} Een lege \texttt{Sequence} betekent dat er geen oplossingen zijn gevonden. Een \texttt{Result} met een \texttt{Substitutions} representeert een oplossing met de resulterende substituties (die leeg kan zijn). Een \texttt{Result} met een \texttt{Failure} betekent een fout of bijzondere logica die afgehandeld moet worden. Er werden twee interfaces gedefinieerd: \begin{minted}{kotlin} interface Resolvent { fun resolvent(goal: Goal, substitutions: Substitutions): Answers } interface Satisfiable { fun satisfy(substitutions: Substitutions): Answers } \end{minted} Klassen die \texttt{Resolvent} implementeren kunnen een goal oplossen, bijvoorbeeld een database of een regel (\textit{rule}). Klassen die \texttt{Satisfiable} implementeren kunnen worden geëvalueerd, bijvoorbeeld operatoren. \subsection{Evaluatiestrategie}\label{subsec:evaluatiestrategie} % KERN: Evaluatiestrategie Ghent Prolog maakt gebruik van een depth-first zoekstrategie. Op het moment dat de databank een goal gevraagd wordt (\textit{query}), geeft die de goal beurtelings door aan de overeenkomende clauses, aan de hand van \texttt{solve}. Eerst wordt de goal met de head van de clause geünificeerd, wat nieuwe substituties kan introduceren. Daarna wordt de body van de clause geëvalueerd (\texttt{body.solve}), die zo de nieuwe goal wordt. Deze procedure wordt herhaald totdat de goal \texttt{true} of \texttt{false} is. Vervolgens zal het programma backtracken naar de functie die de laatste stap uitvoerde, en de pas geïntroduceerde substituties omhoog doorgeven. Daar kan dan logica uitgevoerd worden, of het resultaat verder doorgegeven worden aan de volgende stap. % TODO Verklarende figuur met substitutie toevoegen % Overzicht van geïmplementeerde predicaten in appendix. Operator-specifieke logica bevindt zich in de klassen van de operatoren zelf. \subsection{Unificatie}\label{subsec:unificatie} Zoals beschreven in Artificial Intelligence a Modern Approach. Algoritme is geïnspireerd door \subsection{Cut}\label{subsec:cut} \subsection{Meta abstracties}\label{subsec:meta-abstractions} \section{Resultaat}\label{sec:resultaat} % Code wordt lelijk en onoverzichtelijk. Door de geneste datatypes veel boilerplate. Omdat er veel overerving is, waarbij toch steeds methoden moeten worden overschreven, is er vaak boilerplate code nodig. Waarschijnlijk is een visitor-patroon beter geschikt. \subsection{Afwijkingen van SWI-Prolog}\label{subsec:afwijkingen} % TODO Maak appendix? % Occurs check % SLD Resolutie In tegenstelling tot ISO Prolog en SWI-Prolog, maak ik geen gebruik van SLD-resolutie. % TODO Bronnen voor SLD-resolutie % TODO Bronnen voor ISO-Prolog maakt gebruik van SLD resolutie % TODO Bronnen voor SWIPL maakt gebruik van SLD resolutie \section{Toekomstig werk}\label{sec:toekomstig-werk} % Stack gebruiken \section{Conclusie}\label{sec:conclusie} \printbibliography \appendix \newpage \section{Aanvullende opmerkingen}\label{sec:aanvullende-opmerkingen} \subsection{Operator precedentie en associativiteit}\label{subsec:operator-precedence} Ghent Prolog ondersteunt operator precedentie en associativiteit. Deze functionaliteit bevindt zich in de parser, omdat de argumenten van een operator steeds rechtstreeks als parameters in de constructor van de klasse worden meegegeven. Operator precedentie en associativiteit werd geïmplementeerd volgens de \href{https://www.swi-prolog.org/pldoc/man?section=operators}{SWI-Prolog documentatie}. \subsection{Test driven development}\label{subsec:ttd} Doorheen de ontwikkeling van grote delen van mijn implementatie heb ik gebruik gemaakt van Test Driven Development, onder andere met behulp van \href{https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-t-o-d-o.html}{Kotlin \texttt{TODO}}. \subsection{Onafgewerkte Lexer en Parser implementatie}\label{subsec:lexer-parser} Bij de start van het project was ik begonnen met het schrijven van mijn eigen lexer en parser. Uit gebruik omdat het eenvoudiger was om de parser library % TODO reference sectie over de parser te gebruiken. De implementatie was gebaseerd op \href{https://craftinginterpreters.com/contents.html}{Crafting Interpreters, Robert Nystrom} en \href{https://www.youtube.com/playlist?list=PLGNbPb3dQJ_5FTPfFIg28UxuMpu7k0eT4}{Building a Parser from scratch, Dmitry Soshnikov}. De voorlopige implementatie van de lexer en parser kunnen hier teruggevonden worden. % TODO Link naar commit met voorlopige implementatie \section{Uitvoeren en testen}\label{sec:uitvoeren-en-testen} Om Ghent Prolog op een Windows, Linux of MacOS uit te voeren is het voldoende om Java te installeren en Ghent Prolog op te roepen met `./src/gpl`. De nodige stappen, waaronder het bouwen van een JAR, worden dan automatisch uitgevoerd. De ingediende JAR kan ook handmatig opgeroepen worden met \texttt{java -jar ./build/gpl.jar}. Er wordt ook een Docker omgeving voorzien waarin Ghent Prolog opgeroepen kan worden met \texttt{gpl}. Het programma ondersteunt de volgende vlaggen: % TODO gpl --help \subsection{Testen}\label{subsec:testen} De testen kunnen uitgevoerd worden door de meeste IDE's. Alternatief kunnen de testen uitgevoerd worden met \texttt{./gradlew test}. Resultaten worden naar \texttt{stdout} geschreven of kunnen bekeken worden met % TODO HTML rapporten. \section{Overzicht van geïmplementeerde predicaten}\label{sec:predicaten} \begin{multicols}{2} \begin{itemize}[label={}] \item \textbf{Analysing and Constructing Terms} \begin{itemize} \item \texttt{functor/3} \item \texttt{arg/3} \item \texttt{=..} \item \texttt{numbervars/1} \item \texttt{numbervars/3} \end{itemize} \item \textbf{Arithmetic} \begin{itemize} \item \texttt{between/3} \item \texttt{succ/2} \item \texttt{plus/3} \item \texttt{=\textbackslash=/2} \item \texttt{=:=/2} \item \texttt{is/2} \item \texttt{-/1} \item \texttt{+/1} \item \texttt{+/2} \item \texttt{*/2} \item \texttt{//2} \item \texttt{inf/0} \end{itemize} \item \textbf{Comparison and Unification of Terms} \begin{itemize} \item \texttt{=/2} \item \texttt{\textbackslash=/2} \item \texttt{==/2} \item \texttt{\textbackslash==/2} \end{itemize} \item \textbf{Control Predicates} \begin{itemize} \item \texttt{fail/0} \item \texttt{false/0} \item \texttt{true/0} \item \texttt{!/0} \item \texttt{,/2} \item \texttt{;/2} \item \texttt{|/2} \item \texttt{\textbackslash+/1} \end{itemize} \item \textbf{Database} \begin{itemize} \item \texttt{retract/1} \item \texttt{retractall/1} \item \texttt{asserta/1} \item \texttt{assertz/1} \item \texttt{assert/1} \end{itemize} \item \textbf{Declaring predicate properties} \begin{itemize} \item \texttt{dynamic/1} \end{itemize} \item \textbf{Delimited continuations} \begin{itemize} \item \texttt{reset/3} \item \texttt{shift/1} \end{itemize} \item \textbf{Examining the program} \begin{itemize} \item \texttt{clause/2} \end{itemize} \item \textbf{Forall} \begin{itemize} \item \texttt{forall/2} \end{itemize} \item \textbf{Loading Prolog source files} \begin{itemize} \item \texttt{consult/1} \item \texttt{initialization/1} \end{itemize} \item \textbf{Meta-Call Predicates} \begin{itemize} \item \texttt{call/1} \item \texttt{once/1} \item \texttt{ignore/1} \end{itemize} \item \textbf{Primitive character I/O} \begin{itemize} \item \texttt{nl/0} \end{itemize} \item \textbf{Term reading and writing} \begin{itemize} \item \texttt{write/1} \item \texttt{writeln/1} \item \texttt{read/1} \end{itemize} \item \textbf{Verify Type of a Term} \begin{itemize} \item \texttt{var/1} \item \texttt{nonvar/1} \item \texttt{atom/1} \item \texttt{compound/1} \end{itemize} \end{itemize} \end{multicols} \section{Bestaande Prolog implementaties}\label{sec:prolog-implementaties} Tijdens mijn onderzoek naar Prolog implementaties, kwam ik verschillende bestaande Prolog implementaties tegen die niet vermeld worden in~\cite{enwiki:1274527056}. Daarom zou ik durven stellen dat er geen gebrek is aan Prolog implementaties, maar dat er een gebrek is aan \emph{goede} Prolog implementaties. Vaak zijn deze implementaties niet meer dan een proof of concept. Ze waren enkel nuttig als inspiratie om specifieke problemen die zich tijdens de ontwikkeling van Ghent Prolog voordeden op te lossen. \begin{itemize} \item \href{https://github.com/adamjstewart/prolog}{adamjstewart/prolog}, in OCaml \item \href{https://plzoo.andrej.com/language/miniprolog.html}{miniprolog}, in OCaml \item \href{https://github.com/benjamin-hodgson/Amateurlog}{benjamin-hodgson/Amateurlog}, in C\# \end{itemize} Van de implementaties die wel vermeld worden in~\cite{enwiki:1274527056}, zijn \href{https://www.swi-prolog.org/}{SWI-Prolog}, \href{https://sicstus.sics.se/index.html}{SICStus Prolog} en \href{https://dobrev.com/}{Strawberry Prolog} de meest interessante. \section{Dankwoord}\label{sec:dankwoord} Bedankt, LogicalCaptain % TODO Link naar account om steeds nuttige informatie en voorbeelden te geven bij SWI-Prolog documentatie die iets minder duidelijk is. Uw nota's waren verhelderend en zorgden voor een beter begrip van en voor de nuances van SWI-Prolog. \end{document}