Hi,

here is my proposal for solving the variable existence checking problem [1].

The typeof macro subroutine in Thorsten Haudes Patch collection
changes the semantics of the macro language to far into the unusable,
because it allows NO_TAG variables everywhere.

This approach only changes the semantics in one case: if a function
retrns nothing (i.e. NO_TAG) this can be assign to a variable.

My trick is to switch the interpreter into a "typeof-mode" where it is
allowed to pass the value of a symbol with NO_TAG onto the stack.

The syntax is currently limited to check only the type of a symbol.
But IMHO this is not a limitation. With this you can check the type of
a variable and also the return type of a function, by assigning it to
a variable first. To assign the return value to a variable first is
not a limitation, else you must evaluate the function twice if you
need the return value.

For thinks like type(array[key]) we have already the in syntax, which
should suffice for this.

The patch [2] that is attached is on top of Tony's lengthly NEdit
macro language enhancement patches, which can be find here [3].

Regards
Bert

[1] http://www.nedit.org/pipermail/discuss/2007-May/009186.html
[2] http://bert.wesarg.googlepages.com/core-typeof-syntax.patch
[3] http://bert.wesarg.googlepages.com/ajbj_series.patch
---

 source/highlightData.c |    2 -
 source/interpret.c     |   75 ++++++++++++++++++++++++++++++++++++++++++-------
 source/interpret.h     |    1 
 source/parse.y         |   10 +++++-
 4 files changed, 75 insertions(+), 13 deletions(-)

diff --quilt old/source/interpret.c new/source/interpret.c
--- old/source/interpret.c
+++ new/source/interpret.c
@@ -153,10 +153,15 @@ static rbTreeNode *arrayEmptyAllocator(v
 static rbTreeNode *arrayAllocateNode(rbTreeNode *src);
 static int arrayEntryCopyToNode(rbTreeNode *dst, rbTreeNode *src);
 static int arrayEntryCompare(rbTreeNode *left, rbTreeNode *right);
 static void arrayDisposeNode(rbTreeNode *src);
 static SparseArrayEntry *allocateSparseArrayEntry(void);
+static int typeOfIn(void);
+static int typeOfOut(void);
+
+/* is the intepreter in the special typeof() mode? */
+static int inTypeOfMode;
 
 /*#define DEBUG_ASSEMBLY*/
 /*#define DEBUG_STACK*/
 
 #if defined(DEBUG_ASSEMBLY) || defined(DEBUG_STACK)
@@ -235,10 +240,11 @@ static int (*OpFns[N_OPS])() = {returnNo
     arrayRefAndAssignSetup, pushArgVal, pushArgCount, pushArgArray,
     anonArrayOpen, anonArraySkip, anonArrayNextVal, anonArrayIndexVal,
     anonArrayClose, namedArg1, namedArgN, swapTop2,
     callSubroutineStackedN,
     unpackArrayToArgs,
+    typeOfIn, typeOfOut,
     };
 
 /* Stack-> symN-sym0(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ... */
 #define FP_ARG_COUNT_INDEX (-1)
 #define FP_FUNCTION_NAME   (-2) /* !! */
@@ -1313,11 +1319,11 @@ static int pushSymVal(void)
 	    return execError(errMsg, s->name);
         }
     	*StackP = result;
     } else
     	return execError("reading non-variable: %s", s->name);
-    if (StackP->tag == NO_TAG) {
+    if (StackP->tag == NO_TAG && !inTypeOfMode) {
     	return execError("variable not set: %s", s->name);
     }
     StackP++;
     if (StackP >= &TheStack[STACK_SIZE]) {
     	return execError(StackOverflowMsg, "");
@@ -1417,11 +1423,11 @@ static int pushArraySymVal(void)
     if (initEmpty && dataPtr->tag == NO_TAG) {
         dataPtr->tag = ARRAY_TAG;
         dataPtr->val.arrayPtr = ArrayNew();
     }
 
-    if (dataPtr->tag == NO_TAG) {
+    if (dataPtr->tag == NO_TAG && !inTypeOfMode) {
         return execError("variable not set: %s", sym->name);
     }
 
     *StackP = *dataPtr;
     StackP++;
@@ -2712,18 +2718,16 @@ static int returnValOrNone(int valOnStac
     	    PUSH(retVal);
 	} else {
 	    PUSH(noValue);
 	}
     } else if (PC->func == fetchRetVal) {
-	if (valOnStack) {
-    	    PUSH(retVal);
-	    PC++;
-	} else {
-	    return execError(
-	    	"using return value of %s which does not return a value",
-	    	((PC-2)->sym->name));
-	}
+        if (valOnStack) {
+            PUSH(retVal);
+        } else {
+            PUSH(noValue);
+        }
+        PC++;
     }
     
     /* NULL return PC indicates end of program */
     return PC == NULL ? STAT_DONE : STAT_OK;
 }
@@ -3401,10 +3405,59 @@ static int deleteArrayElement(void)
         return(execError("attempt to delete from non-array", NULL));
     }
     return(STAT_OK);
 }
 
