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>
|
Sun Sep 14 20:08:19 2008 Minero Aoki <aamine@loveruby.net>
|
||||||
|
|
||||||
* net/loveruby/cflat/compiler/Compiler.java: use CflatToken to
|
* net/loveruby/cflat/compiler/Compiler.java: use CflatToken to
|
||||||
|
|
|
@ -8,10 +8,11 @@ import java.util.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
public class Compiler {
|
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) {
|
static public void main(String[] args) {
|
||||||
new Compiler(programId).commandMain(args);
|
new Compiler(ProgramID).commandMain(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ErrorHandler errorHandler;
|
protected ErrorHandler errorHandler;
|
||||||
|
@ -25,19 +26,41 @@ public class Compiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void commandMain(String[] origArgs) {
|
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 (opts.isMode("--check-syntax")) {
|
||||||
if (isValidSyntax(opts)) {
|
Iterator inputs = srcs.iterator();
|
||||||
System.out.println("Syntax OK");
|
boolean failed = false;
|
||||||
System.exit(0);
|
while (inputs.hasNext()) {
|
||||||
}
|
SourceFile src = (SourceFile)inputs.next();
|
||||||
else {
|
if (isValidSyntax(src, opts)) {
|
||||||
System.exit(1);
|
System.out.println(src.name() + ": Syntax OK");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
System.out.println(src.name() + ": Syntax Error");
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
System.exit(failed ? 1 : 0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
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) {
|
catch (CompileException ex) {
|
||||||
errorHandler.error(ex.getMessage());
|
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) {
|
private void errorExit(String msg) {
|
||||||
errorHandler.error(msg);
|
errorHandler.error(msg);
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isValidSyntax(Options opts) {
|
protected boolean isValidSyntax(SourceFile src, Options opts) {
|
||||||
try {
|
try {
|
||||||
parseFile(opts);
|
parseFile(src, opts);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (SyntaxException ex) {
|
catch (SyntaxException ex) {
|
||||||
|
@ -199,39 +88,44 @@ public class Compiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void compileFile(Options opts) throws CompileException {
|
protected void compileFile(SourceFile src, Options opts)
|
||||||
AST ast = parseFile(opts);
|
throws CompileException {
|
||||||
if (opts.isMode("--dump-tokens")) {
|
if (src.isCflatSource()) {
|
||||||
dumpTokens(ast.sourceTokens(), System.out);
|
AST ast = parseFile(src, opts);
|
||||||
return;
|
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")) {
|
if (! opts.isAssembleRequired()) return;
|
||||||
ast.dump();
|
if (src.isAssemblySource()) {
|
||||||
return;
|
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) {
|
protected void dumpTokens(Iterator tokens, PrintStream s) {
|
||||||
|
@ -267,61 +161,66 @@ public class Compiler {
|
||||||
return null; // never reach
|
return null; // never reach
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AST parseFile(Options opts)
|
protected AST parseFile(SourceFile src, Options opts)
|
||||||
throws SyntaxException, FileException {
|
throws SyntaxException, FileException {
|
||||||
return Parser.parseFile(new File(opts.inputFile),
|
return Parser.parseFile(new File(src.currentName()),
|
||||||
opts.loader,
|
opts.loader(),
|
||||||
errorHandler,
|
errorHandler,
|
||||||
opts.debugParser);
|
opts.doesDebugParser());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void semanticAnalysis(AST ast, Options opts)
|
protected void semanticAnalysis(AST ast, Options opts)
|
||||||
throws SemanticException {
|
throws SemanticException {
|
||||||
JumpResolver.resolve(ast, errorHandler);
|
JumpResolver.resolve(ast, errorHandler);
|
||||||
LocalReferenceResolver.resolve(ast, errorHandler);
|
LocalReferenceResolver.resolve(ast, errorHandler);
|
||||||
TypeResolver.resolve(ast, opts.typeTable, errorHandler);
|
TypeResolver.resolve(ast, opts.typeTable(), errorHandler);
|
||||||
opts.typeTable.semanticCheck(errorHandler);
|
opts.typeTable().semanticCheck(errorHandler);
|
||||||
DereferenceChecker.check(ast, errorHandler);
|
DereferenceChecker.check(ast, errorHandler);
|
||||||
if (opts.isMode("--dump-reference")) {
|
if (opts.isMode("--dump-reference")) {
|
||||||
ast.dump();
|
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,
|
protected void assemble(String srcPath,
|
||||||
String destPath,
|
String destPath,
|
||||||
Options opts) throws IPCException {
|
Options opts) throws IPCException {
|
||||||
String[] cmd = {
|
List cmd = new ArrayList();
|
||||||
"as",
|
cmd.add("as");
|
||||||
"-o", destPath,
|
cmd.addAll(opts.asOptions());
|
||||||
srcPath
|
cmd.add("-o");
|
||||||
};
|
cmd.add(destPath);
|
||||||
invoke(cmd, opts.verbose);
|
cmd.add(srcPath);
|
||||||
|
invoke(cmd, opts.isVerboseMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void link(String srcPath,
|
protected void generateExecutable(Options opts) throws IPCException {
|
||||||
String destPath,
|
List cmd = new ArrayList();
|
||||||
Options opts) throws IPCException {
|
cmd.add("ld");
|
||||||
String[] cmd = {
|
// FIXME: -dynamic-linker required only on dynamic linking
|
||||||
"ld",
|
cmd.add("-dynamic-linker");
|
||||||
"-dynamic-linker", "/lib/ld-linux.so.2",
|
cmd.add("/lib/ld-linux.so.2");
|
||||||
"/usr/lib/crt1.o",
|
if (! opts.noStartFiles()) cmd.add("/usr/lib/crt1.o");
|
||||||
"/usr/lib/crti.o",
|
if (! opts.noStartFiles()) cmd.add("/usr/lib/crti.o");
|
||||||
srcPath,
|
cmd.addAll(opts.ldArgs());
|
||||||
"/usr/lib/libc_nonshared.a",
|
if (! opts.noDefaultLibs()) cmd.add("-lc");
|
||||||
"-lc",
|
if (! opts.noStartFiles()) cmd.add("/usr/lib/crtn.o");
|
||||||
"/usr/lib/crtn.o",
|
cmd.add("-o");
|
||||||
"-o", destPath
|
cmd.add(opts.exeFileName());
|
||||||
};
|
invoke(cmd, opts.isVerboseMode());
|
||||||
invoke(cmd, opts.verbose);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invoke(String[] cmd, boolean debug) throws IPCException {
|
protected void invoke(List cmdArgs, boolean debug) throws IPCException {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
dumpCommand(cmd);
|
dumpCommand(cmdArgs.iterator());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
String[] cmd = stringListToArray(cmdArgs);
|
||||||
Process proc = Runtime.getRuntime().exec(cmd);
|
Process proc = Runtime.getRuntime().exec(cmd);
|
||||||
proc.waitFor();
|
proc.waitFor();
|
||||||
passThrough(proc.getInputStream());
|
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 = "";
|
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(sep); sep = " ";
|
||||||
System.out.print(cmd[i]);
|
System.out.print(arg);
|
||||||
}
|
}
|
||||||
System.out.println("");
|
System.out.println("");
|
||||||
}
|
}
|
||||||
|
@ -380,35 +291,4 @@ public class Compiler {
|
||||||
throw new FileException("file error");
|
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
|
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() {
|
assert_not_exist() {
|
||||||
local file="$1"; shift
|
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
|
assert_stat 0 ./noreturn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_33_multipleinput() {
|
||||||
|
assert_compile_success src1.cb src2.cb -o src
|
||||||
|
assert_status 4 ./src
|
||||||
|
}
|
||||||
|
|
||||||
###
|
###
|
||||||
### Local Assertions
|
### Local Assertions
|
||||||
###
|
###
|
||||||
|
|
Loading…
Reference in New Issue