// +-------------------------------------------------------------------------+ // | Script Plus Plus vers. 0.3.72 | // | Copyright (c) Andrey V. Stolyarov 2003--2023 | // | ----------------------------------------------------------------------- | // | This is free software. Permission is granted to everyone to use, copy | // | or modify this software under the terms and conditions of | // | GNU LESSER GENERAL PUBLIC LICENSE, v. 2.1 | // | as published by Free Software Foundation (see the file LGPL.txt) | // | | // | Please visit http://www.croco.net/software/scriptpp to get a fresh copy | // | ----------------------------------------------------------------------- | // | This code is provided strictly and exclusively on the "AS IS" basis. | // | !!! THERE IS NO WARRANTY OF ANY KIND, NEITHER EXPRESSED NOR IMPLIED !!! | // +-------------------------------------------------------------------------+ #include "scrvect.hpp" #include "scrsubs.hpp" ScriptSubstitution::~ScriptSubstitution() { while(first) { item *p = first; first = p->next; delete p->var; delete p; } } ScriptSubstitution::VarId ScriptSubstitution::AddVar(Var* v) { item *p = new item; p->var = v; p->next = first; first = p; return first; } bool ScriptSubstitution::RemoveVar(ScriptSubstitution::VarId id) { for(item **p = &first; *p; p = &(*p)->next) { if(*p == id) { item *tmp = *p; *p = tmp->next; delete tmp->var; delete tmp; return true; } } return false; } ScriptVariable ScriptSubstitution::Substitute(const ScriptVariable &src) const { return DoSubstitution(src, first, recursion_limit); } ScriptVariable ScriptSubstitution:: Substitute(const ScriptVariable &src, const ScriptVector &argv, int idx, int count) const { item it; it.next = first; it.var = new ScriptSubstitutionArgv(&argv, false, idx, count); ScriptVariable res = DoSubstitution(src, &it, recursion_limit); delete it.var; return res; } ScriptVariable ScriptSubstitution:: Substitute(const ScriptVariable &src, const Var *aux) const { item it; it.next = first; it.var = aux; return DoSubstitution(src, &it, recursion_limit); } ScriptVariable ScriptSubstitution:: DoSubstitution(const ScriptVariable &src, item *itemlist, int reclim) const { if(src.IsInvalid()) return ScriptVariableInv(); if(reclim <= 0) return src; ScriptVariable res(""); const char *p = src.c_str(); while(*p) { if(*p == esc_char) { if(p[1] == left_br) { int len; PerformNesting(p+2, itemlist, res, len); p += (len + 2); } else if(p[1] == left_lazy) { int len; PerformLazy(p+2, itemlist, res, len, reclim); p += (len + 2); } else if(p[1] == esc_char || p[1] == right_lazy) { res += p[1]; p += 2; } else { int len; PerformSimple(p+1, itemlist, res, len); p += (len + 1); } } else { res += *p; p++; } } return res; } void ScriptSubstitution:: PerformSimple(const char *p, item *lst, ScriptVariable &res, int &len) const { len = 0; while(p[len] && p[len] != esc_char && p[len] != '\n') len++; if(!p[len]) return; if(p[len] == '\n') { len++; return; } if(len == 0) { res += esc_char; len++; return; } else { if(!AppendReplacement(p, len, lst, res)) AppendUntouched(p-1, len+2, res); len++; } } void ScriptSubstitution:: PerformNesting(const char *p, item *lst, ScriptVariable &res, int &len) const { ScriptVariable inner = ""; len = 0; while(*p) { if(*p == esc_char) { int inlen; if(p[1] == left_br) { PerformNesting(p+2, lst, inner, inlen); inlen += 2; } else if(p[1] == left_lazy) { PerformLazy(p+2, lst, inner, inlen, recursion_limit); inlen += 2; } else { PerformSimple(p+1, lst, inner, inlen); inlen += 1; } p += inlen; len += inlen; } else if(*p == right_br) { // inner string construction finished len++; if(!AppendReplacement(inner.c_str(), inner.Length(), lst, res)) { res += esc_char; res += left_br; res += inner; res += right_br; } return; } else { inner += *p; p++; len++; } } // no construction ending found, leave everything as is res += esc_char; res += left_br; res += inner; } void ScriptSubstitution::PerformLazy(const char *p, item *lst, ScriptVariable &res, int &len, int reclim) const { int level = 1; len = 0; while(p[len]) { if(p[len] == esc_char) { if(!p[len+1]) { break; } else if(p[len+1] == left_lazy) { level++; len += 2; } else if(p[len+1] == right_lazy) { // ``escaped'' '}' doesn't affect levels len += 2; } else if(p[len+1] == esc_char) { // "%%" might be considered something special, but not now len += 2; } else { // nothing special, we're lazy! len += 1; } } else if(p[len] == right_lazy) { len++; level--; if(level == 0) { // here we go! ScriptVariable inner_res; if(!AppendReplacement(p, len-1, lst, inner_res)) { AppendUntouched(p-2, len+3, inner_res); } res += DoSubstitution(inner_res, lst, reclim - 1); return; } } else { len++; } } res += esc_char; res += left_lazy; len = 2; } bool ScriptSubstitution:: AppendReplacement(const char *p, int len, item *lst, ScriptVariable &res) { for(item *t = lst; t; t = t->next) { ScriptVariable v = t->var->Sub(p, len); if(!v.IsInvalid()) { res += v; return true; } } // just tell the parent to add everything back as it was return false; } void ScriptSubstitution:: AppendUntouched(const char *p, int len, ScriptVariable &res) { int i; for(i = 0; i < len; i++) res += p[i]; } ScriptSubstitutionSingleName:: ScriptSubstitutionSingleName(const ScriptVariable &n) : name(n) {} ScriptVariable ScriptSubstitutionSingleName::Sub(const char *nm, int len) const { if(name.Length() != len) return ScriptVariableInv(); for(int i = 0; i < len && nm[i]; i++) if(nm[i] != name[i]) return ScriptVariableInv(); return Text(); } ScriptSubstitutionConst:: ScriptSubstitutionConst(const ScriptVariable &n, const ScriptVariable &v) : ScriptSubstitutionSingleName(n), val(v) {} ScriptVariable ScriptSubstitutionConst::Text() const { return val; } ScriptSubstitutionScrVar:: ScriptSubstitutionScrVar(const ScriptVariable &n, ScriptVariable *pv) : ScriptSubstitutionSingleName(n), val(pv) {} ScriptVariable ScriptSubstitutionScrVar::Text() const { return *val; } ScriptSubstitutionPrefixRequest:: ScriptSubstitutionPrefixRequest(const ScriptVariable &p, const char *spec) : prefix(p + spec[0]), delim(spec[0]), esc_char(spec[1]), left_br(spec[2]), right_br(spec[3]), left_lazy(spec[4]), right_lazy(spec[5]) { } ScriptVariable ScriptSubstitutionPrefixRequest::Sub(const char *name, int len) const { int pl = prefix.Length(); if(len < pl || !ScriptVariable(name).HasPrefix(prefix)) return ScriptVariableInv(); ScriptVector params; int npar = 0; params[npar] = ""; /* to make sure it is valid */ int level = 0; for(int i = prefix.Length(); i < len; i++) if(name[i] == delim && level == 0) { npar++; params[npar] = ""; } else if(name[i] == esc_char && i+1 < len) { params[npar] += name[i]; if(name[i+1] == left_br || name[i+1] == left_lazy) { params[npar] += name[i+1]; level++; i++; } else if(name[i+1] == right_lazy) { // right_br can't be escaped params[npar] += name[i+1]; i++; } } else if(name[i] == right_br || name[i] == right_lazy) { params[npar] += name[i]; level--; } else { params[npar] += name[i]; } return Handle(params); } ScriptSubstitutionArgv:: ScriptSubstitutionArgv(const ScriptVector *avec, bool copy, int aidx, int cnt) : vec_owned(copy), idx(aidx), count(cnt) { if(copy) { vec = new ScriptVector(*avec, idx, count); idx = 0; } else { vec = avec; } if(count == -1 || count > (vec->Length() - idx)) count = vec->Length() - idx; } ScriptSubstitutionArgv::~ScriptSubstitutionArgv() { if(vec_owned) delete vec; } ScriptVariable ScriptSubstitutionArgv::Sub(const char *name, int len) const { if(len == 1 && *name == '*') { ScriptVariable res(""); for(int i = 1; i < count; i++) { if(i>1) res += ' '; res += (*vec)[i+idx]; } return res; } int n = 0; for(int i = 0; i < len; i++) { if(name[i] > '9' || name[i] < '0') return ScriptVariableInv(); n *= 10; n += name[i] - '0'; } if(n >= count) return ScriptVariable(""); else return (*vec)[n+idx]; } ScriptSubstitutionDictionary:: ScriptSubstitutionDictionary(const ScriptVector &dict, bool copy) : vec_owned(copy) { if(copy) the_dict = new ScriptVector(dict); else the_dict = &dict; } ScriptSubstitutionDictionary::~ScriptSubstitutionDictionary() { if(vec_owned) delete the_dict; } ScriptVariable ScriptSubstitutionDictionary::Sub(const char *name, int len) const { int i; for(i = 0; i < the_dict->Length() - 1; i += 2) { if((*the_dict)[i].Length() == len) { bool ok = true; for(int k = 0; k < len; k++) if(name[k] != (*the_dict)[i][k]) { ok = false; break; } if(ok) return (*the_dict)[i+1]; } } return ScriptVariableInv(); }