added Ant build file, readme, CLI and JAR build

This commit is contained in:
User 2018-03-27 17:56:36 +02:00
parent 11a0616010
commit d7683fb10d
9 changed files with 398 additions and 38 deletions

6
.gitignore vendored
View File

@ -1,3 +1,5 @@
bin/
src/pp/iloc/sample/
.meta/
bin/
dist/
doc/junit/
src/pp/iloc/sample/

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Vertalerbouw</name>
<name>boppi</name>
<comment></comment>
<projects>
</projects>

27
README.md Normal file
View File

@ -0,0 +1,27 @@
# Prerequisites
Boppi requires JDK 1.8 or higher and [Ant](https://ant.apache.org/). Both must be available in the environment.
# Installation
Run `ant build` to perform a basic build of the project: it initialises output directories, generates _ANTLR_ files and compiles all java files. To skip _ANTLR_ file generation, run `ant do-init` and `ant do-build` separately.
Run `ant build-all` to do the above and generate _javadoc_ documentation, run _JUnit_ tests and produce a runnable _JAR_ file.
To see all possible targets, run `ant -verbose -projecthelp`.
# Command line use
After building a _JAR_ file, a command `boppi` becomes available in the `dist/` folder. This command can be used to compile and run files or perform an interactive session. See `boppi --help` and `boppi interactive --help` for more information.
# Directory structure
- `bin` contains compiled java code and required text files (after `ant do-build`)
- `dist` contains a runnable JAR, script files and a copy of the libraries required to run the JAR (after `ant do-build-jar`)
- `doc` contains a report of the project and attached example files
- `doc/javadoc` contains _javadoc_ documentation of the project (after `ant do-javadoc`)
- `doc/junit` contains a report of _JUnit_ tests (after `ant do-junit` and `ant do-junit-report`)
- `lib` contains Java libraries required for the project, excluding those in the JDK 1.8
- `src`
- `src/pp/iloc` contains java code for a slightly modified ILOC virtual machine
- `src/pp/s1184725/boppi` contains java code for the _Boppi_ language
- `util` contains [Pygments](http://pygments.org/) lexers for both ILOC and Boppi and scripts to run the Boppi command line interface (used for the JAR build).

151
build.xml Normal file
View File

@ -0,0 +1,151 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project basedir="." name="boppi">
<property environment="env" />
<property name="junit.output.dir" value="doc/junit" />
<property name="g4.iloc" value="src/pp/iloc/parse" />
<property name="g4.boppi" value="src/pp/s1184725/boppi/antlr" />
<property name="debuglevel" value="source,lines,vars" />
<property name="target" value="1.8" />
<property name="source" value="1.8" />
<path id="boppi.classpath">
<pathelement location="bin" />
<pathelement location="lib/antlr-4.7-complete.jar" />
<pathelement location="lib/hamcrest-all-1.3.jar" />
<pathelement location="lib/junit-4.12.jar" />
<pathelement location="lib/commons-lang3-3.5.jar" />
<pathelement location="lib/commons-cli-1.4.jar" />
</path>
<path id="libraries.path">
<fileset dir="lib">
<include name="*.jar" />
</fileset>
</path>
<target name="build-all" depends="build,build-jar,javadoc,junit" description="Build all targets." />
<target name="build" depends="do-init,do-build-antlr,do-build" description="Build the ANTLR parser code and compile all Java code." />
<target name="build-jar" depends="build,do-build-jar" description="Build a runnable JAR to compile and run programs." />
<target name="javadoc" depends="do-build-antlr,do-javadoc" description="Generate Javadoc." />
<target name="junit" depends="build,do-junit,do-junit-report" description="Run JUnit tests and generate a report." />
<target name="clean" depends="do-clean" description="Clear all generated files." />
<target name="do-init">
<mkdir dir="bin" />
<mkdir dir="bin/lib" />
<mkdir dir="${junit.output.dir}" />
<mkdir dir="dist" />
<copy includeemptydirs="false" todir="bin">
<fileset dir="src">
<exclude name="**/*.launch" />
<exclude name="**/*.xtend" />
<exclude name="**/*.java" />
<exclude name="**/*.g4" />
</fileset>
</copy>
</target>
<target name="do-build">
<echo message="${ant.project.name}: ${ant.file}" />
<javac debug="true" debuglevel="${debuglevel}" destdir="bin" includeantruntime="false" source="${source}" target="${target}">
<src path="src" />
<classpath refid="boppi.classpath" />
</javac>
</target>
<target name="do-clean">
<delete dir="bin" />
<delete dir="doc/javadoc" />
<delete dir="dist" />
<delete dir="${junit.output.dir}" />
<delete>
<fileset dir="${g4.iloc}" casesensitive="yes">
<include name="ILOC*.java" />
<include name="*.tokens" />
</fileset>
<fileset dir="${g4.boppi}" casesensitive="yes">
<include name="Boppi*.java" />
<include name="*.tokens" />
</fileset>
</delete>
</target>
<target name="do-junit">
<junit fork="yes" dir="src" printsummary="withOutAndErr">
<formatter type="xml" />
<batchtest todir="${junit.output.dir}">
<fileset dir="src">
<include name="pp/iloc/**/*Test.java" />
<include name="pp/s1184725/**/*Test.java" />
</fileset>
</batchtest>
<jvmarg line="-ea" />
<classpath refid="boppi.classpath" />
</junit>
</target>
<target name="do-junit-report">
<junitreport todir="${junit.output.dir}">
<fileset dir="${junit.output.dir}">
<include name="TEST-*.xml" />
</fileset>
<report format="frames" todir="${junit.output.dir}" />
</junitreport>
</target>
<target name="do-build-antlr">
<java classname="org.antlr.v4.Tool">
<classpath>
<pathelement location="lib/antlr-4.7-complete.jar" />
</classpath>
<arg value="${g4.boppi}/Boppi.g4" />
<arg value="-o" />
<arg value="${g4.boppi}" />
<arg value="-package" />
<arg value="pp.s1184725.boppi.antlr" />
<arg value="-no-listener" />
<arg value="-visitor" />
</java>
<java classname="org.antlr.v4.Tool">
<classpath>
<pathelement location="lib/antlr-4.7-complete.jar" />
</classpath>
<arg value="${g4.iloc}/ILOC.g4" />
<arg value="-o" />
<arg value="${g4.iloc}" />
<arg value="-listener" />
<arg value="-no-visitor" />
</java>
</target>
<target name="do-javadoc">
<javadoc access="public" author="true" classpath="lib/commons-lang3-3.5.jar;lib/commons-cli-1.4.jar;lib/antlr-4.7-complete.jar;lib/junit-4.12.jar;lib/hamcrest-all-1.3.jar" destdir="doc/javadoc" nodeprecated="false" nodeprecatedlist="false" noindex="false" nonavbar="false" notree="false" packagenames="pp.*" source="1.8" sourcepath="src" splitindex="false" use="true" version="true">
<link href="http://www.antlr.org/api/Java/" />
<link href="http://hamcrest.org/JavaHamcrest/javadoc/1.3/" />
<link href="https://docs.oracle.com/javase/8/docs/api/" />
<link href="http://junit.org/junit4/javadoc/4.12/" />
<link href="https://commons.apache.org/proper/commons-lang/javadocs/api-3.5/" />
<link href="http://commons.apache.org/proper/commons-cli/javadocs/api-release/" />
</javadoc>
</target>
<target name="do-build-jar">
<copy todir="dist">
<fileset dir="util" includes="boppi.*" />
</copy>
<copy todir="dist/lib" flatten="true">
<path refid="libraries.path" />
</copy>
<manifestclasspath property="manifest.classpath" jarfile="${ant.project.name}.jar">
<classpath refid="libraries.path" />
</manifestclasspath>
<jar destfile="dist/${ant.project.name}.jar" basedir="bin">
<manifest>
<attribute name="Main-Class" value="pp.s1184725.boppi.util.CommandLineInterface" />
<attribute name="Class-Path" value="${manifest.classpath}" />
</manifest>
</jar>
</target>
</project>

View File

@ -0,0 +1,191 @@
package pp.s1184725.boppi.util;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.function.Predicate;
import java.util.logging.*;
import org.antlr.v4.runtime.tree.ParseTree;
import org.apache.commons.cli.*;
import pp.iloc.Assembler;
import pp.iloc.model.Program;
import pp.iloc.parse.FormatException;
import pp.s1184725.boppi.*;
/**
* Command line interface for compiling and running Boppi programs.
*
* @author Frank Wibbelink
*/
public class CommandLineInterface {
private static CommandLine cmd;
private static Level desiredLogLevel;
/**
* Runs the command line.
*
* @param args
* CLI arguments
*/
public static void main(String[] args) {
Options options = new Options();
options.addOption("o", "output", true, "output file for ILOC code");
options.addOption("d", "directory", true, "output directory for ILOC code");
options.addOption("w", "warnings", true, "set the warning level ('all', 'error' or 'none')");
options.addOption("r", "run", false, "run an ILOC program");
options.addOption("h", "help", false, "display this message");
if (args.length >= 1 && args[0].equalsIgnoreCase("interactive")) {
InteractivePrompt.main(Arrays.copyOfRange(args, 1, args.length));
return;
}
try {
cmd = new DefaultParser().parse(options, args);
} catch (ParseException e) {
System.err.println(e.getLocalizedMessage());
System.err.println("Use 'boppi -h' for help.");
return;
}
if (cmd.hasOption("h") || args.length == 0) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("boppi [interactive] [OPTION]... FILE...",
"To start an interactive prompt, have 'interactive' as the first parameter. "
+ "This mode has its own help message.",
options, "By default output files will be written to the same path as the "
+ "input files but with the .iloc extension.");
return;
}
if (cmd.hasOption("o") && cmd.getArgList().size() > 1) {
System.err.println("Cannot compile multiple files to one file.");
return;
}
switch (cmd.getOptionValue("w", "error")) {
case "all":
desiredLogLevel = Level.ALL;
break;
case "none":
desiredLogLevel = Level.OFF;
break;
default:
desiredLogLevel = Level.SEVERE;
}
if (cmd.hasOption("r")) {
for (String fileName : cmd.getArgList())
runFile(Paths.get(fileName));
} else {
for (String fileName : cmd.getArgList()) {
Path infile = Paths.get(fileName);
Path outfile;
if (cmd.hasOption("o"))
outfile = Paths.get(cmd.getOptionValue("o"));
else if (cmd.hasOption("d"))
outfile = replaceExtension(Paths.get(cmd.getOptionValue("d")).resolve(infile.getFileName()),
"iloc");
else
outfile = replaceExtension(infile, "iloc");
compileFile(infile, outfile);
}
}
}
/**
* Loads the given file as ILOC assembly and runs it with the input tied to
* {@link System#in} and the output tied to {@link System#out}. If the VM
* halts with an interrupt code, the whole program is terminated and will
* return it as the exit code.
*
* @param path
* the ILOC file to load
*/
private static void runFile(Path path) {
try {
Logger logger = Logger.getAnonymousLogger();
logger.setLevel(desiredLogLevel);
Program prog = Assembler.instance().assemble(path.toFile());
ToolChain.execute(prog, logger, System.in, System.out);
if (ToolChain.machine.getInterrupt() != 0)
System.exit(ToolChain.machine.getInterrupt());
// System.err.println(String.format("Program exited with status:
// %8x", ToolChain.machine.getInterrupt()));
} catch (FormatException | IOException e) {
e.printStackTrace();
}
}
/**
* Tries to compile a Boppi file by parsing it, typechecking it and
* generating code. The process is broken off if at any stage a
* {@link Level#SEVERE} error is encountered. If the code successfully
* compiles, it is written to the given output location.
*
* @param infile
* the Boppi file to read from
* @param outfile
* the ILOC file to write to
*/
private static void compileFile(Path infile, Path outfile) {
Logger logger = Logger.getAnonymousLogger();
List<LogRecord> logList = ToolChain.makeListLog(logger);
logger.setLevel(Level.ALL);
ParseTree ast = ToolChain.getParser(ToolChain.getLexer(ToolChain.getCharStream(infile), logger), logger)
.program();
if (logList.stream().noneMatch(severityAtLeast(Level.SEVERE))) {
Annotations an = ToolChain.getAnnotations(ast, logger);
if (logList.stream().noneMatch(severityAtLeast(Level.SEVERE))) {
Program iloc = ToolChain.getILOC(ast, logger, an);
if (logList.stream().noneMatch(severityAtLeast(Level.SEVERE))) {
try {
Files.write(outfile, iloc.prettyPrint().getBytes("utf-8"), StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
System.err.println("Cannot write to " + outfile);
}
}
}
}
logList.stream().filter(severityAtLeast(desiredLogLevel)).map(LogRecord::getMessage)
.forEach(System.err::println);
}
private static Predicate<LogRecord> severityAtLeast(Level level) {
return (logRecord) -> logRecord.getLevel().intValue() >= level.intValue();
}
private static Path replaceExtension(Path path, String newExt) {
Path folder = path.getParent();
String filename = path.getFileName().toString();
return folder == null ? Paths.get(replaceExtension(filename, newExt))
: folder.resolve(replaceExtension(filename, newExt));
}
private static String replaceExtension(String filename, String newExt) {
int index = filename.lastIndexOf('.');
if (index >= 0)
filename = filename.substring(0, index);
return filename + "." + newExt;
}
}

View File

@ -27,7 +27,8 @@ public class InteractivePrompt {
public static void main(String[] args) {
Options options = new Options();
options.addOption("s", "stateless", false, "run stateless (every command will run in a fresh environment)");
options.addOption("n", "empty-line", false, "keep reading a single command until the user enters an empty line");
options.addOption("n", "empty-line", false,
"keep reading a single command until the user enters an empty line");
options.addOption("h", "help", false, "display this message");
options.addOption("p", "print-program", false, "prints the program before executing it");
options.addOption("D", "debug", false, "runs a program in debug mode");
@ -35,8 +36,7 @@ public class InteractivePrompt {
try {
cmd = new DefaultParser().parse(options, args);
}
catch (ParseException e) {
} catch (ParseException e) {
System.err.println(e.getLocalizedMessage());
return;
}
@ -57,7 +57,7 @@ public class InteractivePrompt {
if (cmd.hasOption("s") || cmd.hasOption("k"))
stateless();
else
System.err.println("Stateful interactive mode is not supported.");
System.err.println("Stateful interactive mode is not yet supported. Please run with --stateless.");
sc.close();
}
@ -68,29 +68,29 @@ public class InteractivePrompt {
while (sc.hasNext()) {
String command = sc.next().trim();
if(command.equals(""))
if (command.equals(""))
continue;
if (command.equals("exit"))
break;
String code = saved+command;
String code = saved + command;
if (cmd.hasOption("k"))
saved = code+";\n";
saved = code + ";\n";
Program program = ToolChain.compile(ToolChain.getCharStream(code), logger);
if (cmd.hasOption("p")) {
for (String line : program.prettyPrint().split("\n"))
System.out.println("--- "+line);
System.out.println("--- " + line);
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ToolChain.execute(program, logger, System.in, out);
for (String str : out.toString().split("\r\n|\r"))
System.out.println("<<< "+str);
System.out.println("<<< " + str);
}
}

1
util/boppi.bat Normal file
View File

@ -0,0 +1 @@
@java -jar boppi.jar %*

1
util/boppi.sh Normal file
View File

@ -0,0 +1 @@
java -jar boppi.jar "$@"

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project default="javadoc">
<target name="javadoc">
<javadoc access="public" author="true" classpath="../lib/commons-lang3-3.5.jar;../lib/commons-cli-1.4.jar;../lib/antlr-4.7-complete.jar;../lib/junit-4.12.jar;../lib/hamcrest-all-1.3.jar" destdir="../doc/javadoc" nodeprecated="false" nodeprecatedlist="false" noindex="false" nonavbar="false" notree="false" packagenames="pp.*" source="1.8" sourcepath="../src" splitindex="false" use="true" version="true">
<link href="http://www.antlr.org/api/Java/"/>
<link href="http://hamcrest.org/JavaHamcrest/javadoc/1.3/"/>
<link href="https://docs.oracle.com/javase/8/docs/api/"/>
<link href="http://junit.org/junit4/javadoc/4.12/"/>
<link href="https://commons.apache.org/proper/commons-lang/javadocs/api-3.5/"/>
<link href="http://commons.apache.org/proper/commons-cli/javadocs/api-release/"/>
</javadoc>
</target>
</project>