[update] Implemented error handlings for Preprocess() function.

This commit is contained in:
falsycat 2019-10-03 00:00:00 +00:00
parent 45e80f7c4f
commit 6c34b1d24d
2 changed files with 75 additions and 19 deletions

View File

@ -0,0 +1,27 @@
/// License: MIT
module sjscript.exception;
import sjscript.Token;
///
class ScriptException : Exception {
public:
///
this(string msg, Token token, string file = __FILE__, size_t line = __LINE__) {
super(msg, file, line);
this.token = token;
}
const Token token;
}
///
class PreprocessException : ScriptException {
public:
mixin ExceptionConstructor;
}
private mixin template ExceptionConstructor() {
public:
this(string msg, Token token, string file = __FILE__, size_t line = __LINE__) {
super(msg, token, file, line);
}
}

View File

@ -4,12 +4,12 @@ module sjscript.preprocess;
import std.algorithm, import std.algorithm,
std.array, std.array,
std.conv, std.conv,
std.exception,
std.range, std.range,
std.range.primitives, std.range.primitives,
std.typecons; std.typecons;
import sjscript.Token; import sjscript.Token,
sjscript.exception;
/// ///
unittest { unittest {
@ -41,6 +41,12 @@ EOS";
EOS"; EOS";
assert(Tokenize(src).equal(["0", "1", "2"].cycle().take(9))); assert(Tokenize(src).equal(["0", "1", "2"].cycle().take(9)));
} }
{
enum src = q"EOS
$unknown_template
EOS";
assertThrown!PreprocessException(Tokenize(src).array);
}
} }
/// ///
@ -114,9 +120,10 @@ private struct Preprocessor(R)
if (nest == 0) break; if (nest == 0) break;
PopFrontWithoutPreprocess(); PopFrontWithoutPreprocess();
(!empty).enforce(); (!empty).enforce(
"all tokens are consumed when expecting close brace", result[][$-1]);
} }
(result[].length >= 2).enforce(); assert(result[].length >= 2);
return result[][1..$-1]; return result[][1..$-1];
} }
@ -139,39 +146,53 @@ private struct Preprocessor(R)
Preprocess(); Preprocess();
} }
void DefineTemplate() { void DefineTemplate() {
const command = front;
PopFrontWithoutPreprocess(); PopFrontWithoutPreprocess();
(!empty).enforce(); (!empty).enforce(
"all tokens are consumed when expecting template name", command);
const name = front; const name = front;
(name.type == TokenType.Ident).enforce(); (name.type == TokenType.Ident).enforce(
"found unexpected token when expecting template body", name);
PopFrontWithoutPreprocess(); PopFrontWithoutPreprocess();
(!empty).enforce(); (!empty).enforce(
"all tokens are consumed when expecting template body", command);
(front.type == TokenType.OpenBrace).enforce(); (front.type == TokenType.OpenBrace).enforce(
"found unexpected token when expecting template body", name);
templates_[name.text] = PopFrontBlockWithoutPreprocess(); templates_[name.text] = PopFrontBlockWithoutPreprocess();
PopFrontWithoutPreprocess(); PopFrontWithoutPreprocess();
} }
void ExpandRepeat() { void ExpandRepeat() {
const command = front;
PopFrontWithoutPreprocess(); PopFrontWithoutPreprocess();
(!empty).enforce(); (!empty).enforce(
"all tokens are consumed when expecting counter name or count", command);
string counter_name; string counter_name;
Token counter_name_token;
if (front.type == TokenType.Ident) { if (front.type == TokenType.Ident) {
counter_name = front.text; counter_name_token = front;
counter_name = counter_name_token.text;
PopFrontWithoutPreprocess(); PopFrontWithoutPreprocess();
(!empty).enforce(); (!empty).enforce(
"all tokens are consumed when expecting count", command);
} }
if (counter_name != "") { if (counter_name != "") {
(!status_.map!"a.counter_name".canFind(counter_name)).enforce(); (!status_.map!"a.counter_name".canFind(counter_name) &&
(!templates_.keys.canFind(counter_name)).enforce(); !templates_.keys.canFind(counter_name)).
enforce("the counter name is duplicated", counter_name_token);
} }
(front.type == TokenType.Number).enforce(); (front.type == TokenType.Number).enforce(
"found unexpected token when expecting count", front);
const count = front.text.to!float.to!int; const count = front.text.to!float.to!int;
PopFrontWithoutPreprocess(); PopFrontWithoutPreprocess();
(!empty).enforce(); (!empty).enforce(
"all tokens are consumed when expecting repeat body", command);
(front.type == TokenType.OpenBrace).enforce(); (front.type == TokenType.OpenBrace).enforce(
"found unexpected token when expecting repeat body", front);
ExpansionState state; ExpansionState state;
state.tokens = PopFrontBlockWithoutPreprocess(); state.tokens = PopFrontBlockWithoutPreprocess();
state.counter_max = count.to!size_t; state.counter_max = count.to!size_t;
@ -182,13 +203,16 @@ private struct Preprocessor(R)
} }
void ExpandTemplate() { void ExpandTemplate() {
const name = front.text[1..$]; const name = front.text[1..$];
(name != "").enforce(); (name != "").
(!status_.map!"a.name".canFind(name)).enforce(); enforce("invalid template specification", front);
(!status_.map!"a.name".canFind(name)).
enforce("recursively template expansion", front);
Token[] body; Token[] body;
const counter = GetCounterValue(name); const counter = GetCounterValue(name);
if (counter.isNull) { if (counter.isNull) {
(name in templates_).enforce(); (name in templates_).
enforce("the template is unknown", front);
body = templates_[name]; body = templates_[name];
} else { } else {
body = [Token(counter.get.to!string, TokenType.Number)]; body = [Token(counter.get.to!string, TokenType.Number)];
@ -215,3 +239,8 @@ private struct Preprocessor(R)
ExpansionState[] status_; ExpansionState[] status_;
} }
private void enforce(T)(T val, string msg, lazy Token token,
string file = __FILE__, size_t line = __LINE__) {
if (!val) throw new PreprocessException(msg, token, file, line);
}