restored/stdlib.inc
← All source files · Download this file
9,565 bytes · SHA256 ba50240fee087595ca98983581d73d486eeb6df1d9c8b13849f24b52dcc5475f
// Pure, bounded standard-library additions. Included after utilities.inc.
// Never change legacy numeric parsing/formatting or mutation contracts here.
static bool standardCommand(const CodeLine& c) {
const auto& op=c.arg[0].text;
if(op=="text" || op=="number") return true;
if(c.arg.size()<2) return false;
const auto& sub=c.arg[1].text;
if(op=="array") return sub=="sort" || sub=="reverse" || sub=="slice" || sub=="find" || sub=="remove" || sub=="unique";
if(op=="json") return sub=="append" || sub=="insert" || sub=="remove" || sub=="length" || sub=="contains" || sub=="merge";
return false;
}
static void standardValidate(const CodeLine& c) {
count(c,3,6);
const auto& a=c.arg; const auto& op=a[0].text; const auto& sub=a[1].text;
auto dest=[&](std::size_t k) { destination(a[k],c.source); };
dest(2);
if(op=="array") {
if(sub=="reverse" || sub=="unique") count(c,4,4);
else if(sub=="slice") count(c,6,6);
else count(c,5,5);
dest(3);
if(sub=="sort" && a[4].text!="numeric" && a[4].text!="lexical") fail(c.source,"array sort mode must be numeric or lexical");
} else if(op=="json") {
if(sub=="insert" || sub=="contains") count(c,5,5);
else if(sub=="merge") count(c,6,6);
else count(c,4,4);
if(sub=="length" || sub=="contains" || sub=="merge") dest(3);
if(sub=="merge") {
dest(4);
if(a[5].text!="left" && a[5].text!="right" && a[5].text!="error") fail(c.source,"json merge policy must be left, right or error");
}
} else if(op=="text") {
if(sub=="contains" || sub=="startswith" || sub=="endswith") count(c,5,5);
else if(sub=="padleft" || sub=="padright") count(c,6,6);
else fail(c.source,"unknown text operation");
} else {
if(sub=="valid" || sub=="convert") count(c,4,4);
else if(sub=="format") count(c,5,5);
else fail(c.source,"unknown number operation");
}
}
static bool standardNumeric(const std::string& text,double& n) {
// Decimal only, optional leading +, no whitespace, locale-independent.
if(text.empty()) return false;
const char* first=text.data(); const char* last=first+text.size();
if(*first=='+') { ++first; if(first==last || *first=='-' || *first=='+') return false; }
auto r=std::from_chars(first,last,n,std::chars_format::general);
return r.ec==std::errc{} && r.ptr==last && std::isfinite(n);
}
static double standardNumber(const std::string& text,const Source& s) {
double n=0;
if(!standardNumeric(text,n)) fail(s,"expected finite decimal number");
return n;
}
static std::size_t standardIndex(const std::string& text,std::size_t maximum,const Source& s) {
auto n=standardNumber(text,s);
if(n<0 || n>static_cast<double>(maximum) || std::trunc(n)!=n) fail(s,"expected integer in bounds");
return static_cast<std::size_t>(n);
}
static void standardCommit(Frame& f,const Token& t,variable result,const Source& s) {
if(result.value.size()>valueLimit) failLimit(s,"value size limit");
auto& target=lookup(f,t,s);
// Single-target transaction; rollback even on fatal frame-budget failure.
std::swap(target,result);
try { frameCheck(f,s); } catch(...) { std::swap(target,result); throw; }
}
static Json standardJsonItem(Frame& f,const Token& t,const Source& s) {
if(!t.literal && !t.text.empty() && t.text[0]=='$') {
auto& v=lookup(f,t,s);
if(v.structured) return v.structured->data;
}
return Json(value(f,t,s));
}
static void standardExecute(Frame& f,const CodeLine& c) {
const auto& a=c.arg; const auto& s=c.source; const auto& op=a[0].text; const auto& sub=a[1].text;
lookup(f,a[2],s);
auto val=[&](std::size_t k) { return value(f,a[k],s); };
variable result;
if(op=="array") {
const auto& source=array(f,a[3],s);
if(sub=="find") {
auto needle=val(4); auto it=std::find(source.begin(),source.end(),needle);
result.value=it==source.end() ? "-1" : std::to_string(it-source.begin());
} else {
result.array=true;
if(sub=="slice") {
auto start=standardIndex(val(4),source.size(),s);
auto length=standardIndex(val(5),source.size()-start,s);
result.items.assign(source.begin()+static_cast<std::ptrdiff_t>(start),source.begin()+static_cast<std::ptrdiff_t>(start+length));
} else if(sub=="unique") {
std::set<std::string_view> seen;
for(const auto& item:source) if(seen.insert(item).second) result.items.push_back(item);
} else {
result.items=source;
if(sub=="reverse") std::reverse(result.items.begin(),result.items.end());
else if(sub=="remove") {
auto index=standardIndex(val(4),source.size(),s);
if(index==source.size()) fail(s,"array index out of bounds");
result.items.erase(result.items.begin()+static_cast<std::ptrdiff_t>(index));
} else if(a[4].text=="lexical") std::stable_sort(result.items.begin(),result.items.end());
else {
// Parse once before sorting; comparison never throws or reparses.
std::vector<std::pair<double,std::size_t>> keys; keys.reserve(source.size());
for(std::size_t i=0;i<source.size();++i) keys.emplace_back(standardNumber(source[i],s),i);
std::stable_sort(keys.begin(),keys.end(),[](const auto& x,const auto& y) { return x.first<y.first; });
for(std::size_t i=0;i<keys.size();++i) result.items[i]=source[keys[i].second];
}
}
}
} else if(op=="text") {
auto text=val(3);
if(sub=="padleft" || sub=="padright") {
auto width=standardIndex(val(4),valueLimit,s); auto pad=val(5);
if(pad.empty()) fail(s,"padding must not be empty");
auto needed=width>text.size() ? width-text.size() : 0;
std::string padding; padding.reserve(needed);
while(padding.size()<needed) padding.append(pad,0,std::min(pad.size(),needed-padding.size()));
result.value=sub=="padleft" ? padding+text : text+padding;
} else {
auto needle=val(4); bool yes=false;
if(sub=="contains") yes=text.find(needle)!=std::string::npos;
else if(sub=="startswith") yes=text.size()>=needle.size() && text.compare(0,needle.size(),needle)==0;
else yes=text.size()>=needle.size() && text.compare(text.size()-needle.size(),needle.size(),needle)==0;
result.value=yes ? "1" : "0";
}
} else if(op=="number") {
auto text=val(3); double n=0;
if(sub=="valid") result.value=standardNumeric(text,n) ? "1" : "0";
else {
n=standardNumber(text,s); if(n==0) n=0;
// Max binary64 fixed representation plus 100 fractional places fits.
char buffer[512]; std::to_chars_result r;
if(sub=="format") r=std::to_chars(buffer,buffer+sizeof(buffer),n,std::chars_format::fixed,static_cast<int>(standardIndex(val(4),100,s)));
else r=std::to_chars(buffer,buffer+sizeof(buffer),n,std::chars_format::general);
if(r.ec!=std::errc{}) fail(s,"number formatting failed");
result.value.assign(buffer,r.ptr);
if(sub=="format" && result.value[0]=='-' && result.value.find_first_not_of("-0.")==std::string::npos) result.value.erase(0,1);
}
} else {
const auto& source=jsonValue(f,a[(sub=="append" || sub=="insert" || sub=="remove") ? 2 : 3],s);
Json copy;
bool structured=true;
if(sub=="length") {
if(!source.is_structured()) fail(s,"json length requires array or object");
result.value=std::to_string(source.size()); structured=false;
} else if(sub=="contains") {
if(!source.is_array()) fail(s,"json contains requires array");
auto item=standardJsonItem(f,a[4],s);
result.value=std::find(source.begin(),source.end(),item)!=source.end() ? "1" : "0"; structured=false;
} else if(sub=="merge") {
const auto& right=jsonValue(f,a[4],s);
if(!source.is_object() || !right.is_object()) fail(s,"json merge requires objects");
copy=source;
for(auto it=right.begin();it!=right.end();++it) {
bool exists=copy.contains(it.key());
if(exists && a[5].text=="error") fail(s,"duplicate JSON merge key");
if(!exists || a[5].text=="right") copy[it.key()]=it.value();
}
} else {
if(!source.is_array()) fail(s,"JSON mutation requires array");
auto index=sub=="append" ? source.size() : standardIndex(val(3),source.size(),s);
if(sub=="remove" && index==source.size()) fail(s,"JSON array index out of bounds");
copy=source;
if(sub=="remove") copy.erase(copy.begin()+static_cast<Json::difference_type>(index));
else {
auto item=standardJsonItem(f,a[sub=="append" ? 3 : 4],s);
copy.insert(copy.begin()+static_cast<Json::difference_type>(index),std::move(item));
}
}
if(structured) {
result.structuredBytes=jsonSize(copy,s);
result.structured=std::make_shared<const Structured>(std::move(copy));
}
}
standardCommit(f,a[2],std::move(result),s);
}