Skip to content

Commit

Permalink
wip: add block comparison between sample definition and its serializa…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
KannarFr committed Apr 19, 2024
1 parent 903ebea commit be696d7
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 29 deletions.
7 changes: 6 additions & 1 deletion src/main/java/org/biscuitsec/biscuit/token/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,16 @@ public String print(SymbolTable symbol_table) {
s.append(this.symbols.symbols);
s.append("\n\t\tcontext: ");
s.append(this.context);
s.append("\n\t\tscopes: [");
for (Scope scope : this.scopes) {
s.append("\n\t\t\t");
s.append(symbol_table.print_scope(scope));
}
if(this.externalKey.isDefined()) {
s.append("\n\t\texternal key: ");
s.append(this.externalKey.get().toString());
}
s.append("\n\t\tfacts: [");
s.append("\n\t\t]\n\t\tfacts: [");
for (Fact f : this.facts) {
s.append("\n\t\t\t");
s.append(symbol_table.print_fact(f));
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/org/biscuitsec/biscuit/token/builder/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,6 @@ public org.biscuitsec.biscuit.token.Block build() {
publicKeys.add(this.symbols.publicKeys().get(i));
}

publicKeys.addAll(this.publicKeys);

SchemaVersion schemaVersion = new SchemaVersion(this.facts, this.rules, this.checks, this.scopes);

return new org.biscuitsec.biscuit.token.Block(symbols, this.context, this.facts, this.rules, this.checks,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,9 @@ public static Either<Error, Tuple2<String, Expression.Op>> binary_op6(String s)
}

public static Either<Error, Tuple2<String, Expression.Op>> binary_op7(String s) {
if(s.startsWith("intersection")) {
return Either.right(new Tuple2<>(s.substring(12), Expression.Op.Intersection));
}
if(s.startsWith("contains")) {
return Either.right(new Tuple2<>(s.substring(8), Expression.Op.Contains));
}
Expand Down
125 changes: 112 additions & 13 deletions src/main/java/org/biscuitsec/biscuit/token/builder/parser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
import java.util.function.Function;

public class Parser {
public static Either<Map<Integer, List<Error>>, Block> datalog(long index, SymbolTable baseSymbols, String s) {
return datalog(index, baseSymbols, null, s);
}

/**
* Takes a datalog string with <code>\n</code> as datalog line separator. It tries to parse
* each line using fact, rule, check and scope sequentially.
Expand All @@ -25,26 +29,82 @@ public class Parser {
*
* @param index block index
* @param baseSymbols symbols table
* @param blockSymbols block's custom symbols table (added to baseSymbols)
* @param s datalog string to parse
* @return Either<Map<Integer, List<Error>>, Block>
*/
public static Either<Map<Integer, List<Error>>, Block> datalog(long index, SymbolTable baseSymbols, String s) {
public static Either<Map<Integer, List<Error>>, Block> datalog(long index, SymbolTable baseSymbols, SymbolTable blockSymbols, String s) {
Block blockBuilder = new Block(index, baseSymbols);
Map<Integer, List<Error>> errors = new HashMap<>();

Stream.of(s.split("\n")).zipWithIndex().forEach(indexedLine -> {
Integer lineNumber = indexedLine._2;
String codeLine = indexedLine._1;
List<Error> lineErrors = new ArrayList<>();
// empty block code
if (s.isEmpty()) {
return Either.right(blockBuilder);
}

fact(codeLine).bimap(lineErrors::add, r -> r._2).map(blockBuilder::add_fact);
rule(codeLine).bimap(lineErrors::add, r -> r._2).map(blockBuilder::add_rule);
check(codeLine).bimap(lineErrors::add, r -> r._2).map(blockBuilder::add_check);
scope(codeLine).bimap(lineErrors::add, r -> r._2).map(blockBuilder::add_scope);
if (blockSymbols != null) {
blockSymbols.symbols.forEach(blockBuilder::addSymbol);
}

if (lineErrors.size() > 3) {
errors.put(lineNumber, lineErrors);
}
Map<Integer, List<Error>> errors = new HashMap<>();

s = removeCommentsAndWhitespaces(s);
String[] codeLines = s.split(";");

Stream.of(codeLines)
.zipWithIndex()
.forEach(indexedLine -> {
String code = indexedLine._1.strip();

if (!code.isEmpty()) {
int lineNumber = indexedLine._2;
System.out.println("NEW CODE LINE");
System.out.println(code);
List<Error> lineErrors = new ArrayList<>();

boolean parsed = false;
parsed = rule(code).fold(e -> {
lineErrors.add(e);
return false;
}, r -> {
blockBuilder.add_rule(r._2);
return true;
});

if (!parsed) {
parsed = scope(code).fold(e -> {
lineErrors.add(e);
return false;
}, r -> {
blockBuilder.add_scope(r._2);
return true;
});
}

if (!parsed) {
parsed = fact(code).fold(e -> {
lineErrors.add(e);
return false;
}, r -> {
blockBuilder.add_fact(r._2);
return true;
});
}

if (!parsed) {
parsed = check(code).fold(e -> {
lineErrors.add(e);
return false;
}, r -> {
blockBuilder.add_check(r._2);
return true;
});
}

if (!parsed) {
lineErrors.forEach(System.out::println);
errors.put(lineNumber, lineErrors);
}
}
});

if (!errors.isEmpty()) {
Expand Down Expand Up @@ -709,4 +769,43 @@ public static Tuple2<String, String> take_while(String s, Function<Character, Bo

return new Tuple2<>(s.substring(0, index), s.substring(index));
}

public static String removeCommentsAndWhitespaces(String s) {
s = removeComments(s);
s = s.replace("\n", "").replace("\\\"", "\"").strip();
return s;
}

public static String removeComments(String str) {
StringBuilder result = new StringBuilder();
String remaining = str;

while (!remaining.isEmpty()) {
remaining = space(remaining); // Skip leading whitespace
if (remaining.startsWith("/*")) {
// Find the end of the multiline comment
remaining = remaining.substring(2); // Skip "/*"
String finalRemaining = remaining;
Tuple2<String, String> split = take_while(remaining, c -> !finalRemaining.startsWith("*/"));
remaining = split._2.length() > 2 ? split._2.substring(2) : ""; // Skip "*/"
} else if (remaining.startsWith("//")) {
// Find the end of the single-line comment
remaining = remaining.substring(2); // Skip "//"
Tuple2<String, String> split = take_while(remaining, c -> c != '\n' && c != '\r');
remaining = split._2;
if (!remaining.isEmpty()) {
result.append(remaining.charAt(0)); // Preserve line break
remaining = remaining.substring(1);
}
} else {
// Take non-comment text until the next comment or end of string
String finalRemaining = remaining;
Tuple2<String, String> split = take_while(remaining, c -> !finalRemaining.startsWith("/*") && !finalRemaining.startsWith("//"));
result.append(split._1);
remaining = split._2;
}
}

return result.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,23 @@ void testRuleWithExpressionOrdering() {
res);
}

@Test
void expressionIntersectionAndContainsTest() {
Either<Error, Tuple2<String, Expression>> res =
Parser.expression("[1, 2, 3].intersection([1, 2]).contains(1)");

assertEquals(Either.right(new Tuple2<>("",
new Expression.Binary(
Expression.Op.Contains,
new Expression.Binary(
Expression.Op.Intersection,
new Expression.Value(Utils.set(new HashSet<>(Arrays.asList(Utils.integer(1), Utils.integer(2), Utils.integer(3))))),
new Expression.Value(Utils.set(new HashSet<>(Arrays.asList(Utils.integer(1), Utils.integer(2)))))
),
new Expression.Value(Utils.integer(1))
))), res);
}

@Test
void ruleWithFreeExpressionVariables() {
Either<Error, Tuple2<String, Rule>> res =
Expand Down Expand Up @@ -366,11 +383,11 @@ void testParens() throws org.biscuitsec.biscuit.error.Error.Execution {
void testDatalogSucceeds() throws org.biscuitsec.biscuit.error.Error.Parser {
SymbolTable symbols = Biscuit.default_symbol_table();

String l1 = "fact1(1)";
String l1 = "fact1(1, 2)";
String l2 = "fact2(\"2\")";
String l3 = "rule1(2) <- fact2(\"2\")";
String l4 = "check if rule1(2)";
String toParse = String.join("\n", Arrays.asList(l1, l2, l3, l4));
String toParse = String.join(";", Arrays.asList(l1, l2, l3, l4));

Either<Map<Integer, List<Error>>, Block> output = Parser.datalog(1, symbols, toParse);
assertTrue(output.isRight());
Expand All @@ -392,9 +409,28 @@ void testDatalogFailed() {

String l1 = "fact(1)";
String l2 = "check fact(1)"; // typo missing "if"
String toParse = String.join("\n", Arrays.asList(l1, l2));
String toParse = String.join(";", Arrays.asList(l1, l2));

Either<Map<Integer, List<Error>>, Block> output = Parser.datalog(1, symbols, toParse);
assertTrue(output.isLeft());
}

@Test
void testDatalogRemoveComment() throws org.biscuitsec.biscuit.error.Error.Parser {
SymbolTable symbols = Biscuit.default_symbol_table();

String l0 = "// test comment";
String l1 = "fact1(1, 2);";
String l2 = "fact2(\"2\");";
String l3 = "rule1(2) <- fact2(\"2\");";
String l4 = "// another comment";
String l5 = "/* test multiline";
String l6 = "comment */ check if rule1(2);";
String l7 = " /* another multiline";
String l8 = "comment */";
String toParse = String.join("", Arrays.asList(l0, l1, l2, l3, l4, l5, l6, l7, l8));

Either<Map<Integer, List<Error>>, Block> output = Parser.datalog(1, symbols, toParse);
assertTrue(output.isRight());
}
}
24 changes: 24 additions & 0 deletions src/test/java/org/biscuitsec/biscuit/datalog/ExpressionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;

public class ExpressionTest {

Expand Down Expand Up @@ -114,4 +115,27 @@ public void testNegativeContainsStr() throws Error.Execution {
e.evaluate(new HashMap<>(), new TemporarySymbolTable(symbols))
);
}

@Test
public void testIntersectionAndContains() throws Error.Execution {
SymbolTable symbols = new SymbolTable();

Expression e = new Expression(new ArrayList<Op>(Arrays.asList(
new Op.Value(new Term.Set(new HashSet<>(Arrays.asList(new Term.Integer(1), new Term.Integer(2), new Term.Integer(3))))),
new Op.Value(new Term.Set(new HashSet<>(Arrays.asList(new Term.Integer(1), new Term.Integer(2))))),
new Op.Binary(Op.BinaryOp.Intersection),
new Op.Value(new Term.Integer(1)),
new Op.Binary(Op.BinaryOp.Contains)
)));

assertEquals(
"[1, 2, 3].intersection([1, 2]).contains(1)",
e.print(symbols).get()
);

assertEquals(
new Term.Bool(true),
e.evaluate(new HashMap<>(), new TemporarySymbolTable(symbols))
);
}
}
Loading

0 comments on commit be696d7

Please sign in to comment.