Rosetta Code/Run examples

From Rosetta Code
Revision as of 13:00, 31 March 2022 by Petelomax (talk | contribs) (→‎{{header|Phix}}: use common code)
Rosetta Code/Run examples is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

This task is based on an idea hatched from this C1R Implementation.

Write a program that will accept as input the name of a task from Rosetta Code and the name of a language. The program should then download the solution for the specified task in the specified language, present the source to the user and prompt the user to confirm running the example.

The program should verify that the tools needed to compile or run the solution are present before running it. If the solution can not be run, a graceful exit should happen. (i.e. the program should not crash)

Besides its own language, the program should support at least two other languages. (Ideally it would support most of the languages available, but that is too much to ask. Though some languages are simple, e.g. python, pike, perl, bash and several more only need the solution saved in a file and given to the language as argument to run, so it should be easy to support many languages like that).

If you know what is needed to support a particular language, please help to add support for that language to implementations in other languages.

Extra credit: add a function to get a list of all solutions of a given language and run them to create a report on which solutions failed to run.

More credit: also test if the output of a solution compares to a given result. The expected output should be loaded from a file with the name of the task. (This way all implementations can share the same set of files, and anyone can add more files. In the future the content of these files could be stored directly in a section of the task on Rosetta Code itself.)

Go

This is a fairly basic program which is designed to work for Go, Perl or Python entries. The latter two have been chosen as they are often included in Linux distros by default.

However, in the case of Python, it assumes a Python 3 compatible program and so some earlier programs written for Python 2 will not work.

Although all necessary resources should still be available for Go programs, I have no idea about the other languages and would have to leave that to the user to judge.

Having checked that the task exists, the program downloads its 'Edit' page and looks for the first runnable program after the language header. In practice, of course, there may be several runnable programs for a given language though this strategy should at least ensure that programs for different languages which use the same syntax highlighting (such as Nim which uses Python S/H) are not extracted by mistake.

No attempt has been at 'extra credit' which would take all day for Go alone and 'more credit' seems pointless as program output is seldom, if ever, exactly the same for all languages. <lang go>package main

import (

   "bufio"
   "fmt"
   "html"
   "io/ioutil"
   "log"
   "net/http"
   "os"
   "os/exec"
   "regexp"
   "strings"

)

