Index: .poseidon ================================================================== --- .poseidon +++ .poseidon @@ -44,13 +44,14 @@ build.bat build.sh + builddoc.bat readme.txt ADDED builddoc.bat Index: builddoc.bat ================================================================== --- builddoc.bat +++ builddoc.bat @@ -0,0 +1,6 @@ +@setlocal ENABLEDELAYEDEXPANSION +@set ARGS= +@for %%I in (main.d polemy\*.d tricks\*.d) do @set ARGS=!ARGS! %%I +@if not exist bin mkdir bin +@echo dmd -o- -Dddoc doc\candydoc\candy.ddoc doc\candydoc\modules.ddoc %ARGS% +@dmd -o- -Dddoc doc\candydoc\candy.ddoc doc\candydoc\modules.ddoc %ARGS% ADDED doc/candydoc/CANDYDOC.txt Index: doc/candydoc/CANDYDOC.txt ================================================================== --- doc/candydoc/CANDYDOC.txt +++ doc/candydoc/CANDYDOC.txt @@ -0,0 +1,55 @@ + +CanDyDOC is fileset for creating advanced documentation of programs written +in D programming language. CanDyDOC adds some eye-candy and advanced navigation +features to .html documents that are generated by D compiler and known as DDOC. +Produced result is AJAX web-application that is compatible with all mainstream +web browsers. + +This is a version 0.80. For latest release see +http://trac.dsource.org/projects/helix/ + +CanDyDOC includes following files: + - candy.ddoc + File with DDOC macro definitions. You haven't to touch it. + + - modules.ddoc + You should enumerate all modules that would be avaible for navigation + here. + + - style.css + Cascading style sheet file that defines look of produced documentation. + You can leave this file without changes or adjust fonts, colors, etc + here. See it for documentation. + + - ie56hack.css + CSS file to force Internet Explorer 5/6 browser show documentation + as it looks like in standard-compliant browsers. + + - tree.js + JavaScript implementing tree control that looks like native one. + + - util.js + Common cross-browser routines. + + - explorer.js + Heart of every documentation's page. Controls generation, behaviour and + navigation of a page. + + - numerous of image files in 'img' folder. + +How to use: + 1) Put 'candydoc' directory in place where documentation will be. + 2) Modify modules.ddoc file: enumerate all modules that should be avaible + for navigation. + 3) Modify style.css file if you want to change style of documentation. Or + leave it unmodified to apply defaul theme. + 4) Run documentation compilation with candy.ddoc and modules.ddoc specified + on command line. + 5) Enjoy a result :) + +Known bugs: + - Explorer window doesn't work on Safari browser. + - Scroll bar positions are not adjusted after explorer's tab change in Opera + browser. So it is posible to see nothing on some tab: solution is to + return to a previous tab, scroll it to top and then return back. + - Overlapping of some elements when too few horizontal place avaible. ADDED doc/candydoc/candy.ddoc Index: doc/candydoc/candy.ddoc ================================================================== --- doc/candydoc/candy.ddoc +++ doc/candydoc/candy.ddoc @@ -0,0 +1,50 @@ +DDOC = + + + +$(TITLE) + + + + + + +
+
+ + + +

$(TITLE)

