1. foreach routine

Hi...

Where are we at on this issue? I searched/read the forum archives on this issue
last night. Any conclusions / plans? I ask because I can't believe that it
wasn't included (and some others) right from the onset. In the "Euphoria
Reference Manual" Part I-Section 2, it states:

"Euphoria data structures are almost infinitely flexible." Further down, "Not
only can a Euphoria program easily represent all conventional data structures
but you can create very useful, flexible structures that would be extremely
hard to declare in a conventional language." That may be true, but what good
is all that if you can't get at / manipulate them easily? IMHO, sequences are
*so huge* in Euphoria that we should scrap some of the functions that are
supported but perhaps rarely used, in favor of beefing up "sequence support".

In the meantime, anybody ever come across a "foreach" C function that could
be called from Euphoria? Later...
--
duke

new topic     » topic index » view message » categorize

2. Re: foreach routine

FMI, what is the benefit of a foreach statement vs. for x = y to z?

for i=1 to length(some_list) do
  call_func(rtn, {some_list[i], d})
end for

for each list in some_list do
  call_func(rtn, {list, d})
end for

Would there be a performance benefit on the backend?

new topic     » goto parent     » topic index » view message » categorize

3. Re: foreach routine

duke normandin wrote:
> 
> Hi...
> 
> Where are we at on this issue? 

Here is a first cut at an interim solution...
-- foreach.e

-- Extended version. Covers the full functionality.

--/func every_item(pTheList, pRID, pData, pStart, pEnd, pSpan)
--/desc Process each specified item in the list
--/ret SEQUENCE: If no errors, a 4-element sequence;
--               [1] = 0 (zero)
--               [2] = 0 (if pRID sees each item)
--                   = integer (whatever pRID returned to end the processing)
--               [3] = The index of the last item processed.
--               [4] = The (possibly updated) list.
--               If errors, a 2-element sequence;
--               [1] = Error code:
--                      1 => The supplied low index is < 1
--                      2 => The supplied low index is > list length
--                      3 => The supplied high index is < 1
--                      4 => The supplied high index is > list length
--                      5 => The supplied routine id is invalid
--               [2] = The parameter in error.
--
-- This function calls the routine id /i(pRID) for each of the items
-- requested in the list /i(pTheList). The range and direction is
-- specified by /i(pStart) and /i(pEnd). /i(pStart) is the index
-- of the first item to be processed and /i(pEnd) is the index
-- of the last item to be processed. If /i(pStart) is higher than
-- /i(pEnd) then the items are processed in right-to-left order
-- otherwise they are processed in left-to-right order. /n
--
-- The /i(pSpan) parameter specifies the 'gap' between successive
-- items to be processed. To process each item use 1 as the span, to
-- process every second item use 2, etc... /n
--
-- The /i(pRID) parameter is a routine id of a caller defined function
-- which is called for each item processed. It is passed three parameters:
--     [1] => The index of the item being processed.
--     [2] => The value of the item being processed.
--     [3] => The /i(pData) parameter. This can be any object.
-- The routine must return either a single integer or a one-element
-- sequence. If it returns a 0 (zero) then next item is processed. If
-- it returns a non-zero integer, the processing of items is stopped.
-- If it returns a sequence, the first element is used to replace the
-- item just processed in the list.
--
-- Example:
--/code
-- sequence res
-- function showit(integer pIdx, object pItem, object pExtra)
--     printf(1, "%d: %s\n", {pIdx, pItem})
--     if pItem = pExtra then return 1 end if
--     return {pItem - ('A' - 'a')}
-- end function
--
-- res = every_item("ABCDEF", routine_id("showit"), 'D', 2, 5, 1)
-- printf(1, "%d %d %d %s\n", res)
--/endcode

----------------------------------------------------------
global function every_item(sequence pTheList, integer pRID, object pData,
                           integer pStart, integer pEnd, integer pSpan)
----------------------------------------------------------

    integer lDir
    object lRes
    integer lLastI
    integer lLow
    integer lHigh

    if length(pTheList) = 0 then
        return {0,0,0, pTheList}
    end if

    if pStart > pEnd then
        lDir  = -1
        lLow  = pEnd
        lHigh = pStart
    else
        lDir  = 1
        lHigh = pEnd
        lLow  = pStart
    end if

    if lLow < 1 then
        return {1, lLow}
    end if

    if lLow > length(pTheList) then
        return {2, lLow}
    end if

    if lHigh < 1 then
        return {3, lHigh}
    end if

    if lHigh > length(pTheList) then
        return {4, lHigh}
    end if

    if pRID < 0 then
        return {5, pRID}
    end if

    lRes = 0
    for i = pStart to pEnd by lDir * pSpan do
        lLastI = i
        lRes = call_func(pRID, {i, pTheList[i], pData})
        if integer(lRes) then
            if lRes != 0 then
                exit
            end if
        else
            pTheList[i] = lRes[1]
        end if
    end for

    return {0, lRes, lLastI, pTheList}

end function

--/func foreach(pTheList, pRID)
--/desc Process every item in the list, from left to right.
--/ret SEQUENCE: If no errors, a 4-element sequence;
--               [1] = 0 (zero)
--               [2] = 0 (if pRID sees each item)
--                   = integer (whatever pRID returned to end the processing)
--               [3] = The index of the last item processed.
--               [4] = The (possibly updated) list.
--               If errors, a 2-element sequence;
--               [1] = 5 (The supplied routine id is invalid)
--               [2] = The pRID value.
--
-- This function calls the routine id /i(pRID) for each of the items
-- in the list /i(pTheList). They are processed in left-to-right order. /n
--
-- The /i(pRID) parameter is a routine id of a caller defined function
-- which is called for each item processed. It is passed three parameters:
--     [1] => The index of the item being processed.
--     [2] => The value of the item being processed.
--     [3] => 0
-- The routine must return either a single integer or a one-element
-- sequence. If it returns a 0 (zero) then next item is processed. If
-- it returns a non-zero integer, the processing of items is stopped.
-- If it returns a sequence, the first element is used to replace the
-- item just processed in the list.
--
-- Example:
--/code
-- sequence res
-- function showit(integer pIdx, object pItem, object pExtra)
--     printf(1, "%d: %s\n", {pIdx, pItem})
--     if pItem = 'D' then return 1 end if
--     return {pItem - ('A' - 'a')}
-- end function
--
-- res = foreach("ABCDEF", routine_id("showit"))
-- printf(1, "%d %d %d %s\n", res)
--/endcode

