# HG changeset patch # User johnsen@johnsen-desktop # Date 1208476898 -7200 # Node ID 2168f4cb73f1be167839890f60a18c74e1ad494f First push diff -r 000000000000 -r 2168f4cb73f1 LICENSE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,19 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + 14 rue de Plaisance, 75014 Paris, France + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. */ diff -r 000000000000 -r 2168f4cb73f1 ast/Decl.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ast/Decl.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,58 @@ +module ast.Decl; + +import ast.Exp, + ast.Stmt; + +import lexer.Token; + +import sema.SymbolTable; + +enum DeclType +{ + VarDecl, + FuncDecl, +} + +class Decl +{ + this(DeclType declType) + { + this.declType = declType; + } + + DeclType declType; + Scope env; +} + +class VarDecl : Decl +{ + this(Identifier type, Identifier identifier, + Exp e = null) + { + super(DeclType.VarDecl); + this.type = type; + this.identifier = identifier; + this.init = e; + } + + Identifier type, identifier; + Exp init; +} + +class FuncDecl : Decl +{ + this(Identifier type, Identifier identifier, + VarDecl[] funcArgs, Stmt[] statements) + { + super(DeclType.FuncDecl); + this.type = type; + this.identifier = identifier; + this.funcArgs = funcArgs; + this.statements = statements; + } + + Identifier type, identifier; + VarDecl[] funcArgs; + Stmt[] statements; +} + diff -r 000000000000 -r 2168f4cb73f1 ast/Exp.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ast/Exp.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,135 @@ +module ast.Exp; + +import tango.text.Util : jhash; + +import lexer.Token; + +import sema.SymbolTable; + +enum ExpType +{ + Binary, + Negate, + IntegerLit, + Identifier, + AssignExp, + CallExp, +} + +class Exp +{ + this(ExpType expType) + { + this.expType = expType; + } + + ExpType expType; + Scope env; +} + +class CallExp : Exp +{ + this(Exp exp, Exp[] args) + { + super(ExpType.CallExp); + this.exp = exp; + this.args = args; + } + + Exp exp; + Exp[] args; +} + +class AssignExp : Exp +{ + this(Identifier identifier, Exp exp) + { + super(ExpType.AssignExp); + this.identifier = identifier; + this.exp = exp; + } + + Identifier identifier; + Exp exp; +} + +class BinaryExp : Exp +{ + public enum Operator : char + { + Mul = '*', Div = '/', + Add = '+', Sub = '-' + } + + this(Operator op, Exp left, Exp right) + { + super(ExpType.Binary); + this.op = op; + this.left = left; + this.right = right; + } + + Operator op; + Exp left, right; +} + +class NegateExp : Exp +{ + this(Exp exp) + { + super(ExpType.Negate); + this.exp = exp; + } + + public Exp exp; +} + +class IntegerLit : Exp +{ + this(Token t) + { + super(ExpType.IntegerLit); + this.token = t; + } + + Token token; +} + +class Identifier : Exp +{ + this(Token t) + { + super(ExpType.Identifier); + this.token = t; + name = t.get; + } + + char[] get() + { + return name; + } + + hash_t toHash() + { + return jhash(name); + } + + int opCmp(Object o) + { + if (auto id = cast(Identifier)o) + return typeid(char[]).compare(&name, &id.name); + return 0; + } + + int opEquals(Object o) + { + if (auto id = cast(Identifier)o) + return typeid(char[]).equals(&name, &id.name); + return 0; + } + + Token token; + char[] name; +} + + diff -r 000000000000 -r 2168f4cb73f1 ast/Stmt.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ast/Stmt.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,57 @@ +module ast.Stmt; + +import ast.Exp, + ast.Decl; + +import sema.SymbolTable; + +enum StmtType +{ + Stmt, + Decl, + Exp, + Return, +} + +class Stmt +{ + this(StmtType stmtType = StmtType.Stmt) + { + this.stmtType = stmtType; + } + + StmtType stmtType; + Scope env; +} + +class ReturnStmt : Stmt +{ + this() + { + super(StmtType.Return); + } + + public Exp exp; +} + +class DeclStmt : Stmt +{ + this(Decl decl) + { + super(StmtType.Decl); + this.decl = decl; + } + + public Decl decl; +} + +class ExpStmt : Stmt +{ + this(Exp exp) + { + super(StmtType.Exp); + this.exp = exp; + } + + public Exp exp; +} diff -r 000000000000 -r 2168f4cb73f1 dang/OptParse.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dang/OptParse.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,810 @@ +/* +Copyright (c) 2007 Kirk McDonald + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +/** + * Command-line option parsing, in the style of Python's optparse. + * + * Refer to the complete docs for more information. + */ +module dang.OptParse; + +import tango.io.Stdout; +import tango.text.Util : locate, locatePrior; +import tango.text.Ascii : toUpper; +import tango.stdc.stdlib : exit, EXIT_FAILURE, EXIT_SUCCESS; +import tango.text.convert.Integer : parse, toInt, toString = toString; +import tango.text.convert.Utf : toString, toString32; + +/* +Options may be in two forms: long and short. Short options start with a single +dash and are one letter long. Long options start with two dashes and may +consist of any number of characters (so long as they don't start with a dash, +though they may contain dashes). Options are case-sensitive. + +Short options may be combined. The following are equivalent: + +$ myapp -a -b -c +$ myapp -abc +$ myapp -ab -c + +If -f and --file are aliases of the same option, which accepts an argument, +the following are equivalent: + +$ myapp -f somefile.txt +$ myapp -fsomefile.txt +$ myapp --file somefile.txt +$ myapp --file=somefile.txt + +The following are also valid: + +$ myapp -abcf somefile.txt +$ myapp -abcfsomefile.txt +$ myapp -abc --file somefile.txt + +If an option occurs multiple times, the last one is the one recorded: + +$ myapp -f somefile.txt --file otherfile.txt + +Matches 'otherfile.txt'. +*/ + +bool startswith(char[] s, char[] start) { + if (s.length < start.length) return false; + return s[0 .. start.length] == start; +} +bool endswith(char[] s, char[] end) { + if (s.length < end.length) return false; + return s[$ - end.length .. $] == end; +} + +/// Thrown if client code tries to set up an improper option. +class OptionError : Exception { + this(char[] msg) { super(msg); } +} +// Thrown if client code tries to extract the wrong type from an option. +class OptionTypeError : Exception { + this(char[] msg) { super(msg); } +} + +/++ +This class represents the results after parsing the command-line. ++/ +class Options { + char[][][char[]] opts; + int[char[]] counted_opts; + /// By default, leftover arguments are placed in this array. + char[][] args; + + /// Retrieves the results of the Store and StoreConst actions. + char[] opIndex(char[] opt) { + char[][]* o = opt in opts; + if (o) { + return (*o)[0]; + } else { + return ""; + } + } + /// Retrieves the results of the Store action, when the type is Integer. + int value(char[] opt) { + char[][]* o = opt in opts; + if (o) { + return toInt((*o)[0]); + } else { + return 0; + } + } + /// Retrieves the results of the Append and AppendConst actions. + char[][] list(char[] opt) { + char[][]* o = opt in opts; + if (o) { + return *o; + } else { + return null; + } + } + /// Retrieves the results of the Append action, when the type is Integer. + int[] valueList(char[] opt) { + char[][]* o = opt in opts; + int[] l; + if (o) { + l.length = (*o).length; + foreach (i, s; *o) { + l[i] = toInt(s); + } + } + return l; + } + /// Retrieves the results of the Count action. + int count(char[] opt) { + int* c = opt in counted_opts; + if (c) { + return *c; + } else { + return 0; + } + } + /// Retrieves the results of the SetTrue and SetFalse actions. + bool flag(char[] opt) { + char[][]* o = opt in opts; + if (o) { + return (*o)[0] == "1"; + } else { + return false; + } + } +} + +// Options, args, this opt's index in args, name[, arg] +/// +alias void delegate(Options, inout char[][], inout int, char[], char[]) OptionCallbackFancyArg; +/// +alias void delegate(Options, inout char[][], inout int, char[], int) OptionCallbackFancyInt; +/// +alias void delegate(Options, inout char[][], inout int, char[]) OptionCallbackFancy; + +/// +alias void delegate(char[]) OptionCallbackArg; +/// +alias void delegate(int) OptionCallbackInt; +/// +alias void delegate() OptionCallback; + +/* +Actions: + * Store: name + * StoreConst: name, const_value + * Append: name + * AppendConst: name, const_value + * Count: name + * CallbackArg: dga + * CallbackVoid: dg +*/ +/// +enum Action { /+++/Store, /+++/StoreConst, /+++/Append, /+++/AppendConst, /+++/Count, /+++/SetTrue, /+++/SetFalse, /+++/Callback, /+++/CallbackFancy, /+++/Help /+++/} +/// +enum ArgType { /+++/None, /+++/String, /+++/Integer /+++/} + +ArgType defaultType(Action action) { + switch (action) { + case Action.Store, Action.Append, Action.Callback, Action.CallbackFancy: + return ArgType.String; + break; + default: + return ArgType.None; + break; + } +} + +/++ +This class represents a single command-line option. ++/ +class Option { + char[][] shortopts, longopts; + Action action; + ArgType type; + char[] name, argname; + char[] const_value; + + char[] default_string; + int default_value; + bool default_flag, has_default; + + OptionCallbackArg callback; + OptionCallbackInt int_callback; + OptionCallback void_callback; + + OptionCallbackFancyArg fancy_callback; + OptionCallbackFancyInt fancy_int_callback; + OptionCallbackFancy fancy_void_callback; + char[] helptext; + this( + char[][] shorts, char[][] longs, ArgType type, + Action act, char[] name, char[] const_value, + OptionCallbackArg dga, OptionCallback dg, + OptionCallbackInt dgi, + OptionCallbackFancyArg fdga, + OptionCallbackFancyInt fdgi, + OptionCallbackFancy fdg + ) { + this.shortopts = shorts; + this.longopts = longs; + this.action = act; + this.type = type; + this.name = name; + this.argname = toUpper(name.dup); + this.default_string = ""; + this.default_value = 0; + this.default_flag = false; + + // Perform sanity checks. + assert (name !is null); + switch (act) { + case Action.Store, Action.Append: + assert(type != ArgType.None); + break; + case Action.StoreConst, Action.AppendConst: + assert(type == ArgType.None); + assert(const_value !is null); + break; + case Action.Callback: + //assert(type != ArgType.None); + switch (type) { + case ArgType.String: + assert(dga !is null); + break; + case ArgType.Integer: + assert(dgi !is null); + break; + case ArgType.None: + assert(dg !is null); + break; + } + break; + case Action.CallbackFancy: + switch (type) { + case ArgType.String: + assert(fdga !is null); + break; + case ArgType.Integer: + assert(fdgi !is null); + break; + case ArgType.None: + assert(fdg !is null); + break; + } + default: + break; + } + this.const_value = const_value; + this.callback = dga; + this.int_callback = dgi; + this.void_callback = dg; + this.fancy_callback = fdga; + this.fancy_int_callback = fdgi; + this.fancy_void_callback = fdg; + } + char[] toString() { + int optCount = this.shortopts.length + this.longopts.length; + char[] result; + bool printed_arg = false; + foreach(i, opt; this.shortopts ~ this.longopts) { + result ~= opt; + if (i < optCount-1) { + result ~= ", "; + } else if (this.hasArg()) { + result ~= "=" ~ toUpper(this.argname.dup); + } + } + return result; + } + //enum Action { Store, StoreConst, Append, AppendConst, Count, SetTrue, SetFalse, Callback, CallbackFancy, Help } + void issue_default(Options results) { + // Only set the default if the option doesn't already have a value. + char[][]* val = this.name in results.opts; + switch (this.action) { + case Action.Store, Action.Append: + if (val !is null) return; + if (this.type == ArgType.String) { + results.opts[name] = [default_string]; + } else { + results.opts[name] = [.toString(default_value)]; + } + break; + case Action.StoreConst, Action.AppendConst: + if (val !is null) return; + results.opts[name] = [default_string]; + break; + case Action.SetTrue, Action.SetFalse: + if (val !is null) return; + if (default_flag) { + results.opts[name] = ["1"]; + } else { + results.opts[name] = ["0"]; + } + break; + default: + return; + } + } + // Does whatever this option is supposed to do. + void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { + int i; + if (this.type == ArgType.Integer) { + // Verify that it's an int. + i = parser.toOptInt(arg); + } + switch (this.action) { + case Action.Store: + results.opts[name] = [arg]; + break; + case Action.Append: + results.opts[name] ~= arg; + break; + case Action.StoreConst: + assert(arg is null, "Got unexpected argument for '"~name~"' option."); + results.opts[name] = [const_value]; + break; + case Action.AppendConst: + assert(arg is null, "Got unexpected argument for '"~name~"' option."); + results.opts[name] ~= const_value; + break; + case Action.Count: + assert(arg is null, "Got unexpected argument for '"~name~"' option."); + ++results.counted_opts[name]; + break; + case Action.SetTrue: + results.opts[name] = ["1"]; + break; + case Action.SetFalse: + results.opts[name] = ["0"]; + break; + case Action.Callback: + switch (type) { + case ArgType.String: + callback(arg); + break; + case ArgType.Integer: + int_callback(i); + break; + case ArgType.None: + void_callback(); + break; + } + break; + case Action.CallbackFancy: + switch (type) { + case ArgType.String: + fancy_callback(results, args, idx, name, arg); + break; + case ArgType.Integer: + fancy_int_callback(results, args, idx, name, i); + break; + case ArgType.None: + fancy_void_callback(results, args, idx, name); + break; + } + break; + case Action.Help: + parser.helpText(); + exit(EXIT_SUCCESS); + break; + } + } + /// Returns whether this option accepts an argument. + bool hasArg() { + return this.type != ArgType.None; + } + /// Sets the help text for this option. + Option help(char[] help) { + this.helptext = help; + return this; + } + /// Sets the name of this option's argument, if it has one. + Option argName(char[] argname) { + this.argname = argname; + return this; + } + Option def(char[] val) { + if ( + (this.type != ArgType.String || (this.action != Action.Store && this.action != Action.Append)) && + this.action != Action.StoreConst && this.action != Action.AppendConst + ) + throw new OptionError("Cannot specify string default for non-string option '"~this.name~"'"); + this.has_default = true; + this.default_string = val; + return this; + } + Option def(int val) { + if (this.type != ArgType.Integer || (this.action != Action.Store && this.action != Action.Append)) + throw new OptionError("Cannot specify integer default for non-integer option '"~this.name~"'"); + this.has_default = true; + this.default_value = val; + return this; + } + Option def(bool val) { + if (this.action != Action.SetTrue && this.action != Action.SetFalse) + throw new OptionError("Cannot specify boolean default for non-flag option '"~this.name~"'"); + this.has_default = true; + this.default_flag = val; + return this; + } + // Returns true if the passed option string matches this option. + bool matches(char[] _arg) { + dchar[] arg = toString32(_arg); + if ( + arg.length < 2 || + arg.length == 2 && (arg[0] != '-' || arg[1] == '-') || + arg.length > 2 && (arg[0 .. 2] != "--" || arg[2] == '-') + ) { + return false; + } + if (arg.length == 2) { + foreach (opt; shortopts) { + if (_arg == opt) { + return true; + } + } + } else { + foreach (opt; longopts) { + if (_arg == opt) { + return true; + } + } + } + return false; + } +} + +/++ +This class is used to define a set of options, and parse the command-line +arguments. ++/ +class OptionParser { + OptionCallbackArg leftover_cb; + /// An array of all of the options known by this parser. + Option[] options; + char[] name, desc; + /// The description of the programs arguments, as used in the Help action. + char[] argdesc; + private void delegate(char[]) error_callback; + + this(char[] desc="") { + this.name = ""; + this.desc = desc; + this.argdesc = "[options] args..."; + } + + /// Sets a callback, to override the default error behavior. + void setErrorCallback(void delegate(char[]) dg) { + error_callback = dg; + } + void unknownOptError(char[] opt) { + error("Unknown argument '"~opt~"'"); + } + void expectedArgError(char[] opt) { + error("'"~opt~"' option expects an argument."); + } + /// Displays an error message and terminates the program. + void error(char[] err) { + if (error_callback !is null) { + error_callback(err); + } else { + this.helpText(); + Stdout.formatln(err); + } + exit(EXIT_FAILURE); + } + int toOptInt(char[] s) { + int i; + uint ate; + i = .parse(s, 10u, &ate); + if (ate != s.length) + error("Could not convert '"~s~"' to an integer."); + return i; + } + + /// Displays useful "help" information about the program's options. + void helpText() { + int optWidth; + char[][] optStrs; + typedef char spacechar = ' '; + spacechar[] padding; + // Calculate the maximum width of the option lists. + foreach(i, opt; options) { + optStrs ~= opt.toString(); + if (optStrs[i].length > optWidth) { + optWidth = optStrs[i].length; + } + } + Stdout.formatln("Usage: {0} {1}", this.name, this.argdesc); + if (this.desc !is null && this.desc != "") Stdout.formatln(this.desc); + Stdout.formatln("\nOptions:"); + foreach(i, opt; options) { + padding.length = optWidth - optStrs[i].length; + Stdout.formatln(" {0}{1} {2}", optStrs[i], cast(char[])padding, opt.helptext); + } + } + + // Checks the passed arg against all the options in the parser. + // Returns null if no match is found. + Option matches(char[] arg) { + foreach(o; options) { + if (o.matches(arg)) { + return o; + } + } + return null; + } + char[] getBaseName(char[] path) { + version(Windows) { + char delimiter = '\\'; + } else { + char delimiter = '/'; + } + uint idx = locatePrior(path, delimiter); + if (idx == path.length) return path; + return path[idx+1 .. $]; + } + char[] getProgramName(char[] path) { + version(Windows) { + // (Unicode note: ".exe" only contains 4 code units, so this slice + // should Just Work.) (Although it remains to be seen how robust + // this code actually is.) + //Stdout.formatln(path); + //assert(path[$-4 .. $] == ".exe"); + //path = path[0 .. $-4]; + } + return getBaseName(path); + } + /// Parses the passed command-line arguments and returns the results. + Options parse(char[][] args) { + this.name = getProgramName(args[0]); + args = args[1 .. $]; + Options options = new Options; + /* + The issue is this: + + $ myapp -abc + + This might be three short opts, or one or two opts, the last of which + accepts an argument. In the three-opt case, we want to get: + + $ myapp -a -b -c + + In the one-opt case, we want: + + $ myapp -a bc + + In the two-opt case, we want: + + $ myapp -a -b c + + We also want to parse apart "--file=somefile" into "--file somefile" + */ + char[] opt, newopt, arg; + dchar[] opt32; + int idx; + Option match; + + for (int i=0; i= 2) { + opt32 = toString32(opt[1 .. $]); + foreach (j, c; opt32) { + newopt = .toString("-" ~ [c]); + match = matches(newopt); + if (match is null) { + unknownOptError(newopt); + } + if (match.hasArg) { + // This is the last char in the group, look to the + // next element of args for the arg. + if (j == opt32.length-1) { + if (i == args.length-1) expectedArgError(match.name); + arg = args[i+1]; + ++i; + // Otherwise, consume the rest of this group for + // the arg. + } else { + arg = .toString(opt32[j+1 .. $]); + match.performAction(this, options, args, i, arg); + break; + } + } else { + arg = null; + } + match.performAction(this, options, args, i, arg); + } + } else { + unknownOptError(opt); + } + } else { + if (this.leftover_cb is null) { + options.args ~= opt; + } else { + this.leftover_cb(opt); + } + } + } + foreach (o; this.options) { + o.issue_default(options); + } + return options; + } + + /++ + Overrides the default behavior of leftover arguments, calling this callback + with them instead of adding them an array. + +/ + void leftoverCallback(OptionCallbackArg dg) { + this.leftover_cb = dg; + } + /// + Option addOption(Option option) { + this.options ~= option; + return option; + } + // options action name type const_value dga dgv dgi fdga fdgi fdg + /// + Option addOption(char[][] options ...) { + return addOption(options, Action.Store, null, defaultType(Action.Store), null, null, null, null, null, null, null); + } + /// + Option addOption(char[][] options, char[] name) { + return addOption(options, Action.Store, name, defaultType(Action.Store), null, null, null, null, null, null, null); + } + /// + Option addOption(char[][] options, Action action) { + return addOption(options, action, null, defaultType(action), null, null, null, null, null, null, null); + } + /// + Option addOption(char[][] options, ArgType type) { + return addOption(options, Action.Store, null, type, null, null, null, null, null, null, null); + } + /// + Option addOption(char[][] options, Action action, ArgType type) { + return addOption(options, action, null, type, null, null, null, null, null, null, null); + } + /// + Option addOption(char[][] options, char[] name, Action action) { + return addOption(options, action, name, defaultType(action), null, null, null, null, null, null, null); + } + /// + Option addOption(char[][] options, char[] name, Action action, ArgType type) { + return addOption(options, action, name, type, null, null, null, null, null, null, null); + } + /// + Option addOption(char[][] options, Action action, char[] const_value) { + return addOption(options, action, null, defaultType(action), const_value, null, null, null, null, null, null); + } + /// + Option addOption(char[][] options, char[] name, char[] const_value) { + return addOption(options, Action.StoreConst, name, defaultType(Action.Store), const_value, null, null, null, null, null, null); + } + /// + Option addOption(char[][] options, char[] name, Action action, char[] const_value) { + return addOption(options, action, name, defaultType(action), const_value, null, null, null, null, null, null); + } + /// + Option addOption(char[][] options, OptionCallbackArg dg) { + return addOption(options, Action.Callback, null, ArgType.String, null, dg, null, null, null, null, null); + } + /// + Option addOption(char[][] options, OptionCallback dg) { + return addOption(options, Action.Callback, null, ArgType.None, null, null, dg, null, null, null, null); + } + /// + Option addOption(char[][] options, OptionCallbackInt dg) { + return addOption(options, Action.Callback, null, ArgType.Integer, null, null, null, dg, null, null, null); + } + /// + Option addOption(char[][] options, OptionCallbackFancyArg dg) { + return addOption(options, Action.CallbackFancy, null, ArgType.String, null, null, null, null, dg, null, null); + } + /// + Option addOption(char[][] options, OptionCallbackFancy dg) { + return addOption(options, Action.CallbackFancy, null, ArgType.None, null, null, null, null, null, null, dg); + } + /// + Option addOption(char[][] options, OptionCallbackFancyInt dg) { + return addOption(options, Action.CallbackFancy, null, ArgType.Integer, null, null, null, null, null, dg, null); + } + /// + Option addOption(char[][] options, char[] name, OptionCallbackFancyArg dg) { + return addOption(options, Action.CallbackFancy, name, ArgType.String, null, null, null, null, dg, null, null); + } + /// + Option addOption(char[][] options, char[] name, OptionCallbackFancy dg) { + return addOption(options, Action.CallbackFancy, name, ArgType.None, null, null, null, null, null, null, dg); + } + /// + Option addOption(char[][] options, char[] name, OptionCallbackFancyInt dg) { + return addOption(options, Action.CallbackFancy, name, ArgType.Integer, null, null, null, null, null, dg, null); + } + // Although users certainly /can/ call this, all those overloads are there + // for a reason. + Option addOption( + char[][] options, + Action action, char[] name, ArgType type, char[] const_value, + OptionCallbackArg callback, OptionCallback vcall, + OptionCallbackInt icall, + OptionCallbackFancyArg fdga, + OptionCallbackFancyInt fdgi, + OptionCallbackFancy fdg + ) { + char[][] shortopts; + char[][] longopts; + dchar[] opt; + Option option; + foreach (_opt; options) { + // (Unicode note: We convert to dchar[] so the length checks work + // out in the event of a short opt with a >127 character.) + opt = toString32(_opt); + if (opt.length < 2) { + throw new OptionError( + "invalid option string '" ~ _opt ~ "': must be at least two characters long" + ); + } else if (opt.length > 2) { + if (opt[0 .. 2] != "--" || opt[2] == '-') + throw new OptionError( + "invalid long option string '" ~ _opt ~ "': must start with --, followed by non-dash" + ); + longopts ~= _opt; + } else { + if (opt[0] != '-' || opt[1] == '-') + throw new OptionError( + "invalid short option string '" ~ _opt ~ "': must be of the form -x, where x is non-dash" + ); + shortopts ~= _opt; + } + } + if (name is null) { + // (Unicode note: We know '-' is a single code unit, so these + // slices are okay.) + if (longopts.length > 0) + name = longopts[0][2 .. $]; + else if (shortopts.length > 0) + name = shortopts[0][1 .. 2]; + else + throw new OptionError( + "No options provided to addOption!" + ); + } + option = new Option(shortopts, longopts, type, action, name, const_value, callback, vcall, icall, fdga, fdgi, fdg); + this.options ~= option; + return option; + } +} + diff -r 000000000000 -r 2168f4cb73f1 dang/compiler.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dang/compiler.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,171 @@ +module dang.compiler; + +import tango.io.Stdout, + tango.core.Signal, + tango.io.FilePath; + +import lexer.Lexer, + parser.Parser; + +import misc.DataSource; + +import ast.Decl; + +import tools.AstPrinter, + tools.DotPrinter; + +import gen.LuaGen, + gen.LLVMGen; + +import sema.Visitor; + +import dang.OptParse; + +void checkFiles(char[][] *files) +{ + bool error = false; + + char[][] validFiles; + + foreach(file ; *files) + { + auto path = new FilePath(file); + + if(!path.exists) + { + Stdout("File '"~file~"' does not exists").newline; + error = true; + + continue; + } + + bool fileInStack = false; + foreach(vFile ; validFiles) + if(vFile == file) + fileInStack = true; + + if(fileInStack) + continue; + + validFiles ~= file; + } + + *files = validFiles; + + if(error) + throw new Exception("Some file(s) did not exist"); +} +void main(char[][] args) +{ + char[][] filesToHandle; + + Signal!(char[][]*) preStart; + + Signal!(char[]) preLex; + Signal!(Lexer) postLex; + + Signal!(Lexer) preParse; + Signal!(Decl[], DataSource) postParse; + + preStart.attach(&checkFiles); + + auto argParse = new OptionParser; + + argParse.addOption( + ["-h", "--help"],{ + argParse.helpText(); + return; + } + ).help("Show this help message"); + + argParse.addOption( + ["--ast-dump-dot"], { + postParse.attach( + (Decl[] decls, DataSource src) { + auto print = new DotPrinter(); + print.print(decls); + }); + } + ).help("Output the AST as dot-graphicz"); + + argParse.addOption( + ["--ast-dump-code"], { + postParse.attach( + (Decl[] decls, DataSource src) { + auto print = new AstPrinter(src); + print.print(decls); + }); + } + ).help("Output the AST as dot-graphicz"); + + argParse.addOption( + ["--gen-lua"], { + postParse.attach( + (Decl[] decls, DataSource src) { + auto luaGen = new LuaGen(); + luaGen.gen(decls); + }); + } + ).help("Compile to Lua code"); + + argParse.addOption( + ["--gen-llvm"], { + postParse.attach( + (Decl[] decls, DataSource src) { + auto llvmGen = new LLVMGen(); + llvmGen.gen(decls); + }); + } + ).help("Compile to LLVM code"); + + auto options = argParse.parse(args); + + filesToHandle ~= options.args; + + try + { + preStart(&filesToHandle); + } + catch(Exception e) + { + return; + } + + foreach(file ; filesToHandle) + { + preLex(file); + + auto src = DataSource(file); + auto lexer = new Lexer(src); + + postLex(lexer); + + preParse(lexer); + + auto parser = new Parser; + auto decls = parser.parse(lexer); + + postParse(decls, src); + } + +/* if (args.length > 1 && args[1] == "lex") + { + Token t; + + t = lexer.next(); + while(t.type != Tok.EOF) + { + Stdout(src.get(t.position, t.length)).newline; + t = lexer.next(); + } + } + else + { + auto decl = parser.parse(lexer); + if(args.length > 1 && args[1] == "dump-ast") + { + auto buffer = new AstBuffer(src.data); + decl.print(buffer); + } + }*/ +} diff -r 000000000000 -r 2168f4cb73f1 dsss.conf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dsss.conf Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,9 @@ +[lexer] +[parser] +[ast] + +[dang/compiler.d] +Target = Dang + +[tests/run.d] +Target = tests/run diff -r 000000000000 -r 2168f4cb73f1 gen/LLVMGen.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gen/LLVMGen.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,409 @@ +module gen.LLVMGen; + +import tango.io.Stdout, + Int = tango.text.convert.Integer; +import tango.core.Array : find; + +import ast.Decl, + ast.Stmt, + ast.Exp; + +import lexer.Token; + +import sema.SymbolTableBuilder; + +class LLVMGen +{ +public: + this() + { + typeToLLVM = + [ + "int"[] : "i32"[], + "byte" : "i8", + "short" : "i16", + "long" : "i64", + "float" : "float", + "double" : "double", + "void" : "void", + "+" : "add", + "-" : "sub", + "*" : "mul", + "/" : "div" + ]; + table = new SimpleSymbolTable(); + } + + void gen(Decl[] decls) + { + // Fill in scopes + (new SymbolTableBuilder).visit(decls); + + table.enterScope; + + foreach(decl ; decls) + genRootDecl(decl); + + table.leaveScope; + } + + void genRootDecl(Decl decl) + { + switch(decl.declType) + { + case DeclType.FuncDecl: + FuncDecl funcDecl = cast(FuncDecl)decl; + + printBeginLine("define "); + print(typeToLLVM[funcDecl.type.token.get]); + print(" @"); + genIdentifier(funcDecl.identifier); + print("("); + + table.enterScope; + Identifier[] args; + foreach(i, funcArg ; funcDecl.funcArgs) + { + print(typeToLLVM[funcArg.type.token.get]); + print(" %"); + print("."~Integer.toString(i)); + args ~= funcArg.identifier; + table.find(funcArg.identifier.get); + if(i+1 < funcDecl.funcArgs.length) + print(", "); + } + + printEndLine(") {"); + + indent; + + foreach(i, arg ; args) + { + auto sym = funcDecl.env.find(arg); + auto type = typeToLLVM[sym.type.get]; + printBeginLine("%"~arg.get); + printEndLine(" = alloca " ~ type); + printBeginLine("store " ~ type ~ " %."); + print(Integer.toString(i)); + print(", " ~ type ~ "* %"); + printEndLine(arg.get); + } + + printEndLine(); + + foreach(stmt ; funcDecl.statements) + { + genStmt(stmt); + } + table.leaveScope; + dedent; + printBeginLine("}"); + printEndLine(); + + break; + + case DeclType.VarDecl: + auto varDecl = cast(VarDecl)decl; + printBeginLine("@"); + genIdentifier(varDecl.identifier); + + print(" = "); + if(varDecl.init) + { + if(cast(IntegerLit)varDecl.init) + printEndLine("global i32 " ~ (cast(IntegerLit)varDecl.init).token.get); + else + assert(0,"Declaring an variable to an expression is not allowed"); + } + else + printEndLine("i32 0"); + + printEndLine(); + + default: + } + } + + void genDecl(Decl decl) + { + switch(decl.declType) + { + case DeclType.VarDecl: + auto varDecl = cast(VarDecl)decl; + printBeginLine("%"); + print(table.find(varDecl.identifier.get)); + print(" = alloca "); + printEndLine(typeToLLVM[varDecl.type.get]); + if(varDecl.init) + { + auto assignExp = new AssignExp(varDecl.identifier, varDecl.init); + assignExp.env = decl.env; + assignExp.identifier.env = decl.env; + genExpression(assignExp); + } + + default: + } + } + + void unify(Ref* a, Ref* b) + { + if (a.type != b.type) + { + auto a_val = intTypes.find(a.type); + auto b_val = intTypes.find(b.type); + // swap types so a is always the "largest" type + if (a_val < b_val) + { + Ref* tmp = b; + b = a; + a = tmp; + } + + auto res = table.find("%.cast"); + printBeginLine(res); + printCastFromTo(b, a); + print(*b); + print(" to "); + printEndLine(a.type); + + b.type = a.type; + b.name = res; + } + } + + Ref genExpression(Exp exp) + { + switch(exp.expType) + { + case ExpType.Binary: + auto binaryExp = cast(BinaryExp)exp; + + auto left = genExpression(binaryExp.left); + auto right = genExpression(binaryExp.right); + + unify(&left, &right); + + auto res = Ref(left.type, table.find); + printBeginLine(res.name); + print(" = "~typeToLLVM[[binaryExp.op]]~" "); + print(left); + print(", "); + printEndLine(right.name); + return res; + case ExpType.IntegerLit: + auto integetLit = cast(IntegerLit)exp; + auto t = integetLit.token; + return Ref("int", t.get, true); + case ExpType.Negate: + auto negateExp = cast(NegateExp)exp; + auto target = genExpression(negateExp.exp); + auto res = table.find; + printBeginLine(res); + print(" = sub "~target.type~" 0, "); + printEndLine(target.name); + return Ref(target.type, res); + case ExpType.AssignExp: + auto assignExp = cast(AssignExp)exp; + auto sym = exp.env.find(assignExp.identifier); + + Ref val = genExpression(assignExp.exp); + Ref r = Ref(typeToLLVM[sym.type.get], val.name); + + if (val.type != r.type) + { + auto res = table.find("%.cast"); + printBeginLine(res); + printCastFromTo(val.type, r.type); + print(val); + print(" to "); + printEndLine(r.type); + r.name = res; + } + + printBeginLine("store "); + print(r); + print(", "); + print(r.type ~ "* %"); + printEndLine(assignExp.identifier.get); + break; + case ExpType.CallExp: + auto callExp = cast(CallExp)exp; + auto func_sym = exp.env.find(cast(Identifier)callExp.exp); + auto func_type = typeToLLVM[func_sym.type.get]; + Ref[] args; + foreach(i, arg ; callExp.args) + args ~= genExpression(arg); + + char[] res = table.find; + printBeginLine(res); + print(" = call "); + print(func_type); + print(" @"); + + print(func_sym.id.get); + + print("("); + foreach(i, arg ; args) + { + print(arg); + if(i+1 < args.length) + print(", "); + } + printEndLine(")"); + return Ref(func_sym.type.get, res); + case ExpType.Identifier: + auto identifier = cast(Identifier)exp; + auto sym = exp.env.find(identifier); + char[] res = table.find; + printBeginLine(res); + print(" = load "); + print(typeToLLVM[sym.type.get]); + print("* %"); + printEndLine(sym.id.name); + return Ref(sym.type.get, res); + } + return Ref(); + } + + void genStmt(Stmt stmt) + { + switch(stmt.stmtType) + { + case StmtType.Return: + auto ret = cast(ReturnStmt)stmt; + Ref res = genExpression(ret.exp); + printBeginLine("ret "); + printEndLine(res); + break; + case StmtType.Decl: + auto declStmt = cast(DeclStmt)stmt; + genDecl(declStmt.decl); + break; + case StmtType.Exp: + auto expStmt = cast(ExpStmt)stmt; + genExpression(expStmt.exp); + break; + + } + } + + void genIdentifier(Identifier identifier) + { + print(identifier.get); + } + + void indent() + { + tabIndex ~= tabType; + } + + void dedent() + { + tabIndex = tabIndex[0 .. $-tabType.length]; + } + + void printBeginLine(char[] line = "") + { + Stdout(tabIndex~line); + } + void printBeginLine(Ref r) + { + Stdout(tabIndex~r.type~" "~r.name); + } + + void printEndLine(char[] line = "") + { + Stdout(line).newline; + } + + void printEndLine(Ref r) + { + Stdout(r.type~" "~r.name).newline; + } + + void print(char[] line) + { + Stdout(line); + } + + void print(Ref r) + { + Stdout(r.type~" "~r.name); + } + + void printCastFromTo(size_t t1, size_t t2) + { + if (t1 < t2) + print(" = zext "); + else + print(" = trunc "); + } + + void printCastFromTo(char[] t1, char[] t2) + { + printCastFromTo(intTypes.find(t1), intTypes.find(t2)); + } + + void printCastFromTo(Ref* t1, Ref* t2) + { + printCastFromTo(intTypes.find(t1.type), intTypes.find(t2.type)); + } + +private: + + char[] tabIndex; + const char[] tabType = " "; // 4 spaces + FuncDecl[char[]] functions; + + SimpleSymbolTable table; + SymbolTable symbolTable; + static char[][char[]] typeToLLVM; + + static char[][] intTypes = [ "i8", "i16", "i32", "i64" ]; +} + +struct Ref +{ + char[] type; + char[] name; + bool atomic = false; + static Ref opCall(char[] type = "void", char[] name = "", bool atomic = false) + { + Ref r; + if(auto llvm_t = type in LLVMGen.typeToLLVM) + r.type = *llvm_t; + else + r.type = type; + r.name = name; + r.atomic = atomic; + return r; + } +} + +class SimpleSymbolTable +{ + int[char[]][] variables; + + void enterScope() + { + variables ~= cast(int[char[]])["__dollar":-1]; + } + + void leaveScope() + { + variables.length = variables.length - 1; + } + + char[] find(char[] v = "%.tmp") + { + foreach_reverse(map ; variables) + { + if(v in map) + return v~"."~Integer.toString(++map[v]); + } + variables[$-1][v] = 0; + return v; + } +} + diff -r 000000000000 -r 2168f4cb73f1 gen/LuaGen.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gen/LuaGen.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,180 @@ +module gen.LuaGen; + +import tango.io.Stdout, + Int = tango.text.convert.Integer; + +import ast.Decl, + ast.Stmt, + ast.Exp; + +import lexer.Token; + +class LuaGen +{ +public: + this() + { + } + + void gen(Decl[] decls) + { + + foreach(decl ; decls) + genDecl(decl); + printBeginLine("main()"); + printEndLine; + } + + void genDecl(Decl decl) + { + switch(decl.declType) + { + case DeclType.FuncDecl: + FuncDecl funcDecl = cast(FuncDecl)decl; + + printBeginLine("function "); + genIdentifier(funcDecl.identifier); + print("("); + foreach(i, funcArg ; funcDecl.funcArgs) + { + genIdentifier(funcArg.identifier); + if(i+1 < funcDecl.funcArgs.length) + print(", "); + } + printEndLine(")"); + indent; + foreach(stmt ; funcDecl.statements) + { + genStmt(stmt); + } + dedent; + printBeginLine("end"); + printEndLine(); + break; + + case DeclType.VarDecl: + genVarDecl(cast(VarDecl)decl); + + default: + } + } + + void genStmt(Stmt stmt) + { + switch(stmt.stmtType) + { + case StmtType.Return: + auto ret = cast(ReturnStmt)stmt; + printBeginLine("return "); + genExpression(ret.exp); + printEndLine(); + break; + case StmtType.Decl: + auto declStmt = cast(DeclStmt)stmt; + genDecl(declStmt.decl); + break; + case StmtType.Exp: + auto expStmt = cast(ExpStmt)stmt; + printBeginLine(); + genExpression(expStmt.exp); + printEndLine(); + break; + + } + } + + void genVarDecl(VarDecl decl) + { + printBeginLine("local "); + genIdentifier(decl.identifier); + if(decl.init) + { + print(" = "); + genExpression(decl.init); + } + printEndLine(); + + } + + void genExpression(Exp exp) + { + switch(exp.expType) + { + case ExpType.Binary: + auto binaryExp = cast(BinaryExp)exp; + genExpression(binaryExp.left); + print(" " ~ [binaryExp.op] ~ " "); + genExpression(binaryExp.right); + break; + case ExpType.IntegerLit: + auto integetLit = cast(IntegerLit)exp; + auto t = integetLit.token; + print(t.get); + break; + case ExpType.Negate: + auto negateExp = cast(NegateExp)exp; + print("-("); + genExpression(negateExp.exp); + print(")"); + break; + case ExpType.AssignExp: + auto assignExp = cast(AssignExp)exp; + genIdentifier(assignExp.identifier); + print(" = "); + genExpression(assignExp.exp); + break; + case ExpType.CallExp: + auto callExp = cast(CallExp)exp; + genExpression(callExp.exp); + print("("); + foreach(i, arg ; callExp.args) + { + genExpression(arg); + if(i+1 < callExp.args.length) + print(", "); + } + print(")"); + break; + case ExpType.Identifier: + auto identifier = cast(Identifier)exp; + print(identifier.token.get); + break; + } + + } + + void genIdentifier(Identifier identifier) + { + print(identifier.token.get); + } + + void indent() + { + tabIndex ~= tabType; + } + + void dedent() + { + tabIndex = tabIndex[0 .. $-tabType.length]; + } + + void printBeginLine(char[] line = "") + { + Stdout(tabIndex~line); + } + + void printEndLine(char[] line = "") + { + Stdout(line).newline; + } + + void print(char[] line) + { + Stdout(line); + } + +private: + char[] tabIndex; + const char[] tabType = " "; // 4 spaces +} + diff -r 000000000000 -r 2168f4cb73f1 lexer/Keyword.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lexer/Keyword.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,25 @@ +module lexer.Keyword; + +import lexer.Token; + +Tok[char[]] keywords; + +static this () +{ + keywords = + [ + "byte"[] : Tok.Byte, + "ubyte" : Tok.Ubyte, + "short" : Tok.Short, + "ushort" : Tok.Ushort, + "int" : Tok.Int, + "uint" : Tok.Uint, + "long" : Tok.Long, + "ulong" : Tok.Ulong, + + "float" : Tok.Float, + "double" : Tok.Double, + + "return" : Tok.Return + ]; +} diff -r 000000000000 -r 2168f4cb73f1 lexer/Lexer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lexer/Lexer.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,224 @@ +module lexer.Lexer; + +import misc.Error, + misc.DataSource; + +import lexer.Token, + lexer.Keyword; + +import tango.io.Stdout; + +class Lexer +{ +public: + this (DataSource source) + { + this.source = source; + this.position = 0; + } + + Token next () + { + switch (getNextChar) + { + case CharType.EOF: + Location l; + return Token (Tok.EOF, l, 0); + + case CharType.Whitespace: + position += 1; + return this.next; + + case CharType.Symbol: + return lexSymbol; + + case CharType.Letter: + return lexLetter; + + case CharType.Number: + return lexNumber; + } + } + + Token peek ( int skip = 0) + { + int oldPosition = this.position; + while(skip-- > 0) + this.next; + Token t = this.next; + this.position = oldPosition; + return t; + } + + public Error[] getErrors() + { + return this.errors; + } +private: + + Token lexNumber () + { + int i = 0; + while(getNextChar(++i) == CharType.Number) + {} + + position += i; + + return Token(Tok.Integer, Location(position - i, this.source), i); + } + + Token lexSymbol () + { + switch(source.data[position++]) + { + case '(': + return Token(Tok.OpenParentheses, Location(position - 1, this.source), 1); + case ')': + return Token(Tok.CloseParentheses, Location(position - 1, this.source), 1); + case '{': + return Token(Tok.OpenBrace, Location(position - 1, this.source), 1); + case '}': + return Token(Tok.CloseBrace, Location(position - 1, this.source), 1); + case ';': + return Token(Tok.Seperator, Location(position - 1, this.source), 1); + case ',': + return Token(Tok.Comma, Location(position - 1, this.source), 1); + case '=': + return Token(Tok.Assign, Location(position - 1, this.source), 1); + case '+': + return Token(Tok.Add, Location(position - 1, this.source), 1); + case '-': + return Token(Tok.Sub, Location(position - 1, this.source), 1); + case '*': + return Token(Tok.Mul, Location(position - 1, this.source), 1); + case '/': + switch(source.data[position]) + { + case '/': + while(getNextChar != CharType.EOF) + { + if(source.data[position++] == '\n') + return this.next; + } + return Token(Tok.EOF, Location(position, this.source), 0); + + case '*': + position += 2; + while(getNextChar != CharType.EOF) + { + ++position; + if(source.data[position-2] == '*') + if(source.data[position-1] == '/') + return this.next; + } + return Token(Tok.EOF, Location(position, this.source), 0); + + case '+': + position += 2; + int nesting = 1; + while(getNextChar != CharType.EOF) + { + ++position; + if(source.data[position-2] == '+') + if(source.data[position-1] == '/') + { + position++; + nesting--; + } + + if(source.data[position-2] == '/') + if(source.data[position-1] == '+') + { + nesting++; + position++; + } + + if(nesting == 0) + return this.next; + } + return Token(Tok.EOF, Location(position, this.source), 0); + + default: + return Token(Tok.Div, Location(position - 1, this.source), 1); + } + } + } + + Token lexLetter () + { + int i = 0; + bool hasNumber = false; + while (getNextChar(++i) == CharType.Letter || + getNextChar(i) == CharType.Number) + { + if (getNextChar(i) == CharType.Number) + { + hasNumber = true; + } + } + + Token t = Token(Tok.Identifier, Location(position, source), i); + + if (!hasNumber) + { + char[] str = source.data[position .. position + i]; + if(str in keywords) + t.type = keywords[str]; + } + + position += i; + + return t; + } + + CharType getNextChar(int offset = 0) + { + if (position + offset >= this.source.data.length) + return CharType.EOF; + + char current = source.data[position + offset]; + + if (current >= 'A' && current <= 'Z' || + current >= 'a' && current <= 'z' || current > 127) + return CharType.Letter; + + if (current >= '0' && current <= '9') + return CharType.Number; + + switch(current) + { + case ' ': + case '\n': + return CharType.Whitespace; + + case '(': + case ')': + case '{': + case '}': + case ';': + case ',': + case '=': + case '+': + case '-': + case '*': + case '/': + return CharType.Symbol; + + } + + } + + DataSource source; + int position; + Error[] errors; +} + +enum CharType : ubyte +{ + Letter, + Number, + Symbol, + Whitespace, + + EOF +} diff -r 000000000000 -r 2168f4cb73f1 lexer/Token.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lexer/Token.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,98 @@ +module lexer.Token; + +public +import misc.Location; + +import Integer = tango.text.convert.Integer; + +struct Token +{ + Tok type; + Location location; + uint length; + + static Token opCall (Tok type, Location location, uint length) + { + Token t; + t.type = type; + t.location = location; + t.length = length; + return t; + } + + char[] getType () + { + return typeToString[this.type]; + } + + char[] toString () + { + return this.getType()~": Len: "~Integer.toString(this.length) + ~", Loc: "~location.toString; + } + + char[] get () + { + return location.get(length); + } +} + +enum Tok : ushort +{ + /* Non-code related tokens */ + EOF, + + /* Basic types */ + Identifier, + Integer, + + /* Basic operators */ + Assign, + Add, Sub, + Mul, Div, + Comma, + + /* Symbols */ + OpenParentheses, + CloseParentheses, + OpenBrace, + CloseBrace, + Seperator, + + /* Keywords */ + Byte, Ubyte, + Short, Ushort, + Int, Uint, + Long, Ulong, + + Float, Double, + + Return, + +} + +public char[][Tok] typeToString; + +static this() +{ + typeToString = + [ + Tok.EOF:"EOF"[], + Tok.Identifier:"Identifier", + Tok.Byte:"Byte", + Tok.Short:"Short", + Tok.Int:"Int", + Tok.Long:"Long", + Tok.OpenParentheses:"OpenParentheses", + Tok.CloseParentheses:"CloseParentheses", + Tok.OpenBrace:"OpenBrace", + Tok.CloseBrace:"CloseBrace", + Tok.Assign:"Assign", + Tok.Add:"Add", + Tok.Sub:"Sub", + Tok.Mul:"Mul", + Tok.Div:"Div", + Tok.Integer:"Interger", + Tok.Seperator:"Seperator" + ]; +} diff -r 000000000000 -r 2168f4cb73f1 misc/DataSource.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/DataSource.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,25 @@ +module misc.DataSource; + +import tango.io.UnicodeFile; + +struct DataSource +{ + char[] name; + char[] data; + + static DataSource opCall(char[] name) + { + DataSource source; + + auto file = new UnicodeFile!(char)(name, Encoding.UTF_8); + + source.name = name; + source.data = file.read(); + return source; + } + + char[] get(uint position, ushort len) + { + return data[position .. position+len]; + } +} diff -r 000000000000 -r 2168f4cb73f1 misc/Error.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/Error.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,9 @@ +module misc.Error; + +import misc.Location; + +struct Error +{ + char[] messeage; + Location errorLocation; +} diff -r 000000000000 -r 2168f4cb73f1 misc/Location.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/Location.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,21 @@ +module misc.Location; + +import misc.DataSource; + +import Integer = tango.text.convert.Integer; + +struct Location +{ + uint position; + DataSource source; + + char[] toString () + { + return Integer.toString (position) ~" in "~source.name; + } + + char[] get(uint length) + { + return source.get(position, length); + } +} diff -r 000000000000 -r 2168f4cb73f1 parser/Parser.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/parser/Parser.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,305 @@ +module parser.Parser; + +import lexer.Lexer, + lexer.Token; + +import ast.Exp, + ast.Stmt, + ast.Decl; + +import tango.io.Stdout, + Integer = tango.text.convert.Integer; + +class Parser +{ + +public: + Decl[] parse(Lexer lexer) + { + this.lexer = lexer; + + + Decl[] declarations; + + while(lexer.peek.type != Tok.EOF) + { + declarations ~= parseDecl; + } + + return declarations; + } + + Decl parseDecl() + { + Token t = lexer.next; + + switch(t.type) + { + case Tok.Byte, Tok.Ubyte, + Tok.Short, Tok.Ushort, + Tok.Int, Tok.Uint, + Tok.Long, Tok.Ulong, + Tok.Float, Tok.Double, + Tok.Identifier: + Identifier type = new Identifier(t); + + Token iden = lexer.next; + switch(iden.type) + { + case Tok.Identifier: + Identifier identifier = new Identifier(iden); + Token p = lexer.peek(); + switch(p.type) + { + case Tok.OpenParentheses: + return parseFunc(type, identifier); + case Tok.Seperator: + require(Tok.Seperator); + return new VarDecl(type, identifier, null); + case Tok.Assign: + lexer.next(); + auto exp = parseExpression(); + require(Tok.Seperator); + return new VarDecl(type, identifier, exp); + default: + char[] c = t.getType; + error("Unexpexted token "~c~" at line "~Integer.toString(__LINE__)); + } + break; + default: + char[] c = t.getType; + error("Unexpexted token "~c~" at line "~Integer.toString(__LINE__)); + } + break; + + case Tok.EOF: + return null; + default: + char[] c = t.getType; + error("Unexpexted token "~c~" at line "~Integer.toString(__LINE__)); + } + } + + Stmt parseStatement() + { + Token t = lexer.peek; + + switch(t.type) + { + case Tok.Return: + lexer.next; + auto ret = new ReturnStmt(); + ret.exp = parseExpression(); + require(Tok.Seperator); + return ret; + case Tok.Identifier: + Token n = lexer.peek(1); + switch(n.type) + { + case Tok.Assign: + lexer.next; + lexer.next; + auto stmt = new ExpStmt(new AssignExp(new Identifier(t), parseExpression())); + require(Tok.Seperator); + return stmt; + break; + + default: + auto e = new ExpStmt(parseExpression()); + require(Tok.Seperator); + return e; + + } + break; + + default: + auto decl = new DeclStmt(parseDecl()); + //require(Tok.Seperator); + return decl; + } + return new Stmt(); + } + + FuncDecl parseFunc(Identifier type, Identifier identifier) + { + VarDecl[] funcArgs = parseFuncArgs(); + + lexer.next; // Remove the "{" + + Stmt[] statements; + + while(lexer.peek.type != Tok.CloseBrace) + statements ~= parseStatement(); + + lexer.next; // Remove "}" + + return new FuncDecl(type, identifier, funcArgs, statements); + } + + VarDecl[] parseFuncArgs() + { + lexer.next; // Remove the "(" token. + + VarDecl[] funcArgs; + + while(lexer.peek.type != Tok.CloseParentheses) + { + funcArgs ~= new VarDecl(parseType, parseIdentifier); + + if(lexer.peek.type == Tok.Comma) + lexer.next; + } + + lexer.next; // Remove the ")" + + return funcArgs; + } + + Identifier parseIdentifier() + { + Token identifier = lexer.next; + + switch(identifier.type) + { + case Tok.Identifier: + return new Identifier(identifier); + break; + default: + error("Unexpexted token in Identifier parsing"); + } + } + + Identifier parseType() + { + Token type = lexer.next; + + switch(type.type) + { + case Tok.Byte, Tok.Ubyte, + Tok.Short, Tok.Ushort, + Tok.Int, Tok.Uint, + Tok.Long, Tok.Ulong, + Tok.Float, Tok.Double, + Tok.Identifier: + return new Identifier(type); + break; + default: + char[] c = type.getType; + error("Unexpexted token in Type parsing. Got "~c); + } + } + + // -- Expression parsing -- // +private: + Exp parseExpression(int p = 0) + { + auto exp = P(); + Token next = lexer.peek(); + BinOp* op = null; + while ((op = binary(next.type)) != null && op.prec >= p) + { + lexer.next(); + int q = op.leftAssoc? 1 + op.prec : op.prec; + auto exp2 = parseExpression(q); + exp = new BinaryExp(op.operator, exp, exp2); + next = lexer.peek(); + } + + return exp; + } + + Exp P() + { + Token next = lexer.next(); + if (auto op = unary(next.type)) + return new NegateExp(parseExpression(op.prec)); + else if (next.type == Tok.OpenParentheses) + { + auto e = parseExpression(0); + require(Tok.CloseParentheses); + return e; + } + else if (next.type == Tok.Identifier) + { + switch(lexer.peek.type) + { + case Tok.OpenParentheses: + lexer.next; + Exp[] args; + while(lexer.peek.type != Tok.CloseParentheses) + { + if(lexer.peek.type == Tok.Comma) + { + lexer.next; + } + args ~= parseExpression(); + } + + lexer.next(); + return new CallExp(new Identifier(next), args); + + default: + return new Identifier(next); + } + } + else if (next.type == Tok.Integer) + return new IntegerLit(next); + + Stdout.formatln("{}", next.getType); + assert(0, "Should not happen"); + } + + struct UnOp + { + Tok tokenType; + int prec; + } + + static UnOp[] _unary = [{Tok.Sub, 4}]; + UnOp* unary(Tok t) + { + foreach (ref op; _unary) + if (op.tokenType == t) + return &op; + return null; + } + + struct BinOp + { + Tok tokenType; + int prec; + bool leftAssoc; + BinaryExp.Operator operator; + } + + static BinOp[] _binary = + [ + {Tok.Add, 3, true, BinaryExp.Operator.Add}, + {Tok.Sub, 3, true, BinaryExp.Operator.Sub}, + {Tok.Mul, 5, true, BinaryExp.Operator.Mul}, + {Tok.Div, 5, true, BinaryExp.Operator.Div} + ]; + BinOp* binary(Tok t) + { + foreach (ref op; _binary) + if (op.tokenType == t) + return &op; + return null; + } + +private: + + void require(Tok t) + { + if (lexer.peek().type != t) + error("Unexpexted token: Got '"~lexer.peek.getType~"' Expected '"~typeToString[t]~"'"); + lexer.next(); + } + + void error(char[] errMsg) + { + throw new Exception("Parser error: " ~errMsg); + } + + Lexer lexer; +} diff -r 000000000000 -r 2168f4cb73f1 sema/SymbolTable.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sema/SymbolTable.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,57 @@ +module sema.SymbolTable; + +import tango.io.Stdout; + +import lexer.Token; + +import ast.Exp : Identifier; + +class SymbolTable +{ +} + +class Scope +{ + this() {} + this(Scope enclosing) + { + this.enclosing = enclosing; + } + + Scope enclosing; + + Symbol add(Identifier id) + { + auto s = new Symbol; + s.id = id; + symbols[id] = s; + return s; + } + + Symbol find(Identifier id) + { + if (auto sym = id in symbols) + return *sym; + if (enclosing !is null) + return enclosing.find(id); + return null; + } + + char[][] names() + { + char[][] res; + foreach (id, sym; symbols) + res ~= sym.id.name ~ " : " ~ sym.type.name; + return res; + } + +private: + Symbol[Identifier] symbols; +} + +class Symbol +{ + Identifier id; + Identifier type; +} + diff -r 000000000000 -r 2168f4cb73f1 sema/SymbolTableBuilder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sema/SymbolTableBuilder.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,78 @@ +module sema.SymbolTableBuilder; + +import tango.io.Stdout; + +public import sema.SymbolTable; + +import sema.Visitor; + +class SymbolTableBuilder : Visitor!(void) +{ + this() + { + table ~= new Scope; + } + + override void visit(Decl[] decls) + { + foreach (decl; decls) + visitDecl(decl); + } + + override void visitDecl(Decl d) + { + d.env = current(); + super.visitDecl(d); + } + + override void visitStmt(Stmt s) + { + s.env = current(); + super.visitStmt(s); + } + + override void visitExp(Exp e) + { + e.env = current(); + super.visitExp(e); + } + + override void visitFuncDecl(FuncDecl d) + { + auto sym = current().add(d.identifier); + sym.type = d.type; + push(); + d.env = current(); + super.visitFuncDecl(d); + pop(); + } + + override void visitVarDecl(VarDecl d) + { + auto sc = current(); + auto sym = sc.add(d.identifier); + sym.type = d.type; + super.visitVarDecl(d); + } + +private: + Scope[] table; + + void push() + { + table ~= new Scope(current()); + } + + Scope pop() + { + auto res = table[$ - 1]; + table.length = table.length - 1; + return res; + } + + Scope current() + { + return table[$ - 1]; + } +} + diff -r 000000000000 -r 2168f4cb73f1 sema/Visitor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sema/Visitor.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,188 @@ +module sema.Visitor; + +import tango.io.Stdout; + +public +import ast.Decl, + ast.Stmt, + ast.Exp; + +import lexer.Token; + +class Visitor(FinalT = int, DeclT = FinalT, StmtT = DeclT, ExpT = StmtT) +{ +public: + FinalT visit(Decl[] decls) + { + foreach (decl; decls) + visitDecl(decl); + static if (is(FinalT == void)) + return; + else + return FinalT.init; + } + + DeclT visitDecl(Decl decl) + { + switch(decl.declType) + { + case DeclType.FuncDecl: + return visitFuncDecl(cast(FuncDecl)decl); + case DeclType.VarDecl: + return visitVarDecl(cast(VarDecl)decl); + default: + throw new Exception("Unknown declaration type"); + } + } + + StmtT visitStmt(Stmt stmt) + { + switch(stmt.stmtType) + { + case StmtType.Return: + return visitReturnStmt(cast(ReturnStmt)stmt); + case StmtType.Decl: + return visitDeclStmt(cast(DeclStmt)stmt); + case StmtType.Exp: + return visitExpStmt(cast(ExpStmt)stmt); + default: + throw new Exception("Unknown statement type"); + } + } + + ExpT visitExp(Exp exp) + { + switch(exp.expType) + { + case ExpType.Binary: + return visitBinaryExp(cast(BinaryExp)exp); + case ExpType.IntegerLit: + return visitIntegerLit(cast(IntegerLit)exp); + case ExpType.Negate: + return visitNegateExp(cast(NegateExp)exp); + case ExpType.AssignExp: + return visitAssignExp(cast(AssignExp)exp); + case ExpType.CallExp: + return visitCallExp(cast(CallExp)exp); + case ExpType.Identifier: + return visitIdentifier(cast(Identifier)exp); + default: + throw new Exception("Unknown expression type"); + } + } + + // Declarations: + DeclT visitVarDecl(VarDecl d) + { + visitExp(d.type); + visitExp(d.identifier); + if (d.init) + visitExp(d.init); + + static if (is(DeclT == void)) + return; + else + return DeclT.init; + } + + DeclT visitFuncDecl(FuncDecl f) + { + visitExp(f.type); + visitExp(f.identifier); + foreach (arg; f.funcArgs) + visitDecl(arg); + foreach (stmt; f.statements) + visitStmt(stmt); + + static if (is(DeclT == void)) + return; + else + return DeclT.init; + } + + // Statements: + StmtT visitReturnStmt(ReturnStmt s) + { + visitExp(s.exp); + static if (is(StmtT == void)) + return; + else + return StmtT.init; + } + + StmtT visitDeclStmt(DeclStmt d) + { + visitDecl(d.decl); + static if (is(StmtT == void)) + return; + else + return StmtT.init; + } + + StmtT visitExpStmt(ExpStmt s) + { + visitExp(s.exp); + static if (is(StmtT == void)) + return; + else + return StmtT.init; + } + + // Expressions: + ExpT visitAssignExp(AssignExp exp) + { + visitExp(exp.identifier); + visitExp(exp.exp); + static if (is(ExpT == void)) + return; + else + return ExpT.init; + } + + ExpT visitBinaryExp(BinaryExp exp) + { + visitExp(exp.left); + visitExp(exp.right); + static if (is(ExpT == void)) + return; + else + return ExpT.init; + } + + ExpT visitCallExp(CallExp exp) + { + visitExp(exp.exp); + foreach (arg; exp.args) + visitExp(arg); + static if (is(ExpT == void)) + return; + else + return ExpT.init; + } + + ExpT visitNegateExp(NegateExp exp) + { + visitExp(exp.exp); + static if (is(ExpT == void)) + return; + else + return ExpT.init; + } + + ExpT visitIntegerLit(IntegerLit exp) + { + static if (is(ExpT == void)) + return; + else + return ExpT.init; + } + + ExpT visitIdentifier(Identifier exp) + { + static if (is(ExpT == void)) + return; + else + return ExpT.init; + } +} + diff -r 000000000000 -r 2168f4cb73f1 test.td --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test.td Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,19 @@ + +int x = 4; + +byte main() +{ + long var1 = 1; + short var2 = 2; + return nice(var1, var2); +} + +int nice(long s, short t) +{ + byte x = 5 + t; + // int f(float z) { return -1 * z; } + t = 5 + 1 * 5 * s + t; + return 2 * (t + -1) - x; +} + + diff -r 000000000000 -r 2168f4cb73f1 tests/lexer/Comments.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/lexer/Comments.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,33 @@ +/* + + + /++ + +++++ + + + /++++ + + */ + +int i = 0; + /// lalalala + +/**** + */ + +/+ + +/+ + ++/ + +/+ + ++/ + ++/ + +/+ /+/ +fdsafasdf ++/ +/ + diff -r 000000000000 -r 2168f4cb73f1 tests/lexer/Comments1.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/lexer/Comments1.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,17 @@ +/* + + +*/ + +/*/ + +*/ + +/+ + ++/ + + +/+/ + ++/ diff -r 000000000000 -r 2168f4cb73f1 tests/run.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/run.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,69 @@ +module run.d; + +import tango.io.Stdout, + tango.io.FilePath, + tango.sys.Process; + + +char[] prog = "./Dang"; + +void main(char[][] args) +{ + auto cPath = FilePath("tests"); + + int succes, failure; + + foreach( path ; cPath.toList((FilePath path, bool isFolder){return isFolder;})) + { + Stdout(path.name)(":").newline; + foreach( p ; path.toList((FilePath path, bool isFolder) + { + if(path.ext == "d" && path.name[0] != '.') + return true; + return false; + })) + { + auto test = new Test(p); + bool result = test.run(); + if(result) + succes++; + else + failure++; + } + } + + Stdout().newline.newline() + ("Result:").newline() + (" - Succes: ")(succes).newline() + (" - Failure: ")(failure).newline; +} + +class Test +{ + FilePath target; + public this(FilePath target) + { + this.target = target; + } + + public bool run() + { + auto process = new Process(prog,target.path~target.file); + + Stdout(" - ")(target.file)(".. "); + + process.execute; + + auto result = process.wait; + if(result.status == 0) + { + Stdout("SUCCES").newline; + return true; + } + else + { + Stdout("FAILURE").newline; + return false; + } + } +} diff -r 000000000000 -r 2168f4cb73f1 tools/AstPrinter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/AstPrinter.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,164 @@ +module tools.AstPrinter; + +import tango.io.Stdout; + +import ast.Decl, + ast.Stmt, + ast.Exp; + +import misc.DataSource; + +class AstPrinter +{ + const char[] tabType = " "; // 4 spaces + + this(DataSource ds) + { + + this.ds = ds; + } + + void print(Decl[] decls) + { + foreach(decl ; decls) + { + printDecl(decl); + } + } + + void printDecl(Decl decl) + { + switch(decl.declType) + { + case DeclType.FuncDecl: + auto funcDecl = cast(FuncDecl)decl; + printBeginLine(); + printIdentifier(funcDecl.type); + printIdentifier(funcDecl.identifier); + printFuncArgs(funcDecl.funcArgs); + printOpenBrace(); + foreach(stmt ; funcDecl.statements) + printStatement(stmt); + printCloseBrace(); + break; + + case DeclType.VarDecl: + auto varDecl = cast(VarDecl)decl; + printBeginLine(); + printIdentifier(varDecl.type); + printIdentifier(varDecl.identifier); + if(varDecl.init) + { + print("= "); + printExp(varDecl.init); + } + printEndLine(";"); + break; + } + } + + void printStatement(Stmt stmt) + { + switch(stmt.stmtType) + { + case StmtType.Return: + auto ret = cast(ReturnStmt)stmt; + printBeginLine("return "); + printExp(ret.exp); + printEndLine(";"); + break; + case StmtType.Decl: + auto declStmt = cast(DeclStmt)stmt; + printDecl(declStmt.decl); + break; + case StmtType.Exp: + auto expStmt = cast(ExpStmt)stmt; + printBeginLine(); + printExp(expStmt.exp); + printEndLine(";"); + break; + + } + } + + void printExp(Exp exp) + { + switch(exp.expType) + { + case ExpType.Binary: + auto binaryExp = cast(BinaryExp)exp; + printExp(binaryExp.left); + print([binaryExp.op] ~ " "); + printExp(binaryExp.right); + break; + case ExpType.IntegerLit: + auto integetLit = cast(IntegerLit)exp; + auto t = integetLit.token; + print(t.get ~ " "); + break; + case ExpType.Negate: + auto negateExp = cast(NegateExp)exp; + print("-"); + printExp(negateExp.exp); + break; + case ExpType.AssignExp: + auto assignExp = cast(AssignExp)exp; + printIdentifier(assignExp.identifier); + print("= "); + printExp(assignExp.exp); + break; + } + + } + + void printFuncArgs(VarDecl[] decls) + { + print("("); + + foreach(i, decl; decls) + { + printIdentifier(decl.type); + printIdentifier(decl.identifier); + if(i+1 < decls.length) + print(","); + } + + printEndLine(")"); + } + + void printIdentifier(Identifier identifier) + { + auto t = identifier.token; + print(t.get ~ " "); + } + + void printOpenBrace() + { + printEndLine(tabIndex~"{"); + tabIndex ~= tabType; + } + + void printCloseBrace() + { + tabIndex = tabIndex[0 .. $-tabType.length]; + printEndLine(tabIndex~"}"); + } + + void printBeginLine(char[] line = "") + { + Stdout(tabIndex~line); + } + + void printEndLine(char[] line = "") + { + Stdout(line).newline; + } + + void print(char[] line) + { + Stdout(line); + } +private: + DataSource ds; + char[] tabIndex; +} diff -r 000000000000 -r 2168f4cb73f1 tools/DotPrinter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/DotPrinter.d Fri Apr 18 02:01:38 2008 +0200 @@ -0,0 +1,167 @@ +module tools.DotPrinter; + +import tango.io.Stdout, + Int = tango.text.convert.Integer; + +import ast.Decl, + ast.Stmt, + ast.Exp; + +import misc.DataSource, + lexer.Token; + +class DotPrinter +{ + this() + { + } + + private char[][void*] identifiers; + private int current_id = 0; + + void print(Decl[] decls) + { + Stdout("digraph {").newline; + foreach(decl ; decls) + { + printDecl(decl); + } + Stdout("}").newline; + } + + void printDecl(Decl decl) + { + switch(decl.declType) + { + case DeclType.FuncDecl: + FuncDecl funcDecl = cast(FuncDecl)decl; + + //printIdentifier(funcDecl.identifier); + //printFuncArgs(funcDecl.funcArgs); + Stdout(dotId(decl))(` [label="function`); + Stdout(`\n name: `)(text(funcDecl.identifier)); + Stdout(`\n return type: `)(text(funcDecl.type)); + Stdout(`", shape=box, fillcolor=lightblue, style=filled]`); + Stdout.newline; + //Stdout(`"`); + foreach(stmt ; funcDecl.statements) + printStatement(dotId(decl), stmt); + break; + + case DeclType.VarDecl: + VarDecl varDecl = cast(VarDecl)decl; + + //printIdentifier(funcDecl.identifier); + //printFuncArgs(funcDecl.funcArgs); + Stdout(dotId(decl))(` [label="var`); + Stdout(`\n name: `)(text(varDecl.identifier)); + Stdout(`\n type: `)(text(varDecl.type)); + Stdout(`"]`).newline; + + if (varDecl.init !is null) + printExpression(dotId(decl), varDecl.init); + break; + } + } + + void printStatement(char[] parent, Stmt stmt) + { + auto id = dotId(stmt); + switch (stmt.stmtType) + { + case StmtType.Stmt: + Stdout(id)(` [label="Statement"]`).newline; + Stdout(parent)(` -> `)(id).newline; + break; + + case StmtType.Decl: + Stdout(id)(` [label="Decl"]`).newline; + Stdout(parent)(` -> `)(id).newline; + auto decl = (cast(DeclStmt)stmt).decl; + printDecl(decl); + Stdout(id)(` -> `)(dotId(decl)).newline; + break; + + case StmtType.Return: + Stdout(id)(` [label="Return"]`).newline; + Stdout(parent)(` -> `)(id).newline; + printExpression(id, (cast(ReturnStmt)stmt).exp); + break; + + case StmtType.Exp: + Stdout(parent)(` -> `)(id).newline; + printExpression(id, (cast(ExpStmt)stmt).exp); + break; + } + } + + void printExpression(char[] parent, Exp exp) + { + auto id = dotId(exp); + + switch(exp.expType) + { + case ExpType.Binary: + auto bin = cast(BinaryExp)exp; + Stdout(id)(` [label="`)(bin.op)(`"]`).newline; + printExpression(id, bin.left); + printExpression(id, bin.right); + break; + + case ExpType.Negate: + auto neg = cast(NegateExp)exp; + Stdout(id)(` [label="Negate"]`).newline; + printExpression(id, neg.exp); + break; + + case ExpType.IntegerLit: + auto e = cast(IntegerLit)exp; + Stdout(id)(` [label="`)(text(e.token))(`"]`).newline; + break; + + case ExpType.Identifier: + auto e = cast(Identifier)exp; + Stdout(id)(` [label="`)(text(e))(`"]`).newline; + break; + + case ExpType.AssignExp: + auto ass = cast(AssignExp)exp; + Stdout(parent)(` [label="Assign"]`).newline; + Stdout(id)(` [label="`)(text(ass.identifier))(`"]`).newline; + printExpression(parent, ass.exp); + break; + + case ExpType.CallExp: + break; + } + Stdout(parent)(` -> `)(id).newline; + } + + char[] dotId(Decl d) { return dotId(cast(void*)d); } + char[] dotId(Stmt s) { return dotId(cast(void*)s); } + char[] dotId(Exp e) { return dotId(cast(void*)e); } + + char[] dotId(void* o) + { + auto id = o in identifiers; + if (id is null) + { + ++current_id; + identifiers[o] = Int.toString(current_id); + id = o in identifiers; + } + return *id; + } + + char[] text(Identifier identifier) + { + auto t = identifier.token; + return t.get; + } + char[] text(Token t) + { + return t.get; + } +private: + DataSource ds; +}