mirror of https://github.com/aamine/cbc
* net/loveruby/cflat/compiler/Compiler.java: handle multiple source files.
* net/loveruby/cflat/compiler/Options.java: new class forked from Compiler.java. Command option parser. * net/loveruby/cflat/compiler/SourceFile.java: new class. * net/loveruby/cflat/compiler/LdOption.java: new class. * net/loveruby/cflat/compiler/LdArg.java: new interface. * net/loveruby/cflat/exception/OptionParseError.java: new error class. * test: test multiple input. git-svn-id: file:///Users/aamine/c/gitwork/public/cbc/trunk@4016 1b9489fe-b721-0410-924e-b54b9192deb8
This commit is contained in:
parent
688d709302
commit
9a0bfed9f0
19
ChangeLog
19
ChangeLog
|
@ -1,3 +1,22 @@
|
|||
Mon Sep 15 00:43:29 2008 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* net/loveruby/cflat/compiler/Compiler.java: handle multiple
|
||||
source files.
|
||||
|
||||
* net/loveruby/cflat/compiler/Options.java: new class forked from
|
||||
Compiler.java. Command option parser.
|
||||
|
||||
* net/loveruby/cflat/compiler/SourceFile.java: new class.
|
||||
|
||||
* net/loveruby/cflat/compiler/LdOption.java: new class.
|
||||
|
||||
* net/loveruby/cflat/compiler/LdArg.java: new interface.
|
||||
|
||||
* net/loveruby/cflat/exception/OptionParseError.java: new error
|
||||
class.
|
||||
|
||||
* test: test multiple input.
|
||||
|
||||
Sun Sep 14 20:08:19 2008 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* net/loveruby/cflat/compiler/Compiler.java: use CflatToken to
|
||||
|
|
|
@ -8,10 +8,11 @@ import java.util.*;
|
|||
import java.io.*;
|
||||
|
||||
public class Compiler {
|
||||
static final private String programId = "cbc";
|
||||
static final public String ProgramID = "cbc";
|
||||
static final public String Version = "1.0.0";
|
||||
|
||||
static public void main(String[] args) {
|
||||
new Compiler(programId).commandMain(args);
|
||||
new Compiler(ProgramID).commandMain(args);
|
||||
}
|
||||
|
||||
protected ErrorHandler errorHandler;
|
||||
|
@ -25,19 +26,41 @@ public class Compiler {
|
|||
}
|
||||
|
||||
public void commandMain(String[] origArgs) {
|
||||
Options opts = parseOptions(listFromArray(origArgs));
|
||||
Options opts = new Options(defaultTypeTable(), new LibraryLoader());
|
||||
List srcs = null;
|
||||
try {
|
||||
srcs = opts.parse(Arrays.asList(origArgs));
|
||||
}
|
||||
catch (OptionParseError err) {
|
||||
errorHandler.error(err.getMessage());
|
||||
errorHandler.error("Try cbc --help for option usage");
|
||||
System.exit(1);
|
||||
}
|
||||
if (opts.isMode("--check-syntax")) {
|
||||
if (isValidSyntax(opts)) {
|
||||
System.out.println("Syntax OK");
|
||||
System.exit(0);
|
||||
}
|
||||
else {
|
||||
System.exit(1);
|
||||
Iterator inputs = srcs.iterator();
|
||||
boolean failed = false;
|
||||
while (inputs.hasNext()) {
|
||||
SourceFile src = (SourceFile)inputs.next();
|
||||
if (isValidSyntax(src, opts)) {
|
||||
System.out.println(src.name() + ": Syntax OK");
|
||||
}
|
||||
else {
|
||||
System.out.println(src.name() + ": Syntax Error");
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
System.exit(failed ? 1 : 0);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
compileFile(opts);
|
||||
Iterator inputs = srcs.iterator();
|
||||
while (inputs.hasNext()) {
|
||||
SourceFile src = (SourceFile)inputs.next();
|
||||
compileFile(src, opts);
|
||||
}
|
||||
if (! opts.isLinkRequired()) System.exit(0);
|
||||
generateExecutable(opts);
|
||||
System.exit(0);
|
||||
}
|
||||
catch (CompileException ex) {
|
||||
errorHandler.error(ex.getMessage());
|
||||
|
@ -46,148 +69,14 @@ public class Compiler {
|
|||
}
|
||||
}
|
||||
|
||||
class Options {
|
||||
public String mode;
|
||||
public String inputFile;
|
||||
public String outputFile;
|
||||
public boolean verbose;
|
||||
public boolean debugParser;
|
||||
//public boolean debugBuild;
|
||||
public TypeTable typeTable;
|
||||
public LibraryLoader loader;
|
||||
//public List asOpts; // List<String>
|
||||
//public List ldOpts; // List<String>
|
||||
//public List ldArgs; // List<LdArg>
|
||||
|
||||
public boolean isMode(String m) {
|
||||
return mode == null ? false : mode.equals(m);
|
||||
}
|
||||
}
|
||||
|
||||
protected Options parseOptions(List args) {
|
||||
ListIterator it = args.listIterator();
|
||||
Options opts = new Options();
|
||||
opts.typeTable = defaultTypeTable();
|
||||
opts.loader = new LibraryLoader();
|
||||
while (it.hasNext()) {
|
||||
String arg = (String)it.next();
|
||||
if (arg.equals("-")) {
|
||||
System.err.println("FIXME: stdin is not supported yet");
|
||||
System.exit(1);
|
||||
}
|
||||
else if (arg.equals("--")) {
|
||||
// "--" Stops command line processing
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
else if (arg.startsWith("-")) {
|
||||
if (arg.equals("--check-syntax")
|
||||
|| arg.equals("--dump-tokens")
|
||||
|| arg.equals("--dump-ast")
|
||||
|| arg.equals("--dump-stmt")
|
||||
|| arg.equals("--dump-reference")
|
||||
|| arg.equals("--dump-semantic")
|
||||
|| arg.equals("-S")
|
||||
|| arg.equals("--dump-asm")
|
||||
|| arg.equals("-c")) {
|
||||
if (opts.mode != null) {
|
||||
errorExit(opts.mode + " option and "
|
||||
+ arg + " option is exclusive");
|
||||
}
|
||||
opts.mode = arg;
|
||||
}
|
||||
else if (arg.startsWith("-I")) {
|
||||
opts.loader.addLoadPath(getOptArg(arg, it));
|
||||
}
|
||||
else if (arg.equals("--debug-parser")) {
|
||||
opts.debugParser = true;
|
||||
}
|
||||
else if (arg.startsWith("-o")) {
|
||||
opts.outputFile = getOptArg(arg, it);
|
||||
}
|
||||
// FIXME: compile options
|
||||
//else if (arg.equals("-fPIC"))
|
||||
//else if (arg.startsWith("-O"))
|
||||
// FIXME: assemble options
|
||||
//else if (arg.startsWith("-Wa,"))
|
||||
//else if (arg.equals("-Xassembler"))
|
||||
// FIXME: link options
|
||||
//else if (arg.equals("-static"))
|
||||
//else if (arg.equals("-shared"))
|
||||
//else if (arg.startsWith("-L"))
|
||||
//else if (arg.startsWith("-l"))
|
||||
//else if (arg.startsWith("-Wl,"))
|
||||
//else if (arg.equals("-Xlinker"))
|
||||
else if (arg.equals("-v")) {
|
||||
opts.verbose = true;
|
||||
}
|
||||
else if (arg.equals("--help")) {
|
||||
printUsage(System.out);
|
||||
System.exit(0);
|
||||
}
|
||||
else {
|
||||
System.err.println("unknown option: " + arg);
|
||||
printUsage(System.err);
|
||||
System.exit(1);
|
||||
}
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: handle many input files
|
||||
if (args.size() == 0) errorExit("no input file");
|
||||
if (args.size() > 1) errorExit("too many input files");
|
||||
opts.inputFile = (String)args.get(0);
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
protected void printUsage(PrintStream out) {
|
||||
// --dump-reference is hidden option
|
||||
out.println("Usage: cbc [option] file");
|
||||
out.println(" --check-syntax Checks syntax.");
|
||||
out.println(" --debug-parser Dumps parsing process.");
|
||||
out.println(" --dump-tokens Parses source file and dumps tokens.");
|
||||
out.println(" --dump-ast Parses source file and dumps AST.");
|
||||
out.println(" --dump-semantic Checks semantics and dumps AST.");
|
||||
out.println(" -S Generates an assembly source.");
|
||||
out.println(" --dump-asm Dumps an assembly source.");
|
||||
out.println(" -c Generates an object file.");
|
||||
out.println(" -I PATH Adds import file loading path.");
|
||||
out.println(" -o PATH Places output in file PATH.");
|
||||
out.println(" --help Prints this message and quit.");
|
||||
}
|
||||
|
||||
private List listFromArray(Object[] a) {
|
||||
List list = new ArrayList();
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
list.add(a[i]);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private String getOptArg(String arg, ListIterator it) {
|
||||
String path = arg.substring(2);
|
||||
if (path.length() != 0) { // -Ipath
|
||||
return path;
|
||||
}
|
||||
else { // -I path
|
||||
if (! it.hasNext()) {
|
||||
errorExit("-I option missing argument");
|
||||
}
|
||||
it.remove();
|
||||
return (String)it.next();
|
||||
}
|
||||
}
|
||||
|
||||
private void errorExit(String msg) {
|
||||
errorHandler.error(msg);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
protected boolean isValidSyntax(Options opts) {
|
||||
protected boolean isValidSyntax(SourceFile src, Options opts) {
|
||||
try {
|
||||
parseFile(opts);
|
||||
parseFile(src, opts);
|
||||
return true;
|
||||
}
|
||||
catch (SyntaxException ex) {
|
||||
|
@ -199,39 +88,44 @@ public class Compiler {
|
|||
}
|
||||
}
|
||||
|
||||
protected void compileFile(Options opts) throws CompileException {
|
||||
AST ast = parseFile(opts);
|
||||
if (opts.isMode("--dump-tokens")) {
|
||||
dumpTokens(ast.sourceTokens(), System.out);
|
||||
return;
|
||||
protected void compileFile(SourceFile src, Options opts)
|
||||
throws CompileException {
|
||||
if (src.isCflatSource()) {
|
||||
AST ast = parseFile(src, opts);
|
||||
if (opts.isMode("--dump-tokens")) {
|
||||
dumpTokens(ast.sourceTokens(), System.out);
|
||||
return;
|
||||
}
|
||||
if (opts.isMode("--dump-ast")) {
|
||||
ast.dump();
|
||||
return;
|
||||
}
|
||||
if (opts.isMode("--dump-stmt")) {
|
||||
findStmt(ast).dump();
|
||||
return;
|
||||
}
|
||||
semanticAnalysis(ast, opts);
|
||||
if (opts.isMode("--dump-reference")) return;
|
||||
if (opts.isMode("--dump-semantic")) {
|
||||
ast.dump();
|
||||
return;
|
||||
}
|
||||
String asm = generateAssembly(ast, opts);
|
||||
if (opts.isMode("--dump-asm")) {
|
||||
System.out.println(asm);
|
||||
return;
|
||||
}
|
||||
writeFile(src.asmFileName(opts), asm);
|
||||
src.setCurrentName(src.asmFileName(opts));
|
||||
if (opts.isMode("-S")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (opts.isMode("--dump-ast")) {
|
||||
ast.dump();
|
||||
return;
|
||||
if (! opts.isAssembleRequired()) return;
|
||||
if (src.isAssemblySource()) {
|
||||
assemble(src.asmFileName(opts), src.objFileName(opts), opts);
|
||||
src.setCurrentName(src.objFileName(opts));
|
||||
}
|
||||
if (opts.isMode("--dump-stmt")) {
|
||||
findStmt(ast).dump();
|
||||
return;
|
||||
}
|
||||
semanticAnalysis(ast, opts);
|
||||
if (opts.isMode("--dump-semantic")) {
|
||||
ast.dump();
|
||||
return;
|
||||
}
|
||||
String asm = CodeGenerator.generate(ast, opts.typeTable, errorHandler);
|
||||
if (opts.isMode("--dump-asm")) {
|
||||
System.out.println(asm);
|
||||
return;
|
||||
}
|
||||
writeFile(asmFileName(opts), asm);
|
||||
if (opts.isMode("-S")) {
|
||||
return;
|
||||
}
|
||||
assemble(asmFileName(opts), objFileName(opts), opts);
|
||||
if (opts.isMode("-c")) {
|
||||
return;
|
||||
}
|
||||
link(objFileName(opts), exeFileName(opts), opts);
|
||||
}
|
||||
|
||||
protected void dumpTokens(Iterator tokens, PrintStream s) {
|
||||
|
@ -267,61 +161,66 @@ public class Compiler {
|
|||
return null; // never reach
|
||||
}
|
||||
|
||||
protected AST parseFile(Options opts)
|
||||
protected AST parseFile(SourceFile src, Options opts)
|
||||
throws SyntaxException, FileException {
|
||||
return Parser.parseFile(new File(opts.inputFile),
|
||||
opts.loader,
|
||||
return Parser.parseFile(new File(src.currentName()),
|
||||
opts.loader(),
|
||||
errorHandler,
|
||||
opts.debugParser);
|
||||
opts.doesDebugParser());
|
||||
}
|
||||
|
||||
protected void semanticAnalysis(AST ast, Options opts)
|
||||
throws SemanticException {
|
||||
JumpResolver.resolve(ast, errorHandler);
|
||||
LocalReferenceResolver.resolve(ast, errorHandler);
|
||||
TypeResolver.resolve(ast, opts.typeTable, errorHandler);
|
||||
opts.typeTable.semanticCheck(errorHandler);
|
||||
TypeResolver.resolve(ast, opts.typeTable(), errorHandler);
|
||||
opts.typeTable().semanticCheck(errorHandler);
|
||||
DereferenceChecker.check(ast, errorHandler);
|
||||
if (opts.isMode("--dump-reference")) {
|
||||
ast.dump();
|
||||
System.exit(1);
|
||||
return;
|
||||
}
|
||||
new TypeChecker(opts.typeTable, errorHandler).check(ast);
|
||||
new TypeChecker(opts.typeTable(), errorHandler).check(ast);
|
||||
}
|
||||
|
||||
protected String generateAssembly(AST ast, Options opts) {
|
||||
return CodeGenerator.generate(ast, opts.typeTable(), errorHandler);
|
||||
}
|
||||
|
||||
protected void assemble(String srcPath,
|
||||
String destPath,
|
||||
Options opts) throws IPCException {
|
||||
String[] cmd = {
|
||||
"as",
|
||||
"-o", destPath,
|
||||
srcPath
|
||||
};
|
||||
invoke(cmd, opts.verbose);
|
||||
List cmd = new ArrayList();
|
||||
cmd.add("as");
|
||||
cmd.addAll(opts.asOptions());
|
||||
cmd.add("-o");
|
||||
cmd.add(destPath);
|
||||
cmd.add(srcPath);
|
||||
invoke(cmd, opts.isVerboseMode());
|
||||
}
|
||||
|
||||
protected void link(String srcPath,
|
||||
String destPath,
|
||||
Options opts) throws IPCException {
|
||||
String[] cmd = {
|
||||
"ld",
|
||||
"-dynamic-linker", "/lib/ld-linux.so.2",
|
||||
"/usr/lib/crt1.o",
|
||||
"/usr/lib/crti.o",
|
||||
srcPath,
|
||||
"/usr/lib/libc_nonshared.a",
|
||||
"-lc",
|
||||
"/usr/lib/crtn.o",
|
||||
"-o", destPath
|
||||
};
|
||||
invoke(cmd, opts.verbose);
|
||||
protected void generateExecutable(Options opts) throws IPCException {
|
||||
List cmd = new ArrayList();
|
||||
cmd.add("ld");
|
||||
// FIXME: -dynamic-linker required only on dynamic linking
|
||||
cmd.add("-dynamic-linker");
|
||||
cmd.add("/lib/ld-linux.so.2");
|
||||
if (! opts.noStartFiles()) cmd.add("/usr/lib/crt1.o");
|
||||
if (! opts.noStartFiles()) cmd.add("/usr/lib/crti.o");
|
||||
cmd.addAll(opts.ldArgs());
|
||||
if (! opts.noDefaultLibs()) cmd.add("-lc");
|
||||
if (! opts.noStartFiles()) cmd.add("/usr/lib/crtn.o");
|
||||
cmd.add("-o");
|
||||
cmd.add(opts.exeFileName());
|
||||
invoke(cmd, opts.isVerboseMode());
|
||||
}
|
||||
|
||||
public void invoke(String[] cmd, boolean debug) throws IPCException {
|
||||
protected void invoke(List cmdArgs, boolean debug) throws IPCException {
|
||||
if (debug) {
|
||||
dumpCommand(cmd);
|
||||
dumpCommand(cmdArgs.iterator());
|
||||
}
|
||||
try {
|
||||
String[] cmd = stringListToArray(cmdArgs);
|
||||
Process proc = Runtime.getRuntime().exec(cmd);
|
||||
proc.waitFor();
|
||||
passThrough(proc.getInputStream());
|
||||
|
@ -342,11 +241,23 @@ public class Compiler {
|
|||
}
|
||||
}
|
||||
|
||||
protected void dumpCommand(String[] cmd) {
|
||||
protected String[] stringListToArray(List list) {
|
||||
String[] a = new String[list.size()];
|
||||
int idx = 0;
|
||||
Iterator it = list.iterator();
|
||||
while (it.hasNext()) {
|
||||
Object o = it.next();
|
||||
a[idx++] = o.toString();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
protected void dumpCommand(Iterator args) {
|
||||
String sep = "";
|
||||
for (int i = 0; i < cmd.length; i++) {
|
||||
while (args.hasNext()) {
|
||||
String arg = args.next().toString();
|
||||
System.out.print(sep); sep = " ";
|
||||
System.out.print(cmd[i]);
|
||||
System.out.print(arg);
|
||||
}
|
||||
System.out.println("");
|
||||
}
|
||||
|
@ -380,35 +291,4 @@ public class Compiler {
|
|||
throw new FileException("file error");
|
||||
}
|
||||
}
|
||||
|
||||
protected String asmFileName(Options opts) {
|
||||
return opts.isMode("-S") && opts.outputFile != null
|
||||
? opts.outputFile
|
||||
: baseName(opts.inputFile, true) + ".s";
|
||||
}
|
||||
|
||||
protected String objFileName(Options opts) {
|
||||
return opts.isMode("-c") && opts.outputFile != null
|
||||
? opts.outputFile
|
||||
: baseName(opts.inputFile, true) + ".o";
|
||||
}
|
||||
|
||||
protected String exeFileName(Options opts) {
|
||||
return opts.outputFile != null
|
||||
? opts.outputFile
|
||||
: baseName(opts.inputFile, true);
|
||||
}
|
||||
|
||||
protected String baseName(String path) {
|
||||
return new File(path).getName();
|
||||
}
|
||||
|
||||
protected String baseName(String path, boolean stripExt) {
|
||||
if (stripExt) {
|
||||
return new File(path).getName().replaceFirst("\\.[^.]*$", "");
|
||||
}
|
||||
else {
|
||||
return baseName(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package net.loveruby.cflat.compiler;
|
||||
|
||||
// package private
|
||||
interface LdArg {
|
||||
abstract public String toString();
|
||||
abstract public boolean isSourceFile();
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package net.loveruby.cflat.compiler;
|
||||
|
||||
// package private
|
||||
class LdOption implements LdArg {
|
||||
protected String option;
|
||||
|
||||
public LdOption(String option) {
|
||||
this.option = option;
|
||||
}
|
||||
|
||||
public boolean isSourceFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.option;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,345 @@
|
|||
package net.loveruby.cflat.compiler;
|
||||
import net.loveruby.cflat.type.TypeTable;
|
||||
import net.loveruby.cflat.exception.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
// package scope
|
||||
class Options {
|
||||
protected String mode;
|
||||
protected TypeTable typeTable;
|
||||
protected LibraryLoader loader;
|
||||
protected String outputFileName;
|
||||
protected boolean verbose;
|
||||
protected boolean debugParser;
|
||||
protected List asOptions; // List<String>
|
||||
protected List ldArgs; // List<LdArg>
|
||||
protected boolean noStartFiles = false;
|
||||
protected boolean noDefaultLibs = false;
|
||||
|
||||
public Options(TypeTable typeTable, LibraryLoader loader) {
|
||||
this.typeTable = typeTable;
|
||||
this.loader = loader;
|
||||
this.asOptions = new ArrayList();
|
||||
this.ldArgs = new ArrayList();
|
||||
}
|
||||
|
||||
public boolean isMode(String m) {
|
||||
return mode.equals(m);
|
||||
}
|
||||
|
||||
public boolean isAssembleRequired() {
|
||||
return modeLevel(mode) >= MODE_LEVEL_ASSEMBLE;
|
||||
}
|
||||
|
||||
public boolean isLinkRequired() {
|
||||
return modeLevel(mode) >= MODE_LEVEL_LINK;
|
||||
}
|
||||
|
||||
static final protected String defaultMode = "link";
|
||||
static protected List modeLevels;
|
||||
static final protected int MODE_LEVEL_ASSEMBLE;
|
||||
static final protected int MODE_LEVEL_LINK;
|
||||
|
||||
static {
|
||||
modeLevels = new ArrayList();
|
||||
modeLevels.add("--check-syntax");
|
||||
modeLevels.add("--dump-tokens");
|
||||
modeLevels.add("--dump-ast");
|
||||
modeLevels.add("--dump-semantic");
|
||||
modeLevels.add("--dump-reference");
|
||||
modeLevels.add("--dump-asm");
|
||||
modeLevels.add("-S");
|
||||
modeLevels.add("-c"); // assemble required
|
||||
modeLevels.add(defaultMode); // link required
|
||||
|
||||
MODE_LEVEL_ASSEMBLE = modeLevels.indexOf("-c");
|
||||
MODE_LEVEL_LINK = modeLevels.indexOf(defaultMode);
|
||||
};
|
||||
|
||||
protected int modeLevel(String mode) {
|
||||
int idx = modeLevels.indexOf(mode);
|
||||
if (idx < 0) throw new Error("unknown compiler mode: " + mode);
|
||||
return idx;
|
||||
}
|
||||
|
||||
public String outputFileNameFor(String mode) {
|
||||
return isMode(mode) ? outputFileName : null;
|
||||
}
|
||||
|
||||
public String exeFileName() {
|
||||
if (outputFileName != null) return outputFileName;
|
||||
List srcs = sourceFiles();
|
||||
if (srcs.size() == 1) {
|
||||
SourceFile src = (SourceFile)srcs.get(0);
|
||||
return src.exeFileName(this);
|
||||
}
|
||||
else {
|
||||
return "a.out";
|
||||
}
|
||||
}
|
||||
|
||||
protected List sourceFiles() {
|
||||
Iterator args = ldArgs.iterator();
|
||||
List result = new ArrayList();
|
||||
while (args.hasNext()) {
|
||||
LdArg arg = (LdArg)args.next();
|
||||
if (arg.isSourceFile()) {
|
||||
result.add(arg);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public TypeTable typeTable() {
|
||||
return this.typeTable;
|
||||
}
|
||||
|
||||
public LibraryLoader loader() {
|
||||
return this.loader;
|
||||
}
|
||||
|
||||
public String outputFileName() {
|
||||
return this.outputFileName;
|
||||
}
|
||||
|
||||
public boolean isVerboseMode() {
|
||||
return this.verbose;
|
||||
}
|
||||
|
||||
public boolean doesDebugParser() {
|
||||
return this.debugParser;
|
||||
}
|
||||
|
||||
// List<String>
|
||||
public List asOptions() {
|
||||
return this.asOptions;
|
||||
}
|
||||
|
||||
// List<ldArg>
|
||||
public List ldArgs() {
|
||||
return this.ldArgs;
|
||||
}
|
||||
|
||||
public boolean noStartFiles() {
|
||||
return this.noStartFiles;
|
||||
}
|
||||
|
||||
public boolean noDefaultLibs() {
|
||||
return this.noDefaultLibs;
|
||||
}
|
||||
|
||||
/** Returns List<SourceFile>. */
|
||||
public List parse(List argsList) {
|
||||
ListIterator args = argsList.listIterator();
|
||||
List srcs = new ArrayList();
|
||||
while (args.hasNext()) {
|
||||
String arg = (String)args.next();
|
||||
// FIXME: stdin
|
||||
if (arg.equals("-")) {
|
||||
System.err.println("FIXME: stdin is not supported yet");
|
||||
System.exit(1);
|
||||
}
|
||||
else if (arg.equals("--")) {
|
||||
// "--" Stops command line processing
|
||||
break;
|
||||
}
|
||||
else if (arg.startsWith("-")) {
|
||||
if (arg.equals("--check-syntax")
|
||||
|| arg.equals("--dump-tokens")
|
||||
|| arg.equals("--dump-ast")
|
||||
|| arg.equals("--dump-stmt")
|
||||
|| arg.equals("--dump-reference")
|
||||
|| arg.equals("--dump-semantic")
|
||||
|| arg.equals("-S")
|
||||
|| arg.equals("--dump-asm")
|
||||
|| arg.equals("-c")) {
|
||||
if (mode != null) {
|
||||
parseError(mode + " option and "
|
||||
+ arg + " option is exclusive");
|
||||
}
|
||||
mode = arg;
|
||||
}
|
||||
else if (arg.startsWith("-I")) {
|
||||
loader.addLoadPath(getOptArg(arg, args));
|
||||
}
|
||||
else if (arg.equals("--debug-parser")) {
|
||||
debugParser = true;
|
||||
}
|
||||
else if (arg.startsWith("-o")) {
|
||||
outputFileName = getOptArg(arg, args);
|
||||
}
|
||||
// FIXME: PIC, PIE
|
||||
//else if (arg.equals("-fpie"))
|
||||
//else if (arg.equals("-fPIC"))
|
||||
//else if (arg.equals("-fpie"))
|
||||
//else if (arg.equals("-fPIE"))
|
||||
// FIXME: optimization
|
||||
//else if (arg.startsWith("-O"))
|
||||
else if (arg.startsWith("-Wa,")) {
|
||||
asOptions.addAll(parseCommaSeparatedOptions(arg));
|
||||
}
|
||||
else if (arg.equals("-Xassembler")) {
|
||||
asOptions.add(nextArg(arg, args));
|
||||
}
|
||||
else if (arg.equals("-static")) {
|
||||
ldArgs.add(new LdOption(arg));
|
||||
}
|
||||
else if (arg.equals("-shared")) {
|
||||
ldArgs.add(new LdOption(arg));
|
||||
}
|
||||
else if (arg.startsWith("-L")) {
|
||||
ldArgs.add(new LdOption("-L" + getOptArg(arg, args)));
|
||||
}
|
||||
else if (arg.startsWith("-l")) {
|
||||
ldArgs.add(new LdOption("-l" + getOptArg(arg, args)));
|
||||
}
|
||||
else if (arg.equals("-nostartfiles")) {
|
||||
noStartFiles = true;
|
||||
}
|
||||
else if (arg.equals("-nodefaultlibs")) {
|
||||
noDefaultLibs = true;
|
||||
}
|
||||
else if (arg.equals("-nostdlib")) {
|
||||
noStartFiles = true;
|
||||
noDefaultLibs = true;
|
||||
}
|
||||
else if (arg.startsWith("-Wl,")) {
|
||||
Iterator opts = parseCommaSeparatedOptions(arg).iterator();
|
||||
while (opts.hasNext()) {
|
||||
String opt = (String)opts.next();
|
||||
ldArgs.add(new LdOption(opt));
|
||||
}
|
||||
}
|
||||
else if (arg.equals("-Xlinker")) {
|
||||
ldArgs.add(new LdOption(nextArg(arg, args)));
|
||||
}
|
||||
// FIXME: -pie
|
||||
//else if (arg.equals("-pie"))
|
||||
else if (arg.equals("-v")) {
|
||||
verbose = true;
|
||||
}
|
||||
else if (arg.equals("--version")) {
|
||||
System.out.println(Compiler.ProgramID
|
||||
+ " version " + Compiler.Version);
|
||||
System.exit(0);
|
||||
}
|
||||
else if (arg.equals("--help")) {
|
||||
printUsage(System.out);
|
||||
System.exit(0);
|
||||
}
|
||||
else {
|
||||
parseError("unknown option: " + arg);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// source file
|
||||
addSourceFile(srcs, ldArgs, arg);
|
||||
}
|
||||
}
|
||||
// args has more arguments when "--" is appeared.
|
||||
while (args.hasNext()) {
|
||||
String arg = (String)args.next();
|
||||
addSourceFile(srcs, ldArgs, arg);
|
||||
}
|
||||
if (srcs.isEmpty()) parseError("no input file");
|
||||
if (mode == null) {
|
||||
mode = defaultMode;
|
||||
}
|
||||
if (! isLinkRequired() && outputFileName != null && srcs.size() > 1) {
|
||||
parseError("-o option requires only 1 input not on linking");
|
||||
}
|
||||
return srcs;
|
||||
}
|
||||
|
||||
protected void parseError(String msg) {
|
||||
throw new OptionParseError(msg);
|
||||
}
|
||||
|
||||
protected void addSourceFile(List srcs, List ldArgs, String sourceName) {
|
||||
SourceFile src = new SourceFile(sourceName);
|
||||
srcs.add(src);
|
||||
// Original argument order does matter when linking.
|
||||
ldArgs.add(src);
|
||||
}
|
||||
|
||||
protected String getOptArg(String opt, ListIterator args) {
|
||||
String path = opt.substring(2);
|
||||
if (path.length() != 0) { // -Ipath
|
||||
return path;
|
||||
}
|
||||
else { // -I path
|
||||
return nextArg(opt, args);
|
||||
}
|
||||
}
|
||||
|
||||
protected String nextArg(String opt, ListIterator args) {
|
||||
if (! args.hasNext()) {
|
||||
parseError("missing argument for " + opt);
|
||||
}
|
||||
return (String)args.next();
|
||||
}
|
||||
|
||||
/** "-Wl,-rpath,/usr/local/lib" -> ["-rpath", "/usr/local/lib"] */
|
||||
protected List parseCommaSeparatedOptions(String opt) {
|
||||
List opts = arrayToList(opt.split(","));
|
||||
opts.remove(0); // remove "-Wl" etc.
|
||||
if (opts.isEmpty()) {
|
||||
parseError("missing argument for " + opt);
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
|
||||
protected List arrayToList(Object[] ary) {
|
||||
List result = new ArrayList();
|
||||
for (int i = 0; i < ary.length; i++) {
|
||||
result.add(ary[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void printUsage(PrintStream out) {
|
||||
out.println("Usage: cbc [options] file...");
|
||||
out.println("Generic Options:");
|
||||
out.println(" --check-syntax Checks syntax.");
|
||||
out.println(" --dump-tokens Parses source file and dumps tokens.");
|
||||
out.println(" --dump-ast Parses source file and dumps AST.");
|
||||
out.println(" --dump-semantic Checks semantics and dumps AST.");
|
||||
// --dump-reference is hidden option
|
||||
out.println(" -S Generates an assembly source.");
|
||||
out.println(" --dump-asm Dumps an assembly source.");
|
||||
out.println(" -c Generates an object file.");
|
||||
out.println(" -o PATH Places output in file PATH.");
|
||||
out.println(" -v Verbose mode.");
|
||||
out.println(" --version Shows compiler version.");
|
||||
out.println(" --help Prints this message and quit.");
|
||||
out.println("");
|
||||
out.println("Parser Options:");
|
||||
out.println(" --debug-parser Dumps parsing process.");
|
||||
out.println("");
|
||||
out.println("Compiler Options:");
|
||||
out.println(" -I PATH Adds PATH as import file directory.");
|
||||
//out.println(" -O Enables optimization.");
|
||||
//out.println(" -O1, -O2, -O3 Equivalent to -O.");
|
||||
//out.println(" -Os Equivalent to -O.");
|
||||
//out.println(" -O0 Disables optimization.");
|
||||
//out.println(" -fPIC Generates PIC assembly.");
|
||||
//out.println(" -fPIE Generates PIE assembly.");
|
||||
out.println("");
|
||||
out.println("Assembler Options:");
|
||||
out.println(" -Wa,OPT Passes OPT to the assembler (as).");
|
||||
out.println(" -Xassembler OPT Passes OPT to the assembler (as).");
|
||||
out.println("");
|
||||
out.println("Linker Options:");
|
||||
out.println(" -l LIB Links the library LIB.");
|
||||
out.println(" -L PATH Adds PATH as library directory.");
|
||||
//out.println(" -shared Linkes with shared library (default).");
|
||||
//out.println(" -static Linkes with static library.");
|
||||
//out.println(" -pie Generates PIE.");
|
||||
out.println(" -nostartfiles Do not link startup files.");
|
||||
out.println(" -nodefaultlibs Do not link default libraries.");
|
||||
out.println(" -nostdlib Enables -nostartfiles and -nodefaultlibs.");
|
||||
out.println(" -Wl,OPT Passes OPT to the linker (ld).");
|
||||
out.println(" -Xlinker OPT Passes OPT to the linker (ld).");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package net.loveruby.cflat.compiler;
|
||||
import java.io.File;
|
||||
|
||||
// package private
|
||||
class SourceFile implements LdArg {
|
||||
protected String originalName;
|
||||
protected String currentName;
|
||||
|
||||
public SourceFile(String name) {
|
||||
this.originalName = name;
|
||||
this.currentName = name;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return originalName();
|
||||
}
|
||||
|
||||
public String originalName() {
|
||||
return this.originalName;
|
||||
}
|
||||
|
||||
public String currentName() {
|
||||
return this.currentName;
|
||||
}
|
||||
|
||||
public void setCurrentName(String name) {
|
||||
this.currentName = name;
|
||||
}
|
||||
|
||||
public boolean isCflatSource() {
|
||||
return extName(currentName).equals(".cb");
|
||||
}
|
||||
|
||||
public boolean isAssemblySource() {
|
||||
return extName(currentName).equals(".s");
|
||||
}
|
||||
|
||||
public boolean isObjectFile() {
|
||||
return extName(currentName).equals(".o");
|
||||
}
|
||||
|
||||
public boolean isSharedLibrary() {
|
||||
return extName(currentName).equals(".so");
|
||||
}
|
||||
|
||||
public boolean isStaticLibrary() {
|
||||
return extName(currentName).equals(".a");
|
||||
}
|
||||
|
||||
public boolean isExecutable() {
|
||||
return extName(currentName).equals("");
|
||||
}
|
||||
|
||||
public String asmFileName(Options opts) {
|
||||
return or(opts.outputFileNameFor("-S"), replaceExt(".s"));
|
||||
}
|
||||
|
||||
public String objFileName(Options opts) {
|
||||
return or(opts.outputFileNameFor("-c"), replaceExt(".o"));
|
||||
}
|
||||
|
||||
public String exeFileName(Options opts) {
|
||||
return or(opts.outputFileName, replaceExt(""));
|
||||
}
|
||||
|
||||
protected String or(String x, String y) {
|
||||
return x != null ? x : y;
|
||||
}
|
||||
|
||||
protected String replaceExt(String ext) {
|
||||
return baseName(originalName, true) + ext;
|
||||
}
|
||||
|
||||
protected String baseName(String path) {
|
||||
return new File(path).getName();
|
||||
}
|
||||
|
||||
protected String baseName(String path, boolean stripExt) {
|
||||
if (stripExt) {
|
||||
return new File(path).getName().replaceFirst("\\.[^.]*$", "");
|
||||
}
|
||||
else {
|
||||
return baseName(path);
|
||||
}
|
||||
}
|
||||
|
||||
protected String extName(String path) {
|
||||
int idx = path.lastIndexOf(".");
|
||||
if (idx < 0) return "";
|
||||
return path.substring(idx);
|
||||
}
|
||||
|
||||
public boolean isSourceFile() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return currentName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package net.loveruby.cflat.exception;
|
||||
|
||||
public class OptionParseError extends Error {
|
||||
public OptionParseError(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
|
@ -180,6 +180,20 @@ assert_not_coredump() {
|
|||
return 0
|
||||
}
|
||||
|
||||
assert_file() {
|
||||
local path="$1"; shift
|
||||
|
||||
shunit_begin_test
|
||||
if [ ! -f "$path" ]
|
||||
then
|
||||
echo "not exist or not a file: $file"
|
||||
echo "----"
|
||||
shunit_test_failed
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
assert_not_exist() {
|
||||
local file="$1"; shift
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
int f(int x) { return x * x; }
|
|
@ -0,0 +1 @@
|
|||
extern int f(int x);
|
|
@ -0,0 +1,2 @@
|
|||
import src1;
|
||||
int main(int argc, char **argv) { return f(2); }
|
|
@ -289,6 +289,11 @@ test_32_noreturn() {
|
|||
assert_stat 0 ./noreturn
|
||||
}
|
||||
|
||||
test_33_multipleinput() {
|
||||
assert_compile_success src1.cb src2.cb -o src
|
||||
assert_status 4 ./src
|
||||
}
|
||||
|
||||
###
|
||||
### Local Assertions
|
||||
###
|
||||
|
|
Loading…
Reference in New Issue