de.codesourcery.jasm16.compiler.Main.java Source code

Java tutorial

Introduction

Here is the source code for de.codesourcery.jasm16.compiler.Main.java

Source

/**
 * Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.codesourcery.jasm16.compiler;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Stack;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;

import de.codesourcery.jasm16.Address;
import de.codesourcery.jasm16.ast.ASTUtils;
import de.codesourcery.jasm16.compiler.ICompiler.CompilerOption;
import de.codesourcery.jasm16.compiler.io.AbstractObjectCodeWriter;
import de.codesourcery.jasm16.compiler.io.AbstractObjectCodeWriterFactory;
import de.codesourcery.jasm16.compiler.io.FileObjectCodeWriter;
import de.codesourcery.jasm16.compiler.io.FileResource;
import de.codesourcery.jasm16.compiler.io.IObjectCodeWriter;
import de.codesourcery.jasm16.compiler.io.IObjectCodeWriterFactory;
import de.codesourcery.jasm16.compiler.io.IResource;
import de.codesourcery.jasm16.compiler.io.SimpleFileObjectCodeWriterFactory;
import de.codesourcery.jasm16.compiler.phases.VerboseCodeGenerationPhase;
import de.codesourcery.jasm16.utils.DebugCompilationListener;
import de.codesourcery.jasm16.utils.FormattingVisitor;
import de.codesourcery.jasm16.utils.Misc;

/**
 * Class to invoke the compiler from the command-line.
 *
 * <p>Run this class without options (or with '--help') to see the available command-line options.</p>
 * @author tobias.gierke@code-sourcery.de
 */
public class Main {

    private final Compiler compiler = new Compiler();

    private ByteArrayOutputStream generatedObjectCode = new ByteArrayOutputStream();
    private File outputFile;

    /*
     * Options.
     */
    private boolean printSymbolTable = false;
    private boolean printStackTraces = false;
    private boolean printDebugStats = false;
    private boolean verboseOutput = false;
    private boolean dumpObjectCode = false;
    private boolean printSourceCode = false;
    private boolean relaxedParsing = false;
    private boolean relaxedValidation = false;
    private boolean disableLiteralInlining = false;
    private boolean enableLocalLabelSupport = false;

    public static void main(String[] args) throws Exception {
        try {
            System.exit(new Main().run(args));
        } catch (Exception e) {
            System.out.println("\n\nERROR: " + e.getMessage() + "\n");
            e.printStackTrace();
            System.exit(1);
        }
    }

    private int run(String[] args) throws Exception {
        final List<ICompilationUnit> units = new ArrayList<ICompilationUnit>();

        final Stack<String> arguments = new Stack<String>();
        for (String arg : args) {
            arguments.push(arg);
        }
        Collections.reverse(arguments);

        while (!arguments.isEmpty()) {
            final String arg = arguments.peek();
            if (arg.startsWith("-") || arg.startsWith("--")) {
                try {
                    handleCommandlineOption(arg, arguments);
                } catch (NoSuchElementException e) {
                    printError("Invalid command line, option " + arg + " lacks argument.");
                    return 1;
                }
            } else {
                units.add(createCompilationUnit(arguments.pop()));
            }
        }

        if (verboseOutput) {
            printVersionInfo();
        }

        if (units.isEmpty()) {
            printError("No input files.");
            return 1;
        }

        setupCompiler(units);

        final ICompilationListener listener;
        if (printDebugStats || verboseOutput) {
            listener = new DebugCompilationListener(printDebugStats);
        } else {
            listener = new CompilationListener();
        }

        if (printSourceCode) {
            compiler.insertCompilerPhaseAfter(new CompilerPhase("format-code") {
                @Override
                protected void run(ICompilationUnit unit, ICompilationContext context) throws IOException {
                    if (unit.getAST() != null) {
                        ASTUtils.visitInOrder(unit.getAST(), new FormattingVisitor(context));
                    }
                };

            }, ICompilerPhase.PHASE_GENERATE_CODE);
        }

        // invoke compiler
        compiler.compile(units, listener);

        boolean hasErrors = false;
        for (ICompilationUnit unit : units) {
            if (unit.hasErrors()) {
                Misc.printCompilationErrors(unit, Misc.readSource(unit), printStackTraces);
                hasErrors = true;
            }
        }

        if (dumpObjectCode) {
            dumpObjectCode();
        }
        return hasErrors ? 1 : 0;
    }