----------------------------------------------------------
global function foreach(sequence pTheList, integer pRID)
----------------------------------------------------------

    return every_item(pTheList, pRID, 0, 1, length(pTheList), 1)

end function

--/func foreach_reverse(pTheList, pRID)
--/desc Process every item in the list, from right to left.
--/ret SEQUENCE: If no errors, a 4-element sequence;
--               [1] = 0 (zero)
--               [2] = 0 (if pRID sees each item)
--                   = integer (whatever pRID returned to end the processing)
--               [3] = The index of the last item processed.
--               [4] = The (possibly updated) list.
--               If errors, a 2-element sequence;
--               [1] = 5 (The supplied routine id is invalid)
--               [2] = The pRID value.
--
-- This function calls the routine id /i(pRID) for each of the items
-- in the list /i(pTheList). They are processed in right-to-left order. /n
--
-- The /i(pRID) parameter is a routine id of a caller defined function
-- which is called for each item processed. It is passed three parameters:
--     [1] => The index of the item being processed.
--     [2] => The value of the item being processed.
--     [3] => 0
-- The routine must return either a single integer or a one-element
-- sequence. If it returns a 0 (zero) then next item is processed. If
-- it returns a non-zero integer, the processing of items is stopped.
-- If it returns a sequence, the first element is used to replace the
-- item just processed in the list.
--
-- Example:
--/code
-- sequence res
-- function showit(integer pIdx, object pItem, object pExtra)
--     printf(1, "%d: %s\n", {pIdx, pItem})
--     if pItem = 'D' then return 1 end if
--     return {pItem - ('A' - 'a')}
-- end function
--
-- res = foreach_reverse("ABCDEF", routine_id("showit"))
-- printf(1, "%d %d %d %s\n", res)
--/endcode

----------------------------------------------------------
global function foreach_reverse(sequence pTheList, integer pRID)
----------------------------------------------------------

    return every_item(pTheList, pRID, 0, length(pTheList), 1, 1)

end function


-- 
Derek Parnell
Melbourne, Australia
Skype name: derek.j.parnell

new topic     » goto parent     » topic index » view message » categorize

4. Re: foreach routine

c.k.lester wrote:
> 
> FMI, what is the benefit of a foreach statement vs. for x = y to z?
> 
> for i=1 to length(some_list) do
>   call_func(rtn, {some_list[i], d})
> end for
> 
> for each list in some_list do
>   call_func(rtn, {list, d})
> end for
> 
> Would there be a performance benefit on the backend?

Hi...

I'm too new to euphoria code to venture even a guess, never mind an opinion.
Coming from Perl, I'm trying to emulate the following:

read(STDIN, $buffer, $ENV{CONTENT_LENGTH});
@pairs = split(/&/, $buffer);
foreach $pair (@pairs){
    ($name, $value) = split(/=/, $pair);
     blah
     blah
}

I'm going to use a `for' loop to `getc' CONTENT_LENGTH amount of chars and
shove them into `sequence buffer'.

I've got a split routine that I think will work.

I just need to walk through the 'sequence pairs' one at a time, split
again and stash each in there own sequence.

Like I said in a prior post -- sequences are so HUGE of a deal in Euphoria
that the support for them ought to be a non-issue. As it stands now, the hoops
that I *think* I'll have to jump through to do anything with sequences are
bizarre. Maybe this is *one* of the issues that needs to be fixed in order
to make a powerful language that much more attractive and efficient to use.

BTW... TP80-cgilib.e ! Still available?? Later....
--
duke

new topic     » goto parent     » topic index » view message » categorize

5. Re: foreach routine

Derek Parnell wrote:
> 
> duke normandin wrote:
> > 
> > Hi...
> > 
> > Where are we at on this issue? 
> 
> Here is a first cut at an interim solution...
> }}}
<eucode>
> -- foreach.e

[snip and saved] 

Nice Code! and well documented! The Euphoria community may just put you in
the Hall-of-Fame you know ;)

Like I said in another post, I basically trying to emulate Perl's `foreach'
routine, as in:
     foreach $pair (@pairs){
        do something
     }

where @pairs is a simple array (no sweat! => a "U4ia" sequence)
      $pair is a variable used in "list" context so it gets a string

So in Perl, what happens here is that the foreach routine is fed an array
(sequence), it walks through it and returns each element one at a time.
This element gets shoved into $pair (in this example). $pair is then
massaged whichever way you need.

In Lua, they have `foreach' to walk through a hash, and `foreachi' to walk
through an "indexed" array. Slick too!
--
duke

new topic     » goto parent     » topic index » view message » categorize

6. Re: foreach routine

duke normandin wrote:

> Like I said in another post, I basically trying to emulate Perl's `foreach'
> routine, as in:
>      foreach $pair (@pairs){
>         do something
>      }
> 
> where @pairs is a simple array (no sweat! => a "U4ia" sequence)
>       $pair is a variable used in "list" context so it gets a string

include foreach.e
function do_something(integer x, object item, object extra)
   -- Do something with the item
   return 0
end function

sequence result
result = foreach(pairs, routine_id("do_something"))
if result[1] = 0 then
  -- No problems
else
  -- Something went wrong
end if

 

I know about 'foreach' as I use it all the time in the D programming language. D
is a bit like a static-typed, object-enabled, compiled Euphoria.

  http://www.digitalmars.com/d/statement.html#ForeachStatement

I also use it in the Progress 4GL (OpenEdge) which is a proprietary database
language.


-- 
Derek Parnell
Melbourne, Australia
Skype name: derek.j.parnell

new topic     » goto parent     » topic index » view message » categorize

7. Re: foreach routine

duke normandin wrote:
> 
> Coming from Perl, I'm trying to emulate the following:
> 
> read(STDIN, $buffer, $ENV{CONTENT_LENGTH});
> @pairs = split(/&/, $buffer);
> foreach $pair (@pairs){
>     ($name, $value) = split(/=/, $pair);
>      blah
>      blah
> }

BTW you will probably miss tightly integrated regular expression too.  I've been
meaning to wrap the perlre.dll for a while now but it's a bit of a headache
everytime I look at the API :)

That and the fact that you can't just drop in a RE in things like split, unpack,
etc.

But I use Perl and Euphoria equally as much... Perl for CGI and general
scripting for server stuff and Euphoria for Windows programming and general
scripting on Windows.

Gary

new topic     » goto parent     » topic index » view message » categorize

8. Re: foreach routine

Hi and welcome to Euphoria Duke,
 
 I don't know anything about Perl, C, other languages etc...
