Author Topic: *error* function and local variables  (Read 1768 times)

0 Members and 1 Guest are viewing this topic.

Kotofej

  • Guest
*error* function and local variables
« on: September 28, 2016, 04:08:08 PM »
Hello to all,

I have a question about the *error* function, as the title suggests. The way I tested it, it is enough to define a generic error handling function which is loaded somewhere in the code, and then it is applied to any command which is run. Also, if I need some specific error handling, I define the *error* function as a local variable in the command and so enable specific error treatment. Now, my concrete question is this:

Let's say that my generic *error* function needs to refer to a specific vairabe, perhaps some old environment setting. Does this variable need to be global, or does the *error* function "remember" all local variables from the function which returned an error. I.e., if, at the beginning of a specific function I save some environment variables into a variable, does this variable, which is in the case of an error used by the generic *error* function (defined outside the function body), need to be global or can it be local to that function?

Thanks in advance for any tips.

roy_043

  • Water Moccasin
  • Posts: 1895
  • BricsCAD 18
Re: *error* function and local variables
« Reply #1 on: September 28, 2016, 04:21:26 PM »
It can be local. Obviously you would then always have to use the same variable name.
« Last Edit: September 28, 2016, 04:24:37 PM by roy_043 »

Lee Mac

  • Seagull
  • Posts: 12913
  • London, England
Re: *error* function and local variables
« Reply #2 on: September 28, 2016, 04:29:03 PM »
Since AutoLISP uses dynamic scoping, subfunctions (such as the *error* function) may access the values of variables defined within the scope of the calling function - e.g.:
Code - Auto/Visual Lisp: [Select]
  1. (defun main ( / *error* )
  2.  
  3.     (defun *error* ( m )
  4.         (print sublocalvar)
  5.         (princ)
  6.     )
  7.  
  8.     (setq mainlocalvar "abc")
  9.     (subfun 0)
  10. )
  11.  
  12. (defun subfun ( x / sublocalvar )
  13.     (print mainlocalvar)
  14.     (setq sublocalvar 123)
  15.     (/ 1 x)
  16. )

When (main) is evaluated, the above will yield:
Code - Auto/Visual Lisp: [Select]
  1. _$ (main)
  2.  
  3. "abc"
  4.  
  5. 123

Kotofej

  • Guest
Re: *error* function and local variables
« Reply #3 on: September 28, 2016, 04:44:15 PM »
Great news, many times thanks!

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Re: *error* function and local variables
« Reply #4 on: September 28, 2016, 04:58:36 PM »
To further illustrate variable scoping:

Code: [Select]
(defun *error* ( msg )
    (princ (strcat "\nError: <" msg ">"))
    (if x (progn (princ "\nAn aside, x = ") (princ x)))
    (princ)
)

(defun c:Test1 ( / x )
    (setq x 1)
    (strcat 1) ;;  force an error
    (princ)
)

(defun c:Test2 ( / x)
    (setq x 2)
    (strcat 1) ;;  force an error
    (princ)
)

(setq x 3)

(c:Test1)
Error: <bad argument type: stringp 1>
An aside, x = 1

(c:Test2)
Error: <bad argument type: stringp 1>
An aside, x = 2

(strcat 1)
Error: <bad argument type: stringp 1>
An aside, x = 3


Cheers.
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

Lee Mac

  • Seagull
  • Posts: 12913
  • London, England