    private void printVersionInfo() {
        System.out.println(Compiler.VERSION + "\n(c) 2012 by tobias.gierke@code-sourcery.de\n");
    }

    private void dumpObjectCode() {
        final byte[] combined = this.generatedObjectCode.toByteArray();

        if (ArrayUtils.isEmpty(combined)) {
            System.out.println("No object code generated.");
            return;
        }

        System.out.println("\nHex dump:\n\n" + Misc.toHexDumpWithAddresses(Address.byteAddress(0), combined, 8));
    }

    private void setupCompiler(List<ICompilationUnit> units) {

        if (printSymbolTable) {
            compiler.insertCompilerPhaseAfter(new PrintSymbolTablePhase(), ICompilerPhase.PHASE_GENERATE_CODE);
        }

        setObjectCodeWriterFactory(units);

        if (verboseOutput) {
            compiler.replaceCompilerPhase(new VerboseCodeGenerationPhase(), ICompilerPhase.PHASE_GENERATE_CODE);
        }

        if (enableLocalLabelSupport) {
            compiler.setCompilerOption(CompilerOption.LOCAL_LABELS_SUPPORTED, true);
        }

        if (printStackTraces) {
            compiler.setCompilerOption(CompilerOption.DEBUG_MODE, true);
        }

        if (relaxedParsing) {
            compiler.setCompilerOption(CompilerOption.RELAXED_PARSING, true);
        }

        if (relaxedValidation) {
            compiler.setCompilerOption(CompilerOption.RELAXED_VALIDATION, true);
        }

        if (disableLiteralInlining) {
            compiler.setCompilerOption(CompilerOption.DISABLE_INLINING, true);
        }
    }

    private void setObjectCodeWriterFactory(List<ICompilationUnit> units) {
        final IObjectCodeWriterFactory factory;

        if (dumpObjectCode) {
            factory = new AbstractObjectCodeWriterFactory() {

                @Override
                protected void deleteOutputHook() throws IOException {
                    generatedObjectCode = new ByteArrayOutputStream();
                }

                @Override
                protected IObjectCodeWriter createObjectCodeWriter(ICompilationContext context) {
                    return new AbstractObjectCodeWriter() {

                        @Override
                        protected void deleteOutputHook() throws IOException {
                            generatedObjectCode = new ByteArrayOutputStream();
                        }

                        @Override
                        protected OutputStream createOutputStream() throws IOException {
                            return generatedObjectCode;
                        }

                        @Override
                        protected void closeHook() throws IOException {
                        }
                    };
                }
            };
        } else if (outputFile != null) {
            outputFile.delete();
            final boolean append = units.size() > 1;
            factory = new SimpleFileObjectCodeWriterFactory(outputFile, append);
        } else {
            // no output file given, just dump source.dasm16 into source.o
            factory = new SimpleFileObjectCodeWriterFactory() {
                @Override
                protected IObjectCodeWriter createObjectCodeWriter(ICompilationContext context) {
                    final IResource resource = context.getCurrentCompilationUnit().getResource();
                    if (!(resource instanceof FileResource)) {
                        throw new RuntimeException("Internal error, not a file resoure: " + resource);
                    }
                    final FileResource fileResource = (FileResource) resource;
                    return new FileObjectCodeWriter(new File(toObjectFileName(fileResource.getFile())), false);
                }

                private String toObjectFileName(File sourceFile) {
                    final List<String> nameComponents = Arrays.asList(sourceFile.getName().split("\\."));
                    if (nameComponents.size() == 1) {
                        return nameComponents.get(0) + ".dcpu16";
                    }
                    final String nameWithoutSuffix = StringUtils
                            .join(nameComponents.subList(0, nameComponents.size() - 1), "");
                    return nameWithoutSuffix + ".dcpu16";
                }

            };
        }
        compiler.setObjectCodeWriterFactory(factory);
    }

