{"id":15031012,"url":"https://github.com/twolodzko/prolog-rs","last_synced_at":"2025-04-09T22:41:22.336Z","repository":{"id":255689180,"uuid":"853404256","full_name":"twolodzko/prolog-rs","owner":"twolodzko","description":"Minimal Prolog implemented in Rust","archived":false,"fork":false,"pushed_at":"2024-09-24T13:15:37.000Z","size":112,"stargazers_count":43,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-24T00:38:16.708Z","etag":null,"topics":["prolog","prolog-implementation","prolog-interpreter","rust","rust-lang"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/twolodzko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-06T15:33:15.000Z","updated_at":"2024-12-07T19:56:36.000Z","dependencies_parsed_at":"2025-02-21T07:31:14.066Z","dependency_job_id":null,"html_url":"https://github.com/twolodzko/prolog-rs","commit_stats":null,"previous_names":["twolodzko/prolog-rs"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twolodzko%2Fprolog-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twolodzko%2Fprolog-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twolodzko%2Fprolog-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twolodzko%2Fprolog-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/twolodzko","download_url":"https://codeload.github.com/twolodzko/prolog-rs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248124848,"owners_count":21051757,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["prolog","prolog-implementation","prolog-interpreter","rust","rust-lang"],"created_at":"2024-09-24T20:14:43.757Z","updated_at":"2025-04-09T22:41:22.320Z","avatar_url":"https://github.com/twolodzko.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# Prolog.rs\n\nThis is a minimal Prolog interpreter implemented in Rust.\nThe implementation covers only a subset of Prolog features,\nis not intended to be fast, or optimal in any sense.\nIt is a learning project that helped me to understand Prolog better.\n\nThe implementation is tested using 350+ unit tests, including\nrunning some code solutions for the [\"99 Prolog problems\"],\nand unit tests checking for ISO Prolog consistency. There are\nsome differences from other Prolog implementations though,\nas described [below](#limitations-and-differences-from-other-implementations).\n\nIf you are looking for a mature Prolog implementation in Rust, \ncheck rather the [Scryer](https://www.scryer.pl/) Prolog.\n\n## Usage\n\nTo run the Prolog interpreter you can use the [Justfile] commands:\n\n```shell\n$ just test      # runs tests\n$ just build     # builds the standalone binary ./prolog\n$ just run FILE  # evaluate the FILE and start REPL\n$ just repl      # start REPL\n```\n\nTo install it, move the `prolog` binary together with the `lib/` directory to some\ndirectory of your choice.\n\nCalling `./prolog -e FILE` from the command line would evaluate the *FILE*, run\nthe `main/0` goal (if available), and exit.\n\n## Data types\n\nThe units of data in Prolog are called *terms*.\n\n* Atoms are name-only datatypes, for example, `foo`.\n  Their names need to start with a lowercase letter.\n* Integers are the only supported numerical type.\n* Variables have no specific value, but they can be initialized\n  with a value during unification (see below). Their names\n  need to start with uppercase letters or `_`.\n* There exist also compound terms:\n  * Structures like `foo(a, b)` have name `foo` and\n    arguments (`c` and `b`).\n  * Lists like `[1, 2, 3]` can contain multiple terms.\n\nIn Prolog everything is a struct, so atom `foo` is the same as `foo()`, operation like\n`2 + 2` is `+(2, 2)`, the \"and\" operator in `a , b` is `,(a, b)`\n(don't be confused with the comma separating the arguments), etc.\n\nLists in Prolog are also structs, so `[1, 2, 3]` is represented as `.(1, .(2, .(3, [])))`, where\n`[]` stands for an empty list. Prolog allows you to create dotted pairs (in [lisp] terms), for example\n`[1 | 2]` is represented as `.(1, 2)`.\n\nThere are no booleans. Terms are evaluated by unification. The term `fail` always fails the unification.\n\n```prolog\n?- 1=1.\nyes\n?- \\+ 1=1.\nno\n?- fail.\nno\n?- \\+ fail.\nyes\n```\n\n## Facts, rules, and questions\n\nProlog programs are defined in terms of three kinds of expressions:\n\n* Facts, like `foo.` or `bar(a,b,32).` state what is \"true\".\n* Rules, like `mortal(Who) :- person(Who).` define logical implications.\n* Questions, like `?- mortal(socrates).` validate if the question is true.\n\nA very simple Prolog program may solve the classical logical question:\n\n```prolog\n% fact\nperson(socrates).\n\n% rule\nmortal(Who) :-\n    person(Who).\n\n% question\n?- mortal(socrates).\n```\n\n## Unification\n\nProlog extensively uses pattern matching. When you ask a *question*, it searches its\ndatabase if any of the recorded *facts* and *rules* that match the goals in the question.\nThis is nicely explained in the *[Adventure in Prolog]* book by Dennis Merritt:\n\n\u003e Prolog's pattern matching is called **unification**. In the case where the logicbase\n\u003e contains only facts, unification succeeds if the following three conditions hold.\n\u003e\n\u003e * The predicate named in the goal and logicbase are the same.\n\u003e * Both predicates have the same arity.\n\u003e * All of the arguments are the same.\n\nWhen variable is unified with the value, the variable becomes equivalent to the value.\nIf two free variables are unified, they become each other's aliases.\n\nThere are also procedures using special evaluation rules instead of unification, for example:\n\n```prolog\n?- X is 1+2.\nX = 3\n?- 1+2 \u003c 5.\nyes\n?- writeln('hello, world!').\nhello, world!\nyes\n```\n\n## Features\n\nBy design, this interpreter covers only a subset of Prolog's features. Those include:\n\n* `fail` is a goal that always fails.\n* `a , b` means that we want to satisfy both *a* and *b*, while `a ; b` means *a* or *b*.\n* `\\+` can be used to negate a goal.\n* `!` is the [cut operator]. It prevents backtracking for the goals preceding it.\n* `-\u003e` is the if-else operator, `Cond -\u003e Then` tries to satisfy `Cond`, if it succeeds,\n  then attempts to satisfy `Then`, otherwise it fails. Underneath, it is a syntactic\n  sugar for expressing `Cond, !, Then`.\n* `=` is a unification operator, it is equivalent to `=(A, A)`.\n* `_` is a wildcard variable that unifies with anything but never holds any value.\n* `is` operator, as in `X is 2 + 2`, evaluates the right-hand-side and if left-hand-side is\n  a free variable, assigns the result to it, otherwise compares the result to it's value\n  (so `4 is 2 + 2` evaluates to \"yes\").\n* The supported mathematical operators and functions are:\n  * unary operators `+`, `-`,\n  * binary operators `+`, `-`, `*`, `/`, `//` (last two are synonyms), and `rem`,\n  * `div` and `mod` (using [`i32::div_euclid`][i32] and [`i32::rem_euclid`][i32]),\n  * `abs` and `sign` functions.\n\n  Those operators can be used together with procedures with special evaluation rules\n  like `is`, `=:=`, `\u003c`, etc. Outside of those procedures, they will create structs,\n  for example `2 + 3` would become `+(2,3)`.\n* `=:=`, `\u003c` are the numerical comparison operators that evaluate both sides\n  and compare them, e.g. `2 + 1 \u003c 7 - 2`.\n  The operators `=\\=`, `=\u003c`, `\u003e`, `\u003e=` are available through `lib/stdlib.pl`.\n* `==`, `@\u003c` are comparison operators checking the [standard order of terms]\n  (see [below](#limitations-and-differences-from-other-implementations)).\n  The operators `\\==`, `@=\u003c`, `@\u003e`, `@\u003e=` are available through `lib/stdlib.pl`.\n* `consult('path/to/file.pl')` loads and evaluates the file. If the file contains a question (`?-`)\n  which cannot be satisfied, it will fail with an error. It takes as an argument an atom with\n  path to the file, or a list of such atoms.\n* `write(X)` prints *X* and `nl` prints a newline.\n* The `trace` and `untrace` commands can be used to turn the tracing logging on and off.\n* `{a, b, c}` is a syntactic sugar for writing `{}(,(a, ,(b, c)))`. It has no special meaning.\n* `atom(X)`, `integer(X)`, `number(X)` are type checkers. `var(X)` checks if *X* is a free variable.\n\nMore functionalities are implemented in the standard library available through `lib/stdlib.pl`.\n\n## Limitations and differences from other implementations\n\n* Only a subset of Prolog's functionalities are implemented. Features such as strings or floats types, DCG's, defining\n  custom operators, etc are not available.\n* The precedence of `;` and `,` operators is reversed, so `a , b ; c, d` is parsed the same as `a , (b ; c) , d`.\n  Use brackets to assure the correct precedence.\n* In quoted atom names only a subset of escape characters are allowed, including: `\\n`, `\\t`, `\\s`, `\\\\`, `\\'`. `\\\"`,\n  or `\\NEWLINE`.\n* Under the [standard order of terms] variables should be sorted by their memory addresses. Since in this\n  implementation variables don't get memory addresses until they are initialized, such ordering is not possible.\n* In Prolog `,/2` is an operator such that `a , b` tries to satisfy `a` and `b`. In this implementation instead\n  of using linked lists, structs are based on Rust `Vec`'s of any size, so `a , b , c` would become `,(a, b, c)`\n  rather than `,(a, ,(b, c))`.\n* Arithmetic `div` and `mod` use [Rust's `i32::div_euclid` and `i32::rem_euclid`][i32] which are defined\n  [differently to Prolog][swipl-div], but meet the requirement of being consistent with each other.\n* Since `_` does not bind, the query like `?- L = [_, _], L = [1, _], L = [2, _]` would give a logically inconsistent\n  answer \"yes\". The same query would work correctly in SWI Prolog, but not in Tau prolog\n  (e.g. [prolog.run](https://prolog.run/)).\n* The implementation is not tail-call optimized, so can easily overflow when satisfying complex goals.\n* Tracing is simplified and limited as compared to the other implementations.\n\n\n[antlr]: https://github.com/antlr/grammars-v4/blob/master/prolog/prolog.g4\n[Adventure in Prolog]: https://www.amzi.com/AdventureInProlog/index.php\n[standard order of terms]: https://www.swi-prolog.org/pldoc/man?section=standardorder\n[cut operator]: https://pages.cs.wisc.edu/~fischer/cs538.s02/prolog/A13CUT.HTM\n[i32]: https://doc.rust-lang.org/std/primitive.i32.html\n[lisp]: https://web.mit.edu/scheme_v9.2/doc/mit-scheme-ref/Lists.html#Lists\n[\"99 Prolog problems\"]: https://www.ic.unicamp.br/~meidanis/courses/mc336/2009s2/prolog/problemas/\n[swipl-div]: https://www.swi-prolog.org/pldoc/man?function=div%2f2\n[Justfile]: https://github.com/casey/just\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwolodzko%2Fprolog-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftwolodzko%2Fprolog-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwolodzko%2Fprolog-rs/lists"}