Skip to content

Commit

Permalink
add: atomic_list_concat/3
Browse files Browse the repository at this point in the history
also remove a leftover debugging log from is_list
  • Loading branch information
guregu committed Apr 8, 2022
1 parent 25875f9 commit 1882687
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ These use strings (lists of characters) for filenames.
### Lists

- `is_list/1`
- `atomic_list_concat/3`

### Package [`taujson`](https://godoc.org/github.com/guregu/predicates/taujson)

Expand Down
62 changes: 60 additions & 2 deletions list.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package predicates

import (
"log"
"context"
"strings"

"github.com/ichiban/prolog/engine"
)
Expand All @@ -23,11 +24,68 @@ func IsList(t engine.Term, k func(*engine.Env) *engine.Promise, env *engine.Env)
for iter.Next() {
}
if iter.Err() != nil {
log.Println("ITER ERR", iter.Err())
return engine.Bool(false)
}
return k(env)
default:
return engine.Bool(false)
}
}

// AtomicListConcat (atomic_list_concat/3) succeeds if atom represents the members of list joined by seperator.
// This can be used to join strings by passing a ground list, or used to split strings by passing a ground atom.
//
// atomic_list_concat(+List, +Seperator, -Atom).
// atomic_list_concat(-List, +Seperator, +Atom).
func AtomicListConcat(list, seperator, atom engine.Term, k func(*engine.Env) *engine.Promise, env *engine.Env) *engine.Promise {
sep, ok := seperator.(engine.Atom)
if !ok {
return engine.Error(engine.TypeErrorAtom(seperator))
}

switch list := env.Resolve(list).(type) {
case engine.Variable:
str, ok := env.Resolve(atom).(engine.Atom)
if !ok {
return engine.Error(engine.ErrInstantiation)
}
split := strings.Split(string(str), string(sep))
atoms := make([]engine.Term, len(split))
for i := 0; i < len(split); i++ {
atoms[i] = engine.Atom(split[i])
}
return engine.Delay(func(context.Context) *engine.Promise {
return engine.Unify(list, engine.List(atoms...), k, env)
})
case *engine.Compound:
if list.Functor != "." || len(list.Args) != 2 {
return engine.Error(engine.TypeErrorList(list))
}
var sb strings.Builder
iter := engine.ListIterator{List: list, Env: env}
for i := 0; iter.Next(); i++ {
cur := env.Resolve(iter.Current())
a, ok := cur.(engine.Atom)
if !ok {
return engine.Error(engine.TypeErrorAtom(a))
}
if i > 0 {
sb.WriteString(string(sep))
}
sb.WriteString(string(a))
}
str := sb.String()
return engine.Delay(func(context.Context) *engine.Promise {
return engine.Unify(atom, engine.Atom(str), k, env)
})
case engine.Atom:
if list != "[]" {
return engine.Error(engine.TypeErrorList(list))
}
return engine.Delay(func(context.Context) *engine.Promise {
return engine.Unify(atom, engine.Atom(""), k, env)
})
default:
return engine.Error(engine.TypeErrorList(list))
}
}
45 changes: 45 additions & 0 deletions list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/guregu/predicates/internal"
"github.com/ichiban/prolog/engine"
)

func TestIsList(t *testing.T) {
Expand Down Expand Up @@ -32,3 +33,47 @@ func TestIsList(t *testing.T) {
t.Run("term is variable", p.Expect(internal.TestFail,
`is_list(X), OK = true.`))
}

func TestAtomicListConcat(t *testing.T) {
p := internal.NewTestProlog()
p.Register3("atomic_list_concat", AtomicListConcat)

t.Run("list is ground", func(t *testing.T) {
t.Run("atom is variable", p.Expect([]map[string]engine.Term{
{"X": engine.Atom("a-b")},
}, `atomic_list_concat([a, b], '-', X).`))

t.Run("atom is variable and seperator is empty", p.Expect([]map[string]engine.Term{
{"X": engine.Atom("ab")},
}, `atomic_list_concat([a, b], '', X).`))

t.Run("atom is ground", p.Expect(internal.TestOK,
`atomic_list_concat([a, b], '/', 'a/b'), OK = true.`))

t.Run("list is empty and atom is variable", p.Expect([]map[string]engine.Term{
{"X": engine.Atom("")},
}, `atomic_list_concat([], '-', X).`))

t.Run("list is empty and atom is ground", p.Expect(internal.TestOK,
`atomic_list_concat([], '/', ''), OK = true.`))
})

t.Run("atom is ground", func(t *testing.T) {
t.Run("list is variable", p.Expect([]map[string]engine.Term{
{"X": engine.List(engine.Atom("a"), engine.Atom("b"), engine.Atom("c"))},
}, `atomic_list_concat(X, '-', 'a-b-c').`))

t.Run("list is variable and seperator is empty", p.Expect([]map[string]engine.Term{
{"X": engine.List(engine.Atom("a"), engine.Atom("b"), engine.Atom("c"))},
}, `atomic_list_concat(X, '', 'abc').`))

t.Run("atom is empty", p.Expect([]map[string]engine.Term{
{"X": engine.Atom("")},
}, `atomic_list_concat([], '-', X).`))

// seems like Tau and SWI both bind [''] instead of [] to the list here
t.Run("atom is empty and list is var", p.Expect([]map[string]engine.Term{
{"X": engine.List(engine.Atom(""))},
}, `atomic_list_concat(X, '/', '').`))
})
}

0 comments on commit 1882687

Please sign in to comment.