From a543e4abb8e20f7f1bbf920f83ed7a414a7f3ccd Mon Sep 17 00:00:00 2001 From: ccamel Date: Tue, 21 Mar 2023 13:42:44 +0100 Subject: [PATCH 1/3] refactor(logic): move bootstrap to its own package ...to avoir circular dependencies when testing --- x/logic/interpreter/{ => bootstrap}/bootstrap.go | 2 +- x/logic/interpreter/{ => bootstrap}/bootstrap.pl | 0 x/logic/keeper/interpreter.go | 3 ++- x/logic/predicate/address_test.go | 2 +- x/logic/predicate/bank_test.go | 2 +- x/logic/predicate/block_test.go | 2 +- x/logic/predicate/chain_test.go | 2 +- x/logic/predicate/crypto_test.go | 2 +- x/logic/predicate/did_test.go | 2 +- 9 files changed, 9 insertions(+), 8 deletions(-) rename x/logic/interpreter/{ => bootstrap}/bootstrap.go (89%) rename x/logic/interpreter/{ => bootstrap}/bootstrap.pl (100%) diff --git a/x/logic/interpreter/bootstrap.go b/x/logic/interpreter/bootstrap/bootstrap.go similarity index 89% rename from x/logic/interpreter/bootstrap.go rename to x/logic/interpreter/bootstrap/bootstrap.go index 8cea8029..14abc214 100644 --- a/x/logic/interpreter/bootstrap.go +++ b/x/logic/interpreter/bootstrap/bootstrap.go @@ -1,4 +1,4 @@ -package interpreter +package bootstrap import _ "embed" diff --git a/x/logic/interpreter/bootstrap.pl b/x/logic/interpreter/bootstrap/bootstrap.pl similarity index 100% rename from x/logic/interpreter/bootstrap.pl rename to x/logic/interpreter/bootstrap/bootstrap.pl diff --git a/x/logic/keeper/interpreter.go b/x/logic/keeper/interpreter.go index ac7f2963..68aaf6b8 100644 --- a/x/logic/keeper/interpreter.go +++ b/x/logic/keeper/interpreter.go @@ -9,6 +9,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ichiban/prolog" "github.com/okp4/okp4d/x/logic/interpreter" + "github.com/okp4/okp4d/x/logic/interpreter/bootstrap" "github.com/okp4/okp4d/x/logic/types" "github.com/okp4/okp4d/x/logic/util" ) @@ -97,7 +98,7 @@ func (k Keeper) newInterpreter(ctx goctx.Context) (*prolog.Interpreter, error) { interpreted, err := interpreter.New( ctx, util.NonZeroOrDefault(interpreterParams.GetRegisteredPredicates(), interpreter.RegistryNames), - util.NonZeroOrDefault(interpreterParams.GetBootstrap(), interpreter.Bootstrap()), + util.NonZeroOrDefault(interpreterParams.GetBootstrap(), bootstrap.Bootstrap()), sdkctx.GasMeter(), k.fsProvider(ctx), ) diff --git a/x/logic/predicate/address_test.go b/x/logic/predicate/address_test.go index 69b4adf1..93101e57 100644 --- a/x/logic/predicate/address_test.go +++ b/x/logic/predicate/address_test.go @@ -117,7 +117,7 @@ func TestBech32(t *testing.T) { ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) Convey("and a vm", func() { - interpreter := testutil.NewInterpreterMust(ctx) + interpreter := testutil.NewLightInterpreterMust(ctx) interpreter.Register2(engine.NewAtom("bech32_address"), Bech32Address) err := interpreter.Compile(ctx, tc.program) diff --git a/x/logic/predicate/bank_test.go b/x/logic/predicate/bank_test.go index c56809b4..86d1dbef 100644 --- a/x/logic/predicate/bank_test.go +++ b/x/logic/predicate/bank_test.go @@ -454,7 +454,7 @@ func TestBank(t *testing.T) { Return(tc.balances) Convey("and a vm", func() { - interpreter := testutil.NewInterpreterMust(ctx) + interpreter := testutil.NewLightInterpreterMust(ctx) interpreter.Register2(engine.NewAtom("bank_balances"), BankBalances) interpreter.Register2(engine.NewAtom("bank_spendable_balances"), BankSpendableBalances) interpreter.Register2(engine.NewAtom("bank_locked_balances"), BankLockedBalances) diff --git a/x/logic/predicate/block_test.go b/x/logic/predicate/block_test.go index 9e9de15e..7adb9833 100644 --- a/x/logic/predicate/block_test.go +++ b/x/logic/predicate/block_test.go @@ -38,7 +38,7 @@ func TestBlock(t *testing.T) { ctx := sdk.NewContext(stateStore, tc.header, false, log.NewNopLogger()) Convey("and a vm", func() { - interpreter := testutil.NewInterpreterMust(ctx) + interpreter := testutil.NewLightInterpreterMust(ctx) interpreter.Register1(engine.NewAtom("block_height"), BlockHeight) interpreter.Register1(engine.NewAtom("block_time"), BlockTime) testutil.CompileMust(ctx, interpreter, fmt.Sprintf("test :- %s.", tc.implication)) diff --git a/x/logic/predicate/chain_test.go b/x/logic/predicate/chain_test.go index 4436d19e..69d8bb6f 100644 --- a/x/logic/predicate/chain_test.go +++ b/x/logic/predicate/chain_test.go @@ -34,7 +34,7 @@ func TestChainID(t *testing.T) { ctx := sdk.NewContext(stateStore, tc.header, false, log.NewNopLogger()) Convey("and an interpreter", func() { - interpreter := testutil.NewInterpreterMust(ctx) + interpreter := testutil.NewLightInterpreterMust(ctx) interpreter.Register1(engine.NewAtom("chain_id"), ChainID) testutil.CompileMust(ctx, interpreter, fmt.Sprintf("test :- %s.", tc.implication)) diff --git a/x/logic/predicate/crypto_test.go b/x/logic/predicate/crypto_test.go index 4a94c6a8..bb3ba709 100644 --- a/x/logic/predicate/crypto_test.go +++ b/x/logic/predicate/crypto_test.go @@ -117,7 +117,7 @@ H == [2252,222,43,46,219,165,107,244,8,96,31,183,33,254,155,92,51,141,16,238,66, ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) Convey("and a vm", func() { - interpreter := testutil.NewInterpreterMust(ctx) + interpreter := testutil.NewLightInterpreterMust(ctx) interpreter.Register2(engine.NewAtom("sha_hash"), SHAHash) interpreter.Register2(engine.NewAtom("hex_bytes"), HexBytes) diff --git a/x/logic/predicate/did_test.go b/x/logic/predicate/did_test.go index 3a061684..9be699b5 100644 --- a/x/logic/predicate/did_test.go +++ b/x/logic/predicate/did_test.go @@ -95,7 +95,7 @@ func TestDID(t *testing.T) { ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) Convey("and a vm", func() { - interpreter := testutil.NewInterpreterMust(ctx) + interpreter := testutil.NewLightInterpreterMust(ctx) interpreter.Register2(engine.NewAtom("did_components"), DIDComponents) err := interpreter.Compile(ctx, tc.program) From b89718c5d59bda8fc73b3a3045de002523452c60 Mon Sep 17 00:00:00 2001 From: ccamel Date: Tue, 21 Mar 2023 13:44:49 +0100 Subject: [PATCH 2/3] feat(logic): add source_files/1 predicate --- x/logic/interpreter/bootstrap/bootstrap.pl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x/logic/interpreter/bootstrap/bootstrap.pl b/x/logic/interpreter/bootstrap/bootstrap.pl index 5eaf096d..af745ed2 100644 --- a/x/logic/interpreter/bootstrap/bootstrap.pl +++ b/x/logic/interpreter/bootstrap/bootstrap.pl @@ -253,3 +253,5 @@ maplist(Cont_7, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s], [E7|E7s]) :- call(Cont_7, E1, E2, E3, E4, E5, E6, E7), maplist(Cont_7, E1s, E2s, E3s, E4s, E5s, E6s, E7s). + +source_files(Files) :- bagof(File, source_file(File), Files). From 67f4889e06691af62d999adbb4ce6662cfe5276b Mon Sep 17 00:00:00 2001 From: ccamel Date: Tue, 21 Mar 2023 13:45:09 +0100 Subject: [PATCH 3/3] test(logic): put source_files/1 predicate under testing --- x/logic/predicate/file_test.go | 54 +++++++++++++++++++++++++++++++--- x/logic/testutil/logic.go | 39 +++++++++++++++++------- 2 files changed, 79 insertions(+), 14 deletions(-) diff --git a/x/logic/predicate/file_test.go b/x/logic/predicate/file_test.go index ac07e0ab..49c89c2a 100644 --- a/x/logic/predicate/file_test.go +++ b/x/logic/predicate/file_test.go @@ -7,9 +7,12 @@ import ( "testing" "time" + goctx "context" + "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/golang/mock/gomock" + "github.com/ichiban/prolog" "github.com/ichiban/prolog/engine" "github.com/okp4/okp4d/x/logic/fs" "github.com/okp4/okp4d/x/logic/testutil" @@ -26,47 +29,90 @@ func TestSourceFile(t *testing.T) { defer ctrl.Finish() cases := []struct { + interpreter func(ctx goctx.Context) (i *prolog.Interpreter) query string wantResult []types.TermResults wantError error wantSuccess bool }{ { + interpreter: testutil.NewLightInterpreterMust, query: "source_file(file).", wantSuccess: false, }, { + interpreter: testutil.NewLightInterpreterMust, query: "consult(file1), consult(file2), source_file(file1).", wantResult: []types.TermResults{{}}, wantSuccess: true, }, { + interpreter: testutil.NewLightInterpreterMust, query: "consult(file1), consult(file2), consult(file3), source_file(file2).", wantResult: []types.TermResults{{}}, wantSuccess: true, }, { + interpreter: testutil.NewLightInterpreterMust, query: "consult(file1), consult(file2), source_file(file3).", wantSuccess: false, }, { + interpreter: testutil.NewLightInterpreterMust, query: "source_file(X).", wantSuccess: false, }, { + interpreter: testutil.NewLightInterpreterMust, query: "consult(file1), consult(file2), source_file(X).", wantResult: []types.TermResults{{"X": "file1"}, {"X": "file2"}}, wantSuccess: true, }, { + interpreter: testutil.NewLightInterpreterMust, query: "consult(file2), consult(file3), consult(file1), source_file(X).", wantResult: []types.TermResults{{"X": "file1"}, {"X": "file2"}, {"X": "file3"}}, wantSuccess: true, }, { - query: "source_file(foo(bar)).", - wantResult: []types.TermResults{}, - wantError: fmt.Errorf("source_file/1: cannot unify file with *engine.compound"), + interpreter: testutil.NewLightInterpreterMust, + query: "source_file(foo(bar)).", + wantResult: []types.TermResults{}, + wantError: fmt.Errorf("source_file/1: cannot unify file with *engine.compound"), + }, + + { + interpreter: testutil.NewComprehensiveInterpreterMust, + query: "source_files([file]).", + wantSuccess: false, + }, + { + interpreter: testutil.NewComprehensiveInterpreterMust, + query: "consult(file1), consult(file2), source_files([file1, file2]).", + wantResult: []types.TermResults{{}}, + wantSuccess: true, + }, + { + interpreter: testutil.NewComprehensiveInterpreterMust, + query: "consult(file1), consult(file2), source_files([file1, file2, file3]).", + wantSuccess: false, + }, + { + interpreter: testutil.NewComprehensiveInterpreterMust, + query: "source_files(X).", + wantSuccess: false, + }, + { + interpreter: testutil.NewComprehensiveInterpreterMust, + query: "consult(file2), consult(file1), source_files(X).", + wantResult: []types.TermResults{{"X": "[file1,file2]"}}, + wantSuccess: true, + }, + { + interpreter: testutil.NewComprehensiveInterpreterMust, + query: "consult(file2), consult(file1), source_files([file1, X]).", + wantResult: []types.TermResults{{"X": "file2"}}, + wantSuccess: true, }, } @@ -87,7 +133,7 @@ func TestSourceFile(t *testing.T) { ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) Convey("and a vm", func() { - interpreter := testutil.NewInterpreterMust(ctx) + interpreter := tc.interpreter(ctx) interpreter.FS = mockedFS interpreter.Register1(engine.NewAtom("source_file"), SourceFile) diff --git a/x/logic/testutil/logic.go b/x/logic/testutil/logic.go index 748af249..100c61cb 100644 --- a/x/logic/testutil/logic.go +++ b/x/logic/testutil/logic.go @@ -7,18 +7,19 @@ import ( "github.com/ichiban/prolog" "github.com/ichiban/prolog/engine" + "github.com/okp4/okp4d/x/logic/interpreter/bootstrap" ) -// NewInterpreterMust returns a new Interpreter with the given context or panics if it fails. +// NewLightInterpreterMust returns a new Interpreter with the given context or panics if it fails. // The Interpreter is configured with minimal settings to support testing. -func NewInterpreterMust(ctx context.Context) (interpreter *prolog.Interpreter) { - interpreter = &prolog.Interpreter{} - interpreter.Register3(engine.NewAtom("op"), engine.Op) - interpreter.Register3(engine.NewAtom("compare"), engine.Compare) - interpreter.Register2(engine.NewAtom("="), engine.Unify) - interpreter.Register1(engine.NewAtom("consult"), engine.Consult) +func NewLightInterpreterMust(ctx context.Context) (i *prolog.Interpreter) { + i = &prolog.Interpreter{} + i.Register3(engine.NewAtom("op"), engine.Op) + i.Register3(engine.NewAtom("compare"), engine.Compare) + i.Register2(engine.NewAtom("="), engine.Unify) + i.Register1(engine.NewAtom("consult"), engine.Consult) - err := interpreter.Compile(ctx, ` + err := i.Compile(ctx, ` :-(op(1200, xfx, ':-')). :-(op(1000, xfy, ',')). :-(op(700, xfx, [==, \==, @<, @=<, @>, @>=])). @@ -35,10 +36,28 @@ func NewInterpreterMust(ctx context.Context) (interpreter *prolog.Interpreter) { return } +// NewComprehensiveInterpreterMust returns a new Interpreter with the given context or panics if it fails. +// The Interpreter is configured with the full boostrap but with a minimal set of predicates. +func NewComprehensiveInterpreterMust(ctx context.Context) (i *prolog.Interpreter) { + i = &prolog.Interpreter{} + i.Register3(engine.NewAtom("op"), engine.Op) + i.Register3(engine.NewAtom("compare"), engine.Compare) + i.Register2(engine.NewAtom("="), engine.Unify) + i.Register1(engine.NewAtom("consult"), engine.Consult) + i.Register3(engine.NewAtom("bagof"), engine.BagOf) + + err := i.Compile(ctx, bootstrap.Bootstrap()) + if err != nil { + panic(err) + } + + return +} + // CompileMust compiles the given source code and panics if it fails. // This is a convenience function for testing. -func CompileMust(ctx context.Context, interpreter *prolog.Interpreter, s string, args ...interface{}) { - err := interpreter.Compile(ctx, s, args...) +func CompileMust(ctx context.Context, i *prolog.Interpreter, s string, args ...interface{}) { + err := i.Compile(ctx, s, args...) if err != nil { panic(err) }