Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - hartrock

#16
Quote from: "ralph.ronnquist"As I see it, the argument for addressing this part of the interpreter is to make it possible to build a generic modularization support framework that avoids everyone having to hard code the modularization into every application.

Introducing $main-args-load-ix is a no-brainer to me, as long as there are no good arguments against introducing it (haven't seen them so far).

It

[*] gives much for building a modularization infrastructure by module(s),
  • [*] is platform agnostic,

  • [*] does not need significant resources,

  • [*] needs only a minimal interpreter code change,

  • [*] does not pollute non-system (non-$) namespace.
  • [/list]

    In summary: there is minimal effort for a big improvement.



    What are the arguments against introducing it?

    Isn't it suited to give exactly this (small) info from inside interpreter, which is needed for a modularization infrastructure, so that all other stuff could be done by module(s) outside interpreter core?


    Quote from: "ralph.ronnquist" I think the support needs from the interpreter includes the two discussed: i.e., knowing the current main-args index, ...
    This would be given by $main-args-load-ix.
    Quote from: "ralph.ronnquist" ... and tracking which files are loading at least while they are loading, ...
    This could well be done by module(s) code (load functions with extended functionality) outside interpreter core.
    Quote from: "ralph.ronnquist" ... but it also will require some way to "stop" the loading/processing part-way into a file.
    Couldn't this be done by module code? Possibly not by stopping of loading something, but by stopping of evaluating it, if some condition has been met.
    Quote from: "ralph.ronnquist"
     Without those, I don't think I would be able to invent a generic modularization framework, and will have to settle for hard-coded modularization special to each application.
    It would be interesting to know, for which usecase $main-args-load-ix together with module(s) code would be insufficient.
    Quote from: "ralph.ronnquist"
    On the other hand, many applications are small enough to not make this an issue. And you can come a long way by compositing applications by copying snippet files to be siblings of the main.
    Putting code in siblings of a script, to be loaded by this script, is not robust, if you don't have a reliable script path for computing its directory.
    #17
    Quote from: "Lutz"Evaluation order in general is always depth first from left to right.



    See also here: https://en.wikipedia.org/wiki/Tree_traversal">https://en.wikipedia.org/wiki/Tree_traversal



    If you want to change that order you would write a fexpr using the define-macro function, which does not evaluate arguments on function entry.

    Thanks for clarification.


    Quote from: "Lutz" There also some - the more complex - built-in functions, which don't follow that order, like for and other flow control flow control functions. Lisp calls these special forms.

    At least for Scheme set! (haven't seen 'set' without '!' there) is mentioned as special form; see e.g. http://www.cs.bham.ac.uk/research/projects/poplog/paradigms_lectures/lecture11.html#setbang">//http://www.cs.bham.ac.uk/research/projects/poplog/paradigms_lectures/lecture11.html#setbang (to backup my remark regarding newLISP set).
    #18
    Quote from: "Lutz"

    > (define (foo (a a)) a)
    (lambda ((a a)) a)
    > (foo)
    123
    > (letn (a a) a)
    123
    > (letex (a a) a)
    123
    > (let (a a) a)
    123

    Thanks for the example, where symbol a refers to different environments: inner new environment and outer one, where it has 123 value (inner val gets outer val assigned here).



    I already have known all of these semantics, but I haven't been sure for 100%, that e.g.:

    (set 'foo 1
         'bar (+ foo 1))
    is and stays the same as:

    (set 'foo 1)
    (set 'bar (+ foo 1))
    At a first look same semantics for both variants may seem to be 'natural'.

    But it had been possible to keep a door open for some kind of optimization: to only use second variant for enforcing sequential set'ing of syms, and reserving first one for allowing set'ing them in parallel by using their values before set expression (which - if used (!) - would make a difference for bar, giving it the value of foo before set'ing this to 1 incremented by 1).

    This door is not open, but IMO it had been a possibility to want to keep it open.
    #19
    It's good to know for sure, that things like:

    (define (do str , (len (length str)))
      ...)
    (set 'str "foo"
         'len (length str))
    are working reliably.

    For bind with evaluation it is left to right, too:

    >
    (define-macro (foo)
      (local (a1 a2 a3)
        (bind (args) true)
        (println "a1: " a1 ", a2: " a2 ", a3: " a3)))

    (lambda-macro ()
     (local (a1 a2 a3)
      (bind (args) true)
      (println "a1: " a1 ", a2: " a2 ", a3: " a3)))
    > (foo (a1 1) (a2 (+ a1 1)) (a3 (+ a2 1)))
    a1: 1, a2: 2, a3: 3
    3
    >

    Is it possible to say - as a rule of thumb for easy memorizing (good not only for beginners) -, that let and letex are the exception of a general left to right evaluation for setting symbols? Or is it more complicated?
    #20
    It could be helpful to mention in the manual (haven't found it), that setting symbols by 'Default variable values' (http://www.newlisp.org/CodePatterns.html#toc-4">//http://www.newlisp.org/CodePatterns.html#toc-4) or set goes from left to right, honoring the values of symbols already set: this is letn semantics (in contrast to let  'using symbol bindings as before the let statement').

    Examples:

    >
    (define (foo (a1 1) (a2 (+ a1 1)) (a3 (+ a2 1)))
      (println "a1: " a1 ", a2: " a2 ", a3: " a3))
    (foo)

    (lambda ((a1 1) (a2 (+ a1 1)) (a3 (+ a2 1))) (println "a1: "
      a1 ", a2: " a2 ", a3: " a3))
    a1: 1, a2: 2, a3: 3
    3
    >
    (set 'a1 1 'a2 (+ a1 1) 'a3 (+ a2 1))
    (println "a1: " a1 ", a2: " a2 ", a3: " a3)

    3
    a1: 1, a2: 2, a3: 3
    3
    >
    If this is not a stable feature, this would be interesting to know, too.
    #21
    Hello Xytroxon,
     
    Quote from: "xytroxon"I should of said that the use of unique in my x-load x-import functions would be done at the C code level, inside of the newLISP load and import functions.

    This would not help, if the name of some repeatedly loaded source continuously changes: e.g. by having some version info (could be a timestamp) as part of the name (so list cannot be condensed by unique)...



    Some feature built-in interpreter source should be (as) robust (as possible) against all imaginable usecases, which it wouldn't be in this example.
    #22
    There has been a trial to get some script info - e.g. for getopts - from outside interpreter core:

    ;;
    ;; script properties
    ;; - could become part of getopts or an own module
    ;;

    ;; *** old basename (now scriptname) too limited ***
    ;;
    ;;;; works for both newLisp and #!/.../newlisp
    ;;(define (basename)
    ;;  (setq execPath (or (main-args 1) (main-args 0)))
    ;;  (last (parse execPath "/")))

    ;;
    ;; A (scriptpath), (scriptname), (scriptargs) solution for skipping newlisp opts
    ;; and their args: could be a helper for getopts.
    ;;
    ;; Should be correct for typical shebang (#!/...) cases, but of interest here
    ;; are newlisp calls like:
    ;;   newlisp -s 4096 -m 10 someScript.lsp
    ;; .
    ;;
    ;; But it has limitations: it is only correkt, if *first* non-option arg of
    ;; newlisp is the script of interest.
    ;; E.g. calling
    ;;   newlisp -m 10 nonExistentFile
    ;; results into
    ;;   > (Logger:scriptname)
    ;;   "nonExistentFile"
    ;; .
    ;; Therefrom it should be allowed and documented how to override; this can be
    ;; done by setting scriptpath_ix explicitely, in case of used heuristics fails.
    ;;
    ;; See file:///usr/share/doc/newlisp/newlisp_manual.html#options:
    ;;
    ;;  -h this help                   -> OK (enters interpreter)
    ;;  -n no init.lsp (must be first) -> OK
    ;;  -x <source> <target> link      -> error: should not been reached by script
    ;;  -v version                     -> OK (enters interpreter)
    ;;  -s <stacksize>                 -> OK
    ;;  -m <max-mem-MB> cell memory    -> OK
    ;;  -e <quoted lisp expression>    -> OK (enters interpreter)
    ;;  -l <path-file> log connections -> OK
    ;;  -L <path-file> log all         -> OK
    ;;  -w <working dir>               -> OK
    ;;  -c no prompts, HTTP            -> OK
    ;;  -C force prompts               -> OK
    ;;  -t <usec-server-timeout>       -> OK
    ;;  -p <port-no>                   -> OK
    ;;  -d <port-no> demon mode        -> OK
    ;;  -http only                     -> OK
    ;;  -6 IPv6 mode                   -> OK
    ;;
    (set'opt_without_arg
     '("-h" ; enters interpreter
       "-n" ; -> skipped
       "-v" ; enters interpreter
       "-c" ; -> skipped
       "-C" ; -> skipped
       "-http" ; -> skipped
       "-6" ; -> skipped
       )
     'opt_with_arg
     '("-s" ; -> skipped
       "-e" ; enters interpreter
       "-m" ; -> skipped
       "-l" ; -> skipped
       "-L" ; -> skipped
       "-w" ; -> skipped
       "-t" ; -> skipped
       "-p" ; -> skipped
       "-d" ; -> skipped
       )
     'opt_with_2_args
     '("-x" ; should not been reached by script
       ;;"-y" ; for testing errorcase...
       ))
    (local (breakFlag skip_next ix execPath)
      (set 'ix 0) ; without any args ix 0 refers to newlisp bin
      (dolist
       (o (1 (main-args)) breakFlag) ; without args, there is no loop here
       (cond
        (skip_next
         (++ ix)
         (set 'skip_next nil)) ; skip once
        ((find o opt_without_arg)
         (++ ix))
        ((find o opt_with_arg)
         (++ ix)
         (set 'skip_next true))
        ((find o opt_with_2_args)
         (throw-error "should not been reached"))
        ("default" ; end loop: first newlisp noopt should be script
         (++ ix) ; needed: loop started with ix of previous element
         (set 'breakFlag true))))
      (set 'scriptpath_ix ix ; 0 or index of first element not being a newlisp option with its args
           'scriptargs_ ((+ 1 scriptpath_ix) (main-args))
           'scriptpath_ (main-args scriptpath_ix)
           'scriptname_ (last (parse scriptpath_ "/"))))
    ;; iface
    (define (scriptpath-ix)
      scriptpath_ix)
    (define (scriptargs) ; good as getopts arg
      scriptargs_)
    (define (scriptpath)
      scriptpath_)
    (define (scriptname) ; Linux (to be extended for other OSes)
      scriptname_)
    (define (shebang?) ; works for Linux; to be extended for other OSes
      (and (= (main-args 0) "/usr/local/bin/newlisp")
           (!= (scriptname) "newlisp")))

    ;;
    ;; .. script properties
    ;;

    But I don't like it:

    [*] it repeats parsing of CLI args of the interpreter;
  • [*] it has serious limitations.
  • [/list]

    From such efforts stems my interest in having $main-args-load-ix (or similar) as base for a better solution.
    #23
    Quote from: "xytroxon"Just do a unique list operation after each file path is added.
    This is a good example for what can well be done from outside the interpreter (you could check for loading the same lib with same path twice, too).

    But in Lutz' example such a solution (with keeping list small by unique added) just wouldn't have any benefits, but could fail: e.g. by loading a versioned source file - including version info in its name - again and again... -> Boom!



    But for the other $main-args-load-ix part of the patch I don't see such risks, and I don't see how to get respective info reliably from outside the interpreter...
    #24
    Quote from: "Lutz"I understand the occasional necessity to remember the names of files loaded, but this should not be built-in to the language. What happens with applications which reload files over and over again to refresh or change a set of data or function definitions? This would make the the stack of filenames bigger and bigger.
    Good point! I haven't thought about this problem. Seems to be a killer argument for the simple variant of $load-list in my patch: I'm agreeing that $load-list this way is a bad idea.

    But this argument does not apply to $main-args-load-ix (see below).
    Quote from: "Lutz"One could built some functionality with little code in .init.lsp, e.g. by changing the definition of module or introduce a new loadx. This would also be easier to adapt to specific requirements.
    This is true.

    BTW: for loading own sources I have built some infrastructure with similar commands and a loaded list to have automated loading of libs just once.




  • [*] a good name for logging (and this is not the name of a source, which has been loaded last from whatever other source);

  • [*] a dir as a good place for relative positioning of files/dirs as a lightweighted alternative to the official NEWLISPDIR;

  • [*] a position being a good start for reliably parsing script arguments.
  • [/list]
    You probably have seen Ralph's sophisticated solution by looking into the procfs to get a reliable solution for specific platform(s), whose working depends on platform, and is far away from simple. The other variant he has mentioned is to
    Quote from: "Ralph"... install a newlisptar binary (embedding lsptar.lsp) at the production places ...
    . If I've understood correctly - don't have experiences here - this would lead to duplicate the newlisp bin for each app/script packaged this way. OK, this is a solution having its usecases and merits, but isn't it the sense of a script interpreter to share it between all scripts? (If possible, of course.)
    Quote from: "Lutz"Ps: How about adding something to the getopts.lsp module?
    This is one usecase for $main-args-load-ix: to know, where we are in the $main-args line, and to have a scriptname.



    I really don't know, how to get the info, which $main-args-load-ix would give, reliably from outside the interpreter (and if possible at all, it seems to be quite complicated and/or having other drawbacks); but from inside the interpreter it's easy and just 5 (!) lines (without any resource risk).

    If I would know a good way of getting this info from outside the interpreter, I'd just use it.



    I think, I'm surely not the only one having this problem.

    @All: what do you think?



    @Lutz: I would be happy, if you could seriously reconsider this part of the patch.
    #25
    Here is a patch providing system variables $main-args-load-ix and $load-list.

    These system variables provide system information, which is difficult to compute outside the interpreter code, but easy from inside, where all needed information is available.



    Patch against newlisp-10.6.4.tgz  2015-09-21 16:07  1.6M:

    sr@free:~/newLISP_Git/mirror$ git diff -p inprogress HEAD
    diff --git a/mirror/newlisp.c b/mirror/newlisp.c
    index 262339d..0190bc5 100644
    --- a/mirror/newlisp.c
    +++ b/mirror/newlisp.c
    @@ -192,6 +192,8 @@ SYMBOL * atSymbol;
     SYMBOL * currentFunc;
     SYMBOL * argsSymbol;
     SYMBOL * mainArgsSymbol;
    +SYMBOL * mainArgsLoadIxSymbol;
    +SYMBOL * loadListSymbol;
     SYMBOL * listIdxSymbol;
     SYMBOL * itSymbol;
     SYMBOL * sysxSymbol;
    @@ -894,6 +896,7 @@ for(idx = 1; idx < argc; idx++)
             exit(0);
             }
         
    +    mainArgsLoadIxSymbol->contents = (UINT)stuffInteger(idx);
         loadFile(argv[idx], 0, 0, mainContext);
         }
     
    @@ -1428,6 +1431,8 @@ questionSymbol = translateCreateSymbol("?", CELL_NIL, mainContext, TRUE);
     atSymbol = translateCreateSymbol("@", CELL_NIL, mainContext, TRUE);
     argsSymbol = translateCreateSymbol("$args", CELL_NIL, mainContext, TRUE);
     mainArgsSymbol = translateCreateSymbol("$main-args", CELL_NIL, mainContext, TRUE);
    +mainArgsLoadIxSymbol = translateCreateSymbol("$main-args-load-ix", CELL_NIL, mainContext, TRUE);
    +loadListSymbol = translateCreateSymbol("$load-list", CELL_NIL, mainContext, TRUE);
     listIdxSymbol = translateCreateSymbol("$idx", CELL_NIL, mainContext, TRUE);
     itSymbol = translateCreateSymbol("$it", CELL_NIL, mainContext, TRUE);
     countSymbol = translateCreateSymbol("$count", CELL_NIL, mainContext, TRUE);
    @@ -1454,6 +1459,8 @@ questionSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
     atSymbol->flags |=  SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
     argsSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
     mainArgsSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
    +mainArgsLoadIxSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
    +loadListSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
     listIdxSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
     itSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
     countSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
    @@ -1465,6 +1472,8 @@ argsSymbol->contents = (UINT)getCell(CELL_EXPRESSION);
     objSymbol.contents = (UINT)nilCell;
     objSymbol.context = mainContext;
     objCell = nilCell;
    +mainArgsLoadIxSymbol->contents = (UINT)nilCell;
    +loadListSymbol->contents = (UINT)getCell(CELL_EXPRESSION);
     
     /* init signal handlers */
     for(i = 0; i < 32; i++)
    @@ -3291,6 +3300,7 @@ if(my_strnicmp(fileName, "http://", 7) == 0)
         pushResult(result);
         if(memcmp((char *)result->contents, "ERR:", 4) == 0)
             return(errorProcExt2(ERR_ACCESSING_FILE, stuffString((char *)result->contents)));
    +    addList((CELL*)loadListSymbol->contents, stuffString(fileName));
         result = copyCell(sysEvalString((char *)result->contents, context, nilCell, EVAL_STRING));
         currentContext = contextSave;
         return(result);
    @@ -3314,6 +3324,7 @@ for(i = 0; i<recursionCount; i++) printf("  ");
     printf("load: %sn", fileName);
     #endif
     
    +addList((CELL*)loadListSymbol->contents, stuffString(fileName));
     result = evaluateStream(&stream, 0, TRUE);
     currentContext = contextSave;




    [*] $main-args-load-ix gives index into $main-args of last file/URL tried to be load by main() loop of the interpreter.

    During evaluation of a script this need not be an index of its path, because it may itself been loaded indirectly by another script given as command line argument.

    But exactly the index of currently evaluated $main-args script is suited for reliable and easy determination of:

    [*] scriptname at command line arg: good for output of user info (e.g. errors and logging);
  • [*] position of script arguments: after that parsing of script arguments can start;

  • [*] fixpoint for relatively located dirs/files: this is good for having an alternative to NEWLISPDIR in file locations related to a user.
  • [/list]

  • [*] $load-list is a push-back list of all files/URLs already loaded by interpreter main() loop or load or module from newLISP; last element is last loaded script. This may be another script/source as given at command line, because it could have performed a load itself, which would have led to one or more additional elements in this list.

    This is good for inspecting,

    [*] what have been loaded at all, and

  • [*] if there have been any duplicate loads of script/code.
  • [/list]
    Note: code currently being evaluated may stay in any of these elements (after a return from calling load by some script, evaluation continues there).
  • [*] Good properties of both patches:

    [*] they should work at all supported platforms;

  • [*] only minimal code changes;

  • [*] no pollution of 'normal' namespace by using symbols with '$' prefix;

  • [*] where are the disadvantages?
  • [/list][/list]



    sr@free:~/newLISP/Examples$ newlisp
    newLISP v.10.6.4 64-bit on Linux IPv4/6 UTF-8 libffi, options: newlisp -h

    > $main-args $main-args-load-ix $load-list
    ("newlisp")
    nil
    ()
    >
    sr@free:~/newLISP/Examples$ newlisp notExisting
    newLISP v.10.6.4 64-bit on Linux IPv4/6 UTF-8 libffi, options: newlisp -h

    > $main-args $main-args-load-ix $load-list
    ("newlisp" "notExisting")
    1
    ()
    >
    sr@free:~/newLISP/Examples$ touch existing.lsp # empty
    sr@free:~/newLISP/Examples$ newlisp -s 1000 existing.lsp --foo
    newLISP v.10.6.4 64-bit on Linux IPv4/6 UTF-8 libffi, options: newlisp -h

    > $main-args $main-args-load-ix $load-list
    ("newlisp" "-s" "1000" "existing.lsp" "--foo")
    4
    ("existing.lsp")
    >
    sr@free:~/newLISP/Examples$ cat printThese.lsp
    #!/usr/bin/env newlisp
    (println "$main-args: " $main-args)
    (println "$main-args-load-ix: " $main-args-load-ix)
    (println "$load-list: " $load-list)
    sr@free:~/newLISP/Examples$ newlisp -s 1000 printThese.lsp --foo
    $main-args: ("newlisp" "-s" "1000" "printThese.lsp" "--foo")
    $main-args-load-ix: 3
    $load-list: ("printThese.lsp")
    newLISP v.10.6.4 64-bit on Linux IPv4/6 UTF-8 libffi, options: newlisp -h

    > $main-args $main-args-load-ix $load-list
    ("newlisp" "-s" "1000" "printThese.lsp" "--foo")
    4
    ("printThese.lsp")
    >

    Calling newlisp loading some .init.lsp at startup, which is loading some other source itself:

    sr@free:~/newLISP/Examples$ newlisp
    newLISP v.10.6.4 64-bit on Linux IPv4/6 UTF-8 libffi, options: newlisp -h

    > $main-args $main-args-load-ix $load-list
    ("newlisp")
    nil
    ("/home/sr/.init.lsp" "/home/sr/newLISP/modules/Init.lsp"
     "/home/sr/newLISP/modules/FOOPReference.lsp" "/home/sr/newLISP/modules/Util.lsp"
     "/home/sr/newLISP/modules/Logger.lsp" "/home/sr/newLISP/modules/LoggerTweakable.lsp"
     "/home/sr/newLISP/modules/Libs.lsp")
    >
    sr@free:~/newLISP/Examples$ newlisp -s 1000 printThese.lsp --foo
    $main-args: ("newlisp" "-s" "1000" "printThese.lsp" "--foo")
    $main-args-load-ix: 3
    $load-list: ("/home/sr/.init.lsp" "/home/sr/newLISP/modules/Init.lsp"
     "/home/sr/newLISP/modules/FOOPReference.lsp" "/home/sr/newLISP/modules/Util.lsp"
     "/home/sr/newLISP/modules/Logger.lsp" "/home/sr/newLISP/modules/LoggerTweakable.lsp"
     "/home/sr/newLISP/modules/Libs.lsp" "printThese.lsp")
    newLISP v.10.6.4 64-bit on Linux IPv4/6 UTF-8 libffi, options: newlisp -h

    > $main-args $main-args-load-ix $load-list
    ("newlisp" "-s" "1000" "printThese.lsp" "--foo")
    4
    ("/home/sr/.init.lsp" "/home/sr/newLISP/modules/Init.lsp"
     "/home/sr/newLISP/modules/FOOPReference.lsp" "/home/sr/newLISP/modules/Util.lsp"
     "/home/sr/newLISP/modules/Logger.lsp" "/home/sr/newLISP/modules/LoggerTweakable.lsp"
     "/home/sr/newLISP/modules/Libs.lsp" "printThese.lsp")
    >



    [*] First there has been a change of (load) according the suggestion from Ralph (see http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=4756#p23433">//http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=4756#p23433), so that (load) (without arguments) returns a copy of $load-list. But this has problems, due to making the semantics of load more complicated (in addition the naming of the load func is questionable for this semantics), so it has been discarded.

    One example: someone could have the idea to try (load int-ix) in analogue to (main-args int-ix), which would not work.
  • [*] After creating these patches I think, that $main-args-load-ix is the system information, which is more important for easing the life of developers than $load-list; but the latter is helpful, too.
  • [/list]
    #26
    Quote from: "hartrock"The interpreter (and its creator ;-) ) has all the knowledge needed; so this shouldn't be very hard:

    Perhaps it's harder than I'm thinking so far, because the process of loading and interpreting files may be more complicated as I know. It may be difficult to find the right place for building suggested file load list: at least for me it would need much time to understand the inner workings of the interpreter better, before trying to do such a thing.

    It may be worth to take the effort (btw: it's interesting to understand the inner workings of the newLISP interpreter better, which is a nice peace of software), if there would be a chance to get such an interpreter extension accepted by Lutz...

    I'm more interested in developing solutions than in writing posts about not existing ones: but such a one has no chance without being accepted as part of interpreter core.
    Quote from: "hartrock" it feels wrong to create hackish workarounds, due to not having important info about script properties, which could be provided by simple interpreter queries. (***)

    I have no idea, how to do this reasonably as a module (which could be located at the official place inside NEWLISPDIR/modules/), to avoid changing interpreter core: any idea? Possibly I'm missing opportunities here.


    Quote from: "hartrock"(***) Meta: This is my technical opinion, amongst others arising from praxis:
    I have started locating parts of software related to NEWLISPDIR, but later changed this to have user-local files/directories. Especially for parts not meant to be reusable by others - e.g. by not having the time to prepare them for this purpose - this seems to be the wrong approach to me: it would solve the 'find a directory for loading parts of the software' issue, but it has drawbacks:

    [*] admin rights needed,
  • [*] namespace/overwrite issues,

  • [*] experimental parts of software at 'official' place,

  • [*] all of the former make it more difficult for users not to fear to just trying out some software.
  • [/list]

    If there would be some policy about

    [*] how to name contexts with some kind of prefix, and
  • [*] how to name subdirs of NEWLISPDIR/modules/,
  • [/list]
    to avoid name clashes with other software, this could be a way to go.

    But there is no such policy, therefrom my interest in having reliable user-local places for installation (which even with such a policy would have their merits).
    Quote from: "hartrock"
     on the other side I'm knowing very well, that even small changes can be impossible, if there is no time to do them.

    And there is more motivation for taken the time needed for some contribution, if there is a chance to get such a contribution accepted...



    PS: Currently my solution - see first post in this thread - continues to look like a pragmatic one in current situation: but it has it's drawbacks - so I don't really like it - like

    [*] not being elegant,
  • [*] having maintenance risks/efforts, and

  • [*] being dangerous for making scripts taken filename arguments (which could be a file named like the script itself, too).
  • [/list]
    #27
    First thanks to all for your input!


    Quote from: "ralph.ronnquist"I understood hartrock's problem to be how to know the name of the script file currently being evaluated, without hard-coding its name into the script. ...
    This is one usecase: e.g. for output of scriptname by logging and getopts.
    Quote from: "ralph.ronnquist"For example, hartrock initially refers to "the directory of the script", ...
    This is another usecase: being able to load code from other files located relative to executed script.

    Using NEWLISPDIR by storing code relative to it, is not an alternative for code not thought as suited or mature enough to stand for itself (*) as published module. Moreover it needs admin rights and has the risk of overwriting something, if something has to be installed in this area as a precondition for getting some software run (**).
    Quote from: "ralph.ronnquist"My suggestion, which (likely) is Linux only, tries to exploit procfs to find the last opened file descriptor (which hangs around a while in procfs even after being closed in the program), and then pick up the file name from there. This method, where it works, locates the canonical path name for that file (i.e., resolving any links), and assumes this to be the name of the script file currently being evaluated, whether mentioned on the command line or loaded recursively or manually.
    I think this can be a good solution, if you are limited to Linux-like OSes. But changing it to a working one for other systems - if someone wants to use some software at another kind of OS - is not trivial (it's Linux expert code (and I cannot say, if and what are the restrictions between different Linux or Linux like OSes here)).
    Quote from: "ralph.ronnquist"
    xytroxon's first approach chooses the second command line argument, and clips that path name. This is a simple and straight-forward method that usually works, except of course if the script is not the second argument.
    This is a show stopper for newlisp calls of scripts with newlisp CLI arguments before them and/or calling multiple scripts.
    Quote from: "ralph.ronnquist"
    And xytroxon's second approach provides the current working directory, which also is good if you are sure that the script file resides there.
    Such an assumption may or may not hold: which is bad for having flexibility and robustness at the same time.



    My current usecase is https://github.com/hartrock/Inspector">//https://github.com/hartrock/Inspector:

    - installation of this app should be as easy as possible,

    - to get it run should be as easy and robust as possible, without the need to write complicated explanations about

    - how to adapt paths in scripts for other configurations, and/or

    - restrictions about how to call start scripts.


    Quote from: "ralph.ronnquist"
    Ideally, I think, the system load function should be modified so as to maintain a push-down list of the files being loaded while they are loaded, and there should be a function (maybe even just load without arguments) that returns that list. Through this, generic code could be written to work out where "sibling" files are for the files being loaded while they are loaded.
    I like this idea!

    In addition to the possibility to write platform independent code, this would even allow to check for duplicated file loads, which usually are not wanted.



    Another point at my wishlist is to have additional info about which (main-args) index executed script has (interpreter knows, script not): this is good for a robust getopts (robustness is very important for a good user (and developer, too!) experience, so I'm stressing this point here).


    Quote from: "ralph.ronnquist"But still, the benefit of having this might not outweigh the effort of doing that modification?

    The interpreter (and its creator ;-) ) has all the knowledge needed; so this shouldn't be very hard: it feels wrong to create hackish workarounds, due to not having important info about script properties, which could be provided by simple interpreter queries. (***)



    Footnotes:

    (*) There may be strong interdependencies between parts of code, nevertheless located in different files. It's not easy to refactor this to modules, standing each for itself.

    (**) This may users let hesitate to just try out some unknown software.

    (***) Meta: This is my technical opinion, amongst others arising from praxis: on the other side I'm knowing very well, that even small changes can be impossible, if there is no time to do them.
    #28
    Inspector v0.3 provides a ping-pong mode for automated updating of symbols in its browser GUI.

    Together with the feature of user created folders of lambda/macro/list symbols at top of tree control, this allows to view changes of a selection of interesting symbols in time.



    In ping-pong mode the following happens:

    [*] Inspector GUI loads symbols in its user created top folders from Inspector server and

       updates their current evaluations;
  • [*] thereafter it releaes the server by sending a command for finishing its

       webservice;

  • [*] server process

       - is free to do other stuff for a while ...

       - ... until it gives back control to GUI by calling [code](Inspector] again (in the meantime Inspector GUI polls to wait for this to happen).
  • [/list]

    In a demo this happens repeatedly by a simple counting loop at server side (could also do other things, of course). After starting the server process, there are instructions in its terminal about how to proceed in GUI; see https://github.com/hartrock/Inspector#ping-pong-mode-demo">//https://github.com/hartrock/Inspector#ping-pong-mode-demo.



    So far no button has been needed ...



    Note:

    Because all of this works by interaction between Inspector's GUI (running as Javascript in the browser) and its (newLISP) server process, all of its code has to be loaded, for being able to use this mechanism for inspecting your own running software.
    #29
    Some effort is needed to detect the dir of a started script; currently there is (it should work with or without shebang start):
     ;; script dir detection
      (set 'Inspector:scriptname "startIt.lsp"
           'Inspector:dir ; be robust against CLI args not containing scriptname
           (0 (- (length Inspector:scriptname)) ; only leave dirpath
              (first (filter (fn (a) (find Inspector:scriptname a))
                             (main-args)))))
      (if (null? Inspector:dir)
          (set 'Inspector:dir ".")) ; cwd

    This is not perfect:

    [*] it's easy to forget changing the scriptname inside, if it will be changed outside (by renaming it);
  • [*] it may fail, if there is an arg before containing script name (e.g. a script loaded before);

  • [*] code is quite long.
  • [/list]

    Any ideas for improvement?
    #30
    Inspector v0.2 at Github: https://github.com/hartrock/Inspector#inspector">//https://github.com/hartrock/Inspector#inspector.



    Now it is possible to explore another newLISP's symbols state by snapshot'ing it, and viewing the result in the browser after starting Inspector.



    Snapshot'ing only needs minimal code:

    #!/usr/bin/env newlisp
    (load "modules/Util.lsp")
    (load "modules/Introspection.lsp")

    ;; make snapshot
    (set 'filepath "/tmp/snapshot.json")
    (define (make-snapshot filepath)
      (write-file filepath
                  (Introspection:symbols-to-JSON (Util:symbols-all))))

    (make-snapshot filepath)
    (println "You may look onto snapshot's symbols byn"
             "  http:localhost:8080/symbols.html?file="
             filepath "n.")

    Result can be viewn by http://localhost:8080/symbols.html?file=/tmp/snapshot.json">//http://localhost:8080/symbols.html?file=/tmp/snapshot.json then: see

    https://github.com/hartrock/Inspector/blob/master/README.md#viewing-snapshoted-symbols-of-another-newlisp-process">//https://github.com/hartrock/Inspector/blob/master/README.md#viewing-snapshoted-symbols-of-another-newlisp-process for a screenshot of viewing context folders of a minimal system; and https://github.com/hartrock/Inspector/blob/master/README.md#snapshoting-and-viewing-symbols-of-another-newlisp-process">//https://github.com/hartrock/Inspector/blob/master/README.md#snapshoting-and-viewing-symbols-of-another-newlisp-process for more info.