Commit ee5c3677 authored by Martin Spoto's avatar Martin Spoto
Browse files

Add comments to remaining classes

parent 43dd651d
......@@ -4,6 +4,10 @@ import com.oracle.truffle.api.TruffleFile;
import java.nio.charset.Charset;
/**
* Class implementing a detector for Prolog files.
* @author Martin Spoto
*/
public final class ProloGraalFileDetector implements TruffleFile.FileTypeDetector {
@Override
......
......@@ -2,10 +2,7 @@ package ch.heiafr.prolograal;
import ch.heiafr.prolograal.nodes.ProloGraalEvalRootNode;
import ch.heiafr.prolograal.parser.ProloGraalParserImpl;
import ch.heiafr.prolograal.runtime.ProloGraalBoolean;
import ch.heiafr.prolograal.runtime.ProloGraalContext;
import ch.heiafr.prolograal.runtime.ProloGraalRuntime;
import ch.heiafr.prolograal.runtime.ProloGraalTerm;
import ch.heiafr.prolograal.runtime.*;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.Truffle;
......@@ -19,7 +16,14 @@ import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
/**
* Class representing the Truffle language for Prolog, ProloGraal.
* Most parts are adapted from the SimpleLanguage demo implementation.
* @author Martin Spoto
*/
@TruffleLanguage.Registration(id = ProloGraalLanguage.ID, name = "Prolog", defaultMimeType =
ProloGraalLanguage.MIME_TYPE, characterMimeTypes = ProloGraalLanguage.MIME_TYPE, contextPolicy =
ContextPolicy.SHARED, fileTypeDetectors = ProloGraalFileDetector.class)
......@@ -52,21 +56,29 @@ public class ProloGraalLanguage extends TruffleLanguage<ProloGraalContext> {
return new ProloGraalContext(env);
}
/**
* Parse the given request and starts the evaluation of the source file.
* This method is called automatically by the launcher.
* @param request the request to parse
* @return a call target representing the "main" of the parsed file.
*/
@Override
protected CallTarget parse(ParsingRequest request) throws Exception {
protected CallTarget parse(ParsingRequest request) {
Source source = request.getSource();
ProloGraalRuntime result = null;
List<ProloGraalClause> clauses = new ArrayList<>();
if (request.getArgumentNames().isEmpty()) {
long time = System.currentTimeMillis();
result = ProloGraalParserImpl.parseProloGraal(this, source);
clauses = ProloGraalParserImpl.parseProloGraal(source);
long time2 = System.currentTimeMillis();
if (DEBUG_MODE) {
System.out.println("Parsing time : " + (time2 - time) + "ms");
}
}
RootNode eval = new ProloGraalEvalRootNode(this, result);
ProloGraalRuntime runtime = new ProloGraalRuntime(clauses, this.getContextReference().get());
RootNode eval = new ProloGraalEvalRootNode(this, runtime);
return Truffle.getRuntime().createCallTarget(eval);
}
......
......@@ -7,6 +7,7 @@ import ch.heiafr.prolograal.runtime.ProloGraalRuntime;
* Abstract base class for built-ins.
* A built-in must inherit from this class and be added to the built-in list to work.
* The built-in list is handled in the installBuiltins method from {@link ProloGraalRuntime}.
* @see ProloGraalRuntime
* @author Martin Spoto
*/
public abstract class ProloGraalBuiltinClause extends ProloGraalClause {
......
......@@ -121,7 +121,10 @@ public class ProloGraalInterpreterNode extends RootNode {
try {
if (!skipParsing) {
// parse the source to get a runtime
runtime = ProloGraalParserImpl.parseProloGraal(language, source);
runtime = new ProloGraalRuntime(
ProloGraalParserImpl.parseProloGraal(source),
language.getContextReference().get()
);
lastRuntime = runtime;
} else {
runtime = lastRuntime;
......
......@@ -7,14 +7,39 @@ import org.antlr.v4.runtime.ParserRuleContext;
import java.util.*;
import java.util.stream.Collectors;
/**
* Class implementing the "listener" ANTLR parsing strategy.
* Each method is called automatically by the default {@link org.antlr.v4.runtime.tree.ParseTreeWalker}.<br>
* The idea is to build the representation of the Prolog source "bottom-up", meaning elements are added to a stack
* once we hit a leaf of the parse tree (like atoms, numbers). This stack is then popped when we exit a container
* like a structure or a list.
* @see ProloGraalParserImpl
* @see ProloGraalParseError
* @author Martin Spoto
*/
public class ProloGraalListenerImpl extends ProloGraalBaseListener {
// both deque are used as stack so we do not need to reverse everything, and because it is encouraged to use
// deque in the stack javadoc
// temporary stack of elements to store and manipulate them until they are finally pushed into a clause
private Deque<ProloGraalTerm<?>> elements = new ArrayDeque<>();
// stack of final clauses
private Deque<ProloGraalClause> clauses = new ArrayDeque<>();
// provides temporary storage for the tail of a list since it needs special handling
private ProloGraalTerm<?> tail;
/**
* Returns the clauses parsed from the file. Must be called after walking through the tree using the default
* {@link org.antlr.v4.runtime.tree.ParseTreeWalker}.
* @return The list of parsed clauses, in order of encounter in the source file
*/
public List<ProloGraalClause> getClauses() {
List<ProloGraalClause> r = new ArrayList<>();
// we use a descending iterator to reverse the order of the clauses
// since a stack is LIFO and we need to read FIFO to get the correct order
Iterator<ProloGraalClause> it = clauses.descendingIterator();
it.forEachRemaining(r::add);
......@@ -35,41 +60,35 @@ public class ProloGraalListenerImpl extends ProloGraalBaseListener {
message);
}
/*
clause :
fact |
composedClause
;
*/
@Override
public void enterClause(ProloGraalParser.ClauseContext ctx) {
// when we enter a clause, we need to create a new "context" for it.
// Clauses are the "top-level" of Prolog.
clauses.push(new ProloGraalClause());
}
@Override
public void exitGoal(ProloGraalParser.GoalContext ctx) {
// add the goal to the current clause
clauses.peek().addGoal(elements.pop());
}
@Override
public void exitHead(ProloGraalParser.HeadContext ctx) {
// set the head of the current clause
clauses.peek().setHead(elements.pop());
}
/*
atom :
ATOM |
LIST_START LIST_END // empty list
;
*/
@Override
public void enterAtom(ProloGraalParser.AtomContext ctx) {
// create an atom an push it to the element stack
ProloGraalAtom atom = new ProloGraalAtom(clauses.peek().getVariables(), ctx.getText());
elements.push(atom);
}
@Override
public void enterNumber(ProloGraalParser.NumberContext ctx) {
// create a number according to its type (Integer or Double) and push it to the element stack
String n = ctx.getText();
try {
elements.push(new ProloGraalIntegerNumber(clauses.peek().getVariables(), Integer.parseInt(n)));
......@@ -80,70 +99,88 @@ public class ProloGraalListenerImpl extends ProloGraalBaseListener {
@Override
public void enterVariable(ProloGraalParser.VariableContext ctx) {
// create a new variable
ProloGraalVariable variable = new ProloGraalVariable(clauses.peek().getVariables(), ctx.getText());
// check if the current clause's variables list already contains a reference to this new variable (using its name to compare)
if (clauses.peek().getVariables().containsKey(variable)) {
elements.push(clauses.peek().getVariables().get(variable)); // add a reference to the previously added one
// if the variable already exists in the clause, we must not add a new one but use a reference instead
elements.push(clauses.peek().getVariables().get(variable));
} else {
// else we can just add the variable to the element stack
elements.push(variable);
// and we must directly add it to the current clause's variables list as well
clauses.peek().getVariables().put(variable, variable);
}
}
/*
composedTerm :
functor ('(' term (',' term)* ')')
;
*/
@Override
public void enterComposedTerm(ProloGraalParser.ComposedTermContext ctx) {
// when we enter a composed term, we push a new structure immediately to "mark" its beginning
elements.push(new ProloGraalStructure(clauses.peek().getVariables()));
}
@Override
public void exitComposedTerm(ProloGraalParser.ComposedTermContext ctx) {
// when we exit a composed term, we need to pop from the element stack
// until we reach the beginning of the structure, indicated by the structure itself
// temporary list to store elements after we pop them
List<ProloGraalTerm<?>> subterms = new ArrayList<>();
while (true) {
// save elements to the temporary list until we reach a structure
// this operation also reverses the elements in the stack into the correct order
while (!(elements.peek() instanceof ProloGraalStructure)) {
subterms.add(elements.pop());
}
if (((ProloGraalStructure) elements.peek()).getArity() > 0) { // element is a composed term already handled
// since we can have nested structures, we need to check that we are at an empty one (nested ones would have
// already been called earlier and wouldn't be empty)
if (((ProloGraalStructure) elements.peek()).getArity() > 0) {
// if it is a nested structure, add it to subterms like normal
subterms.add(elements.pop());
} else {
// else we reached the "root" of this structure and we can break out
break;
}
}
// add elements to the structure
ProloGraalStructure struct = (ProloGraalStructure) elements.peek();
for (int i = subterms.size() - 1; i >= 0; i--) {
struct.addSubterm(subterms.get(i));
}
// no need to do anything else since it was already in the element stack (we do not remove it here)
}
@Override
public void exitFunctor(ProloGraalParser.FunctorContext ctx) {
try {
// a functor is always an atom so we can just pop from the element stack
ProloGraalAtom functor = (ProloGraalAtom) elements.pop();
// it will also always be preceded by a structure so we can retrieve it
ProloGraalStructure struct = (ProloGraalStructure) elements.peek();
// and set its functor correctly
struct.setFunctor(functor);
} catch (ClassCastException ex) {
throwParseError(ctx, "Invalid state : " + ex.getLocalizedMessage());
}
}
private ProloGraalTerm<?> tail;
@Override
public void exitTail(ProloGraalParser.TailContext ctx) {
// retrieve the tail which is the latest element in the stack
// since we do this on exit, this will also work even if the tail is itself a list
tail = elements.pop();
}
@Override
public void enterList(ProloGraalParser.ListContext ctx) {
// similar to structures, add a new list to the stack when we enter one
elements.push(new ProloGraalList(clauses.peek().getVariables()));
}
@Override
public void exitList(ProloGraalParser.ListContext ctx) {
// similar logic to exitComposedTerm, except that we need to handle the tail correctly
List<ProloGraalTerm<?>> items = new ArrayList<>();
while (true) {
......@@ -161,10 +198,12 @@ public class ProloGraalListenerImpl extends ProloGraalBaseListener {
for (int i = items.size() - 1; i >= 0; i--) {
list.addItem(items.get(i));
}
// sets the tail if one was found
if (tail != null) {
list.setTail(tail);
tail = null;
}
// and build the internal representation of the list
list.buildInteralRepresentation();
}
......
......@@ -5,6 +5,13 @@ import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
/**
* Class representing an error during the parsing process.
* Contains information about the location of the parsing error.
* @see ProloGraalListenerImpl
* @see ProloGraalParserImpl
* @author Martin Spoto
*/
public class ProloGraalParseError extends RuntimeException implements TruffleException {
public static final long serialVersionUID = 1L;
......
package ch.heiafr.prolograal.parser;
import ch.heiafr.prolograal.runtime.ProloGraalClause;
import com.oracle.truffle.api.source.Source;
import org.antlr.v4.runtime.BaseErrorListener;
......@@ -14,9 +15,20 @@ import org.antlr.v4.runtime.tree.ParseTreeWalker;
import ch.heiafr.prolograal.ProloGraalLanguage;
import ch.heiafr.prolograal.runtime.ProloGraalRuntime;
import java.util.List;
/**
* Class handling the parsing process using the "listener" method and the classes generated by ANTLR4.
* @see ProloGraalListenerImpl
* @see ProloGraalParseError
* @see ProloGraalRuntime
* @see ProloGraalLanguage
* @author Martin Spoto
*/
public class ProloGraalParserImpl {
static Source source;
// syntax error handler
private static final class BailoutErrorListener extends BaseErrorListener {
private final Source source;
......@@ -31,7 +43,11 @@ public class ProloGraalParserImpl {
}
}
static void throwParseError(Source source, int line, int charPositionInLine, Token token, String message) {
/**
* Convenience method used to format and throw a new parsing error.
* @throws ProloGraalParseError Exception containing details on the parsing error.
*/
static void throwParseError(Source source, int line, int charPositionInLine, Token token, String message) throws ProloGraalParseError {
int col = charPositionInLine + 1;
String location = "-- line " + line + " col " + col + ": ";
int length = token == null ? 1 : Math.max(token.getStopIndex() - token.getStartIndex(), 0);
......@@ -39,24 +55,33 @@ public class ProloGraalParserImpl {
String.format("Error(s) parsing script:%n" + location + message));
}
public static ProloGraalRuntime parseProloGraal(ProloGraalLanguage language, Source source) {
/**
* Parse the given source file using the listener method.
* @return The list of clauses in the source file, in the order in which they are encountered.
*/
public static List<ProloGraalClause> parseProloGraal(Source source) {
ProloGraalLexer lexer = new ProloGraalLexer(CharStreams.fromString(source.getCharacters().toString()));
ProloGraalParser parser = new ProloGraalParser(new CommonTokenStream(lexer));
lexer.removeErrorListeners();
parser.removeErrorListeners();
BailoutErrorListener errListener = new BailoutErrorListener(source);
lexer.addErrorListener(errListener);
parser.addErrorListener(errListener);
ProloGraalParserImpl.source = source;
ParseTree tree = parser.prolograal();
// create our listener implementation
ProloGraalListenerImpl listener = new ProloGraalListenerImpl();
// and walk through it using the default walker
ParseTreeWalker.DEFAULT.walk(listener, tree);
if (ProloGraalLanguage.DEBUG_MODE) {
listener.debug();
}
return new ProloGraalRuntime(listener.getClauses(), language.getContextReference().get());
return listener.getClauses();
}
}
\ No newline at end of file
......@@ -2,7 +2,12 @@ package ch.heiafr.prolograal.runtime;
import java.util.Map;
/**
* Class representing a Prolog atom.
* @author Martin Spoto
*/
public final class ProloGraalAtom extends ProloGraalTerm<ProloGraalAtom> {
// the name of the atom
private final String name;
public ProloGraalAtom(Map<ProloGraalVariable, ProloGraalVariable> variables, String name) {
......@@ -22,14 +27,22 @@ public final class ProloGraalAtom extends ProloGraalTerm<ProloGraalAtom> {
@Override
public boolean equals(Object obj) {
// two atoms are equal if they have the same name
return obj instanceof ProloGraalAtom && this.name.equals(((ProloGraalAtom) obj).name);
}
/**
* Unify this atom with the given term.<br>
* An atom is unifiable to another atom having the same name or to a variable, given that the variable is either
* currently unbound or bound to an unifiable atom.
* @return True if this atom is unifiable with the other term
*/
@Override
public boolean unify(ProloGraalTerm<?> other) {
if (this.equals(other)) {
return true;
} else if (other instanceof ProloGraalVariable) {
// if other is a variable we delegate the unification process to the variable
return other.unify(this);
}
return false;
......
......@@ -2,6 +2,12 @@ package ch.heiafr.prolograal.runtime;
import com.oracle.truffle.api.interop.TruffleObject;
/**
* Abstract class representing a boolean in Prolog : either a success or a fail.
* @see ProloGraalSuccess
* @see ProloGraalFailure
* @author Martin Spoto
*/
public abstract class ProloGraalBoolean implements TruffleObject {
public abstract boolean asBoolean();
}
\ No newline at end of file
package ch.heiafr.prolograal.runtime;
import ch.heiafr.prolograal.builtins.predicates.ProloGraalBuiltinClause;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Class representing a clause.
* @see ProloGraalBuiltinClause
* @author Martin Spoto
*/
public class ProloGraalClause {
// the head of this clause
private ProloGraalTerm<?> head;
// the goals of this clause
private List<ProloGraalTerm<?>> goals;
// the variables contained in this clause and its goals
// mapping Variable to Variable to be able to retrieve elements by their hash (a Set can only tell us if the hash
// is there or not, we cannot get the element)
private Map<ProloGraalVariable, ProloGraalVariable> variables;
......@@ -19,6 +29,7 @@ public class ProloGraalClause {
variables = new HashMap<>();
}
// copy constructor
public ProloGraalClause(ProloGraalClause other) {
variables = new HashMap<>();
this.head = other.head.copy(variables);
......
package ch.heiafr.prolograal.runtime;
import ch.heiafr.prolograal.ProloGraalLanguage;
import ch.heiafr.prolograal.nodes.ProloGraalEvalRootNode;
import ch.heiafr.prolograal.nodes.ProloGraalInterpreterNode;
import ch.heiafr.prolograal.nodes.ProloGraalResolverNode;
import com.oracle.truffle.api.TruffleLanguage;
......@@ -7,12 +9,23 @@ import com.oracle.truffle.api.TruffleLanguage;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Class holding the general context of the application, with the main nodes and runtime, as well as input/output. <br>
* The access to the instance is handled by the {@link ProloGraalLanguage#getContextReference()} method.
* @see ProloGraalEvalRootNode
* @see ProloGraalLanguage
* @author Martin Spoto
*/
public class ProloGraalContext {
// the main runtime containing the parsed clauses
private ProloGraalRuntime runtime;
// the interpreter node
private ProloGraalInterpreterNode interpreterNode;
// the resolver node
private ProloGraalResolverNode resolverNode;
// input and output streams that we get from the environment
private final InputStream input;
private final OutputStream output;
......
......@@ -2,6 +2,10 @@ package ch.heiafr.prolograal.runtime;
import java.util.Map;
/**
* Class representing a Prolog floating point number. Unification is handled in the super class.
* @author Martin Spoto
*/
public final class ProloGraalDoubleNumber extends ProloGraalNumber<ProloGraalDoubleNumber> {
private final double value;
......
package ch.heiafr.prolograal.runtime;
/**
* Class representing a failure during the resolution process.
*/
public class ProloGraalFailure extends ProloGraalBoolean {
@Override
public String toString() {
......
......@@ -2,6 +2,10 @@ package ch.heiafr.prolograal.runtime;
import java.util.Map;
/**
* Class representing a Prolog integer number. Unification is handled in the super class.
* @author Martin Spoto
*/
public final class ProloGraalIntegerNumber extends ProloGraalNumber<ProloGraalIntegerNumber> {
private final int value;
......
......@@ -6,8 +6,16 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Class representing a Prolog list, extending a structure to provide additional functionalities.<br>
* @implNote The current implementation is not very useful, but this class could be handy to provide performance
* enhancement for built-in list operations.
* @author Martin Spoto
*/
public class ProloGraalList extends ProloGraalStructure {
// attributes for the list are distinct from the internal structure
// to allow per use selection of the best internal data representation
private List<ProloGraalTerm<?>> items;
private ProloGraalTerm<?> tail = ProloGraalBuiltinAtoms.EMPTY_LIST;
......@@ -24,12 +32,18 @@ public class ProloGraalList extends ProloGraalStructure {
return tail;
}
public static ProloGraalList fromInternal(ProloGraalStructure internal) {
/**
* Creates a new list from the given structure, assuming it has the correct format.
* @param internal The structure that serves as a base for a new list
* @throws IllegalArgumentException if the given structure cannot be converted to a list
* @return The new list with the elements of the given structure.
*/
public static ProloGraalList fromInternal(ProloGraalStructure internal) throws IllegalArgumentException {
ProloGraalList r = new ProloGraalList(null);
while (true) {
if (!internal.getFunctor().equals(ProloGraalBuiltinAtoms.DOT_OPERATOR) || internal.getArity() != 2) {
throw new RuntimeException("Cannot build list from internal representation");
throw new IllegalArgumentException("Cannot build list from internal representation");
}
ProloGraalTerm<?> head = internal.getArguments().get(0).getRootValue();
ProloGraalTerm<?> tail = internal.getArguments().get(1).getRootValue();
......@@ -61,19 +75,30 @@ public class ProloGraalList extends ProloGraalStructure {
return items.size();
}
/**
* Creates the internal representation of this list, by populating the parent structure.