diff --git a/src/cs_gen.cc b/src/cs_gen.cc index c7e9542..83cb724 100644 --- a/src/cs_gen.cc +++ b/src/cs_gen.cc @@ -20,6 +20,10 @@ std::size_t gen_state::count() const { return code.size(); } +std::uint32_t gen_state::peek(std::size_t idx) const { + return code[idx]; +} + bcode_ref gen_state::steal_ref() { auto *cp = bcode_alloc(ts.istate, code.size()); std::memcpy(cp, code.data(), code.size() * sizeof(std::uint32_t)); @@ -50,6 +54,71 @@ void gen_state::gen_not(int ltype) { code.push_back(BC_INST_NOT | ret_code(ltype)); } +bool gen_state::gen_if(std::size_t tpos, std::size_t fpos, int ltype) { + auto inst1 = code[tpos]; + auto op1 = inst1 & ~BC_INST_RET_MASK; + auto tlen = std::uint32_t(fpos - tpos - 1); + if (!fpos) { + if (is_block(tpos, fpos)) { + code[tpos] = (tlen << 8) | BC_INST_JUMP_B | BC_INST_FLAG_FALSE; + code[tpos + 1] = BC_INST_ENTER_RESULT; + code[tpos + tlen] = ( + code[tpos + tlen] & ~BC_INST_RET_MASK + ) | ret_code(ltype); + return true; + } + gen_block(); + } else { + auto inst2 = code[fpos]; + auto flen = std::uint32_t(count() - fpos - 1); + if (is_block(fpos)) { + if (is_block(tpos, fpos)) { + code[tpos] = (std::uint32_t(fpos - tpos) << 8) + | BC_INST_JUMP_B | BC_INST_FLAG_FALSE; + code[tpos + 1] = BC_INST_ENTER_RESULT; + code[tpos + tlen] = ( + code[tpos + tlen] & ~BC_INST_RET_MASK + ) | ret_code(ltype); + code[fpos] = (flen << 8) | BC_INST_JUMP; + code[fpos + 1] = BC_INST_ENTER_RESULT; + code[fpos + flen] = ( + code[fpos + flen] & ~BC_INST_RET_MASK + ) | ret_code(ltype); + return true; + } else if (op1 == BC_INST_EMPTY) { + code[tpos] = BC_INST_NULL | (inst2 & BC_INST_RET_MASK); + code[fpos] = (flen << 8) | BC_INST_JUMP_B | BC_INST_FLAG_TRUE; + code[fpos + 1] = BC_INST_ENTER_RESULT; + code[fpos + flen] = ( + code[fpos + flen] & ~BC_INST_RET_MASK + ) | ret_code(ltype); + return true; + } + } + } + return false; +} + +void gen_state::gen_and_or(bool is_or, std::size_t start, int ltype) { + std::uint32_t op; + if (is_or) { + op = (BC_INST_JUMP_RESULT | BC_INST_FLAG_TRUE); + } else { + op = (BC_INST_JUMP_RESULT | BC_INST_FLAG_FALSE); + } + code.push_back(op); + std::size_t end = count(); + while ((start + 1) < end) { + uint32_t len = code[start] >> 8; + code[start] = std::uint32_t((end - start - 1) << 8) | op; + code[start + 1] = BC_INST_ENTER; + code[start + len] = ( + code[start + len] & ~BC_INST_RET_MASK + ) | ret_code(ltype); + start += len + 1; + } +} + void gen_state::gen_val_null() { code.push_back(BC_INST_VAL_INT | BC_RET_NULL); } @@ -392,6 +461,15 @@ void gen_state::gen_main_float(float_type v) { code.push_back(BC_INST_EXIT); } +bool gen_state::is_block(std::size_t idx, std::size_t epos) const { + if (!epos) { + epos = count(); + } + return ((code[idx] & ~BC_INST_RET_MASK) == ( + BC_INST_BLOCK | (std::uint32_t(epos - idx - 1) << 8) + )); +} + void gen_state::gen_block() { code.push_back(BC_INST_EMPTY); } diff --git a/src/cs_gen.hh b/src/cs_gen.hh index 326a29d..0717191 100644 --- a/src/cs_gen.hh +++ b/src/cs_gen.hh @@ -15,7 +15,6 @@ namespace cubescript { struct gen_state { thread_state &ts; - valbuf code; gen_state() = delete; gen_state(thread_state &tsr): @@ -23,6 +22,7 @@ struct gen_state { {} std::size_t count() const; + std::uint32_t peek(std::size_t idx) const; bcode_ref steal_ref(); @@ -33,6 +33,8 @@ struct gen_state { void gen_force(int ltype); void gen_not(int ltype = 0); + bool gen_if(std::size_t tpos, std::size_t fpos, int ltype = 0); + void gen_and_or(bool is_or, std::size_t start, int ltype = 0); void gen_val_null(); void gen_result_null(int ltype = 0); @@ -92,11 +94,16 @@ struct gen_state { void gen_main_integer(integer_type v); void gen_main_float(float_type v); + bool is_block(std::size_t idx, std::size_t epos = 0) const; + void gen_block(); std::pair gen_block( std::string_view v, std::size_t line, int ret_type = BC_RET_NULL, int term = '\0' ); + +private: + valbuf code; }; } /* namespace cubescript */ diff --git a/src/cs_parser.cc b/src/cs_parser.cc index cfc97fe..df8bcd5 100644 --- a/src/cs_parser.cc +++ b/src/cs_parser.cc @@ -1137,62 +1137,28 @@ static void compile_if( parser_state &gs, ident *id, bool &more, int rettype ) { if (more) { + /* condition */ more = compilearg(gs, VAL_ANY); } if (!more) { + /* no condition: expr is nothing */ gs.gs.gen_result_null(rettype); } else { - std::size_t start1 = gs.gs.code.size(); + auto tpos = gs.gs.count(); + /* true block */ more = compilearg(gs, VAL_CODE); if (!more) { + /* we only had condition: expr is nothing */ gs.gs.gen_pop(); gs.gs.gen_result_null(rettype); } else { - std::size_t start2 = gs.gs.code.size(); + auto fpos = gs.gs.count(); + /* false block */ more = compilearg(gs, VAL_CODE); - std::uint32_t inst1 = gs.gs.code[start1]; - std::uint32_t op1 = inst1 & ~BC_INST_RET_MASK; - auto len1 = std::uint32_t(start2 - (start1 + 1)); - if (!more) { - if (op1 == (BC_INST_BLOCK | (len1 << 8))) { - gs.gs.code[start1] = (len1 << 8) | BC_INST_JUMP_B | BC_INST_FLAG_FALSE; - gs.gs.code[start1 + 1] = BC_INST_ENTER_RESULT; - gs.gs.code[start1 + len1] = ( - gs.gs.code[start1 + len1] & ~BC_INST_RET_MASK - ) | ret_code(rettype); - return; - } - gs.gs.gen_block(); - } else { - std::uint32_t inst2 = gs.gs.code[start2]; - std::uint32_t op2 = inst2 & ~BC_INST_RET_MASK; - auto len2 = std::uint32_t(gs.gs.code.size() - (start2 + 1)); - if (op2 == (BC_INST_BLOCK | (len2 << 8))) { - if (op1 == (BC_INST_BLOCK | (len1 << 8))) { - gs.gs.code[start1] = (std::uint32_t(start2 - start1) << 8) - | BC_INST_JUMP_B | BC_INST_FLAG_FALSE; - gs.gs.code[start1 + 1] = BC_INST_ENTER_RESULT; - gs.gs.code[start1 + len1] = ( - gs.gs.code[start1 + len1] & ~BC_INST_RET_MASK - ) | ret_code(rettype); - gs.gs.code[start2] = (len2 << 8) | BC_INST_JUMP; - gs.gs.code[start2 + 1] = BC_INST_ENTER_RESULT; - gs.gs.code[start2 + len2] = ( - gs.gs.code[start2 + len2] & ~BC_INST_RET_MASK - ) | ret_code(rettype); - return; - } else if (op1 == (BC_INST_EMPTY | (len1 << 8))) { - gs.gs.code[start1] = BC_INST_NULL | (inst2 & BC_INST_RET_MASK); - gs.gs.code[start2] = (len2 << 8) | BC_INST_JUMP_B | BC_INST_FLAG_TRUE; - gs.gs.code[start2 + 1] = BC_INST_ENTER_RESULT; - gs.gs.code[start2 + len2] = ( - gs.gs.code[start2 + len2] & ~BC_INST_RET_MASK - ) | ret_code(rettype); - return; - } - } + if (!gs.gs.gen_if(tpos, more ? fpos : 0)) { + /* can't fully compile: use a call */ + gs.gs.gen_command_call(*id, BC_INST_COM, rettype); } - gs.gs.code.push_back(BC_INST_COM | ret_code(rettype) | (id->get_index() << 8)); } } } @@ -1202,9 +1168,11 @@ static void compile_and_or( ) { std::uint32_t numargs = 0; if (more) { + /* first */ more = compilearg(gs, VAL_COND); } if (!more) { + /* no first: generate true or false */ if (ident_p{*id}.impl().p_type == ID_AND) { gs.gs.gen_result_true(rettype); } else { @@ -1212,47 +1180,33 @@ static void compile_and_or( } } else { numargs++; - std::size_t start = gs.gs.code.size(), end = start; + std::size_t start = gs.gs.count(), end = start; for (;;) { + /* keep going as long as we only get blocks */ more = compilearg(gs, VAL_COND); if (!more) { break; } numargs++; - if ((gs.gs.code[end] & ~BC_INST_RET_MASK) != ( - BC_INST_BLOCK | (uint32_t(gs.gs.code.size() - (end + 1)) << 8) - )) { + if (!gs.gs.is_block(end)) { break; } - end = gs.gs.code.size(); + end = gs.gs.count(); } if (more) { + /* last parsed thing was not a block, fall back to call */ for (;;) { + /* but first, parse out the remainder of values */ more = compilearg(gs, VAL_COND); if (!more) { break; } numargs++; } - gs.gs.code.push_back( - BC_INST_COM_V | ret_code(rettype) | (id->get_index() << 8) - ); - gs.gs.code.push_back(numargs); + gs.gs.gen_command_call(*id, BC_INST_COM_V, rettype, numargs); } else { - std::uint32_t op = (ident_p{*id}.impl().p_type == ID_AND) - ? (BC_INST_JUMP_RESULT | BC_INST_FLAG_FALSE) - : (BC_INST_JUMP_RESULT | BC_INST_FLAG_TRUE); - gs.gs.code.push_back(op); - end = gs.gs.code.size(); - while ((start + 1) < end) { - uint32_t len = gs.gs.code[start] >> 8; - gs.gs.code[start] = std::uint32_t((end - (start + 1)) << 8) | op; - gs.gs.code[start + 1] = BC_INST_ENTER; - gs.gs.code[start + len] = ( - gs.gs.code[start + len] & ~BC_INST_RET_MASK - ) | ret_code(rettype); - start += len + 1; - } + /* all blocks and nothing left */ + gs.gs.gen_and_or((ident_p{*id}.impl().p_type != ID_AND), start); } } }