Skip to content

Commit e02bb4c

Browse files
committed
Merge branch 'misc/loose-ends'
2 parents f5f4d56 + c575680 commit e02bb4c

File tree

15 files changed

+203
-78
lines changed

15 files changed

+203
-78
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,13 @@ jobs:
129129
echo 'config solver="sat4j"' >> $line
130130
done
131131
- name: SAT4j part 1
132-
run: java -jar logic-compiler.jar sat/problem_sat.logic
132+
run: java -jar logic-compiler.jar sat/problem_sat.logic result.txt
133133
- name: Check for correct output 1
134134
run: diff result.txt sat/solution_sat.txt # will fail if diffs
135135
- name: Quick cleanup
136136
run: rm result.txt
137137
- name: SAT4j part 2
138-
run: java -jar logic-compiler.jar sat/problem_unsat.logic
138+
run: java -jar logic-compiler.jar sat/problem_unsat.logic result.txt
139139
- name: Check for correct output 2
140140
run: diff result.txt sat/solution_unsat.txt
141141

README.md

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,18 @@
1-
# Logic app
2-
(better name needed)
1+
# CAIR Logic Platform
32

43
## Purpose
54
The goal of this repo is to provide a platform for generic logic operations (checking for consistency, validity, explanations, etc.) by way of an easy-to-use DSL.
65

76
## Mission
87
The purpose is achieved by way of three parts:
98
- The DSL specification and generator (`za.org.cair.logic_app`)
10-
- An IDE plugin (`za.org.cair.logic_app.ide`)
9+
- An IDE plugin (`za.org.cair.logic_app.ide`) (Not implemented)
1110
- A webapp editor (`za.org.cair.logic_app.web`)
1211

1312
## Usage
1413

