• Re: Syntactic Sugar (Was: The Art of Unix Programming - Case Study:awk)

    From Kaz Kylheku@480-992-1380@kylheku.com to comp.lang.awk on Fri Feb 11 17:38:47 2022
    From Newsgroup: comp.lang.awk

    On 2022-02-11, Kenny McCormack <gazelle@shell.xmission.com> wrote:
    In article <20220209235134.861@kylheku.com>,
    Kaz Kylheku <480-992-1380@kylheku.com> wrote:
    ...
    Sure, but you don't get to use pattern/action pairs on the result.

    But that's largely just syntactic sugar for a glorified case statement.

    Instead of

    /abc/ { ... }
    $2 > $3 { ... }

    you have to write

    if (/abc/) { ... }
    if ($2 > $3) { ... }

    kind of thing.

    Of course, it can be (and often has been) argued that everything in any programming language is just "syntactic sugar"

    Not by me.

    for the underling machine
    code.

    When I say "syntactic sugar", I'm referring to a light transformation to improve the taste. This is justified for the above example because the
    "machine code" differs from the "HLL" counterpart only in that
    "if (...)" has been wrapped around the test expressions.

    I have a lot of experience implementing complicated language features
    as code transformations, which I wouldn't call syntactic sugar.

    Personally, I tend to agree with Ben here, that not being able to use the "automatic input loop" (aka, the "pattern/action" facility for which AWK is

    Ben didn't need a loop in the specific situation because he just wanted
    to take a field and further split it as if it were a record.

    You'd benefit from a loop if you wanted to take a string and treat it
    as an entire file, separated into records and then fields.

    I agree that having to implement that loop around your conditional
    statements would start to get more than a little inconvenient.

    I made a Lisp version of Awk as a macro. You can use it anywhere you can
    use an expression, and cleanly nest it with itself.

    E.g. we can use it to make a function which returns a list of objects:

    This is the TXR Lisp interactive listener of TXR 273.
    Quit with :quit or Ctrl-D on an empty line. Ctrl-X ? for cheatsheet.
    This area is under 24 hour TTY surveillance.
    1> (defun wrapped-awk (strings)
    (build ;; establish sccope for procedural list construction
    ;; built-up list is returned when scope terminates
    (awk (:inputs strings)
    (:set fs ":") ;; field separator is colon (:)
    (t (fconv - i i)) ;; convert second and third fields to integer
    (#/plus/ (add (+ [f 1] [f 2])))
    (#/minus/ (add (- [f 1] [f 2]))))))
    wrapped-awk

    Now we just pass a list of strings into this function: they become
    records:

    2> (wrapped-awk '("plus:1:2" "plus:3:4" "minus:5:5"))
    (3 7 0)

    The list of sums and differences is returned.

    Now, let's use the wrapped-awk function inside another awk loop;
    this time we will feed it input from the TTY interactively:

    3> (awk (t (prn (wrapped-awk f))))
    plus:1:2 plus:3:4 minus:5:5
    3 7 0
    minus:15:20
    -5

    "If the 't' condition is true, which is always, then print the list
    obtained by passing the delimited fields f into wrapped-awk."

    And I want to be able to use the pattern/action facility on piped-in input (and also output - see below), I have written an extension library for GAWK that enables that. That is, I can do:

    @load "pipeline"
    BEGIN { pipeline("in","Some Shell Command Here") }
    /foo/ { bar...}

    Let's pipe the above into a character string.

    4> (with-out-string-stream (*stdout*)
    (awk (t (prn (wrapped-awk f)))))
    plus:10:20 minus:13:7
    [Ctrl-D][Enter]
    "30 6\n"

    with-out-string-stream creates a scope in which it binds a variable
    to a string stream. Everything sent to that stream is appended to
    a string, and that string is returned when with-out-string-stream
    terminates.

    If we choose *stdout* for the variable name, then standard output
    is temporary bound to this string stream. Everything that would go
    to standard output goes into the string stream.

    Already in the 1980s, people were able to use FFI to define bindings
    to foreign libraries, and not have to write any glue code that had to be compiled.

    Here is my "extension lib" for calling size_t wcslen(const wchar_t *str):

    5> (with-dyn-lib nil
    (deffi wcslen "wcslen" size-t (wstr)))
    wcslen
    (wcslen "abcd")
    4

    How about the structure-returning function

    ldiv_t lldiv(long numerator, long long denominator);

    Define the foreign structure:

    6> (deffi-struct lldiv
    (quot longlong)
    (rem longlong))
    #<ffi-type (struct lldiv (quot longlong) (rem longlong))>

    Define the foreign function binding:

    7> (with-dyn-lib nil
    (deffi lldiv "lldiv" lldiv (longlong longlong)))
    lldiv

    Test:

    8> (lldiv 127 15)
    #S(lldiv quot 8 rem 7)

    This shit is unsafe, since we are calling C:

    9> (lldiv 127 0)
    Floating point exception (core dumped)

    We could catch that signal. Let's try it again:

    1> (deffi-struct lldiv (quot longlong) (rem longlong))
    #<ffi-type (struct lldiv (quot longlong) (rem longlong))>
    2> (with-dyn-lib nil
    (deffi lldiv "lldiv" lldiv (longlong longlong)))
    lldiv

    Now install signal handler for SIGFPE, binding it to a
    lambda function which throws the yikes symbol as an exception:

    3> (set-sig-handler sig-fpe (lambda (signal async-p) (throw 'yikes)))
    t

    Now:

    4> (lldiv 127 15)
    #S(lldiv quot 8 rem 7)
    5> (lldiv 127 0)
    ** yikes exception, args: nil
    6>
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    --- Synchronet 3.19b-Linux NewsLink 1.113
  • From Ben Bacarisse@ben.usenet@bsb.me.uk to comp.lang.awk on Fri Feb 11 19:48:27 2022
    From Newsgroup: comp.lang.awk

    Kaz Kylheku <480-992-1380@kylheku.com> writes:

    Ben didn't need a loop in the specific situation because he just wanted
    to take a field and further split it as if it were a record.

    I see this sketch of mine now has a life of it's own. I wrote '...' as
    the second AWK source, so how anyone can know that I did not need a loop
    I can't imagine.

    The first AWK picked out lines of interest (they matched dates and AWK
    would allow me to use date functions in the picking out though I had, on
    this occasion, no need for that). The second AWK matched logged 'add'
    and 'remove' actions that the second command counted -- anything added
    more than it was removed remained for the END action to print.

    Of course this could be one AWK command. What I don't get is why such a
    simple use should raise so many people's hackles. It took about 30
    seconds to write, and it did the job.
    --
    Ben.
    --- Synchronet 3.19b-Linux NewsLink 1.113