prolog

package module
v0.15.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 11, 2023 License: MIT Imports: 10 Imported by: 32

README

prolog - the only reasonable scripting engine for Go

Go Reference Actions Status Go Report Card codecov Mentioned in Awesome Go

What is this?

ichiban/prolog is an embeddable ISO Prolog interpreter in Go.

Comparison with Other Libraries

prolog otto go-lua
Language ISO Prolog ECMA Script Lua
Paradigm 🎓 Logic Object-oriented Object-oriented
Go API 😻 database/sql-like original original
Declarative
Sandboxing

Getting started

Install latest version
go get -u github.com/ichiban/prolog
Usage
Instantiate an interpreter
p := prolog.New(os.Stdin, os.Stdout) // Or `prolog.New(nil, nil)` if you don't need user_input/user_output.

Or, if you want a sandbox interpreter without any built-in predicates:

// See examples/sandboxing/main.go for details.
p := new(prolog.Interpreter)
Load a Prolog program
if err := p.Exec(`
	human(socrates).       % This is a fact.
	mortal(X) :- human(X). % This is a rule.
`); err != nil {
	panic(err)
}

Similar to database/sql, you can use placeholder ? to insert Go data as Prolog data.

if err := p.Exec(`human(?).`, "socrates"); err != nil { // Same as p.Exec(`human(socrates).`)
	panic(err)
}
Run the Prolog program
sols, err := p.Query(`mortal(?).`, "socrates") // Same as p.Query(`mortal(socrates).`)
if err != nil {
	panic(err)
}
defer sols.Close()

// Iterates over solutions.
for sols.Next() {
	fmt.Printf("Yes.\n") // ==> Yes.
}

// Check if an error occurred while querying.
if err := sols.Err(); err != nil {
	panic(err)
}

Or, if you want to query for the variable values for each solution:

sols, err := p.Query(`mortal(Who).`)
if err != nil {
	panic(err)
}
defer sols.Close()

// Iterates over solutions.
for sols.Next() {
	// Prepare a struct with fields which name corresponds with a variable in the query.
	var s struct {
		Who string
	}
	if err := sols.Scan(&s); err != nil {
		panic(err)
	}
	fmt.Printf("Who = %s\n", s.Who) // ==> Who = socrates
}

// Check if an error occurred while querying.
if err := sols.Err(); err != nil {
	panic(err)
}

The Default Language

ichiban/prolog adheres the ISO standard and comes with the ISO predicates as well as the Prologue for Prolog and DCG predicates.

See the Wiki for the directives and the built-in predicates.

Top Level

1pl is an experimental top level command for testing the default language and its compliance to the ISO standard.

You can install it with go install:

go install github.com/ichiban/prolog/cmd/1pl@latest

Then, you can enter the top level with 1pl:

$(go env GOPATH)/bin/1pl [<file>...]

Extensions

  • predicates: Native predicates for ichiban/prolog.

License

Distributed under the MIT license. See LICENSE for more information.

Contributing

