changeset 309:b4d842b0d2c7

- Added new files Settings.d, config.d and lang_en.d - Removed redundant break statements from Lexer.d. - Added function Cast() to SyntaxTree.d. - Relocated compiler version info to Settings.d. - Removed messages variable from module Messages. Compiler messages are loaded dynamically now. - Relocated some functions from module Information to module Messages, and added some format functions to it. - Message tables are located in their own lang_*.d files. - Added getString() method to class StringLiteralsExpression. - Module Settings has a static struct GlobalSettings. It loads global settings from config.d and a language file.
author aziz
date Wed, 15 Aug 2007 16:07:05 +0000
parents 3b9fc1e72210
children f01cdff9db0c
files trunk/src/Expressions.d trunk/src/Information.d trunk/src/Lexer.d trunk/src/Messages.d trunk/src/Settings.d trunk/src/SyntaxTree.d trunk/src/config.d trunk/src/lang_en.d trunk/src/main.d
diffstat 9 files changed, 244 insertions(+), 111 deletions(-) [+]
line wrap: on
line diff
--- a/trunk/src/Expressions.d	Tue Aug 14 18:35:02 2007 +0000
+++ b/trunk/src/Expressions.d	Wed Aug 15 16:07:05 2007 +0000
@@ -693,6 +693,16 @@
     mixin(set_kind);
     this.strings = strings;
   }
+
+  string getString()
+  {
+    char[] buffer;
+    foreach (strTok; strings)
+    {
+      buffer ~= strTok.str[0..$-1];
+    }
+    return buffer;
+  }
 }
 
 class ArrayLiteralExpression : Expression
--- a/trunk/src/Information.d	Tue Aug 14 18:35:02 2007 +0000
+++ b/trunk/src/Information.d	Wed Aug 15 16:07:05 2007 +0000
@@ -4,7 +4,6 @@
 +/
 module Information;
 import Messages;
-import std.string;
 import std.stdarg;
 
 enum InfoType
@@ -31,31 +30,6 @@
 
   string getMsg()
   {
-    char[] msg = messages[id];
-
-    if (arguments.length == 0)
-      return msg;
-
-    foreach (i, arg; arguments)
-      msg = replace(msg, format("{%s}", i+1), arg);
-
-    return msg;
+    return format_args(GetMsg(id), arguments);
   }
 }
-
-char[][] arguments(TypeInfo[] tinfos, void* argptr)
-{
-  char[][] args;
-  foreach (ti; tinfos)
-  {
-    if (ti == typeid(char[]))
-      args ~= format(va_arg!(char[])(argptr));
-    else if (ti == typeid(int))
-      args ~= format(va_arg!(int)(argptr));
-    else if (ti == typeid(dchar))
-      args ~= format(va_arg!(dchar)(argptr));
-    else
-      assert(0, "argument type not supported yet.");
-  }
-  return args;
-}
--- a/trunk/src/Lexer.d	Tue Aug 14 18:35:02 2007 +0000
+++ b/trunk/src/Lexer.d	Wed Aug 15 16:07:05 2007 +0000
@@ -879,13 +879,9 @@
             ++p;
             if (c == 0xFFFF)
               error(MID.UndefinedHTMLEntity, (begin-1)[0..p-(begin-1)]);
-            break;
           }
           else
-          {
             error(MID.UnterminatedHTMLEntity);
-            break;
-          }
         }
         else
           error(MID.InvalidBeginHTMLEntity);
--- a/trunk/src/Messages.d	Tue Aug 14 18:35:02 2007 +0000
+++ b/trunk/src/Messages.d	Wed Aug 15 16:07:05 2007 +0000
@@ -3,10 +3,13 @@
   License: GPL3
 +/
 module Messages;
+import Settings;
+import std.stdarg;
 