Re: *error* function and local variables
« Reply #5 on: September 28, 2016, 05:14:41 PM »
FWIW, I visualise the process in the following way (using MP's code as an example):

With dynamic scoping, each defined symbol has a stack of bindings which I would visualise as a list of values assigned to that symbol. By declaring a symbol local to a function, you are adding (pushing) a new value onto the front of the list (stack), which is then removed (popped) from the list when the function completes evaluation.

In the context of MP's example, this process is as follows:

Evaluated Code                            |  Stack for symbol 'x'
------------------------------------------+----------------------
                                          |  (   )
(setq x 3)                                |  ( 3 )
                                          |  ( 3 )
(c:Test1)                                 |  ( 1 3 )
Error: <bad argument type: stringp 1>     |  ( 1 3 )
An aside, x = 1                           |  ( 1 3 )
                                          |  ( 3 )
(c:Test2)                                 |  ( 2 3 )
Error: <bad argument type: stringp 1>     |  ( 2 3 )
An aside, x = 2                           |  ( 2 3 )
                                          |  ( 3 )
(strcat 1)                                |  ( 3 )
Error: <bad argument type: stringp 1>     |  ( 3 )
An aside, x = 3                           |  ( 3 )


Since the symbol 'x' is not declared local to the function *error*, the symbol accesses the value at the top of the stack when evaluated.

Lee Mac

  • Seagull
  • Posts: 12913
  • London, England
Re: *error* function and local variables
« Reply #6 on: September 28, 2016, 05:41:27 PM »
As a further illustration of this concept, consider the following example:
Code: [Select]
(setq x 0)
(princ "\nAn aside, x = ") (princ x)

(defun c:Test1 ( / x ) ;; x local
    (princ "\nAn aside, x = ") (princ x)
    (setq x 1)
    (princ "\nAn aside, x = ") (princ x)
    (c:Test2)
    (princ "\nAn aside, x = ") (princ x)
    (princ)
)

(defun c:Test2 ( ) ;; x not local
    (princ "\nAn aside, x = ") (princ x)
    (setq x 2)
    (princ "\nAn aside, x = ") (princ x)
    (c:Test3)
    (princ "\nAn aside, x = ") (princ x)
    (princ)
)

(defun c:Test3 ( / x ) ;; x local
    (princ "\nAn aside, x = ") (princ x)
    (setq x 3)
    (princ "\nAn aside, x = ") (princ x)
    (princ)
)

Upon loading the above and evaluating function c:Test1, the stack could be visualised in the following way:


Evaluated Code                                      |  Stack for symbol 'x'
----------------------------------------------------+----------------------
                                                    |  (   )
(setq x 0)                                          |  ( 0 )
(princ "\nAn aside, x = ") (princ x)                |  An aside, x = 0
                                                    |  ( 0 )
(defun c:Test1 ( / x ) ;; x local                   |  ([ ] 0 )   <-- x declared local, new value pushed onto stack
    (princ "\nAn aside, x = ") (princ x)            |  An aside, x = nil
    (setq x 1)                                      |  ( 1 0 )
    (princ "\nAn aside, x = ") (princ x)            |  An aside, x = 1
    (c:Test2) :                                     |  ( 1 0 )
    (defun c:Test2 ( ) ;; x not local               |  ( 1 0 )    <-- x not local, stack remains as is
        (princ "\nAn aside, x = ") (princ x)        |  An aside, x = 1
        (setq x 2)                                  |  ( 2 0 )
        (princ "\nAn aside, x = ") (princ x)        |  An aside, x = 2
        (c:Test3) :                                 |  ( 2 0 )
        (defun c:Test3 ( / x ) ;; x local           |  ([ ] 2 0 ) <-- x declared local, new value pushed onto stack
            (princ "\nAn aside, x = ") (princ x)    |  An aside, x = nil
            (setq x 3)                              |  ( 3 2 0 )
            (princ "\nAn aside, x = ") (princ x)    |  An aside, x = 3
            (princ)                                 |  ( 3 2 0 )
        )                                           |  ( 2 0 )    <-- x local to c:Test3, value popped from stack
        (princ "\nAn aside, x = ") (princ x)        |  An aside, x = 2
        (princ)                                     |  ( 2 0 )
    )                                               |  ( 2 0 )
    (princ "\nAn aside, x = ") (princ x)            |  An aside, x = 2
    (princ)                                         |  ( 2 0 )    <-- x local to c:Test1, value popped from stack
)                                                   |  ( 0 )      <-- global x value


This agrees with the output received when running the code:


An aside, x = 0
_$ (c:Test1)

An aside, x = nil
An aside, x = 1
An aside, x = 1
An aside, x = 2
An aside, x = nil
An aside, x = 3
An aside, x = 2
An aside, x = 2
_$ x
0


JohnK

  • Administrator
  • Seagull
  • Posts: 10626
Re: *error* function and local variables
« Reply #7 on: September 29, 2016, 08:10:29 AM »
I learned the term as "Lexical Scoping" but great thread/posts, guys!
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org