See ARCHITECTURE.md for architecture details.

  1. Fork it (https://github.com/ichiban/prolog/fork)
  2. Create your feature branch (git checkout -b feature/fooBar)
  3. Commit your changes (git commit -am 'Add some fooBar')
  4. Push to the branch (git push origin feature/fooBar)
  5. Create a new Pull Request

Acknowledgments

We would like to extend our thanks to the following individuals for their contributions to this project:

We are grateful for the support and contributions of everyone involved in this project. Arigatou gozaimasu!

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrClosed = errors.New("closed")

ErrClosed indicates the Solutions are already closed and unable to perform the operation.

View Source
var ErrNoSolutions = errors.New("no solutions")

ErrNoSolutions indicates there's no solutions for the query.

Functions

This section is empty.

Types

type Interpreter added in v0.2.0

type Interpreter struct {
	engine.VM
	// contains filtered or unexported fields
}

Interpreter is a Prolog interpreter. The zero value is a valid interpreter without any predicates/operators defined.

func New added in v0.2.0

func New(in io.Reader, out io.Writer) *Interpreter

New creates a new Prolog interpreter with predefined predicates/operators.

Example (AcyclicTerm)
p := New(nil, nil)

sols, _ := p.Query(`acyclic_term(a(1, _)).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`X = f(X), acyclic_term(X).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()
Output:

true
false
Example (Arg)
p := New(nil, nil)

sols, _ := p.Query(`arg(1, foo(a, b), a).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`arg(1, foo(a, b), X).`)
for sols.Next() {
	var s struct {
		X string
	}
	_ = sols.Scan(&s)
	fmt.Printf("X = %s\n", s.X)
}
_ = sols.Close()

sols, _ = p.Query(`arg(1, foo(X, b), a).`)
for sols.Next() {
	var s struct {
		X string
	}
	_ = sols.Scan(&s)
	fmt.Printf("X = %s\n", s.X)
}
_ = sols.Close()

sols, _ = p.Query(`arg(1, foo(X, b), Y), X = a.`)
for sols.Next() {
	var s struct {
		X, Y string
	}
	_ = sols.Scan(&s)
	fmt.Printf("X = %s, Y = %s\n", s.X, s.Y)
}
_ = sols.Close()

sols, _ = p.Query(`arg(1, foo(a, b), b).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`arg(0, foo(a, b), foo).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`arg(3, foo(a, b), N).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`arg(X, foo(a, b), a).`)
sols.Next()
fmt.Printf("%v\n", sols.Err())
_ = sols.Close()

sols, _ = p.Query(`arg(1, X, a).`)
sols.Next()
fmt.Printf("%v\n", sols.Err())
_ = sols.Close()

sols, _ = p.Query(`arg(0, atom, A).`)
sols.Next()
fmt.Printf("%v\n", sols.Err())
_ = sols.Close()

sols, _ = p.Query(`arg(0, 3, A).`)
sols.Next()
fmt.Printf("%v\n", sols.Err())
_ = sols.Close()
Output:

true
X = a
X = a
X = a, Y = a
false
false
false
error(instantiation_error,arg/3)
error(instantiation_error,arg/3)
error(type_error(compound,atom),arg/3)
error(type_error(compound,3),arg/3)
Example (Callable)
p := New(nil, nil)

sols, _ := p.Query(`callable(a).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`callable(3).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`callable(X).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`callable((1,2)).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()
Output:

true
false
false
true
Example (Ground)
p := New(nil, nil)

sols, _ := p.Query(`ground(3).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`ground(a(1, _)).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()
Output:

true
false
Example (Phrase)
p := New(nil, nil)
_ = p.Exec(`
determiner --> [the].
determiner --> [a].

noun --> [boy].
noun --> [girl].

verb --> [likes].
verb --> [scares].

noun_phrase --> determiner, noun.
noun_phrase --> noun.

verb_phrase --> verb.
verb_phrase --> verb, noun_phrase.

sentence --> noun_phrase, verb_phrase.
`)

sols, _ := p.Query(`phrase([the], [the]).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`phrase(sentence, [the, girl, likes, the, boy]).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`phrase(sentence, [the, girl, likes, the, boy, today]).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`phrase(sentence, [the, girl, likes]).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`phrase(sentence, Sentence).`)
for sols.Next() {
	var s struct {
		Sentence []string
	}
	_ = sols.Scan(&s)
	fmt.Printf("Sentence = %s\n", s.Sentence)
	break // Many other sentences follow.
}
_ = sols.Close()

sols, _ = p.Query(`phrase(noun_phrase, [the, girl, scares, the, boy], Rest).`)
for sols.Next() {
	var s struct {
		Rest []string
	}
	_ = sols.Scan(&s)
	fmt.Printf("Rest = %s\n", s.Rest)
}
_ = sols.Close()
Output:

true
true
false
true
Sentence = [the boy likes]
Rest = [scares the boy]
Example (Sort)
p := New(nil, nil)

sols, _ := p.Query(`sort([1, 1], Sorted).`)
for sols.Next() {
	var s struct {
		Sorted []int
	}
	_ = sols.Scan(&s)
	fmt.Printf("Sorted = %d\n", s.Sorted)
}
_ = sols.Close()

sols, _ = p.Query(`sort([X, 1], [1, 1]).`)
for sols.Next() {
	var s struct {
		X int
	}
	_ = sols.Scan(&s)
	fmt.Printf("X = %d\n", s.X)
}
_ = sols.Close()

sols, _ = p.Query(`sort([1, 1], [1, 1]).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`sort([V], V).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`sort([f(U),U,U,f(V),f(U),V],L).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()
Output:

Sorted = [1]
X = 1
false
true
true
Example (Subsumes_term)
p := New(nil, nil)

sols, _ := p.Query(`subsumes_term(a, a).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`subsumes_term(f(X,Y), f(Z,Z)).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`subsumes_term(f(Z,Z), f(X,Y)).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`subsumes_term(g(X), g(f(X))).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`subsumes_term(X, f(X)).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()

sols, _ = p.Query(`subsumes_term(X, Y), subsumes_term(Y, f(X)).`)
fmt.Printf("%t\n", sols.Next())
_ = sols.Close()
Output:

true
true
false
false
false
true

func (*Interpreter) Exec added in v0.2.0

func (i *Interpreter) Exec(query string, args ...interface{}) error

Exec executes a prolog program.

Example (Placeholders)
p := New(nil, os.Stdout)

_ = p.Exec(`my_atom(?).`, "foo")
sols, _ := p.Query(`my_atom(A), atom(A), write(A), nl.`)
sols.Next()
_ = sols.Close()

_ = p.Exec(`my_int(?, ?, ?, ?, ?).`, int8(1), int16(1), int32(1), int64(1), 1)
sols, _ = p.Query(`my_int(I, I, I, I, I), integer(I), write(I), nl.`)
sols.Next()
_ = sols.Close()

_ = p.Exec(`my_float(?, ?).`, float32(1), float64(1))
sols, _ = p.Query(`my_float(F, F), float(F), write(F), nl.`)
sols.Next()
_ = sols.Close()

_ = p.Exec(`my_atom_list(?).`, []string{"foo", "bar", "baz"})
sols, _ = p.Query(`my_atom_list(As), maplist(atom, As), write(As), nl.`)
sols.Next()
_ = sols.Close()

_ = p.Exec(`my_int_list(?).`, []int{1, 2, 3})
sols, _ = p.Query(`my_int_list(Is), maplist(integer, Is), write(Is), nl.`)
sols.Next()
_ = sols.Close()

_ = p.Exec(`my_float_list(?).`, []float64{1, 2, 3})
sols, _ = p.Query(`my_float_list(Fs), maplist(float, Fs), write(Fs), nl.`)
sols.Next()
_ = sols.Close()
Output:

foo
1
1.0
[foo,bar,baz]
[1,2,3]
[1.0,2.0,3.0]

func (*Interpreter) ExecContext added in v0.3.0

func (i *Interpreter) ExecContext(ctx context.Context, query string, args ...interface{}) error

ExecContext executes a prolog program with context.

func (*Interpreter) Query added in v0.2.0

func (i *Interpreter) Query(query string, args ...interface{}) (*Solutions, error)

Query executes a prolog query and returns *Solutions.

Example (Placeholders)
p := New(nil, os.Stdout)
sols, _ := p.Query(`A = ?, atom(A), write(A), nl.`, "foo")
sols.Next()
_ = sols.Close()
sols, _ = p.Query(`(I, I, I, I, I) = (?, ?, ?, ?, ?), integer(I), write(I), nl.`, int8(1), int16(1), int32(1), int64(1), 1)
sols.Next()
_ = sols.Close()
sols, _ = p.Query(`(F, F) = (?, ?), float(F), write(F), nl.`, float32(1), float64(1))
sols.Next()
_ = sols.Close()
sols, _ = p.Query(`L = ?, maplist(atom, L), write(L), nl.`, []string{"foo", "bar", "baz"})
sols.Next()
_ = sols.Close()
sols, _ = p.Query(`L = ?, maplist(integer, L), write(L), nl.`, []int{1, 2, 3})
sols.Next()
_ = sols.Close()
sols, _ = p.Query(`L = ?, maplist(float, L), write(L), nl.`, []float64{1, 2, 3})
sols.Next()
_ = sols.Close()
Output:

foo
1
1.0
[foo,bar,baz]
[1,2,3]
[1.0,2.0,3.0]

func (*Interpreter) QueryContext added in v0.3.0

func (i *Interpreter) QueryContext(ctx context.Context, query string, args ...interface{}) (*Solutions, error)

QueryContext executes a prolog query and returns *Solutions with context.

func (*Interpreter) QuerySolution added in v0.6.0

func (i *Interpreter) QuerySolution(query string, args ...interface{}) *Solution

QuerySolution executes a Prolog query for the first solution.

func (*Interpreter) QuerySolutionContext added in v0.6.0

func (i *Interpreter) QuerySolutionContext(ctx context.Context, query string, args ...interface{}) *Solution

QuerySolutionContext executes a Prolog query with context.

type Scanner added in v0.15.0

type Scanner interface {
	Scan(vm *engine.VM, term engine.Term, env *engine.Env) error
}

Scanner is an interface for custom conversion from term to Go value.

type Solution added in v0.6.0

type Solution struct {
	// contains filtered or unexported fields
}

Solution is the single result of a query.

func (*Solution) Err added in v0.6.0

func (s *Solution) Err() error

Err returns an error that occurred while querying for the Solution, if any.

func (*Solution) Scan added in v0.6.0

func (s *Solution) Scan(dest interface{}) error

Scan copies the variable values of the solution into the specified struct/map.

type Solutions added in v0.2.0

type Solutions struct {
	// contains filtered or unexported fields
}

Solutions is the result of a query. Everytime the Next method is called, it searches for the next solution. By calling the Scan method, you can retrieve the content of the solution.

func (*Solutions) Close added in v0.2.0

func (s *Solutions) Close() error

Close closes the Solutions and terminates the search for other solutions.

func (*Solutions) Err added in v0.2.0

func (s *Solutions) Err() error

Err returns the error if exists.

func (*Solutions) Next added in v0.2.0

func (s *Solutions) Next() bool

Next prepares the next solution for reading with the Scan method. It returns true if it finds another solution, or false if there's no further solutions or if there's an error.

func (*Solutions) Scan added in v0.2.0

func (s *Solutions) Scan(dest interface{}) error

Scan copies the variable values of the current solution into the specified struct/map.

Example
p := New(nil, nil)
sols, _ := p.Query(`A = foo, I = 42, F = 3.14.`)
for sols.Next() {
	var s struct {
		A string
		I int
		F float64
	}
	_ = sols.Scan(&s)
	fmt.Printf("A = %s\n", s.A)
	fmt.Printf("I = %d\n", s.I)
	fmt.Printf("F = %.2f\n", s.F)
}
Output:

A = foo
I = 42
F = 3.14
Example (List)
p := New(nil, nil)
sols, _ := p.Query(`Atoms = [foo, bar], Integers = [1, 2], Floats = [1.1, 2.1], Mixed = [foo, 1, 1.1].`)
for sols.Next() {
	var s struct {
		Atoms    []string
		Integers []int64
		Floats   []float64
		Mixed    []interface{}
	}
	_ = sols.Scan(&s)

	fmt.Printf("Atoms = %s\n", s.Atoms)
	fmt.Printf("Integers = %d\n", s.Integers)
	fmt.Printf("Floats = %.1f\n", s.Floats)
	fmt.Printf("Mixed = %v\n", s.Mixed)
}
Output:

Atoms = [foo bar]
Integers = [1 2]
Floats = [1.1 2.1]
Mixed = [foo 1 1.1]
Example (Tag)
p := New(nil, nil)
sols, _ := p.Query(`A = foo, I = 42, F = 3.14.`)
for sols.Next() {
	var s struct {
		Atom    string  `prolog:"A"`
		Integer int     `prolog:"I"`
		Float   float64 `prolog:"F"`
	}
	_ = sols.Scan(&s)
	fmt.Printf("Atom = %s\n", s.Atom)
	fmt.Printf("Integer = %d\n", s.Integer)
	fmt.Printf("Float = %.2f\n", s.Float)
}
Output:

Atom = foo
Integer = 42
Float = 3.14

type TermString added in v0.15.0

type TermString string

TermString is a string representation of term.

func (*TermString) Scan added in v0.15.0

func (t *TermString) Scan(vm *engine.VM, term engine.Term, env *engine.Env) error

Scan implements Scanner interface.

Directories

Path Synopsis
cmd
1pl
examples
dcg

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL