view trunk/src/SettingsLoader.d @ 798:c24be8d4f6ab

Added documentation comments.
author Aziz K?ksal <aziz.koeksal@gmail.com>
date Sat, 01 Mar 2008 02:53:06 +0100
parents 8e6fed11bb68
children
line wrap: on
line source

/++
  Author: Aziz Köksal
  License: GPL3
+/
module SettingsLoader;

import Settings;
import dil.Messages;
import dil.ast.Node, dil.ast.Declarations, dil.ast.Expressions;
import dil.semantic.Module;
import dil.semantic.Pass1;
import dil.semantic.Symbol;
import dil.semantic.Symbols;
import dil.Information;
import dil.Compilation;
import common;

import tango.io.FilePath;

/// Loads settings from a D module file.
class SettingsLoader
{
  InfoManager infoMan; /// Collects error messages.
  Module mod; /// Current module.

  this(InfoManager infoMan)
  {
    this.infoMan = infoMan;
  }

  static SettingsLoader opCall(InfoManager infoMan)
  {
    return new SettingsLoader(infoMan);
  }

  /// Creates an error report.
  /// Params:
  ///   token = where the error occurred.
  ///   formatMsg = error message.
  void error(Token* token, char[] formatMsg, ...)
  {
    auto location = token.getErrorLocation();
    auto msg = Format(_arguments, _argptr, formatMsg);
    infoMan ~= new SemanticError(location, msg);
  }

  T getValue(T)(char[] name)
  {
    auto var = mod.lookup(name);
    if (!var) // Returning T.init instead of null, because dmd gives an error.
      return error(mod.firstToken, "variable '{}' is not defined", name), T.init;
    auto t = var.node.begin;
    if (!var.isVariable)
      return error(t, "'{}' is not a variable declaration", name), T.init;
    auto value = var.to!(Variable).value;
    if (!value)
      return error(t, "'{}' variable has no value set", name), T.init;
    T val = value.Is!(T); // Try casting to T.
    if (!val)
      error(value.begin, "the value of '{}' is not of type {}", name, typeof(T).stringof);
    return val;
  }

  T castTo(T)(Node n)
  {
    char[] type;
    is(T == StringExpression) && (type = "char[]");
    if (!n.Is!(T))
      error(n.begin, "expression is not of type {}", type);
    return n.Is!(T);
  }

  void load()
  {
    scope execPath = new FilePath(GetExecutableFilePath());
    execPath = new FilePath(execPath.folder());

    // Load config.d
    auto filePath = resolvePath(execPath, "config.d");
    mod = new Module(filePath, infoMan);
    mod.parse();

    if (mod.hasErrors)
      return;

    auto context = new CompilationContext;
    auto pass1 = new SemanticPass1(mod, context);
    pass1.start();

    if (auto array = getValue!(ArrayInitExpression)("version_ids"))
      foreach (value; array.values)
        if (auto str = castTo!(StringExpression)(value))
          GlobalSettings.versionIds ~= str.getString();
    if (auto val = getValue!(StringExpression)("langfile"))
      GlobalSettings.langFile = val.getString();
    if (auto array = getValue!(ArrayInitExpression)("import_paths"))
      foreach (value; array.values)
        if (auto str = castTo!(StringExpression)(value))
          GlobalSettings.importPaths ~= str.getString();
    if (auto array = getValue!(ArrayInitExpression)("ddoc_files"))
      foreach (value; array.values)
        if (auto str = castTo!(StringExpression)(value))
          GlobalSettings.ddocFilePaths ~= resolvePath(execPath, str.getString());
    if (auto val = getValue!(StringExpression)("xml_map"))
      GlobalSettings.xmlMapFile = val.getString();
    if (auto val = getValue!(StringExpression)("html_map"))
      GlobalSettings.htmlMapFile = val.getString();
    if (auto val = getValue!(StringExpression)("lexer_error"))
      GlobalSettings.lexerErrorFormat = val.getString();
    if (auto val = getValue!(StringExpression)("parser_error"))
      GlobalSettings.parserErrorFormat = val.getString();
    if (auto val = getValue!(StringExpression)("semantic_error"))
      GlobalSettings.semanticErrorFormat = val.getString();

    // Load language file.
    filePath = resolvePath(execPath, GlobalSettings.langFile);
    mod = new Module(filePath);
    mod.parse();

    if (mod.hasErrors)
      return;

    pass1 = new SemanticPass1(mod, context);
    pass1.start();

    if (auto array = getValue!(ArrayInitExpression)("messages"))
    {
      char[][] messages;
      foreach (value; array.values)
        if (auto str = castTo!(StringExpression)(value))
          messages ~= str.getString();
      if (messages.length != MID.max+1)
        error(mod.firstToken,
              "messages table in {} must exactly have {} entries, but not {}.",
              filePath, MID.max+1, messages.length);
      GlobalSettings.messages = messages;
      dil.Messages.SetMessages(messages);
    }
    if (auto val = getValue!(StringExpression)("lang_code"))
      GlobalSettings.langCode = val.getString();
  }
}

