bug-gawk
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] Feature: parameters declared in body


From: Kaz Kylheku
Subject: Re: [PATCH] Feature: parameters declared in body
Date: Fri, 08 Apr 2022 15:06:32 -0700
User-agent: Roundcube Webmail/1.4.13

On 2022-04-08 01:20, Manuel Collado wrote:
Perhaps a simpler approach is to introduce a @local directive. So
instead of writing:

function myfun( arg1, arg2,        local1, local2) {
    ...
}

We could write:

function myfun( arg1, arg2 ) {
    @local local1, local2
    ....
}

Yes; this is more or less the obvious approach that is less of
a computer science flunk-job compared to local variables
that can be accidentally initialized by parameters.

Depending on what shape that takes, it may be less usable to me.

If @local name1, name2 ...  is a kind of statement, then that
makes things harder.  Say I have a macro mac which generates this:

   mac(...)  ->   for (__i = 0; __i < __limit; __i++)

The programmer just sticks a statement after this:

   mac(...) { body }

and that is nice. If this is generated:

   @local __i, __limit; for (__i = 0; ...; ...)

then that is not nice any more.  While this "works" in isolation:


   mac(...) { body }

this breaks:

   if (condition)
     mac(...) { body }

because mac(...) is producing two statements, only the first
of which is inside the if.  We then need some opening and closing
macro:

   if (condition)
     mac_begin { body } mac_end

which will hide opening and closing braces.

Now, @local could be a statement prefix instead, similar to for(;;).
Or a statement label in C:

   @local (__i, __limit)
     for (__i = 0; ...; ...)

I'm good with that. Only problem is, it slightly stinks.
It looks like a let construct that binds lexical variables
over just the scope of a statement, but isn't; the variables
are function-wide and visible to subsequent statements.
This stink isn't any worse than locals that can receive
parameters by accident, though.

I think that a good solution would be to have both the
@local name1, name2, ... statement (terminated by a newline
or semicolon), and the @local:NAME expression syntax.

These surface syntaxes share the same underlying mechanism of
allocating locals, where the complexity lies.

I have ideas about supporting locals in the parameter space,
which do not take arguments. Simply a function has to separately
track the total size of the local frame, versus how many
entries in that frame are the parameter are that receives
arguments from the caller.

Gawk issues a warning when extra arguments are passed.
That would stay the same:

   function f(a, b)
   {
      @local c = 100              # local, the complete statement
      @local:d = c + a + b        # local, the expression
      return d
   }

   BEGIN {
print f(1, 2, 3) # warning: awk: line:nnn:function `f' called with more arguments than declared
   }

Here, 3 is evaluated and thrown away; the value does not transmit to c.

The output should be 103.

If only one argument is passed, then b has an undefined value and behaves like
a local: the usual Awk semantics.

---

Thinking longer term now, I can think of ways to have block-scoped locals also,
perhaps under a syntax like this:

   @let (x = 3, y = ..., z, ...) stmt

Here, the variables go out of scope after stmt terminates.

@let variables could be allocated out of the same locals area, as anonymous
entries. Whenever a @let scope terminates (at compile time, I mean)
the compiler would release those locals into a free list. Another @let
could pull them from the free list to re-use those locations.

@local would never re-use locations, always getting fresh spots in the
frame, because locals are function-wide.

So given:

   @let(x) stmt1
   @let(y, z) stmt2

Here, x and y, for instance, could alias to the same location in
the function's frame.

Because, like C, Awk doesn't have lexical closures this is fine;
it cannot be the case that stmt1 captures x in a lexical closure,
which can then be called out of stmt2, where the overlapping allocation
of x and y would then be a problem. This sort of thing is one of the
few saving graces of not implementing closures.

Every @let would have to have emit a prolog of entry code which resets the
variables to undefined values. You don't want this:

  @let(x) x = 3
  @let(y) print y   # prints 3? Yikes; y must be undefined

emitting those instructions would be a waste of time for variables that
are not known to overlap, because the entire frame is already initialized
to undefined values on entry into the function. The compiler can easily
know that a variable is getting a fresh spot: it wasn't allocated from
the free list, and the syntax is not surrounded by any loop:

  function foo()
  {
    @let (x) {  // x doesn't need initing to "undefined"
       for(;;) {
          @let(y) { // y does need initing: it's in a loop
          }
       }
       @let (z) {  // z needs initing: it's reusing the y location
       }
    }
  }

One step at a time, though.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]