> > 
> > Coming from Perl, I'm trying to emulate the following:
> > 
> > read(STDIN, $buffer, $ENV{CONTENT_LENGTH});
> > @pairs = split(/&/, $buffer);
> > foreach $pair (@pairs){
> >     ($name, $value) = split(/=/, $pair);
> >      blah
> >      blah
> > }
> 


include strlist.e I put this in my stock include files (that come with Euphoria setup),

even though you must download it separtly (in the acrchives).

list=split(line,'/') this will make a lot of little sequences out of one big one.

Don Cole }}}

new topic     » goto parent     » topic index » view message » categorize

9. Re: foreach routine

ags wrote:
> 
> duke normandin wrote:
> > 
> > Coming from Perl, I'm trying to emulate the following:
> > 
> > read(STDIN, $buffer, $ENV{CONTENT_LENGTH});
> > @pairs = split(/&/, $buffer);
> > foreach $pair (@pairs){
> >     ($name, $value) = split(/=/, $pair);
> >      blah
> >      blah
> > }
> 
> BTW you will probably miss tightly integrated regular expression too.  I've
> been meaning to wrap the perlre.dll for a while now but it's a bit of a
> headache
> everytime I look at the API :)
> 
> That and the fact that you can't just drop in a RE in things like split,
> unpack,
> etc.
> 
> But I use Perl and Euphoria equally as much... Perl for CGI and general
> scripting
> for server stuff and Euphoria for Windows programming and general scripting
> on Windows.

My gut feeling tells me that U4ia can do CGI and various backend stuff OK.
The issue for me is how efficiently? So far, lots of hoops to jump through
to get simple things done. However I'm serious about U4ia. I'm still reading the
reference manual. In section 2.3 it states:

"Programming in Euphoria is based entirely on creating and manipulating
flexible, dynamic sequences. Sequences are IT - there are no other
structures to learn"

Nice --- but hardly no tools to do the above "manipulating" of sequences.
Its sad!
--
duke

new topic     » goto parent     » topic index » view message » categorize

10. Re: foreach routine

don cole wrote:
> 
> 
>  Hi and welcome to Euphoria Duke,
>  
>  I don't know anything about Perl, C, other languages etc...
> > > 
> > > Coming from Perl, I'm trying to emulate the following:
> > > 
> > > read(STDIN, $buffer, $ENV{CONTENT_LENGTH});
> > > @pairs = split(/&/, $buffer);
> > > foreach $pair (@pairs){
> > >     ($name, $value) = split(/=/, $pair);
> > >      blah
> > >      blah
> > > }
> > 
>  }}}
<eucode>
> 
> include strlist.e
>   -- I put this in my stock include files (that come with Euphoria setup),
>   -- even though you must download it separtly (in the acrchives).
>   
>    list=split(line,'/') --this will make a lot of little sequences out of one
>    big one.

I might have to hunt you down like a dog and buy you a cup of coffee (or
something) ;) Thanks!
--
duke

new topic     » goto parent     » topic index » view message » categorize

11. Re: foreach routine

duke normandin wrote:
> 
> Coming from Perl, I'm trying to emulate the following:
> 
> read(STDIN, $buffer, $ENV{CONTENT_LENGTH});
> @pairs = split(/&/, $buffer);
> foreach $pair (@pairs){
>     ($name, $value) = split(/=/, $pair);
>      blah
>      blah
> }

These might help ...
-- split.e

--/func split_char(pDelim, pText)
--/desc This will create subsequences of pText which is delimited by pDelim
--/ret SEQUENCE: A set of subsequences.
--This looks for the delimiter in pText and breaks it up into subsequences
--at the delimiter boundaries.
--
-- Example:
--/code
--  res = split_char('=', "age=59")
--  -- Returns {"age", "59"}
--  res = split_char(';', "abc;defg;hi;jkl;;qwerty")
--  -- Returns {"abc","defg","hi","jkl","","qwerty"}
--/endcode

global function split_char(integer pDelim, sequence pText)
    sequence lResult
    integer lPos
    integer lNewPos

    lResult = {}
    lNewPos = 1
    lPos = find(pDelim, pText)
    while lPos > 0 do
        lResult = append(lResult, pText[lNewPos..lPos-1])
        lNewPos = find(pDelim, pText[lPos+1..$])
        if lNewPos > 0 then
            lPos += lNewPos
            lNewPos = lPos - lNewPos + 1
        else
            lNewPos = lPos+1
            lPos = 0
        end if
    end while

    lResult = append(lResult, pText[lNewPos..$])
    return lResult
end function

--/func split(pDelim, pText)
--/desc This will create nested subsequences of pText which is delimited by
pDelim
--/ret SEQUENCE: A set of nested subsequences.
--/i(pDelim) is a set of one-character delimiters. /i(pText) is recursively
-- split based on successive delimiters in /i(pDelim).
--
-- Example:
--/code
--  res = split("&=", "dude=duke&age=59&style=kewl")
--  -- Returns {{"dude","duke"}, {"age", "59"}, {"style", "kewl"}}
--/endcode
--
--However, if /i(pDelim) is either an integer or a single character sequence
-- the result is equivalent to the split_char() function.
--
--Example:
--/code
--  res = split('=', "age=59")
--  -- Returns {"age", "59"}
--  res = split(";", "abc;defg;hi;jkl;;qwerty")
--  -- Returns {"abc","defg","hi","jkl","","qwerty"}
--/endcode

global function split(object pDelim, sequence pText)
    sequence lResult

    if integer(pDelim) then
        return split_char(pDelim, pText)
    end if

    lResult = split_char(pDelim[1], pText)
    if length(pDelim) > 1 then
        for i = 1 to length(lResult) do
            lResult[i] = split(pDelim[2..$], lResult[i])
        end for
    end if

    return lResult
end function

--/func toKeyValue(pPairs)
--/desc Converts the set of paired subsequences into two sequences of keys and
values.
--/ret SEQUENCE: A 2-element sequence. The first contains a list of keys, the
2nd a list of corresponding values.
--The input is assumed to be a list of 2-element (pairs) sequences. The
--output is just the pairs rearranged such that the first output subsequence
-- is a list of all the keys, and the second output subsequences is
-- a list of the corresponding values for those keys.
--
--Example:
--/code
--  res = toKeyValue( {{"dude","duke"}, {"age", "59"}, {"style", "kewl"}} )
--  -- Returns ...
--  --   { {"dude", "age", "style"}, {"duke", "59", "kewl"} }
--/endcode

