Skip to content

Commit

Permalink
Merge pull request #2 from olligobber/recordtypes
Browse files Browse the repository at this point in the history
Add syntax to make record constructors have type information
  • Loading branch information
olligobber authored Feb 4, 2019
2 parents 80626eb + 34742d7 commit 011fd34
Show file tree
Hide file tree
Showing 36 changed files with 572 additions and 558 deletions.
22 changes: 18 additions & 4 deletions src/AstArgument.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#pragma once

#include "AstNode.h"
#include "AstType.h"
#include "AstTypes.h"
#include "FunctorOps.h"
#include "SymbolTable.h"
Expand Down Expand Up @@ -254,7 +255,7 @@ class AstNullConstant : public AstConstant {

/** Print argument to the given output stream */
void print(std::ostream& os) const override {
os << '-';
os << "nil";
}

/** Creates a clone of this AST sub-structure */
Expand Down Expand Up @@ -484,11 +485,24 @@ class AstRecordInit : public AstArgument {
/** The list of components to be aggregated into a record */
std::vector<std::unique_ptr<AstArgument>> args;

/** The type of record being construted */
AstTypeIdentifier type;

public:
AstRecordInit() = default;

AstRecordInit(const AstTypeIdentifier& type) : type(type) {}

~AstRecordInit() override = default;

void setType(const AstTypeIdentifier& t) {
type = t;
}

AstTypeIdentifier getType() const {
return type;
}

void add(std::unique_ptr<AstArgument> arg) {
args.push_back(std::move(arg));
}
Expand All @@ -498,12 +512,12 @@ class AstRecordInit : public AstArgument {
}

void print(std::ostream& os) const override {
os << "[" << join(args, ",", print_deref<std::unique_ptr<AstArgument>>()) << "]";
os << "*" << type << "[" << join(args, ",", print_deref<std::unique_ptr<AstArgument>>()) << "]";
}

/** Creates a clone of this AST sub-structure */
AstRecordInit* clone() const override {
auto res = new AstRecordInit();
auto res = new AstRecordInit(type);
for (auto& cur : args) {
res->args.push_back(std::unique_ptr<AstArgument>(cur->clone()));
}
Expand Down Expand Up @@ -532,7 +546,7 @@ class AstRecordInit : public AstArgument {
bool equal(const AstNode& node) const override {
assert(nullptr != dynamic_cast<const AstRecordInit*>(&node));
const auto& other = static_cast<const AstRecordInit&>(node);
return equal_targets(args, other.args);
return equal_targets(args, other.args) && type == other.type;
}
};

Expand Down
8 changes: 8 additions & 0 deletions src/ComponentModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,14 @@ ComponentContent getInstantiatedContent(const AstComponentInit& componentInit,
}
}
});

// rename type information in record constructor
visitDepthFirst(node, [&](const AstRecordInit& record) {
auto pos = typeNameMapping.find(record.getType());
if (pos != typeNameMapping.end()) {
const_cast<AstRecordInit&>(record).setType(pos->second);
}
});
};

