rewirte compileblockmain

master
Daniel Kolesa 2021-04-10 05:32:07 +02:00
parent eac137e3c8
commit e5cf9452f2
2 changed files with 109 additions and 77 deletions

View File

@ -593,7 +593,10 @@ lookup_id:
lookup_invalid(gs, ltype); lookup_invalid(gs, ltype);
} }
/* parses @... macro substitutions within block strings */ /* parses @... macro substitutions within block strings
*
* only called from within parse_blockarg
*/
bool parser_state::parse_subblock() { bool parser_state::parse_subblock() {
charbuf lookup{ts}; charbuf lookup{ts};
switch (current()) { switch (current()) {
@ -646,130 +649,158 @@ lookup_id:
return true; return true;
} }
static void compileblockmain(parser_state &gs, int wordtype) { /* [...] argument parser */
char const *start = gs.source; void parser_state::parse_blockarg(int ltype) {
size_t curline = gs.current_line; char const *start = source;
int concs = 0; /* current bracket level */
for (int brak = 1; brak;) { std::size_t blevel = 1;
switch (gs.skip_until("@\"/[]")) { std::size_t curline = current_line;
/* number of strings to concatenate */
std::size_t concs = 0;
while (blevel > 0) {
/* skip until a significant character */
switch (skip_until("@\"/[]")) {
/* EOS */
case '\0': case '\0':
throw error{*gs.ts.pstate, "missing \"]\""}; throw error{*ts.pstate, "missing \"]\""};
return; /* inner string, parse through it */
case '\"': case '\"':
gs.get_str(); get_str();
break; break;
/* possibly a comment */
case '/': case '/':
gs.next_char(); next_char();
if (gs.current() == '/') { if (current() == '/') {
gs.skip_until('\n'); /* yup, just go until we reach a newline */
skip_until('\n');
} }
break; break;
/* keep it nice and balanced */
case '[': case '[':
gs.next_char(); next_char();
brak++; ++blevel;
break; break;
case ']': case ']':
gs.next_char(); next_char();
brak--; --blevel;
break; break;
/* macro substitution */
case '@': { case '@': {
char const *esc = gs.source; char const *end = source;
int level = 0; std::size_t alevel = 0;
while (gs.current() == '@') { while (current() == '@') {
++level; ++alevel;
gs.next_char(); next_char();
} }
if (brak > level) { if (blevel > alevel) {
continue; /* deeper block level than macro level
} else if (brak < level) { * we can't substitute at this point so leave it alone
throw error{*gs.ts.pstate, "too many @s"}; */
return; continue;
} }
gs.gs.gen_val_block(std::string_view{start, esc}); if (blevel < alevel) {
concs++; /* shallower block level than macro level
if (gs.parse_subblock()) { * this is an error, we can't substitute this
concs++; */
throw error{*ts.pstate, "too many @s"};
}
/* generate a block string for everything until now */
if (start != end) {
gs.gen_val_block(std::string_view{start, end});
++concs;
}
if (parse_subblock()) {
++concs;
} }
if (concs) { if (concs) {
start = gs.source; start = source;
curline = gs.current_line; curline = current_line;
} }
break; break;
} }
/* actually unreachable, we handle all incl. EOS */
default: default:
gs.next_char(); next_char();
break; break;
} }
} }
if (gs.source - 1 > start) { if ((source - 1) <= start) {
if (!concs) { /* possibly empty */
switch (wordtype) { goto done;
case VAL_POP: }
return; /* non-empty */
case VAL_CODE: if (!concs) {
case VAL_COND: { /* one contiguous string */
auto ret = gs.gs.gen_block(std::string_view{ switch (ltype) {
start, gs.send case VAL_POP:
}, curline, VAL_NULL, ']'); /* ignore */
gs.source = ret.second.data(); return;
gs.send = ret.second.data() + ret.second.size(); case VAL_CODE:
gs.current_line = ret.first; case VAL_COND: {
return; /* compile */
} auto ret = gs.gen_block(
case VAL_IDENT: std::string_view{start, send}, curline, VAL_NULL, ']'
gs.gs.gen_val_ident(std::string_view{ );
start, std::size_t((gs.source - 1) - start) source = ret.second.data();
}); send = source + ret.second.size();
return; current_line = ret.first;
return;
} }
} case VAL_IDENT:
gs.gs.gen_val_block(std::string_view{start, gs.source - 1}); gs.gen_val_ident(std::string_view{start, source - 1});
if (concs > 1) { return;
concs++; default:
gs.gen_val_block(std::string_view{start, source - 1});
goto done;
} }
} }
gs.gs.gen_concat(concs, false, wordtype); gs.gen_val_block(std::string_view{start, source - 1});
switch (wordtype) { /* concat the pieces */
gs.gen_concat(++concs, false, ltype);
done:
bool got_val = (concs || ((source - 1) > start));
/* handle the result */
switch (ltype) {
case VAL_POP: case VAL_POP:
if (concs || gs.source - 1 > start) { if (got_val) {
gs.gs.gen_pop(); gs.gen_pop();
} }
break; break;
case VAL_COND: case VAL_COND:
if (!concs && gs.source - 1 <= start) { if (!got_val) {
gs.gs.gen_val_null(); gs.gen_val_null();
} else { } else {
gs.gs.gen_compile(true); gs.gen_compile(true);
} }
break; break;
case VAL_CODE: case VAL_CODE:
if (!concs && gs.source - 1 <= start) { if (!got_val) {
gs.gs.gen_block(); gs.gen_block();
} else { } else {
gs.gs.gen_compile(); gs.gen_compile();
} }
break; break;
case VAL_IDENT: case VAL_IDENT:
if (!concs && gs.source - 1 <= start) { if (!got_val) {
gs.gs.gen_val_ident(); gs.gen_val_ident();
} else { } else {
gs.gs.gen_ident_lookup(); gs.gen_ident_lookup();
} }
break; break;
case VAL_STRING: case VAL_STRING:
case VAL_NULL: case VAL_NULL:
case VAL_ANY: case VAL_ANY:
case VAL_WORD: case VAL_WORD:
if (!concs && gs.source - 1 <= start) { if (!got_val) {
gs.gs.gen_val_string(); gs.gen_val_string();
} }
break; break;
default: default:
if (!concs) { if (!concs) {
if (gs.source - 1 <= start) { if ((source - 1) <= start) {
gs.gs.gen_val(wordtype); gs.gen_val(ltype);
} else { } else {
gs.gs.gen_force(wordtype); gs.gen_force(ltype);
} }
} }
break; break;
@ -852,7 +883,7 @@ static bool compilearg(
} }
case '[': case '[':
gs.next_char(); gs.next_char();
compileblockmain(gs, wordtype); gs.parse_blockarg(wordtype);
return true; return true;
default: default:
switch (wordtype) { switch (wordtype) {

View File

@ -70,6 +70,7 @@ struct parser_state {
void parse_lookup(int ltype); void parse_lookup(int ltype);
bool parse_subblock(); bool parse_subblock();
void parse_blockarg(int ltype);
}; };
} /* namespace cubescript */ } /* namespace cubescript */