diff src/SettingsLoader.d @ 806:bcb74c9b895c

Moved out files in the trunk folder to the root.
author Aziz K?ksal <aziz.koeksal@gmail.com>
date Sun, 09 Mar 2008 00:12:19 +0100
parents trunk/src/SettingsLoader.d@c24be8d4f6ab
children 525ee3f848d9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/SettingsLoader.d	Sun Mar 09 00:12:19 2008 +0100
@@ -0,0 +1,252 @@
+/++
+  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.");