15-
### On the CLI
16-
17-
Xtext compilers are not typically used on the command like (like e.g. `gcc` and `javac`) so support (especially in CI contexts) is very limited. Through some shady hacks I got it working (for now).
18-
19-
Download the compiler jar from the [CLI logic lang compiler workflow](https://github.com/Koellewe/logic-app/actions?query=workflow%3A%22Build+CLI+logic+lang+compiler%22) build artifacts. Create a file written in the specified logic language (with `.logic` as file extension). Compile as follows:
20-
21-
```sh
22-
java -jar logic-compiler.jar example.logic
23-
```
24-
25-
Ignore the reflection warnings. They are caused in Java 11 onwards by a lib xtext depends on and will be updated once the developers there get their act together. [More info](https://github.com/eclipse/xtext-core/issues/506).
26-
27-
28-
### As library / on Web / in IDE
29-
30-
TODO
14+
See the [wiki entry](https://github.com/Koellewe/logic-app/wiki/Running-and-Deployment).
3115

16+
## More info
3217

18+
See the [wiki](https://github.com/Koellewe/logic-app/wiki).

za.org.cair.logic_app/src/main/java/za/org/cair/logic_app/LogicLangHelper.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package za.org.cair.logic_app;
22

3+
import java.util.Random;
4+
35
import org.eclipse.emf.mwe2.language.mwe2.impl.BooleanLiteralImplCustom;
46

57
import za.org.cair.logic_app.logicLang.BooleanLiteral;
@@ -19,6 +21,9 @@
1921
import za.org.cair.logic_app.logicLang.impl.NegationImpl;
2022

2123
public class LogicLangHelper {
24+
25+
// used for #randomHash()
26+
private static Random rand = new Random();
2227

2328
/**
2429
* Checks if the sentence is complex (i.e. has a left and right side)
@@ -165,4 +170,11 @@ public static Sentence copyOf(Sentence sent) {
165170
}
166171
}
167172

173+
/**
174+
* Create a random hash. Useful for e.g. concurrent filesystem access
175+
*/
176+
public static String randomHash(int basis) {
177+
return Integer.toHexString(basis * (rand.nextInt()+1));
178+
}
179+
168180
}

za.org.cair.logic_app/src/main/java/za/org/cair/logic_app/external/Sat4j.xtend

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import java.io.BufferedWriter
1010
import java.util.HashMap
1111
import java.io.BufferedReader
1212
import java.io.InputStreamReader
13+
import za.org.cair.logic_app.LogicLangHelper
1314

1415
class Sat4j {
1516

1617
val SAT4J_JAR = "sat4j.jar"
17-
val DIMACS_FILE = "dimacs.cnf"
1818

1919
List<Proposition> inputProps
2020

@@ -35,9 +35,11 @@ class Sat4j {
3535
*/
3636
def Pair<Boolean, Map<String, Boolean>> solve(){
3737

38+
val dimacsFile = LogicLangHelper.randomHash(inputProps.hashCode())+".cnf";
39+
3840
// create dimacs file
3941
val dimacsContent = new CNFConverter().convertToDIMACS(inputProps)
40-
val writer = new BufferedWriter(new FileWriter("dimacs.cnf"))
42+
val writer = new BufferedWriter(new FileWriter(dimacsFile))
4143
writer.write(dimacsContent)
4244
writer.close()
4345

@@ -53,7 +55,7 @@ class Sat4j {
5355
}
5456

5557
// run sat4j
56-
val proc = Runtime.runtime.exec("java -jar "+SAT4J_JAR+" "+DIMACS_FILE)
58+
val proc = Runtime.runtime.exec("java -jar "+SAT4J_JAR+" "+dimacsFile)
5759
if (proc.errorStream.read != -1){
5860
throw new RuntimeException("Sat4j failed to solve.")
5961
}
@@ -86,7 +88,7 @@ class Sat4j {
8688
}
8789

8890
// clean up
89-
new File(DIMACS_FILE).delete()
91+
new File(dimacsFile).delete()
9092

9193
// finish
9294
return Pair.of(satis, solution)

za.org.cair.logic_app/src/main/java/za/org/cair/logic_app/generator/Main.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package za.org.cair.logic_app.generator;
22

3+
import java.io.BufferedWriter;
4+
import java.io.File;
5+
import java.io.FileWriter;
6+
import java.io.IOException;
37
import java.util.Iterator;
48
import java.util.List;
59
import java.util.Map.Entry;
@@ -20,6 +24,7 @@
2024
import com.google.inject.Injector;
2125
import com.google.inject.Provider;
2226

27+
import za.org.cair.logic_app.LogicLangHelper;
2328
import za.org.cair.logic_app.LogicLangStandaloneSetup;
2429

2530
public class Main {
@@ -96,4 +101,30 @@ protected void runGenerator(String[] args) {
96101

97102
}
98103
}
104+
105+
/**
106+
* These methods are used to run validation on some source code.
107+
* They are needed, because for some bizarre reason validation doesn't
108+
* work in JUnit 5 test context.
109+
* @param src source code
110+
* @return A set of warnings and errors produced by the validator.
111+
* @throws IOException If there was an error creating or deleting temporary files.
112+
*/
113+
public static List<Issue> justValidate(String src) throws IOException{
114+
Injector injector = new LogicLangStandaloneSetup().createInjectorAndDoEMFRegistration();
115+
Main main = injector.getInstance(Main.class);
116+
return main.internalJustValidate(src);
117+
}
118+
119+
private List<Issue> internalJustValidate(String src) throws IOException{
120+
String tmpFile = LogicLangHelper.randomHash(src.hashCode())+".logic";
121+
BufferedWriter writer = new BufferedWriter(new FileWriter(tmpFile));
122+
writer.write(src);
123+
writer.close();
124+
125+
Resource resource = resourceSetProvider.get().getResource(URI.createFileURI(tmpFile), true);
126+
List<Issue> issues = validator.validate(resource, CheckMode.ALL, CancelIndicator.NullImpl);
127+
new File(tmpFile).delete();
128+
return issues;
129+
}
99130
}

za.org.cair.logic_app/src/main/java/za/org/cair/logic_app/validation/CommandValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public void checkForCommands(Model model) {
2020
if(model.getCommands().size() == 0) {
2121
warning("No commands specified. Nothing to do.",
2222
LogicLangPackage.Literals.MODEL__COMMANDS,
23-
LogicLangValidator.NO_COMMANDS);
23+
LogicLangValidator.ISSUE_NO_COMMANDS);
2424
}
2525
}
2626

za.org.cair.logic_app/src/main/java/za/org/cair/logic_app/validation/ConfigValidator.java

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
package za.org.cair.logic_app.validation;
22

3+
import java.util.HashSet;
4+
35
import org.eclipse.xtext.util.Arrays;
46
import org.eclipse.xtext.validation.AbstractDeclarativeValidator;
57
import org.eclipse.xtext.validation.Check;
68
import org.eclipse.xtext.validation.CheckType;
79
import org.eclipse.xtext.validation.EValidatorRegistrar;
810

11+
import za.org.cair.logic_app.LogicLangHelper;
12+
import za.org.cair.logic_app.logicLang.BooleanLiteral;
913
import za.org.cair.logic_app.logicLang.Command;
1014
import za.org.cair.logic_app.logicLang.Config;
1115
import za.org.cair.logic_app.logicLang.ConfigKey;
1216
import za.org.cair.logic_app.logicLang.LogicLangPackage;
1317
import za.org.cair.logic_app.logicLang.Model;
18+
import za.org.cair.logic_app.logicLang.Negation;
19+
import za.org.cair.logic_app.logicLang.Proposition;
20+
import za.org.cair.logic_app.logicLang.Sentence;
1421
import za.org.cair.logic_app.logicLang.SolutionRequest;
1522
import za.org.cair.logic_app.logicLang.SolveCommand;
1623

1724
public class ConfigValidator extends AbstractDeclarativeValidator {
1825

26+
// supported SAT solvers to be listed here
1927
private static String[] SOLVERS = new String[] {"sat4j"};
2028

2129
@Override
@@ -25,16 +33,26 @@ public void register(EValidatorRegistrar registrar) {
2533

2634
@Check(CheckType.NORMAL)
2735
public void checkConfigUnique(Model model) {
28-
// TODO
36+
HashSet<ConfigKey> configs = new HashSet<>();
37+
for (Config cfg : model.getConfig()) {
38+
if (configs.contains(cfg.getKey())) { // duplicate key
39+
error("Duplicate config item for key: "+cfg.getKey().getName(),
40+
cfg, LogicLangPackage.Literals.CONFIG__KEY,
41+
LogicLangValidator.ISSUE_CONFIG_DUPE);
42+
} else {
43+
configs.add(cfg.getKey());
44+
}
45+
}
2946
}
3047

3148
@Check(CheckType.NORMAL)
3249
public void checkConfig(Config cfg) {
3350
if (cfg.getKey() == ConfigKey.SOLVER) {
51+
// check that solver is valid
3452
if (!Arrays.contains(SOLVERS, cfg.getValue())) {
3553
error("Solver '"+cfg.getValue()+"' not supported.",
3654
cfg, LogicLangPackage.Literals.CONFIG__VALUE,
37-
LogicLangValidator.CONFIG_ISSUE);
55+
LogicLangValidator.ISSUE_UNSUPPORTED_SOLVER);
3856
}
3957
}// more cfg checks here
4058
}
@@ -62,9 +80,34 @@ public void checkSolverSpecified(Model model) {
6280
if (!aptConfig) {
6381
error("No solver specified in config.",
6482
commandInQuestion, LogicLangPackage.Literals.SOLVE_COMMAND__WHAT,
65-
LogicLangValidator.SOLVER_ISSUE);
83+
LogicLangValidator.ISSUE_NO_SOLVER);
6684
}
85+
86+
// check for boolean literals
87+
for (Proposition prop : model.getPropositions()) {
88+
warnBooleanLiteral(prop.getSentence());
89+
}
90+
6791
}
6892
}
6993

94+
/**
95+
* Recursively find boolean literals and throw a warning for each.
96+
* Rationale: SAT solvers interpret "True" and "False" as variables and
97+
* so may end up assigning True=False to find satisfiability. Best to do
98+
* some math and drop the literals.
99+
*/
100+
private void warnBooleanLiteral(Sentence sent) {
101+
if (sent instanceof BooleanLiteral) {
102+
warning("Do not use boolean literals with SAT solving", sent,
103+
LogicLangPackage.Literals.BOOLEAN_LITERAL__TRUTH,
104+
LogicLangValidator.ISSUE_SAT_BOOL);
105+
}else if (LogicLangHelper.isComplexSentence(sent)) {
106+
warnBooleanLiteral(LogicLangHelper.getLeftSide(sent));
107+
warnBooleanLiteral(LogicLangHelper.getRightSide(sent));
108+
}else if (sent instanceof Negation) {
109+
warnBooleanLiteral(((Negation) sent).getExpression());
110+
} // else BooleanVariable
111+
}
112+
70113
}

za.org.cair.logic_app/src/main/java/za/org/cair/logic_app/validation/LogicLangValidator.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,21 @@
2323
public class LogicLangValidator extends AbstractLogicLangValidator {
2424

2525
// List all validation issue codes here:
26-
public static final String NO_PROPOSITIONS = "noPropositions";
27-
public static final String NO_COMMANDS = "noCommands";
28-
public static final String INCONSISTENT_SYMBOLOGY = "inconsistentSymbology";
29-
public static final String CONFIG_ISSUE = "configIssue";
30-
public static final String SOLVER_ISSUE = "solverIssue";
26+
public static final String ISSUE_NO_PROPOSITIONS = "noPropositions";
27+
public static final String ISSUE_NO_COMMANDS = "noCommands";
28+
public static final String ISSUE_INCONSISTENT_SYMBOLOGY = "inconsistentSymbology";
29+
public static final String ISSUE_CONFIG_DUPE = "configDuplicate";
30+
public static final String ISSUE_UNSUPPORTED_SOLVER = "unsupportedSolver";
31+
public static final String ISSUE_NO_SOLVER = "noSolver";
32+
public static final String ISSUE_SAT_BOOL = "satBool";
3133

3234
@Check(CheckType.NORMAL) // only check at build-time
3335
public void checkForPropositions(Model model) {
3436
if (model.getPropositions().size() == 0) {
3537
warning("No propositions specified. No input to work on.",
3638
// Note: LogicLangPackage stuff is auto-generated from the grammar
3739
LogicLangPackage.Literals.MODEL__PROPOSITIONS,
38-
LogicLangValidator.NO_PROPOSITIONS);
40+
LogicLangValidator.ISSUE_NO_PROPOSITIONS);
3941
}
4042
}
4143

za.org.cair.logic_app/src/main/java/za/org/cair/logic_app/validation/VariantValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ private void checkSymbolInconsistency(AtomicBoolean checkingStarted, AtomicBoole
9292
if (thisVariant != runningVariant.get())
9393
warning("Inconsistent logic symbology", where,
9494
appropriateRef(where),
95-
LogicLangValidator.INCONSISTENT_SYMBOLOGY);
95+
LogicLangValidator.ISSUE_INCONSISTENT_SYMBOLOGY);
9696
// either case do nothing (continue checking)
9797

9898
}else { // this is the first ever check: set this variant to the running variant
Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,46 @@
11
package za.org.cair.logic_app.tests
22

3-
import org.junit.jupiter.api.^extension.ExtendWith
4-
import org.eclipse.xtext.testing.extensions.InjectionExtension
53
import org.eclipse.xtext.testing.InjectWith
6-
import javax.inject.Inject
7-
import org.eclipse.xtext.testing.util.ParseHelper
8-
import za.org.cair.logic_app.logicLang.Model
9-
import static org.junit.Assert.assertNotNull
4+
import org.eclipse.xtext.testing.extensions.InjectionExtension
105
import org.junit.jupiter.api.Test
11-
//import org.eclipse.xtext.testing.validation.ValidationTestHelper
12-
//import za.org.cair.logic_app.logicLang.LogicLangPackage
13-
//import za.org.cair.logic_app.validation.LogicLangValidator
6+
import org.junit.jupiter.api.^extension.ExtendWith
7+
import za.org.cair.logic_app.validation.LogicLangValidator
148

159
@ExtendWith(InjectionExtension)
1610
@InjectWith(LogicLangInjectorProvider)
1711
class CommandsTest {
18-
19-
@Inject
20-
ParseHelper<Model> parseHelper
21-
22-
// @Inject
23-
// ValidationTestHelper validationHelper
2412

2513
@Test
2614
def void testNoCommands() {
27-
val result = parseHelper.parse('''
15+
16+
val src = '''
2817
prop A | B
2918
prop C -> D
30-
''')
31-
assertNotNull(result)
32-
// val valres = validationHelper.validate(result)
33-
// println('valresses: ' + valres.size)
34-
//
35-
// TestingHelper.assertWarning(result, LogicLangValidator.NO_COMMANDS)
36-
37-
// TODO: validation not working yet. Removing this test for now.
19+
'''
20+
21+
TestingHelper.assertIssue(src, LogicLangValidator.ISSUE_NO_COMMANDS);
3822

3923
}
24+
25+
@Test
26+
def void satNoSolver(){
27+
TestingHelper.assertIssue('''
28+
prop Ayy | Bee
29+
cmd solve satisfiability
30+
''',
31+
LogicLangValidator.ISSUE_NO_SOLVER
32+
)
33+
}
34+
35+
@Test
36+
def void satBool(){
37+
TestingHelper.assertIssue('''
38+
prop var1 & True
39+
cmd solve satisfiability
40+
config solver='sat4j'
41+
''',
42+
LogicLangValidator.ISSUE_SAT_BOOL
43+
)
44+
}
45+
4046
}

0 commit comments

Comments
 (0)