[add] Added Preprocess() function.
This commit is contained in:
parent
98240dfe29
commit
45e80f7c4f
217
sjscript/src/sjscript/preprocess.d
Normal file
217
sjscript/src/sjscript/preprocess.d
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
/// License: MIT
|
||||||
|
module sjscript.preprocess;
|
||||||
|
|
||||||
|
import std.algorithm,
|
||||||
|
std.array,
|
||||||
|
std.conv,
|
||||||
|
std.exception,
|
||||||
|
std.range,
|
||||||
|
std.range.primitives,
|
||||||
|
std.typecons;
|
||||||
|
|
||||||
|
import sjscript.Token;
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest {
|
||||||
|
import std;
|
||||||
|
import dast.tokenize;
|
||||||
|
|
||||||
|
auto Tokenize(string src) {
|
||||||
|
return src.
|
||||||
|
Tokenize!TokenType().
|
||||||
|
filter!(x => x.type != TokenType.Whitespace).
|
||||||
|
Preprocess().
|
||||||
|
map!"a.text";
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
enum src = q"EOS
|
||||||
|
$define temp1 { $temp2 }
|
||||||
|
$define temp2 { hoge }
|
||||||
|
|
||||||
|
$temp1 $temp2
|
||||||
|
EOS";
|
||||||
|
assert(Tokenize(src).equal(["hoge", "hoge"]));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
enum src = q"EOS
|
||||||
|
$repeat i 3 {
|
||||||
|
$repeat j 3 { $j }
|
||||||
|
}
|
||||||
|
EOS";
|
||||||
|
assert(Tokenize(src).equal(["0", "1", "2"].cycle().take(9)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
auto Preprocess(R)(R tokenizer) {
|
||||||
|
auto p = Preprocessor!R(tokenizer);
|
||||||
|
p.Preprocess();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct Preprocessor(R)
|
||||||
|
if (isInputRange!R && is(ElementType!R == Token)) {
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
@property bool empty() {
|
||||||
|
return status_.length == 0 && tokens_.empty;
|
||||||
|
}
|
||||||
|
///
|
||||||
|
@property Token front() in (!empty) {
|
||||||
|
if (status_.length == 0) {
|
||||||
|
return tokens_.front;
|
||||||
|
}
|
||||||
|
const status = status_[$-1];
|
||||||
|
return status.tokens[status.index];
|
||||||
|
}
|
||||||
|
///
|
||||||
|
void popFront() in (!empty) {
|
||||||
|
PopFrontWithoutPreprocess();
|
||||||
|
Preprocess();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static struct ExpansionState {
|
||||||
|
public:
|
||||||
|
size_t index;
|
||||||
|
Token[] tokens;
|
||||||
|
|
||||||
|
size_t counter_max;
|
||||||
|
size_t counter;
|
||||||
|
|
||||||
|
string name;
|
||||||
|
string counter_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PopStatus() {
|
||||||
|
if (status_.length == 0) return;
|
||||||
|
auto status = &status_[$-1];
|
||||||
|
if (++status.index >= status.tokens.length) {
|
||||||
|
status.index = 0;
|
||||||
|
if (++status.counter >= status.counter_max) {
|
||||||
|
status_ = status_[0..$-1];
|
||||||
|
PopStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void PopFrontWithoutPreprocess() in (!empty) {
|
||||||
|
PopStatus();
|
||||||
|
if (status_.length == 0) {
|
||||||
|
return tokens_.popFront();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Token[] PopFrontBlockWithoutPreprocess()
|
||||||
|
in (!empty && front.type == TokenType.OpenBrace) {
|
||||||
|
auto result = appender!(Token[]);
|
||||||
|
|
||||||
|
size_t nest;
|
||||||
|
while (true) {
|
||||||
|
const front = front;
|
||||||
|
if (front.type == TokenType.OpenBrace) ++nest;
|
||||||
|
if (front.type == TokenType.CloseBrace) --nest;
|
||||||
|
result ~= front;
|
||||||
|
|
||||||
|
if (nest == 0) break;
|
||||||
|
PopFrontWithoutPreprocess();
|
||||||
|
(!empty).enforce();
|
||||||
|
}
|
||||||
|
(result[].length >= 2).enforce();
|
||||||
|
return result[][1..$-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preprocess() {
|
||||||
|
if (empty || front.type != TokenType.PreprocessCommand) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (front.text) {
|
||||||
|
case "$define":
|
||||||
|
DefineTemplate();
|
||||||
|
break;
|
||||||
|
case "$repeat":
|
||||||
|
ExpandRepeat();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ExpandTemplate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Preprocess();
|
||||||
|
}
|
||||||
|
void DefineTemplate() {
|
||||||
|
PopFrontWithoutPreprocess();
|
||||||
|
(!empty).enforce();
|
||||||
|
|
||||||
|
const name = front;
|
||||||
|
(name.type == TokenType.Ident).enforce();
|
||||||
|
PopFrontWithoutPreprocess();
|
||||||
|
(!empty).enforce();
|
||||||
|
|
||||||
|
(front.type == TokenType.OpenBrace).enforce();
|
||||||
|
templates_[name.text] = PopFrontBlockWithoutPreprocess();
|
||||||
|
PopFrontWithoutPreprocess();
|
||||||
|
}
|
||||||
|
void ExpandRepeat() {
|
||||||
|
PopFrontWithoutPreprocess();
|
||||||
|
(!empty).enforce();
|
||||||
|
|
||||||
|
string counter_name;
|
||||||
|
if (front.type == TokenType.Ident) {
|
||||||
|
counter_name = front.text;
|
||||||
|
PopFrontWithoutPreprocess();
|
||||||
|
(!empty).enforce();
|
||||||
|
}
|
||||||
|
if (counter_name != "") {
|
||||||
|
(!status_.map!"a.counter_name".canFind(counter_name)).enforce();
|
||||||
|
(!templates_.keys.canFind(counter_name)).enforce();
|
||||||
|
}
|
||||||
|
|
||||||
|
(front.type == TokenType.Number).enforce();
|
||||||
|
const count = front.text.to!float.to!int;
|
||||||
|
PopFrontWithoutPreprocess();
|
||||||
|
(!empty).enforce();
|
||||||
|
|
||||||
|
(front.type == TokenType.OpenBrace).enforce();
|
||||||
|
ExpansionState state;
|
||||||
|
state.tokens = PopFrontBlockWithoutPreprocess();
|
||||||
|
state.counter_max = count.to!size_t;
|
||||||
|
state.counter_name = counter_name;
|
||||||
|
|
||||||
|
if (state.counter_max == 0 || state.tokens.length == 0) return;
|
||||||
|
status_ ~= state;
|
||||||
|
}
|
||||||
|
void ExpandTemplate() {
|
||||||
|
const name = front.text[1..$];
|
||||||
|
(name != "").enforce();
|
||||||
|
(!status_.map!"a.name".canFind(name)).enforce();
|
||||||
|
|
||||||
|
Token[] body;
|
||||||
|
const counter = GetCounterValue(name);
|
||||||
|
if (counter.isNull) {
|
||||||
|
(name in templates_).enforce();
|
||||||
|
body = templates_[name];
|
||||||
|
} else {
|
||||||
|
body = [Token(counter.get.to!string, TokenType.Number)];
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpansionState state;
|
||||||
|
state.tokens = body;
|
||||||
|
state.counter_max = 1;
|
||||||
|
state.name = name;
|
||||||
|
|
||||||
|
if (state.tokens.length == 0) return;
|
||||||
|
status_ ~= state;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nullable!size_t GetCounterValue(string name) {
|
||||||
|
auto found_states = status_.retro.find!"a.counter_name == b"(name);
|
||||||
|
if (found_states.empty) return Nullable!size_t.init;
|
||||||
|
return found_states.front.counter.nullable;
|
||||||
|
}
|
||||||
|
|
||||||
|
R tokens_;
|
||||||
|
|
||||||
|
Token[][string] templates_;
|
||||||
|
|
||||||
|
ExpansionState[] status_;
|
||||||
|
}
|
Reference in New Issue
Block a user