1742 lines
44 KiB
C
1742 lines
44 KiB
C
/*
|
|
* mooscript-value.c
|
|
*
|
|
* Copyright (C) 2004-2006 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* See COPYING file that comes with this distribution.
|
|
*/
|
|
|
|
#include "mooscript-value-private.h"
|
|
#include "mooscript-func-private.h"
|
|
#include "mooscript-context.h"
|
|
#include <string.h>
|
|
|
|
|
|
static MSValue *MS_None;
|
|
static MSValue *MS_True;
|
|
static MSValue *MS_False;
|
|
|
|
static MSValueClass types[MS_VALUE_INVALID];
|
|
|
|
#if GLIB_CHECK_VERSION(2,10,0)
|
|
#define ms_value_alloc() g_slice_new0 (MSValue)
|
|
#define ms_value_free(v) g_slice_free (MSValue, v)
|
|
#define ms_value_array_alloc(n) g_slice_alloc (n * sizeof (MSValue*))
|
|
#define ms_value_array_free(a, n) g_slice_free1 (n * sizeof (MSValue*), a)
|
|
#else
|
|
#define ms_value_alloc() g_new0 (MSValue, 1)
|
|
#define ms_value_free(v) g_free (v)
|
|
#define ms_value_array_alloc(n) g_new (MSValue*, n)
|
|
#define ms_value_array_free(a, n) g_free (a)
|
|
#endif
|
|
|
|
static MSValue *
|
|
ms_value_new (MSValueClass *klass)
|
|
{
|
|
MSValue *val = ms_value_alloc ();
|
|
val->ref_count = 1;
|
|
val->klass = klass;
|
|
return val;
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_none (void)
|
|
{
|
|
if (!MS_None)
|
|
return MS_None = ms_value_new (&types[MS_VALUE_NONE]);
|
|
else
|
|
return ms_value_ref (MS_None);
|
|
}
|
|
|
|
|
|
gboolean
|
|
ms_value_is_none (MSValue *value)
|
|
{
|
|
g_return_val_if_fail (value != NULL, FALSE);
|
|
return value == MS_None;
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_false (void)
|
|
{
|
|
if (!MS_False)
|
|
return MS_False = ms_value_int (FALSE);
|
|
else
|
|
return ms_value_ref (MS_False);
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_true (void)
|
|
{
|
|
if (!MS_True)
|
|
return MS_True = ms_value_int (TRUE);
|
|
else
|
|
return ms_value_ref (MS_True);
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_bool (gboolean val)
|
|
{
|
|
return val ? ms_value_true () : ms_value_false ();
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_int (int ival)
|
|
{
|
|
MSValue *val = ms_value_new (&types[MS_VALUE_INT]);
|
|
val->u.ival = ival;
|
|
return val;
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_string (const char *string)
|
|
{
|
|
return ms_value_take_string (g_strdup (string));
|
|
}
|
|
|
|
MSValue *
|
|
ms_value_string_printf (const char *format,
|
|
...)
|
|
{
|
|
MSValue *value;
|
|
va_list args;
|
|
|
|
va_start (args, format);
|
|
value = ms_value_take_string (_ms_vaprintf (format, args));
|
|
va_end (args);
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_string_len (const char *string,
|
|
int chars)
|
|
{
|
|
if (chars < 0)
|
|
return ms_value_string (string);
|
|
else
|
|
return ms_value_take_string (g_strndup (string,
|
|
g_utf8_offset_to_pointer (string, chars) - string));
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_take_string (char *string)
|
|
{
|
|
MSValue *val;
|
|
|
|
if (!string)
|
|
return ms_value_none ();
|
|
|
|
val = ms_value_new (&types[MS_VALUE_STRING]);
|
|
val->u.str = string;
|
|
return val;
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_gvalue (const GValue *gval)
|
|
{
|
|
MSValue *val;
|
|
g_return_val_if_fail (G_IS_VALUE (gval), NULL);
|
|
val = ms_value_new (&types[MS_VALUE_GVALUE]);
|
|
|
|
val->u.gval = g_new (GValue, 1);
|
|
val->u.gval->g_type = 0;
|
|
g_value_init (val->u.gval, G_VALUE_TYPE (gval));
|
|
g_value_copy (gval, val->u.gval);
|
|
|
|
return val;
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_from_gvalue (const GValue *gval)
|
|
{
|
|
char c_val;
|
|
const char *s_val;
|
|
|
|
g_return_val_if_fail (gval != NULL, NULL);
|
|
|
|
switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (gval)))
|
|
{
|
|
case G_TYPE_CHAR:
|
|
c_val = g_value_get_char (gval);
|
|
return c_val ? ms_value_string_len (&c_val, 1) : ms_value_none ();
|
|
case G_TYPE_UCHAR:
|
|
c_val = g_value_get_uchar (gval);
|
|
return c_val ? ms_value_string_len (&c_val, 1) : ms_value_none ();
|
|
case G_TYPE_BOOLEAN:
|
|
return ms_value_bool (g_value_get_boolean (gval));
|
|
case G_TYPE_INT:
|
|
return ms_value_int (g_value_get_int (gval));
|
|
case G_TYPE_UINT:
|
|
return ms_value_int (g_value_get_uint (gval));
|
|
case G_TYPE_LONG:
|
|
return ms_value_int (g_value_get_long (gval));
|
|
case G_TYPE_ULONG:
|
|
return ms_value_int (g_value_get_ulong (gval));
|
|
case G_TYPE_INT64:
|
|
return ms_value_int (g_value_get_int64 (gval));
|
|
case G_TYPE_UINT64:
|
|
return ms_value_int (g_value_get_uint64 (gval));
|
|
|
|
case G_TYPE_STRING:
|
|
s_val = g_value_get_string (gval);
|
|
return s_val ? ms_value_string (s_val) : ms_value_none ();
|
|
|
|
case G_TYPE_BOXED:
|
|
case G_TYPE_OBJECT:
|
|
case G_TYPE_POINTER:
|
|
case G_TYPE_DOUBLE:
|
|
case G_TYPE_FLOAT:
|
|
case G_TYPE_FLAGS:
|
|
case G_TYPE_ENUM:
|
|
return ms_value_gvalue (gval);
|
|
|
|
default:
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
}
|
|
|
|
|
|
gpointer
|
|
ms_value_get_object (MSValue *value)
|
|
{
|
|
g_return_val_if_fail (value != NULL, NULL);
|
|
g_return_val_if_fail (MS_VALUE_TYPE (value) == MS_VALUE_GVALUE, NULL);
|
|
return g_value_get_object (value->u.gval);
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_object (gpointer object)
|
|
{
|
|
GValue gval;
|
|
MSValue *val;
|
|
|
|
g_return_val_if_fail (!object || G_IS_OBJECT (object), NULL);
|
|
|
|
gval.g_type = 0;
|
|
g_value_init (&gval, G_TYPE_OBJECT);
|
|
g_value_set_object (&gval, object);
|
|
val = ms_value_gvalue (&gval);
|
|
g_value_unset (&gval);
|
|
|
|
return val;
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_list (guint n_elms)
|
|
{
|
|
MSValue *val;
|
|
guint i;
|
|
|
|
val = ms_value_new (&types[MS_VALUE_LIST]);
|
|
val->u.list.elms = ms_value_array_alloc (n_elms);
|
|
val->u.list.n_elms = n_elms;
|
|
|
|
for (i = 0; i < n_elms; ++i)
|
|
val->u.list.elms[i] = ms_value_none ();
|
|
|
|
return val;
|
|
}
|
|
|
|
|
|
void
|
|
ms_value_list_set_elm (MSValue *list,
|
|
guint index,
|
|
MSValue *elm)
|
|
{
|
|
g_return_if_fail (list != NULL);
|
|
g_return_if_fail (elm != NULL);
|
|
g_return_if_fail (MS_VALUE_TYPE (list) == MS_VALUE_LIST);
|
|
g_return_if_fail (index < list->u.list.n_elms);
|
|
|
|
if (list->u.list.elms[index] != elm)
|
|
{
|
|
ms_value_unref (list->u.list.elms[index]);
|
|
list->u.list.elms[index] = ms_value_ref (elm);
|
|
}
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_dict (void)
|
|
{
|
|
MSValue *val;
|
|
|
|
val = ms_value_new (&types[MS_VALUE_DICT]);
|
|
val->u.hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
|
|
(GDestroyNotify) ms_value_unref);
|
|
|
|
return val;
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_dict_get_elm (MSValue *dict,
|
|
const char *key)
|
|
{
|
|
MSValue *val;
|
|
g_return_val_if_fail (dict != NULL, NULL);
|
|
g_return_val_if_fail (key != NULL, NULL);
|
|
g_return_val_if_fail (MS_VALUE_TYPE (dict) == MS_VALUE_DICT, NULL);
|
|
val = g_hash_table_lookup (dict->u.hash, key);
|
|
return ms_value_ref (val);
|
|
}
|
|
|
|
|
|
void
|
|
ms_value_dict_set_elm (MSValue *dict,
|
|
const char *key,
|
|
MSValue *val)
|
|
{
|
|
MSValue *old;
|
|
|
|
g_return_if_fail (dict != NULL);
|
|
g_return_if_fail (key != NULL);
|
|
g_return_if_fail (MS_VALUE_TYPE (dict) == MS_VALUE_DICT);
|
|
|
|
old = g_hash_table_lookup (dict->u.hash, key);
|
|
|
|
if (old == val)
|
|
return;
|
|
|
|
if (!val)
|
|
g_hash_table_remove (dict->u.hash, key);
|
|
else
|
|
g_hash_table_insert (dict->u.hash, g_strdup (key),
|
|
ms_value_ref (val));
|
|
}
|
|
|
|
void
|
|
ms_value_dict_set_string (MSValue *dict,
|
|
const char *key,
|
|
const char *val)
|
|
{
|
|
MSValue *value;
|
|
|
|
g_return_if_fail (dict != NULL);
|
|
g_return_if_fail (key != NULL);
|
|
|
|
value = val ? ms_value_string (val) : ms_value_none ();
|
|
ms_value_dict_set_elm (dict, key, value);
|
|
ms_value_unref (value);
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_ref (MSValue *val)
|
|
{
|
|
if (val)
|
|
val->ref_count++;
|
|
return val;
|
|
}
|
|
|
|
|
|
void
|
|
ms_value_unref (MSValue *val)
|
|
{
|
|
guint i;
|
|
|
|
if (!val || --val->ref_count)
|
|
return;
|
|
|
|
switch (MS_VALUE_TYPE (val))
|
|
{
|
|
case MS_VALUE_STRING:
|
|
g_free (val->u.str);
|
|
break;
|
|
|
|
case MS_VALUE_NONE:
|
|
if (val == MS_None)
|
|
MS_None = NULL;
|
|
break;
|
|
|
|
case MS_VALUE_INT:
|
|
if (val == MS_False)
|
|
MS_False = NULL;
|
|
else if (val == MS_True)
|
|
MS_True = NULL;
|
|
break;
|
|
|
|
case MS_VALUE_GVALUE:
|
|
g_value_unset (val->u.gval);
|
|
g_free (val->u.gval);
|
|
break;
|
|
|
|
case MS_VALUE_LIST:
|
|
for (i = 0; i < val->u.list.n_elms; ++i)
|
|
ms_value_unref (val->u.list.elms[i]);
|
|
ms_value_array_free (val->u.list.elms,
|
|
val->u.list.n_elms);
|
|
break;
|
|
|
|
case MS_VALUE_DICT:
|
|
g_hash_table_destroy (val->u.hash);
|
|
break;
|
|
|
|
case MS_VALUE_FUNC:
|
|
g_object_unref (val->u.func.func);
|
|
ms_value_unref (val->u.func.obj);
|
|
break;
|
|
|
|
case MS_VALUE_INVALID:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
if (val->methods)
|
|
g_hash_table_destroy (val->methods);
|
|
|
|
ms_value_free (val);
|
|
}
|
|
|
|
|
|
const char *
|
|
_ms_binary_op_name (MSBinaryOp op)
|
|
{
|
|
static const char *names[MS_BINARY_OP_LAST] = {
|
|
"@PLUS", "@MINUS", "@MULT", "@DIV", "@AND", "@OR",
|
|
"@EQ", "@NEQ", "@LT", "@GT", "@LE", "@GE", "@FORMAT",
|
|
"@IN"
|
|
};
|
|
|
|
g_return_val_if_fail (op < MS_BINARY_OP_LAST, NULL);
|
|
return names[op];
|
|
}
|
|
|
|
|
|
const char *
|
|
_ms_unary_op_name (MSUnaryOp op)
|
|
{
|
|
static const char *names[MS_UNARY_OP_LAST] = {
|
|
"@UMINUS", "@NOT", "@LEN"
|
|
};
|
|
|
|
g_return_val_if_fail (op < MS_UNARY_OP_LAST, NULL);
|
|
return names[op];
|
|
}
|
|
|
|
|
|
gboolean
|
|
ms_value_get_bool (MSValue *val)
|
|
{
|
|
g_return_val_if_fail (val != NULL, FALSE);
|
|
|
|
switch (MS_VALUE_TYPE (val))
|
|
{
|
|
case MS_VALUE_STRING:
|
|
return val->u.str[0] != 0;
|
|
case MS_VALUE_INT:
|
|
return val->u.ival != 0;
|
|
case MS_VALUE_NONE:
|
|
return FALSE;
|
|
case MS_VALUE_LIST:
|
|
return val->u.list.n_elms != 0;
|
|
case MS_VALUE_DICT:
|
|
return g_hash_table_size (val->u.hash) != 0;
|
|
case MS_VALUE_FUNC:
|
|
return TRUE;
|
|
case MS_VALUE_GVALUE:
|
|
switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (val->u.gval)))
|
|
{
|
|
case G_TYPE_CHAR:
|
|
return g_value_get_char (val->u.gval) != 0;
|
|
case G_TYPE_UCHAR:
|
|
return g_value_get_uchar (val->u.gval) != 0;
|
|
case G_TYPE_BOOLEAN:
|
|
return g_value_get_boolean (val->u.gval);
|
|
case G_TYPE_INT:
|
|
return g_value_get_int (val->u.gval) != 0;
|
|
case G_TYPE_UINT:
|
|
return g_value_get_uint (val->u.gval) != 0;
|
|
case G_TYPE_LONG:
|
|
return g_value_get_long (val->u.gval) != 0;
|
|
case G_TYPE_ULONG:
|
|
return g_value_get_ulong (val->u.gval) != 0;
|
|
case G_TYPE_INT64:
|
|
return g_value_get_int64 (val->u.gval) != 0;
|
|
case G_TYPE_UINT64:
|
|
return g_value_get_uint64 (val->u.gval) != 0;
|
|
case G_TYPE_ENUM:
|
|
return g_value_get_enum (val->u.gval) != 0;
|
|
case G_TYPE_FLAGS:
|
|
return g_value_get_flags (val->u.gval) != 0;
|
|
case G_TYPE_FLOAT:
|
|
return g_value_get_float (val->u.gval) != 0;
|
|
case G_TYPE_DOUBLE:
|
|
return g_value_get_double (val->u.gval) != 0;
|
|
case G_TYPE_STRING:
|
|
return g_value_get_string (val->u.gval) != NULL &&
|
|
*g_value_get_string (val->u.gval) != 0;
|
|
case G_TYPE_POINTER:
|
|
return g_value_get_pointer (val->u.gval) != NULL;
|
|
case G_TYPE_BOXED:
|
|
return g_value_get_boxed (val->u.gval) != NULL;
|
|
case G_TYPE_OBJECT:
|
|
return g_value_get_object (val->u.gval) != NULL;
|
|
default:
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
case MS_VALUE_INVALID:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
|
|
gboolean
|
|
ms_value_get_int (MSValue *val,
|
|
int *ival)
|
|
{
|
|
g_return_val_if_fail (val != NULL, FALSE);
|
|
g_return_val_if_fail (ival != NULL, FALSE);
|
|
|
|
switch (MS_VALUE_TYPE (val))
|
|
{
|
|
case MS_VALUE_INT:
|
|
*ival = val->u.ival;
|
|
return TRUE;
|
|
|
|
case MS_VALUE_NONE:
|
|
*ival = 0;
|
|
return TRUE;
|
|
|
|
case MS_VALUE_GVALUE:
|
|
switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (val->u.gval)))
|
|
{
|
|
case G_TYPE_CHAR:
|
|
*ival = g_value_get_char (val->u.gval) != 0;
|
|
return TRUE;
|
|
case G_TYPE_UCHAR:
|
|
*ival = g_value_get_uchar (val->u.gval) != 0;
|
|
return TRUE;
|
|
case G_TYPE_BOOLEAN:
|
|
*ival = g_value_get_boolean (val->u.gval);
|
|
return TRUE;
|
|
case G_TYPE_INT:
|
|
*ival = g_value_get_int (val->u.gval) != 0;
|
|
return TRUE;
|
|
case G_TYPE_UINT:
|
|
*ival = g_value_get_uint (val->u.gval) != 0;
|
|
return TRUE;
|
|
case G_TYPE_LONG:
|
|
*ival = g_value_get_long (val->u.gval) != 0;
|
|
return TRUE;
|
|
case G_TYPE_ULONG:
|
|
*ival = g_value_get_ulong (val->u.gval) != 0;
|
|
return TRUE;
|
|
case G_TYPE_INT64:
|
|
*ival = g_value_get_int64 (val->u.gval) != 0;
|
|
return TRUE;
|
|
case G_TYPE_UINT64:
|
|
*ival = g_value_get_uint64 (val->u.gval) != 0;
|
|
return TRUE;
|
|
case G_TYPE_ENUM:
|
|
*ival = g_value_get_enum (val->u.gval) != 0;
|
|
return TRUE;
|
|
case G_TYPE_FLAGS:
|
|
*ival = g_value_get_flags (val->u.gval) != 0;
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
|
|
gboolean
|
|
ms_value_get_gvalue (MSValue *val,
|
|
GValue *dest)
|
|
{
|
|
g_return_val_if_fail (val != NULL, FALSE);
|
|
g_return_val_if_fail (dest != NULL, FALSE);
|
|
g_return_val_if_fail (!dest->g_type, FALSE);
|
|
|
|
switch (MS_VALUE_TYPE (val))
|
|
{
|
|
case MS_VALUE_INT:
|
|
g_value_init (dest, G_TYPE_INT);
|
|
g_value_set_int (dest, val->u.ival);
|
|
return TRUE;
|
|
|
|
case MS_VALUE_NONE:
|
|
g_value_init (dest, G_TYPE_STRING);
|
|
g_value_set_string (dest, NULL);
|
|
return TRUE;
|
|
|
|
case MS_VALUE_GVALUE:
|
|
g_value_init (dest, G_VALUE_TYPE (val->u.gval));
|
|
g_value_copy (val->u.gval, dest);
|
|
return TRUE;
|
|
|
|
case MS_VALUE_STRING:
|
|
g_value_init (dest, G_TYPE_STRING);
|
|
g_value_set_string (dest, val->u.str);
|
|
return TRUE;
|
|
|
|
case MS_VALUE_LIST:
|
|
case MS_VALUE_DICT:
|
|
case MS_VALUE_FUNC:
|
|
case MS_VALUE_INVALID:
|
|
return FALSE;
|
|
}
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
|
|
static char *
|
|
print_list (MSValue **elms,
|
|
guint n_elms)
|
|
{
|
|
guint i;
|
|
GString *string = g_string_sized_new (6 * n_elms + 2);
|
|
|
|
g_string_append_c (string, '[');
|
|
|
|
for (i = 0; i < n_elms; ++i)
|
|
{
|
|
char *s;
|
|
if (i)
|
|
g_string_append (string, ", ");
|
|
s = ms_value_repr (elms[i]);
|
|
g_string_append (string, s);
|
|
g_free (s);
|
|
}
|
|
|
|
g_string_append_c (string, ']');
|
|
return g_string_free (string, FALSE);
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
const char *key;
|
|
MSValue *val;
|
|
} KeyValPair;
|
|
|
|
static KeyValPair *
|
|
key_val_pair_new (const char *key,
|
|
MSValue *val)
|
|
{
|
|
KeyValPair *p;
|
|
|
|
g_assert (key != NULL);
|
|
g_assert (val != NULL);
|
|
|
|
p = g_new (KeyValPair, 1);
|
|
p->key = key;
|
|
p->val = val;
|
|
|
|
return p;
|
|
}
|
|
|
|
static void
|
|
key_val_pair_free (KeyValPair *pair)
|
|
{
|
|
g_free (pair);
|
|
}
|
|
|
|
static void
|
|
add_key_val (const char *key,
|
|
MSValue *val,
|
|
GSList **list)
|
|
{
|
|
*list = g_slist_prepend (*list, key_val_pair_new (key, val));
|
|
}
|
|
|
|
static int
|
|
compare_key_val (KeyValPair *a,
|
|
KeyValPair *b)
|
|
{
|
|
int ret;
|
|
|
|
ret = strcmp (a->key, b->key);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
return ms_value_cmp (a->val, b->val);
|
|
}
|
|
|
|
static GSList *
|
|
dict_get_pairs (GHashTable *table)
|
|
{
|
|
GSList *list = NULL;
|
|
g_hash_table_foreach (table, (GHFunc) add_key_val, &list);
|
|
list = g_slist_sort (list, (GCompareFunc) compare_key_val);
|
|
return list;
|
|
}
|
|
|
|
|
|
static void
|
|
print_dict_elm (KeyValPair *pair,
|
|
gpointer user_data)
|
|
{
|
|
char *strval;
|
|
|
|
struct {
|
|
GString *string;
|
|
gboolean first;
|
|
} *data = user_data;
|
|
|
|
if (!data->first)
|
|
g_string_append (data->string, ", ");
|
|
data->first = FALSE;
|
|
|
|
strval = ms_value_repr (pair->val);
|
|
g_string_append_printf (data->string, "%s = %s", pair->key, strval);
|
|
g_free (strval);
|
|
}
|
|
|
|
static char *
|
|
print_dict (GHashTable *table)
|
|
{
|
|
GSList *list;
|
|
GString *string;
|
|
struct {
|
|
GString *string;
|
|
gboolean first;
|
|
} data;
|
|
|
|
string = g_string_sized_new (6 * g_hash_table_size (table) + 2);
|
|
g_string_append_c (string, '{');
|
|
|
|
list = dict_get_pairs (table);
|
|
data.string = string;
|
|
data.first = TRUE;
|
|
g_slist_foreach (list, (GFunc) print_dict_elm, &data);
|
|
g_slist_foreach (list, (GFunc) key_val_pair_free, NULL);
|
|
g_slist_free (list);
|
|
|
|
g_string_append_c (string, '}');
|
|
return g_string_free (string, FALSE);
|
|
}
|
|
|
|
|
|
static char *
|
|
print_func (MSValue *val)
|
|
{
|
|
char *obj, *str;
|
|
|
|
g_assert (MS_VALUE_TYPE (val) == MS_VALUE_FUNC);
|
|
|
|
if (!val->u.func.meth)
|
|
return g_strdup ("<function>");
|
|
else if (!val->u.func.obj)
|
|
return g_strdup ("<method>");
|
|
|
|
obj = ms_value_repr (val->u.func.obj);
|
|
str = g_strdup_printf ("<method of %s>", obj);
|
|
g_free (obj);
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
char *
|
|
ms_value_print (MSValue *val)
|
|
{
|
|
g_return_val_if_fail (val != NULL, NULL);
|
|
|
|
switch (MS_VALUE_TYPE (val))
|
|
{
|
|
case MS_VALUE_STRING:
|
|
return g_strdup (val->u.str);
|
|
case MS_VALUE_INT:
|
|
return g_strdup_printf ("%d", val->u.ival);
|
|
case MS_VALUE_NONE:
|
|
return g_strdup ("none");
|
|
case MS_VALUE_LIST:
|
|
return print_list (val->u.list.elms, val->u.list.n_elms);
|
|
case MS_VALUE_DICT:
|
|
return print_dict (val->u.hash);
|
|
case MS_VALUE_FUNC:
|
|
return print_func (val);
|
|
case MS_VALUE_GVALUE:
|
|
switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (val->u.gval)))
|
|
{
|
|
case G_TYPE_CHAR:
|
|
return g_strdup_printf ("%c", g_value_get_char (val->u.gval));
|
|
case G_TYPE_UCHAR:
|
|
return g_strdup_printf ("%c", g_value_get_uchar (val->u.gval));
|
|
case G_TYPE_BOOLEAN:
|
|
return g_strdup_printf ("%d", g_value_get_boolean (val->u.gval));
|
|
case G_TYPE_INT:
|
|
return g_strdup_printf ("%d", g_value_get_int (val->u.gval));
|
|
case G_TYPE_UINT:
|
|
return g_strdup_printf ("%u", g_value_get_uint (val->u.gval));
|
|
case G_TYPE_LONG:
|
|
return g_strdup_printf ("%ld", g_value_get_long (val->u.gval));
|
|
case G_TYPE_ULONG:
|
|
return g_strdup_printf ("%lu", g_value_get_ulong (val->u.gval));
|
|
case G_TYPE_INT64:
|
|
return g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (val->u.gval));
|
|
case G_TYPE_UINT64:
|
|
return g_strdup_printf ("%" G_GUINT64_FORMAT, g_value_get_uint64 (val->u.gval));
|
|
case G_TYPE_ENUM:
|
|
return g_strdup_printf ("%d", g_value_get_enum (val->u.gval));
|
|
case G_TYPE_FLAGS:
|
|
return g_strdup_printf ("%u", g_value_get_flags (val->u.gval));
|
|
case G_TYPE_FLOAT:
|
|
return g_strdup_printf ("%f", g_value_get_float (val->u.gval));
|
|
case G_TYPE_DOUBLE:
|
|
return g_strdup_printf ("%f", g_value_get_double (val->u.gval));
|
|
case G_TYPE_STRING:
|
|
return g_value_get_string (val->u.gval) ?
|
|
g_strdup (g_value_get_string (val->u.gval)) : g_strdup ("NULL");
|
|
case G_TYPE_POINTER:
|
|
return g_strdup_printf ("Pointer %p", g_value_get_pointer (val->u.gval));
|
|
case G_TYPE_BOXED:
|
|
return g_strdup_printf ("Boxed %p", g_value_get_boxed (val->u.gval));
|
|
case G_TYPE_OBJECT:
|
|
return g_strdup_printf ("Object %p", g_value_get_object (val->u.gval));
|
|
default:
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
|
|
case MS_VALUE_INVALID:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
|
|
|
|
char *
|
|
ms_value_repr (MSValue *val)
|
|
{
|
|
char *tmp, *ret;
|
|
|
|
g_return_val_if_fail (val != NULL, NULL);
|
|
|
|
switch (MS_VALUE_TYPE (val))
|
|
{
|
|
case MS_VALUE_STRING:
|
|
tmp = g_strescape (val->u.str, NULL);
|
|
ret = g_strdup_printf ("\"%s\"", tmp);
|
|
g_free (tmp);
|
|
return ret;
|
|
|
|
case MS_VALUE_INT:
|
|
return g_strdup_printf ("%d", val->u.ival);
|
|
|
|
case MS_VALUE_NONE:
|
|
return g_strdup ("none");
|
|
|
|
case MS_VALUE_LIST:
|
|
return print_list (val->u.list.elms, val->u.list.n_elms);
|
|
|
|
case MS_VALUE_DICT:
|
|
return print_dict (val->u.hash);
|
|
|
|
case MS_VALUE_FUNC:
|
|
return print_func (val);
|
|
|
|
case MS_VALUE_GVALUE:
|
|
switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (val->u.gval)))
|
|
{
|
|
case G_TYPE_CHAR:
|
|
return g_strdup_printf ("'%c'", g_value_get_char (val->u.gval));
|
|
case G_TYPE_UCHAR:
|
|
return g_strdup_printf ("'%c'", g_value_get_uchar (val->u.gval));
|
|
case G_TYPE_BOOLEAN:
|
|
return g_strdup_printf ("%d", g_value_get_boolean (val->u.gval));
|
|
case G_TYPE_INT:
|
|
return g_strdup_printf ("%d", g_value_get_int (val->u.gval));
|
|
case G_TYPE_UINT:
|
|
return g_strdup_printf ("%u", g_value_get_uint (val->u.gval));
|
|
case G_TYPE_LONG:
|
|
return g_strdup_printf ("%ld", g_value_get_long (val->u.gval));
|
|
case G_TYPE_ULONG:
|
|
return g_strdup_printf ("%lu", g_value_get_ulong (val->u.gval));
|
|
case G_TYPE_INT64:
|
|
return g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (val->u.gval));
|
|
case G_TYPE_UINT64:
|
|
return g_strdup_printf ("%" G_GUINT64_FORMAT, g_value_get_uint64 (val->u.gval));
|
|
case G_TYPE_ENUM:
|
|
return g_strdup_printf ("<%d>", g_value_get_enum (val->u.gval));
|
|
case G_TYPE_FLAGS:
|
|
return g_strdup_printf ("<%u>", g_value_get_flags (val->u.gval));
|
|
case G_TYPE_FLOAT:
|
|
return g_strdup_printf ("%f", g_value_get_float (val->u.gval));
|
|
case G_TYPE_DOUBLE:
|
|
return g_strdup_printf ("%f", g_value_get_double (val->u.gval));
|
|
case G_TYPE_STRING:
|
|
tmp = (char*) g_value_get_string (val->u.gval);
|
|
tmp = tmp ? g_strescape (tmp, NULL) : NULL;
|
|
ret = tmp ? g_strdup_printf ("\"%s\"", tmp) : g_strdup ("(null)");
|
|
g_free (tmp);
|
|
return ret;
|
|
case G_TYPE_POINTER:
|
|
return g_strdup_printf ("<pointer %p>", g_value_get_pointer (val->u.gval));
|
|
case G_TYPE_BOXED:
|
|
return g_strdup_printf ("<boxed %p>", g_value_get_boxed (val->u.gval));
|
|
case G_TYPE_OBJECT:
|
|
return g_strdup_printf ("<object %p>", g_value_get_object (val->u.gval));
|
|
default:
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
|
|
case MS_VALUE_INVALID:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
|
|
|
|
static MSValue *
|
|
func_plus (MSValue *a, MSValue *b, MSContext *ctx)
|
|
{
|
|
if (MS_VALUE_TYPE (a) == MS_VALUE_INT && MS_VALUE_TYPE (b) == MS_VALUE_INT)
|
|
return ms_value_int (a->u.ival + b->u.ival);
|
|
else if (MS_VALUE_TYPE (a) == MS_VALUE_STRING && MS_VALUE_TYPE (b) == MS_VALUE_STRING)
|
|
return ms_value_take_string (g_strdup_printf ("%s%s", a->u.str, b->u.str));
|
|
|
|
return ms_context_set_error (ctx, MS_ERROR_TYPE,
|
|
"invalid PLUS");
|
|
}
|
|
|
|
|
|
static MSValue *
|
|
func_minus (MSValue *a, MSValue *b, MSContext *ctx)
|
|
{
|
|
if (MS_VALUE_TYPE (a) == MS_VALUE_INT && MS_VALUE_TYPE (b) == MS_VALUE_INT)
|
|
return ms_value_int (a->u.ival - b->u.ival);
|
|
return ms_context_set_error (ctx, MS_ERROR_TYPE,
|
|
"invalid MINUS");
|
|
}
|
|
|
|
|
|
static MSValue *
|
|
func_mult (MSValue *a, MSValue *b, MSContext *ctx)
|
|
{
|
|
if (MS_VALUE_TYPE (a) == MS_VALUE_INT && MS_VALUE_TYPE (b) == MS_VALUE_INT)
|
|
return ms_value_int (a->u.ival * b->u.ival);
|
|
|
|
if (MS_VALUE_TYPE (a) == MS_VALUE_STRING && MS_VALUE_TYPE (b) == MS_VALUE_INT)
|
|
{
|
|
char *s;
|
|
guint len;
|
|
int i;
|
|
|
|
if (b->u.ival < 0)
|
|
return ms_context_set_error (ctx, MS_ERROR_TYPE,
|
|
"string * negative int");
|
|
if (b->u.ival == 0)
|
|
return ms_value_string ("");
|
|
|
|
len = strlen (a->u.str);
|
|
s = g_new (char, len * b->u.ival + 1);
|
|
s[len * b->u.ival] = 0;
|
|
|
|
for (i = 0; i < b->u.ival; ++i)
|
|
memcpy (&s[i*len], a->u.str, len);
|
|
|
|
return ms_value_take_string (s);
|
|
}
|
|
|
|
return ms_context_set_error (ctx, MS_ERROR_TYPE,
|
|
"invalid MULT");
|
|
}
|
|
|
|
|
|
static MSValue *
|
|
func_div (MSValue *a, MSValue *b, MSContext *ctx)
|
|
{
|
|
if (MS_VALUE_TYPE (a) == MS_VALUE_INT && MS_VALUE_TYPE (b) == MS_VALUE_INT)
|
|
{
|
|
if (b->u.ival)
|
|
return ms_value_int (a->u.ival / b->u.ival);
|
|
else
|
|
return ms_context_set_error (ctx, MS_ERROR_VALUE,
|
|
"division by zero");
|
|
}
|
|
|
|
return ms_context_set_error (ctx, MS_ERROR_TYPE,
|
|
"invalid DIV");
|
|
}
|
|
|
|
|
|
static MSValue *
|
|
func_and (MSValue *a,
|
|
MSValue *b,
|
|
G_GNUC_UNUSED MSContext *ctx)
|
|
{
|
|
if (ms_value_get_bool (a) && ms_value_get_bool (b))
|
|
return ms_value_ref (b);
|
|
else
|
|
return ms_value_false ();
|
|
}
|
|
|
|
|
|
static MSValue *
|
|
func_or (MSValue *a,
|
|
MSValue *b,
|
|
G_GNUC_UNUSED MSContext *ctx)
|
|
{
|
|
if (ms_value_get_bool (a))
|
|
return ms_value_ref (a);
|
|
else if (ms_value_get_bool (b))
|
|
return ms_value_ref (b);
|
|
else
|
|
return ms_value_false ();
|
|
}
|
|
|
|
|
|
static gboolean
|
|
list_equal (MSValue *a, MSValue *b)
|
|
{
|
|
guint i;
|
|
|
|
if (a->u.list.n_elms != b->u.list.n_elms)
|
|
return FALSE;
|
|
|
|
for (i = 0; i < a->u.list.n_elms; ++i)
|
|
if (!ms_value_equal (a->u.list.elms[i], b->u.list.elms[i]))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
check_key (const char *key,
|
|
G_GNUC_UNUSED MSValue *val,
|
|
GHashTable *table)
|
|
{
|
|
return g_hash_table_lookup (table, key) == NULL;
|
|
}
|
|
|
|
static gboolean
|
|
check_key_and_val (const char *key,
|
|
MSValue *val,
|
|
GHashTable *table)
|
|
{
|
|
MSValue *other = g_hash_table_lookup (table, key);
|
|
|
|
if (!other)
|
|
return TRUE;
|
|
else
|
|
return !ms_value_equal (val, other);
|
|
}
|
|
|
|
static gboolean
|
|
dict_equal (GHashTable *a,
|
|
GHashTable *b)
|
|
{
|
|
if (g_hash_table_find (a, (GHRFunc) check_key_and_val, b))
|
|
return FALSE;
|
|
|
|
if (g_hash_table_find (b, (GHRFunc) check_key, a))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
gboolean
|
|
ms_value_equal (MSValue *a, MSValue *b)
|
|
{
|
|
if (a == b)
|
|
return TRUE;
|
|
|
|
if (MS_VALUE_TYPE (a) != MS_VALUE_TYPE (b))
|
|
return FALSE;
|
|
|
|
switch (MS_VALUE_TYPE (a))
|
|
{
|
|
case MS_VALUE_INT:
|
|
return a->u.ival == b->u.ival;
|
|
case MS_VALUE_NONE:
|
|
return TRUE;
|
|
case MS_VALUE_STRING:
|
|
return !strcmp (a->u.str, b->u.str);
|
|
case MS_VALUE_LIST:
|
|
return list_equal (a, b);
|
|
case MS_VALUE_DICT:
|
|
return dict_equal (a->u.hash, b->u.hash);
|
|
case MS_VALUE_GVALUE:
|
|
g_return_val_if_reached (FALSE);
|
|
case MS_VALUE_FUNC:
|
|
return a->u.func.func == b->u.func.func &&
|
|
a->u.func.obj == b->u.func.obj;
|
|
case MS_VALUE_INVALID:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
|
|
#define CMP(a, b) ((a) < (b) ? -1 : ((a) > (b) ? 1 : 0))
|
|
|
|
static int
|
|
list_cmp (MSValue *a, MSValue *b)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < a->u.list.n_elms && i < b->u.list.n_elms; ++i)
|
|
{
|
|
int c = ms_value_cmp (a->u.list.elms[i], b->u.list.elms[i]);
|
|
|
|
if (c)
|
|
return c;
|
|
}
|
|
|
|
return CMP (a->u.list.n_elms, b->u.list.n_elms);
|
|
}
|
|
|
|
|
|
static int
|
|
dict_cmp (GHashTable *a,
|
|
GHashTable *b)
|
|
{
|
|
int ret;
|
|
GSList *list_a, *list_b, *la, *lb;
|
|
|
|
list_a = dict_get_pairs (a);
|
|
list_b = dict_get_pairs (b);
|
|
|
|
for (la = list_a, lb = list_b; la && lb; la = la->next, lb = lb->next)
|
|
{
|
|
KeyValPair *pa = la->data, *pb = lb->data;
|
|
|
|
ret = compare_key_val (pa, pb);
|
|
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
if (la)
|
|
ret = 1;
|
|
else if (lb)
|
|
ret = -1;
|
|
else
|
|
ret = 0;
|
|
|
|
out:
|
|
g_slist_foreach (list_a, (GFunc) key_val_pair_free, NULL);
|
|
g_slist_foreach (list_b, (GFunc) key_val_pair_free, NULL);
|
|
g_slist_free (list_a);
|
|
g_slist_free (list_b);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int
|
|
ms_value_cmp (MSValue *a, MSValue *b)
|
|
{
|
|
if (a == b)
|
|
return 0;
|
|
|
|
if (MS_VALUE_TYPE (a) != MS_VALUE_TYPE (b))
|
|
return a < b ? -1 : 1;
|
|
|
|
switch (MS_VALUE_TYPE (a))
|
|
{
|
|
case MS_VALUE_INT:
|
|
return CMP (a->u.ival, b->u.ival);
|
|
case MS_VALUE_NONE:
|
|
return 0;
|
|
case MS_VALUE_STRING:
|
|
return strcmp (a->u.str, b->u.str);
|
|
case MS_VALUE_LIST:
|
|
return list_cmp (a, b);
|
|
case MS_VALUE_DICT:
|
|
return dict_cmp (a->u.hash, b->u.hash);
|
|
case MS_VALUE_GVALUE:
|
|
g_return_val_if_reached (CMP (a, b));
|
|
case MS_VALUE_FUNC:
|
|
return a->u.func.func < b->u.func.func ? -1 :
|
|
(a->u.func.func > b->u.func.func ? 1 :
|
|
CMP (a->u.func.obj, b->u.func.obj));
|
|
case MS_VALUE_INVALID:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
g_return_val_if_reached (CMP (a, b));
|
|
}
|
|
|
|
|
|
static MSValue *
|
|
func_eq (MSValue *a,
|
|
MSValue *b,
|
|
G_GNUC_UNUSED MSContext *ctx)
|
|
{
|
|
return ms_value_bool (ms_value_equal (a, b));
|
|
}
|
|
|
|
|
|
static MSValue *
|
|
func_neq (MSValue *a,
|
|
MSValue *b,
|
|
G_GNUC_UNUSED MSContext *ctx)
|
|
{
|
|
return ms_value_bool (!ms_value_equal (a, b));
|
|
}
|
|
|
|
|
|
static MSValue *
|
|
func_lt (MSValue *a,
|
|
MSValue *b,
|
|
G_GNUC_UNUSED MSContext *ctx)
|
|
{
|
|
return ms_value_bool (ms_value_cmp (a, b) < 0);
|
|
}
|
|
|
|
static MSValue *
|
|
func_gt (MSValue *a,
|
|
MSValue *b,
|
|
G_GNUC_UNUSED MSContext *ctx)
|
|
{
|
|
return ms_value_bool (ms_value_cmp (a, b) > 0);
|
|
}
|
|
|
|
static MSValue *
|
|
func_le (MSValue *a,
|
|
MSValue *b,
|
|
G_GNUC_UNUSED MSContext *ctx)
|
|
{
|
|
return ms_value_bool (ms_value_cmp (a, b) <= 0);
|
|
}
|
|
|
|
static MSValue *
|
|
func_ge (MSValue *a,
|
|
MSValue *b,
|
|
G_GNUC_UNUSED MSContext *ctx)
|
|
{
|
|
return ms_value_bool (ms_value_cmp (a, b) >= 0);
|
|
}
|
|
|
|
|
|
static MSValue *
|
|
list_in (MSValue *list,
|
|
MSValue *val)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < list->u.list.n_elms; ++i)
|
|
if (ms_value_equal (val, list->u.list.elms[i]))
|
|
return ms_value_true ();
|
|
|
|
return ms_value_false ();
|
|
}
|
|
|
|
static MSValue *
|
|
dict_in (MSValue *dict,
|
|
MSValue *val)
|
|
{
|
|
if (MS_VALUE_TYPE (val) != MS_VALUE_STRING)
|
|
return ms_value_false ();
|
|
|
|
return g_hash_table_lookup (dict->u.hash, val->u.str) ?
|
|
ms_value_true () : ms_value_false ();
|
|
}
|
|
|
|
static MSValue *
|
|
string_in (MSValue *string,
|
|
MSValue *val)
|
|
{
|
|
if (MS_VALUE_TYPE (val) != MS_VALUE_STRING)
|
|
return ms_value_false ();
|
|
|
|
return strstr (string->u.str, val->u.str) ?
|
|
ms_value_true () : ms_value_false ();
|
|
}
|
|
|
|
static MSValue *
|
|
func_in (MSValue *val,
|
|
MSValue *list,
|
|
MSContext *ctx)
|
|
{
|
|
switch (MS_VALUE_TYPE (list))
|
|
{
|
|
case MS_VALUE_LIST:
|
|
return list_in (list, val);
|
|
case MS_VALUE_DICT:
|
|
return dict_in (list, val);
|
|
case MS_VALUE_STRING:
|
|
return string_in (list, val);
|
|
default:
|
|
ms_context_format_error (ctx, MS_ERROR_TYPE,
|
|
"invalid left hand side '%v' of operator in",
|
|
list);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static char *
|
|
format_value (char format,
|
|
MSValue *value,
|
|
MSContext *ctx)
|
|
{
|
|
int ival;
|
|
|
|
switch (format)
|
|
{
|
|
case 's':
|
|
return ms_value_print (value);
|
|
|
|
case 'd':
|
|
if (!ms_value_get_int (value, &ival))
|
|
{
|
|
ms_context_set_error (ctx, MS_ERROR_TYPE, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
return g_strdup_printf ("%d", ival);
|
|
|
|
default:
|
|
ms_context_set_error (ctx, MS_ERROR_VALUE, "invalid format");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static MSValue *
|
|
func_format (MSValue *format, MSValue *tuple, MSContext *ctx)
|
|
{
|
|
GString *ret;
|
|
guint n_items, items_written;
|
|
char *str, *p, *s;
|
|
MSValue *val;
|
|
|
|
if (MS_VALUE_TYPE (format) != MS_VALUE_STRING)
|
|
return ms_context_set_error (ctx, MS_ERROR_TYPE, "invalid '%'");
|
|
|
|
if (MS_VALUE_TYPE (tuple) == MS_VALUE_LIST)
|
|
n_items = tuple->u.list.n_elms;
|
|
else
|
|
n_items = 1;
|
|
|
|
p = str = format->u.str;
|
|
ret = g_string_new (NULL);
|
|
items_written = 0;
|
|
|
|
while (*p)
|
|
{
|
|
if (*p == '%')
|
|
{
|
|
if (p > str)
|
|
g_string_append_len (ret, str, p - str);
|
|
|
|
switch (p[1])
|
|
{
|
|
case '%':
|
|
g_string_append_c (ret, '%');
|
|
p += 2;
|
|
str = p;
|
|
break;
|
|
|
|
case 's':
|
|
case 'd':
|
|
if (items_written == n_items)
|
|
{
|
|
ms_context_set_error (ctx, MS_ERROR_VALUE,
|
|
"invalid conversion");
|
|
g_string_free (ret, TRUE);
|
|
return NULL;
|
|
}
|
|
|
|
if (MS_VALUE_TYPE (tuple) == MS_VALUE_LIST)
|
|
val = tuple->u.list.elms[items_written];
|
|
else
|
|
val = tuple;
|
|
|
|
s = format_value (p[1], val, ctx);
|
|
|
|
if (!s)
|
|
{
|
|
g_string_free (ret, TRUE);
|
|
return NULL;
|
|
}
|
|
|
|
g_string_append (ret, s);
|
|
g_free (s);
|
|
items_written++;
|
|
p += 2;
|
|
str = p;
|
|
break;
|
|
|
|
default:
|
|
ms_context_set_error (ctx, MS_ERROR_VALUE,
|
|
"invalid conversion");
|
|
g_string_free (ret, TRUE);
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
++p;
|
|
}
|
|
}
|
|
|
|
if (str < p)
|
|
g_string_append (ret, str);
|
|
|
|
return ms_value_take_string (g_string_free (ret, FALSE));
|
|
}
|
|
|
|
|
|
MSCFunc_2
|
|
_ms_binary_op_cfunc (MSBinaryOp op)
|
|
{
|
|
static MSCFunc_2 funcs[MS_BINARY_OP_LAST] = {
|
|
func_plus, func_minus, func_mult, func_div,
|
|
func_and, func_or,
|
|
func_eq, func_neq, func_lt, func_gt, func_le, func_ge,
|
|
func_format, func_in
|
|
};
|
|
|
|
g_return_val_if_fail (op < MS_BINARY_OP_LAST, NULL);
|
|
return funcs[op];
|
|
}
|
|
|
|
|
|
static MSValue *
|
|
func_uminus (MSValue *val,
|
|
MSContext *ctx)
|
|
{
|
|
if (MS_VALUE_TYPE (val) == MS_VALUE_INT)
|
|
return ms_value_int (-val->u.ival);
|
|
return ms_context_set_error (ctx, MS_ERROR_TYPE, NULL);
|
|
}
|
|
|
|
|
|
static MSValue *
|
|
func_not (MSValue *val,
|
|
G_GNUC_UNUSED MSContext *ctx)
|
|
{
|
|
return !ms_value_get_bool (val) ?
|
|
ms_value_true () : ms_value_false ();
|
|
}
|
|
|
|
|
|
static MSValue *
|
|
func_len (MSValue *val,
|
|
MSContext *ctx)
|
|
{
|
|
switch (MS_VALUE_TYPE (val))
|
|
{
|
|
case MS_VALUE_STRING:
|
|
return ms_value_int (strlen (val->u.str));
|
|
case MS_VALUE_LIST:
|
|
return ms_value_int (val->u.list.n_elms);
|
|
default:
|
|
return ms_context_set_error (ctx, MS_ERROR_TYPE, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
MSCFunc_1
|
|
_ms_unary_op_cfunc (MSUnaryOp op)
|
|
{
|
|
static MSCFunc_1 funcs[MS_UNARY_OP_LAST] = {
|
|
func_uminus, func_not, func_len
|
|
};
|
|
|
|
g_return_val_if_fail (op < MS_UNARY_OP_LAST, NULL);
|
|
return funcs[op];
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_func (MSFunc *func)
|
|
{
|
|
MSValue *val;
|
|
g_return_val_if_fail (MS_IS_FUNC (func), NULL);
|
|
val = ms_value_new (&types[MS_VALUE_FUNC]);
|
|
val->u.func.func = g_object_ref (func);
|
|
return val;
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_meth (MSFunc *func)
|
|
{
|
|
MSValue *val;
|
|
g_return_val_if_fail (MS_IS_FUNC (func), NULL);
|
|
val = ms_value_func (func);
|
|
val->u.func.meth = TRUE;
|
|
return val;
|
|
}
|
|
|
|
|
|
MSValue *
|
|
ms_value_bound_meth (MSFunc *func,
|
|
MSValue *obj)
|
|
{
|
|
MSValue *val;
|
|
g_return_val_if_fail (MS_IS_FUNC (func), NULL);
|
|
g_return_val_if_fail (obj != NULL, NULL);
|
|
val = ms_value_meth (func);
|
|
val->u.func.obj = ms_value_ref (obj);
|
|
return val;
|
|
}
|
|
|
|
|
|
gboolean
|
|
_ms_value_is_func (MSValue *val)
|
|
{
|
|
g_return_val_if_fail (val != NULL, FALSE);
|
|
return MS_VALUE_TYPE (val) == MS_VALUE_FUNC;
|
|
}
|
|
|
|
|
|
MSValue *
|
|
_ms_value_call (MSValue *func,
|
|
MSValue **args,
|
|
guint n_args,
|
|
MSContext *ctx)
|
|
{
|
|
MSValue *ret;
|
|
MSValue **real_args;
|
|
guint i;
|
|
|
|
g_return_val_if_fail (func != NULL, NULL);
|
|
g_return_val_if_fail (!n_args || args, NULL);
|
|
g_return_val_if_fail (MS_IS_CONTEXT (ctx), NULL);
|
|
g_return_val_if_fail (MS_VALUE_TYPE (func) == MS_VALUE_FUNC, NULL);
|
|
|
|
if (!func->u.func.meth || !func->u.func.obj)
|
|
return _ms_func_call (func->u.func.func, args, n_args, ctx);
|
|
|
|
real_args = ms_value_array_alloc (n_args + 1);
|
|
real_args[0] = ms_value_ref (func->u.func.obj);
|
|
|
|
for (i = 0; i < n_args; ++i)
|
|
real_args[i+1] = ms_value_ref (args[i]);
|
|
|
|
ret = _ms_func_call (func->u.func.func, real_args, n_args + 1, ctx);
|
|
|
|
for (i = 0; i < n_args + 1; ++i)
|
|
ms_value_unref (real_args[i]);
|
|
ms_value_array_free (real_args, n_args + 1);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void
|
|
ms_type_init (void)
|
|
{
|
|
guint i;
|
|
static gboolean done = FALSE;
|
|
|
|
if (done)
|
|
return;
|
|
|
|
done = TRUE;
|
|
|
|
for (i = 0; i < MS_VALUE_INVALID; ++i)
|
|
types[i].type = i;
|
|
|
|
_ms_type_init_builtin (types);
|
|
}
|
|
|
|
|
|
void
|
|
_ms_value_class_add_method (MSValueClass *klass,
|
|
const char *name,
|
|
MSFunc *func)
|
|
{
|
|
g_return_if_fail (klass != NULL && klass->type < MS_VALUE_INVALID);
|
|
g_return_if_fail (name != NULL);
|
|
g_return_if_fail (MS_IS_FUNC (func));
|
|
|
|
if (!klass->methods)
|
|
klass->methods = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
g_free, g_object_unref);
|
|
|
|
g_object_ref (func);
|
|
g_hash_table_insert (klass->methods, g_strdup (name), func);
|
|
}
|
|
|
|
|
|
void
|
|
ms_value_add_method (MSValue *val,
|
|
const char *name,
|
|
MSFunc *func)
|
|
{
|
|
g_return_if_fail (val != NULL);
|
|
g_return_if_fail (name != NULL);
|
|
g_return_if_fail (MS_IS_FUNC (func));
|
|
|
|
if (!val->methods)
|
|
val->methods = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
g_free, g_object_unref);
|
|
|
|
g_object_ref (func);
|
|
g_hash_table_insert (val->methods, g_strdup (name), func);
|
|
}
|
|
|
|
|
|
MSValue *
|
|
_ms_value_get_method (MSValue *value,
|
|
const char *name)
|
|
{
|
|
MSFunc *func = NULL;
|
|
|
|
g_return_val_if_fail (value != NULL, NULL);
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
if (value->methods)
|
|
func = g_hash_table_lookup (value->methods, name);
|
|
|
|
if (!func && value->klass->methods)
|
|
func = g_hash_table_lookup (value->klass->methods, name);
|
|
|
|
if (func)
|
|
return ms_value_bound_meth (func, value);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
char *
|
|
_ms_printf (const char *format,
|
|
...)
|
|
{
|
|
char *string;
|
|
va_list args;
|
|
|
|
va_start (args, format);
|
|
string = _ms_vaprintf (format, args);
|
|
va_end (args);
|
|
|
|
return string;
|
|
}
|
|
|
|
|
|
char *
|
|
_ms_vaprintf (const char *format,
|
|
va_list args)
|
|
{
|
|
GString *buffer;
|
|
char *arg_s;
|
|
int arg_i;
|
|
MSValue *arg_v;
|
|
char c;
|
|
|
|
buffer = g_string_new (NULL);
|
|
|
|
while ((c = *format++))
|
|
{
|
|
switch (c)
|
|
{
|
|
case '\\':
|
|
c = *format++;
|
|
|
|
switch (c)
|
|
{
|
|
case 0:
|
|
g_warning ("%s: trailing backslash", G_STRLOC);
|
|
break;
|
|
case '\\':
|
|
g_string_append_c (buffer, '\\');
|
|
break;
|
|
case 'b':
|
|
g_string_append_c (buffer, '\b');
|
|
break;
|
|
case 'r':
|
|
g_string_append_c (buffer, '\r');
|
|
break;
|
|
case 'n':
|
|
g_string_append_c (buffer, '\n');
|
|
break;
|
|
default:
|
|
g_warning ("%s: unknown escaped symbol '%c'", G_STRLOC, c);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case '%':
|
|
c = *format++;
|
|
|
|
switch (c)
|
|
{
|
|
case 0:
|
|
g_warning ("%s: trailing '%%'", G_STRLOC);
|
|
break;
|
|
case '%':
|
|
g_string_append_c (buffer, '%');
|
|
break;
|
|
case 's':
|
|
arg_s = va_arg (args, char*);
|
|
g_string_append (buffer, arg_s);
|
|
break;
|
|
case 'c':
|
|
arg_i = va_arg (args, int);
|
|
g_string_append_c (buffer, arg_i);
|
|
break;
|
|
case 'd':
|
|
case 'i':
|
|
arg_i = va_arg (args, int);
|
|
g_string_append_printf (buffer, "%d", arg_i);
|
|
break;
|
|
case 'v':
|
|
arg_v = va_arg (args, MSValue*);
|
|
arg_s = ms_value_print (arg_v);
|
|
g_string_append (buffer, arg_s);
|
|
g_free (arg_s);
|
|
break;
|
|
case 'r':
|
|
arg_v = va_arg (args, MSValue*);
|
|
arg_s = ms_value_repr (arg_v);
|
|
g_string_append (buffer, arg_s);
|
|
g_free (arg_s);
|
|
break;
|
|
default:
|
|
g_warning ("%s: unknown format modifier %c", G_STRLOC, c);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
g_string_append_c (buffer, c);
|
|
}
|
|
}
|
|
|
|
return g_string_free (buffer, FALSE);
|
|
}
|
|
|
|
|
|
GType
|
|
_ms_value_get_type (void)
|
|
{
|
|
static GType type;
|
|
|
|
if (!type)
|
|
type = g_boxed_type_register_static ("MSValue",
|
|
(GBoxedCopyFunc) ms_value_ref,
|
|
(GBoxedFreeFunc) ms_value_unref);
|
|
|
|
return type;
|
|
}
|