diff --git a/dub.sdl b/dub.sdl index 2eac444644..f70cc98bae 100644 --- a/dub.sdl +++ b/dub.sdl @@ -100,6 +100,7 @@ subPackage { "src/dmd/escape.d" \ "src/dmd/expression.d" \ "src/dmd/expressionsem.d" \ + "src/dmd/frontend.d" \ "src/dmd/func.d" \ "src/dmd/gluelayer.d" \ "src/dmd/hdrgen.d" \ diff --git a/src/dmd/frontend.d b/src/dmd/frontend.d new file mode 100644 index 0000000000..581f8dd9e5 --- /dev/null +++ b/src/dmd/frontend.d @@ -0,0 +1,56 @@ +/** + * Compiler implementation of the + * $(LINK2 http://www.dlang.org, D programming language). + * + * This module contains high-level interfaces for interacting + with DMD as a library. + * + * Copyright: Copyright (c) 1999-2017 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/id.d, _id.d) + */ +module dmd.frontend; + +// Online documentation: https://dlang.org/phobos/dmd_frontend.html + +/** +Initializes the DMD compiler +*/ +void initDMD() +{ + import dmd.dmodule : Module; + import dmd.globals : global; + import dmd.id : Id; + import dmd.mtype : Type; + import dmd.target : Target; + import dmd.expression : Expression; + import dmd.objc : Objc; + import dmd.builtin : builtin_init; + + global._init; + + version(linux) + global.params.isLinux = 1; + else version(OSX) + global.params.isOSX = 1; + else version(FreeBSD) + global.params.isFreeBSD = 1; + else version(Windows) + global.params.isWindows = 1; + else version(Solaris) + global.params.isSolaris = 1; + else version(OpenBSD) + global.params.isOpenBSD = 1; + else + static assert(0, "OS not supported yet."); + + Type._init(); + Id.initialize(); + Module._init(); + Target._init(); + Expression._init(); + Objc._init(); + builtin_init(); +} + diff --git a/test/dub_package/frontend.d b/test/dub_package/frontend.d new file mode 100755 index 0000000000..2f8e26e318 --- /dev/null +++ b/test/dub_package/frontend.d @@ -0,0 +1,162 @@ +#!/usr/bin/env dub +/+dub.sdl: +dependency "dmd" path="../.." ++/ +import std.stdio; + +// add import paths +void addImports(T)(T path) +{ + import dmd.globals : global; + import dmd.arraytypes : Strings; + + stderr.writefln("addImport: %s", path); + + Strings* res = new Strings(); + foreach (p; path) + { + import std.string : toStringz; + Strings* a = new Strings(); + a.push(p.toStringz); + res.append(a); + } + global.path = res; +} + +// finds a dmd.conf and parses it for import paths +auto findImportPaths() +{ + import std.file : exists, getcwd; + import std.string : fromStringz, toStringz; + import std.path : buildPath, buildNormalizedPath, dirName; + import std.process : env = environment, execute; + import dmd.dinifile : findConfFile; + import dmd.errors : fatal; + import std.algorithm, std.range, std.regex; + + auto dmdEnv = env.get("DMD", "dmd"); + auto whichDMD = execute(["which", dmdEnv]); + if (whichDMD.status != 0) + { + stderr.writeln("Can't find DMD."); + fatal; + } + + immutable dmdFilePath = whichDMD.output; + string iniFile; + + if (dmdEnv.canFind("ldmd")) + { + immutable ldcConfig = "ldc2.conf"; + immutable binDir = dmdFilePath.dirName; + // https://wiki.dlang.org/Using_LDC + auto ldcConfigs = [ + getcwd.buildPath(ldcConfig), + binDir.buildPath(ldcConfig), + binDir.dirName.buildPath("etc", ldcConfig), + "~/.ldc".buildPath(ldcConfig), + binDir.buildPath("etc", ldcConfig), + binDir.buildPath("etc", "ldc", ldcConfig), + "/etc".buildPath(ldcConfig), + "/etc/ldc".buildPath(ldcConfig), + ].filter!exists; + assert(!ldcConfigs.empty, "No ldc2.conf found"); + iniFile = ldcConfigs.front; + } + else + { + auto f = findConfFile(dmdFilePath.toStringz, "dmd.conf"); + iniFile = f.fromStringz.idup; + assert(iniFile.exists, "No dmd.conf found."); + } + + return File(iniFile, "r") + .byLineCopy + .map!(l => l.matchAll(`-I[^ "]+`.regex) + .joiner + .map!(a => a.drop(2) + .replace("%@P%", dmdFilePath.dirName) + .replace("%%ldcbinarypath%%", dmdFilePath.dirName))) + .joiner + .array + .sort + .uniq + .map!buildNormalizedPath; +} + +// test frontend +void main() +{ + import dmd.astcodegen : ASTCodegen; + import dmd.dmodule : Module; + import dmd.globals : global, Loc; + import dmd.frontend : initDMD; + import dmd.parse : Parser; + import dmd.statement : Identifier; + import dmd.tokens : TOKeof; + import dmd.id : Id; + + initDMD; + findImportPaths.addImports; + + auto parse(Module m, string code) + { + scope p = new Parser!ASTCodegen(m, code, false); + p.nextToken; // skip the initial token + auto members = p.parseModule; + assert(!p.errors, "Parsing error occurred."); + return members; + } + + Identifier id = Identifier.idPool("test"); + auto m = new Module("test.d", id, 0, 0); + m.members = parse(m, q{ + void foo() + { + foreach (i; 0..10) {} + } + }); + + void semantic() + { + import dmd.dsymbolsem : dsymbolSemantic; + import dmd.semantic : semantic2, semantic3; + + m.importedFrom = m; + m.importAll(null); + m.dsymbolSemantic(null); + m.semantic2(null); + m.semantic3(null); + } + semantic(); + + auto prettyPrint() + { + import dmd.root.outbuffer: OutBuffer; + import dmd.hdrgen : HdrGenState, PrettyPrintVisitor; + + OutBuffer buf = { doindent: 1 }; + HdrGenState hgs = { fullDump: 1 }; + scope PrettyPrintVisitor ppv = new PrettyPrintVisitor(&buf, &hgs); + m.accept(ppv); + return buf; + } + auto buf = prettyPrint(); + + auto expected =q{import object; +void foo() +{ + { + int __key3 = 0; + int __limit4 = 10; + for (; __key3 < __limit4; __key3 += 1) + { + int i = __key3; + } + } +} +}; + import std.string : replace, fromStringz; + auto generated = buf.extractData.fromStringz.replace("\t", " "); + assert(expected == generated, generated); +}