mirror of https://github.com/buggins/dlangui.git
support for SDL file format in addition to JSON - for dlangide dub.sdl support
This commit is contained in:
parent
391a9d8b38
commit
c369ee182f
|
@ -52,12 +52,19 @@ enum SettingType {
|
||||||
NULL
|
NULL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// settings file format
|
||||||
|
enum SettingsFileFormat {
|
||||||
|
JSON,
|
||||||
|
SDL,
|
||||||
|
}
|
||||||
|
|
||||||
/// Settings object whith file information
|
/// Settings object whith file information
|
||||||
class SettingsFile {
|
class SettingsFile {
|
||||||
protected Setting _setting;
|
protected Setting _setting;
|
||||||
protected string _filename;
|
protected string _filename;
|
||||||
protected SysTime _lastModificationTime;
|
protected SysTime _lastModificationTime;
|
||||||
protected bool _loaded;
|
protected bool _loaded;
|
||||||
|
protected SettingsFileFormat _format = SettingsFileFormat.JSON;
|
||||||
|
|
||||||
@property Setting setting() { return _setting; }
|
@property Setting setting() { return _setting; }
|
||||||
@property Setting copySettings() {
|
@property Setting copySettings() {
|
||||||
|
@ -1576,9 +1583,11 @@ final class Setting {
|
||||||
private static struct JsonParser {
|
private static struct JsonParser {
|
||||||
string json;
|
string json;
|
||||||
int pos;
|
int pos;
|
||||||
void initialize(string s) {
|
bool allowEol; // for SDL parsing where EOLs are meaningful
|
||||||
|
void initialize(string s, bool allowEol) {
|
||||||
json = s;
|
json = s;
|
||||||
pos = 0;
|
pos = 0;
|
||||||
|
this.allowEol = allowEol;
|
||||||
}
|
}
|
||||||
/// returns current char
|
/// returns current char
|
||||||
@property char peek() {
|
@property char peek() {
|
||||||
|
@ -1641,10 +1650,42 @@ final class Setting {
|
||||||
static import std.ascii;
|
static import std.ascii;
|
||||||
return std.ascii.isAlphaNum(ch) || ch == '_';
|
return std.ascii.isAlphaNum(ch) || ch == '_';
|
||||||
}
|
}
|
||||||
|
/// skip spaces and comments, return next available character
|
||||||
@property char skipSpaces() {
|
@property char skipSpaces() {
|
||||||
static import std.ascii;
|
static import std.ascii;
|
||||||
for(;pos < json.length;pos++) {
|
for(;pos < json.length;pos++) {
|
||||||
char ch = json[pos];
|
char ch = json[pos];
|
||||||
|
char nextch = pos + 1 < json.length ? json[pos + 1] : 0;
|
||||||
|
if (allowEol && ch == '\n')
|
||||||
|
break;
|
||||||
|
if (ch == '#' || (ch == '/' && nextch == '/') || (ch == '-' && nextch == '-')) {
|
||||||
|
// skip one line comment // or # or --
|
||||||
|
pos++;
|
||||||
|
for(;pos < json.length;pos++) {
|
||||||
|
ch = json[pos];
|
||||||
|
if (ch == '\n')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (allowEol && ch == '\n')
|
||||||
|
break;
|
||||||
|
continue;
|
||||||
|
} else if (ch == '/' && nextch == '*') {
|
||||||
|
// skip multiline /* */ comment
|
||||||
|
pos += 2;
|
||||||
|
for(;pos < json.length;pos++) {
|
||||||
|
ch = json[pos];
|
||||||
|
nextch = pos + 1 < json.length ? json[pos + 1] : 0;
|
||||||
|
if (ch == '*' && nextch == '/') {
|
||||||
|
pos += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (ch == '\\' && nextch == '\n') {
|
||||||
|
// continue to next line
|
||||||
|
pos += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!std.ascii.isWhite(ch))
|
if (!std.ascii.isWhite(ch))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1669,17 +1710,18 @@ final class Setting {
|
||||||
@property string parseString() {
|
@property string parseString() {
|
||||||
char[] res;
|
char[] res;
|
||||||
char ch = peek;
|
char ch = peek;
|
||||||
if (ch != '\"')
|
char quoteChar = ch;
|
||||||
|
if (ch != '\"' && ch != '`')
|
||||||
error("cannot parse string");
|
error("cannot parse string");
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ch = nextChar;
|
ch = nextChar;
|
||||||
if (!ch)
|
if (!ch)
|
||||||
error("unexpected end of file while parsing string");
|
error("unexpected end of file while parsing string");
|
||||||
if (ch == '\"') {
|
if (ch == quoteChar) {
|
||||||
nextChar;
|
nextChar;
|
||||||
return cast(string)res;
|
return cast(string)res;
|
||||||
}
|
}
|
||||||
if (ch == '\\') {
|
if (ch == '\\' && quoteChar != '`') {
|
||||||
// escape sequence
|
// escape sequence
|
||||||
ch = nextChar;
|
ch = nextChar;
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
|
@ -1718,7 +1760,7 @@ final class Setting {
|
||||||
}
|
}
|
||||||
@property string parseIdent() {
|
@property string parseIdent() {
|
||||||
char ch = peek;
|
char ch = peek;
|
||||||
if (ch == '\"') {
|
if (ch == '\"' || ch == '`') {
|
||||||
return parseString;
|
return parseString;
|
||||||
}
|
}
|
||||||
char[] res;
|
char[] res;
|
||||||
|
@ -1733,7 +1775,7 @@ final class Setting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
error("cannot parse string");
|
error("cannot parse ident");
|
||||||
return cast(string)res;
|
return cast(string)res;
|
||||||
}
|
}
|
||||||
bool parseKeyword(string ident) {
|
bool parseKeyword(string ident) {
|
||||||
|
@ -1898,13 +1940,349 @@ final class Setting {
|
||||||
void parseJSON(string s) {
|
void parseJSON(string s) {
|
||||||
clear(SettingType.NULL);
|
clear(SettingType.NULL);
|
||||||
JsonParser parser;
|
JsonParser parser;
|
||||||
parser.initialize(s);
|
parser.initialize(convertEols(s), false);
|
||||||
parseJSON(parser);
|
parseJSON(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// SDL identifiers to be converted to JSON array (name should be changed, with 's' suffix)
|
||||||
|
private static immutable (string[]) identsToConvertToArrays = [
|
||||||
|
"subPackage", // in JSON it's subPackages
|
||||||
|
"configuration", // in JSON it's configurations
|
||||||
|
"buildType", // in JSON it's buildTypes
|
||||||
|
];
|
||||||
|
|
||||||
|
/// SDL identifiers to be converted to JSON object (name should be changed, with 's' suffix)
|
||||||
|
private static immutable (string[]) identsToConvertToObjects = [
|
||||||
|
"dependency", // in JSON it's dependencies
|
||||||
|
"subConfiguration", // in JSON it's subConfigurations
|
||||||
|
];
|
||||||
|
|
||||||
|
/// SDL identifiers of JSON array w/o name conversion
|
||||||
|
private static immutable (string[]) arrayIdents = [
|
||||||
|
"authors",
|
||||||
|
"x:ddoxFilterArgs",
|
||||||
|
"sourcePaths",
|
||||||
|
"importPaths",
|
||||||
|
"buildOptions",
|
||||||
|
"libs",
|
||||||
|
"sourceFiles",
|
||||||
|
"buildRequirements",
|
||||||
|
"excludedSourceFiles",
|
||||||
|
"copyFiles",
|
||||||
|
"versions",
|
||||||
|
"debugVersions",
|
||||||
|
"stringImportPaths",
|
||||||
|
"preGenerateCommands",
|
||||||
|
"postGenerateCommands",
|
||||||
|
"preBuildCommands",
|
||||||
|
"postBuildCommands",
|
||||||
|
"dflags",
|
||||||
|
"lflags",
|
||||||
|
"platforms",
|
||||||
|
];
|
||||||
|
|
||||||
|
protected bool isArrayItemNameIdent(string ident) {
|
||||||
|
foreach(s; identsToConvertToArrays) {
|
||||||
|
if (ident == s)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool isObjectItemNameIdent(string ident) {
|
||||||
|
foreach(s; identsToConvertToObjects) {
|
||||||
|
if (ident == s)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool isArrayIdent(string ident) {
|
||||||
|
foreach(s; arrayIdents) {
|
||||||
|
if (ident == s)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipEol(ref JsonParser parser) {
|
||||||
|
char ch = parser.skipSpaces;
|
||||||
|
if (ch == 0)
|
||||||
|
return;
|
||||||
|
if (ch == '\n') {
|
||||||
|
parser.nextChar;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parser.error("end of line expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseSDLAttributes(ref JsonParser parser, bool ignorePlatformAttribute = true) {
|
||||||
|
string attrName;
|
||||||
|
Setting attrValue;
|
||||||
|
for (;;) {
|
||||||
|
char ch = parser.skipSpaces;
|
||||||
|
if (ch == 0)
|
||||||
|
return;
|
||||||
|
if (ch == '\n') {
|
||||||
|
parser.nextChar;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!JsonParser.isAlpha(ch))
|
||||||
|
parser.error("attr=value expected");
|
||||||
|
attrName = parser.parseIdent();
|
||||||
|
attrValue = new Setting();
|
||||||
|
ch = parser.skipSpaces;
|
||||||
|
if (ch != '=')
|
||||||
|
parser.error("= expected after " ~ attrName);
|
||||||
|
ch = parser.nextChar; // skip '='
|
||||||
|
ch = parser.skipSpaces;
|
||||||
|
if (ch == '\"' || ch == '`') {
|
||||||
|
// string value
|
||||||
|
string v = parser.parseString;
|
||||||
|
attrValue = v;
|
||||||
|
if (!ignorePlatformAttribute || attrName != "platform")
|
||||||
|
this[attrName] = attrValue;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (JsonParser.isAlpha(ch)) {
|
||||||
|
string v = parser.parseIdent;
|
||||||
|
if (v == "true" || v == "on") {
|
||||||
|
attrValue = true;
|
||||||
|
this[attrName] = attrValue;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (v == "false" || v == "off") {
|
||||||
|
attrValue = false;
|
||||||
|
this[attrName] = attrValue;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
parser.error("unexpected attribue value " ~ v);
|
||||||
|
}
|
||||||
|
parser.error("only string and boolean values supported for SDL attributes now");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// peek platform="value" from current line
|
||||||
|
private string peekSDLPlatformAttribute(ref JsonParser parser) {
|
||||||
|
string res = null;
|
||||||
|
int oldpos = parser.pos; // save position
|
||||||
|
for(;;) {
|
||||||
|
char ch = parser.skipSpaces;
|
||||||
|
if (ch == 0 || ch == '\n' || ch == '{' || ch == '}')
|
||||||
|
break;
|
||||||
|
if (parser.isAlpha(ch)) {
|
||||||
|
string ident = parser.parseIdent;
|
||||||
|
ch = parser.skipSpaces;
|
||||||
|
if (ch != '=')
|
||||||
|
continue;
|
||||||
|
parser.nextChar;
|
||||||
|
ch = parser.skipSpaces;
|
||||||
|
string attrvalue;
|
||||||
|
if (ch == '\"' || ch == '`')
|
||||||
|
attrvalue = parser.parseString;
|
||||||
|
else if (parser.isAlpha(ch))
|
||||||
|
attrvalue = parser.parseIdent;
|
||||||
|
if (ident == "platform") {
|
||||||
|
res = attrvalue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (ch == '\"' || ch == '`') {
|
||||||
|
string str = parser.parseString;
|
||||||
|
} else if (ch == '=') {
|
||||||
|
parser.nextChar;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parser.pos = oldpos; // restore position
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipPlatformAttribute(ref JsonParser parser) {
|
||||||
|
char ch = parser.skipSpaces;
|
||||||
|
int oldpos = parser.pos;
|
||||||
|
if (parser.isAlpha(ch)) {
|
||||||
|
string attrName = parser.parseIdent;
|
||||||
|
if (attrName == "platform") {
|
||||||
|
ch = parser.skipSpaces;
|
||||||
|
if (ch == '=') {
|
||||||
|
parser.nextChar;
|
||||||
|
ch = parser.skipSpaces;
|
||||||
|
string value = parser.parseString;
|
||||||
|
return; // skipped platform attribute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no changes
|
||||||
|
parser.pos = oldpos;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Setting parseSDL(ref JsonParser parser, bool insideCurly = false) {
|
||||||
|
//static import std.ascii;
|
||||||
|
for (;;) {
|
||||||
|
// looking for ident
|
||||||
|
char ch = parser.skipSpaces;
|
||||||
|
if (ch == 0)
|
||||||
|
break;
|
||||||
|
if (ch == '\n') {
|
||||||
|
parser.nextChar; // skip
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch == '}') {
|
||||||
|
if (!insideCurly)
|
||||||
|
parser.error("unexpected }");
|
||||||
|
parser.nextChar; // skip
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
string ident = parser.parseIdent();
|
||||||
|
if (!ident.length)
|
||||||
|
parser.error("identifier expected");
|
||||||
|
ch = parser.skipSpaces;
|
||||||
|
string platform = peekSDLPlatformAttribute(parser);
|
||||||
|
bool isArrayConvName = isArrayItemNameIdent(ident);
|
||||||
|
bool isObjectConvName= isObjectItemNameIdent(ident);
|
||||||
|
bool isArrayName = isArrayIdent(ident) || isArrayConvName;
|
||||||
|
if (isArrayConvName || isObjectConvName) {
|
||||||
|
import std.algorithm : endsWith;
|
||||||
|
if (ident.endsWith("y"))
|
||||||
|
ident = ident[0 .. $-1] ~ "ies"; // e.g. dependency->dependencies
|
||||||
|
else if (!ident.endsWith("s"))
|
||||||
|
ident = ident ~ "s"; // a.g. author->authors
|
||||||
|
}
|
||||||
|
if (platform.length)
|
||||||
|
ident = ident ~ "-" ~ platform;
|
||||||
|
Setting valueObj = this[ident]; // looking for existing object
|
||||||
|
if (!valueObj) { // create if not exist
|
||||||
|
valueObj = new Setting();
|
||||||
|
this[ident] = valueObj;
|
||||||
|
}
|
||||||
|
if (isArrayName) {
|
||||||
|
if (!valueObj.isArray) {
|
||||||
|
// convert to array
|
||||||
|
valueObj.clear(SettingType.ARRAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now we have identifier
|
||||||
|
if (ch == '\"' || ch == '`') {
|
||||||
|
string value = parser.parseString;
|
||||||
|
skipPlatformAttribute(parser);
|
||||||
|
ch = parser.skipSpaces;
|
||||||
|
if (ch == '{') {
|
||||||
|
/*
|
||||||
|
ident "name" {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
parser.nextChar; // skip {
|
||||||
|
Setting obj = isArrayName ? new Setting() : valueObj;
|
||||||
|
obj["name"] = value;
|
||||||
|
obj.parseSDL(parser, true);
|
||||||
|
if (isArrayName)
|
||||||
|
valueObj.array = valueObj.array ~ obj;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (JsonParser.isAlpha(ch)) {
|
||||||
|
// ident=value pairs after "name"
|
||||||
|
Setting obj = (isArrayName || isObjectConvName) ? new Setting() : valueObj;
|
||||||
|
if (!isObjectConvName)
|
||||||
|
obj["name"] = value;
|
||||||
|
obj.parseSDLAttributes(parser);
|
||||||
|
if (isArrayName)
|
||||||
|
valueObj.array = valueObj.array ~ obj;
|
||||||
|
else if (isObjectConvName)
|
||||||
|
valueObj[value] = obj;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isArrayName) {
|
||||||
|
Setting[] values = valueObj.array;
|
||||||
|
Setting svalue = new Setting();
|
||||||
|
svalue = value;
|
||||||
|
values ~= svalue;
|
||||||
|
for (;;) {
|
||||||
|
skipPlatformAttribute(parser);
|
||||||
|
ch = parser.skipSpaces;
|
||||||
|
if (ch == '\n' || ch == 0)
|
||||||
|
break;
|
||||||
|
if (ch == '\"' || ch == '`') {
|
||||||
|
value = parser.parseString;
|
||||||
|
svalue = new Setting();
|
||||||
|
svalue = value;
|
||||||
|
values ~= svalue;
|
||||||
|
} else
|
||||||
|
parser.error("array of strings expected");
|
||||||
|
}
|
||||||
|
valueObj.array = values;
|
||||||
|
} else {
|
||||||
|
if (isObjectConvName) {
|
||||||
|
string svalue = parser.parseString;
|
||||||
|
valueObj[value] = svalue;
|
||||||
|
} else {
|
||||||
|
valueObj = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skipPlatformAttribute(parser);
|
||||||
|
skipEol(parser);
|
||||||
|
continue;
|
||||||
|
} else if (ch == '{') {
|
||||||
|
// object
|
||||||
|
parser.nextChar; // skip {
|
||||||
|
if (isArrayName) {
|
||||||
|
Setting[] values = valueObj.array;
|
||||||
|
Setting item = new Setting();
|
||||||
|
item.clear(SettingType.OBJECT);
|
||||||
|
item.parseSDL(parser, true);
|
||||||
|
values ~= item;
|
||||||
|
valueObj.array = values;
|
||||||
|
} else {
|
||||||
|
valueObj.parseSDL(parser, true);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
parser.error("cannot parse SDL value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (insideCurly)
|
||||||
|
parser.error("} expected");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseSDL(string s) {
|
||||||
|
clear(SettingType.NULL);
|
||||||
|
JsonParser parser;
|
||||||
|
parser.initialize(convertEols(s), true);
|
||||||
|
parseSDL(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// convert CR LF, LF CR, LF, CR to '\n' eol format
|
||||||
|
static string convertEols(string src) {
|
||||||
|
char[] res;
|
||||||
|
res.assumeSafeAppend;
|
||||||
|
for (int i = 0; i < src.length; i++) {
|
||||||
|
char ch = src[i];
|
||||||
|
if (ch == '\r' || ch == '\n') {
|
||||||
|
char nextch = i + 1 < src.length ? src[i + 1] : 0;
|
||||||
|
if (nextch != ch && (ch == '\r' || ch == '\n')) {
|
||||||
|
// pair \r\n or \n\r
|
||||||
|
res ~= '\n';
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
// single \r or \n
|
||||||
|
res ~= '\n';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res ~= ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res.dup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// load from file; autodetect SDL format using ".sdl" and ".SDL" extension mask; returns true if loaded successfully
|
||||||
bool load(string filename) {
|
bool load(string filename) {
|
||||||
try {
|
try {
|
||||||
|
import std.algorithm : endsWith;
|
||||||
string s = readText(filename);
|
string s = readText(filename);
|
||||||
|
if (filename.endsWith(".sdl") || filename.endsWith(".SDL"))
|
||||||
|
parseSDL(s);
|
||||||
|
else
|
||||||
parseJSON(s);
|
parseJSON(s);
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
Loading…
Reference in New Issue