[update] Implemented error handlings for Preprocess() function.
This commit is contained in:
parent
45e80f7c4f
commit
6c34b1d24d
27
sjscript/src/sjscript/exception.d
Normal file
27
sjscript/src/sjscript/exception.d
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user