├── LICENSE ├── README.md ├── back-end ├── assembly.xml ├── input ├── lib │ ├── antlr-4.7.1-complete.jar │ ├── automaton.jar │ ├── com.microsoft.z3.jar │ ├── javatuples-1.2.jar │ ├── libz3.dylib │ ├── libz3java.dylib │ ├── libz3java.so │ ├── resnax.jar │ ├── win32 │ │ ├── Microsoft.Z3.deps.json │ │ ├── Microsoft.Z3.dll │ │ ├── com.microsoft.z3.jar │ │ ├── libz3.dll │ │ ├── libz3.lib │ │ ├── libz3java.dll │ │ ├── libz3java.lib │ │ ├── msvcp140.dll │ │ ├── msvcp140_1.dll │ │ ├── msvcp140_2.dll │ │ ├── vcomp140.dll │ │ └── z3.exe │ └── win64 │ │ ├── Microsoft.Z3.deps.json │ │ ├── Microsoft.Z3.dll │ │ ├── com.microsoft.z3.jar │ │ ├── libz3.dll │ │ ├── libz3.lib │ │ ├── libz3java.dll │ │ ├── libz3java.lib │ │ ├── msvcp140.dll │ │ ├── msvcp140_1.dll │ │ ├── msvcp140_2.dll │ │ ├── vcomp140.dll │ │ └── z3.exe ├── pom.xml └── src │ ├── .DS_Store │ ├── main │ └── java │ │ └── edu │ │ └── harvard │ │ └── seas │ │ └── synthesis │ │ ├── CoverageDrivenInputGenerator.java │ │ ├── Example.java │ │ ├── ExampleBasedInputGenerator.java │ │ ├── ExplanationGenerator.java │ │ ├── HTTPServer.java │ │ ├── Regex.java │ │ ├── ResnaxRunner.java │ │ ├── ServerCommandLineParser.java │ │ ├── SynthesisServer.java │ │ ├── SynthesisServerHandler.java │ │ └── logging │ │ └── SynthesisLogger.java │ └── test │ ├── .DS_Store │ └── java │ ├── .DS_Store │ └── edu │ ├── .DS_Store │ └── harvard │ ├── .DS_Store │ └── seas │ ├── .DS_Store │ └── synthesis │ ├── CoverageDrivenInputGenerationTest.java │ ├── InputGeneratorTest.java │ ├── ResnaxRunnerTest.java │ └── ServerCommandParserTest.java ├── front-end ├── comparison.html ├── functions.js ├── index.html ├── my-style.css └── my-table.css └── package.sh /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tianyi Zhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Interactive Program Synthesis by Augmented Examples 3 | 4 | This repository contains the source code of an interactive program synthesizer for regular expressions. It is implemented based on the interaction model proposed in our UIST 2020 paper *[Interactive Program Synthesis by Augmented Examples](https://tianyi-zhang.github.io/files/uist2020-interactive-program-synthesis.pdf)*. Users can guide the synthesis process by specifying how different parts of their examples should be treated by a synthesizer via light-weight annotations (i.e., *semantic augmentation*). Furthermore, users can ask the synthesizer to generate additional examples and corner cases to generate by revealing how a synthesized program behave on additional examples from a hypothetical input space (i.e., *data augmentation*). 5 | 6 | ## Video Tutorial ([link](https://www.youtube.com/watch?v=dMyd9i31NxE)) 7 | 8 | ## Software Prerequisites 9 | 1. Java 1.8 or higher ([download](https://www.java.com/en/download/)) 10 | 11 | **Note:** Use `java --version` to check the versions. We recommend using the 64-bit version of Java so we can allocate more memory to the program synthesizer that runs in a JVM. If you use a 32-bit version, we can only allocate a maximum amount of 4G memory to the synthesizer theorectically. In practice, the actual allocated memory could be as low as 1G. When downloading Java, please try to download the distribution or installer with `x64` in its name (not `x86`). 12 | 13 | ## Install from Pre-built Distribution 14 | 15 | 1. Download our software distribution [here](https://drive.google.com/file/d/1__OffkKOnTEYXQHBAQM94dfKBrtB1ilE/view?usp=sharing). 16 | 2. Unzip the downloaded file. 17 | 3. In terminal, go into the unzipped folder and start the server. 18 | `java -cp ips-backend.jar edu.harvard.seas.synthesis.HTTPServer -s lib/` 19 | 4. Open `http://localhost:8080` in your web browser. 20 | 21 | **Note1:** Don't forget to add a backslash to escape a whitespace if your file path contains a whitespace. 22 | 23 | ## Install from Source Code 24 | 25 | 1. Clone this project. 26 | 2. Import the `back-end` folder into Eclipse as an existing Maven project ([instruction](https://vaadin.com/learn/tutorials/import-maven-project-eclipse)). 27 | 3. In Eclipse, add `-s lib/` as the runtime commandline argument of the `HTTPServer` class ([Tutorial: How to add a commandline argument in Eclipse](https://www.codejava.net/ides/eclipse/how-to-pass-arguments-when-running-a-java-program-in-eclipse)). 28 | 4. Run `HTTPServer` to start the server. 29 | 5. Open `http://localhost:8080` in your web browser.ser. 30 | 31 | **Note1:** We use Eclipse for development, so the instructions above are based on Eclipse. You can also use other IDEs such as [IntelliJ](https://www.lagomframework.com/documentation/1.6.x/java/IntellijMaven.html). We recommend using a modern IDE since it is easier to run and debug. 32 | 33 | **Note2:** If you want to build the project from a terminal, run `mvn package` to build and package the project. A jar of the back-end server is generated in the `target` folder. Then run the jar following Step 3-5 in the next section. 34 | 35 | **Note3:** Run `sh package.sh` to build the distribution. 36 | 37 | ## Backend Server Usage 38 | **Usage:** 39 | `java -jar ips-backend.jar -s [-n ] [-t ] [-h]` 40 | 41 | **Options:** 42 | 43 | -s,--synthesizer (Required) specify the path for the program synthesis libraries 44 | 45 | -n,--example-num (Optional) specify the number of input examples generated per cluster per example seed. The default value is 5. 46 | 47 | -t,--timeout (Optional) specify the timeout for the synthesis. The default value is 60 seconds. 48 | 49 | -h,--help Print the help information. 50 | 51 | 52 | ## Troubleshooting 53 | 1. In Mac, you may see the following error. 54 | ```libz3java.dylib cannot be opened because it is from an unidentified developer.``` 55 | The underlying synthesizer in our tool depends on a theorem prover, [Z3](https://github.com/Z3Prover/z3), developed by Microsoft Research. Please grant the permission to this app by 1) open System Preferences, 2) click Security & Privacy, 3) click General, and 4) click "Open Anyway" next to the warning of this app. 56 | 57 | 2. Our synthesizer only works with Z3 4.8.9 or lower. The newer Z3 has changed their class signatures and are no longer compatible with our code. 58 | 59 | 60 | 3. If the synthesis progress bar is stuck at 96% for a while (e.g., more than 2 minutes), it is likely that Z3 libraries are not found in your machine. To confirm this, please a) check if there is a `resnax-error` file, b) check if `resnax-error` has exceptions like `Exception in thread "main" java.lang.UnsatisfiedLinkError: ...\libz3java.dll: Can't find dependent libraries`. By default, I have included the Z3 libraries for Windows/Linux/Mac in the `lib` folder and these libraries will be dynamically linked and loaded to run Z3. Yet in some versions of OS, this default mechanism doesn't work, causing errors like `UnsatisfiedLinkError`. I have found several workarounds. Please try each of them one by one until the problem is solved. If none of the following solutions work, please please feel free to contact me (tianyi@g.harvard.edu) and I am happy to help. 61 | - (Mac OS) If you are a Mac user, Mac OS has some restrictions on loading dynamic libraries and such restrictions have subtle differences across Mac OS versions. According to the [offical doc from Apple](https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/DynamicLibraryUsageGuidelines.html#//apple_ref/doc/uid/TP40001928-SW21), the working directory of the current process, `/usr/lib/`, `/usr/local/lib`, `~/lib` are the several locations MacOS searches for. Yet different MacOS versions may purge dynamic libraries from some of these directories due to System Integrity Protection. For example, in Mac OSX Catalina 10.15.7, only copying Z3 libraries (`libz3java.dylib`, `com.microsoft.z3.jar`, and `libz3.dylib`) to the current directory (i.e., the `ips` folder) works. But in some other Mac OS versions such as Mojave and High Sierra, copying these files to `/usr/local/lib` works. So I recommend you to try to copy these Z3 libraries to each of the paths until the `UnsatisfiedLinkError` issue is solved. 62 | - (Linux) The default mechanism works in Ubuntu 14.04 and 18.04 (haven't tested 16.04 yet). But I run into this `UnsatisfiedLinkError` when installing our tool in Amazon Linux AMI. It seems Amazon EC2 doesn't allow to set custom dynamic library paths. The only way I can solve it is by placing those Z3 libraries in the default LD_LIBRARY_PATH, e.g., `/usr/lib`, `/usr/lib64`, and also removing the `Djava.library.path` argument in [this line of code](https://github.com/tianyi-zhang/interactive-program-synthesis/blob/main/back-end/src/main/java/edu/harvard/seas/synthesis/ResnaxRunner.java#L278). If your machine environment also does not allow you to set custome dynamic library paths, this solution is worth trying first. 63 | - If none of the solutions above work on your machine, maybe it's because none of the Z3 distributions included in our tool is compatible with your machine. In such a case, I recommend you to manually install Z3 and its Java bindings following the instructions [here](https://github.com/Z3Prover/z3). I also copied the essential steps here. 64 | ```bash 65 | python scripts/mk_make.py --java ### Please include the --java flag to build Java bindings 66 | cd build 67 | make 68 | sudo make install 69 | ``` 70 | 71 | To test if Z3 is successfully installed, please run `z3` in command line. 72 | -------------------------------------------------------------------------------- /back-end/assembly.xml: -------------------------------------------------------------------------------- 1 | 4 | jar-with-dependencies 5 | 6 | jar 7 | 8 | false 9 | 10 | 11 | / 12 | true 13 | true 14 | runtime 15 | 16 | 17 | / 18 | true 19 | system 20 | 21 | 22 | -------------------------------------------------------------------------------- /back-end/input: -------------------------------------------------------------------------------- 1 | "ABBCCAABBC",+ 2 | "ABCAAAAAA",+ 3 | "ABCAAAAAA",+ 4 | "A",+ 5 | "ABBC",+ 6 | "ABBDF",- 7 | "ASD",- 8 | "",- 9 | 10 | // ground truth 11 | repeatatleast(or(,or(,)),1) -------------------------------------------------------------------------------- /back-end/lib/antlr-4.7.1-complete.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/antlr-4.7.1-complete.jar -------------------------------------------------------------------------------- /back-end/lib/automaton.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/automaton.jar -------------------------------------------------------------------------------- /back-end/lib/com.microsoft.z3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/com.microsoft.z3.jar -------------------------------------------------------------------------------- /back-end/lib/javatuples-1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/javatuples-1.2.jar -------------------------------------------------------------------------------- /back-end/lib/libz3.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/libz3.dylib -------------------------------------------------------------------------------- /back-end/lib/libz3java.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/libz3java.dylib -------------------------------------------------------------------------------- /back-end/lib/libz3java.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/libz3java.so -------------------------------------------------------------------------------- /back-end/lib/resnax.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/resnax.jar -------------------------------------------------------------------------------- /back-end/lib/win32/Microsoft.Z3.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win32/Microsoft.Z3.dll -------------------------------------------------------------------------------- /back-end/lib/win32/com.microsoft.z3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win32/com.microsoft.z3.jar -------------------------------------------------------------------------------- /back-end/lib/win32/libz3.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win32/libz3.dll -------------------------------------------------------------------------------- /back-end/lib/win32/libz3.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win32/libz3.lib -------------------------------------------------------------------------------- /back-end/lib/win32/libz3java.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win32/libz3java.dll -------------------------------------------------------------------------------- /back-end/lib/win32/libz3java.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win32/libz3java.lib -------------------------------------------------------------------------------- /back-end/lib/win32/msvcp140.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win32/msvcp140.dll -------------------------------------------------------------------------------- /back-end/lib/win32/msvcp140_1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win32/msvcp140_1.dll -------------------------------------------------------------------------------- /back-end/lib/win32/msvcp140_2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win32/msvcp140_2.dll -------------------------------------------------------------------------------- /back-end/lib/win32/vcomp140.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win32/vcomp140.dll -------------------------------------------------------------------------------- /back-end/lib/win32/z3.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win32/z3.exe -------------------------------------------------------------------------------- /back-end/lib/win64/Microsoft.Z3.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win64/Microsoft.Z3.dll -------------------------------------------------------------------------------- /back-end/lib/win64/com.microsoft.z3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win64/com.microsoft.z3.jar -------------------------------------------------------------------------------- /back-end/lib/win64/libz3.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win64/libz3.dll -------------------------------------------------------------------------------- /back-end/lib/win64/libz3.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win64/libz3.lib -------------------------------------------------------------------------------- /back-end/lib/win64/libz3java.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win64/libz3java.dll -------------------------------------------------------------------------------- /back-end/lib/win64/libz3java.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win64/libz3java.lib -------------------------------------------------------------------------------- /back-end/lib/win64/msvcp140.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win64/msvcp140.dll -------------------------------------------------------------------------------- /back-end/lib/win64/msvcp140_1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win64/msvcp140_1.dll -------------------------------------------------------------------------------- /back-end/lib/win64/msvcp140_2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win64/msvcp140_2.dll -------------------------------------------------------------------------------- /back-end/lib/win64/vcomp140.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win64/vcomp140.dll -------------------------------------------------------------------------------- /back-end/lib/win64/z3.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/lib/win64/z3.exe -------------------------------------------------------------------------------- /back-end/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | interactive-program-synthesizer 4 | back-end 5 | 0.0.1-SNAPSHOT 6 | back-end 7 | 8 | 9 | 10 | 11 | junit 12 | junit 13 | 4.13 14 | test 15 | 16 | 17 | 18 | org.eclipse.jetty 19 | jetty-server 20 | 9.4.8.v20171121 21 | 22 | 23 | 24 | org.eclipse.jetty.websocket 25 | websocket-server 26 | 9.4.8.v20171121 27 | 28 | 29 | 30 | org.eclipse.jetty.websocket 31 | websocket-api 32 | 9.4.8.v20171121 33 | 34 | 35 | 36 | commons-io 37 | commons-io 38 | 2.6 39 | 40 | 41 | 42 | com.fasterxml.jackson.core 43 | jackson-databind 44 | 2.9.4 45 | 46 | 47 | commons-cli 48 | commons-cli 49 | 1.4 50 | 51 | 52 | dk.brics.automaton 53 | automaton 54 | system 55 | 1.12-1 56 | ${basedir}/lib/automaton.jar 57 | 58 | 59 | org.javatuples 60 | javatuples 61 | 1.2 62 | 63 | 64 | 65 | org.apache.commons 66 | commons-text 67 | 1.8 68 | 69 | 70 | 71 | 72 | 1.8 73 | 1.8 74 | 75 | 76 | 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-assembly-plugin 81 | 82 | 83 | package 84 | 85 | single 86 | 87 | 88 | 89 | 90 | 91 | edu.harvard.seas.synthesis.SynthesisServer 92 | 93 | 94 | 95 | ${basedir}/assembly.xml 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /back-end/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/src/.DS_Store -------------------------------------------------------------------------------- /back-end/src/main/java/edu/harvard/seas/synthesis/CoverageDrivenInputGenerator.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.Comparator; 6 | import java.util.HashMap; 7 | import java.util.HashSet; 8 | import java.util.LinkedHashMap; 9 | import java.util.LinkedList; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Set; 13 | import java.util.concurrent.ThreadLocalRandom; 14 | 15 | import org.javatuples.Pair; 16 | 17 | import dk.brics.automaton.Automaton; 18 | import dk.brics.automaton.BasicOperations; 19 | import dk.brics.automaton.RegExp; 20 | import dk.brics.automaton.State; 21 | import dk.brics.automaton.Transition; 22 | 23 | public class CoverageDrivenInputGenerator { 24 | int num = 5; // num of strings per cluster 25 | int cap = 20; 26 | 27 | String dsl_regex = ""; 28 | 29 | public CoverageDrivenInputGenerator() { 30 | 31 | } 32 | 33 | public CoverageDrivenInputGenerator(String dsl_regex) { 34 | this.dsl_regex = dsl_regex; 35 | } 36 | 37 | public Map> generate(String regex, String[] examples) { 38 | RegExp exp = new RegExp(regex); 39 | Automaton a = exp.toAutomaton(); 40 | if(!a.isDeterministic()) { 41 | a.determinize(); 42 | a.minimize(); 43 | } 44 | 45 | // combining overlapping and adjacent edge intervals with same destination. 46 | a.reduce(); 47 | 48 | // identify which paths have been covered 49 | // note that here we assume all examples are positive examples 50 | // Set> coveredPaths = new HashSet>(); 51 | // for(String s : examples) { 52 | // List path = new ArrayList(); 53 | // State p = a.getInitialState(); 54 | // path.add(p); 55 | // for (int i = 0; i < s.length(); i++) { 56 | // State q = p.step(s.charAt(i)); 57 | // path.add(q); 58 | // p = q; 59 | // } 60 | // coveredPaths.add(path); 61 | // } 62 | 63 | // generate a pool of positive examples 64 | // try to generate all positive examples first 65 | // ArrayList> pos = generateInputStrings(a, new HashSet>(), true); 66 | 67 | ArrayList> neg = generateInputStrings(a, new HashSet>(), false); 68 | 69 | if(neg.isEmpty()) { 70 | // this regex accepts any string 71 | ArrayList> pos = generateInputStrings(a, new HashSet>(), true); 72 | // aggregate all positive strings 73 | Map m = new HashMap(); 74 | for(Map cluster : pos) { 75 | m.putAll(cluster); 76 | } 77 | Map> clusters = new LinkedHashMap>(); 78 | if(m.isEmpty()) { 79 | return clusters; 80 | } else { 81 | String s = "We didn't find any negative examples. This regex can accept any string."; 82 | clusters.put(s, m); 83 | return clusters; 84 | } 85 | } 86 | 87 | // re-cluster negative examples based on their failure reasons 88 | Map> negWithExplanation = new LinkedHashMap>(); 89 | for(Map cluster : neg) { 90 | if(cluster.isEmpty()) { 91 | negWithExplanation.put("", cluster); 92 | } else { 93 | // pick one example 94 | String s = cluster.keySet().iterator().next(); 95 | // remove the failure-inducing character index 96 | s = s.substring(0, s.lastIndexOf(',')); 97 | String explanation = ExplanationGenerator.generateExplanation(s, a, dsl_regex); 98 | // System.out.println(explanation); 99 | if(negWithExplanation.containsKey(explanation)) { 100 | Map map = negWithExplanation.get(explanation); 101 | map.putAll(cluster); 102 | negWithExplanation.put(explanation, map); 103 | } else { 104 | Map map = new HashMap(); 105 | map.putAll(cluster); 106 | negWithExplanation.put(explanation, map); 107 | } 108 | } 109 | } 110 | 111 | Map> map = new LinkedHashMap>(); 112 | // enumerate paths to all positive examples 113 | Set> paths = enumeratePaths(a); 114 | // TODO: try to pair each negative example is paired with a positive one 115 | for(String explanation : negWithExplanation.keySet()) { 116 | Map cluster = negWithExplanation.get(explanation); 117 | 118 | Map m; 119 | if(map.containsKey(explanation)) { 120 | m = map.get(explanation); 121 | } else { 122 | m = new LinkedHashMap(); 123 | } 124 | 125 | for(String example : cluster.keySet()) { 126 | // put example into map 127 | m.put(example, false); 128 | 129 | example = example.substring(0, example.lastIndexOf(',')); 130 | 131 | if(example.isEmpty()) { 132 | continue; 133 | } 134 | 135 | State p = a.getInitialState(); 136 | List l = new ArrayList(); 137 | for(int i = 0; i < example.length(); i++) { 138 | l.add(p); 139 | State q = p.step(example.charAt(i)); 140 | if (q == null) { 141 | break; 142 | } else if (i == example.length() - 1 && !q.isAccept()) { 143 | l.add(q); 144 | break; 145 | } 146 | 147 | p = q; 148 | } 149 | 150 | String sub = example.substring(0, l.size()-1); 151 | for(List path : paths) { 152 | if(path.containsAll(l)) { 153 | // generate a corresponding positive example 154 | String pos = continueToGenerateExample(sub, path); 155 | 156 | m.put(pos, true); 157 | break; 158 | } 159 | } 160 | } 161 | 162 | map.put(explanation, m); 163 | } 164 | 165 | return map; 166 | } 167 | 168 | 169 | public String continueToGenerateExample(String s, List path) { 170 | String example = s; 171 | for(int i = s.length(); i < path.size()-1; i++) { 172 | State src = path.get(i); 173 | State dst = path.get(i+1); 174 | Set ts = src.getTransitions(); 175 | if(ts.size() > 0) { 176 | Transition t = ts.iterator().next(); 177 | if(!t.getDest().equals(dst)) { 178 | // not the correct edge to the next state 179 | continue; 180 | } 181 | 182 | char min = t.getMin(); 183 | if(min < '\u0020') { 184 | min = '\u0020'; 185 | } 186 | char max = t.getMax(); 187 | if(max > '\u007E') { 188 | max = '\u007E'; 189 | } 190 | 191 | int randomNum = ThreadLocalRandom.current().nextInt(min, max + 1); 192 | char c = (char) randomNum; 193 | example += c; 194 | } 195 | } 196 | 197 | return example; 198 | } 199 | 200 | public ArrayList> generateInputStrings(Automaton a, Set> coveredPaths, boolean accepted) { 201 | Automaton curr = a; 202 | if(!accepted) { 203 | // generate negative examples 204 | // create a complement (or you can call it negation) of the automaton 205 | curr = BasicOperations.complement(a); 206 | } 207 | 208 | Set> paths = enumeratePaths(curr); 209 | // explore paths that have not been covered by user-given examples 210 | paths.removeAll(coveredPaths); 211 | List> clusters = generateBasedOnPath(paths, accepted); 212 | // if (a.getSingleton() != null) { 213 | // if (accepted) 214 | // return a.getSingleton(); 215 | // else if (a.getSingleton().length() > 0) 216 | // return ""; 217 | // else 218 | // return "\u0000"; 219 | // 220 | // } 221 | 222 | ArrayList> l = new ArrayList>(); 223 | for(List cluster : clusters) { 224 | HashMap m = new HashMap(); 225 | for(String s : cluster) { 226 | if(!accepted) { 227 | // If this is a negative example 228 | // get the index of the failure-inducing character 229 | // and append it to the end 230 | State p = a.getInitialState(); 231 | int index = 0; 232 | for (int i = 0; i < s.length(); i++) { 233 | State q = p.step(s.charAt(i)); 234 | if (q == null) { 235 | break; 236 | } else if (i == s.length() - 1 && !q.isAccept()) { 237 | break; 238 | } 239 | 240 | index = i + 1; 241 | p = q; 242 | } 243 | 244 | s += "," + index; 245 | } 246 | 247 | m.put(s, accepted); 248 | 249 | // apply the cap of examples in each cluster 250 | if(m.size() == cap) break; 251 | } 252 | l.add(m); 253 | } 254 | 255 | return l; 256 | } 257 | 258 | public Set> enumeratePaths(Automaton a) { 259 | Set> acceptedPaths = new HashSet>(); 260 | 261 | // init 262 | List l = new ArrayList(); 263 | State s0 = a.getInitialState(); 264 | l.add(s0); 265 | if(s0.isAccept()) { 266 | List p0 = new ArrayList(); 267 | p0.add(s0); 268 | acceptedPaths.add(p0); 269 | } 270 | 271 | Set> paths = new HashSet>(); // the set of paths to explore 272 | paths.add(l); 273 | 274 | // use edge coverage to drive the traversal 275 | // state coverage is too weak while path coverage is unrealistic when there is a loop 276 | Set> edges = new HashSet>(); 277 | for(State s : a.getStates()) { 278 | for(Transition t : s.getTransitions()) { 279 | edges.add(new Pair(s, t.getDest())); 280 | } 281 | } 282 | 283 | while(!paths.isEmpty() && !hasFullCoverage(acceptedPaths, edges)) { 284 | Set> newPaths = new HashSet>(); 285 | for(List path : paths) { 286 | State q = path.get(path.size() - 1); 287 | 288 | // BFS 289 | for (Transition t : q.getTransitions()) { 290 | // append to existing paths ending with q 291 | // duplicate these paths if there are multiple outcoming edges from q 292 | ArrayList copy = new ArrayList(); 293 | copy.addAll(path); 294 | State dst = t.getDest(); 295 | copy.add(dst); 296 | if(dst.isAccept()) { 297 | acceptedPaths.add(copy); 298 | } 299 | 300 | if(dst.getTransitions().size() > 0) { 301 | // add the current path to continue exploring 302 | newPaths.add(copy); 303 | } 304 | } 305 | } 306 | paths = newPaths; 307 | } 308 | 309 | return acceptedPaths; 310 | } 311 | 312 | private boolean hasFullCoverage(Set> paths, Set> edges) { 313 | Set> coverSet = new HashSet>(); 314 | for(List p : paths) { 315 | for(int i = 0; i < p.size() - 1; i++) { 316 | State src = p.get(i); 317 | State dst = p.get(i+1); 318 | coverSet.add(new Pair(src, dst)); 319 | } 320 | } 321 | 322 | if(coverSet.equals(edges)) { 323 | return true; 324 | } else { 325 | return false; 326 | } 327 | } 328 | 329 | public List> generateBasedOnPath(Set> paths, boolean accepted) { 330 | List> clusters = new ArrayList>(); 331 | // sort by length 332 | List> sortedPaths = new ArrayList>(); 333 | sortedPaths.addAll(paths); 334 | Comparator> pathLengthComparator = new Comparator>() 335 | { 336 | @Override 337 | public int compare(List p1, List p2) 338 | { 339 | return Integer.compare(p1.size(), p2.size()); 340 | } 341 | }; 342 | 343 | Collections.sort(sortedPaths, pathLengthComparator); 344 | 345 | for(List p : sortedPaths) { 346 | List cluster = new ArrayList(); 347 | List temp = generateBasedOnPath(p); 348 | cluster.addAll(temp); 349 | 350 | // System.out.println(cluster); 351 | clusters.add(cluster); 352 | } 353 | 354 | return clusters; 355 | } 356 | 357 | public List generateBasedOnPath(List path) { 358 | ArrayList examples = new ArrayList(); 359 | examples.add(""); 360 | 361 | if(path.size() == 1) { 362 | // the initial state is an accept state 363 | // meaning it accepts an empty string 364 | return examples; 365 | } 366 | 367 | for(int i = 0; i < path.size() - 1; i++) { 368 | State src = path.get(i); 369 | State dst = path.get(i+1); 370 | ArrayList newExamples = new ArrayList(); 371 | for(Transition t : src.getTransitions()) { 372 | if(!t.getDest().equals(dst)) { 373 | // not the correct edge to the next state 374 | continue; 375 | } 376 | 377 | ArrayList copy = new ArrayList(); 378 | 379 | char min = t.getMin(); 380 | if(min < '\u0020') { 381 | min = '\u0020'; 382 | } 383 | char max = t.getMax(); 384 | if(max > '\u007E') { 385 | max = '\u007E'; 386 | } 387 | 388 | // append this random char to the end of each path 389 | for(int j = 0; j < examples.size(); j++) { 390 | // pick a random char between min and max 391 | HashSet set = new HashSet(); 392 | for(int k = 0; k < num; k++) { 393 | int randomNum = ThreadLocalRandom.current().nextInt(min, max + 1); 394 | char c = (char) randomNum; 395 | set.add(c + ""); 396 | } 397 | 398 | for(String c : set) { 399 | String p = examples.get(j); 400 | String np = p + c; 401 | copy.add(np); 402 | } 403 | } 404 | 405 | newExamples.addAll(copy); 406 | } 407 | 408 | if(newExamples.size() > 100) { 409 | // truncate to avoid out of memory error 410 | examples = new ArrayList(newExamples.subList(0, 100)); 411 | } else { 412 | examples = newExamples; 413 | } 414 | } 415 | 416 | return examples; 417 | } 418 | 419 | static String getShortestExample(State s, boolean accepted) { 420 | Map path = new HashMap(); 421 | LinkedList queue = new LinkedList(); 422 | path.put(s, ""); 423 | queue.add(s); 424 | String best = null; 425 | while (!queue.isEmpty()) { 426 | State q = queue.removeFirst(); 427 | String p = path.get(q); 428 | if (q.isAccept() == accepted) { 429 | if (best == null || p.length() < best.length() || (p.length() == best.length() && p.compareTo(best) < 0)) 430 | best = p; 431 | } else { 432 | for (Transition t : q.getTransitions()) { 433 | String tp = path.get(t.getDest()); 434 | char min = t.getMin(); 435 | if(min < '\u0020') { 436 | min = '\u0020'; 437 | } 438 | char max = t.getMax(); 439 | if(max > '\u007E') { 440 | max = '\u007E'; 441 | } 442 | 443 | // pick a random number between min and max 444 | int randomNum = ThreadLocalRandom.current().nextInt(min, max + 1); 445 | char c = (char) randomNum; 446 | String np = p + c; 447 | if (tp == null || (tp.length() == np.length() && np.compareTo(tp) < 0)) { 448 | if (tp == null) 449 | queue.addLast(t.getDest()); 450 | path.put(t.getDest(), np); 451 | } 452 | } 453 | } 454 | } 455 | return best; 456 | } 457 | } 458 | -------------------------------------------------------------------------------- /back-end/src/main/java/edu/harvard/seas/synthesis/Example.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis; 2 | 3 | public class Example { 4 | public String input; 5 | public String[] exact; 6 | public String[] unmatch; 7 | public String[] generalize; 8 | public Boolean output; 9 | 10 | @Override 11 | public String toString() { 12 | return input; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /back-end/src/main/java/edu/harvard/seas/synthesis/ExampleBasedInputGenerator.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.charset.Charset; 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.HashSet; 9 | import java.util.LinkedHashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Random; 13 | import java.util.Set; 14 | import java.util.concurrent.ThreadLocalRandom; 15 | 16 | import org.apache.commons.io.FileUtils; 17 | 18 | import com.fasterxml.jackson.databind.ObjectMapper; 19 | 20 | import dk.brics.automaton.Automaton; 21 | import dk.brics.automaton.RegExp; 22 | import dk.brics.automaton.State; 23 | import dk.brics.automaton.Transition; 24 | 25 | public class ExampleBasedInputGenerator { 26 | public static String python3_path = "/usr/local/bin/python"; 27 | public static int num_of_examples_per_cluster = 5; 28 | public static String input_generator_path = "example-generation/main.py"; 29 | 30 | private String dfa_file_path = "dfa.txt"; 31 | 32 | public Map> generate(String example, String automaton_regex, String dsl_regex) { 33 | Map> clusters = new LinkedHashMap>(); 34 | 35 | if(example.isEmpty()) { 36 | return clusters; 37 | } 38 | 39 | // parse the regex to an automaton 40 | RegExp exp = new RegExp(automaton_regex); 41 | Automaton a = exp.toAutomaton(); 42 | if(!a.isDeterministic()) { 43 | a.determinize(); 44 | a.minimize(); 45 | } 46 | 47 | // combining overlapping and adjacent edge intervals with same destination. 48 | a.reduce(); 49 | 50 | State p = a.getInitialState(); 51 | for (int i = 0; i < example.length(); i++) { 52 | State q = p.step(example.charAt(i)); 53 | 54 | // mutate this i-th character to generate positive example 55 | HashSet positives = new HashSet(); 56 | HashSet negatives = new HashSet(); 57 | ArrayList chars = new ArrayList(); 58 | // find out what char ranges are not accepted by this state 59 | char c = '\u0020'; 60 | while(c <= '\u007E') { 61 | chars.add(c); 62 | c = (char) (c + 1); 63 | } 64 | for(Transition t : p.getTransitions()) { 65 | if(t.getDest().equals(q)) { 66 | for(int j = 0; j < num_of_examples_per_cluster; j++) { 67 | String pos = mutateChar(example, i, t, chars); 68 | positives.add(pos); 69 | } 70 | } else { 71 | // another branch, may or may not be accepted after mutation 72 | for(int j = 0; j < num_of_examples_per_cluster; j++) { 73 | String s = mutateChar(example, i, t, chars); 74 | if(a.run(s)) { 75 | positives.add(s); 76 | } else { 77 | negatives.add(s); 78 | } 79 | } 80 | } 81 | } 82 | 83 | // mutate to generate negative examples 84 | if(!chars.isEmpty()) { 85 | for(int j = 0; j < num_of_examples_per_cluster; j++) { 86 | int randomNum = ThreadLocalRandom.current().nextInt(0, chars.size()); 87 | char mut = chars.get(randomNum); 88 | String s; 89 | if(i == example.length() - 1) { 90 | s = example.substring(0, i) + mut; 91 | } else { 92 | s = example.substring(0, i) + mut + example.substring(i+1); 93 | } 94 | negatives.add(s); 95 | } 96 | } 97 | 98 | 99 | Map cluster = new HashMap(); 100 | for(String positive : positives) { 101 | cluster.put(positive, true); 102 | } 103 | 104 | if(negatives.isEmpty() && !positives.isEmpty()) { 105 | // we didn't find any negative examples 106 | String s = "Positive examples only"; 107 | clusters.put(s, cluster); 108 | } else { 109 | for(String negative: negatives) { 110 | // If this is a negative example 111 | // get the index of the failure-inducing character 112 | // and append it to the end 113 | int index = getIndexOfFailure(a, negative); 114 | cluster.put(negative + "," + index, false); 115 | String explanation = 116 | ExplanationGenerator.generateExplanation(negative, a, dsl_regex); 117 | if(clusters.containsKey(explanation)) { 118 | Map existing_cluster = clusters.get(explanation); 119 | existing_cluster.putAll(cluster); 120 | clusters.put(explanation, existing_cluster); 121 | } else { 122 | Map new_cluster = new HashMap(); 123 | new_cluster.putAll(cluster); 124 | clusters.put(explanation, new_cluster); 125 | } 126 | } 127 | } 128 | } 129 | 130 | if(clusters.size() > 1 && clusters.containsKey("Positive examples only")) { 131 | clusters.remove("Positive examples only"); 132 | } 133 | 134 | return clusters; 135 | } 136 | 137 | private int getIndexOfFailure(Automaton a, String s) { 138 | State p = a.getInitialState(); 139 | int index = 0; 140 | for (int i = 0; i < s.length(); i++) { 141 | State q = p.step(s.charAt(i)); 142 | if (q == null) { 143 | break; 144 | } else if (i == s.length() - 1 && !q.isAccept()) { 145 | break; 146 | } 147 | 148 | index = i + 1; 149 | p = q; 150 | } 151 | 152 | return index; 153 | } 154 | 155 | private String getRandomItem(HashSet set){ 156 | Random random = new Random(); 157 | int randomNumber = random.nextInt(set.size()); 158 | 159 | int currentIndex = 0; 160 | String randomElement = null; 161 | 162 | for(String element : set){ 163 | randomElement = element; 164 | 165 | if(currentIndex == randomNumber) 166 | return randomElement; 167 | 168 | currentIndex++; 169 | } 170 | 171 | return randomElement; 172 | } 173 | 174 | private String mutateChar(String example, int index, Transition t, ArrayList chars) { 175 | char min = t.getMin(); 176 | if(min < '\u0020') { 177 | min = '\u0020'; 178 | } 179 | char max = t.getMax(); 180 | if(max > '\u007E') { 181 | max = '\u007E'; 182 | } 183 | 184 | // add this range to chars 185 | for(int i = 0; i < max - min + 1; i++) { 186 | char c = (char) (min + i); 187 | chars.remove((Character) c); 188 | } 189 | 190 | int randomNum = ThreadLocalRandom.current().nextInt(min, max + 1); 191 | char c = (char) randomNum; 192 | String s; 193 | if(index == example.length() - 1) { 194 | s = example.substring(0, index) + c; 195 | } else { 196 | s = example.substring(0, index) + c + example.substring(index+1); 197 | } 198 | return s; 199 | } 200 | 201 | @Deprecated 202 | public Map> generateFromPythonScript(String example, String regex) { 203 | Map> clusters = new LinkedHashMap>(); 204 | 205 | // parse the regex to an automaton 206 | RegExp exp = new RegExp(regex); 207 | Automaton automaton = exp.toAutomaton(); 208 | if(!automaton.isDeterministic()) { 209 | automaton.determinize(); 210 | automaton.minimize(); 211 | } 212 | 213 | // combining overlapping and adjacent edge intervals with same destination. 214 | automaton.reduce(); 215 | String s = writeAutomatonToFile(automaton); 216 | File dfa_file = new File(dfa_file_path); 217 | if(dfa_file.exists()) { 218 | dfa_file.delete(); 219 | } 220 | try { 221 | FileUtils.write(dfa_file, s, Charset.defaultCharset()); 222 | } catch (IOException e) { 223 | e.printStackTrace(); 224 | } 225 | 226 | // invoke the python script for input generation 227 | String[] cmd = {python3_path, input_generator_path, example, 228 | dfa_file_path, "-a", "" + num_of_examples_per_cluster}; 229 | ProcessBuilder processBuilder = new ProcessBuilder(cmd); 230 | try { 231 | File fError = new File("example-generator-error"); 232 | processBuilder.redirectError(fError); 233 | File fOutput = new File("example-generator-output"); 234 | processBuilder.redirectOutput(fOutput); 235 | Process process = processBuilder.start(); 236 | process.waitFor(); 237 | int exitCode = process.exitValue(); 238 | 239 | if(exitCode != 0) { 240 | // there is an error in the python script execution, abort 241 | return clusters; 242 | } 243 | 244 | // read results from the python script output 245 | String output = FileUtils.readFileToString(fOutput, Charset.defaultCharset()); 246 | ObjectMapper mapper = new ObjectMapper(); 247 | Map>> map = mapper.readValue(output, Map.class); 248 | for(String cluster_id : map.keySet()) { 249 | Map> examples = map.get(cluster_id); 250 | ArrayList positives = examples.get("positive"); 251 | ArrayList negatives = examples.get("negative"); 252 | Map cluster = new HashMap(); 253 | for(String positive : positives) { 254 | cluster.put(positive, true); 255 | } 256 | 257 | for(String negative: negatives) { 258 | cluster.put(negative + "," + cluster_id, false); 259 | String explanation = 260 | ExplanationGenerator.generateExplanation(negative, automaton, regex); 261 | if(clusters.containsKey(explanation)) { 262 | Map existing_cluster = clusters.get(explanation); 263 | existing_cluster.putAll(cluster); 264 | clusters.put(explanation, existing_cluster); 265 | } else { 266 | Map new_cluster = new HashMap(); 267 | new_cluster.putAll(cluster); 268 | clusters.put(explanation, new_cluster); 269 | } 270 | } 271 | 272 | // int index = Integer.parseInt(cluster_id); 273 | // clusters.put("Examples generated by changing the " + 274 | // ExplanationGenerator.getOrdinalNum(index+1) + " character", cluster); 275 | } 276 | } catch (IOException e) { 277 | e.printStackTrace(); 278 | } catch (InterruptedException e) { 279 | e.printStackTrace(); 280 | } 281 | 282 | return clusters; 283 | } 284 | 285 | @Deprecated 286 | public String writeAutomatonToFile(Automaton automaton) { 287 | // convert the automaton to the compatible format in a file that can be accepted by the python script 288 | Set states = automaton.getStates(); 289 | HashMap map = new HashMap(); 290 | State initialState = automaton.getInitialState(); 291 | map.put(initialState, 0); 292 | String s = "0" + System.lineSeparator(); 293 | Set finalStates = new HashSet(); 294 | int count = 1; 295 | for(State state : states) { 296 | int id; 297 | if(map.containsKey(state)) { 298 | id = map.get(state); 299 | } else { 300 | id = count; 301 | count++; 302 | map.put(state, id); 303 | } 304 | 305 | if(state.isAccept()) { 306 | finalStates.add(state); 307 | } 308 | 309 | List edges = state.getSortedTransitions(false); 310 | for(Transition edge : edges) { 311 | State destination = edge.getDest(); 312 | int d_id; 313 | if(map.containsKey(destination)) { 314 | d_id = map.get(destination); 315 | } else { 316 | d_id = count; 317 | count++; 318 | map.put(destination, d_id); 319 | } 320 | 321 | s += id + "," + d_id + ","; 322 | String minChar = toCharString(edge.getMin()); 323 | String maxChar = toCharString(edge.getMax()); 324 | s += minChar + "-" + maxChar + System.lineSeparator(); 325 | } 326 | } 327 | // append the final states 328 | for(State state : finalStates) { 329 | s += map.get(state) + ","; 330 | } 331 | s = s.substring(0, s.length() - 1); 332 | return s; 333 | } 334 | 335 | @Deprecated 336 | String toCharString(char c) { 337 | if (c >= 0x21 && c <= 0x7e && c != '\\' && c != '"') 338 | return "" + c; 339 | else { 340 | String charString = "\\u"; 341 | String s = Integer.toHexString(c); 342 | if (c < 0x10) 343 | charString += "000" + s; 344 | else if (c < 0x100) 345 | charString += "00" + s; 346 | else if (c < 0x1000) 347 | charString += "0" + s; 348 | else 349 | charString += s; 350 | 351 | return charString; 352 | } 353 | } 354 | } 355 | -------------------------------------------------------------------------------- /back-end/src/main/java/edu/harvard/seas/synthesis/ExplanationGenerator.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import org.apache.commons.text.StringEscapeUtils; 9 | 10 | import dk.brics.automaton.Automaton; 11 | import dk.brics.automaton.State; 12 | import dk.brics.automaton.Transition; 13 | 14 | public class ExplanationGenerator { 15 | public static String getOrdinalNum(int i) { 16 | if(i == 1) { 17 | return "1st"; 18 | } else if (i == 2) { 19 | return "2nd"; 20 | } else if (i == 3) { 21 | return "3rd"; 22 | } else { 23 | return i + "th"; 24 | } 25 | } 26 | 27 | // Currently we only support generate explanations for negative examples 28 | public static String generateExplanation(String s, Automaton a, String dsl_regex) { 29 | String explanation = ""; 30 | 31 | if(s.isEmpty()) { 32 | explanation = "Examples rejected because the selected regex expects non-empty string."; 33 | return explanation; 34 | } 35 | 36 | State p = a.getInitialState(); 37 | for (int i = 0; i < s.length(); i++) { 38 | State q = p.step(s.charAt(i)); 39 | if (q == null) { 40 | if(p.isAccept() && p.getTransitions().size() == 0) { 41 | explanation = "Examples rejected because they have " 42 | + "more than " + i + " characters."; 43 | } else { 44 | String range = getAcceptedCharsFromState(p); 45 | if(range.startsWith("not ")) { 46 | String chars = range.substring(4); 47 | if(chars.length() == 1) { 48 | // single character 49 | String l = "concat(<" + chars + ">,<" + chars + ">)"; 50 | if(dsl_regex.contains(l)) { 51 | // special case 52 | explanation = "Examples rejected because they contain " + StringEscapeUtils.escapeHtml4(chars + chars) + "."; 53 | break; 54 | } 55 | } 56 | 57 | explanation = "Examples rejected because the " + ExplanationGenerator.getOrdinalNum(i+1) 58 | + " character is " + StringEscapeUtils.escapeHtml4(chars) + "."; 59 | } else { 60 | explanation = "Examples rejected because the " + ExplanationGenerator.getOrdinalNum(i+1) 61 | + " character is not " + StringEscapeUtils.escapeHtml4(range) + "."; 62 | } 63 | } 64 | 65 | break; 66 | } else if (i == s.length() - 1 && !q.isAccept()) { 67 | // the regex expects more characters 68 | if(dsl_regex.contains("contain")) { 69 | List tToAccept = new ArrayList(); 70 | for(Transition t : q.getTransitions()) { 71 | if(t.getDest().isAccept()) { 72 | tToAccept.add(t); 73 | } 74 | } 75 | 76 | if(tToAccept.isEmpty()) { 77 | // none of the following states are accept state 78 | // the regex is likely to expect a sequence of chars 79 | // pick the transition that only accepts a single char 80 | String thatChar = null; 81 | for(Transition t : q.getTransitions()) { 82 | if(t.getMax() == t.getMin()) { 83 | thatChar = t.getMin() + ""; 84 | break; 85 | } else if (t.getMax() == '9') { 86 | thatChar = t.getMin() + " to 9"; 87 | break; 88 | } else if (t.getMax() == 'a' && t.getMin() == 'z') { 89 | thatChar = "a to z"; 90 | break; 91 | } else if (t.getMax() == 'A' && t.getMin() == 'Z') { 92 | thatChar = "A to Z"; 93 | break; 94 | } 95 | } 96 | 97 | if(thatChar == null) { 98 | explanation = "Failed to generate a cluster header"; 99 | } else { 100 | explanation = "Examples rejected because the selected regex " 101 | + "expects more characters. The next character can be " 102 | + StringEscapeUtils.escapeHtml4(thatChar) + "."; 103 | } 104 | } else { 105 | explanation = "Examples rejected because the selected regex " 106 | + "expects the example to contain " + 107 | StringEscapeUtils.escapeHtml4(getAcceptedCharsInTransitions(tToAccept)) + "."; 108 | } 109 | } else if (dsl_regex.contains("endwith")) { 110 | List tToAccept = new ArrayList(); 111 | for(Transition t : q.getTransitions()) { 112 | if(t.getDest().isAccept()) { 113 | tToAccept.add(t); 114 | } 115 | } 116 | 117 | explanation = "Examples rejected because the selected regex " 118 | + "expects the example to end with " 119 | + StringEscapeUtils.escapeHtml4(getAcceptedCharsInTransitions(tToAccept)) + "."; 120 | } else { 121 | String range = getAcceptedCharsFromState(p); 122 | if (range.startsWith("not " )){ 123 | explanation = "Examples rejected because the selected regex " 124 | + "expects more characters. The next character cannot be " 125 | + StringEscapeUtils.escapeHtml4(range.substring(4)) + "."; 126 | } else { 127 | explanation = "Examples rejected because the selected regex " 128 | + "expects more characters. The next character can be " 129 | + StringEscapeUtils.escapeHtml4(range) + "."; 130 | } 131 | } 132 | 133 | break; 134 | } 135 | 136 | p = q; 137 | } 138 | 139 | return explanation; 140 | } 141 | 142 | public static String generateExplanation(String example, Automaton[] automata, String[] regexes, Boolean[] results) { 143 | String explanation = ""; 144 | Map m = new HashMap(); // explanation -> an concatenation of regexes with the same explanation 145 | for (int j = 0; j < results.length; j++) { 146 | if(!results[j]) { 147 | String s = ExplanationGenerator.generateExplanation(example, automata[j], regexes[j]); 148 | if(m.containsKey(s)) { 149 | String existing_regex = m.get(s); 150 | existing_regex += " and " + regexes[j]; 151 | m.put(s, existing_regex); 152 | } else { 153 | m.put(s, regexes[j]); 154 | } 155 | } 156 | } 157 | 158 | for(String s : m.keySet()) { 159 | String regex = m.get(s); 160 | if(s.contains("the selected regex") && regex.contains(" and ")) { 161 | // multiple regexes fail for the same reason 162 | s = s.replace("the selected regex", "these regexes"); 163 | } else if (s.contains("the selected regex")) { 164 | // single regex fails 165 | s = s.replace("the selected regex", "this regex"); 166 | } 167 | if(explanation.endsWith("and also rejected by ")) { 168 | explanation += regex + s.substring("Examples rejected".length()); 169 | } else { 170 | s = s.replace("Examples rejected", "Examples rejected by " 171 | + StringEscapeUtils.escapeHtml4(regex) + ""); 172 | explanation += s; 173 | } 174 | 175 | explanation += ", and also rejected by "; 176 | } 177 | 178 | explanation = explanation.substring(0, explanation.lastIndexOf(',')); 179 | 180 | return explanation; 181 | } 182 | 183 | private static String getAcceptedCharsFromState(State s) { 184 | if(s.getTransitions().isEmpty()) { 185 | return ""; 186 | } 187 | 188 | List trans = s.getSortedTransitions(true); 189 | String range = getAcceptedCharsInTransitions(trans); 190 | return range; 191 | } 192 | 193 | private static String getAcceptedCharsInTransitions(List trans) { 194 | char min = trans.get(0).getMin(); 195 | char max = trans.get(trans.size() - 1).getMax(); 196 | String range = ""; 197 | if(min == '\u0000' && max == '\uffff') { 198 | // this represents a character from the very first char to the end char 199 | // maybe without certain characters 200 | range = ""; 201 | if(trans.size() == 1) { 202 | range = "any characters"; 203 | } else { 204 | range = "not "; 205 | for(int i = 0; i < trans.size() - 1; i++) { 206 | Transition t1 = trans.get(i); 207 | Transition t2 = trans.get(i+1); 208 | char start = (char) (t1.getMax() + 1); 209 | char end = (char) (t2.getMin() - 1); 210 | 211 | if(start < '\u0020') { 212 | start = '\u0020'; 213 | } 214 | if(end > '\u007E') { 215 | end = '\u007E'; 216 | } 217 | 218 | if(start == end) { 219 | range += (start == '\u0020' ? "empty space" : start) + " or "; 220 | } else if (end - start == 1) { 221 | range += (start == '\u0020' ? "empty space" : start) + " or " + end + " or "; 222 | } else if (end - start == 2) { 223 | range += (start == '\u0020' ? "empty space" : start) + " or " + (char) (start + 1) + " or " + end + " or "; 224 | } else { 225 | range += (start == '\u0020' ? "empty space" : start) + " to " + end + " or "; 226 | } 227 | } 228 | range = range.substring(0, range.length() - 4); 229 | } 230 | } else { 231 | for (Transition t : trans) { 232 | char start = t.getMin(); 233 | if(start < '\u0020') { 234 | start = '\u0020'; 235 | } 236 | char end = t.getMax(); 237 | if(end > '\u007E') { 238 | end = '\u007E'; 239 | } 240 | 241 | if(start == end) { 242 | range += (start == '\u0020' ? "empty space" : start) + " or "; 243 | } else if (end - start == 1) { 244 | range += (start == '\u0020' ? "empty space" : start) + " or " + end + " or "; 245 | } else if (end - start == 2) { 246 | range += (start == '\u0020' ? "empty space" : start) + " or " + (char) (start + 1) + " or " + end + " or "; 247 | } else { 248 | range += (start == '\u0020' ? "empty space" : start) + " to " + end + " or "; 249 | } 250 | } 251 | 252 | range = range.substring(0, range.length() - 4); 253 | } 254 | 255 | return range; 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /back-end/src/main/java/edu/harvard/seas/synthesis/HTTPServer.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | 6 | import org.eclipse.jetty.server.Handler; 7 | import org.eclipse.jetty.server.Server; 8 | import org.eclipse.jetty.server.handler.DefaultHandler; 9 | import org.eclipse.jetty.server.handler.HandlerList; 10 | import org.eclipse.jetty.server.handler.ResourceHandler; 11 | import org.eclipse.jetty.websocket.server.WebSocketHandler; 12 | import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; 13 | 14 | public class HTTPServer { 15 | public static final String session_id = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss").format(new Date()); 16 | 17 | public static void main(String[] args) throws Exception { 18 | ServerCommandLineParser cmdParser = new ServerCommandLineParser(); 19 | boolean b = cmdParser.parse(args); 20 | if(!b) { 21 | return; 22 | } 23 | 24 | // config the synthesizer and the input generator 25 | ResnaxRunner.resnax_path = cmdParser.resnax_path; 26 | ResnaxRunner.timeout = cmdParser.timeout; 27 | // ExampleBasedInputGenerator.input_generator_path = cmdParser.input_generator_path; 28 | ExampleBasedInputGenerator.num_of_examples_per_cluster = cmdParser.num_of_examples_per_cluster; 29 | // ExampleBasedInputGenerator.python3_path = cmdParser.python3_path; 30 | 31 | 32 | Server server = new Server(8080); 33 | 34 | WebSocketHandler wsHandler = new WebSocketHandler() { 35 | @Override 36 | public void configure(WebSocketServletFactory factory) { 37 | factory.register(SynthesisServerHandler.class); 38 | } 39 | }; 40 | 41 | ResourceHandler resource_handler = new ResourceHandler(); 42 | resource_handler.setDirectoriesListed(true); 43 | resource_handler.setWelcomeFiles(new String[]{"index.html"}); 44 | resource_handler.setResourceBase("../front-end/"); 45 | 46 | HandlerList handlers = new HandlerList(); 47 | handlers.setHandlers(new Handler[] {wsHandler, resource_handler, new DefaultHandler() }); 48 | server.setHandler(handlers); 49 | server.start(); 50 | server.join(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /back-end/src/main/java/edu/harvard/seas/synthesis/Regex.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis; 2 | 3 | public class Regex { 4 | public String regex; 5 | public String[] include; 6 | public String[] exclude; 7 | 8 | @Override 9 | public String toString() { 10 | return regex; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /back-end/src/main/java/edu/harvard/seas/synthesis/ResnaxRunner.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.lang.management.ManagementFactory; 6 | import java.nio.charset.Charset; 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | import java.util.HashSet; 10 | import java.util.List; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | import org.apache.commons.io.FileUtils; 14 | 15 | public class ResnaxRunner { 16 | public static String resnax_path = "lib"; 17 | public static int timeout = 60; // 60 seconds 18 | 19 | private String java_class_path; 20 | private String z3_lib_path; 21 | 22 | private String example_file_path = "input"; 23 | private String program_file_path = "program"; 24 | private String log_dir_path = "resnax_log" + File.separator; 25 | private String temp_dir_path = "resnax_temp" + File.separator; 26 | 27 | private static ResnaxRunner single_instance = null; 28 | 29 | public HashMap dsl_to_automaton_regex = new HashMap(); 30 | public Process process = null; 31 | public int counter = 0; 32 | 33 | private ResnaxRunner() { 34 | if(!log_dir_path.endsWith(File.separator)) { 35 | log_dir_path += File.separator; 36 | } 37 | 38 | if(!temp_dir_path.endsWith(File.separator)) { 39 | temp_dir_path += File.separator; 40 | } 41 | 42 | File log_dir = new File(log_dir_path); 43 | if(log_dir.exists()) { 44 | log_dir.delete(); 45 | } 46 | log_dir.mkdir(); 47 | 48 | File temp_dir = new File(temp_dir_path); 49 | if(temp_dir.exists()) { 50 | temp_dir.delete(); 51 | } 52 | temp_dir.mkdir(); 53 | 54 | // By default 55 | z3_lib_path = resnax_path; 56 | 57 | String os = System.getProperty("os.name").toLowerCase(); 58 | String jvmBitVersion = System.getProperty("sun.arch.data.model"); 59 | if(os.indexOf("win") >= 0) { 60 | if(jvmBitVersion.equals("32")) { 61 | z3_lib_path = resnax_path + File.separator + "win32"; 62 | } 63 | else if(jvmBitVersion.equals("64")) { 64 | z3_lib_path = resnax_path + File.separator + "win64"; 65 | } 66 | } 67 | 68 | // enumerate all jar files in the classpath 69 | java_class_path = resnax_path + File.separator + "resnax.jar" 70 | + File.pathSeparator + resnax_path + File.separator + "antlr-4.7.1-complete.jar" 71 | + File.pathSeparator + resnax_path + File.separator + "automaton.jar" 72 | + File.pathSeparator + resnax_path + File.separator + "com.microsoft.z3.jar" 73 | + File.pathSeparator + resnax_path + File.separator + "javatuples-1.2.jar" 74 | + File.pathSeparator + resnax_path + File.separator + "libz3java.dylib" 75 | + File.pathSeparator + resnax_path + File.separator + "libz3.dylib" 76 | + File.pathSeparator + resnax_path + File.separator + "libz3java.so"; 77 | } 78 | 79 | public static ResnaxRunner getInstance() { 80 | if(single_instance == null) { 81 | single_instance = new ResnaxRunner(); 82 | } 83 | 84 | return single_instance; 85 | } 86 | 87 | public static void reset() { 88 | if(single_instance == null) { 89 | return; 90 | } 91 | 92 | // kill the current synthesis process 93 | if(single_instance.process != null && single_instance.process.isAlive()) { 94 | single_instance.process.destroy(); 95 | try { 96 | Thread.sleep(2000); 97 | } catch (InterruptedException e) { 98 | e.printStackTrace(); 99 | } 100 | if(single_instance.process.isAlive()) { 101 | single_instance.process.destroyForcibly(); 102 | } 103 | } 104 | // remove the two files 105 | File f1 = new File(single_instance.example_file_path); 106 | f1.delete(); 107 | File f2 = new File(single_instance.program_file_path); 108 | f2.delete(); 109 | 110 | // remove log files if any 111 | File fError = new File("resnax-error"); 112 | if(fError.exists()) { 113 | fError.delete(); 114 | } 115 | File fOutput = new File("resnax-output"); 116 | if(fOutput.exists()) { 117 | fOutput.delete(); 118 | } 119 | 120 | single_instance = null; 121 | } 122 | 123 | private String prev_sketch = ""; 124 | private String prev_excludes = ""; 125 | private HashSet prev_examples = new HashSet(); 126 | public List run(Example[] examples, Regex[] regexes) { 127 | // reset the previous mapping between DSL regexes and automaton regexes 128 | dsl_to_automaton_regex.clear(); 129 | 130 | // write the input examples to the example file 131 | File f = new File(example_file_path); 132 | String s = ""; 133 | HashSet example_set = new HashSet(); 134 | for(Example example : examples) { 135 | s += "\"" + example.input + "\"," + (example.output ? "+" : "-") + System.lineSeparator(); 136 | example_set.add(example.input + "," + example.output); 137 | } 138 | s+= System.lineSeparator(); 139 | 140 | // use a random ground truth, it does not matter, just a requirement of the synthesizer 141 | s += "repeatatleast(or(,or(,)),1)"; 142 | s += System.lineSeparator(); 143 | 144 | // parse the annotations to sketches 145 | String sketch = parseAnnotationToSketch(examples, regexes); 146 | 147 | HashSet exclude_set = new HashSet(); 148 | for(Regex regex : regexes) { 149 | if(regex.exclude.length == 0) continue; 150 | 151 | for(String exclude : regex.exclude) { 152 | exclude_set.add(exclude); 153 | } 154 | } 155 | 156 | String excludes = ""; 157 | for(String e : exclude_set) { 158 | excludes += e + "&&"; 159 | } 160 | 161 | if(!excludes.isEmpty()) { 162 | excludes = excludes.substring(0, excludes.length() - 2); 163 | } else { 164 | excludes = ","; 165 | } 166 | 167 | boolean restart; 168 | HashSet copy = new HashSet(prev_examples); 169 | copy.removeAll(example_set); 170 | if(process == null) { 171 | // this is the first iteration 172 | counter = 0; 173 | restart = true; 174 | } else if(!sketch.equals(prev_sketch) || !excludes.equals(prev_excludes) || !copy.isEmpty()) { 175 | // user intent may have changed, redo the synthesis from scratch 176 | counter = 0; 177 | restart = true; 178 | } else if(process != null && !process.isAlive()) { 179 | // the synthesis process has crashed due to error such as out of memory 180 | // have to restart 181 | counter = 0; 182 | restart = true; 183 | } else { 184 | restart = false; 185 | } 186 | 187 | // set the signal 188 | s += "READY-" + counter; 189 | 190 | try { 191 | // write the examples to the example file 192 | FileUtils.write(f, s, Charset.defaultCharset(), false); 193 | } catch (IOException e) { 194 | e.printStackTrace(); 195 | } 196 | 197 | try { 198 | invokeResnax(sketch, excludes, restart); 199 | } catch (IOException e1) { 200 | e1.printStackTrace(); 201 | } catch (InterruptedException e1) { 202 | e1.printStackTrace(); 203 | } 204 | 205 | // get the new synthesized programs 206 | ArrayList new_regexes = new ArrayList(); 207 | try { 208 | File log_file = new File(program_file_path); 209 | if(log_file.exists()) { 210 | List lines = FileUtils.readLines(log_file, Charset.defaultCharset()); 211 | for(int i = 0; i < lines.size()-1; i+=2) { 212 | String curr_dsl_regex = lines.get(i).trim(); 213 | String curr_automaton_regex = lines.get(i+1).trim(); 214 | if(!new_regexes.contains(curr_dsl_regex)) { 215 | // avoid duplication 216 | new_regexes.add(curr_dsl_regex); 217 | dsl_to_automaton_regex.put(curr_dsl_regex, curr_automaton_regex); 218 | } 219 | } 220 | 221 | if(new_regexes.isEmpty()) { 222 | System.err.println("Synthesis timeout. No program is generated."); 223 | } 224 | 225 | log_file.delete(); 226 | } else { 227 | System.err.println("No resnax log file exists. The synthesizer crashed."); 228 | } 229 | } catch (IOException e) { 230 | e.printStackTrace(); 231 | } 232 | 233 | prev_sketch = sketch; 234 | prev_excludes = excludes; 235 | prev_examples = example_set; 236 | counter++; 237 | 238 | return new_regexes; 239 | } 240 | 241 | public void invokeResnax(String sketch, String excludes, boolean restart) throws IOException, InterruptedException { 242 | File fError = new File("resnax-error"); 243 | File fOutput = new File("resnax-output"); 244 | if(restart) { 245 | // check if the process is still alive, if it is , then kill it. 246 | if(process != null && process.isAlive()) { 247 | process.destroy(); 248 | Thread.sleep(2000); 249 | if(process.isAlive()) { 250 | process.destroyForcibly(); 251 | } 252 | } 253 | 254 | int maxMem; 255 | try { 256 | // This is specific to Oracle JVM 257 | long memorySize = ((com.sun.management.OperatingSystemMXBean) ManagementFactory 258 | .getOperatingSystemMXBean()).getTotalPhysicalMemorySize(); 259 | maxMem = (int) (memorySize / (1024 * 1024 * 1024)); 260 | } catch(Exception e) { 261 | // catch any exceptions that arise in other JVMs 262 | // if any exception occurs, make a conversative choice of only allocating a max of 8G memory 263 | maxMem = 8; 264 | } 265 | 266 | String jvmBitVersion = System.getProperty("sun.arch.data.model"); 267 | if(jvmBitVersion.equals("32")) { 268 | // If the JVM is 32-bit, we can only allocate a max of 4G memory. 269 | maxMem = 4; 270 | 271 | // If it is a Windows system, be more conservative and only allocate 1G memory 272 | String os = System.getProperty("os.name").toLowerCase(); 273 | if(os.indexOf("win") >= 0) { 274 | maxMem = 1; 275 | } 276 | } 277 | 278 | // invoke resnax in a separate thread and set a timeout 279 | String[] cmd = {"java", "-Xmx" + maxMem + "G", "-Djava.library.path=" + z3_lib_path, 280 | "-cp", java_class_path, "-ea", "MRGA.Main", 281 | "0", // dataset : 0 - so, 1 - deepregex, 2 - kb13 282 | example_file_path, // file path to the input-output examples 283 | log_dir_path, // path to the log directory 284 | sketch, 285 | "1", 286 | "2", // mode : 1 - normal, 2 - prune, 3 - forgot, 4 - pure-enumeration, 5 - example-only 287 | "0", // extended mode, what is this? 288 | temp_dir_path, 289 | "5", 290 | excludes, 291 | example_file_path, 292 | program_file_path, 293 | timeout * 1000 + "", 294 | ",", 295 | ",", 296 | ","}; 297 | ProcessBuilder processBuilder = new ProcessBuilder(cmd); 298 | processBuilder.redirectError(fError); 299 | processBuilder.redirectOutput(fOutput); 300 | process = processBuilder.start(); 301 | } 302 | 303 | while(true) { 304 | // wait till the signal is there 305 | File f = new File(program_file_path); 306 | if(f.exists()) { 307 | String data = FileUtils.readFileToString(f, Charset.defaultCharset()); 308 | if (data.contains("READY-" + counter)) { 309 | break; 310 | } 311 | } else if (fError.exists()) { 312 | String errorMessage = FileUtils.readFileToString(fError, Charset.defaultCharset()); 313 | if(!errorMessage.trim().isEmpty()) { 314 | // an error occurs 315 | if(fOutput.exists()) { 316 | System.out.println(FileUtils.readFileToString(fOutput, Charset.defaultCharset())); 317 | fOutput.delete(); 318 | } 319 | System.out.println("Error occurs during program synthesis"); 320 | System.out.println(errorMessage); 321 | fError.delete(); 322 | return; 323 | } 324 | } 325 | 326 | 327 | // sleep 5 seconds 328 | Thread.sleep(5000); 329 | } 330 | 331 | if(fError.exists()) { 332 | System.out.println(FileUtils.readFileToString(fError, Charset.defaultCharset())); 333 | fError.delete(); 334 | } 335 | if(fOutput.exists()) { 336 | System.out.println(FileUtils.readFileToString(fOutput, Charset.defaultCharset())); 337 | fOutput.delete(); 338 | } 339 | } 340 | 341 | public String parseAnnotationToSketch(Example[] examples, Regex[] regexes) { 342 | HashSet exact_matches = new HashSet(); 343 | HashSet not_matches = new HashSet(); 344 | HashSet char_families = new HashSet(); 345 | HashSet includes = new HashSet(); 346 | 347 | for(Example example : examples) { 348 | if(example.output) { 349 | // only consider exact match in positive examples 350 | for(String s : example.exact) { 351 | exact_matches.add(s); 352 | } 353 | } 354 | 355 | if(!example.output) { 356 | // only consider unmatch in negative examples 357 | for(String s : example.unmatch) { 358 | not_matches.add(s); 359 | } 360 | } 361 | 362 | for(String s : example.generalize) { 363 | String char_family = s.substring(s.lastIndexOf("@@@") + 3); 364 | if(char_family.equals("any")) { 365 | // handle it outside 366 | continue; 367 | } 368 | 369 | char_families.add('<' + char_family + '>'); 370 | 371 | // comment out this heuristic, seems not so helpful 372 | // if(!example.output) { 373 | // char_families.add("not(contain(<" + char_family + ">))"); 374 | // } 375 | } 376 | } 377 | 378 | for(Regex regex : regexes) { 379 | for(String s : regex.include) { 380 | includes.add(s); 381 | } 382 | } 383 | 384 | for(Regex regex : regexes) { 385 | for(String s : regex.include) { 386 | includes.add(s); 387 | } 388 | } 389 | 390 | String sketch = "?"; 391 | String sketch_includes = ""; 392 | HashSet single_chars = new HashSet(); 393 | HashSet sequences = new HashSet(); 394 | 395 | for(String match : exact_matches) { 396 | if(match.length() == 1) { 397 | // a single character 398 | single_chars.add("<" + match + ">"); 399 | } else { 400 | if(match.equals("--")) { 401 | // this is a trick 402 | // handle -- as a sequence 403 | sequences.add("concat(<->,<->)"); 404 | continue; 405 | } 406 | char[] chars = match.toCharArray(); 407 | // Option 1: treat multiple characters as a sequence 408 | String s = ""; 409 | for(int i = 0; i < chars.length - 1; i++) { 410 | s += "concat(<" + chars[i] + ">,"; 411 | } 412 | s+= "<" + chars[chars.length - 1] + ">"; 413 | for(int i = 0; i < chars.length - 1; i++) { 414 | s+= ")"; 415 | } 416 | sequences.add(s); 417 | // Option 2: treat multiple characters separately, not as a sequence 418 | for(char c : chars) { 419 | single_chars.add("<" + c + ">"); 420 | } 421 | } 422 | } 423 | 424 | for(String unmatch : not_matches) { 425 | if(unmatch.length() == 1) { 426 | // a single character 427 | single_chars.add("<" + unmatch + ">"); 428 | } else { 429 | char[] chars = unmatch.toCharArray(); 430 | // Option 1: treat multiple characters as a sequence 431 | String s = ""; 432 | for(int i = 0; i < chars.length - 1; i++) { 433 | s += "concat(<" + chars[i] + ">,"; 434 | } 435 | s+= "<" + chars[chars.length - 1] + ">"; 436 | for(int i = 0; i < chars.length - 1; i++) { 437 | s+= ")"; 438 | } 439 | sequences.add(s); 440 | 441 | // Option 2: treat multiple characters separately, not as a sequence 442 | for(char c : chars) { 443 | single_chars.add("<" + c + ">"); 444 | } 445 | } 446 | } 447 | 448 | for(String char_family : char_families) { 449 | sequences.add(char_family); 450 | } 451 | 452 | for(String include : includes) { 453 | if(include.equals("repeatatleast")) { 454 | include = "repeatatleast(?,-1)"; 455 | } else if (include.equals("contain")) { 456 | include = "contain(?)"; 457 | } else if (include.equals("or")) { 458 | include = "or(?,?)"; 459 | } else if (include.equals("startwith")) { 460 | include = "startwith(?)"; 461 | } else if (include.equals("endwith")) { 462 | include = "endwith(?)"; 463 | } else if (include.equals("optional")) { 464 | include = "optional(?)"; 465 | } else if (include.equals("star")) { 466 | include = "star(?)"; 467 | } else if (include.equals("kleenestar")) { 468 | include = "kleenestar(?)"; 469 | } else if (include.equals("repeat")) { 470 | include = "repeat(?,-1)"; 471 | continue; 472 | } else if (include.equals("repeatrange")) { 473 | include = "repeatrange(?,-1,-1)"; 474 | } else if (include.equals("concat")) { 475 | include = "concat(?,?)"; 476 | } else if (include.equals("not")) { 477 | include = "not(?)"; 478 | } else if (include.equals("notcc")) { 479 | include = "notcc(?)"; 480 | } else if (include.equals("and")) { 481 | include = "and(?,?)"; 482 | } else if (include.equals("or")) { 483 | include = "or(?,?)"; 484 | } else if (include.equals("sep")) { 485 | include = "sep(?,?)"; 486 | } 487 | 488 | sketch_includes += include + ","; 489 | } 490 | 491 | ArrayList l = new ArrayList(single_chars); 492 | if(l.size() > 1) { 493 | sketch += "{"; 494 | // add a disjunction of all single chars 495 | for(int i = 0; i < l.size() - 1; i++) { 496 | sketch += "or(" + l.get(i)+ ","; 497 | } 498 | sketch += l.get(l.size() - 1) ; 499 | for(int i = 0; i < l.size() - 1; i++) { 500 | sketch += ")"; 501 | } 502 | 503 | // then add individual chars 504 | for(int i = 0; i < l.size(); i++) { 505 | sketch += "," + l.get(i); 506 | } 507 | } else if (l.size() == 1) { 508 | sketch += "{" + l.get(0); 509 | } 510 | 511 | if(sequences.size() > 0) { 512 | if(sketch.contains("{")) { 513 | sketch += ","; 514 | } else { 515 | sketch += "{"; 516 | } 517 | 518 | for(String sequence : sequences) { 519 | sketch += sequence + ","; 520 | } 521 | 522 | if(sketch.endsWith(",")) { 523 | sketch = sketch.substring(0, sketch.length() - 1); 524 | } 525 | } 526 | 527 | if(!sketch_includes.isEmpty()) { 528 | if(sketch.contains("{")) { 529 | sketch += "," + sketch_includes; 530 | } else { 531 | sketch += "{" + sketch_includes; 532 | } 533 | } 534 | 535 | if(sketch.contains("{")) { 536 | if(sketch.endsWith(",")) { 537 | sketch = sketch.substring(0, sketch.length() - 1); 538 | } 539 | sketch += "}"; 540 | } 541 | 542 | return sketch; 543 | } 544 | } 545 | -------------------------------------------------------------------------------- /back-end/src/main/java/edu/harvard/seas/synthesis/ServerCommandLineParser.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis; 2 | 3 | import java.io.File; 4 | import org.apache.commons.cli.CommandLine; 5 | import org.apache.commons.cli.CommandLineParser; 6 | import org.apache.commons.cli.DefaultParser; 7 | import org.apache.commons.cli.HelpFormatter; 8 | import org.apache.commons.cli.Option; 9 | import org.apache.commons.cli.Options; 10 | import org.apache.commons.cli.ParseException; 11 | 12 | public class ServerCommandLineParser { 13 | private final static String APPLICATION_NAME = "java -jar ips-backend.jar"; 14 | 15 | // config for program synthesis 16 | public int timeout; // Optional. 60 seconds by default. 17 | // public String z3_lib_path; // Required. 18 | public String resnax_path; // Required. 19 | 20 | 21 | // config for input example geneartion 22 | // public String python3_path; // Optional. 23 | // public String input_generator_path; // Required. 24 | public int num_of_examples_per_cluster; // Optional. 5 by default. 25 | 26 | public boolean parse(String[] args) { 27 | CommandLineParser parser = new DefaultParser(); 28 | CommandLine commandLine = null; 29 | try{ 30 | commandLine = parser.parse(ServerCommandLineParser.getOptions(), args); 31 | } catch (ParseException e){ 32 | if(!(args.length == 1 && (args[0].equals("-h") || args[0].equals("--helper")))){ 33 | System.out.println("Could not parse arguments" + System.lineSeparator() 34 | + "Exception information:" + System.lineSeparator() + e.getLocalizedMessage()); 35 | } 36 | 37 | printHelp(commandLine); 38 | return false; 39 | } 40 | 41 | assert(commandLine != null); 42 | 43 | // this.z3_lib_path = commandLine.getOptionValue('z'); 44 | this.resnax_path = commandLine.getOptionValue('s').trim(); 45 | File f = new File(resnax_path); 46 | if(f.exists()) { 47 | this.resnax_path = f.getAbsolutePath(); 48 | } else { 49 | System.out.println("The path to the program synthesizer's libraries, " + this.resnax_path + 50 | ", is not valid. The directory does not exist."); 51 | return false; 52 | } 53 | 54 | // this.input_generator_path = commandLine.getOptionValue('g').trim(); 55 | // f = new File(input_generator_path); 56 | // if(f.exists()) { 57 | // this.input_generator_path = f.getAbsolutePath(); 58 | // } else { 59 | // System.out.println("The path to the input generator's script, " + this.input_generator_path + 60 | // ", is not valid. The script does not exist."); 61 | // return false; 62 | // } 63 | 64 | if(commandLine.hasOption('t')) { 65 | try { 66 | this.timeout = Integer.parseInt(commandLine.getOptionValue('t').trim()); 67 | } catch(NumberFormatException e) { 68 | System.out.println("Please make sure the value of the timeout option is a valid integer."); 69 | return false; 70 | } 71 | 72 | if(this.timeout <= 0) { 73 | System.out.println("Please make sure the value of the timeout option is a positive number."); 74 | return false; 75 | } 76 | } else { 77 | this.timeout = 60; 78 | } 79 | 80 | // if(commandLine.hasOption('p')) { 81 | // this.python3_path = commandLine.getOptionValue('p').trim(); 82 | // f = new File(python3_path); 83 | // if(f.exists()) { 84 | // this.python3_path = f.getAbsolutePath(); 85 | // } else { 86 | // System.out.println("The path to the python command, " + this.python3_path + ", is not valid. " 87 | // + "The python file does not exist."); 88 | // return false; 89 | // } 90 | // } else { 91 | // this.python3_path = "python"; 92 | // } 93 | 94 | // check python version 95 | // String[] cmd = {python3_path, "--version"}; 96 | // ProcessBuilder processBuilder = new ProcessBuilder(cmd); 97 | // File fError = new File("tmp-error"); 98 | // processBuilder.redirectError(fError); 99 | // File fOutput = new File("tmp-output"); 100 | // processBuilder.redirectOutput(fOutput); 101 | // try { 102 | // Process process = processBuilder.start(); 103 | // process.waitFor(); 104 | // 105 | // String output = FileUtils.readFileToString(fOutput, Charset.defaultCharset()); 106 | // String error = FileUtils.readFileToString(fError, Charset.defaultCharset()); 107 | // 108 | // if(!error.isEmpty() || !output.contains("Python 3.")) { 109 | // System.out.println("Please make sure you have installed Python 3 " 110 | // + "or have provided the correct path to the Python 3 command using the p option."); 111 | // return false; 112 | // } 113 | // } catch (IOException e) { 114 | // e.printStackTrace(); 115 | // } catch (InterruptedException e) { 116 | // e.printStackTrace(); 117 | // } finally { 118 | // fError.delete(); 119 | // fOutput.delete(); 120 | // } 121 | 122 | if(commandLine.hasOption('n')) { 123 | try { 124 | this.num_of_examples_per_cluster = Integer.parseInt(commandLine.getOptionValue('n')); 125 | } catch(NumberFormatException e) { 126 | System.out.println("Please make sure the value of the example number option is a valid integer."); 127 | return false; 128 | } 129 | 130 | if(this.timeout <= 0) { 131 | System.out.println("Please make sure the value of the example number option is a positive number."); 132 | return false; 133 | } 134 | } else { 135 | this.num_of_examples_per_cluster = 5; 136 | } 137 | 138 | return true; 139 | } 140 | 141 | private static void printHelp(CommandLine commandLine){ 142 | HelpFormatter helpFormatter = new HelpFormatter(); 143 | String header = ""; 144 | String footer = ""; 145 | 146 | helpFormatter.printHelp(APPLICATION_NAME, header, getOptions(),footer, true); 147 | System.out.println(); 148 | } 149 | 150 | private static Options getOptions(){ 151 | // Option z3PathOption = Option.builder("z") 152 | // .desc("Specify the path for Z3") 153 | // .longOpt("z3") 154 | // .hasArg() 155 | // .required(true) 156 | // .optionalArg(false) 157 | // .build(); 158 | 159 | Option helper = Option.builder("h") 160 | .desc("Print the help information.") 161 | .longOpt("help") 162 | .hasArg(false) 163 | .required(false) 164 | .build(); 165 | 166 | // I have reimplemented London's python code in Java. 167 | // We do not need to run python any more. 168 | // Option pythonPathOption = Option.builder("p") 169 | // .desc("Specify which python to run. This option should " 170 | // + "be set if you have multiple python versions in your OS. " 171 | // + "Current we only support python 3. If not specified, we will " 172 | // + "use the default python in your OS.") 173 | // .longOpt("python") 174 | // .hasArg() 175 | // .required(false) 176 | // .build(); 177 | 178 | // Option inputGeneratorOption = Option.builder("g") 179 | // .desc("Specify the path to the input example generator.") 180 | // .longOpt("input-generator") 181 | // .hasArg() 182 | // .required(true) 183 | // .optionalArg(false) 184 | // .build(); 185 | 186 | Option numPerClusterOption = Option.builder("n") 187 | .desc("Specify the number of input examples generated per cluster per example seed. The default value is 5.") 188 | .longOpt("example-num") 189 | .hasArg() 190 | .required(false) 191 | .build(); 192 | 193 | Option resnaxLibPathOption = Option.builder("s") 194 | .desc("Specify the path for the program synthesis libraries") 195 | .longOpt("synthesizer") 196 | .hasArg() 197 | .required(true) 198 | .optionalArg(false) 199 | .build(); 200 | 201 | Option timeoutOption = Option.builder("t") 202 | .desc("Specify the timeout for the synthesis. 60 seconds if not specified.") 203 | .longOpt("timeout") 204 | .hasArg() 205 | .required(false) 206 | .build(); 207 | 208 | Options toReturn = new Options(); 209 | toReturn.addOption(resnaxLibPathOption); 210 | toReturn.addOption(timeoutOption); 211 | // toReturn.addOption(z3PathOption); 212 | // toReturn.addOption(pythonPathOption); 213 | // toReturn.addOption(inputGeneratorOption); 214 | toReturn.addOption(numPerClusterOption); 215 | toReturn.addOption(helper); 216 | 217 | return toReturn; 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /back-end/src/main/java/edu/harvard/seas/synthesis/SynthesisServer.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis; 2 | 3 | import org.eclipse.jetty.server.Server; 4 | import org.eclipse.jetty.websocket.server.WebSocketHandler; 5 | import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; 6 | 7 | import java.text.SimpleDateFormat; 8 | import java.util.Date; 9 | 10 | @Deprecated 11 | public class SynthesisServer { 12 | 13 | public static void main(String[] args) throws Exception { 14 | 15 | ServerCommandLineParser cmdParser = new ServerCommandLineParser(); 16 | boolean b = cmdParser.parse(args); 17 | if(!b) { 18 | return; 19 | } 20 | 21 | // config the synthesizer and the input generator 22 | ResnaxRunner.resnax_path = cmdParser.resnax_path; 23 | ResnaxRunner.timeout = cmdParser.timeout; 24 | // ExampleBasedInputGenerator.input_generator_path = cmdParser.input_generator_path; 25 | ExampleBasedInputGenerator.num_of_examples_per_cluster = cmdParser.num_of_examples_per_cluster; 26 | // ExampleBasedInputGenerator.python3_path = cmdParser.python3_path; 27 | 28 | Server server = new Server(8070); 29 | WebSocketHandler wsHandler = new WebSocketHandler() { 30 | @Override 31 | public void configure(WebSocketServletFactory factory) { 32 | factory.register(SynthesisServerHandler.class); 33 | } 34 | }; 35 | 36 | server.setHandler(wsHandler); 37 | server.start(); 38 | server.join(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /back-end/src/main/java/edu/harvard/seas/synthesis/SynthesisServerHandler.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis; 2 | 3 | import java.io.IOException; 4 | import java.text.MessageFormat; 5 | import java.util.*; 6 | 7 | import edu.harvard.seas.synthesis.logging.SynthesisLogger; 8 | import org.eclipse.jetty.websocket.api.Session; 9 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; 10 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; 11 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; 12 | import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; 13 | import org.eclipse.jetty.websocket.api.annotations.WebSocket; 14 | 15 | import com.fasterxml.jackson.core.JsonGenerationException; 16 | import com.fasterxml.jackson.core.JsonParseException; 17 | import com.fasterxml.jackson.databind.JsonMappingException; 18 | import com.fasterxml.jackson.databind.ObjectMapper; 19 | 20 | import dk.brics.automaton.Automaton; 21 | import dk.brics.automaton.BasicOperations; 22 | import dk.brics.automaton.RegExp; 23 | import dk.brics.automaton.State; 24 | 25 | @WebSocket 26 | public class SynthesisServerHandler { 27 | Session session = null; 28 | private String ip = null; 29 | 30 | @OnWebSocketClose 31 | public void onClose(int statusCode, String reason) { 32 | this.session = null; 33 | System.out.println("Close: statusCode=" + statusCode + ", reason=" 34 | + reason); 35 | } 36 | 37 | @OnWebSocketError 38 | public void onError(Throwable t) { 39 | System.out.println("Error: " + t.getMessage()); 40 | } 41 | 42 | @OnWebSocketConnect 43 | public void onConnect(Session session) { 44 | this.session = session; 45 | this.ip = session.getRemoteAddress().getAddress().getHostAddress(); 46 | 47 | System.out.println("Connect: " 48 | + session.getRemoteAddress().getAddress()); 49 | } 50 | 51 | private static HashMap dsl_to_automaton = null; 52 | 53 | @OnWebSocketMessage 54 | public void onMessage(String message) { 55 | System.out.println("Message: "); 56 | System.out.println(message); 57 | SynthesisLogger.getSynthesisLogger().logString(message); 58 | 59 | if(message.equals("Reset") || message.equals("Window closed")) { 60 | // the synthesis task has been changed 61 | dsl_to_automaton = null; 62 | ResnaxRunner.reset(); 63 | } else if (message.startsWith("Synthesize Regexes:")) { 64 | String runID = UUID.randomUUID().toString(); 65 | message = message.substring("Synthesize Regexes: ".length()); 66 | String s1 = message.split("\n")[0]; 67 | String s2 = message.split("\n")[1]; 68 | // Parse the json message 69 | ObjectMapper mapper = new ObjectMapper(); 70 | try { 71 | Example[] examples = mapper.readValue(s1, Example[].class); 72 | Regex[] regexes = mapper.readValue(s2, Regex[].class); 73 | 74 | SynthesisLogger.getSynthesisLogger().logString(MessageFormat.format("[Run:{0}] Number of examples: {1}", runID, examples.length)); 75 | logOperatorAnnotation(runID, regexes); 76 | 77 | // invoke the resnax runner 78 | ResnaxRunner runner = ResnaxRunner.getInstance(); 79 | List new_regexes = runner.run(examples, regexes); 80 | if(new_regexes.size() > 20) { 81 | // only display the first 20 to avoid an error "Frame is too large" 82 | new_regexes = new_regexes.subList(0, 20); 83 | } 84 | sendJSONMessage(new_regexes, "regexes"); 85 | dsl_to_automaton = runner.dsl_to_automaton_regex; 86 | 87 | if(new_regexes.size() == 0){ 88 | SynthesisLogger.getSynthesisLogger().logString(MessageFormat.format("[Run:{0}] Synthesis timed out", runID)); 89 | } else { 90 | SynthesisLogger.getSynthesisLogger().logObjectWithMessage(MessageFormat.format("[Run:{0}] Synthesis successful. Generated {1} regexes.", runID, new_regexes.size()), new_regexes); 91 | } 92 | 93 | } catch (JsonParseException e) { 94 | e.printStackTrace(); 95 | } catch (JsonMappingException e) { 96 | e.printStackTrace(); 97 | } catch (IOException e) { 98 | e.printStackTrace(); 99 | } 100 | } else if (message.startsWith("Generate Examples:")) { 101 | message = message.substring("Generate Examples: ".length()); 102 | String s1 = message.split("\n")[0]; 103 | String s2 = message.split("\n")[1]; 104 | // Parse the json message 105 | ObjectMapper mapper = new ObjectMapper(); 106 | try { 107 | String[] examples = mapper.readValue(s1, String[].class); 108 | String[] regexes = mapper.readValue(s2, String[].class); 109 | 110 | if(dsl_to_automaton == null) { 111 | return; 112 | } 113 | 114 | // generate the synthetic examples 115 | if(regexes.length == 1) { 116 | Map> similarExamples = generateSimilarExamples(regexes[0], examples); 117 | Map> wildExamples = generateWildExamples(regexes[0], examples); 118 | 119 | // reorder wild examples to show unseen corner cases first 120 | Map> reorderedWildExamples = 121 | reorderWildExamples(similarExamples, wildExamples); 122 | sendJSONMessage(similarExamples, reorderedWildExamples, "examples"); 123 | 124 | SynthesisLogger.getSynthesisLogger().logObjectWithMessage("similar examples: ", similarExamples); 125 | SynthesisLogger.getSynthesisLogger().logObjectWithMessage("wild examples: ", reorderedWildExamples); 126 | } else { 127 | // the user wants to generate examples for multiple regexes 128 | // focus on generating input examples that exhibit different behaviors among those regexes 129 | Map> similarExamples = 130 | generateSimilarDistinguishingExamples(regexes, examples); 131 | Map> wildExamples = 132 | generateWildDistinguishingExamples(regexes); 133 | 134 | sendJSONMessage(similarExamples, wildExamples, "examples"); 135 | 136 | SynthesisLogger.getSynthesisLogger().logObjectWithMessage("similar examples: ", similarExamples); 137 | SynthesisLogger.getSynthesisLogger().logObjectWithMessage("wild examples: ", wildExamples); 138 | } 139 | } catch (JsonParseException e) { 140 | e.printStackTrace(); 141 | } catch (JsonMappingException e) { 142 | e.printStackTrace(); 143 | } catch (IOException e) { 144 | e.printStackTrace(); 145 | } 146 | } 147 | } 148 | 149 | private void logOperatorAnnotation(String runID, Regex[] regexes){ 150 | try { 151 | if (regexes.length <= 1) { 152 | return; 153 | } 154 | List regexList = Arrays.asList(regexes); 155 | regexList = regexList.subList(1, regexList.size()); 156 | 157 | StringBuilder stringBuilder = new StringBuilder(); 158 | int count = 0; 159 | List annotations = new ArrayList<>(); 160 | 161 | for(Regex regex : regexList) { 162 | stringBuilder.delete(0, stringBuilder.length()); 163 | if (regex.include.length == 0 && regex.exclude.length == 0) { 164 | continue; 165 | } 166 | stringBuilder.append("[" + regex.regex + "] "); 167 | if (regex.include.length > 0) { 168 | count += regex.include.length; 169 | stringBuilder.append("include: "); 170 | stringBuilder.append(String.join(",", regex.include)); 171 | stringBuilder.append(";"); 172 | } 173 | if (regex.exclude.length > 0) { 174 | count += regex.exclude.length; 175 | stringBuilder.append("exclude: "); 176 | stringBuilder.append(String.join(",", regex.exclude)); 177 | stringBuilder.append(";"); 178 | } 179 | 180 | annotations.add(stringBuilder.toString()); 181 | } 182 | 183 | SynthesisLogger.getSynthesisLogger().logString(MessageFormat.format("[Run:{0}] Number of Operator annotations: {1} \n{2}", runID, count, String.join("\n", annotations))); 184 | } 185 | catch (Exception e) { 186 | SynthesisLogger.getSynthesisLogger().logException(e); 187 | } 188 | 189 | } 190 | 191 | private Map> reorderWildExamples(Map> familiarExamples, 192 | Map> wildExamples) { 193 | LinkedHashMap> map = new LinkedHashMap>(); 194 | 195 | // 1. add empty string cluster first 196 | for(String s : wildExamples.keySet()) { 197 | if(s.contains("non-empty string")) { 198 | map.put(s, wildExamples.get(s)); 199 | break; 200 | } 201 | } 202 | 203 | // 2. add clusters about char length 204 | for(String s : wildExamples.keySet()) { 205 | if(s.contains("more than")) { 206 | map.put(s, wildExamples.get(s)); 207 | break; 208 | } 209 | } 210 | 211 | // 3. add clusters that have not been seen in the familiar examples 212 | for(String s : wildExamples.keySet()) { 213 | if(!familiarExamples.containsKey(s)) { 214 | map.put(s, wildExamples.get(s)); 215 | } 216 | } 217 | 218 | // 4. add the remaining clusters 219 | for(String s : wildExamples.keySet()) { 220 | if(!map.containsKey(s)) { 221 | map.put(s, wildExamples.get(s)); 222 | } 223 | } 224 | 225 | return map; 226 | } 227 | 228 | private Map> generateSimilarExamples(String regex, String[] examples) { 229 | String automaton_regex = dsl_to_automaton.get(regex); 230 | Map> clusters = new LinkedHashMap>(); 231 | ExampleBasedInputGenerator exGen = new ExampleBasedInputGenerator(); 232 | for(String example : examples) { 233 | Map> map = exGen.generate(example, automaton_regex, regex); 234 | for(String explanation : map.keySet()) { 235 | Map cluster = map.get(explanation); 236 | if(clusters.containsKey(explanation)) { 237 | Map existingCluster = clusters.get(explanation); 238 | existingCluster.putAll(cluster); 239 | clusters.put(explanation, existingCluster); 240 | } else { 241 | Map new_cluster = new HashMap(); 242 | new_cluster.putAll(cluster); 243 | clusters.put(explanation, new_cluster); 244 | } 245 | } 246 | } 247 | 248 | if(clusters.size() == 1 249 | && clusters.containsKey("Positive examples only")) { 250 | String s = "We didn't find any negative examples. It seems this regex can accept any string. " + 251 | "Do you want to double check some corner cases instead?"; 252 | Map cluster = clusters.get("Positive examples only"); 253 | clusters.clear(); 254 | clusters.put(s, cluster); 255 | } else { 256 | if(clusters.containsKey("Positive examples only")) { 257 | clusters.remove("Positive examples only"); 258 | } 259 | } 260 | 261 | return clusters; 262 | } 263 | 264 | private Map> generateWildExamples(String regex, String[] examples) { 265 | String automaton_regex = dsl_to_automaton.get(regex); 266 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator(regex); 267 | Map> clusters = gen.generate(automaton_regex, examples); 268 | return clusters; 269 | } 270 | 271 | private Map> generateSimilarDistinguishingExamples(String[] regexes, String[] examples) { 272 | Map> distinguishing_examples = new LinkedHashMap>(); 273 | 274 | // convert regexes to automata 275 | Automaton[] automata = new Automaton[regexes.length]; 276 | for(int i = 0; i < regexes.length; i++) { 277 | String automaton_regex = dsl_to_automaton.get(regexes[i]); 278 | RegExp exp = new RegExp(automaton_regex); 279 | Automaton a = exp.toAutomaton(); 280 | automata[i] = a; 281 | } 282 | 283 | HashSet tested = new HashSet(); 284 | for(int i = 0; i < regexes.length; i++) { 285 | String regex = regexes[i]; 286 | Map> clusters = generateSimilarExamples(regex, examples); 287 | for(String explanation : clusters.keySet()) { 288 | Map cluster = clusters.get(explanation); 289 | for(String example : cluster.keySet()) { 290 | boolean result = cluster.get(example); 291 | if(!result) { 292 | // remove the failure-inducing character index appended at the end of the neagtive example 293 | example = example.substring(0, example.lastIndexOf(',')); 294 | } 295 | 296 | if(tested.contains(example)) { 297 | continue; 298 | } 299 | 300 | Boolean[] results = new Boolean[regexes.length]; 301 | results[i] = result; 302 | for(int j = 0; j < regexes.length; j++) { 303 | if(j != i) { 304 | boolean result2 = automata[j].run(example); 305 | results[j] = result2; 306 | } 307 | } 308 | 309 | for(Boolean b : results) { 310 | if(b != result) { 311 | // this is a distinguishing example 312 | // generate explanation for all failed regexes 313 | String explanation2 = 314 | ExplanationGenerator.generateExplanation(example, automata, regexes, results); 315 | 316 | Map map; 317 | if(distinguishing_examples.containsKey(explanation2)) { 318 | map = distinguishing_examples.get(explanation2); 319 | } else { 320 | map = new HashMap(); 321 | } 322 | 323 | map.put(example, results); 324 | distinguishing_examples.put(explanation2, map); 325 | break; 326 | } 327 | } 328 | 329 | tested.add(example); 330 | } 331 | } 332 | } 333 | 334 | return distinguishing_examples; 335 | } 336 | 337 | private Map> generateWildDistinguishingExamples(String[] regexes) { 338 | Map> distinguishing_examples = new HashMap>(); 339 | 340 | Automaton[] automata = new Automaton[regexes.length]; 341 | for(int i = 0; i < regexes.length; i++) { 342 | String automaton_regex = dsl_to_automaton.get(regexes[i]); 343 | RegExp exp = new RegExp(automaton_regex); 344 | Automaton a = exp.toAutomaton(); 345 | automata[i] = a; 346 | } 347 | 348 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator(); 349 | for(int i = 0; i < automata.length; i++) { 350 | Automaton a1 = automata[i]; 351 | for(int j = i + 1; j < automata.length; j++) { 352 | Automaton a2 = automata[j]; 353 | Automaton diff1 = BasicOperations.minus(a1, a2); 354 | ArrayList> clusters = 355 | gen.generateInputStrings(diff1, new HashSet>(), true); 356 | for(Map cluster : clusters) { 357 | for(String example : cluster.keySet()) { 358 | Boolean[] results = new Boolean[automata.length]; 359 | results[i] = true; 360 | results[j] = false; 361 | for(int k = 0; k < automata.length; k++) { 362 | if(k!=i && k!=j) { 363 | results[k] = automata[k].run(example); 364 | } 365 | } 366 | 367 | String explanation = 368 | ExplanationGenerator.generateExplanation(example, automata, regexes, results); 369 | 370 | Map map; 371 | if(distinguishing_examples.containsKey(explanation)) { 372 | map = distinguishing_examples.get(explanation); 373 | } else { 374 | map = new HashMap(); 375 | } 376 | 377 | map.put(example, results); 378 | distinguishing_examples.put(explanation, map); 379 | } 380 | } 381 | 382 | Automaton diff2 = BasicOperations.minus(a2, a1); 383 | clusters = gen.generateInputStrings(diff2, new HashSet>(), true); 384 | for(Map cluster : clusters) { 385 | for(String example : cluster.keySet()) { 386 | Boolean[] results = new Boolean[automata.length]; 387 | results[i] = false; 388 | results[j] = true; 389 | for(int k = 0; k < automata.length; k++) { 390 | if(k!=i && k!=j) { 391 | results[k] = automata[k].run(example); 392 | } 393 | } 394 | 395 | String explanation = 396 | ExplanationGenerator.generateExplanation(example, automata, regexes, results); 397 | 398 | Map map; 399 | if(distinguishing_examples.containsKey(explanation)) { 400 | map = distinguishing_examples.get(explanation); 401 | } else { 402 | map = new HashMap(); 403 | } 404 | 405 | map.put(example, results); 406 | distinguishing_examples.put(explanation, map); 407 | } 408 | } 409 | } 410 | } 411 | 412 | return distinguishing_examples; 413 | } 414 | 415 | private void sendJSONMessage(Object sentObject, String header) { 416 | // send the JSON message to the front end 417 | ObjectMapper mapper = new ObjectMapper(); 418 | try { 419 | String jsonMessage = mapper.writeValueAsString(sentObject); 420 | System.out.println(jsonMessage); 421 | session.getRemote().sendString(header + ": " + jsonMessage); 422 | } catch (JsonGenerationException e) { 423 | e.printStackTrace(); 424 | } catch (JsonMappingException e) { 425 | e.printStackTrace(); 426 | } catch (IOException e) { 427 | e.printStackTrace(); 428 | } 429 | } 430 | 431 | private void sendJSONMessage(Object o1, Object o2, String header) { 432 | // send the JSON message to the front end 433 | ObjectMapper mapper = new ObjectMapper(); 434 | try { 435 | String jsonMessage1 = mapper.writeValueAsString(o1); 436 | System.out.println(jsonMessage1); 437 | String jsonMessage2 = mapper.writeValueAsString(o2); 438 | System.out.println(jsonMessage2); 439 | session.getRemote().sendString(header + ": " + jsonMessage1 + "\n" + jsonMessage2); 440 | } catch (JsonGenerationException e) { 441 | e.printStackTrace(); 442 | } catch (JsonMappingException e) { 443 | e.printStackTrace(); 444 | } catch (IOException e) { 445 | e.printStackTrace(); 446 | } 447 | } 448 | } 449 | -------------------------------------------------------------------------------- /back-end/src/main/java/edu/harvard/seas/synthesis/logging/SynthesisLogger.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis.logging; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | 5 | import edu.harvard.seas.synthesis.HTTPServer; 6 | 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.nio.file.Paths; 10 | import java.text.MessageFormat; 11 | import java.util.Date; 12 | import java.util.logging.*; 13 | 14 | public class SynthesisLogger { 15 | 16 | public static boolean isLoggingEnabled = false; // disable logging by default 17 | 18 | private static SynthesisLogger _synthesisLogger; 19 | 20 | private final Logger logger = Logger.getLogger(SynthesisLogger.class.getName()); 21 | private FileHandler fileHandler = null; 22 | private SimpleFormatter formatter = null; 23 | 24 | public static SynthesisLogger getSynthesisLogger() { 25 | if(_synthesisLogger == null){ 26 | _synthesisLogger = new SynthesisLogger(); 27 | } 28 | 29 | return _synthesisLogger; 30 | } 31 | 32 | private SynthesisLogger(){ 33 | logger.setLevel(Level.ALL); 34 | logger.setUseParentHandlers(false); 35 | 36 | // Setting custom formatter 37 | formatter = new SimpleFormatter() { 38 | private static final String format = "$$$ [%1$tF %1$tT] [%2$-7s] %3$s %n"; 39 | 40 | @Override 41 | public synchronized String format(LogRecord lr) { 42 | return String.format(format, 43 | new Date(lr.getMillis()), 44 | lr.getLevel().getLocalizedName(), 45 | lr.getMessage() 46 | ); 47 | } 48 | }; 49 | 50 | try { 51 | String currentDirectory = System.getProperty("user.dir"); 52 | Path logDirectory = Paths.get( currentDirectory, "/instrument_log/").toAbsolutePath(); 53 | Files.createDirectories(logDirectory); 54 | String logfile_path = Paths.get(logDirectory.toAbsolutePath().toString(), "synthesizer_log_" + HTTPServer.session_id + ".log").toString(); 55 | fileHandler = new FileHandler(logfile_path, true); 56 | fileHandler.setFormatter(formatter); 57 | logger.addHandler(fileHandler); 58 | } catch (Exception e){ 59 | System.out.println(e.getStackTrace()); 60 | } 61 | } 62 | 63 | private boolean canLog() { 64 | return isLoggingEnabled && (fileHandler != null); 65 | } 66 | 67 | public void logString(String message){ 68 | if(!canLog()) { 69 | return; 70 | } 71 | try { 72 | LogRecord lr = new LogRecord(Level.INFO, MessageFormat.format("{0}\r\n", message)); 73 | logger.log(lr); 74 | } 75 | catch (Exception e){ 76 | logException(e); 77 | } 78 | } 79 | 80 | public void logException(Exception e){ 81 | if(!canLog()) { 82 | return; 83 | } 84 | try { 85 | LogRecord lr = new LogRecord(Level.SEVERE, MessageFormat.format("{0}\r\n", e.getStackTrace())); 86 | logger.log(lr); 87 | } 88 | catch (Exception exp){ 89 | System.out.println(exp.getStackTrace()); 90 | } 91 | } 92 | 93 | public void logObject(Object obj) { 94 | if(!canLog()) { 95 | return; 96 | } 97 | 98 | ObjectMapper mapper = new ObjectMapper(); 99 | try { 100 | LogRecord lr = new LogRecord(Level.INFO, MessageFormat.format("{0}\r\n", mapper.writeValueAsString(obj))); 101 | logger.log(lr); 102 | } 103 | catch (Exception e){ 104 | logException(e); 105 | } 106 | } 107 | 108 | public void logObjectWithMessage(String message, Object obj) { 109 | if(!canLog()) { 110 | return; 111 | } 112 | 113 | ObjectMapper mapper = new ObjectMapper(); 114 | try { 115 | LogRecord lr = new LogRecord(Level.INFO, MessageFormat.format("{0} {1}\r\n", message, mapper.writeValueAsString(obj))); 116 | logger.log(lr); 117 | } 118 | catch (Exception e){ 119 | logException(e); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /back-end/src/test/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/src/test/.DS_Store -------------------------------------------------------------------------------- /back-end/src/test/java/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/src/test/java/.DS_Store -------------------------------------------------------------------------------- /back-end/src/test/java/edu/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/src/test/java/edu/.DS_Store -------------------------------------------------------------------------------- /back-end/src/test/java/edu/harvard/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/src/test/java/edu/harvard/.DS_Store -------------------------------------------------------------------------------- /back-end/src/test/java/edu/harvard/seas/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tianyi-zhang/interactive-program-synthesis/f5b737837a7f8e114b04fba89ec19d2b97fecc13/back-end/src/test/java/edu/harvard/seas/.DS_Store -------------------------------------------------------------------------------- /back-end/src/test/java/edu/harvard/seas/synthesis/CoverageDrivenInputGenerationTest.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.util.ArrayList; 7 | import java.util.HashSet; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.Set; 11 | 12 | import org.junit.Test; 13 | 14 | import dk.brics.automaton.Automaton; 15 | import dk.brics.automaton.BasicOperations; 16 | import dk.brics.automaton.RegExp; 17 | import dk.brics.automaton.State; 18 | 19 | public class CoverageDrivenInputGenerationTest { 20 | @Test 21 | public void testGeneratePositiveExample1() { 22 | String regex = "([^D]){1,}"; 23 | RegExp exp = new RegExp(regex); 24 | Automaton a = exp.toAutomaton(); // two states, one state has a loop 25 | System.out.println(a.toDot()); 26 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator(); 27 | Set> pathsToAccept = gen.enumeratePaths(a); 28 | assertEquals(2, pathsToAccept.size()); 29 | 30 | List> clusters = gen.generateBasedOnPath(pathsToAccept, true); 31 | assertEquals(2, clusters.size()); 32 | } 33 | 34 | @Test 35 | public void testGenerateCornerCase1() { 36 | String regex = "([^D]){1,}"; // repeatatleast(notcc(),1) 37 | 38 | String[] examples = new String[2]; 39 | examples[0] = "A"; 40 | examples[1] = "B"; 41 | 42 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator(); 43 | Map> clusters = gen.generate(regex, examples); 44 | 45 | assertEquals(4, clusters.size()); // 8 clusters if not clustering by explanation 46 | } 47 | 48 | @Test 49 | public void testGeneratePositiveExample2() { 50 | String regex = "([^D]){1,10}"; 51 | RegExp exp = new RegExp(regex); 52 | Automaton a = exp.toAutomaton(); // 11 states, sequential, 10 of them are accept states 53 | // System.out.println(a.toDot()); 54 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator(); 55 | Set> pathsToAccept = gen.enumeratePaths(a); 56 | assertEquals(10, pathsToAccept.size()); 57 | 58 | List> clusters = gen.generateBasedOnPath(pathsToAccept, true); 59 | assertEquals(10, clusters.size()); 60 | } 61 | 62 | @Test 63 | public void testGenerateCornerCase2() { 64 | String regex = "([^D]){1,10}"; // repeatatleast(notcc(),1,10) 65 | 66 | String[] examples = new String[2]; 67 | examples[0] = "A"; 68 | examples[1] = "B"; 69 | 70 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator(); 71 | Map> clusters = gen.generate(regex, examples); 72 | 73 | assertEquals(12, clusters.size()); // 76 clusters if not clustering by explanation 74 | } 75 | 76 | @Test 77 | public void testGeneratePositiveExample3() { 78 | String regex = ".*((([C])|([A])))"; 79 | RegExp exp = new RegExp(regex); 80 | Automaton a = exp.toAutomaton(); // two states, each state has a loop, and there is a loop between two states 81 | // System.out.println(a.toDot()); 82 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator(); 83 | Set> pathsToAccept = gen.enumeratePaths(a); 84 | assertEquals(7, pathsToAccept.size()); 85 | 86 | List> clusters = gen.generateBasedOnPath(pathsToAccept, true); 87 | assertEquals(7, clusters.size()); 88 | } 89 | 90 | @Test 91 | public void testGenerateCornerCase3() { 92 | String regex = ".*((([C])|([A])))"; // endwith(or(,)) 93 | 94 | String[] examples = new String[2]; 95 | examples[0] = "A"; 96 | examples[1] = "C"; 97 | 98 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator("endwith(or(,)"); 99 | Map> clusters = gen.generate(regex, examples); 100 | 101 | assertEquals(2, clusters.size()); // 14 clusters if not clustered by explanation 102 | } 103 | 104 | @Test 105 | public void testGeneratePositiveExample4() { 106 | String regex = "((([A])|((([B])|([C]))))){1,}"; 107 | RegExp exp = new RegExp(regex); 108 | Automaton a = exp.toAutomaton(); // 2 states, one with a loop 109 | // System.out.println(a.toDot()); 110 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator(); 111 | Set> pathsToAccept = gen.enumeratePaths(a); 112 | assertEquals(2, pathsToAccept.size()); 113 | 114 | List> clusters = gen.generateBasedOnPath(pathsToAccept, true); 115 | assertEquals(2, clusters.size()); 116 | } 117 | 118 | @Test 119 | public void testGenerateCornerCase4() { 120 | String regex = "((([A])|((([B])|([C]))))){1,}"; // repeatatleast(or(,or(,),1)) 121 | 122 | String[] examples = new String[2]; 123 | examples[0] = "A"; 124 | examples[1] = "C"; 125 | 126 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator("repeatatleast(or(,or(,),1))"); 127 | Map> clusters = gen.generate(regex, examples); 128 | 129 | assertEquals(4, clusters.size()); // 8 clusters if not clustered by explanation 130 | } 131 | 132 | @Test 133 | public void testGeneratePositiveExample5() { 134 | String regex = "(([+])?)(([9])([0-9]))"; 135 | RegExp exp = new RegExp(regex); 136 | Automaton a = exp.toAutomaton(); // 4 states, there is a branch, one accept state 137 | // System.out.println(a.toDot()); 138 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator(); 139 | Set> pathsToAccept = gen.enumeratePaths(a); 140 | assertEquals(2, pathsToAccept.size()); 141 | 142 | List> clusters = gen.generateBasedOnPath(pathsToAccept, true); 143 | assertEquals(2, clusters.size()); 144 | } 145 | 146 | @Test 147 | public void testGenerateCornerCase5() { 148 | String regex = "(([+])?)(([9])([0-9]))"; 149 | 150 | String[] examples = new String[2]; 151 | examples[0] = "A"; 152 | examples[1] = "C"; 153 | 154 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator("concat(optional(<+>),concat(<9>,))"); 155 | Map> clusters = gen.generate(regex, examples); 156 | 157 | assertEquals(8, clusters.size()); // 8 clusters if not clustered by explanation 158 | } 159 | 160 | @Test 161 | public void testGenerateCornerCase6() { 162 | String regex = "((.*([B]).*)|([A]))"; 163 | 164 | String[] examples = new String[2]; 165 | examples[0] = "A"; 166 | examples[1] = "C"; 167 | 168 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator("or(contain(),)"); 169 | Map> clusters = gen.generate(regex, examples); 170 | 171 | assertEquals(2, clusters.size()); // 2 clusters if not clustered by explanation 172 | } 173 | 174 | @Test 175 | public void testGenerateCornerCase7() { 176 | String regex = "(([+])?).*"; 177 | 178 | String[] examples = new String[2]; 179 | examples[0] = "A"; 180 | examples[1] = "C"; 181 | 182 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator("startwith(optional(<+>))"); 183 | Map> clusters = gen.generate(regex, examples); 184 | 185 | assertEquals(1, clusters.size()); 186 | String header = "We didn't find any negative examples. This regex can accept any string."; 187 | assertEquals(header, clusters.keySet().iterator().next()); 188 | } 189 | 190 | @Test 191 | public void testGenerateNegativeExample1() { 192 | String regex = "([^D]){1,}"; 193 | RegExp exp = new RegExp(regex); 194 | Automaton a = exp.toAutomaton(); // two states, one state has a loop 195 | // Automaton a2 = BasicOperations.complement(a); 196 | // System.out.println(a2.toDot()); 197 | 198 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator(); 199 | ArrayList> negatives = gen.generateInputStrings(a, new HashSet>(), false); 200 | 201 | assertEquals(7, negatives.size()); 202 | } 203 | 204 | @Test 205 | public void testGenerateNegativeExample2() { 206 | String regex = "([^D]){1,10}"; 207 | RegExp exp = new RegExp(regex); 208 | Automaton a = exp.toAutomaton(); // 11 states, sequential, 10 of them are accept states 209 | // Automaton a2 = BasicOperations.complement(a); 210 | // System.out.println(a2.toDot()); 211 | 212 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator(); 213 | ArrayList> negatives = gen.generateInputStrings(a, new HashSet>(), false); 214 | 215 | assertEquals(67, negatives.size()); 216 | } 217 | 218 | @Test 219 | public void testGenerateNegativeExample3() { 220 | String regex = "(.*((([C])|([A]))).*)"; 221 | RegExp exp = new RegExp(regex); 222 | Automaton a = exp.toAutomaton(); 223 | // System.out.println(a.toDot()); 224 | // Automaton a2 = BasicOperations.complement(a); 225 | // System.out.println(a2.toDot()); 226 | 227 | CoverageDrivenInputGenerator gen = new CoverageDrivenInputGenerator("contain(or(,))"); 228 | ArrayList> negatives = gen.generateInputStrings(a, new HashSet>(), false); 229 | assertEquals(2, negatives.size()); 230 | 231 | assertTrue(negatives.get(1).keySet().iterator().next().contains(",0")); 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /back-end/src/test/java/edu/harvard/seas/synthesis/InputGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.Set; 11 | import java.util.concurrent.ThreadLocalRandom; 12 | 13 | import org.junit.Ignore; 14 | import org.junit.Test; 15 | 16 | import dk.brics.automaton.Automaton; 17 | import dk.brics.automaton.BasicOperations; 18 | import dk.brics.automaton.RegExp; 19 | import dk.brics.automaton.State; 20 | import dk.brics.automaton.Transition; 21 | 22 | public class InputGeneratorTest { 23 | 24 | @Test 25 | @Ignore 26 | public void testAutomatonToFile1() { 27 | String regex = "([^D]){1,}"; 28 | RegExp exp = new RegExp(regex); 29 | Automaton a = exp.toAutomaton(); 30 | ExampleBasedInputGenerator gen = new ExampleBasedInputGenerator(); 31 | String s = gen.writeAutomatonToFile(a); 32 | System.out.println(s); 33 | } 34 | 35 | @Test 36 | @Ignore 37 | public void testAutomatonToFile2() { 38 | String regex = ".*((([C])|([A])))"; 39 | RegExp exp = new RegExp(regex); 40 | Automaton a = exp.toAutomaton(); 41 | ExampleBasedInputGenerator gen = new ExampleBasedInputGenerator(); 42 | String s = gen.writeAutomatonToFile(a); 43 | System.out.println(s); 44 | } 45 | 46 | @Test 47 | @Ignore 48 | public void testAutomatonToFile3() { 49 | String regex = "([^D]){1,10}"; 50 | RegExp exp = new RegExp(regex); 51 | Automaton a = exp.toAutomaton(); 52 | ExampleBasedInputGenerator gen = new ExampleBasedInputGenerator(); 53 | String s = gen.writeAutomatonToFile(a); 54 | System.out.println(s); 55 | } 56 | 57 | @Test 58 | @Ignore 59 | public void testAutomatonToFile4() { 60 | String regex = "((([A])|((([B])|([C]))))){1,}"; 61 | RegExp exp = new RegExp(regex); 62 | Automaton a = exp.toAutomaton(); 63 | ExampleBasedInputGenerator gen = new ExampleBasedInputGenerator(); 64 | String s = gen.writeAutomatonToFile(a); 65 | System.out.println(s); 66 | } 67 | 68 | @Test 69 | @Ignore 70 | public void testAutomatonToFile5() { 71 | String regex = "(([+])?)(([9])([0-9]))"; 72 | RegExp exp = new RegExp(regex); 73 | Automaton a = exp.toAutomaton(); 74 | ExampleBasedInputGenerator gen = new ExampleBasedInputGenerator(); 75 | String s = gen.writeAutomatonToFile(a); 76 | System.out.println(s); 77 | } 78 | 79 | 80 | @Test 81 | @Ignore 82 | public void testAutomatonToFile6() { 83 | String regex = "(([+])?)(([1-9])([1-9]))"; 84 | RegExp exp = new RegExp(regex); 85 | Automaton a = exp.toAutomaton(); 86 | ExampleBasedInputGenerator gen = new ExampleBasedInputGenerator(); 87 | String s = gen.writeAutomatonToFile(a); 88 | System.out.println(s); 89 | } 90 | 91 | @Test 92 | public void testInputGenerator1() { 93 | String regex = "(([+])?)(([1-9])([1-9]))"; // concat(optional(<+>),concat(,) 94 | String example = "91"; 95 | ExampleBasedInputGenerator gen = new ExampleBasedInputGenerator(); 96 | Map> clusters = gen.generate(example, regex, "concat(optional(<+>),concat(,)"); 97 | assertEquals(3, clusters.size()); 98 | } 99 | 100 | @Test 101 | @Ignore 102 | public void testInputGenerator2() { 103 | String regex = "(([+])?)(([9])([0-9]))"; // concat(optional(<+>),concat(<9>,) 104 | String example = "+91"; 105 | ExampleBasedInputGenerator gen = new ExampleBasedInputGenerator(); 106 | Map> clusters = gen.generate(example, regex, "concat(optional(<+>),concat(<9>,)"); 107 | assertEquals(4, clusters.size()); // this could be flaky depends on the sampled examples. 108 | } 109 | 110 | @Test 111 | public void testInputGenerator3() { 112 | String regex = "((([A])|((([B])|([C]))))){1,}"; 113 | String example = "ABC"; 114 | ExampleBasedInputGenerator gen = new ExampleBasedInputGenerator(); 115 | Map> clusters = gen.generate(example, regex, "repeatatleast(or(,or(,),1))"); 116 | assertEquals(3, clusters.size()); 117 | } 118 | 119 | @Test 120 | public void testInputGenerator4() { 121 | String regex = "([^D]){1,10}"; 122 | String example = "ABC"; 123 | ExampleBasedInputGenerator gen = new ExampleBasedInputGenerator(); 124 | Map> clusters = gen.generate(example, regex, "repeatatleast(not(),1,10))"); 125 | assertEquals(3, clusters.size()); 126 | } 127 | 128 | @Test 129 | public void testInputGenerator5() { 130 | String regex = "~(.*(([-])([-])).*)"; 131 | String example = "a-b"; 132 | ExampleBasedInputGenerator gen = new ExampleBasedInputGenerator(); 133 | Map> clusters = gen.generate(example, regex, "not(contain(concat(<->,<->)))"); 134 | assertEquals(1, clusters.size()); 135 | } 136 | 137 | @Test 138 | public void testInputGenerator6() { 139 | String regex = "(([+])?).*"; 140 | String example = "+abc"; 141 | ExampleBasedInputGenerator gen = new ExampleBasedInputGenerator(); 142 | Map> clusters = gen.generate(example, regex, "startwith(optional(<+>))"); 143 | // it can be matched with anything!!!! 144 | assertEquals(1, clusters.size()); 145 | String header = "Positive examples only"; 146 | assertEquals(header, clusters.keySet().iterator().next()); 147 | } 148 | 149 | @Test 150 | public void testInputGenerator7() { 151 | String regex = ".*(([1])([0-9])).*"; 152 | String example = "123"; 153 | ExampleBasedInputGenerator gen = new ExampleBasedInputGenerator(); 154 | Map> clusters = gen.generate(example, regex, "contain(concat(<1>,))"); 155 | assertEquals(1, clusters.size()); 156 | } 157 | 158 | @Test 159 | public void testInputGenerator8() { 160 | String regex = ".*(([0-9])([2])).*"; 161 | String example = "12+"; 162 | ExampleBasedInputGenerator gen = new ExampleBasedInputGenerator(); 163 | Map> clusters = gen.generate(example, regex, " contain(concat(,<2>))"); 164 | assertEquals(1, clusters.size()); 165 | } 166 | 167 | @Test 168 | public void testAutomatonMinus() { 169 | String regex1 = "([^D]){1,}"; 170 | String regex2 = "([^D]){1,10}"; 171 | RegExp exp1 = new RegExp(regex1); 172 | RegExp exp2 = new RegExp(regex2); 173 | Automaton automaton1 = exp1.toAutomaton(); 174 | Automaton automaton2 = exp2.toAutomaton(); 175 | Automaton diff1 = BasicOperations.minus(automaton1, automaton2); 176 | String accept1 = getShortestExample(diff1, true); 177 | System.out.println(diff1.toDot()); 178 | System.out.println(accept1); 179 | 180 | Automaton diff2 = BasicOperations.minus(automaton2, automaton1); 181 | String accept2 = getShortestExample(diff2, true); 182 | System.out.println(diff2.toDot()); 183 | System.out.println(accept2); 184 | } 185 | 186 | public static String getShortestExample(Automaton a, boolean accepted) { 187 | if (a.getSingleton() != null) { 188 | if (accepted) 189 | return a.getSingleton(); 190 | else if (a.getSingleton().length() > 0) 191 | return ""; 192 | else 193 | return "\u0000"; 194 | 195 | } 196 | return getShortestExample(a.getInitialState(), accepted); 197 | } 198 | 199 | static String getShortestExample(State s, boolean accepted) { 200 | Map path = new HashMap(); 201 | LinkedList queue = new LinkedList(); 202 | path.put(s, ""); 203 | queue.add(s); 204 | String best = null; 205 | while (!queue.isEmpty()) { 206 | State q = queue.removeFirst(); 207 | String p = path.get(q); 208 | if (q.isAccept() == accepted) { 209 | if (best == null || p.length() < best.length() || (p.length() == best.length() && p.compareTo(best) < 0)) 210 | best = p; 211 | } else { 212 | for (Transition t : q.getTransitions()) { 213 | String tp = path.get(t.getDest()); 214 | char min = t.getMin(); 215 | if(min < '\u0020') { 216 | min = '\u0020'; 217 | } 218 | char max = t.getMax(); 219 | if(max > '\u007E') { 220 | max = '\u007E'; 221 | } 222 | 223 | // pick a random number between min and max 224 | int randomNum = ThreadLocalRandom.current().nextInt(min, max + 1); 225 | char c = (char) randomNum; 226 | String np = p + c; 227 | if (tp == null || (tp.length() == np.length() && np.compareTo(tp) < 0)) { 228 | if (tp == null) 229 | queue.addLast(t.getDest()); 230 | path.put(t.getDest(), np); 231 | } 232 | } 233 | } 234 | } 235 | return best; 236 | } 237 | } -------------------------------------------------------------------------------- /back-end/src/test/java/edu/harvard/seas/synthesis/ResnaxRunnerTest.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import java.util.List; 8 | 9 | import org.junit.After; 10 | import org.junit.Test; 11 | 12 | public class ResnaxRunnerTest { 13 | 14 | @After 15 | public void cleanup() { 16 | ResnaxRunner.reset(); 17 | } 18 | 19 | @Test 20 | public void test() { 21 | ResnaxRunner runner = ResnaxRunner.getInstance(); 22 | List l = null; 23 | try { 24 | Example[] examples = new Example[2]; 25 | Example e1 = new Example(); 26 | e1.input = "a"; 27 | e1.output = true; 28 | e1.exact = new String[0]; 29 | e1.unmatch = new String[0]; 30 | e1.generalize = new String[0]; 31 | examples[0] = e1; 32 | Example e2 = new Example(); 33 | e2.input = "A"; 34 | e2.output = false; 35 | e2.exact = new String[0]; 36 | e2.unmatch = new String[0]; 37 | e2.generalize = new String[0]; 38 | examples[1] = e2; 39 | Regex[] regexes = new Regex[0]; 40 | l = runner.run(examples, regexes); 41 | } finally { 42 | ResnaxRunner.reset(); 43 | } 44 | 45 | assertEquals(5, l.size()); 46 | assertTrue(l.contains("")); 47 | } 48 | 49 | @Test 50 | public void testIncremental() { 51 | ResnaxRunner runner = ResnaxRunner.getInstance(); 52 | List l = null; 53 | try { 54 | Example[] examples = new Example[2]; 55 | Example e1 = new Example(); 56 | e1.input = "a"; 57 | e1.output = true; 58 | e1.exact = new String[0]; 59 | e1.unmatch = new String[0]; 60 | e1.generalize = new String[0]; 61 | examples[0] = e1; 62 | Example e2 = new Example(); 63 | e2.input = "A"; 64 | e2.output = false; 65 | e2.exact = new String[0]; 66 | e2.unmatch = new String[0]; 67 | e2.generalize = new String[0]; 68 | examples[1] = e2; 69 | Regex[] regexes = new Regex[0]; 70 | l = runner.run(examples, regexes); 71 | 72 | assertEquals(5, l.size()); 73 | assertTrue(l.contains("")); 74 | assertTrue(l.contains("")); 75 | 76 | examples = new Example[3]; 77 | examples[0] = e1; 78 | examples[1] = e2; 79 | Example e3 = new Example(); 80 | e3.input = "b"; 81 | e3.output = true; 82 | e3.exact = new String[0]; 83 | e3.unmatch = new String[0]; 84 | e3.generalize = new String[0]; 85 | examples[2] = e3; 86 | l = runner.run(examples, regexes); 87 | 88 | assertEquals(5, l.size()); 89 | assertTrue(l.contains("")); 90 | assertFalse(l.contains("")); 91 | } finally { 92 | ResnaxRunner.reset(); 93 | } 94 | } 95 | 96 | @Test 97 | public void testTimeout() { 98 | ResnaxRunner runner = ResnaxRunner.getInstance(); 99 | List l = null; 100 | try { 101 | Example[] examples = new Example[2]; 102 | Example e1 = new Example(); 103 | e1.input = "a"; 104 | e1.output = true; 105 | e1.exact = new String[0]; 106 | e1.unmatch = new String[0]; 107 | e1.generalize = new String[0]; 108 | examples[0] = e1; 109 | Example e2 = new Example(); 110 | e2.input = "A"; 111 | e2.output = false; 112 | e2.exact = new String[0]; 113 | e2.unmatch = new String[0]; 114 | e2.generalize = new String[0]; 115 | examples[1] = e2; 116 | Regex[] regexes = new Regex[0]; 117 | l = runner.run(examples, regexes); 118 | 119 | assertEquals(5, l.size()); 120 | assertTrue(l.contains("")); 121 | assertTrue(l.contains("")); 122 | 123 | // add a conflicting example 124 | examples = new Example[3]; 125 | examples[0] = e1; 126 | examples[1] = e2; 127 | Example e3 = new Example(); 128 | e3.input = "a"; 129 | e3.output = false; 130 | e3.exact = new String[0]; 131 | e3.unmatch = new String[0]; 132 | e3.generalize = new String[0]; 133 | examples[2] = e3; 134 | l = runner.run(examples, regexes); 135 | 136 | assertEquals(0, l.size()); 137 | 138 | // remove the conflicting example 139 | examples = new Example[2]; 140 | examples[0] = e1; 141 | examples[1] = e2; 142 | l = runner.run(examples, regexes); 143 | 144 | assertEquals(5, l.size()); 145 | assertTrue(l.contains("")); 146 | assertTrue(l.contains("")); 147 | } finally { 148 | ResnaxRunner.reset(); 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /back-end/src/test/java/edu/harvard/seas/synthesis/ServerCommandParserTest.java: -------------------------------------------------------------------------------- 1 | package edu.harvard.seas.synthesis; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import org.junit.Ignore; 8 | import org.junit.Test; 9 | 10 | public class ServerCommandParserTest { 11 | 12 | @Test 13 | public void testPrintHelper() { 14 | String[] cmd = new String[1]; 15 | cmd[0] = "-h"; 16 | ServerCommandLineParser cmdParser = new ServerCommandLineParser(); 17 | cmdParser.parse(cmd); 18 | } 19 | 20 | @Test 21 | public void testShortRequiredOptions() { 22 | String path = "/Users/tz/Research/Interactive Program Synthesis/interactive-program-synthesizer/back-end/lib"; 23 | String[] cmd = new String[4]; 24 | cmd[0] = "-s"; 25 | cmd[1] = path; 26 | cmd[2] = "-n"; 27 | cmd[3] = "10"; 28 | ServerCommandLineParser cmdParser = new ServerCommandLineParser(); 29 | assertTrue(cmdParser.parse(cmd)); 30 | assertEquals(path, cmdParser.resnax_path); 31 | assertEquals(10, cmdParser.num_of_examples_per_cluster); 32 | } 33 | 34 | @Test 35 | @Ignore 36 | public void testWrongPythonVersion() { 37 | String path = "/Users/tz/Research/Interactive Program Synthesis/interactive-program-synthesizer/back-end/lib"; 38 | String[] cmd = new String[2]; 39 | cmd[0] = "-s"; 40 | cmd[1] = path; 41 | ServerCommandLineParser cmdParser = new ServerCommandLineParser(); 42 | assertFalse(cmdParser.parse(cmd)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /front-end/comparison.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Interactive Program Synthesizer for Regular Expressions 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |

Interactive Program Synthesizer for Regular Expressions

26 |
27 |
28 | Programming Task 29 | : 38 |
39 | Write a regular expression that accepts strings that contain + or digits but no ++. 40 | 41 | 42 | 43 | 44 |
45 |
46 |
47 | 48 |
49 |
50 |
51 |
52 |

Examples

53 |
54 |
55 |

Regex Candidates

56 |
57 |
58 |
59 |
60 |
61 |
62 | 67 |
68 |
69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 151 | 152 | 153 | 180 | 181 | 182 | 227 | 228 | 229 | 283 | 284 |
InputOutput
285 | 286 |
287 |
288 |
289 | 290 | 291 | 292 |
293 | 294 |
295 |
296 | 0% 297 |
298 |
299 |
300 | 302 |
303 |
304 | 316 |
317 | 318 |
319 |
320 | 323 |
324 |
325 |
326 | Character Family
327 | <num> --- a digit from 0 to 9)
328 | <num1-9> --- a digit from 1 to 9)
329 | <let> --- an English letter (a - z and A - Z)
330 | <low> --- a lowercase letter (a-z)
331 | <cap> --- a uppercase letter (A-Z)
332 | <any> --- any character
333 | <alphanum> --- any alphanumeric character (0 - 9, a - z, A - Z)
334 |
335 | Regex Operator
336 | <startwith(r)> --- returns true if a given string starts with r
337 | <endwith(r)> --- returns true if a given string ends with r
338 | <contain(r)> --- returns true if any substring matches r
339 | <notcc(c)> --- returns true if not containing a character c
340 | <concat(r1,r2)> --- returns true if the first part of a string matches r1 and the second part of the string matches r2
341 | <or(r1,r2)> --- returns true if a given string matches either r1 or r2
342 | <and(r1,r2)> --- returns true if a given string matches both r1 and r2
343 | <not(r)> --- returns true if a given string does not match r
344 | <optional(r)> --- returns true if a given string matches r zero or one time
345 | <star(r)> --- returns true if a given string matches r zero or more times
346 | <repeat(r,k)> --- returns true if r repeats k times in a given string
347 | <repeatatleast(r,k)> --- returns true if r repeats at least k times in a given string
348 | <repeatrange(r,k1,k2)> --- returns true if r repeats between k1 to k2 times in a given string
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 | 357 | 358 | -------------------------------------------------------------------------------- /front-end/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Interactive Program Synthesizer for Regular Expressions 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 67 | 68 |
69 |

