Quad EC C Source Code Analysis: Difference between revisions
(→Comparison Prelim Setups - Different Returned Outputs: added ⎕TS example) |
(→Comparison Structure Configurations - Different Returned Outputs: string/char_array example added) |
||
(10 intermediate revisions by the same user not shown) | |||
Line 33: | Line 33: | ||
<br>If execution failed: '''⎕EC''' returns the '''error message''' associated with that failure, a unicode string - as '''[[System_Function_EC|⎕EC]]''''s third output element. | <br>If execution failed: '''⎕EC''' returns the '''error message''' associated with that failure, a unicode string - as '''[[System_Function_EC|⎕EC]]''''s third output element. | ||
==Comparison | ==Comparison Structure Configurations - Different Returned Outputs== | ||
1. '''[[System_Function_T|⎕T]]''' (file <'''qf_t.c'''> code extract) initial | 1. '''[[System_Function_T|⎕T]]''' (file <'''qf_t.c'''> code extract) <u>'''initial'''</u> structure configuration for returned solitary '''scalar number''': | ||
<pre> | <pre> | ||
// Allocate a new YYRes | // Allocate a new YYRes | ||
Line 41: | Line 41: | ||
// Fill in the result token | // Fill in the result token | ||
lpYYRes->tkToken.tkFlags.TknType = TKT_VARIMMED; | lpYYRes->tkToken.tkFlags.TknType = TKT_VARIMMED; | ||
lpYYRes->tkToken.tkFlags.ImmType = IMMTYPE_FLOAT; | lpYYRes->tkToken.tkFlags.ImmType = IMMTYPE_FLOAT; // Or use IMMTYPE_INT for an integer | ||
// lpYYRes->tkToken.tkFlags.NoDisplay = FALSE; // Already zero from YYAlloc | // lpYYRes->tkToken.tkFlags.NoDisplay = FALSE; // Already zero from YYAlloc | ||
lpYYRes->tkToken.tkData.tkFloat = aplFloatSec; | lpYYRes->tkToken.tkData.tkFloat = aplFloatSec; // DataType APLFLOAT aplFloatSec Or use DataType APLINT with tkData.tkInteger | ||
lpYYRes->tkToken.tkCharIndex = lptkFunc->tkCharIndex; | lpYYRes->tkToken.tkCharIndex = lptkFunc->tkCharIndex; | ||
</pre> | </pre> | ||
<br>2. '''[[System_Function_EC|⎕EC]]''' (file <'''qf_ec.c'''> code extract) initial | <br>2. '''[[System_Function_EC|⎕EC]]''' (file <'''qf_ec.c'''> code extract) <u>'''initial'''</u> structure configuration for returned 3-element '''nested vector''': | ||
<pre> | <pre> | ||
// Allocate a new YYRes | // Allocate a new YYRes | ||
lpYYRes = YYAlloc (); | lpYYRes = YYAlloc (); // Setup line # 1 for lpYYRes | ||
// Fill in the result token | // Fill in the result token | ||
lpYYRes->tkToken.tkFlags.TknType = TKT_VARARRAY; | lpYYRes->tkToken.tkFlags.TknType = TKT_VARARRAY; // Setup line # 2 for lpYYRes | ||
// lpYYRes->tkToken.tkFlags.ImmType = IMMTYPE_ERROR; // Already zero from YYAlloc | // lpYYRes->tkToken.tkFlags.ImmType = IMMTYPE_ERROR; // Already zero from YYAlloc | ||
// lpYYRes->tkToken.tkFlags.NoDisplay = FALSE; // Already zero from YYAlloc | // lpYYRes->tkToken.tkFlags.NoDisplay = FALSE; // Already zero from YYAlloc | ||
lpYYRes->tkToken.tkData.tkGlbData = MakePtrTypeGlb (hGlbRes); | lpYYRes->tkToken.tkData.tkGlbData = MakePtrTypeGlb (hGlbRes); // Setup line # 3 for lpYYRes | ||
lpYYRes->tkToken.tkCharIndex = lptkFunc->tkCharIndex; | lpYYRes->tkToken.tkCharIndex = lptkFunc->tkCharIndex; // Setup line # 4 for lpYYRes | ||
</pre> | </pre> | ||
Differences: Note '''TKT_VARIMMED for ⎕T''' vs. '''TKT_VARARRAY for ⎕EC'''. Also note ⎕EC's use of hGlbRes for nested returned data, re: lpYYRes->tkToken.tkData.tkGlbData = MakePtrTypeGlb (hGlbRes); | Differences: Note '''TKT_VARIMMED for ⎕T''' vs. '''TKT_VARARRAY for ⎕EC'''. Also note ⎕EC's use of hGlbRes for nested returned data, re: lpYYRes->tkToken.tkData.tkGlbData = MakePtrTypeGlb (hGlbRes); | ||
Nesting occurs in ⎕EC output through the use of '''TKT_VARARRAY''' as token flags token type and through use of nested memory pointer '''MakePtrTypeGlb (hGlbRes)''' in var lpYYRes via the token's global data. From a purely structural perspective, excluding 1)pre-setup work in ⎕EC, 2)comment lines and 3)post-op processing C code, only 4 lines of C code were needed (above) to '''setup''' (not calculate) return result variable lpYYRes for ⎕EC. | |||
See also '''[[Frequently_Used_TypeDef_Structures_and_Variables#tkToken Structures More Detailed Sample Uses Table|tkToken Sample Uses Table]]'''. | |||
<br>3. '''[[System_Function_TS|⎕TS]]''' (file <'''qf_ts.c'''> code extract) | <br>3. '''[[System_Function_TS|⎕TS]]''' (file <'''qf_ts.c'''> code extract) <u>'''more'''</u> substantive structure configuration for a returned plain-vanilla / homogeneous 7-element '''unmixed vector''': | ||
<pre> | <pre> | ||
#define lpMemData ((LPAPLINT) lpMemRes) // The following seven vector elements returned are all integers | #define lpMemData ((LPAPLINT) lpMemRes) // The following seven vector elements returned are all integers | ||
Line 73: | Line 75: | ||
#undef lpMemData | #undef lpMemData | ||
// Memory ptr no longer needed, | // Memory ptr no longer needed, allow unlocking/freeing hGlbRes(technically freed upon returning from ⎕TS), reset lpMemRes pointer to null | ||
MyGlobalUnlock (hGlbRes); lpMemRes = NULL; | MyGlobalUnlock (hGlbRes); lpMemRes = NULL; | ||
Line 89: | Line 91: | ||
</pre> | </pre> | ||
The above return information C coding for '''[[System_Function_TS|⎕TS]]''' is included because setup for ⎕TS's integers output vector is relatively straightforward, i.e. less complex than a <u>nested</u> vector('''[[System_Function_EC|⎕EC]]''') but not much more complex than a scalar ('''[[System_Function_T|⎕T]]'''). | The above return information C coding for '''[[System_Function_TS|⎕TS]]''' is included because setup for ⎕TS's integers output vector is relatively straightforward, i.e. less complex than a <u>nested</u> vector('''[[System_Function_EC|⎕EC]]''') but not much more complex than a scalar ('''[[System_Function_T|⎕T]]'''). | ||
<br>4. '''[[System_Function_DR|⎕DR]]''' (file <'''qf_dr.c'''> code extract) detailed structure configuration for returned string (CHAR_ARRAY) '''char/string vector''' (extracted from function SysFnDR_Show_EM_YY): | |||
<pre> | |||
// Variable declarations (type varname), needed for C code extracted just below: | |||
APLUINT ByteRes; // # bytes in the result | |||
LPPL_YYSTYPE lpYYRes; // Ptr to the result | |||
APLNELM aplNELMRes; // Result NELM | |||
APLCHAR wszTemp[512]; // Temporary APLCHARs Unicode string variable | |||
HGLOBAL hGlbRes; // Result ... | |||
LPVOID lpMemRes; // Ptr to result global memory | |||
// Get the result NELM | |||
aplNELMRes = lstrlenW (wszTemp); | |||
// Calculate space needed for the result | |||
ByteRes = CalcArraySize (ARRAY_CHAR, aplNELMRes, 1); | |||
// Check for overflow | |||
if (ByteRes NE (APLU3264) ByteRes) | |||
goto WSFULL_EXIT; | |||
// Allocate space for the result | |||
hGlbRes = DbgGlobalAlloc (GHND, (APLU3264) ByteRes); | |||
if (hGlbRes EQ NULL) | |||
goto WSFULL_EXIT; | |||
// Lock the memory to get a ptr to it | |||
lpMemRes = MyGlobalLock (hGlbRes); | |||
#define lpHeader ((LPVARARRAY_HEADER) lpMemRes) | |||
// Fill in the header | |||
lpHeader->Sig.nature = VARARRAY_HEADER_SIGNATURE; | |||
lpHeader->ArrType = ARRAY_CHAR; | |||
////lpHeader->PermNdx = PERMNDX_NONE; // Already zero from GHND | |||
////lpHeader->SysVar = FALSE; // Already zero from GHND | |||
lpHeader->RefCnt = 1; | |||
lpHeader->NELM = aplNELMRes; | |||
lpHeader->Rank = 1; | |||
#undef lpHeader | |||
// Save the dimension in the result | |||
*VarArrayBaseToDim (lpMemRes) = aplNELMRes; | |||
// Point to the data (APLAPA struct) | |||
lpMemRes = VarArrayDataFmBase (lpMemRes); | |||
// Copy text to the result | |||
CopyMemory (lpMemRes, wszTemp, (APLU3264) BytesIn (ARRAY_CHAR, aplNELMRes)); | |||
// No longer need this ptr | |||
MyGlobalUnlock (hGlbRes); lpMemRes = NULL; | |||
// Allocate a new YYRes | |||
lpYYRes = YYAlloc (); | |||
// Fill in the result token | |||
lpYYRes->tkToken.tkFlags.TknType = TKT_VARARRAY; | |||
////lpYYRes->tkToken.tkFlags.ImmType = IMMTYPE_ERROR; // Already zero from YYAlloc | |||
////lpYYRes->tkToken.tkFlags.NoDisplay = FALSE; // Already zero from YYAlloc | |||
lpYYRes->tkToken.tkData.tkGlbData = MakePtrTypeGlb (hGlbRes); | |||
lpYYRes->tkToken.tkCharIndex = lptkFunc->tkCharIndex; | |||
return lpYYRes; | |||
</pre> |
Latest revision as of 11:30, 21 August 2015
⎕EC - C Source Code Analysis:
File <qf_ec.c> (supporting APL System Function System Function ⎕EC - Execute Controlled) located in NARS trunk folder is a model example for returning a nested vector to APL from a System Function call. Remember per your technical knowledge of APL: ⎕EC returns a 3-element nested vector to APL.
Executive Summary - Conceptual C Programmers Preliminary Overview/Preview
Variable lpYYRes upon completion of ⎕EC contains the entire 3-element nested vector result of the call to <qf_ec.c>, specifically, from monadic function SysFnMonEC_EM_YY. lpYYRes also contains a pointer to an object <hGlbRes> that is created by other code in the same file.
That global memory handle <hGlbRes> points (via lpMemRes = MyGlobalLock (hGlbRes)) to a VARARRAY_HEADER followed by the actual data (a three-element vector). The header contains various fields such as Array Type (ARRAY_NESTED), the NELM (3), the Rank (1), the dimension (3), followed by the data. As it is a nested array that gets returned, the 3-elements of the data are all pointers to a SYMENTRY (for immediate values -- BOOL, INT, FLT, and CHAR) or a global memory handle for all other items, e.g. Global Numeric, APA, HETERO (mixed array-datatype e.g. integer, char/unicode string, float double, etc.), any non-immediate array, etc.
In a sense, the internal structure of arrays in NARS C code are nested just as APL arrays themselves are nested. Nesting is ubiquitous in NARS/APL2, etc.. Think about C coding source layouts and program flow from the standpoint of "How would you design the internal structures for an APL interpreter?" The C source code would, wherever possible, parallel the APL code which APL users view and understand.
⎕EC is somewhat more complex than other APL system functions in that ⎕EC executes its right argument under controlled circumstances - this also holds true for the C code itself - complexity in the returned data structure, i.e. ⎕EC mirrors APL variable structuring.
Subordinate execution of ⎕EC's right argument returns an <LPPL_YYSTYPE> variable-type pointer(ptr) just as it's umbrella overarching function ⎕EC does. Many APL prime functions such as rho, take, iota, etc. as coded in C - also return nested arguments while many APL System ⎕ Functions merely return scalars.
Variable name <lpYYRes2> in file <qf_ec.c>'s C source code indicates it is a secondary result (re the execution of ⎕EC's right argument).
Independent of how actual execution of the APL user's argument proceeded, after executing that right arg, the Exit Type is converted into an integer Return Code (as that's the form of the first output item - the first element in ⎕EC's output vector, in ⎕EC's result vector). That first item is a simple numeric scalar, but since the result of ⎕EC is a three-element nested vector, <qf_ec.c>/SysFnMonEC_EM_YY must convert the Return Code into a LPSYMENTRY via <MakeSymEntry_EM>.
In particular - (file <qf_ec.c> C source code extract just below):
// Save the return code in the FIRST ELEMENT of ⎕EC - C's index origin is zero hence ref. to lpMemRes[0] lpMemRes[0] = MakeSymEntry_EM (IMMTYPE_INT, // Immediate type &aplLongestRC, // Ptr to immediate value lptkFunc); // Ptr to function token
Technically, ⎕EC's second output element is the value of ⎕ET, usually a 2-element vector of zeroes, at present.
What happens next depends upon whether or not the APL user's right argument execution succeeded:
If execution succeeded: ⎕EC returns the value or string value of the executed result as the third element in its output vector.
If execution failed: ⎕EC returns the error message associated with that failure, a unicode string - as ⎕EC's third output element.
Comparison Structure Configurations - Different Returned Outputs
1. ⎕T (file <qf_t.c> code extract) initial structure configuration for returned solitary scalar number:
// Allocate a new YYRes lpYYRes = YYAlloc (); // Fill in the result token lpYYRes->tkToken.tkFlags.TknType = TKT_VARIMMED; lpYYRes->tkToken.tkFlags.ImmType = IMMTYPE_FLOAT; // Or use IMMTYPE_INT for an integer // lpYYRes->tkToken.tkFlags.NoDisplay = FALSE; // Already zero from YYAlloc lpYYRes->tkToken.tkData.tkFloat = aplFloatSec; // DataType APLFLOAT aplFloatSec Or use DataType APLINT with tkData.tkInteger lpYYRes->tkToken.tkCharIndex = lptkFunc->tkCharIndex;
2. ⎕EC (file <qf_ec.c> code extract) initial structure configuration for returned 3-element nested vector:
// Allocate a new YYRes lpYYRes = YYAlloc (); // Setup line # 1 for lpYYRes // Fill in the result token lpYYRes->tkToken.tkFlags.TknType = TKT_VARARRAY; // Setup line # 2 for lpYYRes // lpYYRes->tkToken.tkFlags.ImmType = IMMTYPE_ERROR; // Already zero from YYAlloc // lpYYRes->tkToken.tkFlags.NoDisplay = FALSE; // Already zero from YYAlloc lpYYRes->tkToken.tkData.tkGlbData = MakePtrTypeGlb (hGlbRes); // Setup line # 3 for lpYYRes lpYYRes->tkToken.tkCharIndex = lptkFunc->tkCharIndex; // Setup line # 4 for lpYYRes
Differences: Note TKT_VARIMMED for ⎕T vs. TKT_VARARRAY for ⎕EC. Also note ⎕EC's use of hGlbRes for nested returned data, re: lpYYRes->tkToken.tkData.tkGlbData = MakePtrTypeGlb (hGlbRes); Nesting occurs in ⎕EC output through the use of TKT_VARARRAY as token flags token type and through use of nested memory pointer MakePtrTypeGlb (hGlbRes) in var lpYYRes via the token's global data. From a purely structural perspective, excluding 1)pre-setup work in ⎕EC, 2)comment lines and 3)post-op processing C code, only 4 lines of C code were needed (above) to setup (not calculate) return result variable lpYYRes for ⎕EC. See also tkToken Sample Uses Table.
3. ⎕TS (file <qf_ts.c> code extract) more substantive structure configuration for a returned plain-vanilla / homogeneous 7-element unmixed vector:
#define lpMemData ((LPAPLINT) lpMemRes) // The following seven vector elements returned are all integers lpMemData[0] = SystemTime.wYear; lpMemData[1] = SystemTime.wMonth; lpMemData[2] = SystemTime.wDay; lpMemData[3] = SystemTime.wHour; lpMemData[4] = SystemTime.wMinute; lpMemData[5] = SystemTime.wSecond; lpMemData[6] = SystemTime.wMilliseconds; #undef lpMemData // Memory ptr no longer needed, allow unlocking/freeing hGlbRes(technically freed upon returning from ⎕TS), reset lpMemRes pointer to null MyGlobalUnlock (hGlbRes); lpMemRes = NULL; // Allocate a new YYRes lpYYRes = YYAlloc (); // Fill in the result token lpYYRes->tkToken.tkFlags.TknType = TKT_VARARRAY; // lpYYRes->tkToken.tkFlags.ImmType = IMMTYPE_ERROR; // Already zero from YYAlloc // lpYYRes->tkToken.tkFlags.NoDisplay = FALSE; // Already zero from YYAlloc lpYYRes->tkToken.tkData.tkGlbData = MakePtrTypeGlb (hGlbRes); lpYYRes->tkToken.tkCharIndex = lptkFunc->tkCharIndex; return lpYYRes;
The above return information C coding for ⎕TS is included because setup for ⎕TS's integers output vector is relatively straightforward, i.e. less complex than a nested vector(⎕EC) but not much more complex than a scalar (⎕T).
4. ⎕DR (file <qf_dr.c> code extract) detailed structure configuration for returned string (CHAR_ARRAY) char/string vector (extracted from function SysFnDR_Show_EM_YY):
// Variable declarations (type varname), needed for C code extracted just below: APLUINT ByteRes; // # bytes in the result LPPL_YYSTYPE lpYYRes; // Ptr to the result APLNELM aplNELMRes; // Result NELM APLCHAR wszTemp[512]; // Temporary APLCHARs Unicode string variable HGLOBAL hGlbRes; // Result ... LPVOID lpMemRes; // Ptr to result global memory // Get the result NELM aplNELMRes = lstrlenW (wszTemp); // Calculate space needed for the result ByteRes = CalcArraySize (ARRAY_CHAR, aplNELMRes, 1); // Check for overflow if (ByteRes NE (APLU3264) ByteRes) goto WSFULL_EXIT; // Allocate space for the result hGlbRes = DbgGlobalAlloc (GHND, (APLU3264) ByteRes); if (hGlbRes EQ NULL) goto WSFULL_EXIT; // Lock the memory to get a ptr to it lpMemRes = MyGlobalLock (hGlbRes); #define lpHeader ((LPVARARRAY_HEADER) lpMemRes) // Fill in the header lpHeader->Sig.nature = VARARRAY_HEADER_SIGNATURE; lpHeader->ArrType = ARRAY_CHAR; ////lpHeader->PermNdx = PERMNDX_NONE; // Already zero from GHND ////lpHeader->SysVar = FALSE; // Already zero from GHND lpHeader->RefCnt = 1; lpHeader->NELM = aplNELMRes; lpHeader->Rank = 1; #undef lpHeader // Save the dimension in the result *VarArrayBaseToDim (lpMemRes) = aplNELMRes; // Point to the data (APLAPA struct) lpMemRes = VarArrayDataFmBase (lpMemRes); // Copy text to the result CopyMemory (lpMemRes, wszTemp, (APLU3264) BytesIn (ARRAY_CHAR, aplNELMRes)); // No longer need this ptr MyGlobalUnlock (hGlbRes); lpMemRes = NULL; // Allocate a new YYRes lpYYRes = YYAlloc (); // Fill in the result token lpYYRes->tkToken.tkFlags.TknType = TKT_VARARRAY; ////lpYYRes->tkToken.tkFlags.ImmType = IMMTYPE_ERROR; // Already zero from YYAlloc ////lpYYRes->tkToken.tkFlags.NoDisplay = FALSE; // Already zero from YYAlloc lpYYRes->tkToken.tkData.tkGlbData = MakePtrTypeGlb (hGlbRes); lpYYRes->tkToken.tkCharIndex = lptkFunc->tkCharIndex; return lpYYRes;