global function toKeyValue(sequence pPairs)
    sequence lResult

    lResult = repeat( repeat({}, length(pPairs)), 2)

    for i = 1 to length(pPairs) do
        lResult[1][i] = pPairs[i][1]
        lResult[2][i] = pPairs[i][2]
    end for

    return lResult
end function

--/func findKeyValue(pKey, pKeyValues, pDefault)
--/desc Returns the value associated with the /i(pKey) parameter.
--/ret OBJECT: The associated value of the key.
--The /i(pKeyValues) parameter must be a 2-element sequence, the
--first containing keys and the second containing values for those keys.
--
--This looks for the /i(pKey) value in the keys subsequence and when
--found returns the corresponding value. If not found, it returns the
--supplied /i(pDefault) value.
--
--Example:
--/code
--  sequence x
--  x = toKeyValue( split("&=", "dude=duke&age=59&style=kewl") )
--  ? findKeyValue("age", x, -1)    -- returns "59"
--  ? findKeyValue("dude", x, -1)   -- returns "duke"
--  ? findKeyValue("rabbit", x, -1) -- returns -1
--  ? findKeyValue("style", x, -1)  -- returns "kewl"
--/endcode

global function findKeyValue(object pKey, sequence pKeyValues, object pDefault)
    integer lPos

    lPos = find(pKey, pKeyValues[1])
    if lPos > 0 then
        return pKeyValues[2][lPos]
    else
        return pDefault
    end if

end function



-- 
Derek Parnell
Melbourne, Australia
Skype name: derek.j.parnell

new topic     » goto parent     » topic index » view message » categorize

12. Re: foreach routine

duke normandin wrote:
> 
> My gut feeling tells me that U4ia can do CGI and various backend stuff OK.
> The issue for me is how efficiently? So far, lots of hoops to jump through
> to get simple things done. However I'm serious about U4ia. I'm still reading
> the
> reference manual. In section 2.3 it states:

Euphoria is capable of most anything. It's a double-edged sword. If you are
looking for a specialty language, then Eu is not for you, but otherwise it does a
very good job at being a general programming language.
Many things seem to be more tedious in Euphoria because it's so general and it
doesn't offer a gamut of 'candy' features that make things like CGI convenient.
You won't find much candy in Euphoria, and that's good. There is plenty of
libraries to make it simpler though.

The main issue with Eu is that because the community is so small compared to
other popular languages, there isn't a very large selection of libraries
available and many are not fully developed.

CGI for example, is an area that has not been entirely addressed by the
community yet. Support for CGI in Eu is still in it's infancy. You can find
several libraries for CGI in the archives, but you certainly won't find it as
convenient or as full-featured as perl or php, which are more designed for the
task.

Having said that, it's most definitely capable of CGI. The Euforum and most of
the functionality of the RDS website is powered by Eu. Several others have made
Euphoria driven websites as well, including myself. I've even written a special
tool that allows Eu to work inline in html, like PHP or SSI.

> "Programming in Euphoria is based entirely on creating and manipulating
> flexible, dynamic sequences. Sequences are IT - there are no other
> structures to learn"
> 
> Nice --- but hardly no tools to do the above "manipulating" of sequences.
> Its sad!

What kind of tools? Most things that people think they need are nothing more
than convenience features, or are due to trying to make Eu like some other
language that they are more accustomed to.

You are right that it does need some more functionality for dealing with
sequences, but it's probably not the kinds of features you are thinking of.

Chris Bensler
~ The difference between ordinary and extraordinary is that little extra ~
http://empire.iwireweb.com - Empire for Euphoria

new topic     » goto parent     » topic index » view message » categorize

13. Re: foreach routine

> Nice --- but hardly no tools to do the above "manipulating" of sequences.
> Its sad!
> --
> duke

take a look at Ricardo Forno's general functions library, I find it very useful
for sequence splicing and dicing.

new topic     » goto parent     » topic index » view message » categorize

14. Re: foreach routine

duke normandin wrote:
> 
> c.k.lester wrote:
> > 
> > FMI, what is the benefit of a foreach statement vs. for x = y to z?

I've considered foreach in the past for various language designs and
preprocessor features. I could see no real gain and it creates issues when one
also wants access to the iterator in a foreach loop, which is often enough that a
foreach construct is too specific to be a good addition to Euphoria.


> > Would there be a performance benefit on the backend?

Not likely, possibly a very tiny one.


> Hi...
> 
> I'm too new to euphoria code to venture even a guess, never mind an opinion.
> Coming from Perl, I'm trying to emulate the following:
> 
> read(STDIN, $buffer, $ENV{CONTENT_LENGTH});
> @pairs = split(/&/, $buffer);
> foreach $pair (@pairs){
>     ($name, $value) = split(/=/, $pair);
>      blah
>      blah
> }
> 
> I'm going to use a `for' loop to `getc' CONTENT_LENGTH amount of chars and
> shove them into `sequence buffer'.
> 
> I've got a split routine that I think will work.
> 
> I just need to walk through the 'sequence pairs' one at a time, split
> again and stash each in there own sequence.
> 
> Like I said in a prior post -- sequences are so HUGE of a deal in Euphoria
> that the support for them ought to be a non-issue. As it stands now, the hoops
> that I *think* I'll have to jump through to do anything with sequences are
> bizarre. Maybe this is *one* of the issues that needs to be fixed in order
> to make a powerful language that much more attractive and efficient to use.
> 
> BTW... TP80-cgilib.e ! Still available?? Later....
> --
> duke

Here is some Eu code to do what you want (split() function sold separately)
Warning: code is not tested, consider it a guideline only

include get.e -- needed for get_bytes() and value()
sequence pairs,pair
sequence name,data -- ('value' is a standard routine name, so I will use 'data'
instead)
sequence buffer
object tmp -- temporary/intermediate variable

tmp = getenv("CONTENT_LENGTH")
if atom(tmp) then
  -- CONTENT_LENGTH was not found
else
  tmp = value(tmp)
  if tmp[1] != GET_SUCCESS then
    -- value() failed
  else
    buffer = get_bytes(STDIN,tmp[2])
    pairs = split(buffer,'&')
    for i = 1 to length(pairs) do
      pair = pairs[i]
      tmp = split(pair,'=')
      name = tmp[1]
      data = tmp[2]
      -- blah
      -- blah
    end for
  end if
end if



Chris Bensler
~ The difference between ordinary and extraordinary is that little extra ~
http://empire.iwireweb.com - Empire for Euphoria

