Internal Scripting emulation of the Lifelines scripting language was done by developing a Lifelines.gcscpt module (included in this extension) that allows many Lifelines commands to work the same way in converted Internal Scripts. The Lifelines emulator was written to minimize the number of changes required to convert a Lifelines script to a Internal Script. Nevertheless, syntax differences between Lifelines scripting language and the Internal Scripting langauge mean that 100% emulation was not possible. The documentation below explains the steps needed to reconcile those difference to translate a Lifelines script into a Internal Script.
This extension comes with translations of selected Lifelines scripts and they appear in the "Scripts" section. The "Text Files" section has all Lifelines scripts as provided with a download using original names but with file's extension changed from "ll" to "txt". An underscore was inserted into the name of any script that has been translated. You can compare the Internal Scripting version to the original to see the translation process in action. The details below can enable translation of more scripts into Internal Scripts.
Documentation details aree:
Imagine you want to convert another Lifelines script originally called script.ll for use in the . The process starts with the following steps:
newscript.gcscpt (i.e. pick any name and use Internal Scripting extension gcscpt).script.txt (i.e., extension "txt" instead of "ll"). Click on that file, copy all the text, and paste into the new newscript.gcscpt.newscript.gcscpt and the value to short text description of the script. Repeat for "menu choices SD" to make the script available in GEDitCOM too.script.txt in the "Text Files" section to indicate translation is done.Once the above steps are completed, you are ready to start translating and testing. The process is explained in the next section. Note that developing Internals Script is best done by using GEDitCOM II and GEDitCOM Editor. Although you can develop Internal Scripts using GEDitCOM, the workflow is different and cumbersome. See the GEDitCOM Editor for workflow details.
Note the goal of this Lifelines emulator was to reproduce the language as much as possible. It was not written to maximize efficiency. If you find a converted Lifelines script to be useful, a logical next step would be to convert the logic of the script into a native Internal Script. The goal would be a script that can run without using the lifeline.gcscpt module.
The remainder of this document gives details on converting Lifelines scripts to Internal Scripts for using the Lifelines.gcscpt module.
The documentation sections are:
For ideas on more scripts to convert, you can refer to the original Lifelines scripts provided in the "Text Files" section of this extension. An on-line tutorial gives a specific example of converting a Lifelines script to the Internal Script.
The section summarizes the process for converting a Lifelines script into an emulated Internal Script. Specific details on some conversion tasks are in the Lifelines Emulation Reference section. The conversion always starts with reorganizing high-level structures are follows:
main method, move it all to the end of the Internal Script, remove the proc main, remove its brackets ({}), and adjust indents (or automatically adjust indents later using GEDitCOM Editor's "Indent Commands" menu command, which works best when translation is farther along).ll_init("name of script")
|
finish() command at the end of the main commands.Lifelines.gcscpt module. It must be at the beginning of the script before any other commands.
load Lifelines |
/* a comment ... */) to Internal Scripting comments (! a comment ...) and an ! is needed on each line in mutliple line Lifelines comments. Some Lifelines scripts have long comments in the beginning and sometimes at the end with sample output. These sections can be deleted (they are always in the original file if needed).Any Lifelines script converted to using these taks should now look like:
! Header comments (if retained)
! load module
Load Lifelines
! Subroutines
(original proc's and func's)
! Preamble
ll_init("My translated script")
! Translated script
(original proc main statements)
! Final Output
finish()
! Final commands (if retained)
The rest of the convertion process deals with syntax and logic of the script. The following items list key tasks with links for more details in the Lifelines Emulation Reference section.
proc and func methods (which should now be at the beginning of the script after moving proc main as described above) to InternalScript sub methods:
proc name ( params ) {
statements
} |
func name ( params ) {
statements
return(result)
} |
to | sub name params:
statements
return result
endsub |
params in the sub definition are delimited by commas or spaces and not group withi parentheses. Besides this syntax change, various subroutine concepts may need more translation as explained below.set commands for variables to pass the first variable by reference. In Internal Scripts, "passing by reference" means to quote that variable, which effective sends the variable name to the subrouting rather than its current value. Change set commands for objects to setobj commands (the first parameter need not be quoted, although works it if is). The second parameter might need to change too. See below for details and other requirements for translating set commands.if ([varb,] expr) {
statements }
elsif ([varb], expr) {
statements }
else {
statements
} |
to | if expr
statements
else if expr
statements
else
statements
endif
|
[varb,] above will need to assign that variable before the conditional."if variable" will be true unless variable is 0 or an empty string, but will cause an error if variable is undefined. To check for an undefined variable, use "ifdef variable" or "ifndef variable" instead."if object" does not work for objects (instead of an error, it will interpret object name as a string and allows be true, i.e., a non-empty string). To check if an object exists, change conditional to "ifdef object" (true if object found) or "ifndef object" (true if object not found). For example, a Lifelines line to assign and check for birth event must be split to to two lines — assign then check using an ifdef command:
if (e, birth(indi)) ... |
to | birth(indi,e) ifdef e ... |
if ([varb,] expr) { statement }
|
to | if expr then statement |
out or cout at the start of line depending on whether the line starts with text or with col(n) to pick a column number.while loops from Lifelines syntax to Internal Scripting repeat loops:
while ([varb,] expr) {
statements }
|
to | repeat if notexpr then break statements endrepeat |
notxpr exits the loop when the expr in the Lifelines command is not true. Notice that Internal Scripts cannot assign a variable and check an expression in the same line. Therefore Lifelines scripts that use the optional [varb,] above will need to initialize that variable before the loop starts.
forindi(), forindiset(), children(), spouses(), families(), forfam(). Copy the Internal Scripting version from this document, paste into script being translated, and then change variable names to those used in the original script.With luck, the conversion will be complete. Try running in and see what happens. The most likely errors are Internal Scripting syntax errors. Each time you run, the script will stop at the first error and print a brief (sometimes vague) reason. The error message will also indicate line in the script. Use "Go To Line..." menu command in GEDitCOM Editor to find that line. An error that says "In loaded subroutines" means the error was in the Lifelines.gcscpt module. It usually means you passed an invalid parameter to an module method.
The following sections list all methods from the Lifelines manual, which is available in this extension in pdf form (control click and use "Back" to return to this documentation) or in a on-line translation). This reference does not define the behavior of the Lifelines functions (you can refer to the Lifelines documentation for that information). Instead, this reference lists all Lifelines methods, whether or not they are implemented, and whether or not they are identical or require some modification. Fully supported methods are in black; methods needing modification are in green, and methods not implemented are in red. The subsections are:
Lifelines defines several types of values and any one can be assigned to a variable name. Internal Scripting has "variables" and "objects." Variables are anything that can be represented by a number or a string. Objects are eveything else. Internal Scripting has many types of objects. You can refer to GEDitCOM Editor help for listing of all possible object types:
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| INT | Variable | Internal scripting variables can be integers or floating point numbers |
| BOOL | Variable | A value of "0" or an empty string is treated as "false" in a conditional. All other values are "true" |
| STRING | Variable | Any text string (including numbers as strings) |
| LIST | Object | Internal Scripting "List" object |
| TABLE | Object | Internal Scripting "Dictionary" object |
| INDI | Object | Internal Scripting "Individual Record" object |
| FAM | Object | Internal Scripting "Family Record" object |
| SET | Object | Custom object implemented using the Lifelines.gcscpt module |
| NODE | Object | Internal Scripting "GEDCOM Record" or "GEDCOM Structure" object |
| EVENT | Object | Internal Scripting "GEDCOM Structure" object |
In Lifelines, all types can be assigned to variable names. In contrast, Internal Script maintains different name spaces for variable names and object names. It is important that a scipt never uses the same name for a variable as a name used for an object.
When writing Internal Scripts, variable names can start with a number sign (#) while objects names cannot. An good approach when writing Internal Scripts is to use a number sign for all variable names. By this approach, variable and object namnes are never in conflict. When transloating a Lifelines script, however, the original script's variables will not start with number signs. You could insert number signs in front of all
All expression should be converted to Internal Scripting expressions, which are very similar to most programming languages. Most Lifelines functions used in expressions are supported for convenience. Expressions in an Internal Script can only combine variables, numbers, and strings. Numbers are combined by usually operators, String are concatenated using ampersands (&).
Expressions cannot use objects unless the entire expression is just the name of the object or a string expression that evaluates to the name of a object.
Conditionals, loops, and procedure calls must all be converted to Internal Scripting syntax as explained above. Some additional statements also require minor changes:
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| include(string) | See below | |
| global(varb) | Designate local variables instead (see Subroutines and Functions) | |
| set(varb, expr) | set("varb",expr) setobj(obj,expr) | Or convert to Internal Scripting asignments; see below |
| continue() | continue | |
| break() | break | |
| return([expr]) | return expr | But a result returned from a subroutine has to read using result() after the subroutine is done (and before another subroutine is called). |
The include(string) statement is not supported in this translation. It can be replaced by putting the other script into an Internal Scripting module in this extension (e.g., myModule.gcscpt) and then importing it with
Load myModule.gcscpt
and the Load command must be at the start of the script along with the Load Lifelines command. Such modules, however, can only contain Internal scripting subroutines and definitions.
For set() to "set" a variable, the variable has to be passed by reference in an Internal Script, which means to put the variable's name in quotes. The variable is set to value of expr, which is any expression of variables that evaluates to a string or number. A common mistake when translating scripts it to forget to quote the variable. The script will not flag that as an error, but the script will not work because the variable will not be set.
Note that emulation of the set() is just to make the translated script "look" like the original Lifelines script. If you are not concerned with looks, it is simpler to replace set() commands for variables with an Internal Script assignment command:
Lifelines: set(varb,expr)
Emulation Method: set("varb",expr) ! quote varb
Assignment Method: varb = expr ! just use Internal Script assignment
Because Internal Scripts have variables and objects, a separate command is needed to set objects. A setobj(obj,expr) sets obj to object name in expr (which is either just an object's name or an expression that evaluates to an object's name). A common method in Lifelines scripts to set an object is:
set(nextgen,parentset(thisgen))
or to set an object to result of a function. Because such subroutines in Internal Scripts cannot return an object and objects are set with setobj(), such commands must be changed. To make this easy, the Lifelines.gcscpt module adds an extra parameter to most functions like parentset(thisgen) to assign a new object. The above line from a Lifelines script could change to:
parentset(thisgen,nextgen)
Note that because Internal Scripts always pass objects by references, the first parameter need not be quoted (although it will still work if it is quoted). The second parameter in emulated functions is optional. If it is not used, the translation would need two lines — one to get the parent set and one to assign it to an object:
parentset(thisgen) setobj(nextgen,result())
The subroutine will return a new object name and that name can be fetched with the return() expression and then used in a setobj() command (return() must be used before calling any other subroutine, because a new call would replace returned value with a new returned value).
Note that emulation of the set() with setobj() for objects is just to make the translated script "look" like the original Lifelines script. If you are not concerned with looks, it is simpler to replace set() for objects with an Internal Script object assignment command:
Lifelines: set(varb,expr) Emulation Method: setobj(varb,object) ! replace expr with pre-calculated object Assignment Method: @varb = object ! just use Internal Script object assignment
Lifelines has procs and funcs. They are similar except funcs can return a value to be used inside an expression. In contrast, Internal Scripts have only subs. While they can return a value, they cannot be used in expressions. Translations of procs and funcs first changes their syntax and then may need one or more of the following changes:
call to gosub or can just delete the call (because gosub is optional in Internal Scripts).funcs that are used in expression have to moved to a separate line and the return value is fetched when done. For example, translation of code to assign an individual's birth object to e using set(e,birth(indi)) can translate two ways
birth(indi) setobj(e,result()) |
birth(indi,e) |
birth(indi) and then uses result() instead of birth(indi) in a setobj() command, which is changed from a set() command because it is setting an object. The second option looks up birth and assigns to an object name passed in a second optional argument. Most emulated functions in the Lifelines.gcscpt module include such an optional argument to simplify translation.proc or func method declares global variables, remove that line and replace with Internal Script methods. All variables in Internal Script default to be global so you never need to declare a global, but this means a subroutine might inadvertently change a global variable. To solve this issue, you can declare variables used only in the subroutine to be local, which is done using local commands immediately after the sub command. Each one can have any number of comma separated variable names.
proc pout(gen,indi) {
statements
}
getindi(indi)
set(gen,0)
call pout(gen,indi) |
sub pout,gen,indi statement endsub getindi(indi) set(gen,0 gosub pout(gen,indi) |
indi (set to string "indi") thereby resulting in both a variable and an object with the same name. The following uses Internal Scripting object assignment to fix the issue:
sub pout,gen,#indi @indi = #indi statements endsub |
sub definition line. Objects need more work. Any object the subroutine will need after a recursive call should be pushed onto an object stack and then pop back when returned. For example:
sub pout,gen,#indi local gen,#indi @indi = #indi statements (that might change indi and gen) pushObject(indi) gosub pout(gen,indi) popObject(indi) endsub |
pushObject be balanced by a corresponding popObject() in reverse order.
These Lifelines methods are mostly supported, but you can also convert to standard, simpler, and more powerful Internal Scripting expressions. The translations are:
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| INT add(x, y) | Same | or change to x + y |
| INT sub(x, y) | Same | or change to x - y |
| INT mul(x, y) | Same | or change to x * y |
| INT div(x, y) | Same | It is integer divide. Using x/y is floating point divide. |
| INT mod(x, y) | Same | |
| INT exp(x, y) | Same | or change to x^y |
| INT neg(x) | Same | or change to -x |
| VOID incr(x) | VOID incr("x") | or change to x += 1 |
| VOID decr(x) | VOID decr("x") | or change to x -= 1 |
| BOOL and(x, y) | Not implemented | Change to x && y |
| BOOL or(x, y) | Not implemented | Change to x || y |
| BOOL not(x) | Not implemented | Can replace if not(x) with Internal Scripting ifndef x |
| BOOL eq(x, y) | Same | or change to x = y |
| BOOL ne(x, y) | Same | or change to x != y |
| BOOL lt(x, y) | Same | or change to x < y |
| BOOL gt(x, y) | Same | or change to x > y |
| BOOL le(x, y) | Same | or change to x <= y |
| BOOL ge(x, y) | Same | or change to x >= y |
The following functions retrieve information about an individual. When the function needs an INDI, you pass a reference to a individual record.
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| STRING name(INDI [,BOOL]) | Same | The second parameter can be used, but it is ignored. A version of name() that uses the second parameter is in Lifelines.gcscpt but commented out. You can switch to that version if you want, but it is less efficient. |
| STRING fullname(INDI,BOOL,BOOL,INT) | VOID fullname(INDI,BOOL,BOOL,INT) | But result has to be read using result() |
| STRING surname(INDI) | Same | |
| STRING givens(INDI) | Same | |
| STRING trimname(INDI,INT) | Same | |
| EVENT birth(INDI) | VOID birth(INDI[,EVENT]) | Set EVENT to a person's event. If optional EVENT not provided, fetch event using result(). |
| EVENT death(INDI) | VOID death(INDI[,EVENT]) | |
| EVENT baptism(INDI) | VOID baptism(INDI[,EVENT]) | |
| EVENT burial(INDI) | VOID burial(INDI[,EVENT]) | |
| (not used) | STRING birth(INDI) | In an expression, this function inserts date for birth or death. |
| (not used) | STRING death(INDI) | |
| INDI father(INDI) | VOID father(INDI[,FATH]) | Set second parameter to a parent. If not provided, fetch parent using result(). |
| INDI mother(INDI) | VOID mother(INDI[,MOTH]) | |
| EVENT nextsib(INDI) | Not implemented | |
| EVENT prevsib(INDI) | Not implemented | |
| STRING sex(INDI) | Same | |
| BOOL male(INDI) | Same | 0 false, 1 true |
| BOOL female(INDI) | Same | 0 false, 1 true |
| STRING pn(INDI) | VOID pn(INDI) | Fetch pronoun from result() and added option to return "they/them" for individuals with unknown sex. |
| INT nspouses(INDI) | Same | |
| INT nfamilies(INDI) | Same | |
| FAM parents(INDI) | VOID parents(INDI[,FAM]) | Set second parameter to family. If not provided, fetch family using result(). |
| STRING title(INDI) | Same | |
| STRING key(INDI|FAM[,BOOL]) | Same | The second parameter can be used, but it is ignored. |
| STRING soundex(INDI) | STRING soundex(STRING) | Input any STRING. To get soundex of a person use soundex(name(indi)).
|
| NODE inode(INDI) | Same or change to INDI | INDI can be passed to any method expecting a NODE |
| NODE root(INDI) | ||
| INDI indi(STRING) | VOID indi(STRING[,INDI]) | Finds person by key (GEDCOM ID without '@' signs). If INDI provided it is set to the person's object. If not, fetch object using result(). |
| INDI firstindi() | VOID firstindi([INDI]) | Set parameter to individual. If not provided, fetch individual using result(). |
| INDI nextindi(INDI) | Not implemented | Loop through individuals in current sort order using forindi() |
| INDI previndi(INDI) | Not implemented | |
spouses(indi,sp,fam,num) { statements } |
spouses(indi,spouselist) repeatwith iter from spouselist spousesiter(iter,sp,fam,"num") statements endrepeat | Note that num is quoted to pass by reference. |
families(indi,fam,sp,num) { statements } | families(indi,famlist) repeatwith iter from famlist familiesiter(iter,fam,sp,"num") statements endrepeat | Note that num is quoted to pass by reference. and that sp object will be None object if that family does not have a spouse |
forindi(indi,num) { statements } | forindi(indilist) repeatwith iter from indilist forindiiter(iter,indi,"num") statements endrepeat | Note that num is quoted to pass by reference. |
The following functions retrieve information about a family. When the function needs an FAM, you pass a reference to a family record.
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| EVENT marriage(FAM) | VOID marriage(FAM[,EVENT]) | Set second parameter to output object. If optional parameter not provided, fetch it using result(). |
| INDI husband(FAM) | VOID husband(FAM[,INDI]) | |
| INDI wife(FAM) | VOID wife(FAM[,INI]) | |
| INT nchildren(FAM) | Same | |
| INDI firstchild(FAM) | VOID firstchild(FAM[,INDI]) | |
| INDI lastchild(FAM) | VOID laastchild(FAM[,INDI]) | |
| STRING key(INDI|FAM[,BOOL]) | Same | The second parameter can be used, but it is ignored. |
| NODE fnode(FAM) | Same or change to FAM | FAM can be passed to any method expecting a NODE |
| NODE root(FAM) | ||
| FAM fam(STRING) | VOID fam(STRING[,FAM]) | Finds family by key (GEDCOM ID without '@' signs). If FAM provided it is set to the family's object. If not, fetch object using result(). |
| INDI firstfam() | VOID firstfam([FAM]) | Set parameter to family. If not provided, fetch family using result(). |
| INDI nextfam(INDI) | Not implemented | Loop through individuals in current sort order using forfam() |
| INDI prevfam(INDI) | Not implemented | |
children(fam,indi,num) { statements } | children(fam,chillist) repeatwith iter from chillist childreniter(iter,indi,"num") statements endrepeat | Note that num is quoted to pass by reference. |
forfam(fam,num) {
statements
} | forfam(famlist) repeatwith iter from famlist forfamiter(iter,fam,"num") statements endrepeat | Note that num is quoted to pass by reference. |
Lists are handled using Internal Scripting lists, which means you can make use of all the Internal Scripting options for lists (see details in GEDitCOM Editor help). The Lifelines options are supported as follows:
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| VOID list(LIST_V) | Same | |
| BOOL empty(LIST) | Same | 1 if empty, 0 if not |
| INT length(LIST) | INT lengthlist(LIST) | Name changed because length is reserved by Internal Scripts. |
| VOID enqueue(LIST,ANY) | Same | |
| ANY dequeue(LIST) | VOID dequeue(LIST,ANY) | |
| VOID requeue(LIST,ANY) | Same | |
| VOID push(LIST,ANY) | Same | |
| ANY pop(LIST) | VOID pop(LIST,ANY) | |
| VOID setel(LIST,INT,ANY) | Same | |
| ANY getel(LIST,INT) | VOID getel(LIST,INT,"ANY") ifndef ANY then ANY=0 | Move ANY to third argument and quote if a variable. Invalid ANY sets ANY undefined while Lifelines returns 0. Second line only needed if list might be empty. |
forlist(alist,val,num) {
statements
} | forlist(alist,listlist) repeatwith iter from listlist forlistiter(iter,objval,"val","num") statements endrepeat |
If objval is defined (check with ifdef objval, the item is an object, but if not defined, the item is variable set equal to val Note that val and num are quoted to pass by reference. |
Tables are handled using Internal Scripting dictionaries, which means you can make use of all the Internal Scripting options for dictionaries (see details in GEDitCOM Editor help). The Lifelines methods are implemented as follows:
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| VOID table(TABLE_V) | Same | |
| VOID insert(TABLE,STRING,ANY) | VOID insert(TABLE,STRING,"ANY") | Pass ANY by reference (object name or quoted variable name) |
| ANY lookup(TABLE,STRING) | VOID lookup(TABLE,STRING,ANY) |
Lifelines nodes are equivalent to structures. In addition, record objects can be used wherever a NODE is expected and it will be treated as the root or level 0 node for that record (a record's tag is its record type).
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| STRING xref(NODE) | Not implemented | |
| STRING tag(NODE) | Same | |
| STRING value(NODE) | Same | Does not work for INDI or FAM records |
| NODE parent(NODE) | VOID parent(NODE[,NODEV]) | If optional NODEV provided it is set, otherwise fetch it using result(). |
| NODE child(NODE) | VOID child(NODE[,NODEV]) | |
| NODE sibling(NODE) | VOID sibling(NODE[,NODEV]) | |
| NODE savenode(NODE) | Not implemented | |
fornodes(node,nodev) {
statements
} | fornodes(node,nodelist) repeatwith nodev from nodelist statements endrepeat |
|
traverse(node,nodev,lev) {
statements
} | traverse(node,nodelist) repeatwith iter from nodelist traverseiter(iter,nodev,"lev") statements endrepeat |
Note that lev is quoted to pass by reference. |
In an Internal Script, an EVENT is a structure and can be used directly in any NODE function. Options in Lifelines are supported as follows:
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| STRING date(EVENT) | Same | |
| STRING place(EVENT) | Same | |
| STRING year(EVENT) | Same | |
| STRING long(EVENT) | Same | |
| STRING short(EVENT) | Same | |
| EVENT gettoday() | STRING gettoday() | Returns today's date as STRING instead of EVENT with that date. |
| VOID dayformat(EVENT) | Same | except 0 is same as 2 |
| VOID monthformat(EVENT) | Same | except 0 is same as 2, 5 is same as 3, and 6 is same as 4 |
| VOID dateformat(EVENT) | Same | |
| STRING stddate(EVENT) | VOID stddate(EVENT[,"STRING"]) | Sets variable in quoted second argument to the date. If omitted fetch the date using result(). Also allows EVENT to be date STRING. |
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| VOID extractdate(NODE,INV_V,INV_V,INV_V) | VOID extractdate(NODE,"INV_V","INV_V","INV_V") | Variables by reference (quoted) |
| VOID extractnames(NODE,LIST_V,INV_V,INV_V) | VOID extractnames(NODE,LIST_V,"INV_V","INV_V") | |
| VOID extractplaces(NODE,LIST_V,INV_V) | VOID extractplaces(NODE,LIST_V,"INV_V") | |
| VOID extracttokens(STRING,LIST_V,INV_V,STRING) | VOID extracttokens(STRING,LIST_V,"INV_V",STRING) |
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| VOID getindi(INDI_V[,STRING]) | Same | If record is selected, return it, otherwise a dialog box to pick record of appropriate type; script exits if user cancels the selection |
| VOID getindimsg(INDI_V,STRING) | Same | |
| VOID getfam(FAM_V[,STRING]) | Same | |
| VOID getfammsg(FAM_V,STRING) | Same | |
| VOID getindiset(SEV_V[,STRING]) | Not implemented | |
| VOID getint(INT_V [,STRING]) | VOID getint("INT_V" [,STRING]) | Pass variable by reference (i.e., quoted); script exits if user cancel's the entry |
| VOID getstr(STRING_V[,STRING]) | VOID getstr("STRING_V"[,STRING]) | |
| VOID getstrmsg(STRING_V,STRING) | VOID getstrmsg("STRING_V",STRING) | |
| INDI choosechild(INDI|FAM) | VOID choosechild(INDI|FAM[,INDI|FAM]) | Return choice in INDI or FAM; if not provided, fetch choice from result(). Returns None if no choice made. |
| FAM choosefam(INDI) | VOID choosefam(INDI[,FAM]) | |
| INDI chooseindi(SET) | VOID chooseindi(SET[,INDI]) | |
| INDI choosespouse(INDI) | VOID choosespouse(INDI[,INDI]) | |
| SET choosesubset(SET) | Not implemented | |
| INT menuchoose(LIST [,STRING]) | VOID menuchoose(LIST [,"INT",STRING]) | Return choice in INT (quote to pass by reference); if not provided, fetch choice from result() |
Internal Scripting has many methods for working with strings. The Lifelines methods are implemented as follows (some just for convenience):
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| STRING lower(STRING) | Same | |
| STRING upper(STRING) | Same | |
| STRING capitalize(STRING) | Same | |
| STRING trim(STRING, INT) | STRING trunc(STRING, INT) | Name change because trim is reserved in Internal Scripts. |
| STRING rjustify(STRING, INT) | Same | |
| STRING save(STRING) | Same | Just returns the string; no need to copy |
| STRING strsave(STRING) | Same | |
| STRING concat(STRING [,STRING]+) | Same | Except changed to maximum of 6 arguments |
| STRING strconcat(STRING [,STRING]+) | Same | |
| INT strlen(STRING) | Same | |
| STRING substring(STRING, INT, INT) | Same | |
| INT index(STRING, STRING, INT) | Same | Always returns index to first occurence; INT is optional and is ignored |
| STRING d(INT) | Same | |
| STRING card(INT) | Same | |
| STRING ord(INT) | Same | |
| STRING alpha(INT) | Same | |
| STRING roman(INT) | Same | |
| STRING strsoundex(STRING) | Same | |
| INT strtoint(STRING) | Same | Causes error if STRING not a number |
| INT atoi(STRING) | Same | |
| INT strcmp(STRING, STRING) | Not implemented | Use Internal Scripting string conditional expressions |
| BOOL eqstr(STRING, STRING) | Not implemented | |
| BOOL nestr(STRING, STRING) | Not implemented |
In Lifelines scripts, elements that are not function calls are sent to the output. This method has to be modified in this emulator. The functions are listed here; details on translating scripts are below the table.
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| VOID linemode() | Same | Must move to before calling ll_init() and cannot change modes within the translated script.
|
| VOID pagemode(INT, INT) | Same | |
| VOID col(INT) | Same | line mode will start new line if INT is before current column number but page will mode will move to that column on current row. |
| VOID row(INT) | Same | Page mode moves to requested row while line mode inserts input number of new lines |
| VOID pos(INT, INT) | Same | Page mode moves to position; line mode inserts new lines and then moves to provided column. |
| VOID pageout() | Same | |
| STRING INT STRING INT ... | out STRING INT ... cout INT STRING INT ... | Lines with alternating strings and column numbers prefaced with out if starts with a STRING (or only has a STRING) or with cout if starts with a column number |
| STRING nl() | Same | Used within an out line it is same as return character. |
| STRING sp() | Same | |
| STRING qt() | Same | |
| VOID newfile(STRING,BOOL) | Same | STRING can be full path to save report to file instead of open in window; if empty user prompted for save file (see below) |
| STRING outfile() | Same | File set by calling newfile() or "window" if report will go to a window. |
| VOID copyfile(STRING) | Not implemented | |
| VOID print(STRING[,STRING]*) | VOID print(STRING[,FRACTION]) | See below |
The line mode is the default mode. In this mode Lines of Lifelines scripts that are not other commands are output to the report. The translation must comnbine text elements into a single string and then preface the line with out. For example, a line such as:
"ANCESTORS OF -- " upper(name(i)) " (" key(i) ") " nl() nl()
should be converted to
out "ANCESTORS OF -- "&upper(name(i))&" ("&key(i)&") "&nl()&nl()
which combines the strings (with &'s) and inserts out() at the start of the line (note: the nl() can optionally be changed to return)
Some Lifeline scripts in line mode output text separated by col() to move to column number before the next block of text. This style is translated as follows: if the line starts in col(), start the line with cout and combine any text sections into single text blocks. For example change:
col(12) name(child) col(63) "(RN=" key(child) ")"
to
cout col(12) name(child) col(63) "(RN="&key(child)&")"
Note that ampersands (&) were used in the last text block to make it a single STRING. If the line starts with a text block, start the command with out and combine text sections into single blocks:
name(child) col(63) "(RN=" key(child) ")"
to
out name(child) col(63) "(RN="&key(child)&")"
In summary, text blocks and col() must alternate and then use cout if the line starts in col() or cout if if it starts with text.
Page mode buffers pages before adding them to a report, which allows output to jump around a page. A common option is to move to position and then output some text. For example, this Lifelines script outputs some text at a position on a page with:
pos(4,10)
" FULL NAME: " name(i) col(46) "(" key(i) ")" nl() nl()
This is translated similar to line mode to:
pos(4,10)
out " FULL NAME: "&name(i)) col(46) "("&key(i)&")"
nl()
nl()
The text blocks are combined when needed (with ampersands &) and text blocks can be separated by col() commands. In page mode for Internal Scripts, nl() or return are converted to spaces. If you really want a new line, use nl() on a line by itself. A report that is outputting consecutive lines, however, need not use explicit nl() commands because a new line will automatically be started when a requested col() within an out line is before the current column position in the report. Note that col() on a line by itself will move that column on current row and never cause a new line.
An enhancement of page mode in Internal Scripts is that if any col() within an out line is changed to -col(), the previous text will be right justified in the space up to that column. This option only applies when using page mode.
By default, script output is sent to a report window with fixed-width font. To direct to a file instead of a report, use an newfile(STRING,INT) command anytime before the final finish() command. The STRING parameter can be a full POSIX path to the desired output file. If it is empty, the user is prompted to pick a file. If user cancels option to pick a file, the report will revert to opening in a report window.
Any output displayed in a report window can also by ouuput to a file later using the "File→Export→Report to Text File" menu command in .
The print command in Lifelines sends messages to standard output. It appears scripts use this to indicate a script is working (such as printing a period) or various other information that is not part of the report. In the command is changed to have a single STRING argument (to translate one that uses multiple STRING's, concatenate them into a single string with ampersands ("&"). The optional second argument is estimate of the fraction of calculations that are completed. It is converted to a percentage and displayed in the scripting panel.
Note that the scripting panel disappears when the script is done. If you want the panel to remain open, such as if the printed messages are useful, include a write " " command in the script. This command also writes to the scripting panel, but also tells the panel to remain open after the script is done.
These functions are implemented with custom Lifelines.gcscpt modulue methods with list objected passed to each method:
This Lifelines emulation does not support editing of the data. You can however, use other script-editing methods to make changes to the data in a script.
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| NODE createnode(STRING, STRING) | Not implemented | |
| VOID addnode(NODE, NODE, NODE) | Not implemented | |
| VOID deletenode(NODE) | Not implemented |
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| BOOLEAN reference(STRING) | Not Implemented | Sending invalid reference to next two just returns None object |
| NODE dereference(STRING) | VOID dereference(STRING,NODE) | STRING is GEDCOM id with '@' signs and NODE is set to record with that id or None object if not found
|
| NODE getrecord(STRING) | VOID getrecord(STRING,NOID) |
| Lifelines | Internal Scripting | Comments |
|---|---|---|
| VOID lock(INDI|FAM) | Not implemented | |
| VOID unlock(INDI|FAM) | Not implemented | |
| STRING database() | Same | Name of document |
| STRING version() | Same | version number |
| (not used) | STRING application() | GEDitCOM II or GEDitCOM application being used |
| VOID system(STRING) | Not implemented |