+static int typeOfIn(void)
+{
+    if (inTypeOfMode) {
+        return(execError("I'm already in typeof-mode", NULL));
+    }
+
+    inTypeOfMode = 1;
+
+    return STAT_OK;
+}
+
+static int typeOfOut(void)
+{
+    DataValue val;
+    DataValue retVal;
+
+    if (!inTypeOfMode) {
+        return(execError("I'm not in typeof-mode", NULL));
+    }
+
+    inTypeOfMode = 0;
+
+    POP(val)
+
+    retVal.tag = STRING_TAG;
+    switch (val.tag) {
+        case NO_TAG:
+            retVal.val.str.rep = PERM_ALLOC_STR("UNDEFINED");
+            break;
+        case INT_TAG:
+            retVal.val.str.rep = PERM_ALLOC_STR("INTEGER");
+            break;
+        case STRING_TAG:
+            retVal.val.str.rep = PERM_ALLOC_STR("STRING");
+            break;
+        case ARRAY_TAG:
+            retVal.val.str.rep = PERM_ALLOC_STR("ARRAY");
+            break;
+    }
+    retVal.val.str.len = strlen(retVal.val.str.rep);
+
+    if (PC->func == fetchRetVal) {
+        PUSH(retVal);
+        PC++;
+    }
+
+    return STAT_OK;
+}
+
 /*
 ** checks errno after operations which can set it.  If an error occured,
 ** creates appropriate error messages and returns false
 */
 static int errCheck(const char *s)
@@ -3685,10 +3738,12 @@ static void disasmInternal(Inst *inst, i
         "NAMED_ARG1",                   /* namedArg1: "fn([...]=..., ...)" */
         "NAMED_ARGN",                   /* namedArgN: "fn(..., [...]=...)" */
         "SWAP_TOP2",                    /* swapTop2: cf namedArgN */
         "SUBR_CALL_STACKED_N",          /* callSubroutineStackedN */
         "UNPACKTOARGS",                 /* unpackArrayToArgs */
+        "TYPEOF_IN",                    /* typeOfIn */
+        "TYPEOF_OUT",                   /* typeOfOut */
     };
     int i, j;
     
     printd("\n");
     for (i = 0; i < nInstr; ++i) {
diff --quilt old/source/interpret.h new/source/interpret.h
--- old/source/interpret.h
+++ new/source/interpret.h
@@ -52,10 +52,11 @@ enum operations {OP_RETURN_NO_VAL, OP_RE
     OP_ANONARRAY_OPEN, OP_ANONARRAY_SKIP, OP_ANONARRAY_NEXT_VAL,
     OP_ANONARRAY_INDEX_VAL, OP_ANONARRAY_CLOSE,
     OP_NAMED_ARG1, OP_NAMED_ARGN, OP_SWAP_TOP2,
     OP_SUBR_CALL_STACKED_N,
     OP_UNPACKTOARGS,
+    OP_TYPEOF_IN, OP_TYPEOF_OUT,
     N_OPS};
 
 enum typeTags {NO_TAG, INT_TAG, STRING_TAG, ARRAY_TAG};
 
 enum execReturnCodes {MACRO_TIME_LIMIT, MACRO_PREEMPT, MACRO_DONE, MACRO_ERROR};
diff --quilt old/source/parse.y new/source/parse.y
--- old/source/parse.y
+++ new/source/parse.y
@@ -65,11 +65,11 @@ static int nextSymIsField = 0;
     int nArgs;
     enum operations oper;
 }
 %token <sym> NUMBER STRING SYMBOL FIELD
 %token DELETE ARG_LOOKUP