new topic     » goto parent     » topic index » view message » categorize

15. Re: foreach routine

Derek Parnell wrote:
> 
> duke normandin wrote:
> > 
> > Coming from Perl, I'm trying to emulate the following:
> > 
> > read(STDIN, $buffer, $ENV{CONTENT_LENGTH});
> > @pairs = split(/&/, $buffer);
> > foreach $pair (@pairs){
> >     ($name, $value) = split(/=/, $pair);
> >      blah
> >      blah
> > }
> 
> These might help ...
> }}}
<eucode>
> -- split.e

[code snipped]

What can I say but *awesome* !! -- and -- thanks a bunch. I've got enough
code to study now so that I'll either "Get it!" or I should imigrate
"down-under" and chase "roos" for a living. ;)  Later...
--
duke

new topic     » goto parent     » topic index » view message » categorize

16. Re: foreach routine

Chris Bensler wrote:

> 
> Here is some Eu code to do what you want (split() function sold separately)
> Warning: code is not tested, consider it a guideline only
> 
> }}}
<eucode>
> include get.e -- needed for get_bytes() and value()
> sequence pairs,pair
> sequence name,data -- ('value' is a standard routine name, so I will use
> 'data' instead)
> sequence buffer
> object tmp -- temporary/intermediate variable
> 
> tmp = getenv("CONTENT_LENGTH")
> if atom(tmp) then
>   -- CONTENT_LENGTH was not found
> else
>   tmp = value(tmp)
>   if tmp[1] != GET_SUCCESS then
>     -- value() failed
>   else
>     buffer = get_bytes(STDIN,tmp[2])
>     pairs = split(buffer,'&')
>     for i = 1 to length(pairs) do
>       pair = pairs[i]
>       tmp = split(pair,'=')
>       name = tmp[1]
>       data = tmp[2]
>       -- blah
>       -- blah
>     end for
>   end if
> end if
> </eucode>
{{{


Thanks Chris.... 

Why am I not getting this?

tmp = getenv("CONTENT_LENGTH")
if atom(tmp) then

CONTENT_LENGTH 
--
duke

new topic     » goto parent     » topic index » view message » categorize

17. Re: foreach routine

Jules Davy wrote:
> 
> > Nice --- but hardly no tools to do the above "manipulating" of sequences.
> > Its sad!
> > --
> > duke
> 
> take a look at Ricardo Forno's general functions library, I find it very
> useful
> for sequence splicing and dicing.

Thanks! I'll have a look! 
--
duke

new topic     » goto parent     » topic index » view message » categorize

18. Re: foreach routine

duke normandin wrote:
> 
> Chris Bensler wrote:
> 
> > 
> > Here is some Eu code to do what you want (split() function sold separately)
> > Warning: code is not tested, consider it a guideline only
> > 
> > }}}
<eucode>
> > include get.e -- needed for get_bytes() and value()
> > sequence pairs,pair
> > sequence name,data -- ('value' is a standard routine name, so I will use
> > 'data' instead)
> > sequence buffer
> > object tmp -- temporary/intermediate variable
> > 
> > tmp = getenv("CONTENT_LENGTH")
> > if atom(tmp) then
> >   -- CONTENT_LENGTH was not found
> > else
> >   tmp = value(tmp)
> >   if tmp[1] != GET_SUCCESS then
> >     -- value() failed
> >   else
> >     buffer = get_bytes(STDIN,tmp[2])
> >     pairs = split(buffer,'&')
> >     for i = 1 to length(pairs) do
> >       pair = pairs[i]
> >       tmp = split(pair,'=')
> >       name = tmp[1]
> >       data = tmp[2]
> >       -- blah
> >       -- blah
> >     end for
> >   end if
> > end if
> > </eucode>
{{{

> 
> Thanks Chris.... 
> 
> Why am I not getting this?
> 
> tmp = getenv("CONTENT_LENGTH")
> if atom(tmp) then
> 
> CONTENT_LENGTH 
> --
> duke

I hit the send key too quickly -- my bad!

What I was going to say is:

CONTENT_LENGTH will either be empty or contain the number of chars/bytes
sent. So 
if atom(tmp) then

checks to see if getenv() returned a number, right? If true, we have that
many chars/bytes. If 0 CONTENT_LENGTH was empty.

Do I need another cup of coffee? or something? It seems that the logic
is backwards. TIA...  (now hit the send button, duke)
--
duke

new topic     » goto parent     » topic index » view message » categorize

19. Re: foreach routine

duke normandin wrote:
> 
> What I was going to say is:
> 
> CONTENT_LENGTH will either be empty or contain the number of chars/bytes
> sent. So 
> if atom(tmp) then
> 
> checks to see if getenv() returned a number, right? If true, we have that
> many chars/bytes. If 0 CONTENT_LENGTH was empty.
> 
> Do I need another cup of coffee? or something? It seems that the logic
> is backwards. TIA...  (now hit the send button, duke)
> --
> duke

The getenv() routine will return -1 if the environment variable CONTENT_LENGTH
doesn't exist. If it does exist, a string will be returned.
Since CONTENT_LENGTH is supposed to be a number, we use value() to convert the
string to a number.

value() also returns a sequence of length = 2, where the 1st element is an error
code and the 2nd is the actual number value.

Hence the 2 error checks.

HTH

Chris Bensler
~ The difference between ordinary and extraordinary is that little extra ~
http://empire.iwireweb.com - Empire for Euphoria

new topic     » goto parent     » topic index » view message » categorize

20. Re: foreach routine

> Why am I not getting this?
>
> tmp = getenv("CONTENT_LENGTH")

You're probably running this from a command line and the server.
Server environment variables are born when the server calls your CGI
app, and they die when it finiishes.

~Greg

new topic     » goto parent     » topic index » view message » categorize

21. Re: foreach routine

duke normandin wrote:
> My gut feeling tells me that U4ia can do CGI and various backend stuff OK.
> The issue for me is how efficiently? So far, lots of hoops to jump through
> to get simple things done.

I've not really had to jump through any "hoops." Using some libraries freely
available in the archive, plus building a little bit of my own, I've got
several web sites being served by Euphoria.

I've built a content management system called Building Blocks CMS. It has
made development of dynamic web sites pretty easy. I'm even incorporating
Ajax functionality now. Cool stuff!

-=ck
"Programming in a state of Euphoria."
http://www.cklester.com/euphoria/

new topic     » goto parent     » topic index » view message » categorize

22. Re: foreach routine

Chris Bensler wrote:
> 
> duke normandin wrote:
> > 
> > What I was going to say is:
> > 
> > CONTENT_LENGTH will either be empty or contain the number of chars/bytes
> > sent. So 
> > if atom(tmp) then
> > 
> > checks to see if getenv() returned a number, right? If true, we have that
> > many chars/bytes. If 0 CONTENT_LENGTH was empty.
> > 
> > Do I need another cup of coffee? or something? It seems that the logic
> > is backwards. TIA...  (now hit the send button, duke)
> > --
> > duke
> 
> The getenv() routine will return -1 if the environment variable CONTENT_LENGTH
> doesn't exist. If it does exist, a string will be returned.

Are you sure? I've never proven this to myself, but it has always been my
understanding that with respect the method=POST, the ENV. VAR. CONTENT_LENGTH
would be set to the amount of bytes sent out. THEREFORE, this is why we "read"
that many bytes from STDIN, and only that many. So my point is that
CONTENT_LENGTH
will in one case be a positive number or a -1 -- both atoms. No?

The above of course does not apply to method=GET, because in the latter, all
the data is stuffed in the ENV. VAR. QUERY_STRING. Here we can check to see
if getenv("QUERY_STRING") returns an atom i.e -1, because QUERY_STRING will
either contain a string, or will be undefined. 
--
duke

new topic     » goto parent     » topic index » view message » categorize

23. Re: foreach routine

duke normandin wrote:
> 
> Chris Bensler wrote:
> > The getenv() routine will return -1 if the environment variable
> > CONTENT_LENGTH
> > doesn't exist. If it does exist, a string will be returned.
> 
> Are you sure? I've never proven this to myself, but it has always been my
> understanding that with respect the method=POST, the ENV. VAR. CONTENT_LENGTH
> would be set to the amount of bytes sent out. THEREFORE, this is why we "read"
> that many bytes from STDIN, and only that many. So my point is that
> CONTENT_LENGTH
> will in one case be a positive number or a -1 -- both atoms. No?

Yes and no. The Euphoria function getenv() always returns a string if the
environment variable exists, and -1 if it doesn't exist. In your case,
CONTENT_LENGTH will most likely always be there due to the CGI environment, but
when getenv() returns its content, it is a string representation of a number and
not the number itself. So if the number of characters to read in from STDIN is
59, getenv("CONTENT_LENGTH") will return "59" and not 59. Thus you have to
convert the string "59" to its number form.

  res = value( "59" )

The value() function always returns a 2-element sequence. The first element is
the success/failure code and the second is the converted number. So in this case,
value() returns {0,59} where the leading 0 indicates successful conversion
occured. So in code ...

object result
result = getenv("CONTENT_LENGTH")
if sequence(result) then 
   -- a string was returned.
   result = value(result)
   if result[1] = 0 then
       --- a good conversion.
       readsize = result[2]
   else
       --- bad data in string!
   end if
else
   --- CONTENT_LENGTH doesn't exist
end if


 
> The above of course does not apply to method=GET, because in the latter, all
> the data is stuffed in the ENV. VAR. QUERY_STRING. Here we can check to see
> if getenv("QUERY_STRING") returns an atom i.e -1, because QUERY_STRING will
> either contain a string, or will be undefined. 
> --
> duke


-- 
Derek Parnell
Melbourne, Australia
Skype name: derek.j.parnell

new topic     » goto parent     » topic index » view message » categorize

24. Re: foreach routine

Derek Parnell wrote:
> 
> duke normandin wrote:
> > 
> > Chris Bensler wrote:
> > > The getenv() routine will return -1 if the environment variable
> > > CONTENT_LENGTH
> > > doesn't exist. If it does exist, a string will be returned.
> > 
> > Are you sure? I've never proven this to myself, but it has always been my
> > understanding that with respect the method=POST, the ENV. VAR.
> > CONTENT_LENGTH
> > would be set to the amount of bytes sent out. THEREFORE, this is why we
> > "read"
> > that many bytes from STDIN, and only that many. So my point is that
> > CONTENT_LENGTH
> > will in one case be a positive number or a -1 -- both atoms. No?
> 
> Yes and no. The Euphoria function getenv() always returns a string if the
> environment
> variable exists, and -1 if it doesn't exist. In your case, CONTENT_LENGTH will
> most likely always be there due to the CGI environment, but when getenv()
> returns
> its content, it is a string representation of a number and not the number
> itself.
> So if the number of characters to read in from STDIN is 59,
> getenv("CONTENT_LENGTH")
> will return "59" and not 59. Thus you have to convert the string "59" to its
> number form. 

I got it! Damn! I've got to watch the data types more carefully. Not used to it.
;)
To recap, getenv returns the atom -1 if the ENV.VAR. is unset OR a string
containing
whatever. In the case of CONTENT_LENGTH I was expecting an integer. I now know
to
convert CONTENT_LENGTH *to* an integer.

>   res = value( "59" )

[snip]

I really don't need to do all that testing.

What the Perl/PHP etc world has done all this time is:

1. getenv("REQUEST_METHOD")
2. if == "POST"
   2.a value_array = value(getenv("CONTENT_LENGTH")
   2.b stash get_bytes(0, value_array[2])
3. if == "GET"
   3.a stash getenv("QUERY_STRING")
4. if == "some_bogus_method"
   4.a cgi_die("gracefully")
5. amen

That is what I want to reproduce in Euphoria come "hell or high water" ;)

Once I have the above "stash", then we use your "foreach" and "split" code to
massage the FORM[key]=values
I love it !! Thanks you all for the mini EU tutorials!!
--
duke

new topic     » goto parent     » topic index » view message » categorize

25. Re: foreach routine

duke normandin wrote:
> 
> Derek Parnell wrote:
> > 
> > duke normandin wrote:
> > > 
> > > Chris Bensler wrote:
> > > > The getenv() routine will return -1 if the environment variable
> > > > CONTENT_LENGTH
> > > > doesn't exist. If it does exist, a string will be returned.
> > > 
> > > Are you sure? I've never proven this to myself, but it has always been my
> > > understanding that with respect the method=POST, the ENV. VAR.
> > > CONTENT_LENGTH
> > > would be set to the amount of bytes sent out. THEREFORE, this is why we
> > > "read"
> > > that many bytes from STDIN, and only that many. So my point is that
> > > CONTENT_LENGTH
> > > will in one case be a positive number or a -1 -- both atoms. No?
> > 
> > Yes and no. The Euphoria function getenv() always returns a string if the
> > environment
> > variable exists, and -1 if it doesn't exist. In your case, CONTENT_LENGTH
> > will
> > most likely always be there due to the CGI environment, but when getenv()
> > returns
> > its content, it is a string representation of a number and not the number
> > itself.
> > So if the number of characters to read in from STDIN is 59,
> > getenv("CONTENT_LENGTH")
> > will return "59" and not 59. Thus you have to convert the string "59" to its
> > number form. 
> 
> I got it! Damn! I've got to watch the data types more carefully. Not used to
> it. ;)
> To recap, getenv returns the atom -1 if the ENV.VAR. is unset OR a string
> containing
> whatever. In the case of CONTENT_LENGTH I was expecting an integer. I now know
> to
> convert CONTENT_LENGTH *to* an integer.
> 
> >   res = value( "59" )
> 
> [snip]
> 
> I really don't need to do all that testing.
> 
> What the Perl/PHP etc world has done all this time is:
> 
> 1. getenv("REQUEST_METHOD")
> 2. if == "POST"
>    2.a value_array = value(getenv("CONTENT_LENGTH")
>    2.b stash get_bytes(0, value_array[2])
> 3. if == "GET"
>    3.a stash getenv("QUERY_STRING")
> 4. if == "some_bogus_method"
>    4.a cgi_die("gracefully")
> 5. amen
> 
> That is what I want to reproduce in Euphoria come "hell or high water" ;)
> 
> Once I have the above "stash", then we use your "foreach" and "split" code to
> massage the FORM[key]=values
> I love it !! Thanks you all for the mini EU tutorials!!
> --
> duke

Good programming practice is to always trap error conditions whenever you can.

I get the impression though, that you meant that you don't want to have to do
the error testing all the time. Make a routine that takes care of it for you
instead.

I use this routine..
This avoids the error by making it default to an empty string.
This routine will always return a valid string or "".

global function cgi_getenv(string name)
 object tmp
  tmp = getenv(name)
  if atom(tmp) then return "" end if
  return tmp
end function


Similarly for value(), I have..
This avoids the error by making it default to 0.
This always returns the converted number, or 0.

global function get_value(object x)
 sequence ret
  ret = value(x)
  if ret[1] = GET_SUCCESS then
    return ret[2]
  else
    return 0
  end if
end function


The above routines are not generally a good way to deal with error cases, but
for specific purposes such as this situation, it should be fine.

Using those two routines, I could make a new routine specifically to return the
content_length..
This routine returns the number value of the content length.

global function cgi_content_length()
 integer len
  len = cgi_getenv("CONTENT_LENGTH")
  return get_value(len)
end function


And a routine to handle the request method...
global function cgi_request_method()
  return cgi_getenv("REQUEST_METHOD")
end function


For further processing of the POST or QUERY, you will need some code like the
following, to convert url-encoded strings to plain text.

function replace_hex(sequence s)
 sequence f,hex
  -- find and store the index of all instances of '%' in s
  f = {}
  for i = 1 to length(s) do
    if s[i] = '%' then
      f &= i
    end if
  end for
  if length(f) then
    for i = length(f) to 1 by -1 do
hex = get_value("#"&s[f[i]+1..f[i]+2]) -- convert the string code to a
      character code
      s = s[1..f[i]-1] & hex & s[f[i]+3..length(s)]
    end for
  end if
  return s
end function

------------------------------------------------
-- This is the function that you are mostly after
-- cgi_parse_request() will convert a url-encoded string to key/value pairs
-- in the form of {{key1,key2,...},{val1,val2,...}}
-- EG. "firstname=Duke&lastname=Normandin"
--  becomes {{"firstname","lastname"},{"Duke","Normandin"}}

global function cgi_parse_request(sequence s)  
  s ={ s , {} }
  s[1] = join(split(s[1],'+'),' ') -- convert '+' to ' '
  s[1] = join(split(s[1],'&'),';') -- convert '&' to ';'
  s[1] = split(s[1],';')

  if length(s[1]) then s[2] = repeat("",length(s[1])) end if
  for i = 1 to length(s[1]) do
    s[1][i] = split(s[1][i],'=')
    s[1][i][1] = replace_hex(s[1][i][1])
    if length(s[1][i]) > 1 then
      s[2][i] = replace_hex(s[1][i][2])
    end if
    s[1][i] = s[1][i][1]
  end for
  return s
end function


Here is a routine to handle fetching the post data.
It will return a sequence of key/values as described for cgi_parse_request()
global function cgi_get_post()
 sequence buf
 integer len
  len = cgi_content_length()
  buf = get_bytes(STDIN,len)
  return cgi_parse_request(buf)
end function


Here is the routine to handle QUERY data.
global function cgi_get_query()
 sequence buf
  buf = cgi_getenv("QUERY_STRING")
  return cgi_parse_request(buf)
end function


Now you can make a routine to actually do the whole thing for you..
global function cgi_get_request()
 sequence req
  req = cgi_request_method()
  if equal(req,"POST") then
    return cgi_get_post()
  elsif equal(req,"GET") then
    return cgi_get_query()
  else
    -- error, invalid request method
  end if
end function



Buene?


You may be wondering why the pairs are stored as {key_list,data_list} instead of
{{key1,data1},{key2,data2}}. The reason is because it's easier to search for a
key that way.

EG.
sequence pairs
integer f
pairs = cgi_get_request()
f = find("firstname",pairs[1])
if f then
 puts(STDOUT,"Your firstname is: " & pairs[2][f] & "\n")
end if


Chris Bensler
~ The difference between ordinary and extraordinary is that little extra ~
http://empire.iwireweb.com - Empire for Euphoria

new topic     » goto parent     » topic index » view message » categorize

26. Re: foreach routine

Hi Duke,
I notice the comparisons to Perl.

There is no such thing as a "perfect" language - its always a trade off.
Euphoria works well for me, its small, very fast, simple but you do need
to create functions or even libraries that are perhaps builtin in other
languages. Often these have already been coded and are in the archive.
Its like reading a manual. While searching for something, you find other
things that you will need later. 

<stirring + punting>

Did you know Rob (RDS) once took a bit of Perl written by Larry Wall, converted
it to Euphoria and sent that back to Larry... it was faster of course
and Larry did not reply - he must have been too busy ;))  