// rename attribute type in headers and atoms in clauses of the relation
Expand Down
9 changes: 5 additions & 4 deletions src/parser.yy
Original file line number Diff line number Diff line change
Expand Up @@ -659,12 +659,13 @@ arg
$$ = new AstIntrinsicFunctor(FunctorOp::LNOT, std::unique_ptr<AstArgument>($2));
$$->setSrcLoc(@$);
}
| LBRACKET RBRACKET {
$$ = new AstRecordInit();
| STAR type_id LBRACKET RBRACKET {
$$ = new AstRecordInit(*$2);
$$->setSrcLoc(@$);
}
| LBRACKET recordlist RBRACKET {
$$ = $2;
| STAR type_id LBRACKET recordlist RBRACKET {
$4->setType(*$2);
$$ = $4;
$$->setSrcLoc(@$);
}
| NIL {
Expand Down
18 changes: 10 additions & 8 deletions src/test/ast_utils_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ TEST(AstUtils, GroundedRecords) {
.decl r ( r : R )
.decl s ( r : N )
s(x) :- r([x,y]).
s(x) :- r(*R [x,y]).
)",
sym, e, d);
Expand All @@ -120,7 +120,7 @@ TEST(AstUtils, GroundedRecords) {
auto clause = program.getRelation("s")->getClause(0);

// check construction
EXPECT_EQ("s(x) :- \n r([x,y]).", toString(*clause));
EXPECT_EQ("s(x) :- \n r(*R[x,y]).", toString(*clause));

// obtain groundness
auto isGrounded = getGroundedTerms(*clause);
Expand Down Expand Up @@ -339,8 +339,9 @@ TEST(AstUtils, GroundTermPropagation) {
R"(
.type D
.decl p(a:D,b:D)
.type R = [ x : D, y : D ]
p(a,b) :- p(x,y), r = [x,y], s = r, s = [w,v], [w,v] = [a,b].
p(a,b) :- p(x,y), r = *R [x,y], s = r, s = *R [w,v], *R [w,v] = *R [a,b].
)",
sym, e, d);

Expand All @@ -349,15 +350,15 @@ TEST(AstUtils, GroundTermPropagation) {
// check types in clauses
AstClause* a = program.getRelation("p")->getClause(0);

EXPECT_EQ("p(a,b) :- \n p(x,y),\n r = [x,y],\n s = r,\n s = [w,v],\n [w,v] = [a,b].",
EXPECT_EQ("p(a,b) :- \n p(x,y),\n r = *R[x,y],\n s = r,\n s = *R[w,v],\n *R[w,v] = *R[a,b].",
toString(*a));

std::unique_ptr<AstClause> res = ResolveAliasesTransformer::resolveAliases(*a);
std::unique_ptr<AstClause> cleaned = ResolveAliasesTransformer::removeTrivialEquality(*res);

EXPECT_EQ(
"p(x,y) :- \n p(x,y),\n [x,y] = [x,y],\n [x,y] = [x,y],\n [x,y] = [x,y],\n [x,y] = "
"[x,y].",
"p(x,y) :- \n p(x,y),\n *R[x,y] = *R[x,y],\n *R[x,y] = *R[x,y],\n *R[x,y] = *R[x,y],\n "
"*R[x,y] = *R[x,y].",
toString(*res));
EXPECT_EQ("p(x,y) :- \n p(x,y).", toString(*cleaned));
}
Expand Down Expand Up @@ -399,14 +400,15 @@ TEST(AstUtils, ResolveGroundedAliases) {
R"(
.type D
.decl p(a:D,b:D)
.type R = [ x : D, y : D ]
p(a,b) :- p(x,y), r = [x,y], s = r, s = [w,v], [w,v] = [a,b].
p(a,b) :- p(x,y), r = *R [x,y], s = r, s = *R [w,v], *R [w,v] = *R [a,b].
)",
sym, e, d);

AstProgram& program = *tu->getProgram();

EXPECT_EQ("p(a,b) :- \n p(x,y),\n r = [x,y],\n s = r,\n s = [w,v],\n [w,v] = [a,b].",
EXPECT_EQ("p(a,b) :- \n p(x,y),\n r = *R[x,y],\n s = r,\n s = *R[w,v],\n *R[w,v] = *R[a,b].",
toString(*program.getRelation("p")->getClause(0)));

std::make_unique<ResolveAliasesTransformer>()->apply(*tu);
Expand Down
5 changes: 2 additions & 3 deletions tests/evaluation/aliases/aliases.dl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// tests the proper handling of aliases

.number_type N

.type R = [x : N, y : N]

.decl n ( a : N )

Expand All @@ -30,5 +30,4 @@ a(3,2).

.decl r ( a : N )
.output r ()
r(z) :- a(x,y), r = [x,y], s = r, s = [v,w], w=z, z=v.

r(z) :- a(x,y), r = *R[x,y], s = r, s = *R[v,w], w=z, z=v.
10 changes: 3 additions & 7 deletions tests/evaluation/components_generic/components_generic.dl
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ StreetMap.e("B","C").

.init SocialNet = Net<Person>

#define Homer ["Homer","Evergreen Terrace 742"]
#define Ned ["Ned","Evergreen Terrace 744"]
#define Edna ["Ned","Evergreen Terrace 82"]
#define Homer *Person["Homer","Evergreen Terrace 742"]
#define Ned *Person["Ned","Evergreen Terrace 744"]
#define Edna *Person["Ned","Evergreen Terrace 82"]

SocialNet.e(Homer,Ned).
SocialNet.e(Ned,Edna).
Expand All @@ -75,7 +75,3 @@ SocialNet.e(Ned,Edna).
.output result()
result("Map Works") :- StreetMap.r("C","A").
result("Social Net Works") :- SocialNet.r(Edna,Homer).




10 changes: 5 additions & 5 deletions tests/evaluation/inline_nqueens/inline_nqueens.dl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ up_to_size(x + 1) :- up_to_size(x), !size(x).

// This relation holds the position of each square of the board.
.decl on_board( p : Position ) inline
on_board([x, y]) :- up_to_size(x), up_to_size(y).
on_board(*Position[x, y]) :- up_to_size(x), up_to_size(y).


// Mutually-non-attacking list of queen positions.
Expand All @@ -40,16 +40,16 @@ on_board([x, y]) :- up_to_size(x), up_to_size(y).

// Arguments are not on the same diagonal.
.decl non_diagonals( p : Position, q : Position ) inline
non_diagonals([px, py], [qx, qy]) :- px - qx != py - qy, qx - px != py - qy, on_board([px, py]), on_board([qx, qy]).
non_diagonals(*Position[px, py], *Position[qx, qy]) :- px - qx != py - qy, qx - px != py - qy, on_board(*Position[px, py]), on_board(*Position[qx, qy]).


// Holds if p and q do not attack each other.
.decl sympathetic( p : Position, q : Position ) inline
sympathetic(p, q) :- non_diagonals(p, q), px != qx, py != qy, p=[px, py], q=[qx,qy].
sympathetic(p, q) :- non_diagonals(p, q), px != qx, py != qy, p=*Position[px, py], q=*Position[qx,qy].


// Holds if the head of qs is mutually non-attacking with its tail, which should itself be compatible.
// Generated column by column, left to right.
.decl compatible( qs : QueenList, n : number )
compatible([p, nil], 1) :- on_board(p). //, p = [1,y].
compatible([p, [head, tail]], n+1) :- sympathetic(p, head), compatible([p, tail], n), compatible([head, tail], n), p = [px, py], head = [hx, hy], px > hx.
compatible(*QueenList[p, nil], 1) :- on_board(p). //, p = *Position[1,y].
compatible(*QueenList[p, *QueenList[head, tail]], n+1) :- sympathetic(p, head), compatible(*QueenList[p, tail], n), compatible(*QueenList[head, tail], n), p = *Position[px, py], head = *Position[hx, hy], px > hx.
8 changes: 4 additions & 4 deletions tests/evaluation/inline_nqueens/inline_nqueens.err
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Warning: Variable py only occurs once in file inline_nqueens.dl at line 55
compatible([p, [head, tail]], n+1) :- sympathetic(p, head), compatible([p, tail], n), compatible([head, tail], n), p = [px, py], head = [hx, hy], px > hx.
----------------------------------------------------------------------------------------------------------------------------^------------------------------
compatible(*QueenList[p, *QueenList[head, tail]], n+1) :- sympathetic(p, head), compatible(*QueenList[p, tail], n), compatible(*QueenList[head, tail], n), p = *Position[px, py], head = *Position[hx, hy], px > hx.
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------^---------------------------------------
Warning: Variable hy only occurs once in file inline_nqueens.dl at line 55
compatible([p, [head, tail]], n+1) :- sympathetic(p, head), compatible([p, tail], n), compatible([head, tail], n), p = [px, py], head = [hx, hy], px > hx.
---------------------------------------------------------------------------------------------------------------------------------------------^-------------
compatible(*QueenList[p, *QueenList[head, tail]], n+1) :- sympathetic(p, head), compatible(*QueenList[p, tail], n), compatible(*QueenList[head, tail], n), p = *Position[px, py], head = *Position[hx, hy], px > hx.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------^-------------
12 changes: 6 additions & 6 deletions tests/evaluation/inline_records/inline_records.dl
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@
]

.decl a(x:Conn, y:Conn) inline
a(["Hello", "Goodbye"], ["temporary", "value"]).
a(["temporary", "value"], ["NO", "NO"]).
a(["can", "do"], ["my", "value"]).
a(*Conn["Hello", "Goodbye"], *Conn["temporary", "value"]).
a(*Conn["temporary", "value"], *Conn["NO", "NO"]).
a(*Conn["can", "do"], *Conn["my", "value"]).

.decl b(x:Conn, y:Conn) inline
b(["good job", "bad job"], ["can", "do"]).
b(x, y) :- a(x, y), x != ["temporary", "value"].
b(*Conn["good job", "bad job"], *Conn["can", "do"]).
b(x, y) :- a(x, y), x != *Conn["temporary", "value"].

.decl c(x:Conn) inline
c(x) :- a(z, y), b(x, z), x != y.

.decl d(x:Thing)
d(x) :- c([x, _]).
d(x) :- c(*Conn[x, _]).
.output d()
10 changes: 5 additions & 5 deletions tests/evaluation/magic_nqueens/magic_nqueens.dl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ up_to_size(x + 1) :- up_to_size(x), !size(x).

// This relation holds the position of each square of the board.
.decl on_board( p : Position )
on_board([x, y]) :- up_to_size(x), up_to_size(y).
on_board(*Position[x, y]) :- up_to_size(x), up_to_size(y).


// Mutually-non-attacking list of queen positions.
Expand All @@ -42,16 +42,16 @@ on_board([x, y]) :- up_to_size(x), up_to_size(y).

// Arguments are not on the same diagonal.
.decl non_diagonals( p : Position, q : Position )
non_diagonals([px, py], [qx, qy]) :- px - qx != py - qy, qx - px != py - qy, on_board([px, py]), on_board([qx, qy]).
non_diagonals(*Position[px, py], *Position[qx, qy]) :- px - qx != py - qy, qx - px != py - qy, on_board(*Position[px, py]), on_board(*Position[qx, qy]).


// Holds if p and q do not attack each other.
.decl sympathetic( p : Position, q : Position )
sympathetic(p, q) :- non_diagonals(p, q), px != qx, py != qy, p=[px, py], q=[qx,qy].
sympathetic(p, q) :- non_diagonals(p, q), px != qx, py != qy, p=*Position[px, py], q=*Position[qx,qy].


// Holds if the head of qs is mutually non-attacking with its tail, which should itself be compatible.
// Generated column by column, left to right.
.decl compatible( qs : QueenList, n : number )
compatible([p, nil], 1) :- on_board(p). //, p = [1,y].
compatible([p, [head, tail]], n+1) :- sympathetic(p, head), compatible([p, tail], n), compatible([head, tail], n), p = [px, py], head = [hx, hy], px > hx.
compatible(*QueenList[p, nil], 1) :- on_board(p). //, p = [1,y].
compatible(*QueenList[p, *QueenList[head, tail]], n+1) :- sympathetic(p, head), compatible(*QueenList[p, tail], n), compatible(*QueenList[head, tail], n), p = *Position[px, py], head = *Position[hx, hy], px > hx.
8 changes: 4 additions & 4 deletions tests/evaluation/magic_nqueens/magic_nqueens.err
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Warning: Variable py only occurs once in file magic_nqueens.dl at line 57
compatible([p, [head, tail]], n+1) :- sympathetic(p, head), compatible([p, tail], n), compatible([head, tail], n), p = [px, py], head = [hx, hy], px > hx.
----------------------------------------------------------------------------------------------------------------------------^------------------------------
compatible(*QueenList[p, *QueenList[head, tail]], n+1) :- sympathetic(p, head), compatible(*QueenList[p, tail], n), compatible(*QueenList[head, tail], n), p = *Position[px, py], head = *Position[hx, hy], px > hx.
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------^---------------------------------------
Warning: Variable hy only occurs once in file magic_nqueens.dl at line 57
compatible([p, [head, tail]], n+1) :- sympathetic(p, head), compatible([p, tail], n), compatible([head, tail], n), p = [px, py], head = [hx, hy], px > hx.
---------------------------------------------------------------------------------------------------------------------------------------------^-------------
compatible(*QueenList[p, *QueenList[head, tail]], n+1) :- sympathetic(p, head), compatible(*QueenList[p, tail], n), compatible(*QueenList[head, tail], n), p = *Position[px, py], head = *Position[hx, hy], px > hx.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------^-------------
4 changes: 2 additions & 2 deletions tests/evaluation/magic_records4/magic_records4.dl
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ m(6).
.type pair = [ first : N, second : N ]

.decl p ( p1 : pair, x: N, p2:pair, y:N )
p([x,y], x*y, [x,y], y/x) :- n(x), m(y).
p(*pair[x,y], x*y, *pair[x,y], y/x) :- n(x), m(y).

.decl r ( x1 : N, y1 : N, z1:N, x2 : N, y2 : N, z2:N )
.output r ()
r(x1,y1,z1, x2, y2, z2) :- p([x1,y1], z1, [x2,y2], z2).
r(x1,y1,z1, x2, y2, z2) :- p(*pair[x1,y1], z1, *pair[x2,y2], z2).
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ C(substr("12",22,12)) :- Nullary().
// TODO (lyndonhenry): The code following this comment is a hack to disable the `-e` option, which happens automatically when a program contains record types. The `substr` functor returns an empty string when called with an invalid index. As such, the invalid substring indices used on line 19 of this test will cause a tuple containing the empty string to occur in the relation of `C`, which is otherwise empty. Using the `-efile` and `-m*` options together causes the intermediate IDB file `C+_f.facts` to be written to `output-dir`, where this file contains a single blank line for the tuple with the empty string in `C`. When this same file is then read as intermediate EDB by successor strata, This is not a bug with either the `-efile` or `-m*` options, nor their use together, but rather something else. The issue is that when empty strings are derived as IDB they can be written as output, however empty strings cannot be read as input EDB.
.type T = [ x:number ]
.decl P(x:T)
P([0]).
P(*T[0]).
7 changes: 3 additions & 4 deletions tests/evaluation/rec_lists/rec_lists.dl
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@ n(3).
.decl len ( l : List, n : number )

s(nil).
s( [n,r] ) :- n(n), s(r), len(r,l), l < 5.
s( *List[n,r] ) :- n(n), s(r), len(r,l), l < 5.

len(nil,0).
len( [n,r] , x+1 ) :- s([n,r]), len(r,x).
len( *List[n,r] , x+1 ) :- s(*List[n,r]), len(r,x).


.decl fst ( l : number )
.output fst ()
fst(x) :- s([x,_]).

fst(x) :- s(*List[x,_]).
6 changes: 3 additions & 3 deletions tests/evaluation/rec_lists2/rec_lists2.dl
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

.decl p ( x : list )

p([1,[2,nil]]).
p([2,[3,nil]]).
p(*list[1,*list[2,nil]]).
p(*list[2,*list[3,nil]]).

.decl r ( x : number )
.output r ()
r(a+b) :- p ([a,[b,nil]]).
r(a+b) :- p (*list[a,*list[b,nil]]).
Loading

0 comments on commit 011fd34

Please sign in to comment.