-%token IF WHILE DO ELSE FOR BREAK CONTINUE RETURN
+%token IF WHILE DO ELSE FOR BREAK CONTINUE RETURN TYPEOF
 %type <nArgs> arrlist arrentry
 %type <nArgs> arglistopt arglist catlist fnarglsopt fnarglist fnarg
 %type <inst> cond comastmts comastmtlst for while do else and or arrayexpr mark
 %type <sym> evalsym
 %type <oper> operassign incrdecr
@@ -273,11 +273,16 @@ arglist:                  blank expr bla
 catlist:              numexpr %prec CONCAT { $$ = 1; }
             | catlist numexpr %prec CONCAT { $$ = $1 + 1; }
             ;
 
 /* function call and its argument lists */
-funccall:     SYMBOL '(' fnarglsopt ')' {
+funccall: TYPEOF '(' {
+                    ADD_OP(OP_TYPEOF_IN);
+                } evalsym ')' {
+                    ADD_OP(OP_TYPEOF_OUT);
+                }
+            | SYMBOL '(' fnarglsopt ')' {
                 ADD_OP(OP_SUBR_CALL);
                 ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
             }
             | SYMBOL '(' blank '=' blank expr blank ')' {
                 /* a single array replaces the argument list */
@@ -637,10 +642,11 @@ static int yylex(void)
             if (!strcmp(symName, "delete") && follow_non_whitespace('(', SYMBOL, DELETE) == DELETE) return DELETE;
             if (!strcmp(symName, "define")) {
                 InPtr -= 6;
                 return 0;
             }
+            if (!strcmp(symName, "typeof")) return TYPEOF;
             if (nextSymIsField) {
                 nextSymIsField = 0;
                 yylval.sym = InstallStringConstSymbol(symName);
                 return FIELD;
             }
diff --quilt old/source/highlightData.c new/source/highlightData.c
--- old/source/highlightData.c
+++ new/source/highlightData.c
@@ -552,11 +552,11 @@ static char *DefaultPatternSets[] = {
         Built-in Pref Vars:\"(?<!\\Y)\\$(?:auto_indent|em_tab_dist|file_format|font_name|font_name_bold|font_name_bold_italic|font_name_italic|highlight_syntax|incremental_backup|incremental_search_line|make_backup_copy|match_syntax_based|overtype_mode|show_line_numbers|show_matching|statistics_line|tab_dist|use_tabs|wrap_margin|wrap_text)>\":::Identifier2::\n\
         Built-in Special Vars:\"(?<!\\Y)\\$(?:[1-9]|list_dialog_button|n_args|read_status|search_end|shell_cmd_status|string_dialog_button|sub_sep)>\":::String1::\n\
         Built-in Subrs:\"<(?:append_file|beep|calltip|clipboard_to_string|dialog|focus_window|get_character|get_pattern_(by_name|at_pos)|get_range|get_selection|get_style_(by_name|at_pos)|getenv|kill_calltip|length|list_dialog|max|min|rangeset_(?:add|create|destroy|get_by_name|includes|info|invert|range|set_color|set_mode|set_name|subtract)|read_file|replace_in_string|replace_range|replace_selection|replace_substring|search|search_string|select|select_rectangle|set_cursor_pos|set_language_mode|set_locked|shell_command|split|string_compare|string_dialog|string_to_clipboard|substring|t_print|tolower|toupper|valid_number|write_file)>\":::Subroutine::\n\
         Menu Actions:\"<(?:new|open|open-dialog|open_dialog|open-selected|open_selected|close|save|save-as|save_as|save-as-dialog|save_as_dialog|revert-to-saved|revert_to_saved|revert_to_saved_dialog|include-file|include_file|include-file-dialog|include_file_dialog|load-macro-file|load_macro_file|load-macro-file-dialog|load_macro_file_dialog|load-tags-file|load_tags_file|load-tags-file-dialog|load_tags_file_dialog|unload_tags_file|load_tips_file|load_tips_file_dialog|unload_tips_file|print|print-selection|print_selection|exit|undo|redo|delete|select-all|select_all|shift-left|shift_left|shift-left-by-tab|shift_left_by_tab|shift-right|shift_right|shift-right-by-tab|shift_right_by_tab|find|find-dialog|find_dialog|find-again|find_again|find-selection|find_selection|find_incremental|start_incremental_find|replace|replace-dialog|replace_dialog|replace-all|replace_all|replace-in-selection|replace_in_selection|replace-again|replace_again|replace_find|replace_find_same|replace_find_again|goto-line-number|goto_line_number|goto-line-number-dialog|goto_line_number_dialog|goto-selected|goto_selected|mark|mark-dialog|mark_dialog|goto-mark|goto_mark|goto-mark-dialog|goto_mark_dialog|match|select_to_matching|goto_matching|find-definition|find_definition|show_tip|split-window|split_window|close-pane|close_pane|uppercase|lowercase|fill-paragraph|fill_paragraph|control-code-dialog|control_code_dialog|filter-selection-dialog|filter_selection_dialog|filter-selection|filter_selection|execute-command|execute_command|execute-command-dialog|execute_command_dialog|execute-command-line|execute_command_line|shell-menu-command|shell_menu_command|macro-menu-command|macro_menu_command|bg_menu_command|post_window_bg_menu|beginning-of-selection|beginning_of_selection|end-of-selection|end_of_selection|repeat_macro|repeat_dialog|raise_window|focus_pane|set_statistics_line|set_incremental_search_line|set_show_line_numbers|set_auto_indent|set_wrap_text|set_wrap_margin|set_highlight_syntax|set_make_backup_copy|set_incremental_backup|set_show_matching|set_match_syntax_based|set_overtype_mode|set_locked|set_tab_dist|set_em_tab_dist|set_use_tabs|set_fonts|set_language_mode)(?=\\s*\\()\":::Subroutine::\n\
         Text Actions:\"<(?:self-insert|self_insert|grab-focus|grab_focus|extend-adjust|extend_adjust|extend-start|extend_start|extend-end|extend_end|secondary-adjust|secondary_adjust|secondary-or-drag-adjust|secondary_or_drag_adjust|secondary-start|secondary_start|secondary-or-drag-start|secondary_or_drag_start|process-bdrag|process_bdrag|move-destination|move_destination|move-to|move_to|move-to-or-end-drag|move_to_or_end_drag|end_drag|copy-to|copy_to|copy-to-or-end-drag|copy_to_or_end_drag|exchange|process-cancel|process_cancel|paste-clipboard|paste_clipboard|copy-clipboard|copy_clipboard|cut-clipboard|cut_clipboard|copy-primary|copy_primary|cut-primary|cut_primary|newline|newline-and-indent|newline_and_indent|newline-no-indent|newline_no_indent|delete-selection|delete_selection|delete-previous-character|delete_previous_character|delete-next-character|delete_next_character|delete-previous-word|delete_previous_word|delete-next-word|delete_next_word|delete-to-start-of-line|delete_to_start_of_line|delete-to-end-of-line|delete_to_end_of_line|forward-character|forward_character|backward-character|backward_character|key-select|key_select|process-up|process_up|process-down|process_down|process-shift-up|process_shift_up|process-shift-down|process_shift_down|process-home|process_home|forward-word|forward_word|backward-word|backward_word|forward-paragraph|forward_paragraph|backward-paragraph|backward_paragraph|beginning-of-line|beginning_of_line|end-of-line|end_of_line|beginning-of-file|beginning_of_file|end-of-file|end_of_file|next-page|next_page|previous-page|previous_page|page-left|page_left|page-right|page_right|toggle-overstrike|toggle_overstrike|scroll-up|scroll_up|scroll-down|scroll_down|scroll_left|scroll_right|scroll-to-line|scroll_to_line|select-all|select_all|deselect-all|deselect_all|focusIn|focusOut|process-return|process_return|process-tab|process_tab|insert-string|insert_string|mouse_pan)>\":::Subroutine::\n\
-        Keyword:\"<(?:break|continue|define|delete|else|for|if|in|return|while)>\":::Keyword::\n\
+        Keyword:\"<(?:break|continue|define|delete|else|for|if|in|return|typeof|while)>\":::Keyword::\n\
         Braces:\"[{}\\[\\]]\":::Keyword::\n\
         Global Variable:\"\\$[A-Za-z0-9_]+\":::Identifier1::\n\
         String:\"\"\"\":\"\"\"\":\"\\n\":String::\n\
         String Escape Char:\"\\\\(?:.|\\n)\":::Text Escape:String:\n\
         Numeric Const:\"(?<!\\Y)-?[0-9]+>\":::Numeric Const::\n\
-- 
NEdit Develop mailing list - [email protected]
http://www.nedit.org/mailman/listinfo/develop

Reply via email to