All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.org.kiama.util.Compiler.scala Maven / Gradle / Ivy

/**
 * This file is part of Kiama.
 *
 * Copyright (C) 2010-2012 Anthony M Sloane, Macquarie University.
 *
 * Kiama is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * Kiama is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Kiama.  (See files COPYING and COPYING.LESSER.)  If not, see
 * .
 */

package org.kiama
package util

import java.io.Reader
import org.kiama.attribution.Attributable
import scala.util.parsing.combinator.RegexParsers

/**
 * Trait to provide basic functionality for a compiler-like program
 * constructed from phases.
 */
trait CompilerBase[T] {

    import java.io.Reader
    import org.kiama.util.Console
    import org.kiama.util.JLineConsole
    import org.kiama.util.Emitter
    import org.kiama.util.IO._
    import org.kiama.util.StringEmitter
    import scala.io.Source

    /**
     * Process the program in the file given as the first command-line
     * argument, read input using JLine input editing, and emit output
     * to the standard output.
     */
    def main (args : Array[String]) {
        driver (args, JLineConsole, new Emitter)
    }

    /**
     * Process the command-line arguments.  Returns the arguments that
     * have not been processed.  Output should be emitted using the
     * provided Emitter.  Default: do no processing.
     */
    def checkargs (args : Array[String], emitter : Emitter) : Array[String] =
        args

    /**
     * Process the arguments, using the given console for input and the
     * given emitter for output.  The arguments are first processed by
     * checkargs.  Any remaining arguments are interpreted as names of
     * UTF-8 encoded files which are processed in turn by using `makeast` to
     * turn their contents into abstract syntax trees (ASTs) and then by
     * process which conducts arbitrary processing on the ASTs.
     */
    def driver (args : Array[String], console : Console, emitter : Emitter) {
        val newargs = checkargs (args, emitter)
        for (arg <- newargs) {
            try {
                val reader = filereader (newargs (0))
                makeast (reader, newargs (0), emitter) match {
                    case Left (ast) =>
                        process (ast, console, emitter)
                    case Right (msg) =>
                        emitter.emitln (msg)
                }
            } catch {
                case e : FileNotFoundException =>
                    emitter.emitln (e.getMessage)
            }
        }
    }

    /**
     * Make an AST from the file with the given name, returning it wrapped in
     * `Left`.  Returns `Right` with an error message if an AST cannot be made.
     */
    def makeast (reader : Reader, filename : String, emitter : Emitter) : Either[T,String]

    /**
     * Function to process the input that was parsed.  `console` should be
     * used to read anything needed by the processing.  `emitter` should be
     * used for output.  Return true if everything worked, false otherwise.
     * If false is returned, messages about the problem should be logged
     * by `process` using the messaging facility.
     */
    def process (ast : T, console : Console, emitter : Emitter) : Boolean

    /**
     * Run the driver using the given args and return the resulting output,
     * which may be error messages or the result of running the compiled
     * program, for example. Read standard input from the specified console.
     * Reset the message buffer before calling the driver.
     */
    def compile (args : Array[String], console : Console) : String = {
        val emitter = new StringEmitter
        Messaging.resetmessages
        driver (args, console, emitter)
        emitter.result
    }

}

/**
 * A compiler that uses a Scala combinator character-level parser. Define
 * `parser` to specify the actual parser to be used.
 */
trait RegexCompiler[T] extends CompilerBase[T] {

    this : RegexParsers =>

    /**
     * The actual parser used to produce the AST.
     */
    def parser : Parser[T]

    def makeast (reader : Reader, filename : String, emitter : Emitter) : Either[T,String] =
        parseAll (parser, reader) match {
            case Success (ast, _) =>
                Left (ast)
            case f =>
                Right (f.toString)
        }

}

/**
 * A compiler that uses RegexParser to produce Attributable ASTs. The AST
 * is initialised with `initTree` by `process`. Override it and call it before
 * performing specific attribution.
 */
trait Compiler[T <: Attributable] extends RegexCompiler[T] {
 
    this : RegexParsers =>

    import org.kiama.attribution.Attribution.initTree

    def process (ast : T, console : Console, emitter : Emitter) : Boolean = {
        initTree (ast)
        true
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy