Skip to content

Commit

Permalink
Add command line argument parsing
Browse files Browse the repository at this point in the history
Can load source files!
  • Loading branch information
Botffy committed Dec 4, 2016
1 parent b3a0a90 commit 4d79436
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 5 deletions.
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ repositories {

dependencies {
compile group: 'com.github.stefanbirkner', name: 'fishbowl', version: '1.4.0'
compile group: 'net.sourceforge.argparse4j', name: 'argparse4j', version: '0.7.0'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.5'

compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.21'
Expand Down Expand Up @@ -62,6 +63,9 @@ processResources {
}

task run(type: JavaExec) {
if(project.hasProperty("appArgs")) {
args Eval.me(appArgs)
}
main = "ppke.itk.xplang.ui.Main"
classpath = sourceSets.main.runtimeClasspath
}
1 change: 1 addition & 0 deletions example.prog
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PROGRAM testing <_< >_> >_> >_> >_> program_vége
2 changes: 1 addition & 1 deletion src/main/java/ppke/itk/xplang/ui/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ public class Main {
*/
public static void main(final String[] args) {
Program program = new Program();
program.run();
program.run(args);
}
}
48 changes: 48 additions & 0 deletions src/main/java/ppke/itk/xplang/ui/OptionParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package ppke.itk.xplang.ui;

import net.sourceforge.argparse4j.ArgumentParsers;
import net.sourceforge.argparse4j.impl.Arguments;
import net.sourceforge.argparse4j.inf.ArgumentParser;
import net.sourceforge.argparse4j.inf.ArgumentParserException;
import net.sourceforge.argparse4j.inf.Namespace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.Arrays;
import java.util.List;

class OptionParser {
private static final Logger log = LoggerFactory.getLogger("Root.UI.ArgumentParser");

private final ArgumentParser parser;

OptionParser() {
parser = ArgumentParsers.newArgumentParser("xplang", true)
.version(String.format("This is XPLanG version %s", Program.getVersion().describe()))
.defaultHelp(true);

parser.addArgument("source")
.metavar("<source-file>")
.type(Arguments.fileType().acceptSystemIn().verifyCanRead().verifyIsFile())
.nargs(1)
.help("Source file");

parser.addArgument("--version")
.action(Arguments.version()); // FIXME this exists with a System.exit(0). That's sort of not ideal.
}

RunConfig parseOptions(String[] args) {
log.debug("Parsing command line arguments: {}", Arrays.asList(args));

try {
Namespace res = parser.parseArgs(args);
List<File> files = res.get("source");
return new RunConfig(Program.Action.getDefaultAction(), files.get(0));
} catch(ArgumentParserException e) {
log.error("Argument error: {}", e.getMessage());
parser.handleError(e);
return new RunConfig(Program.Action.NONE, null);
}
}
}
54 changes: 50 additions & 4 deletions src/main/java/ppke/itk/xplang/ui/Program.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,58 @@
import ppke.itk.xplang.parser.Parser;
import ppke.itk.xplang.util.VersionInfo;

import java.io.Reader;
import java.io.StringReader;
import java.io.*;
import java.nio.charset.StandardCharsets;


class Program {
private final static Logger log = LoggerFactory.getLogger("Root.UI");
private final static VersionInfo version = new VersionInfo();

static VersionInfo getVersion() {
return version;
}

/**
* Describes the course of action the program should take.
*/
enum Action {
/** The program should just call it a day and quit. */
NONE,

/** The program should perform a dry-run: analyse the source, but do nothing afterwards. */
PARSE_ONLY,

/** The program should take the source code, parse it, build up the AST, then execute it. */
INTERPRET;

static Action getDefaultAction() {
return INTERPRET;
}
};

Program() {
// empty ctor
}

/**
* Run the program.
*/
void run() {
void run(String[] args) {
log.info("XPLanG starting");
log.info("OS: {}", System.getProperty("os.name"));
log.info("Java: {}", System.getProperty("java.version"));
log.info("Version: {}", version.describe());

Reader source = new StringReader("PROGRAM testing <_< >_> >_> \n >_> >_> program_vége");
OptionParser optionParser = new OptionParser();
RunConfig run = optionParser.parseOptions(args);

if(run.getAction() == Action.NONE) {
log.info("Exiting");
return;
}

Reader source = getSourceReader(run);

ErrorLog errorLog = new ErrorLog();
Grammar grammar = new PlangGrammar();
Expand All @@ -52,6 +82,22 @@ void run() {
interpreter.visit(root);
}

private Reader getSourceReader(RunConfig run) {
Reader Result;
try {
if(run.getSourceFile().getName().equals("-")) {
Result = new InputStreamReader(System.in);
log.info("Opened stdin for reading");
} else {
Result = new InputStreamReader(new FileInputStream(run.getSourceFile()), StandardCharsets.UTF_8);
log.info("Opened {} for reading", run.getSourceFile());
}
} catch(FileNotFoundException e) {
throw new RuntimeException("Could not open source file for reading.", e);
}
return Result;
}

private void printErrors(ErrorLog errorLog) {
for(CompilerMessage message : errorLog.getErrorMessages()) {
System.out.println(message);
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/ppke/itk/xplang/ui/RunConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package ppke.itk.xplang.ui;

import java.io.File;

/**
* Configures the program's behaviour.
*/
class RunConfig {
private final Program.Action action;
private final File sourceFile;

RunConfig(Program.Action action, File sourceFile) {
this.action = action;
this.sourceFile = sourceFile;
}

/**
* The course of action the program should take.
* @return
*/
Program.Action getAction() {
return action;
}

/**
* Where is the source code the program should operate on?
* @return the File object representing the source code. The file is weakly guaranteed to exist and be readable.
* If the {@code name} property of the File is set to '-', that is a signal the program should read the
* source code from the standard input stream.
*/
File getSourceFile() {
return sourceFile;
}
}
79 changes: 79 additions & 0 deletions src/test/java/ppke/itk/xplang/ui/OptionParserTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package ppke.itk.xplang.ui;

import org.junit.Before;
import org.junit.Test;

import java.io.File;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;

public class OptionParserTest {
private OptionParser parser;

@Before
public void setUp() {
this.parser = new OptionParser();
}

@Test
public void getHelp() {
RunConfig config = parser.parseOptions(new String[]{"--help"});
assertSame("Requesting the help screen should result in NONE action",
Program.Action.NONE, config.getAction()
);

config = parser.parseOptions(new String[]{"--help", "example.plang"});
assertSame("The --help switch and a source file should result in NONE action",
Program.Action.NONE, config.getAction()
);
}

//@Test // FIXME don't run until --version does System.exit(1)
public void getVersion() {
RunConfig config = parser.parseOptions(new String[]{"--version"});
assertSame("Requesting the version screen should result in NONE action",
Program.Action.NONE, config.getAction()
);

config = parser.parseOptions(new String[]{"--version", "example.prog"});
assertSame("The --version switch and a source file should result in NONE action",
Program.Action.NONE, config.getAction()
);
}

@Test
public void specifyingSourceFiles() {
RunConfig config = parser.parseOptions(new String[]{"example.prog"});
assertSame("Existing file as source input should be accepted, and should trigger the default action",
Program.Action.getDefaultAction(), config.getAction()
);
assertEquals("Existing file as source input should be accepted.",
"example.prog", config.getSourceFile().getName()
);
}

@Test
public void stdInSource() {
RunConfig config = parser.parseOptions(new String[]{"-"});
assertSame("- as source input should be accepted, and should trigger the default action",
Program.Action.getDefaultAction(), config.getAction()
);
assertEquals("- as source input should be accepted (representing the standard input)",
new File("-"), config.getSourceFile()
);
}

@Test
public void errors() {
RunConfig config = parser.parseOptions(new String[]{"--invalid", "example.prog"});
assertSame("Invalid options should result in a NONE action",
Program.Action.NONE, config.getAction()
);

config = parser.parseOptions(new String[]{"nonesuch.prog"});
assertSame("Specifying nonexistent files should result in a NONE action",
Program.Action.NONE, config.getAction()
);
}
}
1 change: 1 addition & 0 deletions src/test/resources/example.prog
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
this file exists

0 comments on commit 4d79436

Please sign in to comment.