source: cpp/frams/util/extvalue.cpp @ 337

Last change on this file since 337 was 337, checked in by Maciej Komosinski, 9 years ago

New math operator semantics: extend mixed int/float to float, forbid number+notnumber

  • Property svn:eol-style set to native
File size: 28.0 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include "extvalue.h"
6#include <frams/param/param.h>
7#include "sstringutils.h"
8#include <ctype.h>
9#include <frams/vm/classes/collectionobj.h>
10#include <frams/vm/classes/3dobject.h>
11#include <frams/vm/classes/genoobj.h>
12#include <common/nonstd_math.h>
13#include <common/Convert.h>
14#include <climits>
15#include <errno.h>
16
17#ifndef NO_BARRIER
18#include <frams/simul/barrier.h>
19#include <common/threads.h>
20#endif
21
22#ifdef MULTITHREADED
23#include <pthread.h>
24//this lock only protects against ref.counter corruption caused by concurrent reads.
25//read/write conficts and nonatomicity are handled by BarrierObject (at least in theory ;-))
26static pthread_mutex_t extobject_ref_lock = PTHREAD_MUTEX_INITIALIZER;
27#define REF_LOCK pthread_mutex_lock(&extobject_ref_lock)
28#define REF_UNLOCK pthread_mutex_unlock(&extobject_ref_lock)
29#else
30#define REF_LOCK
31#define REF_UNLOCK
32#endif
33
34void ExtObject::incref() const
35{
36        if (subtype & 1)
37        {
38                REF_LOCK;
39                dbobject->refcount++;
40                REF_UNLOCK;
41        }
42}
43
44void ExtObject::decref() const
45{
46        if (subtype & 1)
47        {
48                REF_LOCK;
49                bool destroy = !--dbobject->refcount;
50                REF_UNLOCK;
51                //another thread can now access the object while we are deleting it
52                //but this is not a bug since we only guarantee read/read safety
53                if (destroy) delete dbobject;
54        }
55}
56
57bool ExtObject::operator==(const ExtObject& src) const
58{
59        if (object != src.object) return false;
60        const char* n1 = interfaceName();
61        const char* n2 = src.interfaceName();
62        return (n1 == n2) || (strcmp(n1, n2) == 0);
63}
64
65bool ExtObject::makeUnique()
66{
67        if (!(subtype & 1)) return false;
68        if (dbobject->refcount == 1) return false;
69        VectorObject* v = VectorObject::fromObject(*this, false);
70        if (v)
71        {
72                VectorObject* n = new VectorObject;
73                n->data.setSize(n->data.size());
74                for (int i = 0; i < v->data.size(); i++)
75                {
76                        ExtValue *x = (ExtValue*)v->data(i);
77                        n->data.set(i, x ? new ExtValue(*x) : NULL);
78                }
79                operator=(n->makeObject());
80                return true;
81        }
82        return false;
83}
84
85void* ExtObject::getTarget(const char* classname, bool through_barrier, bool warn) const
86{
87        if (!strcmp(interfaceName(), classname))
88                return getTarget();
89#ifndef NO_BARRIER
90        if (through_barrier)
91        {
92                BarrierObject *bo = BarrierObject::fromObject(*this);
93                if (bo)
94                        return bo->getSourceObject().getTarget(classname, true, warn);
95        }
96#endif
97
98        if (warn)
99        {
100                FMprintf("ExtValue", "getObjectTarget", FMLV_WARN, "%s object expected, %s found", classname, interfaceName());
101        }
102
103        return NULL;
104}
105
106
107SString ExtObject::toString() const
108{
109        if (isEmpty()) return SString("null");
110        Param tmp_param;
111        ParamInterface *p = getParamInterface(tmp_param);
112        int tostr = p->findId("toString");
113        if (tostr >= 0)
114        {
115                return SString(p->getString(tostr));
116        }
117        else
118        {
119                SString tmp("<");
120                tmp += p->getName();
121                tmp += SString::sprintf(" object at %p>", object ? object : paraminterface);
122                return tmp;
123        }
124}
125
126THREAD_LOCAL_DEF(ExtObject::Serialization, ExtObject::serialization);
127
128void ExtObject::Serialization::begin()
129{
130        if (level == 0)
131                refs.clear();
132        level++;
133}
134
135int ExtObject::Serialization::add(const ExtObject &o)
136{
137        if (o.isEmpty()) return -1;
138        for (int i = 0; i < (int)refs.size(); i++)
139        {
140                ExtObject& r = refs[i];
141                if (r == o) return i;
142        }
143        refs.push_back(o);
144        return -1;
145}
146
147void ExtObject::Serialization::replace(const ExtObject& o, const ExtObject& other)
148{
149        if (o.isEmpty()) return;
150        for (int i = 0; i < (int)refs.size(); i++)
151        {
152                ExtObject& r = refs[i];
153                if (r == o)
154                {
155                        r = other;
156                        return;
157                }
158        }
159}
160
161void ExtObject::Serialization::remove(const ExtObject& o)
162{
163        if (o.isEmpty()) return;
164        for (int i = 0; i < (int)refs.size(); i++)
165        {
166                ExtObject& r = refs[i];
167                if (o == r) refs.erase(refs.begin() + i);
168        }
169}
170
171const ExtObject* ExtObject::Serialization::get(int ref)
172{
173        if (ref < 0) return NULL;
174        if (ref >= (int)refs.size()) return NULL;
175        return &refs[ref];
176}
177
178void ExtObject::Serialization::end()
179{
180        level--;
181        if (level == 0)
182                refs.clear();
183}
184
185SString ExtObject::serialize_inner() const
186{
187        int ref = tlsGetRef(serialization).add(*this);
188        if (ref >= 0)
189                return SString::sprintf("^%d", ref);
190
191        if (isEmpty()) return SString("null");
192        VectorObject *vec = VectorObject::fromObject(*this, false);
193        if (vec)
194                return vec->serialize();
195        DictionaryObject *dic = DictionaryObject::fromObject(*this, false);
196        if (dic)
197                return dic->serialize();
198        Param tmp_param;
199        ParamInterface *p = getParamInterface(tmp_param);
200        int m = p->findId("toVector");
201        if (m < 0)
202                m = p->findId("toDictionary");
203        if (m >= 0)
204        {
205                ExtObject o(p->getObject(m));
206                SString ret = SString(interfaceName()) + o.serialize();
207                return ret;
208        }
209        m = p->findId("toString");
210        if (m >= 0)
211        {
212                SString str = p->getString(m);
213                sstringQuote(str);
214                SString ret = SString(interfaceName()) + "\"" + str + "\"";
215                return ret;
216        }
217
218        tlsGetRef(serialization).remove(*this);//undo nonserializable reference
219        SString ret = interfaceName();
220        ret += SString::sprintf("<%p>", object ? object : paraminterface);
221        return ret;
222}
223
224SString ExtObject::serialize() const
225{
226        tlsGetRef(serialization).begin();
227        SString ret = serialize_inner();
228        tlsGetRef(serialization).end();
229        return ret;
230}
231
232///////////////////////////////////////
233
234SString ExtValue::typeDescription() const
235{
236        switch (type)
237        {
238        case TInt: return SString("int");
239        case TDouble: return SString("float");
240        case TString: return SString("string");
241        case TUnknown: return SString("null");
242        case TInvalid: return SString("invalid");
243        case TObj: return getObject().isEmpty() ? SString("null") : SString(getObject().interfaceName());
244        }
245        return SString::empty();
246}
247
248SString ExtValue::typeAndValue() const
249{
250        SString msg = typeDescription();
251        switch (type)
252        {
253        case TInt: case TDouble: case TString: case TObj:
254                msg += " '";
255                msg += getString();
256                msg += "'";
257        default:;
258        }
259        return msg;
260}
261
262void *ExtValue::getObjectTarget(const char* classname, bool warn) const
263{
264        if (type != TObj)
265        {
266                if (warn)
267                {
268                        SString tmp = getString();
269                        if (tmp.len() > 30) tmp = tmp.substr(0, 30) + "...";
270                        if (type == TString) tmp = SString("\"") + tmp + SString("\"");
271                        FMprintf("ExtValue", "getObjectTarget", FMLV_WARN, "%s object expected, %s found", classname, (const char*)tmp);
272                }
273                return NULL;
274        }
275
276        return getObject().getTarget(classname, true, warn);
277}
278
279void ExtValue::set(const ExtValue& src)
280{
281        switch (src.type)
282        {
283        case TString: sets(src.sdata()); break;
284        case TInt: seti(src.idata()); break;
285        case TDouble: setd(src.ddata()); break;
286        case TObj: seto(src.odata()); break;
287        default:type = src.type; break;
288        }
289}
290
291void ExtValue::setEmpty()
292{
293        switch (type)
294        {
295#ifdef EXTVALUEUNION
296        case TString: sdata().~SString(); break;
297        case TObj: odata().~ExtObject(); break;
298#else
299        case TString: delete s; break;
300        case TObj: delete o; break;
301#endif
302        default:;
303        }
304        type = TUnknown;
305}
306
307static ExtValue::CompareResult longsign(paInt x)
308{
309        if (x < 0) return ExtValue::ResultLower;
310        if (x > 0) return ExtValue::ResultHigher;
311        return ExtValue::ResultEqual;
312}
313
314static ExtValue::CompareResult compareNull(const ExtValue& v)
315{
316        if (v.isNull()) return ExtValue::ResultEqualUnordered;
317        if ((v.getType() == TInt) && (v.getInt() == 0)) return ExtValue::ResultUnequal_RelaxedEqual;
318        return ExtValue::ResultUnequal_RelaxedUnequal; //comparing anything else with null is valid but null is neither higher nor lower than numbers or strings
319}
320
321static ExtValue::CompareResult compareFloat(double a, double b)
322{
323        double t = a - b;
324        if (t < 0) return ExtValue::ResultLower;
325        else if (t > 0) return ExtValue::ResultHigher;
326        return ExtValue::ResultEqual;
327}
328
329static ExtValue::CompareResult compareString(const SString &a, const SString &b)
330{
331        const char* s1 = (const char*)a;
332        const char* s2 = (const char*)b;
333        return longsign(strcmp(s1, s2));
334}
335
336ExtValue::CompareResult ExtValue::compare(const ExtValue& src) const
337{
338        if (isNull())
339                return compareNull(src);
340        else if (src.isNull())
341                return compareNull(*this);
342        switch (type)
343        {
344
345        case TInt:
346
347                if (src.getType() == TInt)
348                {
349                        paInt t = src.getInt();
350                        if (idata() > 0)
351                        {
352                                if (t > 0) return longsign(idata() - t); else return ResultHigher;
353                        }
354                        else
355                        {
356                                if (t <= 0) return longsign(idata() - t); else return ResultLower;
357                        }
358                }
359                else if (src.getType() == TDouble)
360                        return compareFloat((double)idata(), src.getDouble());
361                else
362                        return ResultMismatch;//comparing numbers with other things is invalid
363                break;
364
365        case TDouble:
366                if ((src.getType() == TDouble) || (src.getType() == TInt))
367                        return compareFloat(getDouble(), src.getDouble());
368                else
369                        return ResultMismatch;
370                break;
371
372        case TString:
373                if (src.getType() == TString)
374                        return compareString(sdata(), src.getString());
375                else
376                        return ResultMismatch;
377                break;
378
379        case TObj:
380        {
381                if (src.type == TObj)
382                        return odata() == src.odata() ? ResultEqualUnordered : ResultUnequal_RelaxedUnequal;
383                if ((src.type == TInt) && (src.getInt() == 0))
384                        return ResultMismatch_RelaxedUnequal;
385                return ResultMismatch;
386        }
387        default:;
388        }
389        return ResultMismatch;
390}
391
392const char* ExtValue::cmp_op_names[] = { "==", "!=", ">=", "<=", ">", "<", "~=", "!~", NULL };
393
394int ExtValue::interpretCompare(CmpOperator op, CompareResult result, CmpContext *context)
395{
396        CompareResult error_threshold = ResultUnequal_RelaxedEqual;//error when ResultUnequal_RelaxedEqual or higher (not comparable)
397        int ret = 0;
398        switch (op)
399        {
400        case CmpEQ: ret = (result == ResultEqual) || (result == ResultEqualUnordered); error_threshold = ResultMismatch_RelaxedUnequal; break;
401        case CmpNE: ret = !((result == ResultEqual) || (result == ResultEqualUnordered)); error_threshold = ResultMismatch_RelaxedUnequal; break;
402        case CmpGT: ret = (result == ResultHigher); error_threshold = ResultEqualUnordered; break;
403        case CmpGE: ret = (result == ResultEqual) || (result == ResultHigher); error_threshold = ResultEqualUnordered; break;
404        case CmpLT: ret = (result == ResultLower); error_threshold = ResultEqualUnordered; break;
405        case CmpLE: ret = (result == ResultEqual) || (result == ResultLower); error_threshold = ResultEqualUnordered; break;
406        case CmpREQ: ret = (result == ResultEqual) || (result == ResultEqualUnordered) || (result == ResultUnequal_RelaxedEqual); error_threshold = ResultMismatch; break;
407        case CmpRNE: ret = !((result == ResultEqual) || (result == ResultEqualUnordered) || (result == ResultUnequal_RelaxedEqual)); error_threshold = ResultMismatch; break;
408        default:;
409        }
410        if (result >= error_threshold)
411        {
412                SString msg = "Type mismatch while comparing";
413                if (context)
414                {
415                        if (context->v1 && context->v2)
416                                msg += SString::sprintf(": %s %s %s",
417                                (const char*)context->v1->typeAndValue(),
418                                cmp_op_names[op - CmpFIRST],
419                                (const char*)context->v2->typeAndValue());
420                }
421                FMprintf("ExtValue", "interpretCompare", FMLV_ERROR, "%s", (const char*)msg);
422                ret = -1;
423        }
424        return ret;
425}
426
427int ExtValue::operator==(const ExtValue& src) const
428{
429        if (type != src.type) return 0;
430        switch (type)
431        {
432        case TInt: return idata() == src.idata();
433        case TDouble: return ddata() == src.ddata();
434        case TString: return sdata() == src.sdata();
435        case TObj: return odata() == src.odata();
436        default:;
437        }
438        return 1;
439}
440
441void ExtValue::operator+=(const ExtValue& src)
442{
443        // return = ok, break = fail
444        switch (type)
445        {
446        case TInt:
447                switch (src.getType())
448                {
449                case TDouble:
450                        FMprintf("ExtValue", "add", FMLV_WARN, "Adding %s to %s", (const char*)src.typeAndValue(), (const char*)typeAndValue());
451                        setDouble(double(getInt()) + src.getDouble());
452                        return;
453                case TString:
454                        break;
455                default:
456                        idata() += src.getInt();
457                        return;
458                }
459                break;
460        case TDouble:
461                switch (src.getType())
462                {
463                case TString:
464                        break;
465                default:
466                        ddata() += src.getDouble();
467                        return;
468                }
469                break;
470        case TString: sdata() += src.getString(); return;
471        case TObj:
472        {
473                VectorObject *vec = VectorObject::fromObject(getObject(), false);
474                VectorObject *vec2 = VectorObject::fromObject(src.getObject(), false);
475                if (vec && vec2)
476                {
477                        for (int i = 0; i < vec2->data.size(); i++)
478                        {
479                                ExtValue *s = (ExtValue*)vec2->data(i);
480                                ExtValue *d = s ? new ExtValue(*s) : NULL;
481                                vec->data += d;
482                        }
483                        return;
484                }
485        }
486                //NO break;
487        default:;
488        }
489        FMprintf("ExtValue", "add", FMLV_ERROR, "Can't add %s to %s", (const char*)src.typeAndValue(), (const char*)typeAndValue());
490}
491
492void ExtValue::operator-=(const ExtValue& src)
493{
494        // return = ok, break = fail
495        switch (type)
496        {
497        case TInt:
498                switch (src.getType())
499                {
500                case TInt:
501                        idata() -= src.getInt();
502                        return;
503                case TDouble:
504                        FMprintf("ExtValue", "subtract", FMLV_WARN, "Subtracting %s from %s", (const char*)src.typeAndValue(), (const char*)typeAndValue());
505                        setDouble(double(getInt()) - src.getDouble());
506                        return;
507                default:;
508                }
509                break;
510        case TDouble:
511                switch (src.getType())
512                {
513                case TDouble:
514                case TInt:
515                        ddata() -= src.getDouble();
516                        return;
517                default:;
518                }
519                break;
520        default:;
521        }
522        FMprintf("ExtValue", "subtract", FMLV_ERROR, "Can't subtract %s from %s", (const char*)src.typeAndValue(), (const char*)typeAndValue());
523}
524
525void ExtValue::operator*=(const ExtValue& src)
526{
527        // return = ok, break = fail
528        switch (type)
529        {
530        case TInt:
531                switch (src.getType())
532                {
533                case TInt:
534                        idata() *= src.getInt();
535                        return;
536                case TDouble:
537                        FMprintf("ExtValue", "multiply", FMLV_WARN, "Multiplying %s by %s", (const char*)typeAndValue(), (const char*)src.typeAndValue());
538                        setDouble(double(getInt())*src.getDouble());
539                        return;
540                default:;
541                }
542                break;
543        case TDouble:
544                switch (src.getType())
545                {
546                case TInt:
547                case TDouble:
548                        ddata() *= src.getDouble();
549                        return;
550                default:;
551                }
552                break;
553        case TString:
554                switch (src.getType())
555                {
556                case TInt: case TDouble:
557                {
558                        SString t;
559                        for (int n = src.getInt(); n > 0; n--)
560                                t += getString();
561                        setString(t);
562                        return;
563                }
564                default:;
565                }
566                break;
567        case TObj:
568        {
569                VectorObject *vec = VectorObject::fromObject(getObject(), false);
570                if (vec)
571                {
572                        int n = src.getInt();
573                        int orig_size = vec->data.size();
574                        if (n <= 0)
575                        {
576                                vec->clear(); return;
577                        }
578                        for (; n > 1; n--)
579                        {
580                                for (int i = 0; i < orig_size; i++)
581                                {
582                                        ExtValue *s = (ExtValue*)vec->data(i);
583                                        ExtValue *d = s ? new ExtValue(*s) : NULL;
584                                        vec->data += d;
585                                }
586                        }
587                        return;
588                }
589        }
590                //NO break;
591        default:;
592        }
593        FMprintf("ExtValue", "multiply", FMLV_WARN, "Can't multiply %s by %s", (const char*)typeAndValue(), (const char*)src.typeAndValue());
594}
595
596#include <common/framsg.h>
597/*#include "fpu_control.h"
598#include <signal.h>
599
600static int fpuexception;
601void mathhandler(int sig)
602{
603printf("fpu exception!\n");
604fpuexception=1;
605signal(SIGFPE,SIG_IGN);
606} */
607
608void ExtValue::divInt(paInt a)
609{
610        if (a)
611                idata() /= a;
612        else
613        {
614                FMprintf("ExtValue", "divide", FMLV_CRITICAL, "Division by zero: %d/0", idata());
615                setInvalid();
616        }
617}
618
619void ExtValue::divDouble(double a)
620{
621        if (a == 0.0)
622        {
623                FMprintf("ExtValue", "divide", FMLV_CRITICAL, "Division by zero: %s/0.0", (const char*)getString());
624                setInvalid();
625        }
626        else
627        {
628                fpExceptDisable();
629                double tmp = getDouble() / a;
630                if (!finite(tmp))
631                {
632                        FMprintf("ExtValue", "divide", FMLV_CRITICAL, "Overflow %s/%g", (const char*)getString(), a); setInvalid();
633                }
634                else
635                        setDouble(tmp);
636                // niby dobrze ale lepiej byloby to robic bardziej systematycznie a nie tylko w dzieleniu?
637                //if (isnan(ddata())) //http://www.digitalmars.com/d/archives/c++/Traping_divide_by_zero_5728.html
638                //        { FMprintf("ExtValue","divide",FMLV_ERROR,"not-a-number",(const char*)getString()); setInvalid(); }
639                fpExceptEnable();
640        }
641}
642
643void ExtValue::operator/=(const ExtValue& src)
644{
645        switch (type)
646        {
647        case TInt:
648                switch (src.getType())
649                {
650                case TInt:
651                        divInt(src.idata());
652                        return;
653                case TDouble:
654                        FMprintf("ExtValue", "divide", FMLV_WARN, "Dividing %s by %s", (const char*)typeAndValue(), (const char*)src.typeAndValue());
655                        divDouble(src.ddata());
656                        return;
657                default:;
658                }
659                break;
660
661        case TDouble:
662                switch (src.getType())
663                {
664                case TInt:
665                        divDouble(src.getDouble());
666                        return;
667                case TDouble:
668                        divDouble(src.ddata());
669                        return;
670                default:;
671                }
672                break;
673
674        default:;
675        }
676        FMprintf("ExtValue", "divide", FMLV_ERROR, "Can't divide %s by %s", (const char*)typeAndValue(), (const char*)src.typeAndValue());
677}
678
679SString ExtValue::format(SString& fmt, const ExtValue **values, int count)
680{
681        SString ret;
682        // "..........%.........%..........%........"
683        //  ^_cur     ^_next
684        //  ^^^^^^^^^^___sub
685        //
686        // "..........%.........%..........%........"
687        //            ^-cur     ^-next
688        //            ^^^^^^^^^^___sub
689        const char* begin = (const char*)fmt, *end = begin + fmt.len(), *curr = begin;
690        int type = 0;
691
692        class Args
693        {
694                const ExtValue **values;
695                int count;
696                int arg;
697        public:
698                Args(const ExtValue **v, int c) :values(v), count(c), arg(0) {}
699                bool finished() { return arg >= count; }
700                const ExtValue *getNext() { const ExtValue *ret = NULL; if ((arg < count) && values[arg]) ret = values[arg]; arg++; return ret; }
701        };
702        Args args(values, count);
703
704        while (curr < end)
705        {
706                const char* next = strchr(curr, '%');
707                if (!next) next = end; else if ((next == curr) && (curr > begin))
708                {
709                        next = strchr(next + 1, '%'); if (!next) next = end;
710                }
711                type = 0;
712                if (curr > begin)
713                {
714                        type = 0;
715                        for (const char* t = curr; t < next; t++)
716                                switch (*t)
717                        {
718                                case 'd': case 'x': case 'X': case 'u': case 'p': case 'c': type = 'd'; t = next; break;
719                                case 'f': case 'g': case 'e': type = 'f'; t = next; break;
720                                case 's': type = 's'; t = next; break;
721                                case 't': case 'T': type = *t; t = next; break;
722                                case '%': if (t > begin) { type = *t; t = next; } break;
723                        }
724                }
725                if (curr > begin) curr--;
726                const ExtValue *a;
727                if (args.finished() && (type != 0) && (type != '%'))
728                {
729                        ret += fmt.substr((int)(curr - begin));
730                        break;
731                }
732                SString sub = fmt.substr((int)(curr - begin), (int)(next - curr));
733                switch (type)
734                {
735                case 'd': a = args.getNext(); ret += SString::sprintf((const char*)sub, a ? a->getInt() : 0); break;
736                case 'f': a = args.getNext(); ret += SString::sprintf((const char*)sub, a ? a->getDouble() : 0); break;
737                case 's': {a = args.getNext(); SString tmp; if (a) tmp = a->getString(); ret += SString::sprintf((const char*)sub, (const char*)tmp); } break;
738                case 't': case 'T':
739                {
740                        a = args.getNext();
741                        time_t ti = a ? a->getInt() : 0;
742                        struct tm tm = Convert::localtime(ti);
743                        SString timtxt;
744                        if (type == 'T')
745                                timtxt = SString::sprintf("%04d-%02d-%02d %02d:%02d:%02d", 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
746                        else
747                                timtxt = Convert::asctime(tm).c_str();
748                        ret += timtxt;
749                        ret += sub.substr(2);
750                }
751                        break;
752                case '%': ret += '%'; ret += sub.substr(2); break;
753                case 0: ret += sub; break;
754                }
755                curr = next + 1;
756        }
757        return ret;
758}
759
760
761void ExtValue::operator%=(const ExtValue& src)
762{
763        switch (type)
764        {
765        case TInt: idata() = idata() % src.getInt(); break;
766        case TDouble: ddata() = fmod(ddata(), src.getDouble()); break;
767
768        case TString:
769        {
770                VectorObject *vec = VectorObject::fromObject(src.getObject(), false);
771                if (vec)
772                        sdata() = format(sdata(), (const ExtValue**)&vec->data.getref(0), vec->data.size());
773                else
774                {
775                        const ExtValue *ptr = &src; sdata() = ExtValue::format(sdata(), &ptr, 1);
776                }
777        }
778                break;
779
780        case TObj: case TUnknown: case TInvalid:
781                FMprintf("ExtValue", "modulo", FMLV_WARN, "Can't apply modulo to %s", (const char*)typeDescription());
782
783        default:;
784        }
785}
786
787bool ExtValue::parseInt(const char* s, paInt &result, bool strict, bool error)
788{
789        ExtValue tmp;
790        const char* after = tmp.parseNumber(s, strict ? TInt : TUnknown);
791        if ((after == NULL) || (after[0] != 0))
792        {
793                if (error)
794                        FMprintf("ExtValue", "parseInt", FMLV_ERROR, "Could not parse '%s'%s", s, strict ? " (strict)" : "");
795                return false;
796        }
797        result = tmp.getInt();
798        return true;
799}
800
801bool ExtValue::parseDouble(const char* s, double &result, bool error)
802{
803        ExtValue tmp;
804        const char* after = tmp.parseNumber(s, TDouble);
805        if ((after == NULL) || (after[0] != 0))
806        {
807                if (error)
808                        FMprintf("ExtValue", "parseDouble", FMLV_ERROR, "Could not parse '%s'", s);
809                return false;
810        }
811        result = tmp.getDouble();
812        return true;
813}
814
815paInt ExtValue::getInt(const char* s, bool strict)
816{
817        paInt result;
818        if (parseInt(s, result, strict, true))
819                return result;
820        return 0;
821}
822
823double ExtValue::getDouble(const char* s)
824{
825        double result;
826        if (parseDouble(s, result, true))
827                return result;
828        return 0;
829}
830
831paInt ExtValue::getInt() const
832{
833        switch (type)
834        {
835        case TInt: return idata();
836        case TDouble: return (int)ddata();
837        case TString: return getInt((const char*)sdata());
838        case TObj:
839                FMprintf("ExtValue", "getInt", FMLV_WARN, "Getting integer value from object reference (%s)", (const char*)getString());
840                return (paInt)(intptr_t)odata().param;
841        default:;
842        }
843        return 0;
844}
845
846double ExtValue::getDouble() const
847{
848        switch (type)
849        {
850        case TDouble: return ddata();
851        case TInt: return (double)idata();
852        case TString: return getDouble((const char*)sdata());
853        case TObj:
854                FMprintf("ExtValue", "getDouble", FMLV_WARN, "Getting floating point value from object reference (%s)", (const char*)getString());
855                return (double)(intptr_t)odata().param;
856        default:;
857        }
858        return 0.0;
859}
860
861SString ExtValue::getString() const
862{
863        switch (type)
864        {
865        case TString: return sdata();
866        case TInt: return SString::valueOf(idata());
867        case TDouble: return SString::valueOf(ddata());
868        case TObj: return odata().toString();
869        case TInvalid:  return SString("invalid");
870        default: return SString("null");
871        }
872}
873
874const SString* ExtValue::getStringPtr() const
875{
876        if (type == TString)
877                return &sdata();
878        return NULL;
879}
880
881SString ExtValue::serialize() const
882{
883        switch (type)
884        {
885        case TString:
886        {
887                SString q = sdata();
888                sstringQuote(q);
889                return SString("\"") + q + SString("\"");
890        }
891        case TInt:
892                return SString::valueOf(idata());
893        case TDouble:
894                return SString::valueOf(ddata());
895        case TObj:
896                return odata().serialize();
897        case TInvalid:
898                return SString("invalid");
899        default:
900                return SString("null");
901        }
902}
903
904/// returns the first character after the parsed number, or NULL if not a number
905/// @param strict_type = restrict the allowed return value (TUnknown = unrestricted)
906const char* ExtValue::parseNumber(const char* in, ExtPType strict_type)
907{
908        char* after;
909        if (in == NULL) return NULL;
910        if (in[0] == 0) return NULL;
911        while (isspace(*in)) in++;
912        bool minus = (in[0] == '-');
913        bool plus = (in[0] == '+');
914        if (((in[0] == '0') && ((in[1] == 'x') || (in[1] == 'X')))
915                || (((minus || plus) && (in[1] == '0') && ((in[2] == 'x') || (in[2] == 'X')))))
916        {
917                in += (minus || plus) ? 3 : 2;
918                if (isspace(*in)) return NULL;
919                errno = 0;
920                unsigned long intvalue = strtoul(in, &after, 16);
921                if ((after > in) && (errno == 0) && (intvalue <= 0xffffffff))
922                {
923                        if (strict_type == TDouble)
924                                setDouble(minus ? -(double)intvalue : (double)intvalue);
925                        else
926                                setInt(minus ? -intvalue : intvalue);
927                        return after;
928                }
929                else
930                        return NULL;
931        }
932
933        errno = 0;
934        double fpvalue = strtod(in, &after);
935        if ((after > in) && (errno == 0))
936        {
937                if (strict_type != TDouble)
938                {
939                        if ((memchr(in, '.', after - in) == NULL) && (memchr(in, 'e', after - in) == NULL) && (memchr(in, 'E', after - in) == NULL) // no "special" characters
940                                && (fpvalue == floor(fpvalue)) // value is integer
941                                && (fpvalue >= INT_MIN) && (fpvalue <= INT_MAX)) // within limits
942                        {
943                                setInt(fpvalue);
944                                return after;
945                        }
946                        else if (strict_type == TInt)
947                                return NULL;
948                }
949                setDouble(fpvalue);
950                return after;
951        }
952        return NULL;
953}
954
955PtrListTempl<ParamInterface*> &ExtValue::getDeserializableClasses()
956{
957        static PtrListTempl<ParamInterface*> classes;
958        return classes;
959}
960
961ParamInterface *ExtValue::findDeserializableClass(const char* name)
962{
963        FOREACH(ParamInterface*, cls, getDeserializableClasses())
964                if (!strcmp(cls->getName(), name))
965                        return cls;
966        return NULL;
967}
968
969static const char* skipWord(const char* in)
970{
971        while (isalpha(*in) || (*in == '_'))
972                in++;
973        return in;
974}
975
976//returns the first character after the parsed portion or NULL if invalid format
977const char* ExtValue::deserialize_inner(const char* in)
978{
979        const char* ret = parseNumber(in);
980        if (ret)
981                return ret;
982        else if (*in == '\"')
983        {
984                ret = skipQuoteString(in + 1, NULL);
985                SString s(in + 1, (int)(ret - (in + 1)));
986                sstringUnquote(s);
987                setString(s);
988                if (*ret == '\"')
989                        return ret + 1;
990                else
991                {
992                        FMprintf("ExtValue", "deserialize", FMLV_ERROR, "Missing '\"' in string: '%s'", ret);
993                        return NULL;
994                }
995        }
996        else if (*in == '[')
997        {
998                VectorObject *vec = new VectorObject;
999                ExtObject o(&VectorObject::par, vec);
1000                tlsGetRef(ExtObject::serialization).add(o);
1001                const char* p = in + 1;
1002                ExtValue tmp;
1003                while (*p)
1004                {
1005                        if (*p == ']') { p++; break; }
1006                        ret = tmp.deserialize(p);
1007                        if (ret)
1008                        {
1009                                vec->data += new ExtValue(tmp);
1010                                p = ret;
1011                                if (*p == ',') p++;
1012                                else if (*p != ']')
1013                                {
1014                                        FMprintf("ExtValue", "deserialize", FMLV_ERROR, "Missing ',' in Vector: '%s'", p);
1015                                        return NULL;
1016                                }
1017                        }
1018                        else
1019                        {
1020                                p = NULL;
1021                                break;
1022                        }
1023                }
1024                setObject(o);
1025                return p;
1026        }
1027        else if (*in == '{')
1028        {
1029                DictionaryObject *dic = new DictionaryObject;
1030                ExtObject o(&DictionaryObject::par, dic);
1031                tlsGetRef(ExtObject::serialization).add(o);
1032                const char* p = in + 1;
1033                ExtValue args[2]/*={value,key}*/, dummy_ret;
1034                while (*p)
1035                {
1036                        if (*p == '}') { p++; break; }
1037                        ret = args[1].deserialize(p);
1038                        if ((!ret) || (args[1].getType() != TString)) { p = NULL; break; }
1039                        p = ret;
1040                        if (*p != ':') { FMprintf("ExtValue", "deserialize", FMLV_ERROR, "Missing ':' in Dictionary: '%s'", p); p = NULL; break; }
1041                        p++;
1042                        ret = args[0].deserialize(p);
1043                        if (!ret) { p = NULL; break; }
1044                        p = ret;
1045                        dic->p_set(args, &dummy_ret);
1046                        if (*p == ',') p++;
1047                        else if (*p != '}')
1048                        {
1049                                FMprintf("ExtValue", "deserialize", FMLV_ERROR, "Missing ',' in Dictionary: '%s'", p);
1050                                return NULL;
1051                        }
1052                }
1053                setObject(o);
1054                return p;
1055        }
1056        else if (!strncmp(in, "null", 4))
1057        {
1058                setEmpty();
1059                return in + 4;
1060        }
1061        else if (!strncmp(in, "invalid", 9))
1062        {
1063                setInvalid();
1064                return in + 9;
1065        }
1066        else if (*in == '<')
1067        { //unserializable object
1068                setInvalid();
1069                while (*in)
1070                        if (*in == '>')
1071                                return in + 1;
1072                        else in++;
1073                        return in;
1074        }
1075        else if (*in == '^')
1076        {
1077                in++;
1078                ExtValue ref;
1079                ret = ref.parseNumber(in, TInt);
1080                if (ret && (ref.getType() == TInt))
1081                {
1082                        const ExtObject* o = tlsGetRef(ExtObject::serialization).get(ref.getInt());
1083                        if (o)
1084                        {
1085                                setObject(*o);
1086                                return ret;
1087                        }
1088                }
1089                FMprintf("ExtValue", "deserialize", FMLV_ERROR, "Invalid reference: '%s'", in - 1);
1090                return NULL;
1091        }
1092        else if ((ret = skipWord(in)) && (ret != in))
1093        {
1094                SString clsname(in, (int)(ret - in));
1095                ExtValue tmp;
1096                ret = tmp.deserialize(ret);
1097                ParamInterface *cls = findDeserializableClass(clsname);
1098                if (cls && (tmp.getType() != TUnknown) && (tmp.getType() != TInvalid))
1099                {
1100                        VectorObject *vec = VectorObject::fromObject(tmp.getObject(), false);
1101                        if (vec)
1102                        {
1103                                int m = cls->findId("newFromVector");
1104                                if (m >= 0)
1105                                {
1106                                        cls->call(m, &tmp, this);
1107                                        tlsGetRef(ExtObject::serialization).replace(tmp.getObject(), getObject());
1108                                        return ret;
1109                                }
1110                        }
1111                        DictionaryObject *dic = DictionaryObject::fromObject(tmp.getObject(), false);
1112                        if (dic)
1113                        {
1114                                int m = cls->findId("newFromDictionary");
1115                                if (m >= 0)
1116                                {
1117                                        cls->call(m, &tmp, this);
1118                                        tlsGetRef(ExtObject::serialization).replace(tmp.getObject(), getObject());
1119                                        return ret;
1120                                }
1121                        }
1122                        if (tmp.getType() == TString)
1123                        {
1124                                int m = cls->findId("newFromString");
1125                                if (m >= 0)
1126                                {
1127                                        cls->call(m, &tmp, this);
1128                                        tlsGetRef(ExtObject::serialization).replace(tmp.getObject(), getObject());
1129                                        return ret;
1130                                }
1131                        }
1132                        tlsGetRef(ExtObject::serialization).remove(tmp.getObject());
1133                        setEmpty();
1134                }
1135                setEmpty();
1136                FMprintf("ExtValue", "deserialize", FMLV_WARN, "object of class \"%s\" could not be deserialized", (const char*)clsname);
1137                return ret;
1138        }
1139        FMprintf("ExtValue", "deserialize", FMLV_ERROR, "Bad syntax: '%s'", in);
1140        setEmpty();
1141        return NULL;
1142}
1143
1144const char* ExtValue::deserialize(const char* in)
1145{
1146        tlsGetRef(ExtObject::serialization).begin();
1147        const char* ret = deserialize_inner(in);
1148        tlsGetRef(ExtObject::serialization).end();
1149        return ret;
1150}
1151
1152ExtObject ExtValue::getObject() const
1153{
1154        if (type == TObj) return odata();
1155        return ExtObject();
1156}
1157
1158ExtValue ExtValue::getExtType()
1159{
1160        static const char* typenames[] = { "null", "int", "float", "string", "", "invalid" };
1161        if (getType() != TObj)
1162                return ExtValue(typenames[(int)getType()]);
1163        ExtObject& o = odata();
1164        return ExtValue(SString(o.isEmpty() ? "" : o.interfaceName()));
1165}
1166
1167SString SString::valueOf(const ExtValue& v)
1168{
1169        return v.getString();
1170}
1171SString SString::valueOf(const ExtObject& v)
1172{
1173        return v.toString();
1174}
Note: See TracBrowser for help on using the repository browser.