// Copyright 1998,1999 Zanshin Inc. // The contents of this file are subject to the Zanshin Public License Version // 1.0 (the "License"); you may not use this file except in compliance with the // License. You should have received a copy of the License with Latte; see // the file COPYING. You may also obtain a copy of the License at // . // // Software distributed under the License is distributed on an "AS IS" basis, // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License // for the specific language governing rights and limitations under the // License. // // The Original Code is Latte. // // The Initial Developer of the Original Code is Zanshin, Inc. #include #include #include #include #include #include #ifdef HAVE_WAIT_H # include #endif // HAVE_WAIT_H #ifdef ENABLE_FLOATING_POINT # include #endif // ENABLE_FLOATING_POINT using namespace std; const shstring & Latte_Lambda::name() const { static const shstring lambda_name = "lambda"; return lambda_name; } Refcounter Latte_Lambda::apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw NoParamList(loc); const Refcounter ¶m_obj = *i++; Latte_List *param_list = param_obj->as_list(); if (!param_list) throw NoParamList(loc); Refcounter closure(new Latte_Closure(param_list->begin(), param_list->end(), i, args_end, activation)); return Refcounter(closure.get()); } class MacroOp : public Latte_Lambda { const shstring &name() const { static const shstring n = "macro"; return n; } Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Refcounter result = Latte_Lambda::apply(ws, loc, args, args_end, activation); Latte_Operator *op = result->as_operator(); if (op) { Latte_Closure *closure = op->as_closure(); if (closure) // xxx else impossible? closure->set_macro(1); } return result; } }; class DefOp : public Latte_Operator { public: DefOp() : Latte_Operator(0) {} const shstring &name() const { static const shstring n = "def"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &definee = *i++; Latte_VarRef *varref = definee->as_varref(); Latte_List *list = definee->as_list(); shstring name; Refcounter val; if (varref) { // {\def \var VALUE} name = varref->name(); val = Refcounter((i == args_end) ? latte_false() : (*i)->eval(activation)); } else if (list) { // {\def {\name ARG ...} BODY ...} Latte_List::const_iterator g = list->begin(); if (g == list->end()) throw BadType(*this, 0, loc); const Refcounter &gfirst = *g++; Latte_VarRef *gvarref = gfirst->as_varref(); if (!gvarref) throw BadType(*this, 0, loc); name = gvarref->name(); val = Refcounter(new Latte_Closure(g, list->end(), i, args_end, activation)); } else throw BadType(*this, 0, loc); activation.define(name, val); return latte_false(); } }; class AddOp : public Latte_Operator { public: AddOp(bool add) : m_add(add) {} const shstring &name() const { static const shstring a = "add"; static const shstring s = "subtract"; return (m_add ? a : s); } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { if (args == args_end) return Refcounter(new Latte_Str(ws, loc, 0)); Latte_List::const_iterator i = args; const Refcounter &obj = *i; Latte_Number_t result = obj->numeric_val(); bool subtrahend = 0; while (++i != args_end) { subtrahend = 1; const Refcounter &obj = *i; Latte_Number_t num = obj->numeric_val(); if (m_add) result += num; else result -= num; } if (!m_add && !subtrahend) result = (-result); return Refcounter(new Latte_Str(ws, loc, result)); } private: bool m_add; }; class MultiplyOp : public Latte_Operator { public: MultiplyOp(bool multiply) : m_multiply(multiply) {} const shstring &name() const { static const shstring m = "multiply"; static const shstring d = "divide"; return (m_multiply ? m : d); } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { if (args == args_end) return Refcounter(new Latte_Str(ws, loc, 0)); Latte_List::const_iterator i = args; const Refcounter &obj = *i; Latte_Number_t result = obj->numeric_val(); long argno = 1; while (++i != args_end) { const Refcounter &obj = *i; Latte_Number_t num = obj->numeric_val(); if (m_multiply) result *= num; else if (num == 0) throw OutOfRange(*this, argno, loc); else result /= num; ++argno; } return Refcounter(new Latte_Str(ws, loc, result)); } private: bool m_multiply; }; class ModuloOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "modulo"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &a = *i++; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &b = *i; long a_num = static_cast(a->numeric_val()); long b_num = static_cast(b->numeric_val()); if (b_num == 0) throw OutOfRange(*this, 1, loc); return Refcounter(new Latte_Str(ws, loc, a_num % b_num)); } }; class ConsOp : public Latte_Operator { public: ConsOp(bool front) : m_front(front) {} const shstring &name() const { static const shstring c = "cons"; static const shstring p = "push-back"; return (m_front ? c : p); } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &first = *i++; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &second = *i; const Latte_List *second_list = second->as_list(); if (!second_list) throw BadType(*this, 1, loc); Refcounter result_list(new Latte_List); if (m_front) { Latte_Tangible *tangible = second->as_tangible(); if (tangible) { Latte_List::const_iterator j = second_list->begin(); if (j != second_list->end()) { result_list->push_back(Latte_WsNode::wrap(**j, tangible->wstate())); result_list->append(++j, second_list->end()); } } else result_list->append(second_list->begin(), second_list->end()); result_list->push_front(first); } else { result_list->append(second_list->begin(), second_list->end()); result_list->push_back(first); } return Latte_WsNode::wrap(*result_list, ws); } private: bool m_front; }; class SetOp : public Latte_Operator { public: SetOp() : Latte_Operator(0) {} const shstring &name() const { static const shstring n = "set!"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &obj = *i++; Latte_VarRef *varref = obj->as_varref(); if (!varref) throw BadType(*this, 0, loc); if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &valobj = *i; varref->lookup(activation) = valobj->eval(activation); return latte_false(); } }; class IfOp : public Latte_Operator { public: IfOp() : Latte_Operator(0) {} const shstring &name() const { static const shstring n = "if"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &test_obj = *i++; Refcounter val = test_obj->eval(activation); if (i != args_end) { if (val->bool_val()) val = (*i)->eval(activation); else if (++i != args_end) val = (*i)->eval(activation); } return Latte_WsNode::wrap(*val, ws); } }; class WhileOp : public Latte_Operator { public: WhileOp() : Latte_Operator(0) {} const shstring &name() const { static const shstring n = "while"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &test_obj = *i++; const Latte_List::const_iterator body_start = i; Refcounter result(new Latte_List); while (1) { Refcounter val = test_obj->eval(activation); if (!(val->bool_val())) break; for (i = body_start; i != args_end; ++i) { const Refcounter &body_obj = *i; Refcounter body_val = body_obj->eval(activation); if (body_val->bool_val()) result->push_back(body_val); } } return Latte_WsNode::wrap(*result, ws); } }; class NotOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "not"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &obj = *i; return (obj->bool_val() ? latte_false() : latte_true()); } }; class ApplyOp : public Latte_Operator { public: ApplyOp(bool funcall) : m_funcall(funcall) {} const shstring &name() const { static const shstring a = "apply"; static const shstring f = "funcall"; return m_funcall ? f : a; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &op_obj = *i++; Latte_Operator *op = op_obj->as_operator(); if (!op) throw BadType(*this, 0, loc); Refcounter new_args(new Latte_List); while (i != args_end) { const Refcounter &arg = *i++; if (!m_funcall && (i == args_end)) { // treat last arg specially: if it's a group/list, its members get // added to new_args. const Latte_List *arg_list = arg->as_list(); if (arg_list) new_args->append(arg_list->begin(), arg_list->end()); else new_args->push_back(arg); } else new_args->push_back(arg); } return op->call_apply(ws, loc, new_args->begin(), new_args->end(), activation); } private: bool m_funcall; }; class LoadFileOp : public Latte_Operator { public: LoadFileOp(bool file) : m_file(file) {} const shstring &name() const { static const shstring f = "load-file"; static const shstring l = "load-library"; return (m_file ? f : l); } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { if (args == args_end) throw InsufficientArgs(*this, loc); Latte_Stringify sv(0); for (Latte_List::const_iterator i = args; i != args_end; ++i) { const Refcounter &obj = *i; obj->visit(sv); } return (m_file ? latte_load_file(sv.str(), activation) : latte_load_library(sv.str(), activation)); } private: bool m_file; }; // This can be defined with a macro in terms of lambda, but this // implementation is quite a bit faster class LetOp : public Latte_Operator { public: LetOp() : Latte_Operator(0) {} const shstring &name() const { static const shstring n = "let"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &bindings_obj = *i++; Latte_List *bindings_list = bindings_obj->as_list(); if (!bindings_list) throw BadType(*this, 0, loc); Refcounter params(new Latte_List); Refcounter vals(new Latte_List); for (Latte_List::const_iterator j = bindings_list->begin(); j != bindings_list->end(); ++j) { const Refcounter &binding_obj = *j; Latte_List *binding_list = binding_obj->as_list(); if (!binding_list) throw BadType(*this, 0, loc); Latte_List::const_iterator k = binding_list->begin(); if (k == binding_list->end()) throw BadType(*this, 0, loc); params->push_back(*k++); vals->push_back((k == binding_list->end()) ? latte_false() : (*k)->eval(activation)); } Refcounter closure(new Latte_Closure(params->begin(), params->end(), i, args_end, activation)); return closure->call_apply(ws, loc, vals->begin(), vals->end(), activation); } }; class LengthOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "length"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &obj = *i; if (obj->as_list()) return Refcounter(new Latte_Str(ws, loc, obj->as_list()->size())); if (obj->as_str()) return Refcounter(new Latte_Str(ws, loc, obj->as_str()->size())); throw BadType(*this, 0, loc); } }; class IneqOp : public Latte_Operator { public: IneqOp(bool greater, bool equal) : m_greater(greater), m_equal(equal) {} const shstring &name() const { static const shstring ge = "greater-equal?"; static const shstring gt = "greater?"; static const shstring le = "less-equal?"; static const shstring lt = "less?"; if (m_greater) { if (m_equal) return ge; return gt; } if (m_equal) return le; return lt; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &obj = *i++; Latte_Number_t val = obj->numeric_val(); while (i != args_end) { Latte_Number_t newval = (*i++)->numeric_val(); if (m_greater) { if (m_equal) { if (!(val >= newval)) return latte_false(); } else { if (!(val > newval)) return latte_false(); } } else { if (m_equal) { if (!(val <= newval)) return latte_false(); } else { if (!(val < newval)) return latte_false(); } } val = newval; } return latte_true(); } private: bool m_greater, m_equal; }; class StrIneqOp : public Latte_Operator { public: StrIneqOp(bool greater, bool equal) : m_greater(greater), m_equal(equal) {} const shstring &name() const { static const shstring ge = "string-greater-equal?"; static const shstring gt = "string-greater?"; static const shstring le = "string-less-equal?"; static const shstring lt = "string-less?"; if (m_greater) { if (m_equal) return ge; return gt; } if (m_equal) return le; return lt; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &obj = *i++; const Latte_Str *refstr = obj->as_str(); long argno = 0; if (!refstr) throw BadType(*this, 0, loc); while (i != args_end) { ++argno; const Latte_Str *newstr = (*i++)->as_str(); if (!newstr) throw BadType(*this, argno, loc); if (m_greater) { if (m_equal) { if (!(refstr >= newstr)) return latte_false(); } else { if (!(*refstr > *newstr)) return latte_false(); } } else { if (m_equal) { if (!(*refstr <= *newstr)) return latte_false(); } else { if (!(*refstr < *newstr)) return latte_false(); } } refstr = newstr; } return latte_true(); } private: bool m_greater, m_equal; }; class AndOp : public Latte_Operator { public: AndOp() : Latte_Operator(0) {} const shstring &name() const { static const shstring n = "and"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Refcounter result = latte_true(); Latte_List::const_iterator i = args; while ((i != args_end) && result->bool_val()) result = (*i++)->eval(activation); return Latte_WsNode::wrap(*result, ws); } }; class OrOp : public Latte_Operator { public: OrOp() : Latte_Operator(0) {} const shstring &name() const { static const shstring n = "or"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Refcounter result = latte_false(); Latte_List::const_iterator i = args; while ((i != args_end) && !(result->bool_val())) result = (*i++)->eval(activation); return Latte_WsNode::wrap(*result, ws); } }; class WarnOp; class WarnOpVisitor : public Latte_Visitor { public: void visit_str(Latte_Str &str) { cerr << suggest_wstate(str.wstate()) << str.str(); } }; class WarnOp : public Latte_Operator { public: WarnOp(bool die) : m_die(die) {} const shstring &name() const { static const shstring w = "warn"; static const shstring e = "error"; return (m_die ? e : w); } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { cerr << endl; // ensure new line if (m_die) cerr << "Error: "; // don't print loc; that gets thrown else cerr << "Warning at " << loc << ": "; for (Latte_List::const_iterator i = args; i != args_end; ++i) { const Refcounter &obj = *i; WarnOpVisitor wov; obj->visit(wov); } cerr << endl; if (m_die) throw Latte_Error(loc); // no more detail than that return latte_false(); } private: bool m_die; }; class RandomOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "random"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { if (args == args_end) throw InsufficientArgs(*this, loc); const Refcounter &obj = *args; long val = static_cast(obj->numeric_val()); if (val < 1) throw OutOfRange(*this, 0, loc); char buf[32]; sprintf(buf, "%ld", random() % val); return Refcounter(new Latte_Str(ws, loc, shstring(buf))); } }; class FileContentsOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "file-contents"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { if (args == args_end) throw InsufficientArgs(*this, loc); Latte_Stringify sv(0); for (Latte_List::const_iterator i = args; i != args_end; ++i) { const Refcounter &obj = *i; obj->visit(sv); } ifstream fstrm(sv.str().c_str()); if (fstrm.fail()) throw Latte_FileError(loc, sv.str()); latte_string result; char buf[1024]; size_t bytes; while (1) { fstrm.read(buf, sizeof buf); bytes = fstrm.gcount(); if (!bytes) break; result.append(buf, bytes); } fstrm.close(); return Refcounter(new Latte_Str(ws, loc, shstring(result))); } }; #ifdef HAVE_WAIT_H class ProcessOutputOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "process-output"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_Stringify sv(1); for (Latte_List::const_iterator i = args; i != args_end; ++i) { const Refcounter &obj = *i; obj->visit(sv); } FILE *pstrm = popen(sv.str().c_str(), "r"); if (!pstrm) throw Latte_SysError(loc); latte_string result; char buf[1024]; size_t bytes; while (1) { bytes = fread(buf, 1, sizeof buf, pstrm); if (!bytes) break; result.append(buf, bytes); } int status = pclose(pstrm); if (!WIFEXITED(status) || WEXITSTATUS(status)) throw Latte_SysError(loc); if ((result.size() > 0) && (result[result.size() - 1] == 10)) // linefeed result.erase(result.size() - 1); if ((result.size() > 0) && (result[result.size() - 1] == 13)) // carriage return result.erase(result.size() - 1); return Refcounter(new Latte_Str(ws, loc, shstring(result))); } }; #endif // HAVE_WAIT_H class AppendOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "append"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Refcounter result(new Latte_List); for (Latte_List::const_iterator i = args; i != args_end; ++i) { const Refcounter &obj = *i; const Latte_List *val_list = obj->as_list(); if (val_list) { Latte_Tangible *tangible = obj->as_tangible(); if (tangible) { Latte_List::const_iterator j = val_list->begin(); if (j != val_list->end()) { result->push_back(Latte_WsNode::wrap(**j, tangible->wstate())); result->append(++j, val_list->end()); } } else result->append(val_list->begin(), val_list->end()); } else result->push_back(obj); } return Latte_WsNode::wrap(*result, ws); } }; class StringAppendOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "string-append"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { latte_string result; long argno = 0; for (Latte_List::const_iterator i = args; i != args_end; ++i, ++argno) { const Refcounter &obj = *i; if (!(obj->as_str())) throw BadType(*this, argno, loc); result += obj->as_str()->str().str(); } return Refcounter(new Latte_Str(ws, loc, result)); } }; class TypePredicateOp : public Latte_Operator { public: enum type_type { tp_string, tp_group, tp_operator }; TypePredicateOp(type_type type) : m_type(type) {} const shstring &name() const { static const shstring s = "string?"; static const shstring g = "group?"; static const shstring o = "operator?"; if (m_type == tp_string) return s; if (m_type == tp_group) return g; // assume tp_operator return o; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { if (args == args_end) throw InsufficientArgs(*this, loc); const Refcounter &obj = *args; if (m_type == tp_group) return (obj->as_list() ? latte_true() : latte_false()); if (m_type == tp_string) return (obj->as_str() ? latte_true() : latte_false()); // assume tp_operator return (obj->as_operator() ? latte_true() : latte_false()); } private: type_type m_type; }; class NthOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "nth"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &n_obj = *i++; if (i == args_end) throw InsufficientArgs(*this, loc); long n = static_cast(n_obj->numeric_val()); const Refcounter &seq_obj = *i++; const Latte_List *seq_list = seq_obj->as_list(); if (seq_list) { if (n < 0) n += seq_list->size(); if ((n < 0) || (n >= seq_list->size())) throw OutOfRange(*this, 0, loc); return Latte_WsNode::wrap(*((*seq_list)[n]), ws); } const Latte_Str *seq_str = seq_obj->as_str(); if (seq_str) { const shstring &shstr = *seq_str; if (n < 0) n += shstr.size(); if ((n < 0) || (n >= shstr.size())) throw OutOfRange(*this, 0, loc); char x[2]; x[0] = shstr.str()[n]; x[1] = 0; return Refcounter(new Latte_Str(ws, loc, shstring(x))); } throw BadType(*this, 1, loc); } }; class CaseOp : public Latte_Operator { public: CaseOp(bool up) : m_up(up) {} const shstring &name() const { static const shstring d = "downcase"; static const shstring u = "upcase"; return m_up ? u : d; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Refcounter result(new Latte_List); long argno = 0; for (Latte_List::const_iterator i = args; i != args_end; ++i, ++argno) { const Refcounter &obj = *i; Latte_Str *valstr = obj->as_str(); if (!valstr) throw BadType(*this, argno, loc); const shstring &shstr = *valstr; latte_string newstr; for (latte_string::const_iterator j = shstr.begin(); j != shstr.end(); ++j) { const char &c = *j; char c2 = c; if (isalpha(c)) { if (m_up && islower(c)) c2 = toupper(c); else if (!m_up && isupper(c)) c2 = tolower(c); } newstr.insert(newstr.end(), c2); } result->push_back(Refcounter(new Latte_Str(valstr->wstate(), valstr->fileloc(), newstr))); } return Latte_WsNode::wrap(*result, ws); } private: bool m_up; }; static bool equalp(const Latte_Obj &a, const Latte_Obj &b) { if (&a == &b) return 1; const Latte_Str *a_str = a.as_str(); const Latte_Str *b_str = b.as_str(); if (a_str) return (b_str && ((a_str->null() && b_str->null()) || (!(a_str->null()) && !(b_str->null()) && (a_str->str().str().compare(b_str->str().str()) == 0)))); const Latte_List *a_list = a.as_list(); const Latte_List *b_list = b.as_list(); if (a_list && b_list) { Latte_List::const_iterator i = a_list->begin(); Latte_List::const_iterator j = b_list->begin(); while ((i != a_list->end()) && (j != b_list->end())) { const Refcounter &p = *i++; const Refcounter &q = *j++; if (!equalp(*p, *q)) return 0; } return ((i == a_list->end()) && (j == b_list->end())); } return 0; } class EqualOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "equal?"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &obj = *i++; if (i == args_end) throw InsufficientArgs(*this, loc); do { if (!equalp(*obj, *(*i++))) return latte_false(); } while (i != args_end); return latte_true(); } }; class SubseqOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "subseq"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &seq_obj = *i++; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &from_obj = *i++; const Latte_List *seq_list = seq_obj->as_list(); if (!seq_list) throw BadType(*this, 0, loc); long from = static_cast(from_obj->numeric_val()); bool have_to = (i != args_end); long to = 0; if (have_to) { const Refcounter &to_obj = *i; to = static_cast(to_obj->numeric_val()); } if (from < 0) from += seq_list->size(); if ((from < 0) || (from > seq_list->size())) throw OutOfRange(*this, 1, loc); if (have_to) { if (to < 0) to += seq_list->size(); if ((to < 0) || (to > seq_list->size()) || (to < from)) throw OutOfRange(*this, 2, loc); } Refcounter result(new Latte_List(seq_list->begin() + from, (have_to ? (seq_list->begin() + to) : seq_list->end()))); return Latte_WsNode::wrap(*result, ws); } }; class SubstrOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "substr"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Latte_List::const_iterator i = args; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &str_obj = *i++; if (i == args_end) throw InsufficientArgs(*this, loc); const Refcounter &from_obj = *i++; const Latte_Str *str_str = str_obj->as_str(); if (!str_str) throw BadType(*this, 0, loc); const shstring &shstr = str_str->str(); long from = static_cast(from_obj->numeric_val()); bool have_to = (i != args_end); long to = 0; if (have_to) { const Refcounter &to_obj = *i; to = static_cast(to_obj->numeric_val()); } if (from < 0) from += shstr.size(); if ((from < 0) || (from > shstr.size())) throw OutOfRange(*this, 1, loc); if (have_to) { if (to < 0) to += shstr.size(); if ((to < 0) || (to > shstr.size()) || (to < from)) throw OutOfRange(*this, 2, loc); } const latte_string &shstrstr = shstr.str(); return Refcounter(new Latte_Str(ws, loc, (have_to ? shstrstr.substr(from, to - from) : shstrstr.substr(from)))); } }; class TruncOp : public Latte_Operator { public: TruncOp(bool floor) : m_floor(floor) {} const shstring &name() const { static const shstring f = "floor"; static const shstring c = "ceiling"; return (m_floor ? f : c); } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { if (args == args_end) throw InsufficientArgs(*this, loc); const Refcounter &obj = *args; Latte_Number_t num = obj->numeric_val(); #ifdef ENABLE_FLOATING_POINT if (m_floor) num = floor(num); else num = ceil(num); #endif // ENABLE_FLOATING_POINT return Refcounter(new Latte_Str(ws, loc, num)); } private: bool m_floor; }; class ExplodeOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "explode"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { Refcounter result(new Latte_List); Latte_Wstate just_one_space; just_one_space.space(1); long argno = 0; for (Latte_List::const_iterator i = args; i != args_end; ++i, ++argno) { const Refcounter &obj = *i; Latte_Str *lstr = obj->as_str(); if (!lstr) throw BadType(*this, argno, loc); const latte_string &str = lstr->str().str(); for (latte_string::const_iterator j = str.begin(); j != str.end(); ++j) { char x[2]; x[0] = *j; x[1] = 0; result->push_back(Refcounter(new Latte_Str(just_one_space, loc, x))); } } return Latte_WsNode::wrap(*result, ws); } }; class IncludeOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "include"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { if (args == args_end) throw InsufficientArgs(*this, loc); Latte_Stringify sv(0); for (Latte_List::const_iterator i = args; i != args_end; ++i) { const Refcounter &obj = *i; obj->visit(sv); } Latte_Listify lv; latte_load_file(sv.str(), activation, lv); return Latte_WsNode::wrap(lv.release(), ws); } }; class GetenvOp : public Latte_Operator { public: const shstring &name() const { static const shstring n = "getenv"; return n; } protected: Refcounter apply(const Latte_Wstate &ws, const Latte_FileLoc &loc, const Latte_List::const_iterator &args, const Latte_List::const_iterator &args_end, Latte_Activation &activation) const { if (args == args_end) throw InsufficientArgs(*this, loc); Latte_Stringify sv(0); for (Latte_List::const_iterator i = args; i != args_end; ++i) { const Refcounter &obj = *i; obj->visit(sv); } const char *env = getenv(sv.str().c_str()); if (env) return Refcounter(new Latte_Str(ws, loc, shstring(env))); return latte_false(); } }; void Latte_Reader::install_standard_definitions() { // these are operators with multiple names Refcounter cons_op(new ConsOp(1)); Refcounter greater_equal_op(new IneqOp(1, 1)); Refcounter greater_op(new IneqOp(1, 0)); Refcounter less_equal_op(new IneqOp(0, 1)); Refcounter less_op(new IneqOp(0, 0)); Refcounter not_op(new NotOp); Refcounter snoc_op(new ConsOp(0)); Refcounter string_append_op(new StringAppendOp); Refcounter str_ge_op(new StrIneqOp(1, 1)); Refcounter str_gt_op(new StrIneqOp(1, 0)); Refcounter str_le_op(new StrIneqOp(0, 1)); Refcounter str_lt_op(new StrIneqOp(0, 0)); #define DEF m_global_activation->define #define REF Refcounter DEF("add", REF(new AddOp(1))); DEF("and", REF(new AndOp)); DEF("append", REF(new AppendOp)); DEF("apply", REF(new ApplyOp(0))); DEF("ceil", REF(new TruncOp(0))); DEF("concat", string_append_op); DEF("cons", cons_op); DEF("def", REF(new DefOp)); DEF("divide", REF(new MultiplyOp(0))); DEF("downcase", REF(new CaseOp(0))); DEF("empty?", not_op); DEF("equal?", REF(new EqualOp)); DEF("error", REF(new WarnOp(1))); DEF("explode", REF(new ExplodeOp)); DEF("floor", REF(new TruncOp(1))); DEF("funcall", REF(new ApplyOp(1))); DEF("ge?", greater_equal_op); DEF("getenv", REF(new GetenvOp)); DEF("greater-equal?", greater_equal_op); DEF("greater?", greater_op); DEF("group?", REF(new TypePredicateOp(TypePredicateOp::tp_group))); DEF("gt?", greater_op); DEF("if", REF(new IfOp)); DEF("include", REF(new IncludeOp)); DEF("file-contents", REF(new FileContentsOp)); #ifdef HAVE_WAIT_H DEF("process-output", REF(new ProcessOutputOp)); #endif // HAVE_WAIT_H DEF("lambda", REF(new Latte_Lambda)); DEF("le?", less_equal_op); DEF("length", REF(new LengthOp)); DEF("less-equal?", less_equal_op); DEF("less?", less_op); DEF("let", REF(new LetOp)); DEF("load-file", REF(new LoadFileOp(1))); DEF("load-library", REF(new LoadFileOp(0))); DEF("lt?", less_op); DEF("macro", REF(new MacroOp)); DEF("modulo", REF(new ModuloOp)); DEF("multiply", REF(new MultiplyOp(1))); DEF("not", not_op); DEF("nth", REF(new NthOp)); DEF("operator?", REF(new TypePredicateOp(TypePredicateOp::tp_operator))); DEF("or", REF(new OrOp)); DEF("push-back", snoc_op); DEF("push-front", cons_op); DEF("random", REF(new RandomOp)); DEF("set!", REF(new SetOp)); DEF("snoc", snoc_op); DEF("string-append", string_append_op); DEF("string-ge?", str_ge_op); DEF("string-greater-equal?", str_ge_op); DEF("string-greater?", str_gt_op); DEF("string-gt?", str_gt_op); DEF("string-le?", str_le_op); DEF("string-less-equal?", str_le_op); DEF("string-less?", str_lt_op); DEF("string-lt?", str_lt_op); DEF("string?", REF(new TypePredicateOp(TypePredicateOp::tp_string))); DEF("subseq", REF(new SubseqOp)); DEF("substr", REF(new SubstrOp)); DEF("subtract", REF(new AddOp(0))); DEF("upcase", REF(new CaseOp(1))); DEF("warn", REF(new WarnOp(0))); DEF("while", REF(new WhileOp)); }