HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Logic Prolog compiler

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
compilerprologlogic

Problem

I'm actually doing my own Prolog compiler just like SWISH one with Java. Prolog is a logic language that is particularly suited to programs that involve symbolic or non-numeric computation. Prolog consists of a series of rules and facts. A program is run by presenting some query and seeing if this can be proved against these known rules and facts.

It works with resolving, thanks to the Horn's clauses method, the last line which is called the goal with the first one which have the same litteral.

mag(paris,mag_a).
mag(paris,mag_b).
mag(lyon,mag_c).
mag(lyon,mag_d).
listeM(M):-mag(V,M),write(M),write( located_in ),write(V),nl.
listeM(X).


would give the goal listeM(X).:

mag_a located_in paris


Another example with the constant predicate fail

mag(paris,mag_a).
mag(paris,mag_b).
mag(lyon,mag_c).
mag(lyon,mag_d).
listeM(M):-mag(V,M),write(M),write( located_in ),write(V),nl,fail.
listeM(M):-write(end_list).
listeM(X).


would give the goal listeM(X).:

mag_a located_in paris
   mag_b located_in paris
   mag_c located_in lyon
   mag_d located_in lyon
   fin liste
   true.


Another example with some arithmetical operations managed by the constant predicate plus and multi

multi(X,1,X).
multi(X,Y,R):-plus(Y1,1,Y),multi(X,Y1,R1),plus(R1,X,R).
multi(2,3,R),write(R).


would give with the goal multi(2,3,R),write(R)., that is to say searching to multiply 2 and 3

6


Or even with the factorial calculation

fact(1,1).
fact(N,R):-plus(N1,1,N),fact(N1,R1),multi(N,R1,R).
multi(X,1,X).
multi(X,Y,R):-plus(Y1,1,Y),multi(X,Y1,R1),plus(R1,X,R).
go(N):-fact(N,R),write(N),write( has_the_following_factorial ),write(R).
go(6).


It would give with the goal go(6).

6 has_the_following_factorial 720
   true


In our program we load the file in the variable static String fichier ="yourFile.pl"; which has to be at the root of your workspace.

Our program actually works, but if you have any idea to make it simpler, I would b

Solution

Parsing is always a bit complicated/ugly in terms of code—a library like ANTLR can do some of the heavy lifting for you. But let's assume some level of reinventing-the-wheel for practice/exercise.

Recommendations

What follows are fairly broad recommendations based on the question: "If I were responsible for maintaining this code, what would I do?"

-
Use lists (expandable) instead of arrays (fixed-length). A number of arrays have sizes that appear arbitrary, such as Clauses[120] (later redefined to length 100). Unless these lengths represent agreed-upon limits, in which case it would be best to name them through constants (e.g. MAX_CLAUSES), consider using an ArrayList instead. ArrayList has comparable performance to arrays, will grow as needed, and will relieve you of having to store or guess the number of elements.

-
Add documentation or help code self-document. There are useful comments in the code, but not enough to get a view on what happens where, when, and why. They probably would be enough if the functions and variables could be renamed to signal use or purpose ( ICF? ri(int noClause)? ).

-
Reduce the number of static, reachable, mutable references. The abundance of statics greatly widens the conceptual scope of named references, making it harder for human beings to read and reason about the code. Reduce this scope by making them instance references—this will also reduce the number of arrays you need—and by restricting access through private where possible.

This will be tedious, make no mistake. It will require you to rethink and remodel some parts. But it will greatly improve the ability for people (including yourself!) to reason about the impact of a change to the code.

-
Consider a Map symbols instead of List listeAff and List listeSymbole. Maps tend to be very fast in lookups, so it will save you processing time. It will also free you from keeping the two lists in sync.

-
Add isInt() to specific classes where needed. All calls to isInt(String) appear to follow the pattern of isInt(Symbole.toString()). Consider adding an instance method isInt() to class Symbole, so that you can change isInt(x.toString()) into x.isInt().

-
Aim for 1 statement per line, and prefer using braces for code blocks. This is not an unbreakable rule: there are cases where 'packing' statements or blocks is defensible for visual or conceptual reasons. But I'd err on the side of caution.

-
Prefer generics over raw collections. Generics help keep your collections type-safe (reducing bugs) and reduce the number of casts you need.

-
Prefer logging over System.out.print. Java comes with a logging utility (java.util.logging); you can also use one like log4j or slf4j-logback. If this feels like overkill, consider moving if (...) System.out.println(...) to methods like debug(String) to reduce clutter.

-
Consider switch-case instead of if-else chains comparing to constants. ri(int) is an example where switch-case would probably both be faster and clearer.

-
Scrutinize extends LinkedList. You'll probably need only two or three of its methods, so you might get away with adding the methods explicitly, and then delegating to a private field. If these classes do need to be list-like, consider adding the List interface instead, and then delegate, or add a method List asList() that provides a view of what you need.

Context

StackExchange Code Review Q#127323, answer score: 2

Revisions (0)

No revisions yet.