</stirring + punting>

new topic     » goto parent     » topic index » view message » categorize

27. Re: foreach routine

Chris Bensler wrote:

> > [snip]
> > 
> > I really don't need to do all that testing.
> > 
> > What the Perl/PHP etc world has done all this time is:
> > 
> > 1. getenv("REQUEST_METHOD")
> > 2. if == "POST"
> >    2.a value_array = value(getenv("CONTENT_LENGTH")
> >    2.b stash get_bytes(0, value_array[2])
> > 3. if == "GET"
> >    3.a stash getenv("QUERY_STRING")
> > 4. if == "some_bogus_method"
> >    4.a cgi_die("gracefully")
> > 5. amen
> > 
> > That is what I want to reproduce in Euphoria come "hell or high water" ;)
> > 
> > Once I have the above "stash", then we use your "foreach" and "split" code
> > to
> > massage the FORM[key]=values
> > I love it !! Thanks you all for the mini EU tutorials!!
> > --
> > duke
> 
> Good programming practice is to always trap error conditions whenever you can.

I agree! But how often you need to trap errors depends on the language you are
using and the number of lines of code you need to generate to get something
done.
 
> I get the impression though, that you meant that you don't want to have to do
> the error testing all the time. Make a routine that takes care of it for you
> instead.

That's what I meant. As a EU novice, I'm quickly seeing that my coding style
and practices *need* to be different than those I was using with Perl and
PHP.

