7 #include <simgear/nasal/nasal.h>
8 #include <simgear/props/props.hxx>
9 #include <simgear/props/vectorPropTemplates.hxx>
11 #include <Main/globals.hxx>
13 #include "NasalSys.hxx"
17 // Implementation of a Nasal wrapper for the SGPropertyNode class,
18 // using the Nasal "ghost" (er... Garbage collection Handle for
19 // OutSide Thingy) facility.
21 // Note that these functions appear in Nasal with prepended
22 // underscores. They work on the low-level "ghost" objects and aren't
23 // intended for use from user code, but from Nasal code you will find
24 // in props.nas. That is where the Nasal props.Node class is defined,
25 // which provides a saner interface along the lines of SGPropertyNode.
27 static void propNodeGhostDestroy(void* ghost)
29 SGPropertyNode* prop = static_cast<SGPropertyNode*>(ghost);
30 if (!SGPropertyNode::put(prop)) delete prop;
33 naGhostType PropNodeGhostType = { propNodeGhostDestroy, "prop" };
35 naRef propNodeGhostCreate(naContext c, SGPropertyNode* ghost)
37 if(!ghost) return naNil();
38 SGPropertyNode::get(ghost);
39 return naNewGhost(c, &PropNodeGhostType, ghost);
42 naRef FGNasalSys::propNodeGhost(SGPropertyNode* handle)
44 return propNodeGhostCreate(_context, handle);
47 SGPropertyNode* ghostToPropNode(naRef ref)
49 if (!naIsGhost(ref) || (naGhost_type(ref) != &PropNodeGhostType))
52 return static_cast<SGPropertyNode*>(naGhost_ptr(ref));
55 #define NASTR(s) s ? naStr_fromdata(naNewString(c),(char*)(s),strlen(s)) : naNil()
58 // Standard header for the extension functions. It turns the "ghost"
59 // found in arg[0] into a SGPropertyNode_ptr, and then "unwraps" the
60 // vector found in the second argument into a normal-looking args
61 // array. This allows the Nasal handlers to do things like:
62 // Node.getChild = func { _getChild(me.ghost, arg) }
65 if(argc < 2 || !naIsGhost(args[0]) || \
66 naGhost_type(args[0]) != &PropNodeGhostType) \
67 naRuntimeError(c, "bad argument to props function"); \
68 SGPropertyNode_ptr node = static_cast<SGPropertyNode*>(naGhost_ptr(args[0]));
75 // Pops the first argument as a relative path if the first condition
76 // is true (e.g. argc > 1 for getAttribute) and if it is a string.
77 // If the second confition is true, then another is popped to specify
78 // if the node should be created (i.e. like the second argument to
81 // Note that this makes the function return nil if the node doesn't
82 // exist, so all functions with a relative_path parameter will
83 // return nil if the specified node does not exist.
85 #define MOVETARGET(cond1, create) \
87 naRef name = naVec_get(argv, 0); \
88 if(naIsString(name)) { \
90 node = node->getNode(naStr_data(name), create); \
91 } catch(const string& err) { \
92 naRuntimeError(c, (char *)err.c_str()); \
95 if(!node) return naNil(); \
96 naVec_removefirst(argv); /* pop only if we were successful */ \
101 // Get the type of a property (returns a string).
103 // props.Node.getType(string relative_path);
104 // props.Node.getType();
105 static naRef f_getType(naContext c, naRef me, int argc, naRef* args)
107 using namespace simgear;
109 MOVETARGET(naVec_size(argv) > 0, false);
110 const char* t = "unknown";
111 switch(node->getType()) {
112 case props::NONE: t = "NONE"; break;
113 case props::ALIAS: t = "ALIAS"; break;
114 case props::BOOL: t = "BOOL"; break;
115 case props::INT: t = "INT"; break;
116 case props::LONG: t = "LONG"; break;
117 case props::FLOAT: t = "FLOAT"; break;
118 case props::DOUBLE: t = "DOUBLE"; break;
119 case props::STRING: t = "STRING"; break;
120 case props::UNSPECIFIED: t = "UNSPECIFIED"; break;
121 case props::VEC3D: t = "VEC3D"; break;
122 case props::VEC4D: t = "VEC4D"; break;
123 case props::EXTENDED: t = "EXTENDED"; break; // shouldn't happen
129 // Get an attribute of a property by name (returns true/false).
131 // props.Node.getType(string relative_path,
132 // string attribute_name);
133 // props.Node.getType(string attribute_name);
134 static naRef f_getAttribute(naContext c, naRef me, int argc, naRef* args)
137 if(naVec_size(argv) == 0) return naNum(unsigned(node->getAttributes()));
138 MOVETARGET(naVec_size(argv) > 1, false);
139 naRef val = naVec_get(argv, 0);
140 const char *a = naStr_data(val);
141 SGPropertyNode::Attribute attr;
143 if(!strcmp(a, "last")) return naNum(SGPropertyNode::LAST_USED_ATTRIBUTE);
144 else if(!strcmp(a, "children")) return naNum(node->nChildren());
145 else if(!strcmp(a, "listeners")) return naNum(node->nListeners());
146 // Number of references without instance used in this function
147 else if(!strcmp(a, "references")) return naNum(node.getNumRefs() - 1);
148 else if(!strcmp(a, "tied")) return naNum(node->isTied());
149 else if(!strcmp(a, "alias")) return naNum(node->isAlias());
150 else if(!strcmp(a, "readable")) attr = SGPropertyNode::READ;
151 else if(!strcmp(a, "writable")) attr = SGPropertyNode::WRITE;
152 else if(!strcmp(a, "archive")) attr = SGPropertyNode::ARCHIVE;
153 else if(!strcmp(a, "trace-read")) attr = SGPropertyNode::TRACE_READ;
154 else if(!strcmp(a, "trace-write")) attr = SGPropertyNode::TRACE_WRITE;
155 else if(!strcmp(a, "userarchive")) attr = SGPropertyNode::USERARCHIVE;
156 else if(!strcmp(a, "preserve")) attr = SGPropertyNode::PRESERVE;
158 naRuntimeError(c, "props.getAttribute() with invalid attribute");
161 return naNum(node->getAttribute(attr));
165 // Set an attribute by name and boolean value or raw (bitmasked) number.
167 // props.Node.setAttribute(string relative_path,
168 // string attribute_name,
170 // props.Node.setAttribute(string attribute_name,
172 // props.Node.setArtribute(int attributes);
173 static naRef f_setAttribute(naContext c, naRef me, int argc, naRef* args)
176 MOVETARGET(naVec_size(argv) > 2, false);
177 naRef val = naVec_get(argv, 0);
178 if(naVec_size(argv) == 1 && naIsNum(val)) {
179 naRef ret = naNum(node->getAttributes());
180 node->setAttributes((int)val.num);
183 SGPropertyNode::Attribute attr;
184 const char *a = naStr_data(val);
186 if(!strcmp(a, "readable")) attr = SGPropertyNode::READ;
187 else if(!strcmp(a, "writable")) attr = SGPropertyNode::WRITE;
188 else if(!strcmp(a, "archive")) attr = SGPropertyNode::ARCHIVE;
189 else if(!strcmp(a, "trace-read")) attr = SGPropertyNode::TRACE_READ;
190 else if(!strcmp(a, "trace-write")) attr = SGPropertyNode::TRACE_WRITE;
191 else if(!strcmp(a, "userarchive")) attr = SGPropertyNode::USERARCHIVE;
192 else if(!strcmp(a, "preserve")) attr = SGPropertyNode::PRESERVE;
194 naRuntimeError(c, "props.setAttribute() with invalid attribute");
197 naRef ret = naNum(node->getAttribute(attr));
198 node->setAttribute(attr, naTrue(naVec_get(argv, 1)) ? true : false);
203 // Get the simple name of this node.
205 // props.Node.getName();
206 static naRef f_getName(naContext c, naRef me, int argc, naRef* args)
209 return NASTR(node->getName());
213 // Get the index of this node.
215 // props.Node.getIndex();
216 static naRef f_getIndex(naContext c, naRef me, int argc, naRef* args)
219 return naNum(node->getIndex());
222 // Check if other_node refers to the same as this node.
224 // props.Node.equals(other_node);
225 static naRef f_equals(naContext c, naRef me, int argc, naRef* args)
229 naRef rhs = naVec_get(argv, 0);
230 if( !naIsGhost(rhs) || naGhost_type(rhs) != &PropNodeGhostType )
233 SGPropertyNode* node_rhs = static_cast<SGPropertyNode*>(naGhost_ptr(rhs));
234 return naNum(node.ptr() == node_rhs);
238 naRef makeVectorFromVec(naContext c, const T& vec)
240 const int num_components
241 = sizeof(vec.data()) / sizeof(typename T::value_type);
242 naRef vector = naNewVector(c);
243 naVec_setsize(c, vector, num_components);
244 for (int i = 0; i < num_components; ++i)
245 naVec_set(vector, i, naNum(vec[i]));
250 // Get the value of a node, with or without a relative path.
252 // props.Node.getValue(string relative_path);
253 // props.Node.getValue();
254 static naRef f_getValue(naContext c, naRef me, int argc, naRef* args)
256 using namespace simgear;
258 MOVETARGET(naVec_size(argv) > 0, false);
259 switch(node->getType()) {
260 case props::BOOL: case props::INT:
261 case props::LONG: case props::FLOAT:
264 double dv = node->getDoubleValue();
265 if (SGMisc<double>::isNaN(dv)) {
266 SG_LOG(SG_NASAL, SG_ALERT, "Nasal getValue: property " << node->getPath() << " is NaN");
274 case props::UNSPECIFIED:
275 return NASTR(node->getStringValue());
277 return makeVectorFromVec(c, node->getValue<SGVec3d>());
279 return makeVectorFromVec(c, node->getValue<SGVec4d>());
286 T makeVecFromVector(naRef vector)
289 const int num_components
290 = sizeof(vec.data()) / sizeof(typename T::value_type);
291 int size = naVec_size(vector);
293 for (int i = 0; i < num_components && i < size; ++i) {
294 naRef element = naVec_get(vector, i);
295 naRef n = naNumValue(element);
303 // Set the value of a node; returns true if it succeeded or
304 // false if it failed. <val> can be a string, number, or a
305 // vector or numbers (for SGVec3D/4D types).
307 // props.Node.setValue(string relative_path,
309 // props.Node.setValue(val);
310 static naRef f_setValue(naContext c, naRef me, int argc, naRef* args)
313 MOVETARGET(naVec_size(argv) > 1, true);
314 naRef val = naVec_get(argv, 0);
316 if(naIsString(val)) {
317 result = node->setStringValue(naStr_data(val));
318 } else if(naIsVector(val)) {
319 if(naVec_size(val) == 3)
320 result = node->setValue(makeVecFromVector<SGVec3d>(val));
321 else if(naVec_size(val) == 4)
322 result = node->setValue(makeVecFromVector<SGVec4d>(val));
324 naRuntimeError(c, "props.setValue() vector value has wrong size");
327 naRuntimeError(c, "props.setValue() with non-number");
329 double d = naNumValue(val).num;
330 if (SGMisc<double>::isNaN(d)) {
331 naRuntimeError(c, "props.setValue() passed a NaN");
334 result = node->setDoubleValue(d);
336 return naNum(result);
339 static naRef f_setIntValue(naContext c, naRef me, int argc, naRef* args)
342 MOVETARGET(naVec_size(argv) > 1, true);
344 // int iv = (int)naNumValue(naVec_get(argv, 0)).num;
346 // Junk to pacify the gcc-2.95.3 optimizer:
347 naRef tmp0 = naVec_get(argv, 0);
348 naRef tmp1 = naNumValue(tmp0);
350 naRuntimeError(c, "props.setIntValue() with non-number");
351 double tmp2 = tmp1.num;
354 return naNum(node->setIntValue(iv));
357 static naRef f_setBoolValue(naContext c, naRef me, int argc, naRef* args)
360 MOVETARGET(naVec_size(argv) > 1, true);
361 naRef val = naVec_get(argv, 0);
362 return naNum(node->setBoolValue(naTrue(val) ? true : false));
365 static naRef f_setDoubleValue(naContext c, naRef me, int argc, naRef* args)
368 MOVETARGET(naVec_size(argv) > 1, true);
369 naRef r = naNumValue(naVec_get(argv, 0));
371 naRuntimeError(c, "props.setDoubleValue() with non-number");
373 if (SGMisc<double>::isNaN(r.num)) {
374 naRuntimeError(c, "props.setDoubleValue() passed a NaN");
377 return naNum(node->setDoubleValue(r.num));
381 // Get the parent of this node as a ghost.
383 // props.Node.getParent();
384 static naRef f_getParent(naContext c, naRef me, int argc, naRef* args)
387 SGPropertyNode* n = node->getParent();
388 if(!n) return naNil();
389 return propNodeGhostCreate(c, n);
393 // Get a child by name and optional index=0, creating if specified (by default it
394 // does not create it). If the node does not exist and create is false, then it
395 // returns nil, else it returns a (possibly new) property ghost.
397 // props.Node.getChild(string relative_path,
399 // bool create=false);
400 static naRef f_getChild(naContext c, naRef me, int argc, naRef* args)
403 naRef child = naVec_get(argv, 0);
404 if(!naIsString(child)) return naNil();
405 naRef idx = naNumValue(naVec_get(argv, 1));
406 bool create = naTrue(naVec_get(argv, 2)) != 0;
410 n = node->getChild(naStr_data(child), create);
412 n = node->getChild(naStr_data(child), (int)idx.num, create);
414 } catch (const string& err) {
415 naRuntimeError(c, (char *)err.c_str());
418 if(!n) return naNil();
419 return propNodeGhostCreate(c, n);
423 // Get all children with a specified name as a vector of ghosts.
425 // props.Node.getChildren(string relative_path);
426 // props.Node.getChildren(); #get all children
427 static naRef f_getChildren(naContext c, naRef me, int argc, naRef* args)
430 naRef result = naNewVector(c);
431 if(naIsNil(argv) || naVec_size(argv) == 0) {
433 for(int i=0; i<node->nChildren(); i++)
434 naVec_append(result, propNodeGhostCreate(c, node->getChild(i)));
436 // Get all children of a specified name
437 naRef name = naVec_get(argv, 0);
438 if(!naIsString(name)) return naNil();
440 vector<SGPropertyNode_ptr> children
441 = node->getChildren(naStr_data(name));
442 for(unsigned int i=0; i<children.size(); i++)
443 naVec_append(result, propNodeGhostCreate(c, children[i]));
444 } catch (const string& err) {
445 naRuntimeError(c, (char *)err.c_str());
453 // Append a named child at the first unused index...
455 // props.Node.addChild(string name,
457 // bool append=true);
458 static naRef f_addChild(naContext c, naRef me, int argc, naRef* args)
461 naRef child = naVec_get(argv, 0);
462 if(!naIsString(child)) return naNil();
463 naRef ref_min_index = naNumValue(naVec_get(argv, 1));
464 naRef ref_append = naVec_get(argv, 2);
469 if(!naIsNil(ref_min_index))
470 min_index = ref_min_index.num;
473 if(!naIsNil(ref_append))
474 append = naTrue(ref_append) != 0;
476 n = node->addChild(naStr_data(child), min_index, append);
478 catch (const string& err)
480 naRuntimeError(c, (char *)err.c_str());
484 return propNodeGhostCreate(c, n);
487 static naRef f_addChildren(naContext c, naRef me, int argc, naRef* args)
490 naRef child = naVec_get(argv, 0);
491 if(!naIsString(child)) return naNil();
492 naRef ref_count = naNumValue(naVec_get(argv, 1));
493 naRef ref_min_index = naNumValue(naVec_get(argv, 2));
494 naRef ref_append = naVec_get(argv, 3);
498 if( !naIsNum(ref_count) )
499 throw string("props.addChildren() missing number of children");
500 count = ref_count.num;
503 if(!naIsNil(ref_min_index))
504 min_index = ref_min_index.num;
507 if(!naIsNil(ref_append))
508 append = naTrue(ref_append) != 0;
510 const simgear::PropertyList& nodes =
511 node->addChildren(naStr_data(child), count, min_index, append);
513 naRef result = naNewVector(c);
514 for( size_t i = 0; i < nodes.size(); ++i )
515 naVec_append(result, propNodeGhostCreate(c, nodes[i]));
518 catch (const string& err)
520 naRuntimeError(c, (char *)err.c_str());
527 // Remove a child by name and index. Returns it as a ghost.
529 // props.Node.removeChild(string relative_path,
531 static naRef f_removeChild(naContext c, naRef me, int argc, naRef* args)
534 naRef child = naVec_get(argv, 0);
535 naRef index = naVec_get(argv, 1);
536 if(!naIsString(child) || !naIsNum(index)) return naNil();
537 SGPropertyNode_ptr n;
539 n = node->removeChild(naStr_data(child), (int)index.num);
540 } catch (const string& err) {
541 naRuntimeError(c, (char *)err.c_str());
543 return propNodeGhostCreate(c, n);
547 // Remove all children with specified name. Returns a vector of all the nodes
548 // removed as ghosts.
550 // props.Node.removeChildren(string relative_path);
551 // props.Node.removeChildren(); #remove all children
552 static naRef f_removeChildren(naContext c, naRef me, int argc, naRef* args)
555 naRef result = naNewVector(c);
556 if(naIsNil(argv) || naVec_size(argv) == 0) {
557 // Remove all children
558 for(int i = node->nChildren() - 1; i >=0; i--)
559 naVec_append(result, propNodeGhostCreate(c, node->removeChild(i)));
561 // Remove all children of a specified name
562 naRef name = naVec_get(argv, 0);
563 if(!naIsString(name)) return naNil();
565 vector<SGPropertyNode_ptr> children
566 = node->removeChildren(naStr_data(name));
567 for(unsigned int i=0; i<children.size(); i++)
568 naVec_append(result, propNodeGhostCreate(c, children[i]));
569 } catch (const string& err) {
570 naRuntimeError(c, (char *)err.c_str());
577 // Remove all children of a property node.
579 // props.Node.removeAllChildren();
580 static naRef f_removeAllChildren(naContext c, naRef me, int argc, naRef* args)
583 node->removeAllChildren();
584 return propNodeGhostCreate(c, node);
587 // Alias this property to another one; returns 1 on success or 0 on failure
588 // (only applicable to tied properties).
590 // props.Node.alias(string global_path);
591 // props.Node.alias(prop_ghost node);
592 // props.Node.alias(props.Node node); #added by props.nas
593 static naRef f_alias(naContext c, naRef me, int argc, naRef* args)
597 naRef prop = naVec_get(argv, 0);
599 if(naIsString(prop)) al = globals->get_props()->getNode(naStr_data(prop), true);
600 else if(naIsGhost(prop)) al = static_cast<SGPropertyNode*>(naGhost_ptr(prop));
601 else throw string("props.alias() with bad argument");
602 } catch (const string& err) {
603 naRuntimeError(c, (char *)err.c_str());
606 return naNum(node->alias(al));
610 // Un-alias this property. Returns 1 on success or 0 on failure (only
611 // applicable to tied properties).
613 // props.Node.unalias();
614 static naRef f_unalias(naContext c, naRef me, int argc, naRef* args)
617 return naNum(node->unalias());
621 // Get the alias of this node as a ghost.
623 // props.Node.getAliasTarget();
624 static naRef f_getAliasTarget(naContext c, naRef me, int argc, naRef* args)
627 return propNodeGhostCreate(c, node->getAliasTarget());
631 // Get a relative node. Returns nil if it does not exist and create is false,
632 // or a ghost object otherwise (wrapped into a props.Node object by props.nas).
634 // props.Node.getNode(string relative_path,
635 // bool create=false);
636 static naRef f_getNode(naContext c, naRef me, int argc, naRef* args)
639 naRef path = naVec_get(argv, 0);
640 bool create = naTrue(naVec_get(argv, 1)) != 0;
641 if(!naIsString(path)) return naNil();
644 n = node->getNode(naStr_data(path), create);
645 } catch (const string& err) {
646 naRuntimeError(c, (char *)err.c_str());
649 return propNodeGhostCreate(c, n);
653 // Create a new property node.
656 static naRef f_new(naContext c, naRef me, int argc, naRef* args)
658 return propNodeGhostCreate(c, new SGPropertyNode());
662 // Get the global root node (cached by props.nas so that it does
663 // not require a function call).
667 static naRef f_globals(naContext c, naRef me, int argc, naRef* args)
669 return propNodeGhostCreate(c, globals->get_props());
676 { f_getType, "_getType" },
677 { f_getAttribute, "_getAttribute" },
678 { f_setAttribute, "_setAttribute" },
679 { f_getName, "_getName" },
680 { f_getIndex, "_getIndex" },
681 { f_equals, "_equals" },
682 { f_getValue, "_getValue" },
683 { f_setValue, "_setValue" },
684 { f_setIntValue, "_setIntValue" },
685 { f_setBoolValue, "_setBoolValue" },
686 { f_setDoubleValue, "_setDoubleValue" },
687 { f_getParent, "_getParent" },
688 { f_getChild, "_getChild" },
689 { f_getChildren, "_getChildren" },
690 { f_addChild, "_addChild" },
691 { f_addChildren, "_addChildren" },
692 { f_removeChild, "_removeChild" },
693 { f_removeChildren, "_removeChildren" },
694 { f_removeAllChildren, "_removeAllChildren" },
695 { f_alias, "_alias" },
696 { f_unalias, "_unalias" },
697 { f_getAliasTarget, "_getAliasTarget" },
698 { f_getNode, "_getNode" },
700 { f_globals, "_globals" },
704 naRef FGNasalSys::genPropsModule()
706 naRef namespc = naNewHash(_context);
707 for(int i=0; propfuncs[i].name; i++)
708 hashset(namespc, propfuncs[i].name,
709 naNewFunc(_context, naNewCCode(_context, propfuncs[i].func)));