Skip to content

Commit 7be6699

Browse files
authored
Merge pull request #17 from axone-protocol/feat/dicts
Add (preliminary) support for dicts structure
2 parents be90013 + f1d5e88 commit 7be6699

18 files changed

+1920
-39
lines changed

.github/workflows/go.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
- name: Set up Go
1717
uses: actions/setup-go@v2
1818
with:
19-
go-version: 1.19
19+
go-version: 1.23
2020

2121
- name: Build
2222
run: go build -v ./...

engine/atom.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var (
2828
atomGreaterThan = NewAtom(">")
2929
atomDot = NewAtom(".")
3030
atomComma = NewAtom(",")
31+
atomDict = NewAtom("dict")
3132
atomBar = NewAtom("|")
3233
atomCut = NewAtom("!")
3334
atomSemiColon = NewAtom(";")
@@ -66,6 +67,7 @@ var (
6667
atomCompound = NewAtom("compound")
6768
atomCreate = NewAtom("create")
6869
atomDebug = NewAtom("debug")
70+
atomDictKey = NewAtom("dict_key")
6971
atomDiscontiguous = NewAtom("discontiguous")
7072
atomDiv = NewAtom("div")
7173
atomDomainError = NewAtom("domain_error")

engine/builtin.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,11 @@ func renamedCopy(t Term, copied map[termID]Term, env *Env) (Term, error) {
453453
}
454454
c.args[i] = cp
455455
}
456+
457+
if _, ok := t.(Dict); ok {
458+
return &dict{c}, nil
459+
}
460+
456461
return &c, nil
457462
default:
458463
return t, nil

engine/builtin_test.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func TestCall(t *testing.T) {
4040
assert.NoError(t, vm.Compile(context.Background(), `
4141
foo.
4242
foo(_, _).
43-
f(g([a, [b, c|X]])).
43+
f(g([a, [b, c|X], Y{x:5}])).
4444
`))
4545

4646
tests := []struct {
@@ -55,14 +55,14 @@ f(g([a, [b, c|X]])).
5555
{title: `undefined atom`, goal: NewAtom("bar"), ok: false, err: existenceError(objectTypeProcedure, atomSlash.Apply(NewAtom("bar"), Integer(0)), nil)},
5656
{title: `defined atom`, goal: NewAtom("foo"), ok: true},
5757
{title: `undefined compound`, goal: NewAtom("bar").Apply(NewVariable(), NewVariable()), ok: false, err: existenceError(objectTypeProcedure, atomSlash.Apply(NewAtom("bar"), Integer(2)), nil)},
58-
{title: `defined compound`, goal: NewAtom("foo").Apply(NewVariable(), NewVariable()), ok: true},
58+
{title: `defined compound`, goal: NewAtom("foo").Apply(NewVariable(), makeDict(NewVariable())), ok: true},
5959
{title: `variable: single predicate`, goal: NewVariable(), ok: false, err: InstantiationError(nil)},
6060
{title: `variable: multiple predicates`, goal: atomComma.Apply(atomFail, NewVariable()), ok: false},
6161
{title: `not callable: single predicate`, goal: Integer(0), ok: false, err: typeError(validTypeCallable, Integer(0), nil)},
6262
{title: `not callable: conjunction`, goal: atomComma.Apply(atomTrue, Integer(0)), ok: false, err: typeError(validTypeCallable, atomComma.Apply(atomTrue, Integer(0)), nil)},
6363
{title: `not callable: disjunction`, goal: atomSemiColon.Apply(Integer(1), atomTrue), ok: false, err: typeError(validTypeCallable, atomSemiColon.Apply(Integer(1), atomTrue), nil)},
6464

65-
{title: `cover all`, goal: atomComma.Apply(atomCut, NewAtom("f").Apply(NewAtom("g").Apply(List(NewAtom("a"), PartialList(NewVariable(), NewAtom("b"), NewAtom("c")))))), ok: true},
65+
{title: `cover all`, goal: atomComma.Apply(atomCut, NewAtom("f").Apply(NewAtom("g").Apply(List(NewAtom("a"), PartialList(NewVariable(), NewAtom("b"), NewAtom("c")), makeDict(NewAtom("foo"), NewAtom("x"), Integer(5)))))), ok: true},
6666
{title: `out of memory`, goal: NewAtom("foo").Apply(NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable()), err: resourceError(resourceMemory, nil), mem: 1},
6767
{title: `panic`, goal: NewAtom("do_not_call"), err: PanicError{errors.New("told you")}},
6868
{title: `panic (lazy)`, goal: NewAtom("lazy_do_not_call"), err: PanicError{errors.New("told you")}},
@@ -583,6 +583,15 @@ func TestTypeCompound(t *testing.T) {
583583
assert.True(t, ok)
584584
})
585585

586+
t.Run("dict is compound", func(t *testing.T) {
587+
ok, err := TypeCompound(nil, &dict{compound{
588+
functor: NewAtom("foo"),
589+
args: []Term{NewAtom("a")},
590+
}}, Success, nil).Force(context.Background())
591+
assert.NoError(t, err)
592+
assert.True(t, ok)
593+
})
594+
586595
t.Run("not compound", func(t *testing.T) {
587596
ok, err := TypeCompound(nil, NewAtom("foo"), Success, nil).Force(context.Background())
588597
assert.NoError(t, err)

engine/clause.go

Lines changed: 102 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,78 @@ type clause struct {
6565

6666
func compileClause(head Term, body Term, env *Env) (clause, error) {
6767
var c clause
68+
var goals []Term
69+
70+
head, goals = desugar(head, goals)
71+
body, goals = desugar(body, goals)
72+
73+
if body != nil {
74+
goals = append(goals, body)
75+
}
76+
if len(goals) > 0 {
77+
body = seq(atomComma, goals...)
78+
}
79+
6880
c.compileHead(head, env)
81+
6982
if body != nil {
7083
if err := c.compileBody(body, env); err != nil {
7184
return c, typeError(validTypeCallable, body, env)
7285
}
7386
}
74-
c.bytecode = append(c.bytecode, instruction{opcode: OpExit})
87+
88+
c.emit(instruction{opcode: OpExit})
7589
return c, nil
7690
}
7791

92+
func desugar(term Term, acc []Term) (Term, []Term) {
93+
switch t := term.(type) {
94+
case charList, codeList:
95+
return t, acc
96+
case list:
97+
l := make(list, len(t))
98+
for i, e := range t {
99+
l[i], acc = desugar(e, acc)
100+
}
101+
return l, acc
102+
case *partial:
103+
c, acc := desugar(t.Compound, acc)
104+
tail, acc := desugar(*t.tail, acc)
105+
return &partial{
106+
Compound: c.(Compound),
107+
tail: &tail,
108+
}, acc
109+
case Compound:
110+
if t.Functor() == atomSpecialDot && t.Arity() == 2 {
111+
tempV := NewVariable()
112+
lhs, acc := desugar(t.Arg(0), acc)
113+
rhs, acc := desugar(t.Arg(1), acc)
114+
115+
return tempV, append(acc, atomDot.Apply(lhs, rhs, tempV))
116+
}
117+
118+
c := compound{
119+
functor: t.Functor(),
120+
args: make([]Term, t.Arity()),
121+
}
122+
for i := 0; i < t.Arity(); i++ {
123+
c.args[i], acc = desugar(t.Arg(i), acc)
124+
}
125+
126+
if _, ok := t.(Dict); ok {
127+
return &dict{c}, acc
128+
}
129+
130+
return &c, acc
131+
default:
132+
return t, acc
133+
}
134+
}
135+
136+
func (c *clause) emit(i instruction) {
137+
c.bytecode = append(c.bytecode, i)
138+
}
139+
78140
func (c *clause) compileHead(head Term, env *Env) {
79141
switch head := env.Resolve(head).(type) {
80142
case Atom:
@@ -88,7 +150,7 @@ func (c *clause) compileHead(head Term, env *Env) {
88150
}
89151

90152
func (c *clause) compileBody(body Term, env *Env) error {
91-
c.bytecode = append(c.bytecode, instruction{opcode: OpEnter})
153+
c.emit(instruction{opcode: OpEnter})
92154
iter := seqIterator{Seq: body, Env: env}
93155
for iter.Next() {
94156
if err := c.compilePred(iter.Current(), env); err != nil {
@@ -107,16 +169,16 @@ func (c *clause) compilePred(p Term, env *Env) error {
107169
case Atom:
108170
switch p {
109171
case atomCut:
110-
c.bytecode = append(c.bytecode, instruction{opcode: OpCut})
172+
c.emit(instruction{opcode: OpCut})
111173
return nil
112174
}
113-
c.bytecode = append(c.bytecode, instruction{opcode: OpCall, operand: procedureIndicator{name: p, arity: 0}})
175+
c.emit(instruction{opcode: OpCall, operand: procedureIndicator{name: p, arity: 0}})
114176
return nil
115177
case Compound:
116178
for i := 0; i < p.Arity(); i++ {
117179
c.compileBodyArg(p.Arg(i), env)
118180
}
119-
c.bytecode = append(c.bytecode, instruction{opcode: OpCall, operand: procedureIndicator{name: p.Functor(), arity: Integer(p.Arity())}})
181+
c.emit(instruction{opcode: OpCall, operand: procedureIndicator{name: p.Functor(), arity: Integer(p.Arity())}})
120182
return nil
121183
default:
122184
return errNotCallable
@@ -126,67 +188,84 @@ func (c *clause) compilePred(p Term, env *Env) error {
126188
func (c *clause) compileHeadArg(a Term, env *Env) {
127189
switch a := env.Resolve(a).(type) {
128190
case Variable:
129-
c.bytecode = append(c.bytecode, instruction{opcode: OpGetVar, operand: c.varOffset(a)})
191+
c.emit(instruction{opcode: OpGetVar, operand: c.varOffset(a)})
130192
case charList, codeList: // Treat them as if they're atomic.
131-
c.bytecode = append(c.bytecode, instruction{opcode: OpGetConst, operand: a})
193+
c.emit(instruction{opcode: OpGetConst, operand: a})
132194
case list:
133-
c.bytecode = append(c.bytecode, instruction{opcode: OpGetList, operand: Integer(len(a))})
195+
c.emit(instruction{opcode: OpGetList, operand: Integer(len(a))})
134196
for _, arg := range a {
135197
c.compileHeadArg(arg, env)
136198
}
137-
c.bytecode = append(c.bytecode, instruction{opcode: OpPop})
199+
c.emit(instruction{opcode: OpPop})
138200
case *partial:
139201
prefix := a.Compound.(list)
140-
c.bytecode = append(c.bytecode, instruction{opcode: OpGetPartial, operand: Integer(len(prefix))})
202+
c.emit(instruction{opcode: OpGetPartial, operand: Integer(len(prefix))})
141203
c.compileHeadArg(*a.tail, env)
142204
for _, arg := range prefix {
143205
c.compileHeadArg(arg, env)
144206
}
145-
c.bytecode = append(c.bytecode, instruction{opcode: OpPop})
207+
c.emit(instruction{opcode: OpPop})
146208
case Compound:
147-
c.bytecode = append(c.bytecode, instruction{opcode: OpGetFunctor, operand: procedureIndicator{name: a.Functor(), arity: Integer(a.Arity())}})
209+
switch a.(type) {
210+
case Dict:
211+
c.emit(instruction{opcode: OpGetDict, operand: Integer(a.Arity())})
212+
default:
213+
c.emit(instruction{opcode: OpGetFunctor, operand: procedureIndicator{name: a.Functor(), arity: Integer(a.Arity())}})
214+
}
215+
148216
for i := 0; i < a.Arity(); i++ {
149217
c.compileHeadArg(a.Arg(i), env)
150218
}
151-
c.bytecode = append(c.bytecode, instruction{opcode: OpPop})
219+
c.emit(instruction{opcode: OpPop})
152220
default:
153-
c.bytecode = append(c.bytecode, instruction{opcode: OpGetConst, operand: a})
221+
c.emit(instruction{opcode: OpGetConst, operand: a})
154222
}
155223
}
156224

157225
func (c *clause) compileBodyArg(a Term, env *Env) {
158226
switch a := env.Resolve(a).(type) {
159227
case Variable:
160-
c.bytecode = append(c.bytecode, instruction{opcode: OpPutVar, operand: c.varOffset(a)})
228+
c.emit(instruction{opcode: OpPutVar, operand: c.varOffset(a)})
161229
case charList, codeList: // Treat them as if they're atomic.
162-
c.bytecode = append(c.bytecode, instruction{opcode: OpPutConst, operand: a})
230+
c.emit(instruction{opcode: OpPutConst, operand: a})
163231
case list:
164-
c.bytecode = append(c.bytecode, instruction{opcode: OpPutList, operand: Integer(len(a))})
232+
c.emit(instruction{opcode: OpPutList, operand: Integer(len(a))})
165233
for _, arg := range a {
166234
c.compileBodyArg(arg, env)
167235
}
168-
c.bytecode = append(c.bytecode, instruction{opcode: OpPop})
236+
c.emit(instruction{opcode: OpPop})
237+
case Dict:
238+
c.emit(instruction{opcode: OpPutDict, operand: Integer(a.Arity())})
239+
for i := 0; i < a.Arity(); i++ {
240+
c.compileBodyArg(a.Arg(i), env)
241+
}
242+
c.emit(instruction{opcode: OpPop})
169243
case *partial:
170244
var l int
171245
iter := ListIterator{List: a.Compound}
172246
for iter.Next() {
173247
l++
174248
}
175-
c.bytecode = append(c.bytecode, instruction{opcode: OpPutPartial, operand: Integer(l)})
249+
c.emit(instruction{opcode: OpPutPartial, operand: Integer(l)})
176250
c.compileBodyArg(*a.tail, env)
177251
iter = ListIterator{List: a.Compound}
178252
for iter.Next() {
179253
c.compileBodyArg(iter.Current(), env)
180254
}
181-
c.bytecode = append(c.bytecode, instruction{opcode: OpPop})
255+
c.emit(instruction{opcode: OpPop})
182256
case Compound:
183-
c.bytecode = append(c.bytecode, instruction{opcode: OpPutFunctor, operand: procedureIndicator{name: a.Functor(), arity: Integer(a.Arity())}})
257+
switch a.(type) {
258+
case Dict:
259+
c.emit(instruction{opcode: OpPutDict, operand: Integer(a.Arity())})
260+
default:
261+
c.emit(instruction{opcode: OpPutFunctor, operand: procedureIndicator{name: a.Functor(), arity: Integer(a.Arity())}})
262+
}
184263
for i := 0; i < a.Arity(); i++ {
185264
c.compileBodyArg(a.Arg(i), env)
186265
}
187-
c.bytecode = append(c.bytecode, instruction{opcode: OpPop})
266+
c.emit(instruction{opcode: OpPop})
188267
default:
189-
c.bytecode = append(c.bytecode, instruction{opcode: OpPutConst, operand: a})
268+
c.emit(instruction{opcode: OpPutConst, operand: a})
190269
}
191270
}
192271

0 commit comments

Comments
 (0)