That said, I've been doing CGI / Web / SQL stuff since 1996 using Perl mostly. I
know how the CGI and back-end stuff works!! I just need to learn to do it
correctly "the EU way". ;)) Nice collection of functions BTW!! I'm sure they
get the job done. Can't wait to put it all together and try it out. Thanks
for the input!
--
duke

new topic     » goto parent     » topic index » view message » categorize

28. Re: foreach routine

duke normandin wrote:
> 
> 
> Once I have the above "stash", then we use your "foreach" and "split" code to
> massage the FORM[key]=values

Have you checked out the CGI code available in the archive? I just do this:

include cgi.e
process_cgi()
-- then
x = get_key_value("whatever")

...or something close to that. Easy! :)

-=ck
"Programming in a state of Euphoria."
http://www.cklester.com/euphoria/

new topic     » goto parent     » topic index » view message » categorize

29. Re: foreach routine

cklester wrote:
> 
> duke normandin wrote:
> > 
> > 
> > Once I have the above "stash", then we use your "foreach" and "split" code
> > to
> > massage the FORM[key]=values
> 
> Have you checked out the CGI code available in the archive? I just do this:
> 
> include cgi.e
> process_cgi()
> -- then
> x = get_key_value("whatever")
> 
> ...or something close to that. Easy! :)

