/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* $Rev$ $Date$ */ #ifndef tuscany_value_hpp #define tuscany_value_hpp /** * Generic value type. */ #include #include #include #include "string.hpp" #include "sstream.hpp" #include "gc.hpp" #include "function.hpp" #include "list.hpp" #include "monad.hpp" namespace tuscany { #ifdef WANT_MAINTAINER_COUNTERS /** * Debug utilities. Counters used to track instances of values */ long int countValues = 0; long int countEValues = 0; long int countCValues = 0; long int countVValues = 0; inline const bool resetValueCounters() { countValues = countEValues = countCValues = countVValues = 0; return true; } inline const bool checkValueCounters() { return countValues == 0; } inline const bool printValueCounters() { cout << "countValues " << countValues << endl; cout << "countEValues " << countEValues << endl; cout << "countCValues " << countCValues << endl; cout << "countVValues " << countVValues << endl; return true; } #else #define resetValueCounters() #define checkValueCounters() true #define printValueCounters() #endif #ifdef WANT_MAINTAINER_WATCH /** * Debug utilities. Macro used to write the contents of a value to * a string, easier to watch in a debugger than the value itself. */ #define debug_watchValue() do { \ this->watch = watchValue(*this); \ } while (0) #else #define debug_watchValue() #endif /** * Common value constants. */ class value; extern const value nilValue; extern const list nilListValue; extern const list nilPairValue; extern const value emptyStringValue; extern const value trueValue; extern const value falseValue; /** * Common value-based lambda types. */ typedef lambda&)> lvvlambda; typedef lambda vvlambda; typedef lambda vblambda; typedef lambda vlambda; /** * Generic value type class. */ class value { public: enum ValueType { Nil, Symbol, String, List, Number, Bool, Lambda, Ptr }; typedef union { const void* mix; const gc_ptr > lst; const string str; const lvvlambda func; const gc_ptr ptr; const double num; const bool boo; } ValueMix; inline value() noexcept : type(value::Nil) { debug_inc(countValues); debug_inc(countEValues); debug_watchValue(); } inline value(const value& v) noexcept : type(v.type) { debug_inc(countValues); debug_inc(countCValues); memcpy((void*)&mix, (void*)&v.mix, sizeof(ValueMix)); #ifdef WANT_MAINTAINER_WATCH watch = v.watch; #endif } inline value(const gc_mutable_ref& r) noexcept : type(((value*)r)->type) { debug_inc(countValues); debug_inc(countCValues); memcpy((void*)&mix, (void*)&(((value*)r)->mix), sizeof(ValueMix)); #ifdef WANT_MAINTAINER_WATCH watch = v.watch; #endif } inline virtual ~value() noexcept { debug_dec(countValues); } inline value(const lvvlambda& f) noexcept : type(value::Lambda), func(f) { debug_inc(countValues); debug_inc(countVValues); debug_watchValue(); } inline value(const string& s) noexcept : type(value::String), str(s) { debug_inc(countValues); debug_inc(countVValues); debug_watchValue(); } inline value(const char* s) noexcept : type(value::Symbol), str(s) { debug_inc(countValues); debug_inc(countVValues); debug_watchValue(); } inline value(const list& l) noexcept : type(value::List), lst(new (gc_new >()) list(l)) { debug_inc(countValues); debug_inc(countVValues); debug_watchValue(); } inline value(const list >& l) noexcept : type(value::List), lst(new (gc_new >()) list(listOfValues(l))) { debug_inc(countValues); debug_inc(countVValues); debug_watchValue(); } inline value(const double d) noexcept : type(value::Number), num(d) { debug_inc(countValues); debug_inc(countVValues); debug_watchValue(); } inline value(const int i) noexcept : type(value::Number), num((double)i) { debug_inc(countValues); debug_inc(countVValues); debug_watchValue(); } inline value(const bool b) noexcept : type(value::Bool), boo(b) { debug_inc(countValues); debug_inc(countVValues); debug_watchValue(); } inline value(const gc_ptr p) noexcept : type(value::Ptr), ptr(p) { debug_inc(countValues); debug_inc(countVValues); debug_watchValue(); } inline value(const failable& m) noexcept : type(value::List), lst(new (gc_new >()) list(hasContent(m)? mklist(content(m)) : rcode(m) == 1? mklist(nilValue, reason(m)) : mklist(nilValue, reason(m), rcode(m)))) { debug_inc(countValues); debug_inc(countVValues); debug_watchValue(); } inline value(const maybe& m) noexcept : type(value::List), lst(new (gc_new >()) list(hasContent(m)? mklist(content(m)) : nilListValue)) { debug_inc(countValues); debug_inc(countVValues); debug_watchValue(); } value& operator=(const value& v) = delete; inline const bool operator!=(const value& v) const noexcept { return !this->operator==(v); } inline const bool operator==(const value& v) const noexcept { if(this == &v) return true; switch(type) { case value::Nil: return v.type == value::Nil; case value::List: return v.type == value::List && *lst == *v.lst; case value::Lambda: return v.type == value::Lambda && func == v.func; case value::Symbol: case value::String: return (v.type == value::Symbol || v.type == value::String) && str == v.str; case value::Number: return v.type == value::Number && num == v.num; case value::Bool: return v.type == value::Bool && boo == v.boo; case value::Ptr: return v.type == value::Ptr && ptr == v.ptr; default: return false; } } inline const bool operator<(const value& v) const noexcept { if(this == &v) return false; switch(type) { case value::List: return v.type == value::List && *lst < *v.lst; case value::Symbol: case value::String: return (v.type == value::Symbol || v.type == value::String) && str < v.str; case value::Bool: return v.type == value::Bool && boo < v.boo; case value::Number: return v.type == value::Number && num < v.num; default: return false; } } inline const bool operator>(const value& v) const noexcept { if(this == &v) return false; switch(type) { case value::List: return v.type == value::List && *lst > *v.lst; case value::Symbol: case value::String: return (v.type == value::Symbol || v.type == value::String) && str > v.str; case value::Bool: return v.type == value::Bool && boo > v.boo; case value::Number: return v.type == value::Number && num > v.num; default: return false; } } inline const value operator()(const list& args) const noexcept { return func(args); } inline operator const string() const noexcept { switch(type) { case value::Symbol: case value::String: return str; case value::Number: { ostringstream os; os << num; return tuscany::str(os); } case value::Bool: return boo? trueString : falseString; default: return emptyString; } } inline operator const double() const noexcept { switch(type) { case value::Symbol: case value::String: return atof(c_str(str)); case value::Number: return (double)num; case value::Bool: return boo? 1.0 : 0.0; default: return 0.0; } } inline operator const int() const noexcept { switch(type) { case value::Symbol: case value::String: return atoi(c_str(str)); case value::Number: return (int)num; case value::Bool: return boo? 1 : 0; default: return 0; } } inline operator const bool() const noexcept { switch(type) { case value::Symbol: case value::String: return str == trueString; case value::Number: return num != 0.0; case value::Bool: return boo; default: return false; } } inline operator const gc_ptr() const noexcept { return ptr; } inline operator const list() const noexcept { return *lst; } inline operator const list >() const noexcept { return listOfListOfValues(*lst); } inline operator const lvvlambda() const noexcept { return func; } private: inline const list listOfValues(const list >& l) const noexcept { if (isNil(l)) return nilListValue; return cons(car(l), listOfValues(cdr(l))); } inline const list > listOfListOfValues(const list& l) const noexcept { if (isNil(l)) return list >(); return cons >(car(l).type == value::List? list(car(l)) : nilPairValue, listOfListOfValues(cdr(l))); } friend ostream& operator<<(ostream&, const value&) noexcept; friend const value::ValueType type(const value& v) noexcept; friend const bool setvalue(value& target, const value& v); #ifdef WANT_MAINTAINER_WATCH friend const string watchValue(const value& v); string watch; #endif const ValueType type; union { void* mix; const gc_ptr > lst; const string str; const lvvlambda func; const gc_ptr ptr; const double num; const bool boo; }; }; /** * Common value constants. */ const value nilValue; const list nilListValue = list(); const list nilPairValue = mklist(nilValue, nilValue); const value emptyStringValue(emptyString); const value trueValue(true); const value falseValue(false); #ifdef WANT_MAINTAINER_WATCH /** * Debug utility used to write the contents of a value to a string, easier * to watch than the value itself in a debugger. */ inline const string watchValue(const value& v) { if (v.type == value::List) return watchList(v); odebugstream os; os << v; return str(os); } #endif /** * Write an escape string to a buffer. */ inline const char* escapestr(const char* const s, char* const buf) noexcept { if (*s == '\0') { *buf = '\0'; return buf; } if (*s == '\\' || *s == '"') { *buf = '\\'; *(buf + 1) = *s; return escapestr(s + 1, buf + 2); } *buf = *s; return escapestr(s + 1, buf + 1); } /** * Write an escaped string value to a stream. */ inline ostream& escvwrite(const string& str, ostream& out) noexcept { char* buf = gc_cnew(length(str) * 2 + 1); escapestr(c_str(str), buf); out << buf; return out; } /** * Write a value to a stream. */ inline ostream& operator<<(ostream& out, const value& v) noexcept { switch(v.type) { case value::List: return out << *v.lst; case value::Lambda: return out << "lambda::" << v.func; case value::Symbol: return out << v.str; case value::String: out << '\"'; escvwrite(v.str, out); return out << '\"'; case value::Number: return out << v.num; case value::Bool: if(v.boo) return out << trueString; else return out << falseString; case value::Ptr: { if (v.ptr == gc_ptr(NULL)) return out << "gc_ptr::null"; return out << "gc_ptr::" << v.ptr; } default: return out << "nil"; } } /** * Returns the type of a value. */ inline const value::ValueType type(const value& v) noexcept { return v.type; } /** * Returns true if a value is nil. */ inline const bool isNil(const value& v) noexcept { return type(v) == value::Nil; } /** * Returns true if a value is a lambda. */ inline const bool isLambda(const value& v) noexcept { return type(v) == value::Lambda; } /** * Returns true if a value is a string. */ inline const bool isString(const value& v) noexcept { return type(v) == value::String; } /** * Returns true if a value is a symbol. */ inline const bool isSymbol(const value& v) noexcept { return type(v) == value::Symbol; } /** * Returns true if a value is a list. */ inline const bool isList(const value& v) noexcept { return type(v) == value::List; } /** * Returns true if a value is a number. */ inline const bool isNumber(const value& v) noexcept { return type(v) == value::Number; } /** * Returns true if a value is a boolean. */ inline const bool isBool(const value& v) noexcept { return type(v) == value::Bool; } /** * Returns true if a value is a pointer. */ inline const bool isPtr(const value& v) noexcept { return type(v) == value::Ptr; } /** * Returns true if a value is a tagged list. */ inline const bool isTaggedList(const value& exp, const value& tag) noexcept { if(isList(exp) && !isNil((list)exp)) return car((list)exp) == tag; return false; } /** * Make a list of values from a list of other things. */ template inline const list mkvalues(const list& l) noexcept { if (isNil(l)) return nilListValue; return cons(car(l), mkvalues(cdr(l))); } /** * Convert a list of values to a list of other things. */ template inline const list convertValues(const list& l) noexcept { if (isNil(l)) return list(); return cons(car(l), convertValues(cdr(l))); } /** * Convert a path string value to a list of values. */ inline const list pathTokens(const char* const p) noexcept { if (p == NULL || p[0] == '\0') return list(); if (p[0] == '/') return tokenize("/", p + 1); return tokenize("/", p); } inline const list pathValues(const value& p) noexcept { return mkvalues(pathTokens(c_str(p))); } /** * Convert a path represented as a list of values to a string value. */ inline const value path(const list& p) noexcept { if (isNil(p)) return emptyString; return string("/") + (string)car(p) + (string)path(cdr(p)); } /** * Make a uuid value. */ inline const value mkuuid() noexcept { apr_uuid_t id; apr_uuid_get(&id); char buf[APR_UUID_FORMATTED_LENGTH]; apr_uuid_format(buf, &id); return value(string(buf, APR_UUID_FORMATTED_LENGTH)); } /** * Make a random alphanumeric value. */ inline const int intrand() noexcept { const apr_uint64_t now = apr_time_now(); srand((unsigned int)(((now >> 32) ^ now) & 0xffffffff)); return rand() & 0x0FFFF; } inline const value mkrand() noexcept { char buf[32]; const char* const an = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; for (int i =0; i < 32; i++) buf[i] = an[intrand() % 62]; return value(string(buf, 32)); } /** * Set a value. Use with moderation. */ inline const bool setvalue(value& target, const value& v) { if (&target == &v) return true; #ifdef WANT_MAINTAINER_WATCH memcpy(&target.watch, &v.watch, sizeof(string)); #endif memcpy((void*)&target.type, (void*)&v.type, sizeof(value::ValueType)); memcpy((void*)&target.mix, (void*)&v.mix, sizeof(value::ValueMix)); return true; } } #endif /* tuscany_value_hpp */