Interactive Program Synthesizer for Regular Expressions

70 |
71 |
72 | Programming Task 73 | : 82 |
83 | Write a regular expression that accepts strings that contain + or digits but no ++. 84 | 85 | 86 | 87 | 88 |
89 |
90 |
91 | 92 |
93 |
94 |
95 |

Examples

96 |
97 |
98 |

Regex Candidates

99 |
100 |
101 |

Show me more examples

102 |

so I don't have to come up with my own

103 |
104 |
105 |
106 |
107 |
108 | 109 | 110 | 111 |
112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 194 | 195 | 196 | 223 | 224 | 225 | 270 | 271 | 272 | 326 | 327 |
InputOutput
328 | 329 |
330 |
331 |
332 | 333 | 334 | 335 |
336 | 337 |
338 |
339 | 0% 340 |
341 |
342 |
343 | 345 |
346 |
347 | 359 |
360 | 361 |
362 |
363 | 366 |
367 |
368 |
369 | Character Family
370 | <num> --- a digit from 0 to 9)
371 | <num1-9> --- a digit from 1 to 9)
372 | <let> --- an English letter (a - z and A - Z)
373 | <low> --- a lowercase letter (a-z)
374 | <cap> --- a uppercase letter (A-Z)
375 | <any> --- any character
376 | <alphanum> --- any alphanumeric character (0 - 9, a - z, A - Z)
377 |
378 | Regex Operator
379 | <startwith(r)> --- returns true if a given string starts with r
380 | <endwith(r)> --- returns true if a given string ends with r
381 | <contain(r)> --- returns true if any substring matches r
382 | <notcc(c)> --- returns true if not containing a character c
383 | <concat(r1,r2)> --- returns true if the first part of a string matches r1 and the second part of the string matches r2
384 | <or(r1,r2)> --- returns true if a given string matches either r1 or r2
385 | <and(r1,r2)> --- returns true if a given string matches both r1 and r2
386 | <not(r)> --- returns true if a given string does not match r
387 | <optional(r)> --- returns true if a given string matches r zero or one time
388 | <star(r)> --- returns true if a given string matches r zero or more times
389 | <repeat(r,k)> --- returns true if r repeats k times in a given string
390 | <repeatatleast(r,k)> --- returns true if r repeats at least k times in a given string
391 | <repeatrange(r,k1,k2)> --- returns true if r repeats between k1 to k2 times in a given string
392 |
393 |
394 |
395 |
396 | 397 |
398 |
399 | 400 | 401 | 406 |
407 |
408 | 410 |
411 |
412 | 413 | 418 | 419 | 497 | 498 |
499 |
500 |
501 |
502 |
503 | 504 | 505 | -------------------------------------------------------------------------------- /front-end/my-style.css: -------------------------------------------------------------------------------- 1 | h3 { 2 | text-align: center; 3 | margin: 10px; 4 | } 5 | 6 | .input-anno-btn { 7 | margin-top: 10px; 8 | margin-bottom: 10px 9 | } 10 | 11 | 12 | .regex { 13 | padding-top: 20px; 14 | padding-left: 40px; 15 | padding-right: 40px; 16 | padding-bottom: 20px; 17 | font-size: large; 18 | font-family:'Courier New'; 19 | color: black; 20 | } 21 | 22 | .header { 23 | margin: 20px; 24 | } 25 | 26 | .must-have { 27 | background-color:#ffc107; 28 | color: #fff; 29 | } 30 | 31 | .not-have { 32 | background-color:#6c757d; 33 | color: #fff; 34 | } 35 | 36 | .char-family { 37 | background-color:#17a2b8; 38 | color: #fff; 39 | } 40 | 41 | .sans-font { 42 | color: #404E67; 43 | font-family: 'Open Sans', sans-serif; 44 | } 45 | 46 | .match { 47 | color:green; 48 | } 49 | 50 | .unmatch { 51 | color: red; 52 | } 53 | 54 | .icon { 55 | text-align:center; 56 | } 57 | 58 | .removable { 59 | font-size: 14px; 60 | padding-right: .6em; 61 | padding-left: .6em; 62 | border-radius: 10rem; 63 | display: inline-block; 64 | line-height: 1; 65 | text-align: center; 66 | margin: 2px; 67 | font-weight: bold; 68 | background-color: #b5bab6; 69 | border-color: #b5bab6; 70 | } 71 | 72 | .annotation { 73 | font-size: 14px; 74 | } 75 | 76 | .cheatsheet { 77 | margin: 20px; 78 | } 79 | 80 | .keyword { 81 | color:#530c8e; 82 | } 83 | 84 | .cheesheetheader { 85 | padding: 0; 86 | } 87 | 88 | .tooltip-inner { 89 | text-align: left; 90 | max-width: 350px; 91 | } 92 | 93 | .code { 94 | font-family: Monaco, Consolas, Menlo, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif; 95 | } -------------------------------------------------------------------------------- /front-end/my-table.css: -------------------------------------------------------------------------------- 1 | .add-new { 2 | float: right; 3 | height: 30px; 4 | font-weight: bold; 5 | font-size: 12px; 6 | text-shadow: none; 7 | min-width: 100px; 8 | border-radius: 50px; 9 | line-height: 13px; 10 | margin-right: 4px; 11 | } 12 | 13 | table.table { 14 | table-layout: fixed; 15 | } 16 | 17 | table.table tr th, table.table tr td { 18 | border-color: #e9e9e9; 19 | padding: .40rem; 20 | } 21 | 22 | table.table th i { 23 | font-size: 13px; 24 | margin: 0 5px; 25 | cursor: pointer; 26 | } 27 | 28 | table.table th:last-child { 29 | width: 100px; 30 | } 31 | 32 | table.table td { 33 | padding:; .50rem; 34 | word-wrap: break-word; 35 | } 36 | 37 | table.table td a { 38 | cursor: pointer; 39 | display: inline-block; 40 | margin: 0 5px; 41 | min-width: 24px; 42 | } 43 | 44 | table.table td a.add { 45 | color: #27C46B; 46 | } 47 | 48 | table.table td a.edit { 49 | color: #FFC107; 50 | } 51 | 52 | table.table td a.delete { 53 | color: #E34724; 54 | } 55 | 56 | table.table td a.move { 57 | color: #27C46B; 58 | } 59 | 60 | table.table td i { 61 | font-size: 19px; 62 | } 63 | 64 | table.table td a.add i { 65 | font-size: 24px; 66 | margin-right: -1px; 67 | position: relative; 68 | top: 3px; 69 | } 70 | 71 | table.table .form-control { 72 | height: 32px; 73 | line-height: 32px; 74 | box-shadow: none; 75 | border-radius: 2px; 76 | } 77 | 78 | table.table td .add { 79 | display: none; 80 | } 81 | 82 | .cluster1 { 83 | color: #004085; 84 | background-color: #b8daff; 85 | white-space: pre; 86 | } 87 | 88 | .cluster2 { 89 | color: #155724; 90 | background-color: #c3e6cb; 91 | white-space: pre; 92 | } 93 | 94 | .cluster3 { 95 | color: #856404; 96 | background-color: #ffeeba; 97 | white-space: pre; 98 | } 99 | 100 | .cluster4 { 101 | color: #0c5460; 102 | background-color: #bee5eb; 103 | white-space: pre; 104 | } 105 | 106 | .cluster5 { 107 | color: #004085; 108 | background-color: #b8daff; 109 | white-space: pre; 110 | } 111 | 112 | .cluster6 { 113 | color: #721c24; 114 | background-color: #f5c6cb; 115 | white-space: pre; 116 | } 117 | 118 | .cluster0 { 119 | color: #383d41; 120 | background-color: #d6d8db; 121 | white-space: pre; 122 | } -------------------------------------------------------------------------------- /package.sh: -------------------------------------------------------------------------------- 1 | cd back-end 2 | mvn package 3 | cp target/back-end-0.0.1-SNAPSHOT-jar-with-dependencies.jar ../ips-backend.jar 4 | cp -R lib/ ../lib 5 | cd .. 6 | zip -r ips.zip lib/ ips-backend.jar front-end/ back-end/ 7 | zip -j ips.zip lib/libz3java.dylib lib/com.microsoft.z3.jar lib/libz3.dylib lib/libz3java.so 8 | rm -r lib/ 9 | rm ips-backend.jar --------------------------------------------------------------------------------