-/// Index into table of error messages.
+/// Index into table of compiler messages.
 enum MID
 {
+  // Lexer messages:
   InvalidUnicodeCharacter,
   InvalidUTF8Sequence,
   // ''
@@ -51,57 +54,55 @@
   HexFloatMissingExpDigits,
   FloatExponentDigitExpected,
 
-  // Parser messages
+  // Parser messages:
   ExpectedButFound,
   RedundantStorageClass,
+
+  // Help messages:
+  HelpMain,
+}
+
+string GetMsg(MID mid)
+{
+  assert(mid < GlobalSettings.messages.length);
+  return GlobalSettings.messages[mid];
+}
+
+char[] format(MID mid, ...)
+{
+  auto args = arguments(_arguments, _argptr);
+  return format_args(GetMsg(mid), args);
+}
+
+char[] format(char[] format_str, ...)
+{
+  auto args = arguments(_arguments, _argptr);
+  return format_args(format_str, args);
 }
 
-string[] messages = [
-  "invalid Unicode character.",
-  "invalid UTF-8 sequence.",
-  // ''
-  "unterminated character literal.",
-  "empty character literal.",
-  // #line
-  "expected 'line' after '#'.",
-  `the filespec must be defined in a double quote string literal (e.g. "filespec".)`,
-  "positive integer expected after #line",
-  "newline not allowed inside special token.",
-  "expected a terminating newline after special token.",
-  // ""
-  "unterminated string literal.",
-  // x""
-  "non-hex character '{1}' found in hex string.",
-  "odd number of hex digits in hex string.",
-  "unterminated hex string.",
-  // /* */ /+ +/
-  "unterminated block comment (/* */).",
-  "unterminated nested comment (/+ +/).",
-  // `` r""
-  "unterminated raw string.",
-  "unterminated back quote string.",
-  // \x \u \U
-  "found undefined escape sequence.",
-  "insufficient number of hex digits in escape sequence.",
-  // \&[a-zA-Z][a-zA-Z0-9]+;
-  "undefined HTML entity '{1}'",
-  "unterminated HTML entity.",
-  "html entities must begin with a letter.",
-  // integer overflows
-  "decimal number overflows sign bit.",
-  "overflow in decimal number.",
-  "overflow in hexadecimal number.",
-  "overflow in binary number.",
-  "overflow in octal number.",
-  "overflow in float number.",
-  "digits 8 and 9 are not allowed in octal numbers.",
-  "invalid hex number; at least one hex digit expected.",
-  "invalid binary number; at least one binary digit expected.",
-  "the exponent of a hexadecimal float number is required.",
-  "missing decimal digits in hexadecimal float exponent.",
-  "exponents have to start with a digit.",
+char[] format_args(char[] format_str, char[][] args)
+{
+  char[] result = format_str;
+
+  foreach (i, arg; args)
+    result = std.string.replace(result, std.string.format("{%s}", i+1), arg);
+
+  return result;
+}
 
-  // Parser messages
-  "expected '{1}', but found '{2}'.",
-  "'{1}' is redundant",
-];
+char[][] arguments(TypeInfo[] tinfos, void* argptr)
+{
+  char[][] args;
+  foreach (ti; tinfos)
+  {
+    if (ti == typeid(char[]))
+      args ~= va_arg!(char[])(argptr);
+    else if (ti == typeid(int))
+      args ~= std.string.format(va_arg!(int)(argptr));
+    else if (ti == typeid(dchar))
+      args ~= std.string.format(va_arg!(dchar)(argptr));
+    else
+      assert(0, "argument type not supported yet.");
+  }
+  return args;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Settings.d	Wed Aug 15 16:07:05 2007 +0000
@@ -0,0 +1,101 @@
+/++
+  Author: Aziz Köksal
+  License: GPL3
++/
+module Settings;
+import Parser, SyntaxTree, Declarations, Expressions;
+import std.metastrings;
+
+version(D2)
+{
+  const VERSION_MAJOR = 2;
+  const VERSION_MINOR = 0;
+}
+else
+{
+  const VERSION_MAJOR = 1;
+  const VERSION_MINOR = 0;
+}
+const string VERSION = Format!("%s.%s", VERSION_MAJOR, VERSION_MINOR);
+
+const COMPILED_WITH = __VENDOR__;
+const COMPILED_VERSION = Format!("%s.%s", __VERSION__/1000, __VERSION__%1000);
+const COMPILED_DATE = __TIMESTAMP__;
+
+const usageHighlight = "highlight (hl) file.d";
+
+struct GlobalSettings
+{
+static:
+  string language; /// Language of messages catalogue to load.
+  string[] messages; /// Table of localized compiler messages.
+  void load()
+  {
+    auto fileName = "config.d"[];
+    auto sourceText = cast(char[]) std.file.read(fileName);
+    auto parser = new Parser(sourceText, fileName);
+    parser.start();
+    auto root = parser.parseModule();
+
+    if (parser.errors.length || parser.lx.errors.length)
+    {
+      throw new Exception("There are errors in " ~ fileName ~ ".");
+    }
+
+    foreach (decl; root.children)
+    {
+      auto v = Cast!(VariableDeclaration)(decl);
+      if (v && v.idents[0].srcText == "language")
+      {
+        auto e = v.values[0];
+        if (!e)
+          throw new Exception("language variable has no value set.");
+        auto val = Cast!(StringLiteralsExpression)(e);
+        if (val)
+        {
+          GlobalSettings.language = val.getString();
+          break;
+        }
+      }
+    }
+
+    // Load messages
+    if (GlobalSettings.language.length)
+    {
+      fileName = "lang_" ~ GlobalSettings.language ~ ".d";
+      sourceText = cast(char[]) std.file.read(fileName);
+      parser = new Parser(sourceText, fileName);
+      parser.start();
+      root = parser.parseModule();
+
+      if (parser.errors.length || parser.lx.errors.length)
+      {
+        throw new Exception("There are errors in "~fileName~".");
+      }
+
+      char[][] messages;
+      foreach (decl; root.children)
+      {
+        auto v = Cast!(VariableDeclaration)(decl);
+        if (v && v.idents[0].srcText == "messages")
+        {
+          auto e = v.values[0];
+          if (!e)
+            throw new Exception("messages variable in "~fileName~" has no value set.");
+          if (auto array = Cast!(ArrayInitializer)(e))
+          {
+            foreach (value; array.values)
+            {
+              if (auto str = Cast!(StringLiteralsExpression)(value))
+                messages ~= str.getString();
+            }
+          }
+          else
+            throw new Exception("messages variable is set to "~e.classinfo.name~" instead of an ArrayInitializer.");
+        }
+      }
+      GlobalSettings.messages = messages;
+    }
+  }
+
+}
--- a/trunk/src/SyntaxTree.d	Tue Aug 14 18:35:02 2007 +0000
+++ b/trunk/src/SyntaxTree.d	Wed Aug 15 16:07:05 2007 +0000
@@ -209,6 +209,14 @@
 /// This string is mixed into the constructor of a class that inherits from Node.
 const string set_kind = `this.kind = mixin("NodeKind." ~ typeof(this).stringof);`;
 
+Class Cast(Class)(Node n)
+{
+  assert(n !is null);
+  if (n.kind == mixin("NodeKind." ~ typeof(Class).stringof))
+    return cast(Class)cast(void*)n;
+  return null;
+}
+
 class Node
 {
   NodeCategory category;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/config.d	Wed Aug 15 16:07:05 2007 +0000
@@ -0,0 +1,1 @@
+option language = "en";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/lang_en.d	Wed Aug 15 16:07:05 2007 +0000
@@ -0,0 +1,65 @@
+/++
+  Author: Aziz Köksal
+  License: GPL3
++/
+
+string[] messages = [
+  "invalid Unicode character.",
+  "invalid UTF-8 sequence.",
+  // ''
+  "unterminated character literal.",
+  "empty character literal.",
+  // #line
+  "expected 'line' after '#'.",
+  `the filespec must be defined in a double quote string literal (e.g. "filespec".)`,
+  "integer expected after #line",
+  "newline not allowed inside special token.",
+  "expected a terminating newline after special token.",
+  // ""
+  "unterminated string literal.",
+  // x""
+  "non-hex character '{1}' found in hex string.",
+  "odd number of hex digits in hex string.",
+  "unterminated hex string.",
+  // /* */ /+ +/
+  "unterminated block comment (/* */).",
+  "unterminated nested comment (/+ +/).",
+  // `` r""
+  "unterminated raw string.",
+  "unterminated back quote string.",
+  // \x \u \U
+  "found undefined escape sequence.",
+  "insufficient number of hex digits in escape sequence.",
+  // \&[a-zA-Z][a-zA-Z0-9]+;
+  "undefined HTML entity '{1}'",
+  "unterminated HTML entity.",
+  "html entities must begin with a letter.",
+  // integer overflows
+  "decimal number overflows sign bit.",
+  "overflow in decimal number.",
+  "overflow in hexadecimal number.",
+  "overflow in binary number.",
+  "overflow in octal number.",
+  "overflow in float number.",
+  "digits 8 and 9 are not allowed in octal numbers.",
+  "invalid hex number; at least one hex digit expected.",
+  "invalid binary number; at least one binary digit expected.",
+  "the exponent of a hexadecimal float number is required.",
+  "missing decimal digits in hexadecimal float exponent.",
+  "exponents have to start with a digit.",
+
+  // Parser messages
+  "expected '{1}', but found '{2}'.",
+  "'{1}' is redundant.",
+
+  `dil v{1}
+Copyright (c) 2007 by Aziz Köksal. All rights reserved. Licensed under GPL3.
+
+Subcommands:
+  {2}
+
+Type 'dil help <subcommand>' for more help on a particular subcommand.
+
+Compiled with {3} v{4} on {5}.
+`
+];
\ No newline at end of file
--- a/trunk/src/main.d	Tue Aug 14 18:35:02 2007 +0000
+++ b/trunk/src/main.d	Wed Aug 15 16:07:05 2007 +0000
@@ -9,45 +9,22 @@
 import Messages;
 import std.stdio;
 import std.file;
-import std.metastrings;
-
-import Declarations, SyntaxTree;
-
-version(D2)
-{
-  const VERSION_MAJOR = 2;
-  const VERSION_MINOR = 0;
-}
-else
-{
-  const VERSION_MAJOR = 1;
-  const VERSION_MINOR = 0;
-}
-const string VERSION = Format!("%s.%s", VERSION_MAJOR, VERSION_MINOR);
-
-const char[] usageHighlight = "highlight (hl) file.d";
-const string helpMain = `dil v`~VERSION~`
-Copyright (c) 2007 by Aziz Köksal
-
-Subcommands:
-  `~usageHighlight~`
-
-Type 'dil help <subcommand>' for more help on a particular subcommand.
-
-Compiled with `~__VENDOR__~` `~Format!("v%s.%s", __VERSION__/1000, __VERSION__%1000)~` on `~__TIMESTAMP__~`.
-`;
+import Settings;
+import Declarations, Expressions, SyntaxTree;
 
 void main(char[][] args)
 {
+  GlobalSettings.load();
+
   if (args.length <= 1)
-    return writefln(helpMain);
+    return writefln(format(MID.HelpMain, VERSION, usageHighlight, COMPILED_WITH, COMPILED_VERSION, COMPILED_DATE));
 
   string command = args[1];
   switch (command)
   {
   case "hl", "highlight":
     if (args.length == 3)
-      highlightTokens(args[2]);
+      tokensToXML(args[2]);
     break;
   default:
   }
@@ -90,7 +67,7 @@
   return result;
 }
 
-void highlightTokens(string fileName)
+void tokensToXML(string fileName)
 {
   auto sourceText = cast(char[]) std.file.read(fileName);
   auto lx = new Lexer(sourceText, fileName);