1020304050607080901001101201301401501601701801902002102202302402502602722802913003103213303403503603723803921400411420430440450461470480490500510520530540550560570580590600610620630640650660670680690700710720730740750760770780790800810821698308414118508640087088148890900912529209309416995096097098099441004410101027651030104211105010610910701080109211110011144112011301140115441160117011801190120412101220123012401250126012701280129013001310132213301340135213601372138013901400141014232143014401450146014781480149015001510152261530154615501562157015801590160216101620163016401650166016701680169017001710172017301740175017661776178017901800181618201832184018501866187018801890190019161920193619461950196619761986199620062010202620302042510205251020602072510208020914210021102122510213021474215021602172510218021974220022174222022320224022502260227251022802295214230023180232802330234023502366237023802390240024102420243024402450246024702480249025002510252552530254558342550256557792575577925855779259026002610262026302645526502660267026802690270902710272902731742740275722760277027818279028002817228202830284028502860287168288028902900291029202930294029502960297029802990300030103020303030403050306030703081430914310031103120313031452315031652317523180319515232003215152322032351523240325284326284327032803295152330033128433203330334515233551523361016033703385233903400341034252343034403450346034703483643490350364351035211735303540355247356035703581173590360036103620363036489365036689367036803690370037103720373193740375123376037712337803791938003810382208383104384038503861938703880389039003910392039363940395639663970398257839904002578401040225784030404744057440604070408257840904107441104122578413257841451304150416641704180419042064210422042304240425042604276342804296343063431634320433280434043528043604373014380439744004410442287443044404450446044728744804497450045104522734530454564555645656457045804592734600461564620463046427346504664504670468564690470047121747204730474634750476047704780479048004810482748304842524850486774870488144890490049104926349304940495049674970 /++ A module containing parsing code utilities Copyright: © 2017 Szabo Bogdan License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. Authors: Szabo Bogdan +/ module trial.discovery.code; import std.algorithm; import std.string; import std.range; import std.file; import std.stdio; import std.conv; version (Have_libdparse) { public import dparse.ast; public import dparse.lexer; public import dparse.parser; } /// Get the module name of a DLang source file string getModuleName(string fileName) { if (!exists(fileName)) { return ""; } if (isDir(fileName)) { return ""; } auto file = File(fileName); auto moduleLine = file.byLine().map!(a => a.to!string).filter!(a => a.startsWith("module")); if (moduleLine.empty) { return ""; } return moduleLine.front.split(' ')[1].split(";")[0]; } version (Have_libdparse) { const(Token)[] stringToDTokens(string data) { try { ubyte[] fileBytes = cast(ubyte[]) data; StringCache cache = StringCache(StringCache.defaultBucketCount); LexerConfig config; config.stringBehavior = StringBehavior.source; config.fileName = ""; config.commentBehavior = CommentBehavior.intern; auto lexer = DLexer(fileBytes, config, &cache); const(Token)[] tokens = lexer.array; return tokens.map!(token => const Token(token.type, token.text.idup, token.line, token.column, token.index)).array; } catch(Throwable t) { t.writeln; return []; } } /// struct DLangAttribute { const(Token)[] tokens; inout { string identifier() { string result; foreach (token; tokens) { if (str(token.type) == "(") { break; } result ~= token.text; } return result; } string value() { bool after; string result; foreach (token; tokens) { if (after) { result ~= token.text.strip('"').strip('`').strip('\''); } if (str(token.type) == "(") { after = true; } } return result; } auto line() { return tokens[0].line; } } } struct DLangFunction { const(DLangAttribute)[] attributes; const(Token)[] tokens; string name() { auto result = TokenIterator(tokens).readUntilType("(").replace("\n", " ") .replace("\r", " ").replace("\t", " ").split(" "); std.algorithm.reverse(result); return result[0]; } bool hasAttribute(string name) { return !attributes.filter!(a => a.identifier == name).empty; } auto getAttribute(string name) { return attributes.filter!(a => a.identifier == name).front; } string testName() { foreach (attribute; attributes) { if (attribute.identifier == "") { return attribute.value; } } return name.camelToSentence; } size_t line() { return TokenIterator(tokens).skipUntilType("(").currentToken.line; } } struct DLangClass { const(Token)[] tokens; /// returns the class name string name() { auto iterator = TokenIterator(tokens); auto name = iterator.readUntilType("{"); import std.stdio; if (name.indexOf(":") != -1) { name = name.split(":")[0]; } return name.strip; } DLangFunction[] functions() { int paranthesisCount; auto iterator = TokenIterator(tokens); iterator.skipUntilType("{"); const(Token)[] currentTokens; DLangFunction[] discoveredFunctions; DLangAttribute[] attributes; bool readingFunction; int functionLevel = 1; foreach (token; iterator) { string type = token.type.str; currentTokens ~= token; if (type == "@") { attributes ~= iterator.readAttribute; } if (type == "{") { paranthesisCount++; } if (type == "}") { paranthesisCount--; if (paranthesisCount == functionLevel) { discoveredFunctions ~= DLangFunction(attributes, currentTokens); } } readingFunction = paranthesisCount > functionLevel; if (type == "}" || (!readingFunction && type == ";")) { currentTokens = []; attributes = []; } } return discoveredFunctions; } } /// An iterator that helps to deal with DLang tokens struct TokenIterator { private { const(Token)[] tokens; size_t index; } /// int opApply(int delegate(const(Token)) dg) { int result = 0; while (index < tokens.length) { result = dg(tokens[index]); index++; if (result) { break; } } return result; } /// ref auto skipWsAndComments() { while (index < tokens.length) { auto type = str(tokens[index].type); if (type != "comment" && type != "whitespace") { break; } index++; } return this; } /// auto currentToken() { return tokens[index]; } /// Skip until a token with a certain text is reached ref auto skipUntil(string text) { while (index < tokens.length) { if (tokens[index].text == text) { break; } index++; } return this; } ref auto skipNextBlock() { readNextBlock(); return this; } auto readNextBlock() { const(Token)[] blockTokens = []; bool readingBlock; int paranthesisCount; while (index < tokens.length) { auto type = str(tokens[index].type); if (type == "{") { paranthesisCount++; readingBlock = true; } if (type == "}") { paranthesisCount--; } blockTokens ~= tokens[index]; index++; if (readingBlock && paranthesisCount == 0) { break; } } return blockTokens; } /// Skip until a token with a certain type is reached ref auto skipUntilType(string type) { while (index < tokens.length) { if (str(tokens[index].type) == type) { break; } index++; } return this; } /// Skip one token ref auto skipOne() { index++; return this; } /// Concatenate all the tokens until the first token of a certain type /// that will be ignored string readUntilType(string type) { string result; while (index < tokens.length) { if (str(tokens[index].type) == type) { break; } result ~= tokens[index].text == "" ? str(tokens[index].type) : tokens[index].text; index++; } return result; } /// Returns a Dlang class. You must call this method after the /// class token was read. DLangClass readClass() { const(Token)[] classTokens = []; bool readingClass; int paranthesisCount; while (index < tokens.length) { auto type = str(tokens[index].type); if (type == "{") { paranthesisCount++; readingClass = true; } if (type == "}") { paranthesisCount--; } classTokens ~= tokens[index]; index++; if (readingClass && paranthesisCount == 0) { break; } } return DLangClass(classTokens); } /// Returns a Dlang attribute. You must call this method after the /// @ token was read. DLangAttribute readAttribute() { const(Token)[] attributeTokens = []; int paranthesisCount; bool readingParams; bool foundWs; while (index < tokens.length) { auto type = str(tokens[index].type); if (type == "whitespace" && paranthesisCount == 0 && !readingParams) { foundWs = true; } if (foundWs && type == ".") { foundWs = false; } if (foundWs && type != "(") { break; } if (type == "(") { paranthesisCount++; readingParams = true; foundWs = false; } if (type == ")") { paranthesisCount--; } attributeTokens ~= tokens[index]; if (readingParams && paranthesisCount == 0) { break; } index++; } return DLangAttribute(attributeTokens); } } } /// Converts a string from camel notation to a readable sentence string camelToSentence(const string name) pure { string sentence; foreach (ch; name) { if (ch.toUpper == ch) { sentence ~= " " ~ ch.toLower.to!string; } else { sentence ~= ch; } } return sentence.capitalize; }
/++ A module containing parsing code utilities Copyright: © 2017 Szabo Bogdan License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. Authors: Szabo Bogdan +/ module trial.discovery.code; import std.algorithm; import std.string; import std.range; import std.file; import std.stdio; import std.conv; version (Have_libdparse) { public import dparse.ast; public import dparse.lexer; public import dparse.parser; } /// Get the module name of a DLang source file string getModuleName(string fileName) { if (!exists(fileName)) { return ""; } if (isDir(fileName)) { return ""; } auto file = File(fileName); auto moduleLine = file.byLine().map!(a => a.to!string).filter!(a => a.startsWith("module")); if (moduleLine.empty) { return ""; } return moduleLine.front.split(' ')[1].split(";")[0]; } version (Have_libdparse) { const(Token)[] stringToDTokens(string data) { try { ubyte[] fileBytes = cast(ubyte[]) data; StringCache cache = StringCache(StringCache.defaultBucketCount); LexerConfig config; config.stringBehavior = StringBehavior.source; config.fileName = ""; config.commentBehavior = CommentBehavior.intern; auto lexer = DLexer(fileBytes, config, &cache); const(Token)[] tokens = lexer.array; return tokens.map!(token => const Token(token.type, token.text.idup, token.line, token.column, token.index)).array; } catch(Throwable t) { t.writeln; return []; } } /// struct DLangAttribute { const(Token)[] tokens; inout { string identifier() { string result; foreach (token; tokens) { if (str(token.type) == "(") { break; } result ~= token.text; } return result; } string value() { bool after; string result; foreach (token; tokens) { if (after) { result ~= token.text.strip('"').strip('`').strip('\''); } if (str(token.type) == "(") { after = true; } } return result; } auto line() { return tokens[0].line; } } } struct DLangFunction { const(DLangAttribute)[] attributes; const(Token)[] tokens; string name() { auto result = TokenIterator(tokens).readUntilType("(").replace("\n", " ") .replace("\r", " ").replace("\t", " ").split(" "); std.algorithm.reverse(result); return result[0]; } bool hasAttribute(string name) { return !attributes.filter!(a => a.identifier == name).empty; } auto getAttribute(string name) { return attributes.filter!(a => a.identifier == name).front; } string testName() { foreach (attribute; attributes) { if (attribute.identifier == "") { return attribute.value; } } return name.camelToSentence; } size_t line() { return TokenIterator(tokens).skipUntilType("(").currentToken.line; } } struct DLangClass { const(Token)[] tokens; /// returns the class name string name() { auto iterator = TokenIterator(tokens); auto name = iterator.readUntilType("{"); import std.stdio; if (name.indexOf(":") != -1) { name = name.split(":")[0]; } return name.strip; } DLangFunction[] functions() { int paranthesisCount; auto iterator = TokenIterator(tokens); iterator.skipUntilType("{"); const(Token)[] currentTokens; DLangFunction[] discoveredFunctions; DLangAttribute[] attributes; bool readingFunction; int functionLevel = 1; foreach (token; iterator) { string type = token.type.str; currentTokens ~= token; if (type == "@") { attributes ~= iterator.readAttribute; } if (type == "{") { paranthesisCount++; } if (type == "}") { paranthesisCount--; if (paranthesisCount == functionLevel) { discoveredFunctions ~= DLangFunction(attributes, currentTokens); } } readingFunction = paranthesisCount > functionLevel; if (type == "}" || (!readingFunction && type == ";")) { currentTokens = []; attributes = []; } } return discoveredFunctions; } } /// An iterator that helps to deal with DLang tokens struct TokenIterator { private { const(Token)[] tokens; size_t index; } /// int opApply(int delegate(const(Token)) dg) { int result = 0; while (index < tokens.length) { result = dg(tokens[index]); index++; if (result) { break; } } return result; } /// ref auto skipWsAndComments() { while (index < tokens.length) { auto type = str(tokens[index].type); if (type != "comment" && type != "whitespace") { break; } index++; } return this; } /// auto currentToken() { return tokens[index]; } /// Skip until a token with a certain text is reached ref auto skipUntil(string text) { while (index < tokens.length) { if (tokens[index].text == text) { break; } index++; } return this; } ref auto skipNextBlock() { readNextBlock(); return this; } auto readNextBlock() { const(Token)[] blockTokens = []; bool readingBlock; int paranthesisCount; while (index < tokens.length) { auto type = str(tokens[index].type); if (type == "{") { paranthesisCount++; readingBlock = true; } if (type == "}") { paranthesisCount--; } blockTokens ~= tokens[index]; index++; if (readingBlock && paranthesisCount == 0) { break; } } return blockTokens; } /// Skip until a token with a certain type is reached ref auto skipUntilType(string type) { while (index < tokens.length) { if (str(tokens[index].type) == type) { break; } index++; } return this; } /// Skip one token ref auto skipOne() { index++; return this; } /// Concatenate all the tokens until the first token of a certain type /// that will be ignored string readUntilType(string type) { string result; while (index < tokens.length) { if (str(tokens[index].type) == type) { break; } result ~= tokens[index].text == "" ? str(tokens[index].type) : tokens[index].text; index++; } return result; } /// Returns a Dlang class. You must call this method after the /// class token was read. DLangClass readClass() { const(Token)[] classTokens = []; bool readingClass; int paranthesisCount; while (index < tokens.length) { auto type = str(tokens[index].type); if (type == "{") { paranthesisCount++; readingClass = true; } if (type == "}") { paranthesisCount--; } classTokens ~= tokens[index]; index++; if (readingClass && paranthesisCount == 0) { break; } } return DLangClass(classTokens); } /// Returns a Dlang attribute. You must call this method after the /// @ token was read. DLangAttribute readAttribute() { const(Token)[] attributeTokens = []; int paranthesisCount; bool readingParams; bool foundWs; while (index < tokens.length) { auto type = str(tokens[index].type); if (type == "whitespace" && paranthesisCount == 0 && !readingParams) { foundWs = true; } if (foundWs && type == ".") { foundWs = false; } if (foundWs && type != "(") { break; } if (type == "(") { paranthesisCount++; readingParams = true; foundWs = false; } if (type == ")") { paranthesisCount--; } attributeTokens ~= tokens[index]; if (readingParams && paranthesisCount == 0) { break; } index++; } return DLangAttribute(attributeTokens); } } } /// Converts a string from camel notation to a readable sentence string camelToSentence(const string name) pure { string sentence; foreach (ch; name) { if (ch.toUpper == ch) { sentence ~= " " ~ ch.toLower.to!string; } else { sentence ~= ch; } } return sentence.capitalize; }