Re: [Flightgear-devel] Fun with Nasal (II)

2005-04-23 Thread Arnt Karlsen
On Fri, 22 Apr 2005 15:21:46 -0700, Andy wrote in message 
<[EMAIL PROTECTED]>:

> Anyway, have fun with it.  I just kinda wanted to show it off. :)

..show-off, shouldn't there be a 622'nd here?  ;o)
http://99-bottles-of-beer.net/n.html  

-- 
..med vennlig hilsen = with Kind Regards from Arnt... ;o)
...with a number of polar bear hunters in his ancestry...
  Scenarios always come in sets of three: 
  best case, worst case, and just in case.


___
Flightgear-devel mailing list
Flightgear-devel@flightgear.org
http://mail.flightgear.org/mailman/listinfo/flightgear-devel
2f585eeea02e2c79d7b1d8c4963bae2d


[Flightgear-devel] Fun with Nasal (II)

2005-04-22 Thread Andy Ross
I wrote:
> Finally, there's now a slight esoteric bind() function

Which sounds weird, I know.  But here's an application:

Languages like Perl and Ruby have a really useful syntax (which they
got from the shell) for "interpolating" variables into strings, so
that for simple output you don't have to bother with stuff like
sprintf() or concatenating strings.  You just do something like:

  "My field value is currently: $fieldVal"

Perl can do that because the support is built into the syntax of the
language.  While it would be nice to have an "interpolate()" function
that you could write in the script itself, this generally isn't
possible because the $fieldVal variable is in the calling function,
not the interpolate() function.  Really, that string expression is a
bit of code that would have be executed dynamically when it appears in
the source file.  Wouldn't it be great if we could dynamically
generate code like that using the caller's scope?

Well, you can.  The implementation is attached.  It supports
interpolation of "$variable" expressions, and even 100% arbitrary
Nasal expressions (something even perl can't do) with the use of curly
brackets: "${ want_number ? 12345 : 'not a number!' }".

And it's amazingly easy.  Almost all of the complexity is in the
parser.  The magic required to handle the calling function's variables
and compile the appropriate function is about 10 lines at the top of
the file.  And the result, being a compiled function that
automatically evaluates the right string, is about as efficient as it
possibly can be (modulo the O(N) copying involved in all the ~
operators for long strings).

You can do stuff like interpolate("string to interpolate") for
simplicity, or use interpolater("string to interpolate") to generate a
function that you can call iteratively whenever you want:

  ip = interpolater("the value of i is $i\n");
  for(var i=0; i<10; i+=1) { print(ip()); } # prints 1, 2, ... 10

Anyway, like I said, this isn't completely appropos to stuff people
will want to do in FlightGear (the original idea was to use this as
the engine for the right-hand-side of a regex substitution).  But it
was awfully cool.

For fun, I wrote up a very similar thing that generates a PHP-like web
page template engine and it's similarly tiny.  Nasal has no I/O
library yet, so I can't use this for an actual CGI script, but the
exercise was promising.

Anyway, have fun with it.  I just kinda wanted to show it off. :)

Andy




##
# Generates a callable function object from an interpolation string.
# Takes a function object as the lexical environment in which the
# interpolated expressions should be evaluated, in almost all cases
# (except interpolate() below) this will be the caller's environment
# and can/should be ignored.
#
interpolater = func(s, lexenv=nil) {
var elems = interparse(s);
var syms = {};
var expr = "";
if(lexenv==nil) { var f = caller(); lexenv = bind(func{}, f[0], f[1]); }
for(var i=0; i= 48 and c <= 57)  { return 1; } # 0-9
if(c >= 65 and c <= 90)  { return 1; } # A-Z
if(c >= 97 and c <= 122) { return 1; } # a-z
if(c == 95) { return 1; }  # _
return 0;
}

# Splits a string into a list of literal strings interleaved with nasal
# expressions.  Even indices (0, 2, ...) are always (possibly zero-length)
# strings.  Odd indices are always nasal expressions.  Does not handle
# escaping of $ characters, so you currently need to do ${'$'} if the $ is
# followed by a '{' or a symbol character ([a-zA-Z0-9_]).
interparse = func(s) {
var str0 = 0;
var list = [];
for(var i=0; i 0) {
append(list, substr(s, str0, i-str0));
append(list, substr(s, open+1, close-open-1));
str0 = close+1;
i = close;
}
} else {
for(var j=i+1; j 1) {
append(list, substr(s, str0, i-str0));
append(list, substr(s, i+1, j-i-1));
str0 = j;
i = j-1;
}
}
}
}
if(str0 < size(s)) { append(list, substr(s, str0)); }
return list;
}
___
Flightgear-devel mailing list
Flightgear-devel@flightgear.org
http://mail.flightgear.org/mailman/listinfo/flightgear-devel
2f585eeea02e2c79d7b1d8c4963bae2d

[Flightgear-devel] Fun with Nasal

2005-04-22 Thread Andy Ross
Heh, it's been a fun day.  I woke up with one of those great insights
where you realize that something you thought was difficult just isn't.
In this case, it was the lack of "+=" syntax (and -=, *=, /= and ~= of
course) in Nasal.  They're checked in now.

I also threw in a forindex(i; list) loop syntax, which works exactly
the way the foreach() loop does, except that the variable gets the
index instead of the element of the list.

Finally, there's now a slight esoteric bind() function in the standard
library that allows you to dynamically modify the lexical environment
of a compiled function (really cool, if not necessarily
FlightGear-appropriate example to follow this email).

And all of the above was achieved in just 56 lines of new code.  Like
I said, it's been a fun day.  Things look pretty solid to the test
code I've written.  Let me know if anything breaks.

Andy

___
Flightgear-devel mailing list
Flightgear-devel@flightgear.org
http://mail.flightgear.org/mailman/listinfo/flightgear-devel
2f585eeea02e2c79d7b1d8c4963bae2d