#include #include "cs_vm.hh" #include "cs_util.hh" #include #include namespace cscript { ostd::string_range cs_gen_state::get_str() { size_t nl; ostd::string_range beg = source; source = util::parse_string(cs, source, nl); current_line += nl - 1; ostd::string_range ret = beg.slice(0, &source[0] - &beg[0]); return ret.slice(1, ret.size() - 1); } cs_string cs_gen_state::get_str_dup(bool unescape) { auto str = get_str(); auto app = ostd::appender(); if (unescape) { util::unescape_string(app, str); } else { app.get() = str; } return std::move(app.get()); } ostd::string_range cs_gen_state::read_macro_name() { auto op = source; char c = current(); if (!isalpha(c) && (c != '_')) { return nullptr; } for (; isalnum(c) || (c == '_'); c = current()) { next_char(); } return op.slice(0, &source[0] - &op[0]); } char cs_gen_state::skip_until(ostd::string_range chars) { char c = current(); while (c && ostd::find(chars, c).empty()) { next_char(); c = current(); } return c; } char cs_gen_state::skip_until(char cf) { char c = current(); while (c && (c != cf)) { next_char(); c = current(); } return c; } static bool cs_is_hspace(char c) { return (c == ' ') || (c == '\t') || (c == '\r'); } void cs_gen_state::skip_comments() { for (;;) { for (char c = current(); cs_is_hspace(c); c = current()) { next_char(); } if (current() == '\\') { char c = current(1); if ((c != '\r') && (c != '\n')) { throw cs_error(cs, "invalid line break"); } /* skip backslash */ next_char(); /* skip CR or LF */ next_char(); /* when CR, try also skipping LF; covers \r, \n, \r\n */ if ((c == '\r') && (current(1) == '\n')) { next_char(); } /* skip whitespace on new line */ continue; } if ((current() != '/') || (current(1) != '/')) { return; } while (current() != '\n') { next_char(); } } } ostd::string_range cs_gen_state::get_word() { auto beg = source; source = util::parse_word(cs, source); return beg.slice(0, &source[0] - &beg[0]); } static inline int cs_ret_code(int type, int def = 0) { if (type >= CsValAny) { return (type == CsValCstring) ? CsRetString : def; } return type << CsCodeRet; } static void compilestatements( cs_gen_state &gs, int rettype, int brak = '\0', int prevargs = 0 ); static inline std::pair compileblock( cs_gen_state &gs, ostd::string_range p, size_t line, int rettype = CsRetNull, int brak = '\0' ); void cs_gen_state::gen_int(ostd::string_range word) { gen_int(cs_parse_int(word)); } void cs_gen_state::gen_float(ostd::string_range word) { gen_float(cs_parse_float(word)); } void cs_gen_state::gen_value(int wordtype, ostd::string_range word, int line) { switch (wordtype) { case CsValCany: if (!word.empty()) { gen_str(word, true); } else { gen_null(); } break; case CsValCstring: gen_str(word, true); break; case CsValAny: if (!word.empty()) { gen_str(word); } else { gen_null(); } break; case CsValString: gen_str(word); break; case CsValFloat: gen_float(word); break; case CsValInt: gen_int(word); break; case CsValCond: if (!word.empty()) { compileblock(*this, word, line); } else { gen_null(); } break; case CsValCode: compileblock(*this, word, line); break; case CsValIdent: gen_ident(word); break; default: break; } } static inline void compileblock(cs_gen_state &gs) { gs.code.push_back(CsCodeEmpty); } static inline std::pair compileblock( cs_gen_state &gs, ostd::string_range p, size_t line, int rettype, int brak ) { size_t start = gs.code.size(); gs.code.push_back(CsCodeBlock); gs.code.push_back(CsCodeOffset | ((start + 2) << 8)); size_t retline = line; if (p) { ostd::string_range op = gs.source; size_t oldline = gs.current_line; gs.source = p; gs.current_line = line; compilestatements(gs, CsValAny, brak); p = gs.source; retline = gs.current_line; gs.source = op; gs.current_line = oldline; } if (gs.code.size() > start + 2) { gs.code.push_back(CsCodeExit | rettype); gs.code[start] |= uint32_t(gs.code.size() - (start + 1)) << 8; } else { gs.code.resize(start); gs.code.push_back(CsCodeEmpty | rettype); } return std::make_pair(p, retline); } static inline void compileunescapestr(cs_gen_state &gs, bool macro = false) { auto str = gs.get_str(); gs.code.push_back(macro ? CsCodeMacro : (CsCodeVal | CsRetString)); gs.code.reserve( gs.code.size() + str.size() / sizeof(uint32_t) + 1 ); size_t bufs = (gs.code.capacity() - gs.code.size()) * sizeof(uint32_t); char *buf = new char[bufs + 1]; auto writer = ostd::char_range(buf, buf + bufs); size_t len = util::unescape_string(ostd::counting_sink(writer), str).get_written(); memset(&buf[len], 0, sizeof(uint32_t) - len % sizeof(uint32_t)); gs.code.back() |= len << 8; uint32_t *ubuf = reinterpret_cast(buf); gs.code.insert(gs.code.end(), ubuf, ubuf + (len / sizeof(uint32_t) + 1)); delete[] buf; } static bool compilearg( cs_gen_state &gs, int wordtype, int prevargs = MaxResults, cs_string *word = nullptr ); static void compilelookup(cs_gen_state &gs, int ltype, int prevargs = MaxResults) { cs_string lookup; gs.next_char(); switch (gs.current()) { case '(': case '[': if (!compilearg(gs, CsValCstring, prevargs)) { goto invalid; } break; case '$': compilelookup(gs, CsValCstring, prevargs); break; case '\"': lookup = gs.get_str_dup(); goto lookupid; default: { lookup = gs.get_word(); if (lookup.empty()) goto invalid; lookupid: cs_ident *id = gs.cs.new_ident(lookup); if (id) { switch (id->get_type()) { case cs_ident_type::Ivar: gs.code.push_back( CsCodeIvar | cs_ret_code(ltype, CsRetInt) | (id->get_index() << 8) ); switch (ltype) { case CsValPop: gs.code.pop_back(); break; case CsValCode: gs.code.push_back(CsCodeCompile); break; case CsValIdent: gs.code.push_back(CsCodeIdentU); break; } return; case cs_ident_type::Fvar: gs.code.push_back( CsCodeFvar | cs_ret_code(ltype, CsRetFloat) | (id->get_index() << 8) ); switch (ltype) { case CsValPop: gs.code.pop_back(); break; case CsValCode: gs.code.push_back(CsCodeCompile); break; case CsValIdent: gs.code.push_back(CsCodeIdentU); break; } return; case cs_ident_type::Svar: switch (ltype) { case CsValPop: return; case CsValCany: case CsValCstring: case CsValCode: case CsValIdent: case CsValCond: gs.code.push_back( CsCodeSvarM | (id->get_index() << 8) ); break; default: gs.code.push_back( CsCodeSvar | cs_ret_code(ltype, CsRetString) | (id->get_index() << 8) ); break; } goto done; case cs_ident_type::Alias: switch (ltype) { case CsValPop: return; case CsValCany: case CsValCond: gs.code.push_back( (id->get_index() < MaxArguments ? CsCodeLookupMarg : CsCodeLookupM ) | (id->get_index() << 8) ); break; case CsValCstring: case CsValCode: case CsValIdent: gs.code.push_back( (id->get_index() < MaxArguments ? CsCodeLookupMarg : CsCodeLookupM ) | CsRetString | (id->get_index() << 8) ); break; default: gs.code.push_back( (id->get_index() < MaxArguments ? CsCodeLookupArg : CsCodeLookup ) | cs_ret_code(ltype, CsRetString) | (id->get_index() << 8) ); break; } goto done; case cs_ident_type::Command: { int comtype = CsCodeCom, numargs = 0; if (prevargs >= MaxResults) { gs.code.push_back(CsCodeEnter); } auto fmt = static_cast(id)->get_args(); for (char c: fmt) { switch (c) { case 'S': gs.gen_str(); numargs++; break; case 's': gs.gen_str(ostd::string_range(), true); numargs++; break; case 'i': gs.gen_int(); numargs++; break; case 'b': gs.gen_int(std::numeric_limits::min()); numargs++; break; case 'f': gs.gen_float(); numargs++; break; case 'F': gs.code.push_back(CsCodeDup | CsRetFloat); numargs++; break; case 'E': case 'T': case 't': gs.gen_null(); numargs++; break; case 'e': compileblock(gs); numargs++; break; case 'r': gs.gen_ident(); numargs++; break; case '$': gs.gen_ident(id); numargs++; break; case 'N': gs.gen_int(-1); numargs++; break; case 'C': comtype = CsCodeComC; goto compilecomv; case 'V': comtype = CsCodeComV; goto compilecomv; case '1': case '2': case '3': case '4': break; } } gs.code.push_back( comtype | cs_ret_code(ltype) | (id->get_index() << 8) ); gs.code.push_back( (prevargs >= MaxResults ? CsCodeExit : CsCodeResultArg ) | cs_ret_code(ltype) ); goto done; compilecomv: gs.code.push_back( comtype | cs_ret_code(ltype) | (numargs << 8) | (id->get_index() << 13) ); gs.code.push_back( (prevargs >= MaxResults ? CsCodeExit : CsCodeResultArg ) | cs_ret_code(ltype) ); goto done; } default: goto invalid; } } gs.gen_str(lookup, true); break; } } switch (ltype) { case CsValCany: case CsValCond: gs.code.push_back(CsCodeLookupMu); break; case CsValCstring: case CsValCode: case CsValIdent: gs.code.push_back(CsCodeLookupMu | CsRetString); break; default: gs.code.push_back(CsCodeLookupU | cs_ret_code(ltype)); break; } done: switch (ltype) { case CsValPop: gs.code.push_back(CsCodePop); break; case CsValCode: gs.code.push_back(CsCodeCompile); break; case CsValCond: gs.code.push_back(CsCodeCond); break; case CsValIdent: gs.code.push_back(CsCodeIdentU); break; } return; invalid: switch (ltype) { case CsValPop: break; case CsValNull: case CsValAny: case CsValCany: case CsValWord: case CsValCond: gs.gen_null(); break; default: gs.gen_value(ltype); break; } } static bool compileblockstr(cs_gen_state &gs, ostd::string_range str, bool macro) { int startc = gs.code.size(); gs.code.push_back(macro ? CsCodeMacro : CsCodeVal | CsRetString); gs.code.reserve(gs.code.size() + str.size() / sizeof(uint32_t) + 1); char *buf = new char[(str.size() / sizeof(uint32_t) + 1) * sizeof(uint32_t)]; int len = 0; while (!str.empty()) { char const *p = str.data(); str = ostd::find_one_of(str, ostd::string_range("\r/\"@]")); memcpy(&buf[len], p, str.data() - p); len += str.data() - p; if (str.empty()) { goto done; } switch (str.front()) { case '\r': str.pop_front(); break; case '\"': { auto start = str; str = util::parse_string(gs.cs, str); auto strr = start.slice(0, &str[0] - &start[0]); memcpy(&buf[len], strr.data(), strr.size()); len += strr.size(); break; } case '/': if (str[1] == '/') { str = ostd::find(str, '\n'); } else { buf[len++] = str.front(); str.pop_front(); } break; case '@': case ']': buf[len++] = str.front(); str.pop_front(); break; } } done: memset(&buf[len], '\0', sizeof(uint32_t) - len % sizeof(uint32_t)); uint32_t *ubuf = reinterpret_cast(buf); gs.code.insert(gs.code.end(), ubuf, ubuf + (len / sizeof(uint32_t) + 1)); gs.code[startc] |= len << 8; delete[] buf; return true; } static bool compileblocksub(cs_gen_state &gs, int prevargs) { cs_string lookup; switch (gs.current()) { case '(': if (!compilearg(gs, CsValCany, prevargs)) { return false; } break; case '[': if (!compilearg(gs, CsValCstring, prevargs)) { return false; } gs.code.push_back(CsCodeLookupMu); break; case '\"': lookup = gs.get_str_dup(); goto lookupid; default: { lookup = gs.read_macro_name(); if (lookup.empty()) { return false; } lookupid: cs_ident *id = gs.cs.new_ident(lookup); if (id) { switch (id->get_type()) { case cs_ident_type::Ivar: gs.code.push_back(CsCodeIvar | (id->get_index() << 8)); goto done; case cs_ident_type::Fvar: gs.code.push_back(CsCodeFvar | (id->get_index() << 8)); goto done; case cs_ident_type::Svar: gs.code.push_back(CsCodeSvarM | (id->get_index() << 8)); goto done; case cs_ident_type::Alias: gs.code.push_back( (id->get_index() < MaxArguments ? CsCodeLookupMarg : CsCodeLookupM ) | (id->get_index() << 8) ); goto done; default: break; } } gs.gen_str(lookup, true); gs.code.push_back(CsCodeLookupMu); done: break; } } return true; } static void compileblockmain(cs_gen_state &gs, int wordtype, int prevargs) { char const *start = gs.source.data(); size_t curline = gs.current_line; int concs = 0; for (int brak = 1; brak;) { switch (gs.skip_until("@\"/[]")) { case '\0': throw cs_error(gs.cs, "missing \"]\""); return; case '\"': gs.get_str(); break; case '/': gs.next_char(); if (gs.current() == '/') { gs.skip_until('\n'); } break; case '[': gs.next_char(); brak++; break; case ']': gs.next_char(); brak--; break; case '@': { char const *esc = gs.source.data(); int level = 0; while (gs.current() == '@') { ++level; gs.next_char(); } if (brak > level) { continue; } else if (brak < level) { throw cs_error(gs.cs, "too many @s"); return; } if (!concs && prevargs >= MaxResults) { gs.code.push_back(CsCodeEnter); } if (concs + 2 > MaxArguments) { gs.code.push_back(CsCodeConcW | CsRetString | (concs << 8)); concs = 1; } if (compileblockstr( gs, ostd::string_range(start, esc), true )) { concs++; } if (compileblocksub(gs, prevargs + concs)) { concs++; } if (concs) { start = gs.source.data(); curline = gs.current_line; } else if (prevargs >= MaxResults) { gs.code.pop_back(); } break; } default: gs.next_char(); break; } } if (gs.source.data() - 1 > start) { if (!concs) { switch (wordtype) { case CsValPop: return; case CsValCode: case CsValCond: { auto ret = compileblock(gs, ostd::string_range( start, gs.source.data() + gs.source.size() ), curline, CsRetNull, ']'); gs.source = ret.first; gs.current_line = ret.second; return; } case CsValIdent: gs.gen_ident(ostd::string_range(start, gs.source.data() - 1)); return; } } switch (wordtype) { case CsValCstring: case CsValCode: case CsValIdent: case CsValCany: case CsValCond: compileblockstr( gs, ostd::string_range(start, gs.source.data() - 1), true ); break; default: compileblockstr( gs, ostd::string_range(start, gs.source.data() - 1), concs > 0 ); break; } if (concs > 1) { concs++; } } if (concs) { if (prevargs >= MaxResults) { gs.code.push_back(CsCodeConcM | cs_ret_code(wordtype) | (concs << 8)); gs.code.push_back(CsCodeExit | cs_ret_code(wordtype)); } else { gs.code.push_back(CsCodeConcW | cs_ret_code(wordtype) | (concs << 8)); } } switch (wordtype) { case CsValPop: if (concs || gs.source.data() - 1 > start) { gs.code.push_back(CsCodePop); } break; case CsValCond: if (!concs && gs.source.data() - 1 <= start) { gs.gen_null(); } else { gs.code.push_back(CsCodeCond); } break; case CsValCode: if (!concs && gs.source.data() - 1 <= start) { compileblock(gs); } else { gs.code.push_back(CsCodeCompile); } break; case CsValIdent: if (!concs && gs.source.data() - 1 <= start) { gs.gen_ident(); } else { gs.code.push_back(CsCodeIdentU); } break; case CsValCstring: case CsValCany: if (!concs && gs.source.data() - 1 <= start) { gs.gen_str(ostd::string_range(), true); } break; case CsValString: case CsValNull: case CsValAny: case CsValWord: if (!concs && gs.source.data() - 1 <= start) { gs.gen_str(); } break; default: if (!concs) { if (gs.source.data() - 1 <= start) { gs.gen_value(wordtype); } else { gs.code.push_back(CsCodeForce | (wordtype << CsCodeRet)); } } break; } } static bool compilearg( cs_gen_state &gs, int wordtype, int prevargs, cs_string *word ) { gs.skip_comments(); switch (gs.current()) { case '\"': switch (wordtype) { case CsValPop: gs.get_str(); break; case CsValCond: { size_t line = gs.current_line; auto s = gs.get_str_dup(); if (!s.empty()) { compileblock(gs, s, line); } else { gs.gen_null(); } break; } case CsValCode: { auto s = gs.get_str_dup(); compileblock(gs); break; } case CsValWord: if (word) { *word = gs.get_str_dup(); } break; case CsValAny: case CsValString: compileunescapestr(gs); break; case CsValCany: case CsValCstring: compileunescapestr(gs, true); break; default: { size_t line = gs.current_line; auto s = gs.get_str_dup(); gs.gen_value(wordtype, s, line); break; } } return true; case '$': compilelookup(gs, wordtype, prevargs); return true; case '(': gs.next_char(); if (prevargs >= MaxResults) { gs.code.push_back(CsCodeEnter); compilestatements( gs, wordtype > CsValAny ? CsValCany : CsValAny, ')' ); gs.code.push_back(CsCodeExit | cs_ret_code(wordtype)); } else { size_t start = gs.code.size(); compilestatements( gs, wordtype > CsValAny ? CsValCany : CsValAny, ')', prevargs ); if (gs.code.size() > start) { gs.code.push_back(CsCodeResultArg | cs_ret_code(wordtype)); } else { gs.gen_value(wordtype); return true; } } switch (wordtype) { case CsValPop: gs.code.push_back(CsCodePop); break; case CsValCond: gs.code.push_back(CsCodeCond); break; case CsValCode: gs.code.push_back(CsCodeCompile); break; case CsValIdent: gs.code.push_back(CsCodeIdentU); break; } return true; case '[': gs.next_char(); compileblockmain(gs, wordtype, prevargs); return true; default: switch (wordtype) { case CsValPop: { return !gs.get_word().empty(); } case CsValCond: { size_t line = gs.current_line; auto s = gs.get_word(); if (s.empty()) { return false; } compileblock(gs, s, line); return true; } case CsValCode: { size_t line = gs.current_line; auto s = gs.get_word(); if (s.empty()) { return false; } compileblock(gs, s, line); return true; } case CsValWord: { auto w = gs.get_word(); if (word) { *word = w; } return !w.empty(); } default: { size_t line = gs.current_line; auto s = gs.get_word(); if (s.empty()) { return false; } gs.gen_value(wordtype, s, line); return true; } } } } static void compile_cmd( cs_gen_state &gs, cs_command *id, bool &more, int rettype, int prevargs ) { int comtype = CsCodeCom, numargs = 0, fakeargs = 0; bool rep = false; auto fmt = id->get_args(); for (; !fmt.empty(); ++fmt) { switch (*fmt) { case 'S': case 's': if (more) { more = compilearg( gs, *fmt == 's' ? CsValCstring : CsValString, prevargs + numargs ); } if (!more) { if (rep) { break; } gs.gen_str(ostd::string_range(), *fmt == 's'); fakeargs++; } else if (fmt.size() == 1) { int numconc = 1; while ((numargs + numconc) < MaxArguments) { more = compilearg( gs, CsValCstring, prevargs + numargs + numconc ); if (!more) { break; } numconc++; } if (numconc > 1) { gs.code.push_back(CsCodeConc | CsRetString | (numconc << 8)); } } numargs++; break; case 'i': if (more) { more = compilearg(gs, CsValInt, prevargs + numargs); } if (!more) { if (rep) { break; } gs.gen_int(); fakeargs++; } numargs++; break; case 'b': if (more) { more = compilearg(gs, CsValInt, prevargs + numargs); } if (!more) { if (rep) { break; } gs.gen_int(std::numeric_limits::min()); fakeargs++; } numargs++; break; case 'f': if (more) { more = compilearg(gs, CsValFloat, prevargs + numargs); } if (!more) { if (rep) { break; } gs.gen_float(); fakeargs++; } numargs++; break; case 'F': if (more) { more = compilearg(gs, CsValFloat, prevargs + numargs); } if (!more) { if (rep) { break; } gs.code.push_back(CsCodeDup | CsRetFloat); fakeargs++; } numargs++; break; case 'T': case 't': if (more) { more = compilearg( gs, *fmt == 't' ? CsValCany : CsValAny, prevargs + numargs ); } if (!more) { if (rep) { break; } gs.gen_null(); fakeargs++; } numargs++; break; case 'E': if (more) { more = compilearg(gs, CsValCond, prevargs + numargs); } if (!more) { if (rep) { break; } gs.gen_null(); fakeargs++; } numargs++; break; case 'e': if (more) { more = compilearg(gs, CsValCode, prevargs + numargs); } if (!more) { if (rep) { break; } compileblock(gs); fakeargs++; } numargs++; break; case 'r': if (more) { more = compilearg(gs, CsValIdent, prevargs + numargs); } if (!more) { if (rep) { break; } gs.gen_ident(); fakeargs++; } numargs++; break; case '$': gs.gen_ident(id); numargs++; break; case 'N': gs.gen_int(numargs - fakeargs); numargs++; break; case 'C': comtype = CsCodeComC; if (more) { while (numargs < MaxArguments) { more = compilearg(gs, CsValCany, prevargs + numargs); if (!more) { break; } numargs++; } } goto compilecomv; case 'V': comtype = CsCodeComV; if (more) { while (numargs < MaxArguments) { more = compilearg(gs, CsValCany, prevargs + numargs); if (!more) { break; } numargs++; } } goto compilecomv; case '1': case '2': case '3': case '4': if (more && (numargs < MaxArguments)) { int numrep = -int(*fmt) + '0' - 1; fmt = ostd::string_range{&fmt[numrep], &fmt[fmt.size()]}; rep = true; } else { while (numargs > MaxArguments) { gs.code.push_back(CsCodePop); --numargs; } } break; } } gs.code.push_back(comtype | cs_ret_code(rettype) | (id->get_index() << 8)); return; compilecomv: gs.code.push_back( comtype | cs_ret_code(rettype) | (numargs << 8) | (id->get_index() << 13) ); } static void compile_alias(cs_gen_state &gs, cs_alias *id, bool &more, int prevargs) { int numargs = 0; while (numargs < MaxArguments) { more = compilearg(gs, CsValAny, prevargs + numargs); if (!more) { break; } ++numargs; } gs.code.push_back( (id->get_index() < MaxArguments ? CsCodeCallArg : CsCodeCall) | (numargs << 8) | (id->get_index() << 13) ); } static void compile_local(cs_gen_state &gs, bool &more, int prevargs) { int numargs = 0; if (more) { while (numargs < MaxArguments) { more = compilearg(gs, CsValIdent, prevargs + numargs); if (!more) { break; } numargs++; } } if (more) { while ((more = compilearg(gs, CsValPop))); } gs.code.push_back(CsCodeLocal | (numargs << 8)); } static void compile_do( cs_gen_state &gs, bool &more, int prevargs, int rettype, int opcode ) { if (more) { more = compilearg(gs, CsValCode, prevargs); } gs.code.push_back((more ? opcode : CsCodeNull) | cs_ret_code(rettype)); } static void compile_if( cs_gen_state &gs, cs_ident *id, bool &more, int prevargs, int rettype ) { if (more) { more = compilearg(gs, CsValCany, prevargs); } if (!more) { gs.code.push_back(CsCodeNull | cs_ret_code(rettype)); } else { int start1 = gs.code.size(); more = compilearg(gs, CsValCode, prevargs + 1); if (!more) { gs.code.push_back(CsCodePop); gs.code.push_back(CsCodeNull | cs_ret_code(rettype)); } else { int start2 = gs.code.size(); more = compilearg(gs, CsValCode, prevargs + 2); uint32_t inst1 = gs.code[start1]; uint32_t op1 = inst1 & ~CsCodeRetMask; uint32_t len1 = start2 - (start1 + 1); if (!more) { if (op1 == (CsCodeBlock | (len1 << 8))) { gs.code[start1] = (len1 << 8) | CsCodeJumpB | CsCodeFlagFalse; gs.code[start1 + 1] = CsCodeEnterResult; gs.code[start1 + len1] = ( gs.code[start1 + len1] & ~CsCodeRetMask ) | cs_ret_code(rettype); return; } compileblock(gs); } else { uint32_t inst2 = gs.code[start2]; uint32_t op2 = inst2 & ~CsCodeRetMask; uint32_t len2 = gs.code.size() - (start2 + 1); if (op2 == (CsCodeBlock | (len2 << 8))) { if (op1 == (CsCodeBlock | (len1 << 8))) { gs.code[start1] = ((start2 - start1) << 8) | CsCodeJumpB | CsCodeFlagFalse; gs.code[start1 + 1] = CsCodeEnterResult; gs.code[start1 + len1] = ( gs.code[start1 + len1] & ~CsCodeRetMask ) | cs_ret_code(rettype); gs.code[start2] = (len2 << 8) | CsCodeJump; gs.code[start2 + 1] = CsCodeEnterResult; gs.code[start2 + len2] = ( gs.code[start2 + len2] & ~CsCodeRetMask ) | cs_ret_code(rettype); return; } else if (op1 == (CsCodeEmpty | (len1 << 8))) { gs.code[start1] = CsCodeNull | (inst2 & CsCodeRetMask); gs.code[start2] = (len2 << 8) | CsCodeJumpB | CsCodeFlagTrue; gs.code[start2 + 1] = CsCodeEnterResult; gs.code[start2 + len2] = ( gs.code[start2 + len2] & ~CsCodeRetMask ) | cs_ret_code(rettype); return; } } } gs.code.push_back(CsCodeCom | cs_ret_code(rettype) | (id->get_index() << 8)); } } } static void compile_and_or( cs_gen_state &gs, cs_ident *id, bool &more, int prevargs, int rettype ) { int numargs = 0; if (more) { more = compilearg(gs, CsValCond, prevargs); } if (!more) { gs.code.push_back( ((id->get_type_raw() == CsIdAnd) ? CsCodeTrue : CsCodeFalse) | cs_ret_code(rettype) ); } else { numargs++; int start = gs.code.size(), end = start; while (numargs < MaxArguments) { more = compilearg(gs, CsValCond, prevargs + numargs); if (!more) { break; } numargs++; if ((gs.code[end] & ~CsCodeRetMask) != ( CsCodeBlock | (uint32_t(gs.code.size() - (end + 1)) << 8) )) { break; } end = gs.code.size(); } if (more) { while (numargs < MaxArguments) { more = compilearg(gs, CsValCond, prevargs + numargs); if (!more) { break; } numargs++; } gs.code.push_back( CsCodeComV | cs_ret_code(rettype) | (numargs << 8) | (id->get_index() << 13) ); } else { uint32_t op = (id->get_type_raw() == CsIdAnd) ? (CsCodeJumpResult | CsCodeFlagFalse) : (CsCodeJumpResult | CsCodeFlagTrue); gs.code.push_back(op); end = gs.code.size(); while ((start + 1) < end) { uint32_t len = gs.code[start] >> 8; gs.code[start] = ((end - (start + 1)) << 8) | op; gs.code[start + 1] = CsCodeEnter; gs.code[start + len] = ( gs.code[start + len] & ~CsCodeRetMask ) | cs_ret_code(rettype); start += len + 1; } } } } static void compilestatements(cs_gen_state &gs, int rettype, int brak, int prevargs) { cs_string idname; for (;;) { gs.skip_comments(); idname.clear(); size_t curline = gs.current_line; bool more = compilearg(gs, CsValWord, prevargs, &idname); if (!more) { goto endstatement; } gs.skip_comments(); if (gs.current() == '=') { switch (gs.current(1)) { case '/': if (gs.current(2) != '/') { break; } [[fallthrough]]; case ';': case ' ': case '\t': case '\r': case '\n': case '\0': gs.next_char(); if (!idname.empty()) { cs_ident *id = gs.cs.new_ident(idname); if (id) { switch (id->get_type()) { case cs_ident_type::Alias: more = compilearg(gs, CsValAny, prevargs); if (!more) { gs.gen_str(); } gs.code.push_back( (id->get_index() < MaxArguments ? CsCodeAliasArg : CsCodeAlias ) | (id->get_index() << 8) ); goto endstatement; case cs_ident_type::Ivar: more = compilearg(gs, CsValInt, prevargs); if (!more) { gs.gen_int(); } gs.code.push_back( CsCodeIvar1 | (id->get_index() << 8) ); goto endstatement; case cs_ident_type::Fvar: more = compilearg(gs, CsValFloat, prevargs); if (!more) { gs.gen_float(); } gs.code.push_back( CsCodeFvar1 | (id->get_index() << 8) ); goto endstatement; case cs_ident_type::Svar: more = compilearg(gs, CsValCstring, prevargs); if (!more) { gs.gen_str(); } gs.code.push_back( CsCodeSvar1 | (id->get_index() << 8) ); goto endstatement; default: break; } } gs.gen_str(idname, true); } more = compilearg(gs, CsValAny); if (!more) { gs.gen_str(); } gs.code.push_back(CsCodeAliasU); goto endstatement; } } if (idname.empty()) { noid: int numargs = 0; while (numargs < MaxArguments) { more = compilearg(gs, CsValCany, prevargs + numargs); if (!more) { break; } ++numargs; } gs.code.push_back(CsCodeCallU | (numargs << 8)); } else { cs_ident *id = gs.cs.get_ident(idname); if (!id) { if (!cs_check_num(idname)) { gs.gen_str(idname, true); goto noid; } switch (rettype) { case CsValAny: case CsValCany: { ostd::string_range end = idname; cs_int val = cs_parse_int(end, &end); if (!end.empty()) { gs.gen_str(idname, rettype == CsValCany); } else { gs.gen_int(val); } break; } default: gs.gen_value(rettype, idname, curline); break; } gs.code.push_back(CsCodeResult); } else { switch (id->get_type_raw()) { case CsIdAlias: compile_alias( gs, static_cast(id), more, prevargs ); break; case CsIdCommand: compile_cmd( gs, static_cast(id), more, rettype, prevargs ); break; case CsIdLocal: compile_local(gs, more, prevargs); break; case CsIdDo: compile_do(gs, more, prevargs, rettype, CsCodeDo); break; case CsIdDoArgs: compile_do(gs, more, prevargs, rettype, CsCodeDoArgs); break; case CsIdIf: compile_if(gs, id, more, prevargs, rettype); break; case CsIdBreak: gs.code.push_back(CsCodeBreak | CsCodeFlagFalse); break; case CsIdContinue: gs.code.push_back(CsCodeBreak | CsCodeFlagTrue); break; case CsIdResult: if (more) { more = compilearg(gs, CsValAny, prevargs); } gs.code.push_back( (more ? CsCodeResult : CsCodeNull) | cs_ret_code(rettype) ); break; case CsIdNot: if (more) { more = compilearg(gs, CsValCany, prevargs); } gs.code.push_back( (more ? CsCodeNot : CsCodeTrue) | cs_ret_code(rettype) ); break; case CsIdAnd: case CsIdOr: compile_and_or(gs, id, more, prevargs, rettype); break; case CsIdIvar: if (!(more = compilearg(gs, CsValInt, prevargs))) { gs.code.push_back(CsCodePrint | (id->get_index() << 8)); } else if (!(id->get_flags() & CS_IDF_HEX) || !( more = compilearg(gs, CsValInt, prevargs + 1) )) { gs.code.push_back(CsCodeIvar1 | (id->get_index() << 8)); } else if (!( more = compilearg(gs, CsValInt, prevargs + 2) )) { gs.code.push_back(CsCodeIvar2 | (id->get_index() << 8)); } else { gs.code.push_back(CsCodeIvar3 | (id->get_index() << 8)); } break; case CsIdFvar: if (!(more = compilearg(gs, CsValFloat, prevargs))) { gs.code.push_back(CsCodePrint | (id->get_index() << 8)); } else { gs.code.push_back(CsCodeFvar1 | (id->get_index() << 8)); } break; case CsIdSvar: if (!(more = compilearg(gs, CsValCstring, prevargs))) { gs.code.push_back(CsCodePrint | (id->get_index() << 8)); } else { int numargs = 0; do { ++numargs; } while (numargs < MaxArguments && ( more = compilearg( gs, CsValCany, prevargs + numargs ) )); if (numargs > 1) { gs.code.push_back( CsCodeConc | CsRetString | (numargs << 8) ); } gs.code.push_back(CsCodeSvar1 | (id->get_index() << 8)); } break; } } } endstatement: if (more) { while (compilearg(gs, CsValPop)); } switch (gs.skip_until(")];/\n")) { case '\0': if (gs.current() != brak) { throw cs_error(gs.cs, "missing \"%c\"", char(brak)); return; } return; case ')': case ']': if (gs.current() == brak) { gs.next_char(); return; } throw cs_error(gs.cs, "unexpected \"%c\"", gs.current()); return; case '/': gs.next_char(); if (gs.current() == '/') { gs.skip_until('\n'); } goto endstatement; default: gs.next_char(); break; } } } void cs_gen_state::gen_main(ostd::string_range s, int ret_type) { source = s; code.push_back(CsCodeStart); compilestatements(*this, CsValAny); code.push_back(CsCodeExit | ((ret_type < CsValAny) ? (ret_type << CsCodeRet) : 0)); } } /* namespace cscript */