func getAllTasks() map[string]bool {

ex := `

  • <a href="/wiki/(.*?)"` re := regexp.MustCompile(ex) url1 := "http://rosettacode.org/wiki/Category:Programming_Tasks" url2 := "http://rosettacode.org/wiki/Category:Draft_Programming_Tasks" urls := []string{url1, url2} tasks := make(map[string]bool) for _, url := range urls { resp, _ := http.Get(url) body, _ := ioutil.ReadAll(resp.Body) // find all tasks matches := re.FindAllStringSubmatch(string(body), -1) resp.Body.Close() for _, match := range matches { // exclude any 'category' references if !strings.HasPrefix(match[1], "Category:") { tasks[match[1]] = true } } } return tasks } func check(err error) { if err != nil { log.Fatal(err) } } func main() { tasks := getAllTasks() for { fmt.Print("Enter the exact name of the task  : ") in := bufio.NewReader(os.Stdin) task, err := in.ReadString('\n') check(err) task = strings.TrimSpace(task) task = strings.ReplaceAll(task, " ", "_") if !tasks[task] { fmt.Println("Sorry a task with that name doesn't exist.") } else { url := "https://rosettacode.org/mw/index.php?title=" + task + "&action=edit" resp, err := http.Get(url) check(err) body, _ := ioutil.ReadAll(resp.Body) resp.Body.Close() var lang string for { fmt.Print("Enter the language Go/Perl/Python : ") lang, err = in.ReadString('\n') check(err) lang = strings.TrimSpace(lang) lang = strings.ToLower(lang) if lang == "go" || lang == "perl" || lang == "python" { break } fmt.Println("Sorry that language is not supported.") } var lang2, lang3, ext string switch lang { case "go": lang2 = "Go" lang3 = "(go|Go|GO)" ext = "go" case "perl": lang2 = "Perl" lang3 = "(perl|Perl)" ext = "pl" case "python": lang2 = "Python" lang3 = "(python|Python)" ext = "py" } fileName := "rc_temp." + ext header := fmt.Sprintf(`(?s)==\{\{header\|%s\}\}==.*?<lang %s>`, lang2, lang3) exp := header + `(.*?)</lang>` re := regexp.MustCompile(exp) page := string(body) matches := re.FindStringSubmatch(page) if matches == nil { fmt.Println("No runnable task entry for that language was detected.") } else { source := html.UnescapeString(matches[2]) fmt.Println("\nThis is the source code for the first or only runnable program:\n") fmt.Println(source) fmt.Print("\nDo you want to run it y/n : ") yn, err := in.ReadString('\n') check(err) if yn[0] == 'y' || yn[0] == 'Y' { err = ioutil.WriteFile(fileName, []byte(source), 0666) check(err) var cmd *exec.Cmd switch lang { case "go": cmd = exec.Command("go", "run", fileName) case "perl": cmd = exec.Command("perl", fileName) case "python": cmd = exec.Command("python3", fileName) } cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Run() // allow any error(s) to go to StdEerr check(os.Remove(fileName)) } } fmt.Print("\nDo another one y/n : ") yn, err := in.ReadString('\n') check(err) if yn[0] != 'y' && yn[0] != 'Y' { break } } } }</lang>
    Output:

    Sample output (same shortish task used for all 3 languages):

    Enter the exact name of the task  : Palindrome dates
    Enter the language Go/Perl/Python : Go
    
    This is the source code for the first or only runnable program:
    
    package main
    
    import (
        "fmt"
        "time"
    )
    
    func reverse(s string) string {
        chars := []rune(s)
        for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 {
            chars[i], chars[j] = chars[j], chars[i]
        }
        return string(chars)
    }
    
    func main() {
        const (
            layout  = "20060102"
            layout2 = "2006-01-02"
        )
        fmt.Println("The next 15 palindromic dates in yyyymmdd format after 20200202 are:")
        date := time.Date(2020, 2, 2, 0, 0, 0, 0, time.UTC)
        count := 0
        for count < 15 {
            date = date.AddDate(0, 0, 1)
            s := date.Format(layout)
            r := reverse(s)
            if r == s {
                fmt.Println(date.Format(layout2))
                count++
            }
        }
    }
    
    Do you want to run it y/n : y
    The next 15 palindromic dates in yyyymmdd format after 20200202 are:
    2021-12-02
    2030-03-02
    2040-04-02
    2050-05-02
    2060-06-02
    2070-07-02
    2080-08-02
    2090-09-02
    2101-10-12
    2110-01-12
    2111-11-12
    2120-02-12
    2121-12-12
    2130-03-12
    2140-04-12
    
    Do another one y/n : y
    Enter the exact name of the task  : Palindrome dates
    Enter the language Go/Perl/Python : Perl
    
    This is the source code for the first or only runnable program:
    
    use Time::Piece;
    my $d = Time::Piece->strptime("2020-02-02", "%Y-%m-%d");
    
    for (my $k = 1 ; $k <= 15 ; $d += Time::Piece::ONE_DAY) {
        my $s = $d->strftime("%Y%m%d");
        if ($s eq reverse($s) and ++$k) {
            print $d->strftime("%Y-%m-%d\n");
        }
    }
    
    Do you want to run it y/n : y
    2020-02-02
    2021-12-02
    2030-03-02
    2040-04-02
    2050-05-02
    2060-06-02
    2070-07-02
    2080-08-02
    2090-09-02
    2101-10-12
    2110-01-12
    2111-11-12
    2120-02-12
    2121-12-12
    2130-03-12
    
    Do another one y/n : y
    Enter the exact name of the task  : Palindrome dates
    Enter the language Go/Perl/Python : Python
    
    This is the source code for the first or only runnable program:
    
    '''Palindrome dates'''
    
    from datetime import datetime
    from itertools import chain
    
    
    # palinDay :: Int -> [ISO Date]
    def palinDay(y):
        '''A possibly empty list containing the palindromic
           date for the given year, if such a date exists.
        '''
        s = str(y)
        r = s[::-1]
        iso = '-'.join([s, r[0:2], r[2:]])
        try:
            datetime.strptime(iso, '%Y-%m-%d')
            return [iso]
        except ValueError:
            return []
    
    
    # --------------------------TEST---------------------------
    # main :: IO ()
    def main():
        '''Count and samples of palindromic dates [2021..9999]
        '''
        palinDates = list(chain.from_iterable(
            map(palinDay, range(2021, 10000))
        ))
        for x in [
                'Count of palindromic dates [2021..9999]:',
                len(palinDates),
                '\nFirst 15:',
                '\n'.join(palinDates[0:15]),
                '\nLast 15:',
                '\n'.join(palinDates[-15:])
        ]:
            print(x)
    
    
    # MAIN ---
    if __name__ == '__main__':
        main()
    
    Do you want to run it y/n : y
    Count of palindromic dates [2021..9999]:
    284
    
    First 15:
    2021-12-02
    2030-03-02
    2040-04-02
    2050-05-02
    2060-06-02
    2070-07-02
    2080-08-02
    2090-09-02
    2101-10-12
    2110-01-12
    2111-11-12
    2120-02-12
    2121-12-12
    2130-03-12
    2140-04-12
    
    Last 15:
    9170-07-19
    9180-08-19
    9190-09-19
    9201-10-29
    9210-01-29
    9211-11-29
    9220-02-29
    9221-12-29
    9230-03-29
    9240-04-29
    9250-05-29
    9260-06-29
    9270-07-29
    9280-08-29
    9290-09-29
    
    Do another one y/n : n
    

    Liberty BASIC

    <lang lb> ' ******************************************************************** ' ** ** ' ** parseAndRun.bas v26b tenochtitlanuk November 2012 ** ' ** ** ' ** select a LB solution from RC site & run it locally ** ' ** ** ' ******************************************************************** 'retrieve proper temporary path and filename to save downloaded HTML: Source$ = GetTempFileName$("htm") 'nomainwin

    ' Download main RC LB page which has current tasks on it. Save as 'source.html' ' run "C:\Program Files\Mozilla Firefox\firefox.exe http://rosettacode.org/wiki/Category:Liberty_BASIC" 'testing routine print " Fetching current RC page of completed Liberty BASIC RC solutions." 'result = DownloadToFile( "http://rosettacode.org/wiki/Category:Liberty_BASIC", "E:\source.html") result = DownloadToFile( "http://rosettacode.org/wiki/Category:Liberty_BASIC", Source$)

    if result <>0 then print "Error downloading LB solved tasks.": end else print: print " Displaying solved tasks.": print

    ' Load source into a string. Go through and save in a 2D array all topic titles ' and the appropriate web addresses to find them. 'open "E:\source.html" for input as #f open Source$ for input as #f

       html$ = input$( #f, lof( #f))
    

    close #f kill Source$ 'remove temp file

    dim solutions$( 500, 2)

    global count count =1 first =0 last =0 reading =0

    ' The first topic is the '100 doors' so skip all html jump ref's earlier than this. do

       r$ =getHtmlSection$( html$, first, last)
       if instr( r$, "/rosettacode.org/mw/index.php") then exit do '   We've read all LB solved tasks.
       if r$ ="wiki/100_doors" then reading =1
    
       if reading =1 then   '   we can start recording path & name
           solutions$( count, 1) ="http://rosettacode.org/" +r$ +"#Liberty_BASIC"
    
           special =instr( r$, "%2B"):    if special <>0 then r$ =left$( r$, special -1) +"+" +mid$( r$, special +3)
           special =instr( r$, "%27"):    if special <>0 then r$ =left$( r$, special -1) +"'" +mid$( r$, special +3)
           special =instr( r$, "%C3%A8"): if special <>0 then r$ =left$( r$, special -1) +chr$( 232) +mid$( r$, special +6)
           solutions$( count, 0) =mid$( r$, 6)  '   we want the bit beyond '/wiki/'
           if instr(  solutions$( count, 0), "/") then
               newName$ =""
               for ii =1 to len(  solutions$( count, 0) )
                   n$ =mid$( solutions$( count, 0), ii, 1)
                   if n$ ="/" then n$ ="_"
                   newName$ =newName$ +n$
               next ii
               solutions$( count, 0) =newName$
           end if
           print count, solutions$( count, 0)'; tab( 60); solutions$( count, 1)
           count =count +1
       end if
    

    loop until 0 print: print count -1; " tasks solved in LB."

    'input " Choose task # "; R ' Choose a page to try. for R =1 to 283 print print " Choosing a task at random viz #"; R; " out of "; count -1; " completed in LB." print " Task is "; chr$( 34); solutions$( R, 0); chr$( 34) print

    '********************run "C:\Program Files\Mozilla Firefox\firefox.exe " +solutions$( R, 1)

    ' Fetch the RC task page with all the sol'ns including LB one. print " Downloading the page for this task." result = DownloadToFile( solutions$( R, 1), "rx.html")

    if result <>0 then print "Error downloading.": end

    print " Now finding the LB section of the html code." ' Now finding the appropriate LB section on this topic.

    open "rx.html" for input as #r

       L =lof( #r)
       print " Length of source html of this topic's page is "; L
       t$ =input$( #r, L)
    

    close #r

    preamble$ =">Liberty BASIC</a>" +chr$( 10)

    lP =len( preamble$) print " Finding the preamble string at "; beg =instr( t$, preamble$)' +len( preamble$) print beg

    lookFor$ ="source" +chr$( 34) +">" beg =instr( t$, lookFor$, beg) ' get to start of BASIC code. beg =beg +len( lookFor$)

    print " Found LB section at "; beg;

    fin =instr( t$, "", beg)

    print " and ending at "; fin

    print " Chopping off unwanted earlier & later sections of html source." t$ =mid$( t$, beg, fin -beg) ' discard earlier & later parts of html code.

    open solutions$( R, 0) +".txt" for output as #LbText

       #LbText t$;
    

    close #LbText

    L =len( t$)

    print " Relevant html code LB section being parsed for LB BASIC code."

    ' Read the rest of the LB code section to section ..

    LB$ ="" j =1

    print " Dropping html tags & translating html entities." print print " LB code follows." print

    do

       nxtChr$  =mid$( t$, j, 1)
       select case '   _______________________________________________________________________________________________________
           case ( nxtChr$ =chr$( 10)) or ( nxtChr$ =chr$( 13))
               j =L
               print "End reached- CRLF"
    
           case nxtChr$ ="<"                                                           '   we've found a html tag. Omit.
               'print " Starting a tag with a <";
               item$ ="<"
               do                                                                      '   keep looking until find a '>' or finish...
                   j =j +1
                   nxtChr$  =mid$( t$, j, 1)
                   item$ =item$ +nxtChr$
               loop until nxtChr$ =">"
               'print " Closing a tag with a >."
    
    if item$ ="" then j =L ' end reached
               if item$ ="
    " then LB$ =LB$ +chr$( 10) ' code for CRLF if item$ ="
    " then LB$ =LB$ +chr$( 10) ' code for CRLF, now if j <>L then j =j +1
           case nxtChr$ ="&"                                                           '   we've found an html entity.
                                                                                       '   replace with plain-text equivalents.
               'print " html entity starting with & ";
               select case '   ..............................................................................
                   case mid$( t$, j+1, 5) ="quot;"
                       LB$ =LB$ +chr$( 34): j =j +6                                    '   &guot;    "
                   case mid$( t$, j+1, 3) ="gt;"
                       LB$ =LB$ +">": j =j +4                                          '   >      >
                   case mid$( t$, j+1, 3) ="lt;"
                       LB$ =LB$ +"<": j =j +4                                          '   <      <
                   case right$( mid$( t$, j, 5), 1) =";"
                       v =val( mid$( t$, j +2, 2)): j =j +5                            '   2-digit character-code
                       if v =39 then LB$ =LB$ +chr$( 39) else LB$ =LB$ +chr$( v)       '       eg '  ( 40,41)   '()
                   case right$( mid$( t$, j, 6), 1) =";"                               '   3-digit character-code
                       v =val( mid$( t$, j +2, 3))
                       if v =160 then v =32    'print "Hard space!"                    '   convert   hard- to soft-space.
                       j =j +6: LB$ =LB$ +chr$( v)
               end select  '   ..............................................................................
               'print " and finishing with ;"
           case else  '   not an html entity nor a tag. Use as-is unless it's the final hard-space plus semi-colon..
    
    if mid$( t$, j +1, 5) ="#160;" and mid$( t$, j +5, 6) ="" then j =L else LB$ =LB$ +nxtChr$: j =j +1
       end select  '   _________________________________________________________________________________________________________
       scan
    

    loop until j >= fin -beg -4

    print: print LB$

    open solutions$( R, 0) +".bas" for output as #LB

       #LB LB$;
    

    close #LB

    print print " Done"

    timer 5000, [on2] wait [on2] timer 0

    ' Run with LB. ' *************************************run chr$( 34) +"C:\Program Files\Liberty BASIC v4.04\liberty.exe" +chr$( 34) +" -R E:\" +solutions$( R, 0) +".bas" next R end

       '   **************************************************************
    

    Function DownloadToFile( urlfile$, localfile$)

       open "URLmon" for dll as #url
       calldll #url, "URLDownloadToFileA",_
       0 as long,_         'null
       urlfile$ as ptr,_   'url to download
       localfile$ as ptr,_ 'save file name
       0 as long,_         'reserved, must be 0
       0 as long,_         'callback address, can be 0
       DownloadToFile as ulong  '0=success
       close #url
    

    end function end

    function getHtmlSection$( string$, byref first, last)

       a                    =instr(  string$, "<a href=" +chr$( 34), first)
           if a =0 then getHtmlSection$ ="  Sorry! html link not found": exit function
       b                    =instr(  string$, chr$( 34), a +9)
       getHtmlSection$      =mid$(   string$, a +10, b -a -10)
       first                =b +1
               '   Reset value of "first" so that in the next call to
               '   getHtmlSection$( the next html link can be found
    

    end function

    function GetTempFileName$(prefix$)

       TempPath$=GetTempPath$()
       TempFile$ = space$(256)+chr$(0)
    
       calldll #kernel32, "GetTempFileNameA",_
       TempPath$ as ptr,_  'directory for temp file
       prefix$ as ptr,_    'desired prefix for temp filename
       0 as ulong,_        '0=file created,nonzero=you must create file
       TempFile$ as ptr,_  'string buffer to hold qualified path and filename
       result as ulong     'nonzero=success
    
       'TempFile$ holds complete path and filename info
       GetTempFileName$ = TempFile$
       end function
    

    Function GetTempPath$()

       CallDLL #kernel32, "GetTempPathA",_
       0 as long,_
       _NULL as long,_
       length as long
    
       buf$ = space$(length)
    
       CallDLL #kernel32, "GetTempPathA",_
       length as long,_
       buf$ as ptr,_
       ret as long
    
       GetTempPath$ = buf$
    

    End Function </lang>


    Nim

    Translation of: Go

    This is a translation of the Go solution. The accepted languages are Go, Nim, Perl and Python (version 3 only). There is no tentative to check if the requirements to run a program are fulfilled. If they are not, the execution of the external program will fail but without consequences on the main program.

    On Linux, Perl and Python are installed by default but third party modules may be not. For Go and Nim, an installation is required.

    Note that when running a Nim program a compilation is done and messages are displayed on the terminal before the messages displayed when executing the compiled program. To avoid compiling issues and execution issues, programs are compiled with options -d:release -d:ssl --threads:on. The tow last options are not needed for most programs.

    The present program must be compiled with option -d:ssl

    <lang Nim>import htmlparser, httpclient, os, osproc, re, sets, strutils, xmltree

    const

     Url1 = "http://rosettacode.org/wiki/Category:Programming_Tasks"
     Url2 = "http://rosettacode.org/wiki/Category:Draft_Programming_Tasks"
     Urls = [Url1, Url2]
    

    proc getAllTasks(client: HttpClient): HashSet[string] =

    let regex = re("""
  • <a href="/wiki/(.*?)"""") var matches: array[1, string] var start = 0 for url in Urls: let body = client.getContent(url) # Find all tasks. while true: start = body.find(regex, matches, start) + 1 if start == 0: break if not matches[0].startsWith("Category:"): result.incl matches[0] let client = newHttpClient() let tasks = client.getAllTasks() while true: stdout.write "Enter the exact name of the task: " stdout.flushFile() let task = stdin.readLine().strip().replace(' ', '_') if task notin tasks: echo "Sorry a task with that name doesn't exist." continue let url = "https://rosettacode.org/mw/index.php?title=" & task & "&action=edit" let body = client.getContent(url) var lang: string while true: stdout.write "Enter the language Go/Nim/Perl/Python : " stdout.flushFile() lang = stdin.readLine().strip().toLowerAscii if lang in ["go", "nim", "perl", "python"]: break echo "Sorry that language is not supported." var lang2, lang3, ext: string case lang of "go": lang2 = "Go" lang3 = "(go|Go|GO)" ext = "go" of "nim": lang2 = "Nim" lang3 = "(nim|Nim)" ext = "nim" of "perl": lang2 = "Perl" lang3 = "(perl|Perl)" ext = "pl" of "python": lang2 = "Python" lang3 = "(python|Python)" ext = "py" let fileName = "rc_temp." & ext let regex = re(r"(?s)==\{\{header\|$#\}\}==.*?<lang $#>(.*?)</lang>".format(lang2, lang3)) var matches: array[2, string] let idx = body.find(regex, matches) if idx < 0: echo "No runnable task entry for that language was detected." continue let source = parseHtml(matches[1]).innerText() echo "\nThis is the source code for the first or only runnable program:\n" echo source stdout.write "\nDo you want to run it (y/n)? " stdout.flushFile() var answer = stdin.readLine() if answer in ["y", "Y"]: fileName.writeFile(source) let cmd = case lang of "go": "go run " & fileName of "nim": "nim r -d:release --threads:on -d:ssl " & fileName of "perl": "perl " & fileName of "python": "python " & fileName else: "" discard execCmd cmd removeFile fileName stdout.write "\nDo another one (y/n)? " stdout.flushFile() answer = stdin.readLine() if answer notin ["y", "Y"]: break</lang>

    Phix

    Screenshot here

    --
    -- demo\rosetta\Run_examples.exw
    -- =============================
    --
    -- Full GUI, supports Go, Julia, Phix, Python, and Wren (those I've installed).
    --
    -- Uses much of the same code as Count_Examples.exw, along with 130MB+ of ".raw"
    --  downloaded files, thankfully cached, and not surprising given that program
    --  reports there are 83,050 entires, which averages out at 1662 bytes per...
    -- It can of course take some time to download 1440 tasks at not much under 100K
    --  each, but once in the cache it only takes a few seconds to load everything.
    --
    -- Should you only want to run Phix programs, demo/pGUI/pdemo.exw offers many
    --  other non-rosettacode demo programs, and many but not all 1440 rc tasks.
    --
    --  No attempt has been made for the "Extra credit" (report which tasks failed
    --  to run) or "More credit" (save output and compare with expected) - for the
    --  latter you might want to look at test/terror[X].exw and if I'm having that
    --  much trouble just on Phix with only things that I actually wrote, well...
    --
    -- To do: Entries such as Boids just get "See Boids/Go" with that link not
    --        even being downloaded into rc_cache at all..... Exactly the same o/c 
    --        for both the Julia and Phix entries for Boids, that is not counting
    --        the additional screenshot link on the Phix entry.
    --
    --        The table and codetext are not shrinking as I would like (help?).
    --
    -- sug: Add buttons "Run (F5)", "Copy cmd (F6)", "Edit rcre.xxx"
    --      Save some IupConfig() settings...
    --
    without js -- (obviously this will never ever run in a browser!)
    include pGUI.e
    requires(WINDOWS) -- currently windows-only, unless you can munge the following
    
    -- Without any doubt these will need editing, or perhaps just commenting out
    --  There are no hard-coded lengths and buttons etc are generated from this
    constant gopath = `C:\Program Files\Go\bin\`,
             jspath = getenv("TEMP"), 
          juliapath = `E:\downloads\misc\wren\julia-1.6.2-win64\julia-1.6.2\bin\`,
           phixpath = `C:\Program Files (x86)\Phix\`, 
             pypath = `C:\Program Files (x86)\Python37-32\`,
           wrenpath = `E:\downloads\misc\wren\wren_cli-windows-0.3.0\`,
            gopause = "var s string\nfmt.Printf(\"done\")\nfmt.Scan(&s)\n",
          phixpause = "?\"done\"\n{}=wait_key()\n",
    --        jsrun = {`pw.exe`, `runjs.exw`}
          cliordome = {`wren_cli-0.3.0.exe`,`dome.exe`},
          -- it should be pretty straightforward to comment out any that you don't want,
          -- and/or insert similar entries for other programming languages that you do:
          languages = {{`Go`,         gopath, `go.exe`,    `.go`, `"%s" run "%s"`, gopause},
                       {`JavaScript`, jspath, `open`,    `.html`, `"%s"`,``},
                       {`Julia`,   juliapath, `julia.exe`, `.jl`, `"%s" "%s"`, ``},
                       {`Phix`,     phixpath, `pw.exe`,   `.exw`, `"%s" "%s"`, phixpause},
                       {`Python`,     pypath, `python.exe`,`.py`, `"%s" "%s"`, ``},
                       {`Wren`,     wrenpath,  cliordome,`.wren`, `"%s" "%s"`, ``}},
              langs = vslice(languages,1)
    
    Ihandle langsel, taskfilter, textsearch, table, add_pause, codetext, statusbar, clipboard
    sequence langchks = {},
             radioset = {},
             disabled = repeat(false,length(languages))
    
    constant help_text = """
    Note you will have to edit the language table in the source code with directories of
    executables you have previously installed. The status bar will show a list of any 
    entries that need editing, when the program is first run, and the corresponding 
    checkboxes and radio buttons will be permanently disabled if the executables cannot 
    be found.
    
    Selecting a language via the dropdown and/or the checkboxes filters the list of tasks
    to those with implementations in the selected language(s). You can actually achieve
    much the same effects just by the checkboxes and ignoring the dropdown, or even by 
    sorting the table by column, apart from the full text search described next.
    
    Text entered into the Filter box excludes all task names that do not match. The search
    text option is only enabled when a language has been selected in the main dropdown, and
    will also search all entries for that language for the specified string as well. The
    latter can be a bit slow, hence the main table and the radios under it are deliberately 
    disabled while filtering to avoid strange crashes caused by clicking on things changing
    underneath your feet, such as selecting a table entry that will be removed in about ten
    seconds.
    
    Selecting an entry in the main task table populates the lower half of the screen and
    enables/disables the radio butons appropriately.
    
    Selecting a language via the radio buttons shows the text of any entry or entries found.
    
    When the first line shows "<1 of n>" use left and right arrows to select.
    
    Press F5 to run the code shown, which you can edit as needed though it will not be saved,
    except in an often-overwritten rcre.<ext> in the executable directory. Note that if you
    delete the "<n of m>" then left and right will no longer cycle through them until it has
    been restored, manually or via selecting table entries or clicking radio buttons.
    
    Note that JavaScript runs html in the browser, making no attempt to run anything under
    node.js (not my thing), and Wren automatically switches to dome.exe when it spots an 
    "import dome", as well as checking that all other imports can be found.
    
    Also note that I am assuming you have write permissions to all the bin directories, for
    said rcre.<ext> files.
    
    The add pause checkbox appends a "done", pause to the end of the code (for some only).
    
    Alternatively F6 copies the command to the clipboard so it can be run in a terminal
    (you may need to cd <bin directory> for that to work).
    
    It should not be spectacularly difficult to run this on Linux, he says.
    
    Lastly, this was written with a fully-populated rc_cache (from previously running
    Count_examples.exw) so I have no real handle on how long that might take to re-
    populate, and hopefully pretty obviously, should you leave this running and something 
    else updates rc_cache, then it will all go horribly wrong.
    """
    --DEV/SUG: ^^ I suppose you could always put date/time/size in the main task_table
    --         and then obviously check and reload the individual entries as needed...
    
    function help()
        IupMessage("Run examples",help_text)
        return IUP_IGNORE
    end function
    
    function get_selected_language()
        for i=1 to length(radioset) do
            if IupGetInt(radioset[i],"VALUE") then
                return i
            end if
        end for
        -- oops?
    end function
    
    function run(atom c)
        string text = IupGetAttribute(codetext,"VALUE"), cmd
        if length(text) then
            if text[1]='<' then
                -- remove any `<n of m>` at the start
                integer k = find('>',text)
                string nofm = text[1..k]
                if match(` of `,nofm) then
                    text = trim_head(text[k+1..$])
                end if
            end if
            integer ldx = get_selected_language(), 
                    mode = 8 -- (no result/wait, no redirect)
            sequence {name,d,exe,ext,fmt} = languages[ldx]
            string filename = join_path({d,"rcre"&ext})
            if name="Wren" then
                exe = exe[iff(match(`import "dome"`,text)?2:1)]
                sequence lines = split(text,"\n"),
                       missing = {}
                for i=1 to length(lines) do
                    string li = lines[i]
                    if length(li)>7 and li[1..7]="import " then
                        string lib = trim(li[8..find(' ',li,9)-1],". \"/")
                        lib &= iff(lib="dome"?".exe":".wren")
                        string libpath = join_path({d,lib})
                        if not file_exists(libpath) then
                            missing = append(missing,lib)
                        end if
                    end if
                end for
                if length(missing) then
                    missing = "NOT INSTALLED:"&join(missing,",")
                    IupSetStrAttribute(statusbar,"TITLE",missing)
                    return IUP_IGNORE
                end if
            elsif name="JavaScript" then
                if not match("<html>",text) then
                    IupSetStrAttribute(statusbar,"TITLE","<html> only")
                    return IUP_IGNORE
                end if
                cmd = sprintf(fmt,{filename})
                mode = 4
            end if
            if mode=8 then
                string execname = join_path({d,exe})
                cmd = sprintf(fmt,{execname,filename})
            end if
    -- erm, does not work, but you do have to be in the right directory.
    --      if c=K_F6 and name="Wren" then
    --          cmd = sprintf("cd \"%s\"; %s",{directory,cmd})
    --      end if
            integer fn = open(filename,"w")
            -- you may need to add write permissons for this...
            if fn=-1 then crash("error opening "&filename) end if
            puts(fn,text)
            if IupGetInt(add_pause,"VALUE") then
                -- I suppose you could check that Go imports fmt,
                -- and maybe append this thing much earlier on,
                -- and maybe disable add_pause when length==0.
                sequence pauses = vslice(languages,6)
                puts(fn,pauses[ldx])
            end if
            close(fn)
            -- (clear CF_UNICODETEXT etc first, as per IupClipboard() docs)
            IupSetAttribute(clipboard,"TEXT",NULL)
            IupSetAttribute(clipboard,"TEXT",cmd)
            IupSetStrAttribute(statusbar, "TITLE", cmd)
            if c=K_F5 then
                string prevd = current_dir()
                if not chdir(d) then
                    crash("cannot chdir to "&d)
                end if
                {} = system_exec(cmd,mode) 
                if not chdir(prevd) then
                    crash("cannot chdir back to "&prevd)
                end if
            end if
        end if
        return IUP_IGNORE -- (for key_cb)
    end function
    
    include rosettacode_cache.e -- see Rosetta_Code/Count_examples#Phix
    
    procedure set_title(string title)
        IupSetStrAttribute(statusbar, "TITLE", title)
    end procedure
    show_title = set_title
    
    --  (We use a few '&' here, fairly obviously for everyone's sanity..)
    constant cleanups = {{`<!--<l`&`ang Phix>(phixonline)-->`,`<l`&`ang Phix>`},
                         {`<!--<l`&`ang Phix>(notonline)-->`,`<l`&`ang Phix>`},
                         {`<!--<l`&`ang Phix>-->`,`<l`&`ang Phix>`},
                         {`<!--</l`&`ang>-->`,`</l`&`ang>`},
                         {`<span style="color: #008080;">`,``},
                         {`<span style="color: #000000;">`,``},
                         {`<span style="color: #0000FF;">`,``},
                         {`<span style="color: #7060A8;">`,``},
                         {`<span style="color: #008000;">`,``},
                         {`<span style="color: #004080;">`,``},
                         {`<span style="color: #004600;">`,``},
                         {`<span style="color: #000080;font-style:italic;">`,``},
                         {`</span>`,``},
                         {`&`&`gt;`,`>`},
                         {`&`&`lt;`,`<`}},
             {cleanstrs,cleanreps} = columnize(cleanups)
    
    function cleanup(string s)
        if match(`<!--<l`&`ang Phix>`,s) then
            -- (Phix "manual" syntax colouring inserts a
            --  space at the start of every single line,
            --  otherwise blank lines break code blocks.)
            s = substitute(s,"\n ","\n")
        end if
        return substitute_all(s,cleanstrs,cleanreps)
    end function
    
    sequence task_table -- [[<task_name>,...langYN,"file.raw",starts,finishes]]
    -- eg [["100 doors","Y","Y","Y","Y","Y","100_doors.raw",
    --      {176247,209655,257786,287434,358111},
    --      {177928,210102,262527,288564,358644}],...]
    -- Each entry is length(languages)+4 long, starts and finishes are
    --  nested tables of length(languages) each. In theory the Y/N could
    --  be removed and starts[idx]!=0 relied on instead, except that we
    --  use/need them for the IupTable() form, as next.
    
    sequence data,  -- task_table in IupTable() form, and filtered
             blocks -- for the selected language
    integer blockn, -- <1 of n> handling
            data_idx = 0 -- selected table entry
    
    procedure set_blocks(sequence di, integer i)
        sequence starts = di[$-1],
                 finishes = di[$]
        integer start = starts[i],
               finish = finishes[i]
        string filename = join_path({"rc_cache",di[$-2]})
        string text = trim(get_text(filename))
        -- (above is full text, below is language-only)
        -- (assumes rc_cache not edited since load_tasks)
        text = cleanup(text[start..finish])
        blocks = {}
        start = 1
        while true do
            start = match(`<l`&`ang `,text,start)
            if start=0 then exit end if
            start = find('>',text,start)+1
            if start=1 then ?9/0 end if
            finish = match(`</l`&`ang>`,text,start)
            blocks = append(blocks,trim_head(text[start..finish-1]))
            start = finish+7
        end while
        -- eg "See Boids/Go" cases (flag as un-runnable?)
        if blocks = {} then blocks = {text} end if
    end procedure
    
    function set_codetext(integer n=0)
        if data_idx!=0 then
            string text
            if n=0 then
                sequence di = data[1][data_idx]
                integer ldx = get_selected_language()
                set_blocks(data[1][data_idx],ldx)
                n = 1
            else
                text = IupGetAttribute(codetext,"VALUE")
                if length(text) 
                and text[1]!='<' then   -- manually removed?
                    return IUP_DEFAULT  -- allow manual edits then
                end if
            end if
            blockn = n
            if length(blocks)=1 then
                text = blocks[1]
            else
                text = sprintf("<%d of %d>\n%s",{n,length(blocks),blocks[n]})
            end if
            IupSetAttribute(codetext,"VALUE",text)
        end if
        return IUP_IGNORE -- (for key_cb)
    end function
    
    procedure disable_radios()
        for i=1 to length(radioset) do
            IupSetInt(radioset[i],"ACTIVE",false)
        end for
    end procedure
    
    bool inIdle = false  -- [so we can IupLoopStep()/quit on esc]
    bool bFilter = false -- (once set, load_tasks() performs filtering)
    
    integer ttdx = 1    -- re-entrant filter index
    
    function load_tasks()
        if not bFilter then
            if inIdle then return IUP_DEFAULT end if
            inIdle = true
            -- don't clobber any "NEED EDITING" message:
            wastitle = IupGetAttribute(statusbar, "TITLE")
            if get_file_type("rc_cache")!=FILETYPE_DIRECTORY then
                if not create_directory("rc_cache") then
                    crash("cannot create rc_cache directory")
                end if
            end if
            sequence tasks = sort(dewiki(open_category("Programming_Tasks"))&
                                  dewiki(open_category("Draft_Programming_Tasks")))
            task_table = {}
            atom t1 = time()+0.2
            for i=1 to length(tasks) do
                string ti = tasks[i],
                       url = sprintf("http://rosettacode.org/mw/index.php?title=%s&action=raw",{ti}),
                       contents = open_download(ti&".raw",url,i,length(tasks)),
                       prev = "", curr
                integer count = 0, start = 1, finishk = 0
                sequence found = repeat("N",length(langs)),
                         starts = repeat(0,length(langs)),
                         finishes = repeat(length(contents),length(langs))
                while true do
                    -- Note this must handle (from Animation.raw):
                    -- ==JavaScript + HTML==
                    -- ==JavaScript + SVG==
                    start = match(`=={`&`{he`&`ader|`,contents,start)
                    if start=0 then exit end if
                    integer finish = match(`}`&`}`,contents,start+1)
                    curr = contents[start+11..finish-1]
                    if curr!=prev then
                        if finishk then
                            finishes[finishk] = start-3
                            finishk = 0
                        end if
                        integer k = find(curr,langs)
                        if k then
                            found[k] = "Y"
                            starts[k] = finish+5
                            finishk = k
                        end if
                        count += 1
                    end if
                    prev = curr
                    start += length(`{`&`{he`&`ader|`)
                end while
                if find("Y",found) then
                    assert(find(true,sq_lt(finishes,starts))=0)
                    found = prepend(found,substitute(html_clean(tasks[i]),'_',' '))
                    found = append(found,ti&".raw")
                    found = append(found,starts)
                    found = append(found,finishes)
                    task_table = append(task_table,found)
                end if
                if platform()!=JS             -- (fat chance! - but code consistently)
                and IupLoopStep()=IUP_CLOSE then -- (nb requires that inIdle handling)
                    return IUP_CLOSE
                end if
                if time()>t1 and wastitle="" then
                    IupSetStrAttribute(statusbar, "TITLE", "Processing %d/%d (%.1f%%)\r",
                                                    {i,length(tasks),i/length(tasks)*100})
                    t1 = time()+0.2
                end if
            end for
            curl_cleanup()
            if wastitle="" then
                IupSetStrAttribute(statusbar, "TITLE", "%d tasks loaded",{length(task_table)})
            end if
            ttdx = 1
            bFilter = true
            return IUP_DEFAULT  -- (brb)
        end if
        -- filtering:
        integer ls = IupGetInt(langsel,"VALUE")
        sequence cs = repeat(-1,length(langchks))
        for i=1 to length(langchks) do
            cs[i] = IupGetInt(langchks[i],"VALUE")
        end for
        string tf = IupGetAttribute(taskfilter,"VALUE")
        integer ts = IupGetInt(textsearch,"VALUE"),
                bcount = 0
        if ttdx=1 then data = {} end if
        data_idx = 0
        IupTableClearSelected(table)
        IupSetInt(table,"ACTIVE",false)
        disable_radios()
        for i=ttdx to length(task_table) do
            sequence ti = task_table[i],
                     starts = ti[$-1],
                     finishes = ti[$]
            if ls=1 or (ls>1 and starts[ls-1]!=0) then
                bool bHas = true
                for c=1 to length(langchks) do
                    bHas = (starts[c]!=0 or not cs[c])
                    if not bHas then exit end if
                end for
                if bHas then
                    bool bText = (tf=="")
                    if not bText then
                        bText = match(tf,ti[1],case_sensitive:=false)!=0
                        if not bText and ls>1 and ts then
                            bcount += 1
    --                      if bcount>=10 then exit end if
                            if bcount>=25 then exit end if -- (marginally better)
    --                      if bcount>=50 then exit end if
                            set_blocks(ti,ls-1)
                            for b=1 to length(blocks) do
                                bText = match(tf,blocks[b],case_sensitive:=false)
                                if bText then exit end if
                            end for
                        end if
                    end if  
                    if bText then
                        data = append(data,ti)
                    end if
                end if
            end if
            ttdx += 1
        end for
        string title
        integer res = IUP_DEFAULT,
                ltt = length(task_table)
        if ttdx>ltt then
            title = sprintf("%d/%d tasks filtered",{length(data),ltt})
            data = {data,{}}
            IupTableSetData(table, data)
            IupSetInt(table,"ACTIVE",true)
    --      enable_radios(true) -- no, when user (re-) selects an entry
            res = IUP_IGNORE    -- remove callback
        else
            title = sprintf("filtering %d/%d",{ttdx,ltt})
        end if
        IupSetStrAttribute(statusbar, "TITLE", title)
        return res
    end function
    
    procedure apply_filters()
        ttdx = 1
        IupSetGlobalFunction("IDLE_ACTION", Icallback("load_tasks"))
    end procedure
    
    function valuechanged_cb(Ihandle ih)
        integer v = IupGetInt(ih,"VALUE")
        if ih=langsel then
            bool bActive = v>1 and not disabled[v-1]
            IupSetInt(textsearch,"ACTIVE",bActive)
            if bActive then
                -- mark/unmark and disable/enable
                -- eg if the dropdown is(/was) Go then that checkbox
                --    would be checked and disabled; if I then change
                --    it to Julia, then enable Go and disable Julia, 
                --    and also uncheck Go (why not) and check Julia.
                v -= 1
                integer sanity_count = 0
                for i=1 to length(langchks) do
                    if not disabled[i]
                    and IupGetInt(langchks[i],"ACTIVE")=(i=v) then
                        IupSetInt(langchks[i],"VALUE",(i=v))
                        IupSetInt(langchks[i],"ACTIVE",(i!=v))
                        sanity_count += 1
                    end if
                end for
                if sanity_count>2 then ?9/0 end if -- what?!
            elsif v=1 then
                -- re-enable and uncheck, when "<any>" re-selected.
                for i=1 to length(langchks) do
                    if not disabled[i]
                    and not IupGetInt(langchks[i],"ACTIVE") then
                        IupSetInt(langchks[i],"ACTIVE",true)
                        IupSetInt(langchks[i],"VALUE",false)
                    end if
                end for
            end if
        end if
        apply_filters()
        return IUP_DEFAULT
    end function
    constant cb_valuechanged = Icallback("valuechanged_cb")
    
    function enteritem_cb(Ihandle table, integer lin, col)
        {} = IupTableEnterItem_cb(table,lin,col) -- as per docs
        data_idx = IupTableGetSelected(table)
        if data_idx then
            -- enable/disable radio buttons and if necessary
            -- transfer selected to something which is legal.
            sequence di = data[1][data_idx],
                     starts = di[$-1]
            integer badx = 0, validx = 0
            for i=1 to length(radioset) do
                if not disabled[i] then
                    bool bActive = (starts[i]!=0)
    --              bool bActive = (di[i+1]=="Y") -- (same)
                    IupSetInt(radioset[i],"ACTIVE",bActive)
                    if bActive then
                        if badx!=0 then
                            -- unset that by setting this
                            IupSetInt(radioset[i],"VALUE",1)
                            badx = 0 -- (not really needed)
                        elsif validx=0 then
                            -- unset future by setting this
                            validx = i
                        end if
                    elsif IupGetInt(radioset[i],"VALUE") then
                        if validx!=0 then
                            -- unset by setting prior
                            IupSetInt(radioset[validx],"VALUE",1)
                        else
                            -- set future to unset this
                            badx = i
                        end if
                    end if
                end if
            end for
            {} = set_codetext()
        end if
        return IUP_DEFAULT
    end function
    
    function radiochanged_cb(Ihandle ih)
        if IupGetInt(ih,"VALUE") then -- (ignore the automatic "unsets")
            {} = set_codetext()
        end if
        return IUP_DEFAULT
    end function
    constant cb_radiochanged = Icallback("radiochanged_cb")
    
    function filter_action_cb(Ihandle /*ih*/)
        apply_filters()
        return IUP_DEFAULT
    end function
    constant cb_filter_action = Icallback(routine_id("filter_action_cb"))
    
    function key_cb(Ihandle /*ih*/, atom c)
        if    c=K_F1 then return help()
        elsif c=K_F5 or c=K_F6 then return run(c)
        elsif c=K_LEFT then return set_codetext(max(1,blockn-1))
        elsif c=K_RIGHT then return set_codetext(min(blockn+1,length(blocks)))
        end if
        return iff(c=K_ESC?IUP_CLOSE:IUP_CONTINUE)
    end function
    
    procedure main()
        IupOpen()
        IupSetGlobal("UTF8MODE","YES")
        langsel = IupList("DROPDOWN=YES, 1=<any>, VALUE=1")
        IupSetCallback(langsel,"VALUECHANGED_CB",cb_valuechanged)
        sequence status_msgs = {}
        for i=1 to length(langs) do
            {string lang, string d, sequence cmd} = languages[i]
            IupSetStrAttributeId(langsel,"",i+1,lang)
            Ihandle checkbox = IupToggle(lang),
                    radiobtn = IupToggle(lang)
            bool bOK = true
            if cmd!="open" then
                bOK = (get_file_type(d)==FILETYPE_DIRECTORY)
                if bOK then
                    if string(cmd) then
                        bOK = file_exists(join_path({d,cmd}))
                    else
                        for c=1 to length(cmd) do
                            bOK = file_exists(join_path({d,cmd[c]}))
                            if not bOK then exit end if
                        end for
                    end if
                end if
                if not bOK then
                    disabled[i] = true
                    status_msgs = append(status_msgs,lang)
                end if
            end if
            IupSetInt({checkbox,radiobtn},"ACTIVE",bOK)
            langchks = append(langchks,checkbox)
            radioset = append(radioset,radiobtn)
        end for
        IupSetCallback(langchks,"VALUECHANGED_CB",cb_valuechanged)
        IupSetCallback(radioset,"VALUECHANGED_CB",cb_radiochanged)
        IupSetInt(langsel,"VISIBLEITEMS",length(langs)+2)
        sequence langset = {IupLabel("Language:"),langsel,
                            IupLabel("and:")} & langchks
        codetext = IupMultiLine(`VISIBLELINES=20, VISIBLECOLUMNS=80`)
        IupSetAttributes(codetext,`EXPAND=YES, FONT="Courier, 10"`)
        taskfilter = IupText("EXPAND=HORIZONTAL")
        textsearch = IupToggle("search text","ACTIVE=NO")
        IupSetCallback(taskfilter,"VALUECHANGED_CB",cb_filter_action)
        IupSetCallback(textsearch,"VALUECHANGED_CB",cb_valuechanged)
        add_pause = IupToggle("add pause")
        sequence columns = {{"Task",200,"ALEFT"}}
        for i=1 to length(langs) do
            columns = append(columns,{langs[i],40,"ACENTER"})
        end for
        table = IupTable(columns,{{},{}})
        statusbar = IupLabel("","EXPAND=HORIZONTAL, PADDING=10x5")
        Ihandle hbfilt = IupHbox({IupLabel("Filter:"),taskfilter,textsearch},
                                 "NORMALIZESIZE=VERTICAL"),
                hlangs = IupHbox(langset,"GAP=20, NORMALIZESIZE=VERTICAL"),
                radios = IupRadio(IupHbox(radioset)),
                hradio = IupHbox({radios,IupFill(),add_pause},
                                 "NORMALIZESIZE=VERTICAL"),
                dlg = IupDialog(IupVbox({hlangs,
                                         hbfilt,
                                         table,
                                         hradio,
                                         codetext,
                                         statusbar}),
                                "MARGIN=10x10, GAP=5, SHRINK=YES") 
        IupSetCallback(table,"ENTERITEM_CB",Icallback("enteritem_cb"))
        IupSetAttribute(dlg,"TITLE","Run examples")
        IupSetCallback(dlg,"KEY_CB",Icallback("key_cb"))
        IupSetAttributeHandle(NULL,"PARENTDIALOG",dlg)
        IupSetInt(radioset,"ACTIVE",false)
        for i=1 to length(radioset) do
            if not disabled[i] then
                IupSetInt(radioset[i],"VALUE",1)
                exit
            end if
        end for
        clipboard = IupClipboard()
        IupShow(dlg)
        if length(status_msgs) then
            string msg = "***NEED EDITING***: "&join(status_msgs,",")
            IupSetStrAttribute(statusbar, "TITLE", msg)
        end if
        apply_filters() -- (set load_tasks() as the idle action)
        if platform()!=JS then -- (no chance, but code consistently)
            IupMainLoop()
            IupClose()
        end if
    end procedure
    main()
    

    Raku

    (formerly Perl 6)

    Works with: Rakudo version 2018.03

    This is a fairly comprehensive task code runner. It is set up to work for Raku by default, but has basic configurations to run Perl, Python, Tcl and Go tasks as well. It can be easily tweaked to work with other languages by adding a load-lang('whatever'){} routine similar to the Raku, Perl, Python, Tcl and Go ones. (And ensuring that the appropriate compiler is installed and accessible.) There is so much variation to the task requirements and calling conventions that it would be problematic to make a general purpose, language agnostic code runner so some configuration is necessary to make it work with other languages.

    (Note that there is no dependency download code nor resource hash for Python and Go, though they can run a remarkable number of tasks without.)

    By default, this will download the Raku section of any (every) task that has a Raku example, extract the code blocks and attempt to run them. Many tasks require files or user interaction to proceed, others are not complete runnable code blocks (example code fragments), some tasks run forever. To try to deal with and compensate for this, this implementation can load a %resource hash that will: supply input files where necessary, skip unrunnable code fragments, limit long and/or infinite running blocks, supply user interaction code where possible, and skip blocks where user interaction is unavoidable.

    There are several command line options to control its actions. See the README in the repository for details.

    The complete implementation is too large and cumbersome to post in it's entirety here, only the main task retrieval and execution code is included.

    For the whole ball of wax see the rc-run github repository.

    Run with no parameters to run every implemented task on Rosetta Code. Feed it a task name to only download / run that task. Give it command line switches to adjust its behaviour.

    Note: This is set up to run under Linux. It could be adapted for Windows (or OSX I suppose) fairly easily but I don't have access to those OSs, nor do I care to seek it.

    <lang perl6>use HTTP::UserAgent; use URI::Escape; use JSON::Fast; use Text::Levenshtein::Damerau; use MONKEY-SEE-NO-EVAL;

    say "Version = 2020-03-15T12:15:31";

    sleep 1;

    my %*SUB-MAIN-OPTS = :named-anywhere;

    unit sub MAIN(

       Str $run = ,        #= Task or file name
       Str :$lang = 'raku',  #= Language, default raku - used to load configuration settings
       Int :$skip = 0,       #= Skip # to continue partially into a list
       Bool :f(:$force),     #= Override any task skip parameter in %resource hash
       Bool :l(:$local),     #= Only use code from local cache
       Bool :r(:$remote),    #= Only use code from remote server (refresh local cache)
       Bool :q(:$quiet),     #= Less verbose, don't display source code
       Bool :d(:$deps),      #= Load dependencies
       Bool :p(:$pause),     #= pause after each task
       Bool :b(:$broken),    #= pause after each task which is broken or fails in some way
       Int  :$sleep = 0,     #= sleep for $sleep after each task
       Bool :t(:$timer),     #= save timing data for each task
    

    );

    die 'You can select local or remote, but not both...' if $local && $remote;

      1. INITIALIZATION

    my $client = HTTP::UserAgent.new; my $url = 'http://rosettacode.org/mw';

    my %c = ( # text colors

       code  => "\e[0;92m", # green
       delim => "\e[0;93m", # yellow
       cmd   => "\e[1;96m", # cyan
       bad   => "\e[0;91m", # red
       warn  => "\e[38;2;255;155;0m", # orange
       dep   => "\e[38;2;248;24;148m", # pink
       clr   => "\e[0m",    # clear formatting
    

    );

    my $view = 'xdg-open'; # image viewer, this will open default under Linux my %l = load-lang($lang); # load language parameters my %resource = load-resources($lang); my $get-tasks = True;

    my @tasks;

    run('clear');

      1. FIGURE OUT WHICH TASKS TO RUN

    if $run {

       if $run.IO.e and $run.IO.f {# is it a file?
           @tasks = $run.IO.lines; # yep, treat each line as a task name
       } else {                    # must be a single task name
           @tasks = ($run);        # treat it so
       }
       $get-tasks = False;         # don't need to retrieve task names from web
    

    }

    if $get-tasks { # load tasks from web if cache is not found, older than one day or forced

       if !"%l<dir>.tasks".IO.e or (now - "%l<dir>.tasks".IO.modified) > 86400 or $remote {
           note 'Retrieving task list from site.';
           @tasks = mediawiki-query( # get tasks from web
           $url, 'pages',
           :generator<categorymembers>,
           :gcmtitle("Category:%l<language>"),
           :gcmlimit<350>,
           :rawcontinue(),
           :prop<title>
           )»<title>.grep( * !~~ /^'Category:'/ ).sort;
           "%l<dir>.tasks".IO.spurt: @tasks.sort.join("\n");
       } else {
           note 'Using cached task list.';
           @tasks = "%l<dir>.tasks".IO.slurp.lines; # load tasks from file
       }
    

    }

    my $tfile; if $timer {

       $tfile = open :w, "{$lang}-time.txt";
       $tfile.close;
    

    }

    note "Skipping first $skip tasks..." if $skip; my $redo;

      1. MAIN LOOP

    for @tasks -> $title {

       $redo = False;
       next if $++ < $skip;
       next unless $title ~~ /\S/; # filter blank lines (from files)
       say my $tasknum = $skip + ++$, ")  $title";
    
       my $name = $title.subst(/<-[-0..9A..Za..z]>/, '_', :g);
       my $taskdir = "./rc/%l<dir>/$name";
    
       my $modified = "$taskdir/$name.txt".IO.e ?? "$taskdir/$name.txt".IO.modified !! 0;
    
       my $entry;
       if $remote or !"$taskdir/$name.txt".IO.e or ((now - $modified) > 86400 * 7) {
           my $page = $client.get("{ $url }/index.php?title={ uri-escape $title }&action=raw").content;
    
           uh-oh("Whoops, can't find page: $url/$title :check spelling.\n\n{fuzzy-search($title)}", 'warn')
               and next if $page.elems == 0;
           say "Getting code from: http://rosettacode.org/wiki/{ $title.subst(' ', '_', :g) }#%l<language>";
    
           $entry = $page.comb(rx:i/'==[[:Category:' $(%l<header>) '|' $(%l<header>) ']] [[Category:' $(%l<header>) ']] Property "Implemented in language" (as page type) with input value "' $(%l) '" contains invalid characters or is incomplete and therefore can cause unexpected results during a query or annotation process.==' .+? [<?before \n'=='<-[={]>*'{{header'> || $] /).Str //
             uh-oh("No code found\nMay be bad markup", 'warn');
    
           if $entry ~~ /^^ 'See [[' (.+?) '/' $(%l<language>) / { # no code on main page, check sub page
               $entry = $client.get("{ $url }/index.php?title={ uri-escape $/[0].Str ~ '/' ~ %l<language> }&action=raw").content;
           }
           mkdir $taskdir unless $taskdir.IO.d;
           spurt( "$taskdir/$name.txt", $entry );
       } else {
           if "$taskdir/$name.txt".IO.e {
               $entry = "$taskdir/$name.txt".IO.slurp;
               say "Loading code from: $taskdir/$name.txt";
           } else {
               uh-oh("Task code $taskdir/$name.txt not found, check spelling or run remote.", 'warn');
               next;
           }
       }
    
       my @blocks = $entry.comb: %l<tag>;
    
       unless @blocks {
           uh-oh("No code found\nMay be bad markup", 'warn') unless %resource{"$name"}<skip> ~~ /'ok to skip'/;
           say "Skipping $name: ", %resource{"$name"}<skip>, "\n" if %resource{"$name"}<skip>
       }
    
       for @blocks.kv -> $k, $v {
           my $n = +@blocks == 1 ??  !! $k;
           spurt( "$taskdir/$name$n%l<ext>", $v );
           if %resource{"$name$n"}<skip> && !$force {
               dump-code ("$taskdir/$name$n%l<ext>");
               if %resource{"$name$n"}<skip> ~~ /'broken'/ {
                   uh-oh(%resource{"$name$n"}<skip>, 'bad');
                   pause if $broken;
               } else {
                   say "{%c<warn>}Skipping $name$n: ", %resource{"$name$n"}<skip>, "{%c<clr>}\n";
               }
               next;
           }
           say "\nTesting $name$n";
           run-it($taskdir, "$name$n", $tasknum);
       }
       say  %c<delim>, '=' x 79, %c<clr>;
       redo if $redo;
       sleep $sleep if $sleep;
       pause if $pause;
    

    }

      1. SUBROUTINES

    sub mediawiki-query ($site, $type, *%query) {

       my $url = "$site/api.php?" ~ uri-query-string(
           :action<query>, :format<json>, :formatversion<2>, |%query);
       my $continue = ;
    
       gather loop {
           my $response = $client.get("$url&$continue");
           my $data = from-json($response.content);
           take $_ for $data.<query>.{$type}.values;
           $continue = uri-query-string |($data.<query-continue>{*}».hash.hash or last);
       }
    

    }

    sub run-it ($dir, $code, $tasknum) {

       my $current = $*CWD;
       chdir $dir;
       if %resource{$code}<file> -> $fn {
           copy "$current/rc/resources/{$_}", "./{$_}" for $fn[]
       }
       dump-code ("$code%l<ext>") unless $quiet;
       check-dependencies("$code%l<ext>", $lang) if $deps;
       my @cmd = %resource{$code}<cmd> ?? |%resource{$code}<cmd> !! "%l<exe> $code%l<ext>\n";
       if $timer {
           $tfile = open :a, "{$current}/{$lang}-time.txt";
       }
       my $time = 'NA: not run or killed before completion';
       for @cmd -> $cmd {
           say "\nCommand line: {%c<cmd>}$cmd",%c<clr>;
           if $timer { $tfile.say: "Command line: $cmd".chomp }
           my $start = now;
           try shell $cmd;
           $time = (now - $start).round(.001);
           CATCH {
               when /'exit code: 137'/ { }
               default {
                   .resume unless $broken;
                   uh-oh($_, 'bad');
                   if %resource{$code}<fail-by-design> {
                       say %c<warn>, 'Fails by design, (or at least, it\'s not unexpected).', %c<clr>;
                   } else {
                       if pause.lc eq 'r' {
                          unlink "$code.txt";
                          $redo = True;
                       }
                   }
                }
           }
       if $timer { $tfile.say("#$tasknum - Wallclock seconds: $time\n") }
       }
       chdir $current;
       say "\nDone task #$tasknum: $code - wallclock seconds: $time\e[?25h";
       $tfile.close if $timer;
    

    }

    sub pause {

       prompt "Press enter to procede:> ";
       # or
       # sleep 5;
    

    }

    sub dump-code ($fn) {

       say "\n", %c<delim>, ('vvvvvvvv' xx 7).join(' CODE '), %c<clr>, "\n", %c;
       print $fn.IO.slurp;
       say %c<clr>,"\n\n",%c<delim>,('^^^^^^^^' xx 7).join(' CODE '),%c<clr>;
    

    }

    sub uri-query-string (*%fields) { %fields.map({ "{.key}={uri-escape .value}" }).join('&') }

    sub clear { "\r" ~ ' ' x 100 ~ "\r" }

    sub uh-oh ($err, $class='warn') { put %c{$class}, "{'#' x 79}\n\n $err \n\n{'#' x 79}", %c<clr> }

    sub fuzzy-search ($title) {

       my @tasknames;
       if "%l<dir>.tasks".IO.e {
           @tasknames = "%l<dir>.tasks".IO.slurp.lines;
       }
       return  unless @tasknames.elems;
       " Did you perhaps mean:\n\n\t" ~
       @tasknames.grep( {.lc.contains($title.lc) or dld($_, $title) < (5 min $title.chars)} ).join("\n\t");
    

    } # Damerau Levenshtein distance ^^^

    multi check-dependencies ($fn, 'raku') {

       my @use = $fn.IO.slurp.comb(/<?after ^^ \h* 'use '> \N+? <?before \h* ';'>/);
       if +@use {
           say %c<dep>, 'Checking dependencies...', %c<clr>;
           for @use -> $module {
               if $module eq any('v6', 'v6.c', 'v6.d', 'nqp', 'NativeCall', 'Test') or $module.contains('MONKEY')
                 or $module.contains('experimental') or $module.starts-with('lib') or $module.contains('from<Perl5>') {
                   print %c<dep>;
                   say 'ok, no installation necessary: ', $module;
                   print %c<clr>;
                   next;
               }
               my $installed = $*REPO.resolve(CompUnit::DependencySpecification.new(:short-name($module)));
               my @mods = $module;
               if './../../../raku-modules.txt'.IO.e {
                   my $fh = open( './../../../perl6-modules.txt', :r ) or die $fh;
                   @mods.append: $fh.lines;
                   $fh.close;
               }
               my $fh = open( './../../../raku-modules.txt', :w ) or die $fh;
               $fh.spurt: @mods.Bag.keys.sort.join: "\n";
               $fh.close;
               print %c<dep>;
               if $installed {
                   say 'ok, installed: ', $module
               } else {
                   say 'not installed: ', $module;
                   shell("zef install $module");
               }
               print %c<clr>;
           }
       }
    

    }

    multi check-dependencies ($fn, 'perl') {

       my @use = $fn.IO.slurp.comb(/<?after ^^ \h* 'use '> \N+? <?before \h* ';'>/);
       if +@use {
           for @use -> $module {
               next if $module eq $module.lc;
               next if $module.starts-with(any('constant','bignum'));
               my $installed = shell( "%l<exe> -e 'eval \"use {$module}\"; exit 1 if \$@'" );
               print %c<dep>;
               if $installed {
                   say 'ok:            ', $module
               } else {
                   say 'not installed: ', $module;
                   try shell("sudo cpan $module");
               }
               print %c<clr>;
           }
       }
    

    }

    multi check-dependencies ($fn, $unknown) {

       note "Sorry, don't know how to handle dependencies for $unknown language."
    

    };

    multi load-lang ('raku') { ( # Language specific variables. Adjust to suit.

       language => 'Raku',  # language category name
       exe      => 'raku',  # executable name to run raku in a shell
       ext      => '.raku', # file extension for raku code (optional, but nice to have)
       dir      => 'raku',  # directory to save tasks to
       header   => 'Raku',  # header text (==Raku==)
       # tags marking blocks of code - spaced out to placate wiki formatter
       # and to avoid getting tripped up when trying to run _this_ task.
       # note that this tag only selects the syntax highlighting, continue to
       # use 'perl6' until 'raku' as added on the site.
       tag => rx/<?after '<lang ' 'perl6' '>' > .*? <?before '</' 'lang>'>/,
    

    ) }

    multi load-lang ('perl') { (

       language => 'Perl',
       exe      => 'perl',
       ext      => '.pl',
       dir      => 'perl',
       header   => 'Perl',
       tag => rx/:i <?after '<lang ' 'perl' '>' > .*? <?before '</' 'lang>'>/,
    

    ) }

    multi load-lang ('python') { (

       language => 'Python',
       exe      => 'python',
       ext      => '.py',
       dir      => 'python',
       header   => 'Python',
       tag => rx/:i <?after '<lang ' 'python' '>' > .*? <?before '</' 'lang>'>/,
    

    ) }

    multi load-lang ('go') { (

       language => 'Go',
       exe      => 'go run',
       ext      => '.go',
       dir      => 'go',
       header   => 'Go',
       tag => rx/:i <?after '<lang ' 'go' '>' > .*? <?before '</' 'lang>'>/,
    

    ) }

    multi load-lang ('tcl') { (

       language => 'Tcl',
       exe      => 'tclsh',
       ext      => '.tcl',
       dir      => 'tcl',
       header   => 'Tcl',
       tag => rx/:i <?after '<lang ' 'tcl' '>' > .*? <?before '</' 'lang>'>/,
    

    ) }

    multi load-lang ($unknown) { die "Sorry, don't know how to handle $unknown language." };

    multi load-resources ($unknown) { () };</lang>

    Output:
    with command line
    raku RC-run.p6 -q "Determine if a string is numeric"
    Retrieving tasks
    1)  Determine if a string is numeric
    Getting code from: http://rosettacode.org/wiki/Determine_if_a_string_is_numeric#Raku
    
    Testing Determine_if_a_string_is_numeric
    
    Command line: raku Determine_if_a_string_is_numeric.p6
    
                   Coerce     Don't coerce
        String   whitespace    whitespace
           <1>      True         True
         <1.2>      True         True
       <1.2.3>     False        False
          <-6>      True         True
         <1/2>      True         True
         <12e>     False        False
         <B17>     False        False
     <1.3e+12>      True         True
      <1.3e12>      True         True
     <-2.6e-3>      True         True
        <zero>     False        False
          <0x>     False        False
       <0xA10>      True         True
      <0b1001>      True         True
        <0o16>      True         True
        <0o18>     False        False
        <2+5i>      True         True
        <True>     False        False
       <False>     False        False
         <Inf>      True         True
         <NaN>      True         True
     <0x10.50>      True         True
       <0b102>     False        False
      <0o_5_3>      True         True
          <௫௯>      True         True
      <  12  >      True         True
       <1 1 1>     False        False
            <>      True        False
           < >      True        False
    
    Done task #1: Determine_if_a_string_is_numeric - wallclock seconds: 0.171
    ===============================================================================

    Or, if the full %resource hash is loaded it will automatically feed input parameters to tasks that require them:
    raku RC-run.p6 Lucky_and_even_lucky_numbers -q

    Retrieving tasks
    1 Lucky_and_even_lucky_numbers
    Getting code from: http://rosettacode.org/wiki/Lucky_and_even_lucky_numbers#Raku
    
    Testing Lucky_and_even_lucky_numbers
    
    Command line: raku Lucky_and_even_lucky_numbers.p6 20 , lucky
    
    79
    
    Command line: raku Lucky_and_even_lucky_numbers.p6 1 20
    
    (1 3 7 9 13 15 21 25 31 33 37 43 49 51 63 67 69 73 75 79)
    
    Command line: raku Lucky_and_even_lucky_numbers.p6 1 20 evenlucky
    
    (2 4 6 10 12 18 20 22 26 34 36 42 44 50 52 54 58 68 70 76)
    
    Command line: raku Lucky_and_even_lucky_numbers.p6 6000 -6100
    
    (6009 6019 6031 6049 6055 6061 6079 6093)
    
    Command line: raku Lucky_and_even_lucky_numbers.p6 6000 -6100 evenlucky
    
    (6018 6020 6022 6026 6036 6038 6050 6058 6074 6090 6092)
    
    Done Lucky_and_even_lucky_numbers
    ===============================================================================

    Try running a Go task - Command line: raku RC-run.p6 -q --lang=go "Determine if a string is numeric"

    Finds two task entries, downloads both and runs each:

    1)  Determine if a string is numeric
    Getting code from: http://rosettacode.org/wiki/Determine_if_a_string_is_numeric#Go
    
    Testing Determine_if_a_string_is_numeric0
    
    Command line: go run Determine_if_a_string_is_numeric0.go
    
    Are these strings numeric?
         1 -> true
      3.14 -> true
      -100 -> true
       1e2 -> true
       NaN -> true
      rose -> false
    
    Done task #1: Determine_if_a_string_is_numeric0 - wallclock seconds: 0.16
    
    Testing Determine_if_a_string_is_numeric1
    
    Command line: go run Determine_if_a_string_is_numeric1.go
    
    Are these strings integers?
        1 -> true
      one -> false
    
    Done task #1: Determine_if_a_string_is_numeric1 - wallclock seconds: 0.147
    ===============================================================================

    Run BASIC

    <lang runbasic>bf$ = "" a$ = httpGet$("http://rosettacode.org/wiki/Category:Run_BASIC") ' get RB tasks from [RC] a1$ = word$(a$,2,"Pages in category ""Run BASIC")

    a1$ = word$(a1$,1,"")

    i = 2

    b$ = word$(a1$,i,"
  • <a href=""/wiki/") ' ' Create a drop down window for selection of a task ' html bf$;"
    " html "" html "
    Tasks
    Task"

    html "<select size=10 id='runProg' name='runProg'>" while b$ <> ""

     b$	= left$(b$,instr(b$,"""")-1)
     b$	= strRep$(b$,"%2B","+")
     b$	= strRep$(b$,"%27","'")
     html "<option>"+b$+"</option>"
     i 	= i + 1
    
    b$ = word$(a1$,i,"
  • <a href=""/wiki/") wend html "</select>
  • "

    ' BUTTON options to Run It or Exit

       button #run, "Run It", [runProg]
       button #ex, "Exit", [quit]
    
    html "
    " ' close the drop down table and wait

    wait

    [runProg] progName$ = #request get$("runProg") print progName$ a$ = httpGet$("http://rosettacode.org/wiki/"+progName$)

    i = instr(a$,"<a href=""#Run_BASIC"">")

    a$ = mid$(a$,i-6,6) a$ = word$(a$,2,"-") a$ = word$(a$,1,"""") cls ' clear screen 'print a$ ' this is the program number used in the [RC] editor

    a$ = httpGet$("http://rosettacode.org/mw/index.php?title="+progName$+"&action=edit&section="+a$)

    a$ = word$(a$,2,"{header|Run BASIC}") i = instr(a$,">") a$ = mid$(a$,i+1) i = instr(a$,"/lang>") a$ = left$(a$,i-5) a$ = strRep$(a$,"<","<") ' this is the good program code ' place the code in the rb$ file rb$ = DefaultDir$ + "\projects\a_project\rcCode.bas" ' RC program open rb$ for output as #f print #f,a$ close #f

    print "================== Run Basic Solution ===========================" run rb$,#handle ' point RunBasic to the file with the program render #handle ' render the runned code [quit] ' that's it folks end

    ' -------------------------------- ' string replace rep str with ' -------------------------------- FUNCTION strRep$(str$,rep$,with$) ln = len(rep$) ln1 = ln - 1 i = 1 while i <= len(str$)

       if mid$(str$,i,ln) = rep$ then
           strRep$ = strRep$ + with$
           i = i + ln1
       else
           strRep$ = strRep$ + mid$(str$,i,1)
       end if
    

    i = i + 1 WEND END FUNCTION</lang>

    Tcl

    This code only includes support for running Tcl task solutions, but it can download any language's; it assumes that the first <lang…> is sufficient when it comes to task extraction (definitely not true universally, but mostly good enough).

    Library: Tcllib (Package: uri)

    <lang tcl># Code to download task contents from find-bare-lang-tags task package require Tcl 8.5 package require http package require uri

    proc getUrlWithRedirect {base args} {

       set url $base?[http::formatQuery {*}$args]
       while 1 {
    

    set t [http::geturl $url] if {[http::status $t] ne "ok"} { error "Oops: url=$url\nstatus=$s\nhttp code=[http::code $token]" } if {[string match 2?? [http::ncode $t]]} { return $t } # OK, but not 200? Must be a redirect... set url [uri::resolve $url [dict get [http::meta $t] Location]] http::cleanup $t

       }
    

    } proc getTaskContent {task} {

       set token [getUrlWithRedirect http://rosettacode.org/mw/index.php \
    

    title $task action raw]

       set content [http::data $token]
       http::cleanup $token
       return $content
    

    }

    1. Code to extract the first <lang> section for a language

    proc getTaskCodeForLanguage {task language} {

       set content [getTaskContent $task]
       set startRE {==\s*\{\{header\|@LANG@(?:\|[^{}]+)?\}\}\s*==}
       set startRE [string map [list @LANG@ $language] $startRE]
       if {![regexp -indices $startRE $content start]} {
    

    error "$language does not implement task \"$task\""

       }
       if {![regexp -indices -start [lindex $start end] \
    

    "==\\s*\\\{\\\{header" $content end]} { set end {end end}

       }
       set content [string range $content [lindex $start 1] [lindex $end 0]]
       # Extended format RE used to allow embedding within _this_ task's <lang>!
       if {![regexp {(?x)<lang .*?>(.*?)</ lang>} $content -> solution]} {
    

    error "$language solution of task \"$task\" has no useful code"

       }
       return "$solution\n"
    

    }

    1. How to download and run a Tcl task

    proc runTclTaskForLanguage {task} {

       puts "Fetching task solution..."
       set solution [getTaskCodeForLanguage $task Tcl]
       set filename rcsoln_[string map {/ _ " " _} $task].tcl
       set f [open $filename w]
       puts $f $solution
       close $f
       puts "Executing task solution with: tclsh $filename"
       exec [info nameofexecutable] $filename <@stdin >@stdout 2>@stderr
    

    } runTclTaskForLanguage {*}$argv</lang>

    UNIX Shell

    See C1R Implementation for an incomplete implementation. (only supports C)

    Wren

    Translation of: Go
    Library: WrenGo
    Library: Wren-set
    Library: Wren-pattern
    Library: Wren-str

    An embedded program with a Go host as Wren-cli currently has no way to download web pages.

    This is designed to work for Go, Perl, Python and Wren itself though see the remarks in the Go entry about using the first three languages and other more general points.

    As far as Wren is concerned, in addition to Wren-cli programs, this should be able to run DOME, Wren-gmp, Wren-sql and Wren-i64 programs as it is easy to detect when these are being used and the filename is always given as a command line argument. All Wren modules are assumed to be present in the current working directory.

    However, no attempt has been made - at least for now - to run other embedded programs (which can be identified by the presence of the 'foreign' keyword) due to a number of technical difficulties in doing so. <lang ecmascript>/* rc_run_examples.wren */

    import "./set" for Set import "./pattern" for Pattern import "./str" for Str

    class Http {

       // gets the response body, copies it to a string and automatically closes it
       foreign static getBodyText(url)
    

    }

    class Html {

       foreign static unescapeString(s)
    

    }

    class Stdin {

       foreign static readLine()
    

    }

    class IOUtil {

       foreign static writeFile(fileName, text)
    
       foreign static removeFile(fileName)
    

    }

    class Exec {

       foreign static run2(lang, fileName)
       foreign static run3(lang, param, fileName)
    

    }

    var getAllTasks = Fn.new {

    var p = Pattern.new("
  • <a href/=\"//wiki//[+0^\"]\"") var url1 = "http://rosettacode.org/wiki/Category:Programming_Tasks" var url2 = "http://rosettacode.org/wiki/Category:Draft_Programming_Tasks" var urls = [url1, url2] var tasks = Set.new() for (url in urls) { var body = Http.getBodyText(url) // find all tasks var matches = p.findAll(body) for (match in matches) { // exclude any 'category' references var task = match.capsText[0] if (!task.startsWith("Category:")) tasks.add(task) } } return tasks } var tasks = getAllTasks.call() var langs = ["go", "perl", "python", "wren"] while (true) { System.write("Enter the exact name of the task : ") var task = Stdin.readLine().trim().replace(" ", "_") if (!tasks.contains(task)) { System.print("Sorry a task with that name doesn't exist.") } else { var url = "https://rosettacode.org/mw/index.php?title=" + task + "&action=edit" var page = Http.getBodyText(url).replace("<", "<") var lang while (true) { System.write("Enter the language Go/Perl/Python/Wren : ") lang = Str.lower(Stdin.readLine().trim()) if (langs.contains(lang)) break System.print("Sorry that language is not supported.") } var lang2 var lang3 var ext if (lang == "go") { lang2 = "Go" lang3 = "[go|Go|GO]" ext = "go" } else if (lang == "perl") { lang2 = "Perl" lang3 = "[perl|Perl]" ext = "pl" } else if (lang == "python") { lang2 = "Python" lang3 = "[python|Python]" ext = "py" } else if (lang == "wren") { lang2 = "Wren" lang3 = "[ecmascript|Ecmascript]" ext = "wren" } var fileName = "rc_temp." + ext var p1 = Pattern.new("/=/=Template:Header//=/=") var p2 = Pattern.new("<lang %(lang3)>") var p3 = Pattern.new("<//lang>") var s = p1.split(page, 1, 0) if (s.count > 1) s = p2.split(s[1], 1, 0) var preamble = s[0] if (s.count > 1) s = p3.split(s[1], 1, 0) if (s.count == 1) { System.print("No runnable task entry for that language was detected.") } else if (lang == "wren" && s[0].contains(" foreign ")) { System.print("This is an embedded script which cannot be run automatically at present.") } else { var source = Html.unescapeString(s[0]) System.print("\nThis is the source code for the first or only runnable program:\n") System.print(source) System.write("\nDo you want to run it y/n : ") var yn = Stdin.readLine().trim()[0] // note that the executable names may differ from the ones I'm currently using if (yn == "y" || yn == "Y") { IOUtil.writeFile(fileName, source) if (lang == "go") { Exec.run3("go", "run", fileName) } else if (lang == "perl") { Exec.run2("perl", fileName) } else if (lang == "python") { Exec.run2("python3", fileName) } else if (lang == "wren") { if (preamble.contains("
    Library: DOME
    ")) {
                           Exec.run2("dome171", fileName)
    
    } else if (preamble.contains("
    Library: Wren-gmp
    ")) {
                           Exec.run2("./wren-gmp", fileName)
    
    } else if (preamble.contains("
    Library: Wren-sql
    ")) {
                           Exec.run2("./wren-sql", fileName)
    
    } else if (preamble.contains("
    Library: Wren-i64
    ")) {
                           Exec.run2("./wren-i64", fileName)
                       } else { // Wren-cli
                           Exec.run2("wren4", fileName)
                       }
                   }
                   IOUtil.removeFile(fileName)
               }
           }
           System.write("\nDo another one y/n : ")
           var yn = Stdin.readLine().trim()[0]
           if (yn != "y" && yn != "Y") return
       }
    

    }</lang> We now embed this script in the following Go program and run it: <lang go>/* go run rc_run_examples.go */

    package main

    import (

       "bufio"
       wren "github.com/crazyinfin8/WrenGo"
       "html"
       "io/ioutil"
       "log"
       "net/http"
       "os"
       "os/exec"
       "strings"
    

    )

    type any = interface{}

    var in = bufio.NewReader(os.Stdin)

    func check(err error) {

       if err != nil {
           log.Fatal(err)
       }
    

    }

    func getBodyText(vm *wren.VM, parameters []any) (any, error) {

       url := parameters[1].(string)
       resp, _ := http.Get(url)
       body, _ := ioutil.ReadAll(resp.Body)
       resp.Body.Close()
       return string(body), nil
    

    }

    func unescapeString(vm *wren.VM, parameters []any) (any, error) {

       s := parameters[1].(string)
       return html.UnescapeString(s), nil
    

    }

    func readLine(vm *wren.VM, parameters []any) (any, error) {

       l, err := in.ReadString('\n')
       check(err)
       return l, nil
    

    }

    func writeFile(vm *wren.VM, parameters []any) (any, error) {

       fileName := parameters[1].(string)
       text := parameters[2].(string)
       err := ioutil.WriteFile(fileName, []byte(text), 0666)
       check(err)
       return nil, nil
    

    }

    func removeFile(vm *wren.VM, parameters []any) (any, error) {

       fileName := parameters[1].(string)
       err := os.Remove(fileName)
       check(err)
       return nil, nil
    

    }

    func run2(vm *wren.VM, parameters []any) (any, error) {

       lang := parameters[1].(string)
       fileName := parameters[2].(string)
       cmd := exec.Command(lang, fileName)
       cmd.Stdout = os.Stdout
       cmd.Stderr = os.Stderr
       cmd.Run()
       return nil, nil
    

    }

    func run3(vm *wren.VM, parameters []any) (any, error) {

       lang := parameters[1].(string)
       param := parameters[2].(string)
       fileName := parameters[3].(string)
       cmd := exec.Command(lang, param, fileName)
       cmd.Stdout = os.Stdout
       cmd.Stderr = os.Stderr
       cmd.Run()
       return nil, nil
    

    }

    func moduleFn(vm *wren.VM, name string) (string, bool) {

       if name != "meta" && name != "random" && !strings.HasSuffix(name, ".wren") {
           name += ".wren"
       }
       return wren.DefaultModuleLoader(vm, name)
    

    }

    func main() {

       cfg := wren.NewConfig()
       cfg.LoadModuleFn = moduleFn
       vm := cfg.NewVM()
    
       httpMethodMap := wren.MethodMap{
           "static getBodyText(_)": getBodyText,
       }
    
       htmlMethodMap := wren.MethodMap{
           "static unescapeString(_)": unescapeString,
       }
    
       stdinMethodMap := wren.MethodMap{
           "static readLine()": readLine,
       }
    
       ioutilMethodMap := wren.MethodMap{
           "static writeFile(_,_)": writeFile,
           "static removeFile(_)":  removeFile,
       }
    
       execMethodMap := wren.MethodMap{
           "static run2(_,_)":   run2,
           "static run3(_,_,_)": run3,
       }
    
       classMap := wren.ClassMap{
           "Http":   wren.NewClass(nil, nil, httpMethodMap),
           "Html":   wren.NewClass(nil, nil, htmlMethodMap),
           "Stdin":  wren.NewClass(nil, nil, stdinMethodMap),
           "IOUtil": wren.NewClass(nil, nil, ioutilMethodMap),
           "Exec":   wren.NewClass(nil, nil, execMethodMap),
       }
    
       module := wren.NewModule(classMap)
       fileName := "rc_run_examples.wren"
       vm.SetModule(fileName, module)
       vm.InterpretFile(fileName)
       vm.Free()
    

    }</lang>

    Output:

    Sample run:

    Enter the exact name of the task : Palindrome dates
    Enter the language Go/Perl/Python/Wren : Go
    
    This is the source code for the first or only runnable program:
    
    package main
    
    import (
        "fmt"
        "time"
    )
    
    func reverse(s string) string {
        chars := []rune(s)
        for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 {
            chars[i], chars[j] = chars[j], chars[i]
        }
        return string(chars)
    }
    
    func main() {
        const (
            layout  = "20060102"
            layout2 = "2006-01-02"
        )
        fmt.Println("The next 15 palindromic dates in yyyymmdd format after 20200202 are:")
        date := time.Date(2020, 2, 2, 0, 0, 0, 0, time.UTC)
        count := 0
        for count < 15 {
            date = date.AddDate(0, 0, 1)
            s := date.Format(layout)
            r := reverse(s)
            if r == s {
                fmt.Println(date.Format(layout2))
                count++
            }
        }
    }
    
    Do you want to run it y/n : y
    The next 15 palindromic dates in yyyymmdd format after 20200202 are:
    2021-12-02
    2030-03-02
    2040-04-02
    2050-05-02
    2060-06-02
    2070-07-02
    2080-08-02
    2090-09-02
    2101-10-12
    2110-01-12
    2111-11-12
    2120-02-12
    2121-12-12
    2130-03-12
    2140-04-12
    
    Do another one y/n : y
    Enter the exact name of the task : Palindrome dates
    Enter the language Go/Perl/Python/Wren : Perl
    
    This is the source code for the first or only runnable program:
    
    use Time::Piece;
    my $d = Time::Piece->strptime("2020-02-02", "%Y-%m-%d");
    
    for (my $k = 1 ; $k <= 15 ; $d += Time::Piece::ONE_DAY) {
        my $s = $d->strftime("%Y%m%d");
        if ($s eq reverse($s) and ++$k) {
            print $d->strftime("%Y-%m-%d\n");
        }
    }
    
    Do you want to run it y/n : y
    2020-02-02
    2021-12-02
    2030-03-02
    2040-04-02
    2050-05-02
    2060-06-02
    2070-07-02
    2080-08-02
    2090-09-02
    2101-10-12
    2110-01-12
    2111-11-12
    2120-02-12
    2121-12-12
    2130-03-12
    
    Do another one y/n : y
    Enter the exact name of the task : Palindrome dates
    Enter the language Go/Perl/Python/Wren : Python
    
    This is the source code for the first or only runnable program:
    
    '''Palindrome dates'''
    
    from datetime import datetime
    from itertools import chain
    
    
    # palinDay :: Int -> [ISO Date]
    def palinDay(y):
        '''A possibly empty list containing the palindromic
           date for the given year, if such a date exists.
        '''
        s = str(y)
        r = s[::-1]
        iso = '-'.join([s, r[0:2], r[2:]])
        try:
            datetime.strptime(iso, '%Y-%m-%d')
            return [iso]
        except ValueError:
            return []
    
    
    # --------------------------TEST---------------------------
    # main :: IO ()
    def main():
        '''Count and samples of palindromic dates [2021..9999]
        '''
        palinDates = list(chain.from_iterable(
            map(palinDay, range(2021, 10000))
        ))
        for x in [
                'Count of palindromic dates [2021..9999]:',
                len(palinDates),
                '\nFirst 15:',
                '\n'.join(palinDates[0:15]),
                '\nLast 15:',
                '\n'.join(palinDates[-15:])
        ]:
            print(x)
    
    
    # MAIN ---
    if __name__ == '__main__':
        main()
    
    Do you want to run it y/n : y
    Count of palindromic dates [2021..9999]:
    284
    
    First 15:
    2021-12-02
    2030-03-02
    2040-04-02
    2050-05-02
    2060-06-02
    2070-07-02
    2080-08-02
    2090-09-02
    2101-10-12
    2110-01-12
    2111-11-12
    2120-02-12
    2121-12-12
    2130-03-12
    2140-04-12
    
    Last 15:
    9170-07-19
    9180-08-19
    9190-09-19
    9201-10-29
    9210-01-29
    9211-11-29
    9220-02-29
    9221-12-29
    9230-03-29
    9240-04-29
    9250-05-29
    9260-06-29
    9270-07-29
    9280-08-29
    9290-09-29
    
    Do another one y/n : y
    Enter the exact name of the task : Palindrome dates
    Enter the language Go/Perl/Python/Wren : Wren
    
    This is the source code for the first or only runnable program:
    
    import "/fmt" for Fmt
    import "/date" for Date
    
    var isPalDate = Fn.new { |date|
        date = date.format(Date.rawDate)
        return date == date[-1..0]
    }
    
    Date.default = Date.isoDate
    System.print("The next 15 palindromic dates in yyyy-mm-dd format after 2020-02-02 are:")
    var date = Date.new(2020, 2, 2)
    var count = 0
    while (count < 15) {
        date = date.addDays(1)
        if (isPalDate.call(date)) {
            System.print(date)
            count = count + 1
        }
    }
    
    Do you want to run it y/n : y
    The next 15 palindromic dates in yyyy-mm-dd format after 2020-02-02 are:
    2021-12-02
    2030-03-02
    2040-04-02
    2050-05-02
    2060-06-02
    2070-07-02
    2080-08-02
    2090-09-02
    2101-10-12
    2110-01-12
    2111-11-12
    2120-02-12
    2121-12-12
    2130-03-12
    2140-04-12
    
    Do another one y/n : y
    Enter the exact name of the task : Deceptive numbers
    Enter the language Go/Perl/Python/Wren : Wren
    
    This is the source code for the first or only runnable program:
    
    /* deceptive_numbers.wren */
    
    import "./gmp" for Mpz
    import "./math" for Int
    
    var count = 0
    var limit = 25
    var n = 17
    var repunit = Mpz.from(1111111111111111)
    var deceptive = []
    while (count < limit) {
        if (!Int.isPrime(n) && n % 3 != 0 && n % 5 != 0) {
            if (repunit.isDivisibleUi(n)) {
                deceptive.add(n)
                count = count + 1
            }
        }
        n = n + 2
        repunit.mul(100).add(11)
    }
    System.print("The first %(limit) deceptive numbers are:")
    System.print(deceptive)
    
    Do you want to run it y/n : y
    The first 25 deceptive numbers are:
    [91, 259, 451, 481, 703, 1729, 2821, 2981, 3367, 4141, 4187, 5461, 6533, 6541, 6601, 7471, 7777, 8149, 8401, 8911, 10001, 11111, 12403, 13981, 14701]
    
    Do another one y/n : n