/// Loads an associative array from a D module file.
class TagMapLoader : SettingsLoader
{
  this(InfoManager infoMan)
  {
    super(infoMan);
  }

  static TagMapLoader opCall(InfoManager infoMan)
  {
    return new TagMapLoader(infoMan);
  }

  string[string] load(string filePath)
  {
    mod = new Module(filePath, infoMan);
    mod.parse();
    if (mod.hasErrors)
      return null;

    auto context = new CompilationContext;
    auto pass1 = new SemanticPass1(mod, context);
    pass1.start();

    string[string] map;
    if (auto array = getValue!(ArrayInitExpression)("map"))
      foreach (i, value; array.values)
      {
        auto key = array.keys[i];
        if (auto valExp = castTo!(StringExpression)(value))
          if (!key)
            error(value.begin, "expected key : value");
          else if (auto keyExp = castTo!(StringExpression)(key))
            map[keyExp.getString()] = valExp.getString();
      }
    return map;
  }
}

/// Resolves the path to a file from the executable's dir path
/// if it is relative.
/// Returns: filePath if it is absolute or execPath + filePath.
string resolvePath(FilePath execPath, string filePath)
{
  if ((new FilePath(filePath)).isAbsolute())
    return filePath;
  return execPath.dup.append(filePath).toString();
}

version(DDoc)
{
  /// Returns the fully qualified path to this executable.
  char[] GetExecutableFilePath();
}
else version(Windows)
{
private extern(Windows) uint GetModuleFileNameA(void*, char*, uint);

char[] GetExecutableFilePath()
{
  alias GetModuleFileNameA GetModuleFileName;
  char[] buffer = new char[256];
  uint count;

  while (1)
  {
    if (buffer is null)
      return null;

    count = GetModuleFileName(null, buffer.ptr, buffer.length);
    if (count == 0)
      return null;
    if (buffer.length != count && buffer[count] == 0)
      break;
    // Increase size of buffer
    buffer.length = buffer.length * 2;
  }
  assert(buffer[count] == 0);
  // Reduce buffer to the actual length of the string (excluding '\0'.)
  if (count < buffer.length)
    buffer.length = count;
  return buffer;
}
}
else version(linux)
{
private extern(C) size_t readlink(char* path, char* buf, size_t bufsize);

char[] GetExecutableFilePath()
{
  char[] buffer = new char[256];
  size_t count;

  while (1)
  {
    // This won't work on very old Linux systems.
    count = readlink("/proc/self/exe".ptr, buffer.ptr, buffer.length);
    if (count == -1)
      return null;
    if (count < buffer.length)
      break;
    buffer.length = buffer.length * 2;
  }
  buffer.length = count;
  return buffer;
}
}
else
  static assert(0, "GetExecutableFilePath() is not implemented on this platform.");