checking in work on lexer and caching
This commit is contained in:
parent
9d6b96135e
commit
c429199c1c
|
@ -75,7 +75,9 @@ list. See the documentation on the --dotComplete option for details
|
||||||
success k
|
success k
|
||||||
|
|
||||||
# JSON output
|
# JSON output
|
||||||
Generates a JSON summary of the input file.
|
Generates a JSON summary of the input file. The JSON output produced complies
|
||||||
|
with a JSON schema included with the project under the "schemas" directory. (Note
|
||||||
|
that the schema is not yet complete)
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
The given D code:
|
The given D code:
|
||||||
|
|
4
build.sh
4
build.sh
|
@ -1,2 +1,2 @@
|
||||||
#dmd *.d -release -noboundscheck -O -w -wi -m64 -property -ofdscanner #-inline
|
dmd *.d -release -noboundscheck -O -w -wi -m64 -property -ofdscanner -L-lsqlite3 #-inline
|
||||||
dmd *.d -g -m64 -w -wi -property -ofdscanner
|
#dmd *.d -g -m64 -w -wi -property -ofdscanner -L-lsqlite3 #-unittest
|
||||||
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
// Copyright Brian Schott (Sir Alaran) 2012.
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
// http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
module cache;
|
||||||
|
|
||||||
|
import etc.c.sqlite3;
|
||||||
|
import std.c.stdlib;
|
||||||
|
import std.datetime;
|
||||||
|
import std.file;
|
||||||
|
import std.uuid;
|
||||||
|
import std.array;
|
||||||
|
import std.string;
|
||||||
|
import std.conv;
|
||||||
|
|
||||||
|
import location;
|
||||||
|
import parser;
|
||||||
|
import types;
|
||||||
|
import tokenizer;
|
||||||
|
|
||||||
|
private sqlite3* database;
|
||||||
|
|
||||||
|
version (Posix)
|
||||||
|
{
|
||||||
|
private immutable char* DB_PATH = "~/.dscanner/cache.db";
|
||||||
|
}
|
||||||
|
else version (Windows)
|
||||||
|
{
|
||||||
|
pragma(msg, "Caching not supported on Windows yet");
|
||||||
|
immutable string DB_PATH = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum Queries : string
|
||||||
|
{
|
||||||
|
getUpdateTime = "select mtime from files where filepath = ?",
|
||||||
|
insertContainer = "insert into containers values ()",
|
||||||
|
deleteContainer = "delete from containers where fileId = ?",
|
||||||
|
deleteSymbol = "delete from symbols where containerId = ?",
|
||||||
|
deleteFile = "delete from files where path = ?",
|
||||||
|
getPublicImports = "select importedId from publicImports where importerId = ?",
|
||||||
|
getModuleId = "select id from files where path = ?",
|
||||||
|
getContainersByModule = "select id from containers where fileId = ?"
|
||||||
|
}
|
||||||
|
|
||||||
|
private sqlite3* getDatabase()
|
||||||
|
{
|
||||||
|
if (database !is null)
|
||||||
|
return database;
|
||||||
|
int status = sqlite3_open(DB_PATH, &database);
|
||||||
|
if (status != SQLITE_OK)
|
||||||
|
{
|
||||||
|
throw new Exception("Could not open %s: %s".format(DB_PATH,
|
||||||
|
sqlite3_errmsg(database)));
|
||||||
|
}
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
|
||||||
|
void closeDatabase()
|
||||||
|
{
|
||||||
|
if (database !is null)
|
||||||
|
{
|
||||||
|
sqlite3_close(database);
|
||||||
|
database = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getCachedModTime(sqlite3* db, sqlite3_stmt* statement, string filePath)
|
||||||
|
{
|
||||||
|
bindText(statement, 1, filePath);
|
||||||
|
if (sqlite3_step(statement) != SQLITE_ROW)
|
||||||
|
throw new Exception("%s".format(sqlite3_errmsg(db)));
|
||||||
|
return sqlite3_column_int64(statement, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the sqlite database with current autocomplete information for the
|
||||||
|
* given modules.
|
||||||
|
*/
|
||||||
|
void updateCache(string dirs[], string moduleNames[])
|
||||||
|
{
|
||||||
|
string[] filePaths;
|
||||||
|
foreach (moduleName; moduleNames)
|
||||||
|
{
|
||||||
|
string path = findAbsPath(dirs, moduleName);
|
||||||
|
if (path is null)
|
||||||
|
continue;
|
||||||
|
filePaths ~= path;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3* db = getDatabase();
|
||||||
|
sqlite3_stmt* statement;
|
||||||
|
scope(exit) { if (statement) sqlite3_finalize(statement); }
|
||||||
|
char* pzTail;
|
||||||
|
scope(exit) { if (pzTail) free(pzTail); }
|
||||||
|
sqlite3_prepare_v2(db, Queries.getUpdateTime.toStringz(),
|
||||||
|
cast(int) Queries.getUpdateTime.length + 1, &statement, &pzTail);
|
||||||
|
|
||||||
|
foreach (string filePath; filePaths)
|
||||||
|
{
|
||||||
|
immutable long mtime = getCachedModTime(db, statement, filePath);
|
||||||
|
SysTime timeLastModified = timeLastModified(filePath);
|
||||||
|
// if the times match, we don't need to update the cache.
|
||||||
|
if (timeLastModified.stdTime == mtime)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// re-parse the module
|
||||||
|
Module m = parseModule(tokenize(readText(filePath)));
|
||||||
|
|
||||||
|
updateCache(m);
|
||||||
|
|
||||||
|
sqlite3_reset(statement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCache(const Module m)
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(m !is null);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private string[] getImportedModules(string modulePath, sqlite3_stmt* statement = null)
|
||||||
|
{
|
||||||
|
auto app = appender!(string[])();
|
||||||
|
sqlite3* db = getDatabase();
|
||||||
|
bool statementAllocated = false;
|
||||||
|
scope(exit) { if (statementAllocated && statement !is null) sqlite3_finalize(statement); }
|
||||||
|
if (statement is null)
|
||||||
|
{
|
||||||
|
statementAllocated = true;
|
||||||
|
char* pzTail;
|
||||||
|
scope(exit) { if (pzTail) free(pzTail); }
|
||||||
|
sqlite3_prepare_v2(db, Queries.getPublicImports.toStringz(),
|
||||||
|
cast(int) Queries.getPublicImports.length + 1, &statement, &pzTail);
|
||||||
|
}
|
||||||
|
|
||||||
|
string moduleId = getModuleIdFromPath(modulePath);
|
||||||
|
bindText(statement, 1, moduleId);
|
||||||
|
while (sqlite3_step(statement) == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
app.put(to!string(sqlite3_column_text(statement, 1)));
|
||||||
|
}
|
||||||
|
sqlite3_reset(statement);
|
||||||
|
foreach (string imported; app.data)
|
||||||
|
{
|
||||||
|
string[] r = getImportedModules(imported, statement);
|
||||||
|
}
|
||||||
|
return app.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string getModuleIdFromPath(string filePath)
|
||||||
|
{
|
||||||
|
sqlite3* db = getDatabase();
|
||||||
|
sqlite3_stmt* statement;
|
||||||
|
char* pzTail;
|
||||||
|
scope(exit) if (pzTail) free(pzTail);
|
||||||
|
sqlite3_prepare_v2(db, Queries.getModuleId.toStringz(),
|
||||||
|
cast(int) Queries.getModuleId.length + 1, &statement,
|
||||||
|
&pzTail);
|
||||||
|
bindText(statement, 1, filePath);
|
||||||
|
if (sqlite3_step(statement) != SQLITE_ROW)
|
||||||
|
return null;
|
||||||
|
return to!string(sqlite3_column_text(statement, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: the container IDs of the containers that have
|
||||||
|
* been imported
|
||||||
|
*/
|
||||||
|
public string[] getContainersImported(string modulePath)
|
||||||
|
{
|
||||||
|
immutable string moduleId = getModuleIdFromPath(modulePath);
|
||||||
|
sqlite3* db = getDatabase();
|
||||||
|
sqlite3_stmt* statement;
|
||||||
|
char* pzTail;
|
||||||
|
scope(exit) if (pzTail) free(pzTail);
|
||||||
|
string[] moduleIds = getImportedModules(modulePath);
|
||||||
|
string[] containerIds;
|
||||||
|
foreach (string id; moduleIds)
|
||||||
|
{
|
||||||
|
containerIds ~= getContainersByModule(id);
|
||||||
|
}
|
||||||
|
return containerIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string[] getContainersByModule(string moduleId)
|
||||||
|
{
|
||||||
|
sqlite3* db = getDatabase();
|
||||||
|
sqlite3_stmt* statement;
|
||||||
|
scope(exit) if (statement !is null) sqlite3_finalize(statement);
|
||||||
|
char* pzTail;
|
||||||
|
prepareStatement(db, statement, Queries.getContainersByModule);
|
||||||
|
bindText(statement, 1, moduleId);
|
||||||
|
string[] rVal;
|
||||||
|
while (sqlite3_step(statement) == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
rVal ~= to!string(sqlite3_column_text(statement, 1));
|
||||||
|
}
|
||||||
|
return rVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareStatement(sqlite3* db, sqlite3_stmt* statement, string query)
|
||||||
|
{
|
||||||
|
char* pzTail;
|
||||||
|
scope(exit) if (pzTail) free(pzTail);
|
||||||
|
sqlite3_prepare_v2(db, query.toStringz(), cast(int) query.length + 1,
|
||||||
|
&statement, &pzTail);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindText(sqlite3_stmt* statement, int argPos, string text)
|
||||||
|
{
|
||||||
|
sqlite3_bind_text(statement, argPos, text.toStringz(),
|
||||||
|
cast(int) text.length + 1, SQLITE_TRANSIENT);
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
|
||||||
|
// Copyright Brian Schott (Sir Alaran) 2012.
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
// http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
module circularbuffer;
|
||||||
|
|
||||||
|
import std.math;
|
||||||
|
import std.array;
|
||||||
|
import std.range;
|
||||||
|
|
||||||
|
struct CircularBuffer(T, R) if (isInputRange!(R) && is (ElementType!(R) == T))
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
this (size_t size, R range)
|
||||||
|
{
|
||||||
|
this.range = range;
|
||||||
|
this.margin = size;
|
||||||
|
data = new T[(margin * 2) + 1];
|
||||||
|
if (range.empty())
|
||||||
|
{
|
||||||
|
_empty = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i <= margin && !this.range.empty(); ++i)
|
||||||
|
{
|
||||||
|
data[i] = this.range.front();
|
||||||
|
this.range.popFront();
|
||||||
|
end++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T opIndex(size_t index) const
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert (index <= sourceIndex + margin);
|
||||||
|
assert (index >= sourceIndex - margin);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
return data[index % data.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
T front() const @property
|
||||||
|
{
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
T peek(int offset)
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(abs(offset) <= margin);
|
||||||
|
assert(sourceIndex + offset >= 0);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
return data[(index + offset) % data.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
T popFront()
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert (!_empty);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
T v = data[index];
|
||||||
|
index = (index + 1) % data.length;
|
||||||
|
++sourceIndex;
|
||||||
|
if (range.empty())
|
||||||
|
{
|
||||||
|
if (index == end)
|
||||||
|
_empty = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data[end] = range.front();
|
||||||
|
end = (end + 1) % data.length;
|
||||||
|
range.popFront();
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const @property
|
||||||
|
{
|
||||||
|
return _empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
R range;
|
||||||
|
immutable size_t margin;
|
||||||
|
T[] data;
|
||||||
|
size_t sourceIndex;
|
||||||
|
size_t end;
|
||||||
|
size_t index;
|
||||||
|
bool _empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
int[] items = [1, 2];
|
||||||
|
auto buf = CircularBuffer!(int, int[])(5, items);
|
||||||
|
auto result = array(buf);
|
||||||
|
assert(result == items);
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
int[] arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
auto buf = CircularBuffer!(int, int[])(2, arr);
|
||||||
|
assert (buf.data.length == 5);
|
||||||
|
auto iterated = array(buf);
|
||||||
|
assert (iterated == arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
int[] arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
auto buf = CircularBuffer!(int, int[])(2, arr);
|
||||||
|
buf.popFront();
|
||||||
|
buf.popFront();
|
||||||
|
buf.popFront();
|
||||||
|
buf.popFront();
|
||||||
|
assert (buf.front == 4);
|
||||||
|
assert (buf[2] == 2);
|
||||||
|
assert (buf[6] == 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
int[] arr = [0, 1, 2, 3];
|
||||||
|
auto buf = CircularBuffer!(int, int[])(2, arr);
|
||||||
|
assert (buf.peek(0) == 0);
|
||||||
|
assert (buf.peek(1) == 1);
|
||||||
|
assert (buf.peek(2) == 2);
|
||||||
|
buf.popFront();
|
||||||
|
buf.popFront();
|
||||||
|
assert (buf.peek(-2) == 0);
|
||||||
|
assert (buf.peek(-1) == 1);
|
||||||
|
assert (buf.peek(0) == 2);
|
||||||
|
assert (buf.peek(1) == 3);
|
||||||
|
}
|
||||||
|
|
26
codegen.d
26
codegen.d
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
// Copyright Brian Schott (Sir Alaran) 2012.
|
// Copyright Brian Schott (Sir Alaran) 2012.
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
@ -110,28 +109,3 @@ string generateCaseTrie(string[] args ...)
|
||||||
}
|
}
|
||||||
return printCaseStatements(t, "");
|
return printCaseStatements(t, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns: true if index points to end of inputString, false otherwise
|
|
||||||
*/
|
|
||||||
pure nothrow bool isEoF(S)(S inputString, size_t index)
|
|
||||||
{
|
|
||||||
// note: EoF is determined according to D specification
|
|
||||||
return index >= inputString.length
|
|
||||||
|| inputString[index] == Character.NUL
|
|
||||||
|| inputString[index] == Character.SUB;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
// Unicode character literals
|
|
||||||
enum Character
|
|
||||||
{
|
|
||||||
// End of file (EoF)
|
|
||||||
NUL = '\u0000', // NUL character
|
|
||||||
SUB = '\u001A', // Substitute character
|
|
||||||
|
|
||||||
// Line feed (EoL)
|
|
||||||
CR = '\u000D', // CR character
|
|
||||||
LF = '\u000A', // LF character
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
create table files (path, mtime, id);
|
||||||
|
create table publicImports (importerId, importedId);
|
||||||
|
create table containers (name, protection, fileId, id);
|
||||||
|
create table symbols (name, type, kind, containerId, id);
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright Brian Schott (Sir Alaran) 2012.
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
// http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
module location;
|
||||||
|
|
||||||
|
import std.string;
|
||||||
|
import std.path;
|
||||||
|
import std.file;
|
||||||
|
import std.stdio;
|
||||||
|
import std.array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: the absolute path of the given module, or null if it could not be
|
||||||
|
* found.
|
||||||
|
*/
|
||||||
|
string findAbsPath(string[] dirs, string moduleName)
|
||||||
|
{
|
||||||
|
// For file names
|
||||||
|
if (endsWith(moduleName, ".d") || endsWith(moduleName, ".di"))
|
||||||
|
{
|
||||||
|
if (isAbsolute(moduleName))
|
||||||
|
return moduleName;
|
||||||
|
else
|
||||||
|
return buildPath(getcwd(), moduleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find the file name from a module name like "std.stdio"
|
||||||
|
foreach(dir; dirs)
|
||||||
|
{
|
||||||
|
string fileLocation = buildPath(dir, replace(moduleName, ".", dirSeparator));
|
||||||
|
string dfile = fileLocation ~ ".d";
|
||||||
|
if (exists(dfile) && isFile(dfile))
|
||||||
|
{
|
||||||
|
return dfile;
|
||||||
|
}
|
||||||
|
if (exists(fileLocation ~ ".di") && isFile(fileLocation ~ ".di"))
|
||||||
|
{
|
||||||
|
return fileLocation ~ ".di";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stderr.writeln("Could not locate import ", moduleName, " in ", dirs);
|
||||||
|
return null;
|
||||||
|
}
|
55
main.d
55
main.d
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
// Copyright Brian Schott (Sir Alaran) 2012.
|
// Copyright Brian Schott (Sir Alaran) 2012.
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
@ -7,21 +6,22 @@
|
||||||
module main;
|
module main;
|
||||||
|
|
||||||
|
|
||||||
import std.file;
|
|
||||||
import std.stdio;
|
|
||||||
import std.algorithm;
|
import std.algorithm;
|
||||||
import std.conv;
|
|
||||||
import std.array;
|
import std.array;
|
||||||
import std.path;
|
import std.conv;
|
||||||
import std.regex;
|
import std.file;
|
||||||
import std.getopt;
|
import std.getopt;
|
||||||
import std.parallelism;
|
import std.parallelism;
|
||||||
import types;
|
import std.path;
|
||||||
import tokenizer;
|
import std.regex;
|
||||||
import parser;
|
import std.stdio;
|
||||||
import langutils;
|
|
||||||
import autocomplete;
|
import autocomplete;
|
||||||
import highlighter;
|
import highlighter;
|
||||||
|
import langutils;
|
||||||
|
import location;
|
||||||
|
import parser;
|
||||||
|
import tokenizer;
|
||||||
|
import types;
|
||||||
|
|
||||||
pure bool isLineOfCode(TokenType t)
|
pure bool isLineOfCode(TokenType t)
|
||||||
{
|
{
|
||||||
|
@ -74,38 +74,6 @@ else
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns: the absolute path of the given module, or null if it could not be
|
|
||||||
* found.
|
|
||||||
*/
|
|
||||||
string findAbsPath(string[] dirs, string moduleName)
|
|
||||||
{
|
|
||||||
// For file names
|
|
||||||
if (endsWith(moduleName, ".d") || endsWith(moduleName, ".di"))
|
|
||||||
{
|
|
||||||
if (isAbsolute(moduleName))
|
|
||||||
return moduleName;
|
|
||||||
else
|
|
||||||
return buildPath(getcwd(), moduleName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to find the file name from a module name like "std.stdio"
|
|
||||||
foreach(dir; dirs)
|
|
||||||
{
|
|
||||||
string fileLocation = buildPath(dir, replace(moduleName, ".", dirSeparator));
|
|
||||||
string dfile = fileLocation ~ ".d";
|
|
||||||
if (exists(dfile) && isFile(dfile))
|
|
||||||
{
|
|
||||||
return dfile;
|
|
||||||
}
|
|
||||||
if (exists(fileLocation ~ ".di") && isFile(fileLocation ~ ".di"))
|
|
||||||
{
|
|
||||||
return fileLocation ~ ".di";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stderr.writeln("Could not locate import ", moduleName, " in ", dirs);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
string[] loadConfig()
|
string[] loadConfig()
|
||||||
{
|
{
|
||||||
|
@ -152,7 +120,8 @@ int main(string[] args)
|
||||||
stderr.writeln(e.msg);
|
stderr.writeln(e.msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (help)
|
if (help || (!sloc && !dotComplete && !json && !parenComplete && !highlight
|
||||||
|
&& !ctags && !format))
|
||||||
{
|
{
|
||||||
printHelp();
|
printHelp();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
6
parser.d
6
parser.d
|
@ -265,7 +265,11 @@ Module parseModule(const Token[] tokens, string protection = "public", string[]
|
||||||
{
|
{
|
||||||
switch(tokens[index].type)
|
switch(tokens[index].type)
|
||||||
{
|
{
|
||||||
case TokenType.Else:
|
case TokenType.Pragma:
|
||||||
|
++index;
|
||||||
|
if (tokens[index] == TokenType.LParen)
|
||||||
|
skipParens(tokens, index);
|
||||||
|
break;
|
||||||
case TokenType.Mixin:
|
case TokenType.Mixin:
|
||||||
case TokenType.Assert:
|
case TokenType.Assert:
|
||||||
++index;
|
++index;
|
||||||
|
|
|
@ -1 +1,93 @@
|
||||||
|
{
|
||||||
|
"description" : "JSON schema for Dscanner's output",
|
||||||
|
"properties" : {
|
||||||
|
"name" : {
|
||||||
|
"required" : true,
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"imports" : {
|
||||||
|
"type" : "array",
|
||||||
|
"items" : {
|
||||||
|
"type" : "string"
|
||||||
|
},
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"interfaces" : {
|
||||||
|
"type": "array",
|
||||||
|
"required": true,
|
||||||
|
"items": {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"functions" : {
|
||||||
|
"type": "array",
|
||||||
|
"required" : true,
|
||||||
|
"items" : {
|
||||||
|
"properties" : {
|
||||||
|
"name": {
|
||||||
|
"type" : "string",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"line" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"required" : true,
|
||||||
|
"minimum" : 1
|
||||||
|
},
|
||||||
|
"protection" : {
|
||||||
|
"type" : "string",
|
||||||
|
"enum" : [
|
||||||
|
"private",
|
||||||
|
"public",
|
||||||
|
"protected",
|
||||||
|
"package",
|
||||||
|
"export",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"attributes" : {
|
||||||
|
"type" : "array",
|
||||||
|
"items" : { "type" : "string" },
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"constraint" : {
|
||||||
|
"type" : "string",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"templateParameters" : {
|
||||||
|
"type" : "array",
|
||||||
|
"items" : { "type" : "string" },
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"parameters" : {
|
||||||
|
"type" : "array",
|
||||||
|
"required" : true,
|
||||||
|
"items" : {
|
||||||
|
"properties" : {
|
||||||
|
"name": {
|
||||||
|
"type" : "string",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"line" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"required" : true,
|
||||||
|
"minimum" : 1
|
||||||
|
},
|
||||||
|
"attributes" : {
|
||||||
|
"type" : "array",
|
||||||
|
"items" : { "type" : "string" },
|
||||||
|
"required" : true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"returnType" : {
|
||||||
|
"type" : "string",
|
||||||
|
"required" : true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
248
tokenizer.d
248
tokenizer.d
|
@ -17,54 +17,70 @@ import std.stdio;
|
||||||
import langutils;
|
import langutils;
|
||||||
import codegen;
|
import codegen;
|
||||||
|
|
||||||
|
pure bool isNewline(R)(R range)
|
||||||
/**
|
|
||||||
* Increments endIndex until it indexes a non-whitespace character in
|
|
||||||
* inputString.
|
|
||||||
* Params:
|
|
||||||
* inputString = the source code to examine
|
|
||||||
* endIndex = an index into inputString
|
|
||||||
* lineNumber = the line number that corresponds to endIndex
|
|
||||||
* style = the code iteration style
|
|
||||||
* Returns: The whitespace, or null if style was CODE_ONLY
|
|
||||||
*/
|
|
||||||
pure nothrow string lexWhitespace(S)(S inputString, ref size_t endIndex,
|
|
||||||
ref uint lineNumber)
|
|
||||||
if (isSomeString!S)
|
|
||||||
{
|
{
|
||||||
immutable startIndex = endIndex;
|
return range.front == '\n' || range.front == '\r';
|
||||||
while (!isEoF(inputString, endIndex) && isWhite(inputString[endIndex]))
|
|
||||||
{
|
|
||||||
if (inputString[endIndex] == '\n')
|
|
||||||
lineNumber++;
|
|
||||||
++endIndex;
|
|
||||||
}
|
}
|
||||||
return inputString[startIndex .. endIndex];
|
|
||||||
|
pure bool isEoF(R)(R range)
|
||||||
|
{
|
||||||
|
return range.empty || range.front == 0 || range.front == 0x1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
char[] popNewline(R)(ref R range)
|
||||||
|
{
|
||||||
|
char[] chars;
|
||||||
|
if (range.front == '\r')
|
||||||
|
{
|
||||||
|
chars ~= range.front;
|
||||||
|
range.popFront();
|
||||||
|
}
|
||||||
|
if (range.front == '\n')
|
||||||
|
{
|
||||||
|
chars ~= range.front;
|
||||||
|
range.popFront();
|
||||||
|
}
|
||||||
|
return chars;
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
auto s = "\r\ntest";
|
||||||
|
assert (popNewline(s) == "\r\n");
|
||||||
|
assert (s == "test");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If inputString starts with #!, increments endIndex until it indexes the next line.
|
* Returns:
|
||||||
* Params:
|
|
||||||
* inputString = the source code to examine
|
|
||||||
* endIndex = an index into inputString
|
|
||||||
* lineNumber = the line number that corresponds to endIndex
|
|
||||||
* Returns: The script line, or null if this inputString doesn't start from script line
|
|
||||||
*/
|
*/
|
||||||
pure nothrow string lexScriptLine(S)(ref S inputString, ref size_t endIndex,
|
string lexWhitespace(R)(ref R range, ref uint lineNumber)
|
||||||
ref uint lineNumber) if (isSomeString!S)
|
|
||||||
{
|
{
|
||||||
auto startIndex = endIndex; // in current implementation endIndex is 0, but that could change (e.g., if BOM is not stripped from inputString)
|
auto app = appender!(char[])();
|
||||||
string result = null;
|
while (!isEoF(range) && isWhite(range.front))
|
||||||
if(inputString.length > 1 && inputString[0..2] == "#!") // safety check
|
{
|
||||||
|
if (isNewline(range))
|
||||||
{
|
{
|
||||||
endIndex = 2; // skip #!
|
|
||||||
while (!isEoF(inputString, endIndex) && inputString[endIndex] != '\n')
|
|
||||||
++endIndex;
|
|
||||||
|
|
||||||
result = inputString[startIndex..endIndex];
|
|
||||||
++lineNumber;
|
++lineNumber;
|
||||||
|
app.put(popNewline(range));
|
||||||
}
|
}
|
||||||
return result;
|
else
|
||||||
|
{
|
||||||
|
app.put(range.front);
|
||||||
|
range.popFront();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return to!string(app.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
import std.stdio;
|
||||||
|
uint lineNum = 1;
|
||||||
|
auto chars = " \n \r\n \tabcde";
|
||||||
|
auto r = lexWhitespace(chars, lineNum);
|
||||||
|
assert (r == " \n \r\n \t");
|
||||||
|
assert (chars == "abcde");
|
||||||
|
assert (lineNum == 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,52 +92,122 @@ pure nothrow string lexScriptLine(S)(ref S inputString, ref size_t endIndex,
|
||||||
* lineNumber = the line number that corresponds to endIndex
|
* lineNumber = the line number that corresponds to endIndex
|
||||||
* Returns: The comment
|
* Returns: The comment
|
||||||
*/
|
*/
|
||||||
pure nothrow string lexComment(S)(ref S inputString, ref size_t endIndex,
|
string lexComment(R)(ref R input, ref uint lineNumber)
|
||||||
ref uint lineNumber) if (isSomeString!S)
|
in
|
||||||
{
|
{
|
||||||
if (isEoF(inputString, endIndex))
|
assert (input.front == '/');
|
||||||
return "";
|
}
|
||||||
auto startIndex = endIndex - 1;
|
body
|
||||||
switch(inputString[endIndex])
|
{
|
||||||
|
auto app = appender!(char[])();
|
||||||
|
app.put(input.front);
|
||||||
|
input.popFront();
|
||||||
|
switch(input.front)
|
||||||
{
|
{
|
||||||
case '/':
|
case '/':
|
||||||
while (!isEoF(inputString, endIndex) && inputString[endIndex] != '\n')
|
while (!isEoF(input) && !isNewline(input))
|
||||||
{
|
{
|
||||||
if (inputString[endIndex] == '\n')
|
app.put(input.front);
|
||||||
++lineNumber;
|
input.popFront();
|
||||||
++endIndex;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '*':
|
case '*':
|
||||||
while (!isEoF(inputString, endIndex)
|
while (!isEoF(input))
|
||||||
&& !inputString[endIndex..$].startsWith("*/"))
|
|
||||||
{
|
{
|
||||||
if (inputString[endIndex] == '\n')
|
if (isNewline(input))
|
||||||
|
{
|
||||||
|
app.put(popNewline(input));
|
||||||
++lineNumber;
|
++lineNumber;
|
||||||
++endIndex;
|
|
||||||
}
|
}
|
||||||
endIndex += 2;
|
else if (input.front == '*')
|
||||||
|
{
|
||||||
|
app.put(input.front);
|
||||||
|
input.popFront();
|
||||||
|
if (input.front == '/')
|
||||||
|
{
|
||||||
|
app.put(input.front);
|
||||||
|
input.popFront();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.put(input.front);
|
||||||
|
input.popFront();
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case '+':
|
case '+':
|
||||||
++endIndex;
|
|
||||||
int depth = 1;
|
int depth = 1;
|
||||||
while (depth > 0 && !isEoF(inputString, endIndex))
|
while (depth > 0 && !isEoF(input))
|
||||||
{
|
{
|
||||||
if (inputString[endIndex] == '\n')
|
if (isNewline(input))
|
||||||
|
{
|
||||||
|
app.put(popNewline(input));
|
||||||
lineNumber++;
|
lineNumber++;
|
||||||
else if (inputString[endIndex..$].startsWith("+/"))
|
|
||||||
depth--;
|
|
||||||
else if (inputString[endIndex..$].startsWith("/+"))
|
|
||||||
depth++;
|
|
||||||
++endIndex;
|
|
||||||
}
|
}
|
||||||
if (!isEoF(inputString, endIndex))
|
else if (input.front == '+')
|
||||||
++endIndex;
|
{
|
||||||
|
app.put(input.front);
|
||||||
|
input.popFront();
|
||||||
|
if (input.front == '/')
|
||||||
|
{
|
||||||
|
app.put(input.front);
|
||||||
|
input.popFront();
|
||||||
|
--depth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (input.front == '/')
|
||||||
|
{
|
||||||
|
app.put(input.front);
|
||||||
|
input.popFront();
|
||||||
|
if (input.front == '+')
|
||||||
|
{
|
||||||
|
app.put(input.front);
|
||||||
|
input.popFront();
|
||||||
|
++depth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.put(input.front);
|
||||||
|
input.popFront();
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return inputString[startIndex..endIndex];
|
return to!string(app.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
uint lineNumber = 1;
|
||||||
|
auto chars = "//this is a comment\r\nthis is not";
|
||||||
|
auto comment = lexComment(chars, lineNumber);
|
||||||
|
assert (chars == "\r\nthis is not");
|
||||||
|
assert (comment == "//this is a comment");
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
uint lineNumber = 1;
|
||||||
|
auto chars = "/* this is a\n\tcomment\r\n */this is not";
|
||||||
|
auto comment = lexComment(chars, lineNumber);
|
||||||
|
assert (chars == "this is not");
|
||||||
|
assert (comment == "/* this is a\n\tcomment\r\n */");
|
||||||
|
assert (lineNumber == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
uint lineNumber = 1;
|
||||||
|
auto chars = "/+this is a /+c/+omm+/ent+/ \r\nthis+/ is not";
|
||||||
|
auto comment = lexComment(chars, lineNumber);
|
||||||
|
assert (chars == " is not");
|
||||||
|
assert (comment == "/+this is a /+c/+omm+/ent+/ \r\nthis+/");
|
||||||
|
assert (lineNumber == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -233,6 +319,9 @@ string lexDelimitedString(S)(ref S inputString, ref size_t endIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Fix this
|
||||||
|
*/
|
||||||
string lexTokenString(S)(ref S inputString, ref size_t endIndex, ref uint lineNumber)
|
string lexTokenString(S)(ref S inputString, ref size_t endIndex, ref uint lineNumber)
|
||||||
{
|
{
|
||||||
/+auto r = byDToken(range, IterationStyle.EVERYTHING);
|
/+auto r = byDToken(range, IterationStyle.EVERYTHING);
|
||||||
|
@ -562,6 +651,23 @@ nothrow void lexHex(S)(ref S inputString, ref size_t startIndex,
|
||||||
token.value = inputString[startIndex .. endIndex];
|
token.value = inputString[startIndex .. endIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
Token t;
|
||||||
|
size_t start, end;
|
||||||
|
start = 0;
|
||||||
|
end = 2;
|
||||||
|
lexHex!string("0x193abfq", start, end, t);
|
||||||
|
assert(t.value == "0x193abf", t.value);
|
||||||
|
assert(t.type == TokenType.IntLiteral);
|
||||||
|
|
||||||
|
start = 0;
|
||||||
|
end = 2;
|
||||||
|
lexHex!string("0x2130xabc", start, end, t);
|
||||||
|
assert(t.value == "0x2130");
|
||||||
|
assert(t.type == TokenType.IntLiteral);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns: true if ch marks the ending of one token and the beginning of
|
* Returns: true if ch marks the ending of one token and the beginning of
|
||||||
|
@ -595,6 +701,19 @@ enum IterationStyle
|
||||||
EVERYTHING
|
EVERYTHING
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TokenRange(R) if (isInputRange(R))
|
||||||
|
{
|
||||||
|
bool empty() const @property
|
||||||
|
{
|
||||||
|
return _empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
R range;
|
||||||
|
bool _empty;
|
||||||
|
}
|
||||||
|
|
||||||
Token[] tokenize(S)(S inputString, IterationStyle iterationStyle = IterationStyle.CODE_ONLY)
|
Token[] tokenize(S)(S inputString, IterationStyle iterationStyle = IterationStyle.CODE_ONLY)
|
||||||
if (isSomeString!S)
|
if (isSomeString!S)
|
||||||
{
|
{
|
||||||
|
@ -638,7 +757,6 @@ Token[] tokenize(S)(S inputString, IterationStyle iterationStyle = IterationStyl
|
||||||
|
|
||||||
outerSwitch: switch(inputString[endIndex])
|
outerSwitch: switch(inputString[endIndex])
|
||||||
{
|
{
|
||||||
// TODO: Re-enable code generator when DMD bug 7900 is fixed
|
|
||||||
mixin(generateCaseTrie(
|
mixin(generateCaseTrie(
|
||||||
"=", "TokenType.Assign",
|
"=", "TokenType.Assign",
|
||||||
"&", "TokenType.BitAnd",
|
"&", "TokenType.BitAnd",
|
||||||
|
|
1
types.d
1
types.d
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
// Copyright Brian Schott (Sir Alaran) 2012.
|
// Copyright Brian Schott (Sir Alaran) 2012.
|
||||||
// Distributed under the Boost Software License, Version 1.0.
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
|
Loading…
Reference in New Issue