    private void handleCommandlineOption(String option, Stack<String> arguments) {
        if ("-d".equalsIgnoreCase(option) || "--debug".equalsIgnoreCase(option)) {
            this.printStackTraces = true;
            this.printDebugStats = true;
            this.verboseOutput = true;

            arguments.pop();
        } else if ("--local-labels".equalsIgnoreCase(option)) {
            this.enableLocalLabelSupport = true;
            arguments.pop();
        } else if ("--relaxed-validation".equalsIgnoreCase(option)) {
            this.relaxedValidation = true;
            arguments.pop();
        } else if ("--disable-literal-inlining".equalsIgnoreCase(option)) {
            this.disableLiteralInlining = true;
            arguments.pop();
        } else if ("--relaxed-parsing".equalsIgnoreCase(option)) {
            this.relaxedParsing = true;
            arguments.pop();
        } else if ("--print".equalsIgnoreCase(option)) {
            this.printSourceCode = true;
            arguments.pop();
        } else if ("--print-symbols".equalsIgnoreCase(option)) {
            this.printSymbolTable = true;
            arguments.pop();
        } else if ("-v".equalsIgnoreCase(option) || "--verbose".equalsIgnoreCase(option)) {
            this.verboseOutput = true;
            arguments.pop();
        } else if ("--dump".equalsIgnoreCase(option)) {
            this.dumpObjectCode = true;
            arguments.pop();
        } else if ("-o".equalsIgnoreCase(option)) {
            arguments.pop();
            this.outputFile = new File(arguments.pop());
        } else if ("-h".equalsIgnoreCase(option) || "--help".equalsIgnoreCase(option)) {
            printUsage();
            System.exit(1);
        } else {
            printError("ERROR: Unrecognized option '" + option + "'\n\n");
            printUsage();
            System.exit(1);
        }
    }

    private void printUsage() {

        printVersionInfo();

        final String usage = "\nUsage: [options] [-o <output file>] source1 source2 ...\n\n"
                + "-o                          => output file to write generated assembly code to, otherwise code will be written to source.dcpu16\n"
                + "-d or --debug               => print debug output\n"
                + "--print                     => print formatted source code along with hex dump of generated assembly\n"
                + "--print-symbols             => print symbol table\n"
                + "--local-labels              => treat identifiers starting with a dot ('.') as local labels\n"
                + "--disable-literal-inlining  => disable inlining of literals -1 ... 30\n"
                + "--dump                      => instead of writing generated object code to a file, write a hexdump to std out\n"
                + "--relaxed-parsing           => relaxed parsing (instructions are parsed case-insensitive)\n"
                + "--relaxed-validation        => out-of-range values only cause a warning)\n"
                + "-v or --verbose             => print more verbose output during compilation\n\n";
        System.out.println(usage);
    }

    // DEBUG
    protected static class PrintSymbolTablePhase extends CompilerPhase {

        public PrintSymbolTablePhase() {
            super("debug-symbols");
        }

        @Override
        protected void run(ICompilationUnit unit, ICompilationContext context) throws IOException {
            final List<ISymbol> symbols = context.getSymbolTable().getSymbols();
            final Comparator<ISymbol> comp = new Comparator<ISymbol>() {

                @Override
                public int compare(ISymbol o1, ISymbol o2) {
                    if (o1 instanceof Label && o2 instanceof Label) {
                        Address addr1 = ((Label) o1).getAddress();
                        Address addr2 = ((Label) o2).getAddress();
                        if (addr1 != null && addr2 != null) {
                            return (int) Math.signum(addr1.getValue() - addr2.getValue());
                        }
                    }
                    return 1;
                }
            };
            Collections.sort(symbols, comp);

            System.out.println("\nSymbol table:\n\n");
            for (ISymbol s : symbols) {
                String name = s.getFullyQualifiedName();
                final String sAddress;
                if (s instanceof Label) {
                    final Address addr = ((Label) s).getAddress();
                    if (addr != null) {
                        sAddress = "0x" + Misc.toHexString(addr.getValue());
                    } else {
                        sAddress = "< not calculated yet >";
                    }
                } else {
                    sAddress = "< not a label? >";
                }
                System.out.println(Misc.padRight(name, 20) + "     " + sAddress);
            }
        }
    }

    private void printError(String message) {
        System.out.println("ERROR: " + message);
    }

    private ICompilationUnit createCompilationUnit(String file) throws IOException {

        final File infile = new File(file);
        if (!infile.exists()) {
            throw new IOException("ERROR: File '" + file + "' does not exist.");
        }
        if (!infile.isFile()) {
            throw new IOException("ERROR: '" + file + "' is no file.");
        }
        return CompilationUnit.createInstance(file, infile);
    }
}