$(BODY)
+ Page was generated with + + on $(DATETIME) +
+
+$(ADD_MODULES) + + + +DDOC_DECL = + +$(DT $0) + + + +DDOC_PSYMBOL = +$0 + + + +DDOC_MEMBERS = + +$(DL $0) + + + +DDOC_PARAM_ID = +$0 + + +DDOC_PARAM =$0 +ADD_MODULES = +MODULE =explorer.packageExplorer.addModule("$0"); ADDED doc/candydoc/explorer.js Index: doc/candydoc/explorer.js ================================================================== --- doc/candydoc/explorer.js +++ doc/candydoc/explorer.js @@ -0,0 +1,310 @@ +/* This file is a part of CanDyDOC fileset. + File is written by Victor Nakoryakov and placed into the public domain. + + This file is javascript with classes that represents explorer window. + And things related to navigation. */ + +var explorer = new Explorer(); + +/////////////////////////////////////////////////////////////////////////////// +// Current symbol marker class constructor +/////////////////////////////////////////////////////////////////////////////// +function Marker() +{ + this.top = document.createElement("div"); + this.middle = document.createElement("div"); + this.bottom = document.createElement("div"); + this.container = document.createElement("div"); + + this.setTo = function(term) + { + // find definition related to `term` + var def = term.nextSibling; + while (def && def.nodeName != "DD") + def = def.nextSibling; + + var defHeight = 0; + var childrenHeight = 0; // children of current declaration + if (def) + { + defHeight = def.offsetHeight; + var child = def.firstChild; + + // traverse until DL tag, until children definition + while (child && child.nodeName != "DL") + child = child.nextSibling; + + if (child) + childrenHeight = child.offsetHeight; + } + + this.top.style.height = term.offsetHeight; + this.middle.style.height = defHeight - childrenHeight; + this.bottom.style.height = childrenHeight; + + if (childrenHeight == 0) + this.bottom.style.display = "none"; + else + this.bottom.style.display = ""; + + this.container.style.left = getLeft(term) - 8; + this.container.style.top = getTop(term); + this.container.style.display = ""; + } + + /////////////////////////////////////////////////////////////////////////// + this.container.style.position = "absolute"; + this.container.style.display = "none"; + + this.top.className = "markertop"; + this.middle.className = "markermiddle"; + this.bottom.className = "markerbottom"; + + this.container.appendChild(this.top); + this.container.appendChild(this.middle); + this.container.appendChild(this.bottom); + + //document.body.appendChild( this.container ); + + // Workaround bug in IE 5/6. We can not append anything to document body until + // full page load. + window.marker = this; + if (window.addEventListener) + window.addEventListener("load", new Function("document.body.appendChild( window.marker.container );"), false); + else if (window.attachEvent) + window.attachEvent("onload", new Function("document.body.appendChild( window.marker.container );")); +} + +/////////////////////////////////////////////////////////////////////////////// +// Outline class constructor +/////////////////////////////////////////////////////////////////////////////// +function Outline() +{ + this.tree = new TreeView(); + this.mountPoint = null; + this.writeEnabled = false; + this.marker = new Marker(); + this.classRegExp = new RegExp; + this.structRegExp = new RegExp; + this.enumRegExp = new RegExp; + this.templateRegExp = new RegExp; + this.aliasRegExp = new RegExp; + this.funcRegExp = new RegExp; + + this.incSymbolLevel = function() + { + if (this.mountPoint == null) + this.mountPoint = this.tree.children[ 0 ]; + else + this.mountPoint = this.mountPoint.lastChild(); + } + + this.decSymbolLevel = function() + { + // place icons near items according to extracted below type + for (var i = 0; i < this.mountPoint.children.length; ++i) + { + child = this.mountPoint.children[i]; + var term = child.termRef; + + // find first span node + var n = term.firstChild; + while (n && n.nodeName != "SPAN") + n = n.nextSibling; + + if (!n) // shouldn't happen + continue; + + var iconSrc; + if (n.firstChild.nodeName == "#text") + { + var text = n.firstChild.data; // text before declaration + + if ( this.classRegExp.test(text) ) + iconSrc = "candydoc/img/outline/class.gif"; + else if ( this.structRegExp.test(text) ) + iconSrc = "candydoc/img/outline/struct.gif"; + else if ( this.enumRegExp.test(text) ) + iconSrc = "candydoc/img/outline/enum.gif"; + else if ( this.templateRegExp.test(text) ) + iconSrc = "candydoc/img/outline/template.gif"; + else if ( this.aliasRegExp.test(text) ) + iconSrc = "candydoc/img/outline/alias.gif"; + else // function or variable? check whether '(' ')' exists on the right + { + var np = n.firstChild; + while (np && np.nodeName != "SCRIPT") // find our script "onDecl" + np = np.nextSibling; + + if (np && np.nextSibling && np.nextSibling.nodeName == "#text" && + this.funcRegExp.test(np.nextSibling.data)) + { + iconSrc = "candydoc/img/outline/func.gif"; + } + else + iconSrc = "candydoc/img/outline/var.gif"; + } + } + else // enum member ? + iconSrc = "candydoc/img/outline/var.gif"; + + child.icon.src = iconSrc; + child.icon.width = 16; + child.icon.height = 16; + } + + this.mountPoint = this.mountPoint.parentNode; + } + + this.addDecl = function(decl) + { + function getLastLeaf(elem) + { + if (elem.childNodes.length > 0) + return getLastLeaf(elem.lastChild); + else + return elem; + } + + function getCurrentTerm() + { + var ret = getLastLeaf( document.getElementById("content") ); + while (ret && ret.nodeName != "DT") + ret = ret.parentNode; + + return ret; + } + + if (this.writeEnabled) + { + var node = this.mountPoint.createChild(decl); + node.termRef = getCurrentTerm(); + node.setOnclick( new Function("explorer.outline.mark(this.termRef);") ); + } + } + + this.mark = function(term) + { + this.marker.setTo(term); + window.scrollTo(0, getTop(term) - getWindowHeight() / 6); + } + + + this.classRegExp.compile("(.*\b)?class(\b.*)?"); + this.structRegExp.compile("(.*\b)?struct(\b.*)?"); + this.enumRegExp.compile("(.*\b)?enum(\b.*)?"); + this.templateRegExp.compile("(.*\b)?template(\b.*)?"); + this.aliasRegExp.compile("(.*\b)?alias(\b.*)?"); + this.funcRegExp.compile(/.*\(.*/); +} + + + + +/////////////////////////////////////////////////////////////////////////////// +// Package explorer class constructor +/////////////////////////////////////////////////////////////////////////////// +function PackageExplorer() +{ + this.tree = new TreeView(true); + + this.addModule = function(mod) + { + var moduleIco = "candydoc/img/outline/module.gif"; + var packageIco = "candydoc/img/outline/package.gif"; + + var path = mod.split("\."); + var node = this.tree.branch(path[0]); + if ( !node ) + { + node = this.tree.createBranch(path[0], (path.length == 1) ? moduleIco : packageIco); + // modified by k.inaba : link for toplevel module + if (path.length == 1) + node.setRef(path[0] + ".html"); + } + + for (var i = 1; i < path.length; ++i) + { + var prev = node; + node = node.child(path[i]); + if (!node) + node = prev.createChild(path[i], (path.length == i + 1) ? moduleIco : packageIco); + + if (path.length == i + 1) + node.setRef(path[i] + ".html"); + } + } +} + + + +/////////////////////////////////////////////////////////////////////////////// +// Explorer class constructor +/////////////////////////////////////////////////////////////////////////////// +function Explorer() +{ + this.outline = new Outline(); + this.packageExplorer = new PackageExplorer(); + this.tabs = new Array(); + this.tabCount = 0; + + this.initialize = function(moduleName) + { + this.tabArea = document.getElementById("tabarea"); + this.clientArea = document.getElementById("explorerclient"); + + // prevent text selection + this.tabArea.onmousedown = new Function("return false;"); + this.tabArea.onclick = new Function("return true;"); + this.tabArea.onselectstart = new Function("return false;"); + this.clientArea.onmousedown = new Function("return false;"); + this.clientArea.onclick = new Function("return true;"); + this.clientArea.onselectstart = new Function("return false;"); + + this.outline.tree.createBranch( moduleName, "candydoc/img/outline/module.gif" ); + + // create tabs + this.createTab("Outline", this.outline.tree.domEntry); + this.createTab("Package", this.packageExplorer.tree.domEntry); + } + + this.createTab = function(name, domEntry) + { + var tab = new Object(); + this.tabs[name] = tab; + this.tabCount++; + + tab.domEntry = domEntry; + tab.labelSpan = document.createElement("span"); + + if (this.tabCount > 1) + { + tab.labelSpan.className = "inactivetab"; + tab.domEntry.style.display = "none"; + } + else + { + tab.labelSpan.className = "activetab"; + tab.domEntry.style.display = ""; + } + + tab.labelSpan.appendChild( document.createTextNode(name) ); + tab.labelSpan.owner = this; + tab.labelSpan.onclick = new Function("this.owner.setSelection('" + name + "');"); + + this.tabArea.appendChild( tab.labelSpan ); + this.clientArea.appendChild( domEntry ); + } + + this.setSelection = function(tabName) + { + for (name in this.tabs) + { + this.tabs[name].labelSpan.className = "inactivetab"; + this.tabs[name].domEntry.style.display = "none"; + } + + this.tabs[tabName].labelSpan.className = "activetab"; + this.tabs[tabName].domEntry.style.display = ""; + } +} ADDED doc/candydoc/ie56hack.css Index: doc/candydoc/ie56hack.css ================================================================== --- doc/candydoc/ie56hack.css +++ doc/candydoc/ie56hack.css @@ -0,0 +1,21 @@ +/* This file is a part of CanDyDOC fileset. + File is written by Victor Nakoryakov and placed into the public domain. + + This file is CSS to work around IE6 and earlier bugs. It's included just + in these browsers. */ + + +/* Some magic to emulate unsupported "position: fixed" style. */ +#tabarea +{ + _position: absolute; + _top: expression(eval(document.body.scrollTop+8)); +} + +/* ditto */ +#explorerclient +{ + _position: absolute; + _top: expression(eval(document.body.scrollTop+24)); + _height: expression(eval(document.body.clientHeight-48)); +} ADDED doc/candydoc/img/bg.gif Index: doc/candydoc/img/bg.gif ================================================================== --- doc/candydoc/img/bg.gif +++ doc/candydoc/img/bg.gif cannot compute difference between binary files ADDED doc/candydoc/img/candydoc.gif Index: doc/candydoc/img/candydoc.gif ================================================================== --- doc/candydoc/img/candydoc.gif +++ doc/candydoc/img/candydoc.gif cannot compute difference between binary files ADDED doc/candydoc/img/outline/alias.gif Index: doc/candydoc/img/outline/alias.gif ================================================================== --- doc/candydoc/img/outline/alias.gif +++ doc/candydoc/img/outline/alias.gif cannot compute difference between binary files ADDED doc/candydoc/img/outline/bg.gif Index: doc/candydoc/img/outline/bg.gif ================================================================== --- doc/candydoc/img/outline/bg.gif +++ doc/candydoc/img/outline/bg.gif cannot compute difference between binary files ADDED doc/candydoc/img/outline/class.gif Index: doc/candydoc/img/outline/class.gif ================================================================== --- doc/candydoc/img/outline/class.gif +++ doc/candydoc/img/outline/class.gif cannot compute difference between binary files ADDED doc/candydoc/img/outline/enum.gif Index: doc/candydoc/img/outline/enum.gif ================================================================== --- doc/candydoc/img/outline/enum.gif +++ doc/candydoc/img/outline/enum.gif cannot compute difference between binary files ADDED doc/candydoc/img/outline/func.gif Index: doc/candydoc/img/outline/func.gif ================================================================== --- doc/candydoc/img/outline/func.gif +++ doc/candydoc/img/outline/func.gif cannot compute difference between binary files ADDED doc/candydoc/img/outline/module.gif Index: doc/candydoc/img/outline/module.gif ================================================================== --- doc/candydoc/img/outline/module.gif +++ doc/candydoc/img/outline/module.gif cannot compute difference between binary files ADDED doc/candydoc/img/outline/package.gif Index: doc/candydoc/img/outline/package.gif ================================================================== --- doc/candydoc/img/outline/package.gif +++ doc/candydoc/img/outline/package.gif cannot compute difference between binary files ADDED doc/candydoc/img/outline/struct.gif Index: doc/candydoc/img/outline/struct.gif ================================================================== --- doc/candydoc/img/outline/struct.gif +++ doc/candydoc/img/outline/struct.gif cannot compute difference between binary files ADDED doc/candydoc/img/outline/template.gif Index: doc/candydoc/img/outline/template.gif ================================================================== --- doc/candydoc/img/outline/template.gif +++ doc/candydoc/img/outline/template.gif cannot compute difference between binary files ADDED doc/candydoc/img/outline/var.gif Index: doc/candydoc/img/outline/var.gif ================================================================== --- doc/candydoc/img/outline/var.gif +++ doc/candydoc/img/outline/var.gif cannot compute difference between binary files ADDED doc/candydoc/img/package/bg.gif Index: doc/candydoc/img/package/bg.gif ================================================================== --- doc/candydoc/img/package/bg.gif +++ doc/candydoc/img/package/bg.gif cannot compute difference between binary files ADDED doc/candydoc/img/tree/shim.gif Index: doc/candydoc/img/tree/shim.gif ================================================================== --- doc/candydoc/img/tree/shim.gif +++ doc/candydoc/img/tree/shim.gif cannot compute difference between binary files ADDED doc/candydoc/img/tree/tb.gif Index: doc/candydoc/img/tree/tb.gif ================================================================== --- doc/candydoc/img/tree/tb.gif +++ doc/candydoc/img/tree/tb.gif cannot compute difference between binary files ADDED doc/candydoc/img/tree/tbr.gif Index: doc/candydoc/img/tree/tbr.gif ================================================================== --- doc/candydoc/img/tree/tbr.gif +++ doc/candydoc/img/tree/tbr.gif cannot compute difference between binary files ADDED doc/candydoc/img/tree/tbrm.gif Index: doc/candydoc/img/tree/tbrm.gif ================================================================== --- doc/candydoc/img/tree/tbrm.gif +++ doc/candydoc/img/tree/tbrm.gif cannot compute difference between binary files ADDED doc/candydoc/img/tree/tbrp.gif Index: doc/candydoc/img/tree/tbrp.gif ================================================================== --- doc/candydoc/img/tree/tbrp.gif +++ doc/candydoc/img/tree/tbrp.gif cannot compute difference between binary files ADDED doc/candydoc/img/tree/tr.gif Index: doc/candydoc/img/tree/tr.gif ================================================================== --- doc/candydoc/img/tree/tr.gif +++ doc/candydoc/img/tree/tr.gif cannot compute difference between binary files ADDED doc/candydoc/img/tree/trm.gif Index: doc/candydoc/img/tree/trm.gif ================================================================== --- doc/candydoc/img/tree/trm.gif +++ doc/candydoc/img/tree/trm.gif cannot compute difference between binary files ADDED doc/candydoc/img/tree/trp.gif Index: doc/candydoc/img/tree/trp.gif ================================================================== --- doc/candydoc/img/tree/trp.gif +++ doc/candydoc/img/tree/trp.gif cannot compute difference between binary files ADDED doc/candydoc/modules.ddoc Index: doc/candydoc/modules.ddoc ================================================================== --- doc/candydoc/modules.ddoc +++ doc/candydoc/modules.ddoc @@ -0,0 +1,10 @@ +MODULES = + $(MODULE main) + $(MODULE tricks.tricks) + $(MODULE tricks.test) + $(MODULE polemy._common) + $(MODULE polemy.lex) + $(MODULE polemy.parse) + $(MODULE polemy.ast) + $(MODULE polemy.eval) + $(MODULE polemy.value) ADDED doc/candydoc/style.css Index: doc/candydoc/style.css ================================================================== --- doc/candydoc/style.css +++ doc/candydoc/style.css @@ -0,0 +1,169 @@ +/* This file is a part of CanDyDOC fileset. + File is written by Victor Nakoryakov and placed into the public domain. + + This file is main CSS file of CanDyDOC. You may adjust some part of + parameters to control how result documentation would looks like. See + further documentation for details. */ + + + +/* This controls how background would looks like and + sets some document-scope defaults. */ +body +{ + /* These parameters control default font. */ + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 10pt; + color: #666666; + + /* These control look of background. Note that you have to use + fixed background to keep documentation good-looking in + IE6 and earlier. Otherwise whole *explorer* will jerk while + scrolling. If you do not want to use background at all use + some invalid url, e.g. url(foo). */ + background-color: #e6fcea; + background: url(img/bg.gif) fixed; + + /* Don't touch. Necessary for IE6 and earlier. */ + height: 100%; +} + + + +/* Style applied to all tables. Actualy there are two: one table is + that contains contant and footer with CanDyDOC logo, and others + are that contains functions' parameters description. */ +table +{ + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 10pt; + color: #666666; + text-align: justify; +} + + +/* Style used for all hyperlinks. */ +a:link { color: #009900; text-decoration: none } +a:visited { color: #009999; text-decoration: none } +a:hover { color: #0033cc; text-decoration: none } +a:active { color: #0033cc; text-decoration: none } + +/* +table.matrix +{ + border-left: double 3px #666666; + border-right: double 3px #666666; + margin-left: 3em; +} +*/ + +/* Style appled to declarations. E.g. 'void foo(int a, float b);' */ +span.decl { font-size: 10pt; font-weight: bold; color: #000000; text-align: left } +/* Style appled to current declaration's symbol. E.g. 'foo' in 'void foo(int a, float b);' */ +span.currsymbol { font-size: 12pt; color: #009900 } +/* Style appled to function's parameters. E.g. 'a' and 'b' in 'void foo(int a, float b);' */ +span.funcparam { font-style: italic; font-weight: normal; color: #331200 } + +/* Style for div that actualy contains documenation. */ +#content +{ + padding-right: 8px; + position: absolute; + left: 245px; + top: 8px; + text-align: justify; +} + +/* Style for table that is inside div considered above. Contains documentaton + itself and footer with CanDyDOC logo. */ +table.content +{ + margin-bottom: 8px; + border-spacing: 0px; + border-collapse: collapse; + background-color: #ffffff; +} + +/* Style for cell of above considered table that contains documentation itself. */ +#docbody +{ + padding: 8px 20px 8px 20px; + border: solid 1px #009900; +} + +/* Style for cell that contains CanDyDOC logo. */ +#docfooter +{ + height: 16px; + background-color: #ddeedd; + padding: 0px 8px 0px 8px; + border: solid 1px #009900; +} + +/* Style applied to currently active tab of explorer window. */ +span.activetab +{ + background-color: #0033cc; + border-top: solid 2px #009900; + color: #ffffff; + font-weight: bold; + padding-left: 4px; + padding-right: 4px; + padding-top: 1px; + margin-right: 1px; +} + +/* Style applied to currently inactive tab of explorer window. */ +span.inactivetab +{ + background-color: #000066; + color: #cccccc; + font-weight: normal; + padding-left: 4px; + padding-right: 4px; + padding-top: 0px; + margin-right: 1px; +} + +/* Style applied to div that contains tabs of explorer. Note that if + you want to change it's position you have to change position of + #explorerclient, #content and corresponding values in ie56hack.css */ +#tabarea +{ + position: fixed; + top: 8px; + width: 205px; + height: 16px; + cursor: default; +} + + +/* Style applied to div that contains tree in explorer. Note that if + you want to change it's position you have to change position of + #tabarea, #content and corresponding values in ie56hack.css */ +#explorerclient +{ + position: fixed; + top: 24px; + bottom: 8px; + width: 205px; + overflow: auto; + background-color: #fcfffc; + border: solid 2px #0033cc; + padding: 4px; + cursor: default; + color: Black; +} + +/* Following 3 styles control appearance of marker that appears + if you click some entity in outline window. */ +div.markertop { border-left: solid 2px #0033cc;} +div.markermiddle{ border-left: dotted 2px #0033cc;} +div.markerbottom{ border-left: dotted 2px #66cc66;} + +/* Style applied to preformated text used to show examples. */ +pre.d_code +{ + border: dotted 1px #9c9; + background-color: #eeffee; +} ADDED doc/candydoc/tree.js Index: doc/candydoc/tree.js ================================================================== --- doc/candydoc/tree.js +++ doc/candydoc/tree.js @@ -0,0 +1,374 @@ +/* This file is a part of CanDyDOC fileset. + File is written by Victor Nakoryakov and placed into the public domain. + + This file is javascript with classes that represents native style tree control. */ + +var pmNone = 0; +var pmPlus = 1; +var pmMinus = 2; + +var hlNone = 0; +var hlGrey = 1; +var hlSelected = 2; + +function TreeView(hrefMode) +{ + this.domEntry = document.createElement("div"); + this.children = new Array(); + this.selection = null; + this.hrefMode = hrefMode; + + this.createBranch = function(text, iconSrc) + { + var root = new TreeNode(text, iconSrc, this.hrefMode); + root.owner = this; + this.children[ this.children.length ] = root; + this.domEntry.appendChild( root.domEntry ); + return root; + } + + this.branch = function(text) + { + var ret = null; + for (var i = 0; i < this.children.length; ++i) + if (this.children[i].textElement.data == text) + { + ret = this.children[i]; + break; + } + + return ret; + } + + this.domEntry.style.fontSize = "10px"; + this.domEntry.style.cursor = "default"; + this.domEntry.style.whiteSpace = "nowrap"; +} + +var idCounter = 0; +function TreeNode(text, iconSrc, hrefMode) +{ + this.id = idCounter++; + this.parentNode = null; + this.children = new Array(); + this.domEntry = document.createElement("div"); + this.icon = document.createElement("img"); + this.textElement = document.createTextNode(text); + this.textSpan = document.createElement("span"); + this.lineDiv = document.createElement("div"); + this.hierarchyImgs = new Array(); + this.onclick = null; + + function createIcon() + { + var img = document.createElement("img"); + img.style.verticalAlign = "middle"; + img.style.position = "relative"; + img.style.top = "-1px"; + img.width = 16; + img.height = 16; + return img; + } + + function createHierarchyImage() + { + var img = createIcon(); + img.pointsTop = false; + img.pointsBottom = false; + img.pointsRight = false; + img.pmState = pmNone; + return img; + } + + function genHierarchyImageSrc(hierarchyImg) + { + var name = ""; + if (hierarchyImg.pointsTop) + name += "t"; + + if (hierarchyImg.pointsBottom) + name += "b"; + + if (hierarchyImg.pointsRight) + name += "r"; + + if (hierarchyImg.pmState == pmPlus) + name += "p"; + else if (hierarchyImg.pmState == pmMinus) + name += "m"; + + if (name == "") + name = "shim"; + + return "candydoc/img/tree/" + name + ".gif"; + } + + function setSrc(icon, src) + { + icon.src = src; + // After src change width and height are reseted in IE. + // Bug workaround: + icon.width = 16; + icon.height = 16; + } + + this.createChild = function(text, iconSrc) + { + var child = new TreeNode(text, iconSrc, this.owner.hrefMode); + this.children[ this.children.length ] = child; + this.domEntry.appendChild( child.domEntry ); + child.parentNode = this; + child.owner = this.owner; + + // insert hierarchy images according to deepness level + // of created child. + + if (this.children.length > 1) + { + // there were already added child before. So copy `level-1` + // hierarchy images from it. + + var prevAddedChild = this.children[ this.children.length - 2 ]; + + for (var i = 0; i < prevAddedChild.hierarchyImgs.length - 1; ++i) + { + var prevAddedChildImg = prevAddedChild.hierarchyImgs[i]; + var img = createHierarchyImage(); + setSrc(img, prevAddedChildImg.src); + img.pointsTop = prevAddedChildImg.pointsTop; + img.pointsBottom = prevAddedChildImg.pointsBottom; + img.pointsRight = prevAddedChildImg.pointsRight; + img.pmState = prevAddedChildImg.pmState; + + child.hierarchyImgs[ child.hierarchyImgs.length ] = img; + child.lineDiv.insertBefore(img, child.icon); + } + + // change last hierarchy image of prevAddedChild from |_ to |- + var lastHierarchyImg = prevAddedChild.hierarchyImgs[ prevAddedChild.hierarchyImgs.length - 1 ]; + lastHierarchyImg.pointsBottom = true; + setSrc(lastHierarchyImg, genHierarchyImageSrc(lastHierarchyImg)); + + // change hierarchy images of prevAddedChild's children on it's last + // level to | + prevAddedChild.addHierarchyTBLine(prevAddedChild.hierarchyImgs.length - 1); + } + else + { + // this is a first child. So copy `level-2` + // hierarchy images from parent, i.e. this. + + for (var i = 0; i < this.hierarchyImgs.length - 1; ++i) + { + var parentImg = this.hierarchyImgs[i]; + var img = createHierarchyImage(); + setSrc(img, parentImg.src); + img.pointsTop = parentImg.pointsTop; + img.pointsBottom = parentImg.pointsBottom; + img.pointsRight = parentImg.pointsRight; + img.pmState = parentImg.pmState; + + child.hierarchyImgs[ child.hierarchyImgs.length ] = img; + child.lineDiv.insertBefore(img, child.icon); + } + + if (this.hierarchyImgs.length > 0) // we are not root + { + // change last hierarchy image of parent (i.e. this): add minus to it + var lastHierarchyImg = this.hierarchyImgs[ this.hierarchyImgs.length - 1]; + lastHierarchyImg.pmState = pmMinus; + setSrc(lastHierarchyImg, genHierarchyImageSrc(lastHierarchyImg)); + lastHierarchyImg.owner = this; + lastHierarchyImg.onclick = new Function("e", "this.owner.processPMClick(e);"); + + // make decision on image on `level-1`. It depends on parent's (ie this) + // image on same level. + var parentL1HierarchyImg = lastHierarchyImg; + var l1HierarchyImg = createHierarchyImage(); + if (parentL1HierarchyImg.pointsBottom) + { + l1HierarchyImg.pointsTop = true; + l1HierarchyImg.pointsBottom = true; + } + setSrc(l1HierarchyImg, genHierarchyImageSrc(l1HierarchyImg)); + child.hierarchyImgs[ child.hierarchyImgs.length ] = l1HierarchyImg; + child.lineDiv.insertBefore(l1HierarchyImg, child.icon); + } + } + + // in any case on last level our child will have icon |_ + var img = createHierarchyImage(); + img.pointsTop = true; + img.pointsRight = true; + setSrc(img, genHierarchyImageSrc(img)); + + child.hierarchyImgs[ child.hierarchyImgs.length ] = img; + child.lineDiv.insertBefore(img, child.icon); + + return child; + } + + this.lastChild = function() + { + return this.children[ this.children.length - 1 ]; + } + + this.child = function(text) + { + var ret = null; + for (var i = 0; i < this.children.length; ++i) + if (this.children[i].textElement.data == text) + { + ret = this.children[i]; + break; + } + + return ret; + } + + this.addHierarchyTBLine = function(level) + { + for (var i = 0; i < this.children.length; ++i) + { + var img = this.children[i].hierarchyImgs[level]; + img.pointsTop = true; + img.pointsBottom = true; + setSrc(img, genHierarchyImageSrc(img)); + this.children[i].addHierarchyTBLine(level); + } + } + + this.expand = function() + { + var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ]; + + if (img.pmState == pmPlus) + { + img.pmState = pmMinus; + setSrc(img, genHierarchyImageSrc(img)); + + for (var i = 0; i < this.children.length; ++i) + this.children[i].domEntry.style.display = ""; + } + } + + this.collapse = function() + { + var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ]; + + if (img.pmState == pmMinus) + { + img.pmState = pmPlus; + setSrc(img, genHierarchyImageSrc(img)); + + for (var i = 0; i < this.children.length; ++i) + this.children[i].domEntry.style.display = "none"; + } + } + + this.toggle = function() + { + var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ]; + if (img.pmState == pmMinus) + this.collapse(); + else + this.expand(); + } + + this.select = function() + { + if (this.owner.selection != this) + { + if (this.owner.selection) + this.owner.selection.setHighlight(hlNone); + + this.owner.selection = this; + this.setHighlight(hlSelected); + } + } + + this.setHighlight = function(mode) + { + if (mode == hlNone) + { + this.textSpan.style.backgroundColor = ""; + this.textSpan.style.color = ""; + this.textSpan.style.border = ""; + } + else if (mode == hlGrey) + { + this.textSpan.style.backgroundColor = "#aaaaaa"; + this.textSpan.style.color = ""; + this.textSpan.style.border = ""; + } + else if (mode == hlSelected) + { + this.textSpan.style.backgroundColor = "3399cc"; + this.textSpan.style.color = "white"; + this.textSpan.style.border = "dotted 1px red"; + } + } + + this.setOnclick = function(proc) + { + this.onclick = proc; + } + + this.setRef = function(url) + { + if (this.anchor) + this.anchor.href = url; + } + + this.processPMClick = function(e) + { + this.toggle(); + + // prevent this line selection, stop bubbling + if (e) + e.stopPropagation(); // Mozilla way + if (window.event) + window.event.cancelBubble = true; // IE way + } + + this.processOnclick = function() + { + this.select(); + if (this.onclick instanceof Function) + this.onclick(); + } + + /////////////////////////////////////////////////////////////////////////// + if (iconSrc) + this.icon.src = iconSrc; + else + { + this.icon.width = 0; + this.icon.height = 0; + } + + this.icon.style.verticalAlign = "middle"; + this.icon.style.position = "relative"; + this.icon.style.top = "-1px"; + this.icon.style.paddingRight = "2px"; + + if (!hrefMode) + { + this.textSpan.appendChild( this.textElement ); + } + else + { + this.anchor = document.createElement("a"); + this.anchor.appendChild( this.textElement ); + this.textSpan.appendChild( this.anchor ); + } + + this.lineDiv.appendChild( this.icon ); + this.lineDiv.appendChild( this.textSpan ); + this.domEntry.appendChild( this.lineDiv ); + + this.lineDiv.owner = this; + + if (!hrefMode) + this.lineDiv.onclick = new Function("this.owner.processOnclick();"); +} ADDED doc/candydoc/util.js Index: doc/candydoc/util.js ================================================================== --- doc/candydoc/util.js +++ doc/candydoc/util.js @@ -0,0 +1,41 @@ +/* This file is a part of CanDyDOC fileset. + File is written by Victor Nakoryakov and placed into the public domain. + + This file is javascript with cross-browser utility functions. */ + +function getLeft(elem) +{ + var ret = 0; + while (elem.offsetParent) + { + ret += elem.offsetLeft; + elem = elem.offsetParent; + } + + return ret; +} + +function getTop(elem) +{ + var ret = 0; + while (elem.offsetParent) + { + ret += elem.offsetTop; + elem = elem.offsetParent; + } + + return ret; +} + +function getWindowHeight() +{ + var ret = 0; + if (typeof(window.innerHeight) == "number") + ret = window.innerHeight; + else if (document.documentElement && document.documentElement.clientHeight) + ret = document.documentElement.clientHeight; + else if (document.body && document.body.clientHeight) + ret = document.body.clientHeight; + + return ret; +} Index: main.d ================================================================== --- main.d +++ main.d @@ -11,10 +11,11 @@ import polemy.lex; import polemy.parse; import polemy.ast; import polemy.eval; +/// Tenuki Read-Eval-Print-Loop class REPL { Table ctx; string buf; Value lastVal; @@ -57,10 +58,12 @@ version(unittest) { bool success = false; static ~this(){ if(!success){writeln("(press enter to exit)"); readln();} } } +/// Entry point. If args.length==1, invoke REPL. +/// Otherwise interpret the argument as a filename. void main( string[] args ) { version(unittest) success=true; if( args.length <= 1 ) Index: polemy/ast.d ================================================================== --- polemy/ast.d +++ polemy/ast.d @@ -6,62 +6,70 @@ */ module polemy.ast; import polemy._common; import polemy.lex; +/// abstract class AST { immutable LexPosition pos; mixin SimpleConstructor; } +/// class StrLiteral : AST { string data; mixin SimpleClass; } +/// class IntLiteral : AST { BigInt data; mixin SimpleClass; this(immutable LexPosition pos, long n) {super(pos); data = n;} this(immutable LexPosition pos, BigInt n) {super(pos); data = n;} this(immutable LexPosition pos, string n) {super(pos); data = BigInt(n);} } +/// class VarExpression : AST { string var; mixin SimpleClass; } +/// class LayeredExpression : AST { string lay; AST expr; mixin SimpleClass; } +/// class LetExpression : AST { string var; string layer; AST init; AST expr; mixin SimpleClass; } +/// class FuncallExpression : AST { AST fun; AST[] args; this(immutable LexPosition pos, AST fun, AST[] args...) { super(pos); this.fun=fun; this.args=args.dup; } mixin SimpleClass; } +/// class FunLiteral : AST { string[] params; AST funbody; mixin SimpleClass; @@ -70,16 +78,17 @@ /// Handy Generator for AST nodes. To use this, mixin EasyAst; /*mixin*/ template EasyAST() { + /// template genEast(T) { T genEast(P...)(P ps) { return new T(LexPosition.dummy, ps); } } - alias genEast!StrLiteral strl; - alias genEast!IntLiteral intl; - auto fun(string[] xs, AST ps) { return genEast!FunLiteral(xs,ps); } // to help type inference of D - alias genEast!VarExpression var; - alias genEast!LayeredExpression lay; - alias genEast!LetExpression let; - alias genEast!FuncallExpression call; + alias genEast!StrLiteral strl; /// + alias genEast!IntLiteral intl; /// + auto fun(string[] xs, AST ps) { return genEast!FunLiteral(xs,ps); } /// + alias genEast!VarExpression var; /// + alias genEast!LayeredExpression lay; /// + alias genEast!LetExpression let; /// + alias genEast!FuncallExpression call; /// } Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -10,11 +10,12 @@ import polemy.ast; import polemy.parse; import polemy.value; import std.typecons; import std.stdio; - + +/// Table createGlobalContext() { auto ctx = new Table; // [TODO] autogenerate these typechecks ctx.set("+", "@v", new FunValue(delegate Value(immutable LexPosition pos, Layer lay, Value[] args){ @@ -88,21 +89,29 @@ Tuple!(Value,"val",Table,"ctx") evalString(S,T...)(S str, T fn_ln_cn) { return eval( polemy.parse.parseString(str, fn_ln_cn) ); } +/// Entry point of this module + Tuple!(Value,"val",Table,"ctx") evalFile(S, T...)(S filename, T ln_cn) { return eval( polemy.parse.parseFile(filename, ln_cn) ); } +/// Entry point of this module + Tuple!(Value,"val",Table,"ctx") eval(AST e) { Table ctx = createGlobalContext(); return typeof(return)(eval(e, ctx, false, "@v"), ctx); } +/// Entry point of this module +/// If splitCtx = true, then inner variable declaration do not overwrite ctx. +/// lay is the layer ID for evaluation (standard value semantics uses "@v"). + Value eval(AST _e, Table ctx, bool splitCtx, Layer lay) { if( auto e = cast(StrLiteral)_e ) { return new StrValue(e.data); Index: polemy/lex.d ================================================================== --- polemy/lex.d +++ polemy/lex.d @@ -7,25 +7,25 @@ module polemy.lex; import polemy._common; import std.file : readText; import std.ctype : isspace, isalnum; -/// Exception from this module - /*mixin*/ template ExceptionWithPosition() { const LexPosition pos; this( const LexPosition pos, string msg, string file=null, size_t line=0, Throwable next=null ) { super(sprintf!"[%s] %s"(pos, msg), file, line, next); this.pos = pos; } } +/// class UnexpectedEOF : Exception { mixin ExceptionWithPosition; } +/// class LexException : Exception { mixin ExceptionWithPosition; }; @@ -95,16 +95,19 @@ assert( !__traits(compiles, t.quoted=true) ); } /// Named Construtors for Lexer -auto lexerFromFile(T...)( string filename, T rest ) +Lexer lexerFromFile(T...)( string filename, T rest ) { return lexerFromString( std.file.readText(filename), filename, rest ); } -auto lexerFromString(CharSeq)( CharSeq str, string filename="", int lineno=1, int column=1 ) +/// Named Construtors for Lexer + +LexerT!(PositionedReader!CharSeq) /* ddoc doesn't recognize auto return... bugzilla:2581 */ +lexerFromString(CharSeq)( CharSeq str, string filename="", int lineno=1, int column=1 ) { return new LexerT!(PositionedReader!CharSeq)( PositionedReader!CharSeq(str, filename, lineno, column) ); } Index: polemy/parse.d ================================================================== --- polemy/parse.d +++ polemy/parse.d @@ -1,48 +1,49 @@ -/** - * Authors: k.inaba - * License: NYSL 0.9982 http://www.kmonos.net/nysl/ - * - * Parser for Polemy programming language - */ -module polemy.parse; -import polemy._common; -import polemy.lex; -import polemy.ast; +/** + * Authors: k.inaba + * License: NYSL 0.9982 http://www.kmonos.net/nysl/ + * + * Parser for Polemy programming language + */ +module polemy.parse; +import polemy._common; +import polemy.lex; +import polemy.ast; -/// Exception from this module - -class ParseException : Exception -{ +/// +class ParseException : Exception +{ mixin ExceptionWithPosition; -} +} + +/// Entry points of this module + +AST parseString(S, T...)(S str, T fn_ln_cn) + { return parserFromString(str, fn_ln_cn).parse(); } /// Entry points of this module -auto parseString(S, T...)(S str, T fn_ln_cn) - { return parserFromString(str, fn_ln_cn).parse(); } - -auto parseFile(S, T...)(S filename, T ln_cn) +AST parseFile(S, T...)(S filename, T ln_cn) { return parserFromFile(filename, ln_cn).parse(); } -/// Named Constructor of Parser +// Named Constructors of Parser -private auto parserFromLexer(Lexer)(Lexer lex) - { return new Parser!Lexer(lex); } - -private auto parserFromString(T...)(T params) - { return parserFromLexer(polemy.lex.lexerFromString(params)); } - -private auto parserFromFile(T...)(T params) +private auto parserFromLexer(Lexer)(Lexer lex) + { return new Parser!Lexer(lex); } + +private auto parserFromString(T...)(T params) + { return parserFromLexer(polemy.lex.lexerFromString(params)); } + +private auto parserFromFile(T...)(T params) { return parserFromLexer(polemy.lex.lexerFromFile(params)); } -/// Parser - +// Parser + private class Parser(Lexer) - if( isForwardRange!(Lexer) && is(ElementType!(Lexer) == Token) ) + if( isForwardRange!(Lexer) && is(ElementType!(Lexer) == Token) ) { - AST parse() + AST parse() { auto e = Body(); if( !lex.empty ) throw genex!ParseException(currentPosition(), "parsing ended but some tokens left"); return e; @@ -92,134 +93,134 @@ if( tryEat(";") && !lex.empty && (lex.front.quoted || (lex.front.str!="}" && lex.front.str!=")")) ) return new LetExpression(pos, "_", "", e, Body()); else return e; } - } - - // [TODO] make customizable from program - static immutable string[][] operator_perferences = [ - ["||"], - ["&&"], - ["!="], - ["=="], - ["<","<=",">",">="], - ["|"], - ["^"], - ["&"], - ["<<", ">>"], + } + + // [TODO] make customizable from program + static immutable string[][] operator_perferences = [ + ["||"], + ["&&"], + ["!="], + ["=="], + ["<","<=",">",">="], + ["|"], + ["^"], + ["&"], + ["<<", ">>"], ["+","-"], ["~"], ["*","/","%"], ["^^"] - ]; - - AST E(int level) - { - if( operator_perferences.length <= level ) - return Funcall(); - else - { - auto ops = operator_perferences[level]; - auto e = E(level+1); - seq: - while( !lex.empty ) - { - auto pos = lex.front.pos; - foreach(op; ops) - if( tryEat(op) ) - { - e = new FuncallExpression(e.pos, new VarExpression(pos, op), e, E(level+1)); - continue seq; - } - break; - } - return e; - } - } - - AST Funcall() - { + ]; + + AST E(int level) + { + if( operator_perferences.length <= level ) + return Funcall(); + else + { + auto ops = operator_perferences[level]; + auto e = E(level+1); + seq: + while( !lex.empty ) + { + auto pos = lex.front.pos; + foreach(op; ops) + if( tryEat(op) ) + { + e = new FuncallExpression(e.pos, new VarExpression(pos, op), e, E(level+1)); + continue seq; + } + break; + } + return e; + } + } + + AST Funcall() + { auto e = BaseExpression(); - while( tryEat("(") ) + while( tryEat("(") ) { auto pos = currentPosition(); - AST[] args; - while( !tryEat(")") ) { + AST[] args; + while( !tryEat(")") ) { if( lex.empty ) - throw genex!UnexpectedEOF(pos,"Closing ')' for arguments not found"); + throw genex!UnexpectedEOF(pos,"Closing ')' for arguments not found"); args ~= E(0); - if( !tryEat(",") ) { - eat(")", "after function parameters"); - break; - } - } - e = new FuncallExpression(e.pos, e, args); - } - return e; - } - - AST BaseExpression() - { + if( !tryEat(",") ) { + eat(")", "after function parameters"); + break; + } + } + e = new FuncallExpression(e.pos, e, args); + } + return e; + } + + AST BaseExpression() + { if( lex.empty ) throw genex!UnexpectedEOF(currentPosition(), "Reached EOF when tried to parse an expression"); - auto pos = lex.front.pos; - if( lex.front.quoted ) - { - scope(exit) lex.popFront; - return new StrLiteral(pos, lex.front.str); - } - if( isNumber(lex.front.str) ) - { - scope(exit) lex.popFront; - return new IntLiteral(pos, BigInt(cast(string)lex.front.str)); - } + auto pos = lex.front.pos; + if( lex.front.quoted ) + { + scope(exit) lex.popFront; + return new StrLiteral(pos, lex.front.str); + } + if( isNumber(lex.front.str) ) + { + scope(exit) lex.popFront; + return new IntLiteral(pos, BigInt(cast(string)lex.front.str)); + } if( tryEat("@") ) { auto lay = "@"~eatId("for layer ID"); eat("(", "for layered execution"); auto e = Body(); eat(")", "after "~lay~"(..."); return new LayeredExpression(pos, lay, e); } - if( tryEat("(") ) - { - auto e = Body(); - eat(")", "after parenthesized expression"); - return e; - } - if( tryEat("if") ) - { - eat("(", "after if"); - auto cond = E(0); - eat(")", "after if condition"); - auto thenPos = lex.front.pos; - eat("{", "after if condition"); - auto th = Body(); - eat("}", "after if-then body"); - auto el = doNothingExpression(); - auto elsePos = (lex.empty ? LexPosition.dummy : lex.front.pos); - if( tryEat("else") ) { - eat("{", "after else"); - el = Body(); - eat("}", "after else body"); - } - return new FuncallExpression(pos, - new VarExpression(pos, "if"), - cond, - new FunLiteral(thenPos, [], th), - new FunLiteral(elsePos, [], el) - ); - } - if( tryEat("fun") || tryEat("\u03BB") ) - { - eat("(", "after fun"); + if( tryEat("(") ) + { + auto e = Body(); + eat(")", "after parenthesized expression"); + return e; + } + if( tryEat("if") ) + { + eat("(", "after if"); + auto cond = E(0); + eat(")", "after if condition"); + auto thenPos = lex.front.pos; + eat("{", "after if condition"); + auto th = Body(); + eat("}", "after if-then body"); + auto el = doNothingExpression(); + auto elsePos = (lex.empty ? LexPosition.dummy : lex.front.pos); + if( tryEat("else") ) { + eat("{", "after else"); + el = Body(); + eat("}", "after else body"); + } + return new FuncallExpression(pos, + new VarExpression(pos, "if"), + cond, + new FunLiteral(thenPos, [], th), + new FunLiteral(elsePos, [], el) + ); + } + if( tryEat("fun") || tryEat("\u03BB") ) + { + eat("(", "after fun"); return parseLambdaAfterOpenParen(pos); - } - scope(exit) lex.popFront; - return new VarExpression(pos, lex.front.str); + } + scope(exit) lex.popFront; + return new VarExpression(pos, lex.front.str); } AST parseLambdaAfterOpenParen(immutable LexPosition pos) { string[] params; @@ -233,34 +234,34 @@ } eat("{", "after function parameters"); auto funbody = Body(); eat("}", "after function body"); return new FunLiteral(pos, params, funbody); - } - -private: + } + +private: Lexer lex; this(Lexer lex) { this.lex = lex; } - - void eat(string kwd, lazy string msg) - { + + void eat(string kwd, lazy string msg) + { if( !tryEat(kwd) ) if( lex.empty ) throw genex!UnexpectedEOF( currentPosition(), sprintf!"%s is expected for %s but not found"(kwd,msg)); else throw genex!ParseException( currentPosition(), sprintf!"%s is expected for %s but not found"(kwd,msg)); - } - - bool tryEat(string kwd) - { - if( lex.empty || lex.front.quoted || lex.front.str!=kwd ) - return false; - lex.popFront; - return true; - } + } + + bool tryEat(string kwd) + { + if( lex.empty || lex.front.quoted || lex.front.str!=kwd ) + return false; + lex.popFront; + return true; + } string eatId(lazy string msg, bool allowQuoted=false) { if( lex.empty ) throw genex!UnexpectedEOF(currentPosition(), "identifier is expected but not found "~msg); @@ -267,14 +268,14 @@ if( !allowQuoted && lex.front.quoted ) throw genex!ParseException(currentPosition(), "identifier is expected but not found "~msg); scope(exit) lex.popFront; return lex.front.str; } - - bool isNumber(string s) - { - return find!(`a<'0'||'9'> "~e.toString()); } @@ -53,12 +53,10 @@ assert_throw!AssertError( assert_throw!Error(nothing()) ); assert_nothrow ( assert_throw!Error(assertError()) ); assert_throw!AssertError( assert_throw!AssertError(error()) ); } -/// Unittest helpers asserting two values are in some relation ==, !=, <, <=, >, >= - template assertOp(string op) { void assertOp(A, B, string fn=__FILE__, size_t ln=__LINE__)(A a, B b, string msg="") { try @@ -67,16 +65,16 @@ { onAssertErrorMsg(fn, ln, msg.length ? msg : "exception ["~e.toString()~"]"); } onAssertErrorMsg(fn, ln, msg.length ? msg : to!string(a)~" !"~op~" "~to!string(b)); } } -alias assertOp!(`==`) assert_eq; -alias assertOp!(`!=`) assert_ne; -alias assertOp!(`<`) assert_lt; -alias assertOp!(`<=`) assert_le; -alias assertOp!(`>`) assert_gt; -alias assertOp!(`>=`) assert_ge; +alias assertOp!(`==`) assert_eq; /// asserts two operands are == +alias assertOp!(`!=`) assert_ne; /// asserts two operands are != +alias assertOp!(`<`) assert_lt; /// asserts two operands are < +alias assertOp!(`<=`) assert_le; /// asserts two operands are <= +alias assertOp!(`>`) assert_gt; /// asserts two operands are > +alias assertOp!(`>=`) assert_ge; /// asserts two operands are >= unittest { assert_nothrow( assert_eq(1, 1) ); assert_nothrow( assert_ne(1, 0) ); Index: tricks/tricks.d ================================================================== --- tricks/tricks.d +++ tricks/tricks.d @@ -26,11 +26,11 @@ assert_throw!Error( sprintf!"%s%s"(1) ); } /// Create an exception with automatically completed filename and lineno information -auto genex(ExceptionType, string fn=__FILE__, int ln=__LINE__, T...)(T params) +ExceptionType genex(ExceptionType, string fn=__FILE__, int ln=__LINE__, T...)(T params) { static if( T.length > 0 && is(T[$-1] : Throwable) ) return new ExceptionType(params[0..$-1], fn, ln, params[$-1]); else return new ExceptionType(params, fn, ln); @@ -46,10 +46,11 @@ /// Mixing-in the bean constructor for a class /*mixin*/ template SimpleConstructor() { + /// member-by-member constructor static if( is(typeof(super) == Object) || super.tupleof.length==0 ) this( typeof(this.tupleof) params ) { static if(this.tupleof.length>0) this.tupleof = params; @@ -91,23 +92,20 @@ // shiyo- desu. Don't use in this way. // Tamp tries to call new Tomp(real) (because it only sees Tomp's members), // but it fails because Tomp takes (int,string,real). assert( !__traits(compiles, { - class Tamp : Tomp - { - mixin SimpleConstructor; - } + class Tamp : Tomp { mixin SimpleConstructor; } }) ); } /// Mixing-in the MOST-DERIVED-member-wise comparator for a class /*mixin*/ template SimpleCompare() { - override bool opEquals(Object rhs_) const + override bool opEquals(Object rhs_) const /// member-by-member equality { if( auto rhs = cast(typeof(this))rhs_ ) { foreach(i,_; this.tupleof) if( this.tupleof[i] != rhs.tupleof[i] ) @@ -115,19 +113,19 @@ return true; } assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); } - override hash_t toHash() const + override hash_t toHash() const /// member-by-member hash { hash_t h = 0; foreach(mem; this.tupleof) h += typeid(mem).getHash(&mem); return h; } - override int opCmp(Object rhs_) const + override int opCmp(Object rhs_) const /// member-by-member compare { if( auto rhs = cast(typeof(this))rhs_ ) { foreach(i,_; this.tupleof) if(auto c = typeid(_).compare(&this.tupleof[i],&rhs.tupleof[i])) @@ -168,11 +166,12 @@ /// Mixing-in a simple toString method /*mixin*/ template SimpleToString() -{ +{ + /// member-by-member toString override string toString() { string str = sprintf!"%s("(typeof(this).stringof); foreach(i,mem; this.tupleof) {