For the command interface, you would probably want to use a stack (the data structure) and implement a parser using a so called "state machine", which can be as simple as a switch/case statement (in C/C++), or simply a hash with command tokens as lookup keys to register callbacks for each command:
- Code: Select all
var CommandMgr = {
'EXEC': func(state, operands) {
},
'OPS': func(state, operands) {
},
'+': func(state, operands) {
},
'-': func(state, operands) {
},
};
A simple parser could then look for each key in the CommandMgr hash (which is just a container for your callbacks, i.e. not using any OOP as is) - and if it finds a registered key, it would call the callback, passing in the current state of the state machine, including any operands the command may have (e.g. a vector of operands).
Obviously, this could also use classes/objects for better encapsulation - or do without any hashes at all, i.e. using just explicit if/else statements if you prefer a more verbose version.
For parsing purposes, you would basically tokenize the string (e.g. looking for valid tokens/separators) and then use a simple parser to call the callback for each token (possibly recursively, and possibly taking operator precedence into account)
by sequences like ITEM 4 + 20.1 +70.0 - 30.5 EXEC which uses + and - as delimiters as well as signs and is shorthand for 'set item 4 to 20.1, item 5 to 70.0 and item 6 to -30.5'.
in that case, you would need a simple stack, so that you know that subsequent + instructions also increment the item index
This kind of thing can be trivially implemented in Nasal, the main keywords are "parsing", "tokenize" and "state machine". However, there already is code in SimGear (C++) that implements an abstract state machine, so another option would be exposing/using that code - then again, performance is likely to be negligible for something like that.
For examples on writing parsers in Nasal you could refer to io.nas (xml parsing) or svg.nas (parsing svg/xml using Nasal).
I would need to look at the trajectory data to see what kind of serialization scheme would work best, but I guess I would suggest to go with conventional PropertyList XML files, because they are easy to process (create, write and read) without having to bother with complex logic like representing vectors or trees, because those are naturally mapped to XML space using the PropertyList subset.
Equally, Nasal hashes can be trivially mapped to PropertyList space and vice versa using the props.nas module, e.g. see the Canvas "menubar" snippet on the wiki, which demonstrates how to load an existing property list XML file into Nasal space and process it without having to do any explicit parsing, simply by mapping XML keys to keys in a Nasal hash:
http://wiki.flightgear.org/Howto:Making ... nubar#Code- Code: Select all
load: func() {
var fgroot_path = getprop("/sim/fg-root");
var path = fgroot_path ~ '/' ~ me._filename;
# next, load menubar.xml using read_properties() (see io.nas) :
var menubar = io.read_properties( path );
# now, turn the whole thing into a Nasal hash for dead-simple processing
me.menubar = menubar.getValues();
},
In this case it is using OOP (me keyword), but what it will do is pretty straightforward:
- assemble a path relative to fgroot
- append the file name using a member field
- call io.read_properties() to load the file and save it in a property object
- turn the tree into a Nasal hash that can be directly processed using a foreach loop
As can be seen in the build() method, there is no explicit parsing involved here, because each XML tag will become Nasal key, with sub-tags becoming child nodes in the hash. Identical nodes (same level) will be turned into vector elements, i.e. can be accessed using foo[0]..foo[x]
BTW: Could you post links to the corresponding Nasal code so that we can take a look ?