-
-
Notifications
You must be signed in to change notification settings - Fork 698
Expose D frontend via DUB #7425
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cbd0d27
b203430
84752b6
2f0b835
e88073d
bb0161d
711f365
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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(); | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,13 @@ | ||
| # Ignore all binary files (files without an extension) | ||
| # Start: Ignore everything | ||
| * | ||
| # Include directories | ||
| !*/ | ||
| # Include all files with extensions | ||
| !*.* | ||
|
|
||
| .dub | ||
| docs.json | ||
| __dummy.html | ||
| *.o | ||
| *.obj | ||
| /parser | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| DMD as a library | ||
| ================ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally I prefer the hash
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I usually use a mix of these styles in my markdown:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it's not that important. That's why I said it's my personal preference. |
||
|
|
||
| The tests in this module are high-level and mostly indented to make sure all | ||
| necessary modules are included in the Dub package. | ||
|
|
||
| The tests are executable single-file packages for the D's package manager `dub` | ||
| and can be directly executed: | ||
|
|
||
| ```bash | ||
| ./lexer.d | ||
| ``` | ||
|
|
||
| If you want to see the log output or want to pass additional options, use `--single`: | ||
|
|
||
| ```bash | ||
| dub --single -v lexer.d | ||
| ``` | ||
|
|
||
| If you don't have `dub` installed on your system, | ||
| [install an official release](https://dlang.org/download.html). | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,162 @@ | ||
| #!/usr/bin/env dub | ||
| /+dub.sdl: | ||
| dependency "dmd" path="../.." | ||
| +/ | ||
| import std.stdio; | ||
|
|
||
| // add import paths | ||
| void addImports(T)(T path) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why isn't this in frontend.d? (src/dmd/frontend.d) |
||
| { | ||
| 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() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why isn't this in frontend.d ? |
||
| { | ||
| 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() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should certainly be in frontend.d and we could call it
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unless there's a reason to not run the full semantic analysis I don't think the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure this is possible. As a tool developer you may want to visit the AST while it is in a certain state. Given the fact that the semantic methods modify the AST quite a lot you might want to take a look at it before a certain semantic pass is done. For example: enums are evaluated at compile time (during semantic3) ; in order to do those modifications the semantic3 stage alters the AST; as a tool developer I may want to do something with the original AST which is not longer possible given that semantic3 has modified it.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hehe that's exactly why I didn't want to push this into
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's not a problem. You have the individual semantic passes, but if you don't care about that you can just call
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please let leave it as is and continue improve it after this is merged. |
||
| { | ||
| 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); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| #!/usr/bin/env dub | ||
| /+dub.sdl: | ||
| dependency "dmd" path="../.." | ||
| +/ | ||
| void main() | ||
| { | ||
| import dmd.lexer; | ||
| import dmd.tokens; | ||
|
|
||
| immutable expected = [ | ||
| TOKvoid, | ||
| TOKidentifier, | ||
| TOKlparen, | ||
| TOKrparen, | ||
| TOKlcurly, | ||
| TOKrcurly | ||
| ]; | ||
|
|
||
| immutable sourceCode = "void test() {} // foobar"; | ||
| scope lexer = new Lexer("test", sourceCode.ptr, 0, sourceCode.length, 0, 0); | ||
| lexer.nextToken; | ||
|
|
||
| TOK[] result; | ||
|
|
||
| do | ||
| { | ||
| result ~= lexer.token.value; | ||
| } while (lexer.nextToken != TOKeof); | ||
|
|
||
| assert(result == expected); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As hopefully more examples will be added, ignoring all binary files in this directory avoids frequent updates of this file.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a cleaner solution would be put all dub artifacts in
generated/as well, usingtargetPath(see http://code.dlang.org/package-format?lang=sdl).