I have searched the archives, but I don't think that I ran across cgi.e. I will
check it out. Thanks!
--
duke

new topic     » goto parent     » topic index » view message » categorize

30. Re: foreach routine

duke normandin wrote:
> 
> Chris Bensler wrote:
> 
> > 
> > Good programming practice is to always trap error conditions whenever you
> > can.
> 
> I agree! But how often you need to trap errors depends on the language you are
> using and the number of lines of code you need to generate to get something
> done.
>  
> > I get the impression though, that you meant that you don't want to have to
> > do
> > the error testing all the time. Make a routine that takes care of it for you
> > instead.
> 
> That's what I meant. As a EU novice, I'm quickly seeing that my coding style
> and practices *need* to be different than those I was using with Perl and
> PHP.

Once you have your 'EUreka!' moment, things will start to click quickly :P


> That said, I've been doing CGI / Web / SQL stuff since 1996 using Perl mostly.
> I
> know how the CGI and back-end stuff works!! I just need to learn to do it
> correctly "the EU way". ;)) Nice collection of functions BTW!! I'm sure they
> get the job done. Can't wait to put it all together and try it out. Thanks
> for the input!
> --
> duke

Sorry, I didn't mean for it to sound preachy. I can tell that you know web
programming and I look forward to the contributions I'm sure you will be making
in no time :)

You are right in thinking that there appears to be alot of 'hoops' to jump
through when using Euphoria. Don't let it turn you away though. Eu is an
excellent language. Eu's greatest hurdle is to get more people to use it, so we
have more people contributing quality code and banging heads.

This thread is precisely why I started the thread about standardized euphoria
and this issue is why I have been working on a design and implementation of an
improved set of standard includes for quite a long time. Something like a CGI
library should be a part of the standard includes (guess where I ripped the cgi
functions I pasted for you? ;). People shouldn't have to construct a hammer
before they can build a house and I hope I can help to resolve that for us.
Anyways I'm getting off topic.


Chris Bensler
~ The difference between ordinary and extraordinary is that little extra ~
http://empire.iwireweb.com - Empire for Euphoria

new topic     » goto parent     » topic index » view message » categorize

31. Re: foreach routine

Chris Bensler wrote:
> 

> > That said, I've been doing CGI / Web / SQL stuff since 1996 using Perl
> > mostly.
> > I
> > know how the CGI and back-end stuff works!! I just need to learn to do it
> > correctly "the EU way". ;)) Nice collection of functions BTW!! I'm sure they
> > get the job done. Can't wait to put it all together and try it out. Thanks
> > for the input!
> 
> Sorry, I didn't mean for it to sound preachy. I can tell that you know web
> programming
> and I look forward to the contributions I'm sure you will be making in no time
> :)

No worries! Really!!
 
> You are right in thinking that there appears to be alot of 'hoops' to jump
> through
> when using Euphoria. Don't let it turn you away though. Eu is an excellent
> language.
> Eu's greatest hurdle is to get more people to use it, so we have more people
> contributing quality code and banging heads.
>
> This thread is precisely why I started the thread about standardized euphoria
> and this issue is why I have been working on a design and implementation of
> an improved set of standard includes for quite a long time. Something like a
> CGI library should be a part of the standard includes (guess where I ripped
> the cgi functions I pasted for you? ;). People shouldn't have to construct a
> hammer before they can build a house and I hope I can help to resolve that for
> us. Anyways I'm getting off topic.

The above paragraphs are reason enough for me to adopt EU as my "native"
language. I like your attitude and style, and I have a sense that, for the
most part, you mirror the prevailing attitude(s) of this fine community. I'm
not a C programmer, so I can't be of much use with the guts of EU, but I
sure can't wait to get up to speed with the language itself in order to
start making a contribution. You know, I may be familiar with Perl and its
CPAN etc, but I left that community and more-and-more the language itself
for a reason or two....! But methinks I've found a home!

Anyway, thank you for your candor and insights. You and Derek Parnell and
others have been just great. Later....
--
duke

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu