LPMuds.net
February 09, 2010, 12:55:15 PM *
Welcome, Guest. Please login or register.

Login with username, password and session length
News: This is the forum page. For the main LPMuds page, visit http://lpmuds.net
 
   Home   SITE FAQ INTERMUD DOWNLOADS LINKS Help Search Login Register  
Pages: [1]   Go Down
  Print  
Author Topic: simple finite mapping type validation  (Read 694 times)
silenus
BFF
***
Offline Offline

Posts: 102


View Profile
« on: September 08, 2008, 02:13:42 PM »

Hi,

This is some code I cooked up to perform simple mapping type validation for the most basic case where all the fields are fixed (though they can be nested). Perhaps it may be of use to someone...

Code:
// Silenus 2008-09-08
private int
_shallow_array_equals(mixed array ar1, mixed array ar2)
{
    return sizeof(ar1 - ar2) == 0 && sizeof(ar2 - ar1) == 0; 
}
 
private mixed
_validate(mapping m1, mapping m2, string array path)
{
    string error;
   
    if( !_shallow_array_equals( keys(m1), keys(m2) ) )
        return "*Keys in mapping do not match at level " + implode(path,"/") +
". Missing :" + implode( keys(m1) - keys(m2), ",") + "." +
        "Extra :" + implode( keys(m2) - keys(m1), ",") + ".";         
    foreach(mixed k, mixed v in m1)
    {
        if( mapp(v) && stringp( error = _validate(v, m2[k], path + ({k}) ) ) )

            return error;
        else if( v != typeof( m2[k] ) )
            return "*Type mismatch for " + implode(path + ({k}) ,"/") +
". Expected: " + v + ", Got: " + typeof( m2[k] ) + ".";       
    }
    return 1;
}

/*
 Accepts a specification and data mapping and validates the data mapping
 conforms to the type specification specified by the specification. This
 procedure works for non-recursive (finite) specifications only.
 
 Example:
         validate_mapping(
            /* specification */
                ([ "name" : "string",
                   "hp" : "int",
                   "properties" :
                       ([
                         "no attack" : "int",
                         "race" : "string"
                       ])
                ]),
               
                /* data */
            ([ "name" : "Silenus",
                   "hp" : "100",
                   "properties" :
                       ([
                         "no attack" : "1",
                         "race" : "elf"
                       ])
                ])
                );       
*/
mixed
validate_mapping(mapping m1, mapping m2)
{
    return _validate(m1,m2,({}));
}
« Last Edit: September 08, 2008, 02:15:17 PM by silenus » Logged
silenus
BFF
***
Offline Offline

Posts: 102


View Profile
« Reply #1 on: September 17, 2008, 04:47:35 PM »

Hi everyone,

Here is an updated version which is slightly more powerful. It allows for labels which allows for recursive types and includes a syntax for signalling whether a field can be one of two types. If you are using mappings as a substitute for classes something like this is probably need to verify that the mapping data is formatted correctly prior to use.  You can obviously do this manually but it's probably easier to do this using some sort of automatic procedure like the one shown below.

Regards,

Silenus.

Code:

// Silenus 2008-09-08
private mixed _type_check_map(mapping,mapping,mapping,string array);

private
mixed
_type_check(mapping defs, mixed spec, mixed v, string array path)
{
    string error;
    int flag;
    switch( typeof(spec) )
    {
        case "string":   
            if( !defs[spec] )
            {
                if( spec != typeof(v) )
                    return "*Type mismatch for " + implode(path,"/") +
". Expected: " + spec + ", Got: " + typeof( v ) + ".";   
            }
            else if( stringp( error = _type_check_map(defs,defs[spec] ,v,path) ) )
            {
                 return error;
            }
            break;
        case "function":
            if( stringp( error = evaluate(spec,v)))
                return "*At : " + implode(path, "/") + ". " + error;
            break;
        case "mapping":
            if( stringp( error = _type_check_map(defs, spec, v, path)))
                return error;
                break;
         case "array":
                if( !sizeof(spec) )
                    error("*Poorly formatted specification mapping. Error at: " + implode(path,"/") + ".");
                if( spec[0] == "array of" && sizeof(spec) == 2)
                {
                      if( !arrayp(v) ) return "*Array expected at: " + implode(path,"/") + ".";
                      foreach(mixed e in v)
                      {
                            if(stringp(error = _type_check(defs, spec[1], e, path)))
                                return "*Array element type error: " + error;
                      }   
                }
                else if( spec[0] == "mapping of" && sizeof(spec[0]) == 3)
                {
                      if( !mapp(v) ) return "*Mapping expected at: " + implode(path,"/") + ".";
                      foreach(mixed k, mixed val in v)
                      {
                          if(spec[1] != typeof(k))
                               return "*Type mismatch in key in mapping at: " + implode(path,"/") + ". Expected: " +
                                   spec[1] +", Got: " + typeof(k) + ".";
                          else if(stringp(error = _type_check(defs, spec[2], val, path)))
                                return "*Mapping value type error: " + error;   
                      }
                }
                else
                {
                      error = "";
                      flag = 0;
                      foreach(mixed k in spec)
                      {
                          if(!stringp( error += _type_check(defs, k, v, path)))
                          {
                              flag = 1;
                              break;
                          }
                      }
                      if ( !flag ) return error;
                }
                break;
           default:
               error("*Poorly formatted specification mapping.");   
    }
    return 1;
}

private
mixed
_type_check_map(mapping defs, mapping m1, mapping m2, string array path)
{
    string error;   
    if( (sizeof( keys(m1) - keys(m2) ) || sizeof( keys(m2) - keys(m1) )) && !m1["*"]  )
        return "*Keys in mapping do not match at level " + implode(path,"/") +
". Missing :" + implode( keys(m1) - keys(m2), ",") + "." +
        "Extra :" + implode( keys(m2) - keys(m1), ",") + ".";

    if( m1["*"] )
        if( sizeof(m1) != 1 )
             return "*Specification mapping at level"+ implode(path,"/") +"with wildcard can only contain a single key";
        else
        {
             foreach(mixed k, mixed v in m2)
             {
                 if( stringp( error = _type_check(defs, m1["*"], v, path + ({k}) ) ) )
                     return error; 
             }
             return 1;
        }         
    foreach(mixed k, mixed v in m1)
    {
        if( stringp( error = _type_check(defs, v, m2[k], path + ({k})  ) ) )
            return error;
    }
    return 1;
}



/*
 Accepts a specification and data mapping and validates the data mapping
 conforms to the type specification specified by the specification. This
 procedure works for non-recursive (finite) specifications only.
 
 see test() for example     
*/
mixed
type_check_map(mapping m1, mapping m2)
{
    return _type_check_map(m1["#defines#"], m1["#spec#"], m2, ({}));
}

#ifdef DEBUG
int test()
{
    return type_check_map(
            // specification
            ([ "#defines#" : ([]),
               "#spec#" :
                ([ "name" : "string",
                   "hp" : "int",
                   "properties" :
                       ([
                         "no attack" : (: ( (intp($1) && ($1 == 0 || $1 == 1)) ? 1 : "Must be either 0 or 1." ) :),
                         "race" : "string"
                       ]),
                   "limbs" : ([ "*" : ([ "max_hp" : "int"]) ])
                ]) ]),
         
            // data
            ([ "name" : "Silenus",
                   "hp" : 100,
                   "properties" :
                       ([
                         "no attack" : 1,
                         "race" : "elf"
                       ]),
                   "limbs" : ([ "head" : ([ "max_hp" : 100 ]), "torso" : ([ "max_hp" : 100 ]) ])
                ])
                ); 
}
#endif

Logged
Pages: [1]   Go Up
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.11 | SMF © 2006-2009, Simple Machines LLC Valid XHTML 1.0! Valid CSS!