import httpclient, json, re, strformat, strutils, algorithm

const
  LangSite = "http://www.rosettacode.org/mw/api.php?action=query&list=categorymembers&cmtitle=Category:Programming_Languages&cmlimit=500&format=json"
  CatSite = "http://www.rosettacode.org/mw/index.php?title=Special:Categories&limit=5000"
let regex = re"title=""Category:(.*?)"">.+?</a>.*\((.*) members\)"

type Rank = tuple[lang: string, count: int]

proc cmp(a, b: Rank): int =
  result = cmp(b.count, a.count)
  if result == 0: result = cmp(a.lang, b.lang)

proc add(langs: var seq[string]; fromJson: JsonNode) =
  for entry in fromJson{"query", "categorymembers"}:
    langs.add entry["title"].getStr.split("Category:")[1]

var client = newHttpClient()
var langs: seq[string]
var url = LangSite
while true:
  let response = client.get(url)
  if response.status != $Http200: break
  let fromJson = response.body.parseJson()
  langs.add fromJson
  if "continue" notin fromJson: break
  let cmcont = fromJson{"continue", "cmcontinue"}.getStr
  let cont = fromJson{"continue", "continue"}.getStr
  url = LangSite & fmt"&cmcontinue={cmcont}&continue={cont}"

var ranks: seq[Rank]
for line in client.getContent(CatSite).findAll(regex):
  let lang = line.replacef(regex, "$1")
  if lang in langs:
    let count = parseInt(line.replacef(regex, "$2").replace(",", "").strip())
    ranks.add (lang, count)

ranks.sort(cmp)
for i, rank in ranks:
  echo &"{i + 1:3} {rank.count:4} - {rank.lang}"


use HTTP;
use RegEx;
use XML;
use Collection;

class RosettaRank {
  function : Main(args : String[]) ~ Nil {
    langs_xml := "";
    client := HttpClient->New();
    in := client->Get("http://rosettacode.org/mw/api.php?action=query&list=categorymembers&cmtitle=Category:Programming_Languages&cmlimit=5000&format=xml");
    each(i : in) {
      langs_xml += in->Get(i)->As(String);
    };

    langs := StringSet->New();
    parser := XmlParser->New(langs_xml);
    if(parser->Parse()) {
      # get first item
      results := parser->FindElements("/api/query/categorymembers/cm");
      each(i : results) {
        element := results->Get(i)->As(XmlElement);
        name := element->GetAttribute("title")->GetValue();
        offset := name->Find(':');
        if(offset > -1) {
          lang := name->SubString(offset + 1, name->Size() - offset - 1);
          langs->Insert(lang->ReplaceAll(" ", " "));
        };
      };
    };

    langs_counts := IntMap->New();
    client := HttpClient->New();
    html := client->Get("http://rosettacode.org/mw/index.php?title=Special:Categories&limit=5000");
    each(i : html) {
      lines := html->Get(i)->As(String);
      html_elements := lines->Split("\n");
      each(j : html_elements) {
        element := html_elements[j];
        name : String; count : String;
        regex := RegEx->New("
");
        found := regex->FindFirst(element);
        if(found <> Nil) {
          group1 := found->Size();
          regex := RegEx->New("(\\w|\\s)+");
          found := regex->Match(element, group1);
          if(found <> Nil & found->Size() > 0) {
            name := found;
            # skip over some junk characters
            group2 := group1 + found->Size() + 10;
            regex := RegEx->New("\\s\\(");
            found := regex->Match(element, group2);
            if(found <> Nil) {
              group3 := group2 + found->Size();
              regex := RegEx->New("\\d+");
              found := regex->Match(element, group3);
              if(found <> Nil & found->Size() > 0) {
                count := found;
              };
            };
          };
        };

        if(name <> Nil & count <> Nil) {
          if(langs->Has(name)) {
            langs_counts->Insert(count->ToInt(), name);
          };
          name := Nil; count := Nil;
        };
      };
    };

    keys := langs_counts->GetKeys();
    count := 1;
    for(i := keys->Size() - 1; i >= 0; i -=1;) {
      key := keys->Get(i);
      IO.Console->Print(count)->Print(". ")->Print(key)->Print(" - ")->PrintLine(langs_counts->Find(key)->As(String));
      count += 1;
    };
  }
}


/* REXX ---------------------------------------------------------------
* Create a list of Rosetta Code languages showing the number of tasks
* This program's logic is basically that of the REXX program
* rearranged to my taste and utilizing the array class of ooRexx
* which offers a neat way of sorting as desired, see :CLASS mycmp below
* For the input to this program open these links:
*  http://rosettacode.org/wiki/Category:Programming_Languages
*  http://www.rosettacode.org/mw/index.php?title=Special:Categories&limit=5000
* and save the pages as LAN.txt and CAT.txt, respectively
* Output: RC_POP.txt list of languages sorted by popularity
* If test=1, additionally:
*         RC_LNG.txt list of languages alphabetically sorted
*         RC_TRN.txt list language name translations (debug)
*--------------------------------------------------------------------*/
test=1
name.='??'
l.=0
safe=''
x00='00'x
linfid='RC_LAN.txt'
linfid='LAN.txt'                       /* language file              */
cinfid='CAT.txt'                       /* category file              */
oid='RC_POP.txt'; 'erase' oid
If test Then Do
  tid='RC.TRN.txt'; 'erase' tid
  tia='RC_LNG.txt'; 'erase' tia
  End
Call init

call read_lang                         /* process language file      */

Call read_cat                          /* process category file      */

Call ot words(lang_list) 'possible languages'
Call ot words(lang_listr) 'relevant languages'
llrn=words(lang_listr)

If test Then
  Call no_member

a=.array~new                           /* create array object        */
cnt.=0
Do i=1 By 1 While lang_listr<>''
  Parse Var lang_listr ddu0 lang_listr
  ddu=translate(ddu0,' ',x00)
  a[i]=right(mem.ddu,3) name.ddu       /* fill array element         */
  z=mem.ddu                            /* number of members          */
  cnt.z=cnt.z+1                        /* number of such languages   */
  End
n=i-1                                  /* number of output lines     */

a~sortWith(.mycmp~new)                 /* sort the array elements    */
                                       /* see :CLASS mycmp below     */
/*---------------------------------------------------------------------
* and now create the output
*--------------------------------------------------------------------*/
Call o ' '
Call o center('timestamp: ' date() time('Civil'),79,'-')
Call o ' '
Call o right(lrecs,9) 'records read from file: ' linfid
Call o right(crecs,9) 'records read from file: ' cinfid
Call o right(llrn,9) 'relevant languages'
Call o ' '

rank=0
rank.=0
Do i=1 To n
  rank=rank+1
  Parse Value a[i] With o . 6 lang
  ol='          rank: 'right(rank,3)'               '||,
                    '('right(o,3) 'entries)  'lang
  If cnt.o>1 Then Do
    If rank.o=0 Then
      rank.o=rank
    ol=overlay(right(rank.o,3),ol,17)
    ol=overlay('[tied]',ol,22)
    End
  Call o ol
  End

Call o ' '
Call o center('+ end of list +',72)
Say 'Output in' oid

If test Then Do
  b=.array~new                         /* create array object        */
  cnt.=0
  Do i=1 By 1 While lang_list<>''
    Parse Var lang_list ddu0 lang_list
    ddu=translate(ddu0,' ',x00)
    b[i]=right(mem.ddu,3) name.ddu       /* fill array element         */
    End
  n=i-1                                  /* number of output lines     */

  b~sortWith(.alpha~new)               /* sort the array elements    */
  Call oa n 'languages'
  Do i=1 To n
    Call oa b[i]
    End
  Say 'Sorted list of languages in' tia
  End
Exit

o:
  Return lineout(oid,arg(1))
ot:
  If test Then Call lineout tid,arg(1)
  Return
oa:
  If test Then Call lineout tia,arg(1)
  Return

read_lang:
/*---------------------------------------------------------------------
* Read the language page to determine the list of possible languages
* Output: l.lang>0  for all languages found
*         name.lang original name of uppercased language name
*         lang_list list of uppercased language names
*         lrecs     number of records read from language file
*--------------------------------------------------------------------*/
  l.=0
  name.='??'
  lang_list=''
  Do lrecs=0 While lines(linfid)\==0
    l=linein(linfid)                   /* read from language file    */
    l=translate(l,' ','9'x)            /* turn tabs to blanks        */
    dd=space(l)                        /* remove extra blanks        */
    ddu=translate(dd)
    If pos('AUTOMATED ADMINISTRATION',ddu)>0 Then /* ignore this     */
      Iterate
    If pos('RETRIEVED FROM',ddu)\==0 Then /* this indicates the end  */
      Leave
    If dd=='' Then                     /* ignore all blank lines.    */
      Iterate
    If left(dd,1)\=='*' Then           /* ignore lines w/o *         */
      Iterate
    ddo=fix_lang(dd)                   /* replace odd language names */
    If ddo<>dd Then Do                 /* show those that we found   */
      Call ot ' ' dd
      Call ot '>' ddo
      dd=ddo
      End
    Parse Var dd '*' dd "<"            /* extract the language name  */
    ddu=strip(translate(dd))           /* translate to uppercase     */
    If name.ddu='??' Then
      name.ddu=dd                      /* remember 1st original name */
    l.ddu=l.ddu+1
    ddu_=translate(ddu,x00,' ')
    If wordpos(ddu_,lang_list)=0 Then
      lang_list=lang_list ddu_
    End
  Return

read_cat:
/*---------------------------------------------------------------------
* Read the category page to get language names and number of members
* Output: mem.ddu   number of members for (uppercase) Language ddu
*         lang_listr  the list of relevant languages
*--------------------------------------------------------------------*/
  mem.=0
  lang_listr=''
  Do crecs=0 While lines(cinfid)\==0
    l=get_cat(cinfid)                  /* read from category file    */
    l=translate(l,' ','9'x)            /* turn tabs to blanks        */
    dd=space(l)                        /* remove extra blanks        */
    If dd=='' Then                     /* ignore all blank lines.    */
      Iterate
    ddu=translate(dd)
    ddo=fix_lang(dd)                   /* replace odd language names */
    If ddo<>dd Then Do                 /* show those that we found   */
      Call ot ' ' dd
      Call ot '>' ddo
      dd=ddo
      End
    du=translate(dd)                   /* get an uppercase version.  */
    If pos('RETRIEVED FROM',du)\==0 Then  /* this indicates the end  */
      Leave
    Parse Var dd dd '<' "(" mems .     /* extract the language name  */
                                       /* and the number of members  */
    dd=space(substr(dd,3))
    _=translate(dd)
    If \l._ Then                       /* not a known language       */
      Iterate                          /* ignore                     */
    if pos(',', mems)\==0  then
      mems=changestr(",", mems, '')    /* remove commas.             */
    If\datatype(mems,'W') Then         /* not a number of members    */
      Iterate                          /* ignore                     */
    ddu=space(translate(dd))
    mem.ddu=mem.ddu+mems               /* set o add number of members*/
    Call memory ddu                    /* list of relevant languages */
    End
  Return

get_cat:
/*---------------------------------------------------------------------
* get a (logical) line from the category file
* These two lines
*   * Lotus 123 Macro Scripting
*     </wiki/Category:Lotus_123_Macro_Scripting>â€�â€Ž (3 members)
* are returned as one line:
*-> * Lotus 123 Macro Scripting     </wiki/Cate  ... (3 members)
* we need language name and number of members in one line
*--------------------------------------------------------------------*/
  Parse Arg fid
  If safe<>'' Then
    ol=safe
  Else Do
    If lines(fid)=0 Then
      Return ''
    ol=linein(fid)
    safe=''
    End
  If left(ol,3)='  *' Then Do
    Do Until left(r,3)=='  *' | lines(fid)=0
      r=linein(fid)
      If left(r,3)=='  *' Then Do
        safe=r
        Return ol
        End
      Else
        ol=ol r
      End
    End
  Return ol

memory:
  ddu0=translate(ddu,x00,' ')
  If wordpos(ddu0,lang_listr)=0 Then
    lang_listr=lang_listr ddu0
  Return

fix_lang: Procedure Expose old. new.
  Parse Arg s
  Do k=1 While old.k\==''         /* translate Unicode variations.  */
    If pos(old.k,s)\==0 Then
      s=changestr(old.k,s,new.k)
    End
  Return s

init:
  old.=''
  old.1='UC++'        /* '55432B2B'X                                 */
  new.1="µC++"        /* old      UC++ --?ASCII-8: µC++              */
  old.2='ÐœÐš-61/52'  /* 'D09CD09A2D36312F3532'X                     */
  new.2='MK-61/52'    /* somewhere a mistranslated: MK-              */
  old.3='DÃ©jÃ  Vu'   /* '44C3A96AC3A0205675'X                       */
  new.3='Déjà Vu'     /* Unicode +¬j+á --?ASCII-8: Déjá              */
  old.4='CachÃ©'      /* '43616368C3A9'X                             */
  new.4='Caché'       /* Unicode ach+¬ --?ASCII-8: Caché             */
  old.5='ÎœC++'       /* 'CE9C432B2B'X                               */
  new.5="MC++"        /* Unicode +£C++ --?ASCII-8: µC++              */
  /*-----------------------------------------------------------------*/
  Call ot 'Language replacements:'
  Do ii=1 To 5
    Call ot ' ' left(old.ii,10) left(c2x(old.ii),20) '->' new.ii
    End
  Call ot ' '
  Return

no_member: Procedure Expose lang_list lang_listr tid x00 test
/*---------------------------------------------------------------------
* show languages found in language file that are not referred to
* in the category file
*--------------------------------------------------------------------*/
  ll =wordsort(lang_list )             /* languages in language file */
  llr=wordsort(lang_listr)             /* languages in category file */
  Parse Var ll l1 ll
  Parse Var llr l2 llr
  nn.=0
  Do Forever
    Select
      When l1=l2 Then Do
        If l1='' Then                  /* both lists empty           */
          Leave
        Parse Var ll l1 ll             /* get the next language      */
        Parse Var llr l2 llr           /* -"-                        */
        End
      When l1<l2 Then Do               /* in language file           */
                                       /* and not in category file   */
        z=nn.0+1
        nn.z='   'translate(l1,' ',x00) /* show in test output       */
        nn.0=z
        Parse Var ll l1 ll             /* next from language file    */
        End
      Otherwise Do
        Call ot '?? 'translate(l2,' ',x00) /* show in test output    */
        Say 'In category file but not in language file:'
        Say '?? 'translate(l2,' ',x00)
        Say 'Hit enter to proceed'
        Pull .
        Parse Var llr l2 llr           /* next from category file    */
        End
      End
    End
  Call ot nn.0 'Languages without members:' /* heading                    */
  Do ii=1 To nn.0
    Call ot nn.ii
    End
  Return

::CLASS mycmp MIXINCLASS Comparator
::METHOD compare
/**********************************************************************
* smaller number is considered higher
* numbers equal: higher language considered higher
* otherwise return lower
**********************************************************************/
  Parse Upper Arg a,b
  Parse Var a na +4 ta
  Parse Var b nb +4 tb
  Select
    When na<<nb THEN res=1
    When na==nb Then Do
      If ta<<tb Then res=-1
                Else res=1
      End
    Otherwise        res=-1
    End
  Return res

::CLASS alpha MIXINCLASS Comparator
::METHOD compare
/**********************************************************************
* higher language considered higher
* otherwise return lower
**********************************************************************/
  Parse Upper Arg a,b
  Parse Var a na +4 ta
  Parse Var b nb +4 tb
  If ta<<tb Then res=-1
            Else res=1
  Return res


declare
  [HTTPClient] = {Module.link ['x-ozlib://mesaros/net/HTTPClient.ozf']}
  [Regex] = {Module.link ['x-oz://contrib/regex']}

  fun {GetPage RawUrl}
     Client = {New HTTPClient.urlGET init(inPrms(toFile:false toStrm:true) _)}
     Url = {VirtualString.toString RawUrl}
     OutParams
     HttpResponseParams
  in
     {Client getService(Url ?OutParams ?HttpResponseParams)}
     {Client closeAll(true)}
     OutParams.sOut
  end

  fun {GetCategories Doc}
     {Map {Regex.allMatches "
]+>([^<]+) \\(([0-9]+) member" Doc}
      fun {$ Match}
	 Category = {Regex.group 1 Match Doc}
	 Count = {String.toInt {ByteString.toString {Regex.group 2 Match Doc}}}
      in
	 Category#Count
      end
     }
  end

  Url = "http://www.rosettacode.org/mw/index.php?title=Special:Categories&limit=5000"

  {System.showInfo "Retrieving..."}
  Doc = {GetPage Url}

  {System.showInfo "Parsing..."}
  Cs = {GetCategories Doc}
in
  for
     Cat#Count in {Sort Cs fun {$ _#C1 _#C2} C1 > C2 end}
     I in 1..20
  do
     {System.showInfo I#". "#Count#" - "#Cat}
  end


use 5.010;
use MediaWiki::API;

my $api =
  MediaWiki::API->new( { api_url => 'http://rosettacode.org/mw/api.php' } );

my @languages;
my $gcmcontinue;
while (1) {
    my $apih = $api->api(
        {
            action      => 'query',
            generator   => 'categorymembers',
            gcmtitle    => 'Category:Programming Languages',
            gcmlimit    => 250,
            prop        => 'categoryinfo',
            gcmcontinue => $gcmcontinue
        }
    );
    push @languages, values %{ $apih->{'query'}{'pages'} };

    last if not $gcmcontinue = $apih->{'continue'}{'gcmcontinue'};
}

for (@languages) {
    $_->{'title'} =~ s/Category://;
    $_->{'categoryinfo'}{'size'} //= 0;
}

my @sorted_languages =
  reverse sort { $a->{'categoryinfo'}{'size'} <=> $b->{'categoryinfo'}{'size'} }
  @languages;

binmode STDOUT, ':encoding(utf8)';
my $n = 1;
for (@sorted_languages) {
    printf "%3d. %20s - %3d\n", $n++, $_->{'title'},
      $_->{'categoryinfo'}{'size'};
}


-- demo\rosetta\Rank_Languages.exw
constant output_users = false,
         limit = 20,    -- 0 to list all
         languages = "http://rosettacode.org/wiki/Category:Programming_Languages",
         categories = "http://www.rosettacode.org/mw/index.php?title=Special:Categories&limit=5000"

include rosettacode_cache.e -- see Rosetta_Code/Count_examples#Phix

function correct_name(string ri)
    ri = substitute(ri,`"`,`"`)
    ri = substitute(ri,`'`,`'`)
    ri = substitute(ri,"\xE2\x80\x99","'")
    ri = substitute(ri,"\xC3\xB6","o")
    ri = substitute(ri,"%3A",":")
    ri = substitute(ri,"%E2%80%93","-")
    ri = substitute(ri,"%E2%80%99","'")
    ri = substitute(ri,"%27","'")
    ri = substitute(ri,"%2B","+")
    ri = substitute(ri,"%C3%A8","e")
    ri = substitute(ri,"%C3%A9","e")
    ri = substitute(ri,"%C3%B6","o")
    ri = substitute(ri,"%C5%91","o")
    ri = substitute(ri,"%22",`"`)
    ri = substitute(ri,"%2A","*")
    ri = substitute(ri,"\xC2\xB5","u")
    ri = substitute(ri,"\xC3\xA0","a")
    ri = substitute(ri,"\xC3\xA6","a")
    ri = substitute(ri,"\xC3\xA9","e")
    ri = substitute(ri,"\xC3\xB4","o")
    ri = substitute(ri,"\xC5\x8D","o")
    ri = substitute(ri,"\xCE\x9C","u")
    ri = substitute(ri,"\xD0\x9C\xD0\x9A","MK")
    ri = substitute(ri,"\xE0\xAE\x89\xE0\xAE\xAF\xE0\xAE\xBF\xE0\xAE\xB0\xE0\xAF\x8D/","")
    ri = substitute(ri,"APEX","Apex")
    ri = substitute(ri,"uC++ ","UC++")
    ri = substitute(ri,`CASIO BASIC`,`Casio BASIC`)
    ri = substitute(ri,`Visual BASIC`,`Visual Basic`)
    ri = substitute(ri,`INTERCAL`,`Intercal`)
    ri = substitute(ri,`SETL4`,`Setl4`)
    ri = substitute(ri,`QBASIC`,`QBasic`)
    ri = substitute(ri,`RED`,`Red`)
    ri = substitute(ri,`OCTAVE`,`Octave`)
    ri = substitute(ri,`OoREXX`,`OoRexx`)
    return ri
end function

include builtins/sets.e

constant cat_title = `title="Category:`

function extract_names()
    sequence results = {} -- {rank,count,name}

    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
    -- 1) extract languages from eg title="Category:Phix"
    sequence language_names = {}
    string langs = open_download("languages.htm",languages),
           language_name
    langs = langs[1..match(`<div class="printfooter">`,langs)-1]
    integer start = match("
Subcategories
",langs), k
    while true do
        k = match(cat_title,langs,start)
        if k=0 then exit end if
        k += length(cat_title)
        start = find('"',langs,k)
        language_name = correct_name(langs[k..start-1])
        language_names = append(language_names,language_name)
    end while

    -- 2) extract results from eg title="Category:Phix">Phix?? (997 members)

    --     but obviously only when we have found that language in the phase above.
    --     (note there is / ignore some wierd uncode-like stuff after the ...)
    string cats = open_download("categories.htm",categories)
    start = 1
    while true do
        k = match(cat_title,cats,start)
        if k=0 then exit end if
        k += length(cat_title)
        start = find('"',cats,k)
        language_name = correct_name(cats[k..start-1])
        start = match("",cats,start)+4
        if output_users then
            if length(language_name)>5
            and language_name[-5..-1] = " User" then
                language_name = correct_name(language_name[1..-6])
            else
                language_name = ""
            end if
        end if
        if length(language_name)
        and find(language_name,language_names) then
            while not find(cats[start],"(<") do start += 1 end while -- (ignore)
            string members = cats[start..find('<',cats,start+1)]
            members = substitute(members,",","")
            sequence res = scanf(members,"(%d member%s)<")
            results = append(results,{0,res[1][1],language_name})
        end if
    end while
    results = sort_columns(results,{-2,3}) -- (descending 2nd column, then asc 3rd)

    --3) assign rank
    integer count, prev = 0, rank
    for i=1 to length(results) do
        count = results[i][2]
        if count=prev then
            results[i][1] = "="
        else
            rank = i
            results[i][1] = sprint(rank)
            prev = count
        end if
    end for

    return results
end function

procedure show(sequence results)
    progress("")
    for i=1 to iff(limit?limit:length(results)) do
        printf(1,"%3s: %,d - %s\n",results[i])
    end for
end procedure

show(extract_names())


(load "@lib/http.l")

(for (I . X)
   (flip
      (sort
         (make
            (client "rosettacode.org" 80
               "mw/index.php?title=Special:Categories&limit=5000"
               (while (from "
<a href=\"/wiki/Category:")
                  (let Cat (till "\"")
                     (from "(")
                     (when (format (till " " T))
                        (link (cons @ (ht:Pack Cat))) ) ) ) ) ) ) )
   (prinl (align 3 I) ". " (car X) " - " (cdr X)) )


$get1 = (New-Object Net.WebClient).DownloadString("http://www.rosettacode.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:Programming_Languages&cmlimit=700&format=json")
$get2 = (New-Object Net.WebClient).DownloadString("http://www.rosettacode.org/w/index.php?title=Special:Categories&limit=5000")
$match1 = [regex]::matches($get1, "`"title`":`"Category:(.+?)`"")
$match2 = [regex]::matches($get2, "title=`"Category:([^`"]+?)`">[^<]+?</a>[^\(]*\((\d+) members\)")
$r = 1
$langs = $match1 | foreach { $_.Groups[1].Value.Replace("\","") }
$res = $match2 | sort -Descending {[Int]$($_.Groups[2].Value)} | foreach {
    if ($langs.Contains($_.Groups[1].Value))
    {
        [pscustomobject]@{
            Rank = "$r"
            Members =  "$($_.Groups[2].Value)"
            Language = "$($_.Groups[1].Value)"
        }
        $r++
    }
}
1..30 | foreach{
    [pscustomobject]@{
        "Rank 1..30" = "$($_)"
        "Members 1..30" =  "$($res[$_-1].Members)"
        "Language 1..30" = "$($res[$_-1].Language)"
        "Rank 31..60" = "$($_+30)"
        "Members 31..60" =  "$($res[$_+30].Members)"
        "Language 31..60" = "$($res[$_+30].Language)"
    }
}| Format-Table -AutoSize


Procedure handleError(value, msg.s)
  If value = 0
    MessageRequester("Error", msg)
    End
  EndIf
EndProcedure

Structure languageInfo
  name.s
  pageCount.i
EndStructure

#JSON_web_data = 0 ;ID# for our parsed JSON web data object

Define NewList languages.languageInfo()

Define blah.s, object_val, allPages_mem, title_mem, page_mem, categoryInfo_mem, continue_mem
Define url$, title$, currentPage$, language$, langPageCount, gcmcontinue$, *bufferPtr

handleError(InitNetwork(), "Unable to initialize network functions.")

Repeat
  url$ = "http://www.rosettacode.org/mw/api.php?action=query" +
         "&generator=categorymembers&gcmtitle=Category:Programming%20Languages" +
         "&gcmlimit=500" + "&gcmcontinue=" + gcmcontinue$ +
         "&prop=categoryinfo&format=json"

  *bufferPtr = ReceiveHTTPMemory(url$)
  handleError(*bufferPtr, "Unable to receive web page data.")
  If CatchJSON(#JSON_web_data, *bufferPtr, MemorySize(*bufferPtr)) = 0
    MessageRequester("Error", JSONErrorMessage() + " at position " +
                              JSONErrorPosition() + " in line " +
                              JSONErrorLine() + " of JSON web Data")
    End
  EndIf
  FreeMemory(*bufferPtr)

  object_val = JSONValue(#JSON_web_data)
  allPages_mem = GetJSONMember(GetJSONMember(object_val, "query"), "pages")
  If ExamineJSONMembers(allPages_mem)
    While NextJSONMember(allPages_mem)
      page_mem = JSONMemberValue(allPages_mem)
      title_mem = GetJSONMember(page_mem, "title")
      If title_mem
        title$ = GetJSONString(title_mem)
        If Left(title$, 9) = "Category:"
          language$ = Mid(title$, 10)
          categoryInfo_mem = GetJSONMember(page_mem, "categoryinfo")
          If categoryInfo_mem
            langPageCount = GetJSONInteger(GetJSONMember(categoryInfo_mem, "pages"))
          Else
            langPageCount = 0
          EndIf

          AddElement(languages())
          languages()\name = language$
          languages()\pageCount = langPageCount
        EndIf
      EndIf
    Wend
  EndIf

  ;check for continue
  continue_mem = GetJSONMember(object_val, "continue")
  If continue_mem
    gcmcontinue$ = GetJSONString(GetJSONMember(continue_mem, "gcmcontinue"))
  Else
    gcmcontinue$ = ""
  EndIf

  FreeJSON(#JSON_web_data)
Until gcmcontinue$ = ""

;all data has been aquired, now process and display it
SortStructuredList(languages(), #PB_Sort_Descending, OffsetOf(languageInfo\pageCount), #PB_Integer)

If OpenConsole()

  If ListSize(languages())
    Define i, *startOfGroupPtr.languageInfo, *lastElementPtr, groupSize, rank
    Define outputSize = 100, outputLine

    PrintN(Str(ListSize(languages())) + " languages." + #CRLF$)
    LastElement(languages())
    *lastElementPtr = @languages() ;pointer to last element
    FirstElement(languages())
    *startOfGroupPtr = @languages() ;pointer to first element
    groupSize = 1
    rank = 1

    While NextElement(languages())
      If languages()\pageCount <> *startOfGroupPtr\pageCount Or *lastElementPtr = @languages()
        ;display a group of languages at the same rank
        ChangeCurrentElement(languages(), *startOfGroupPtr)
        For i = 1 To groupSize
          ;display output in groups to allow viewing of all entries
          If outputLine = 0
            PrintN(" Rank   Tasks  Language")
            PrintN(" ------ -----  --------")
          EndIf

          PrintN(RSet(Str(rank), 6) + ".  " +
                 RSet(Str(languages()\pageCount), 4) + "  " +
                 languages()\name)

          outputLine + 1
          If outputLine >= outputSize
            Print(#CRLF$ + #CRLF$ + "Press ENTER to continue" + #CRLF$): Input()
            outputLine = 0
          EndIf

          NextElement(languages())
        Next

        rank + groupSize
        groupSize = 1
        *startOfGroupPtr = @languages()
      Else
        groupSize + 1
      EndIf
    Wend
  Else
    PrintN("No language categories found.")
  EndIf

  Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input()
  CloseConsole()
EndIf


import requests
import re

response = requests.get("http://rosettacode.org/wiki/Category:Programming_Languages").text
languages = re.findall('title="Category:(.*?)">',response)[:-3] # strip last 3

response = requests.get("http://rosettacode.org/mw/index.php?title=Special:Categories&limit=5000").text
response = re.sub('(\d+),(\d+)',r'\1'+r'\2',response)           # strip ',' from popular languages above 999 members
members  = re.findall('
]+>([^<]+)[^(]*[(](\\d+) member[s]*[)]
',response) # find language and members

for cnt, (language, members) in enumerate(sorted(members, key=lambda x: -int(x[1]))[:15]): # show only top 15 languages
    if language in languages:
        print("{:4d} {:4d} - {}".format(cnt+1, int(members), language))


library(rvest)
library(plyr)
library(dplyr)
options(stringsAsFactors=FALSE)

langUrl <- "http://rosettacode.org/mw/api.php?format=xml&action=query&generator=categorymembers&gcmtitle=Category:Programming%20Languages&prop=categoryinfo&gcmlimit=5000"
langs <- html(langUrl) %>%
  html_nodes('page')

ff <- function(xml_node) {
  language <- xml_node %>% html_attr("title")
  language <- sub("^Category:", "", language)
  npages <- xml_node %>% html_nodes('categoryinfo') %>%
    html_attr("pages")
  c(language, npages)
}
tbl <- ldply(sapply(langs, ff), rbind)
names(tbl) <- c("language", "n")
tbl %>%
  mutate(n=as.integer(n)) %>%
  arrange(desc(n)) %>%
  head


#lang racket

(require racket/hash
         net/url
         json)

(define limit 15)
(define (replacer cat) (regexp-replace #rx"^Category:(.*?)$" cat "\\1"))
(define category "Category:Programming Languages")
(define entries "entries")

(define api-url (string->url "http://rosettacode.org/mw/api.php"))
(define (make-complete-url gcmcontinue)
  (struct-copy url api-url
               [query `([format . "json"]
                        [action . "query"]
                        [generator . "categorymembers"]
                        [gcmtitle . ,category]
                        [gcmlimit . "200"]
                        [gcmcontinue . ,gcmcontinue]
                        [continue . ""]
                        [prop . "categoryinfo"])]))

(define @ hash-ref)

(define table (make-hash))

(let loop ([gcmcontinue ""])
  (define resp (read-json (get-pure-port (make-complete-url gcmcontinue))))
  (hash-union! table
               (for/hash ([(k v) (in-hash (@ (@ resp 'query) 'pages))])
                 (values (@ v 'title #f) (@ (@ v 'categoryinfo (hash)) 'size 0))))
  (cond [(@ resp 'continue #f) => (λ (c) (loop (@ c 'gcmcontinue)))]))

(for/fold ([prev #f] [rank #f] #:result (void))
          ([item (in-list (sort (hash->list table) > #:key cdr))] [i (in-range limit)])
  (match-define (cons cat size) item)
  (define this-rank (if (equal? prev size) rank (add1 i)))
  (printf "Rank: ~a ~a ~a\n"
          (~a this-rank #:align 'right #:min-width 2)
          (~a (format "(~a ~a)" size entries) #:align 'right #:min-width 14)
          (replacer cat))
  (values size this-rank))


use HTTP::UserAgent;
use URI::Escape;
use JSON::Fast;
use Sort::Naturally;

my $client = HTTP::UserAgent.new;

my $url = 'http://rosettacode.org/mw';

my $tablefile = './RC_Popularity.txt';

my %cat = (
    'Programming_Tasks' => 'Task',
    'Draft_Programming_Tasks' => 'Draft'
);
my %tasks;

for %cat.keys.sort -> $cat {
    mediawiki-query(
        $url, 'pages',
        :generator<categorymembers>,
        :gcmtitle("Category:$cat"),
        :gcmlimit<350>,
        :rawcontinue(),
        :prop<title>
    ).map({ %tasks{%cat{$cat}}++ });
}

my %counts =
    mediawiki-query(
        $url, 'pages',
        :generator<categorymembers>,
        :gcmtitle<Category:Programming Languages>,
        :gcmlimit<350>,
        :rawcontinue(),
        :prop<categoryinfo>
    )
    .map({
        my $title = .<title>.subst(/^'Category:'/, '');
        my $tasks = (.<categoryinfo><pages> || 0);
        my $categories = (.<categoryinfo><subcats> || 0);
        my $total = (.<categoryinfo><size> || 0);
       $title  => [$tasks ,$categories, $total]
   });

my $out = open($tablefile, :w)  or die "$!\n";

# Add table boilerplate and header
$out.say:
    "\{|class=\"wikitable sortable\"\n",
    "|+ As of { Date.today } :: {+%counts} Languages\n",
    '!Rank!!Language!!Task
Entries!!Tasks
done %!!Non-task
Subcate-
gories!!Total
Categories'
;

my @bg = <#fff; #ccc;>;
my $ff = 0;
my $rank = 1;
my $ties = 0;

# Get sorted unique task counts
for %counts.values»[0].unique.sort: -* -> $count {
    $ff++;
    # Get list of tasks with this count
    my @these = %counts.grep( *.value[0] == $count )».keys.sort: *.&naturally;

    for @these {
        $ties++;
        $out.say:
          "|- style=\"background-color: { @bg[$ff % 2] }\"\n"~
          "|$rank\n"~
          "|[[:Category:$_|]]\n"~
          "|$count\n"~
          "|{(100 * $count/%tasks<Draft Task>.sum).round(.01)} %\n"~
          "|{%counts{$_}[1]}\n"~
          "|{%counts{$_}[2]}"
    }
    $rank += $ties;
    $ties = 0;
}
$out.say( "|}" );
$out.say('=' x 5, " query, download & processing: {(now - INIT now).round(.01)} seconds ", '=' x 5);
$out.close;

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 uri-query-string (*%fields) {
    join '&', %fields.map: { "{.key}={uri-escape .value}" }
}


Red []

data: read http://www.rosettacode.org/mw/index.php?title=Special:Categories&limit=5000
lb: make block! 500
;;data: read %data.html ;; for testing save html and use flat file
arr: split data newline

k: "Category:"
;; exclude list:
exrule: [thru ["programming"
                | "users"
                | "Implementations"
                | "Solutions by "
                | "Members"
                | "WikipediaSourced"
                | "Typing/Strong"
                | "Impl needed"
                ]
            to end
          ]

foreach line arr [
  unless find line k [continue]
  parse line [ thru k thru ">" copy lang to "<" to end ] ;; extract/parse language
  if 20 < length? lang [continue]
  if parse lang [exrule] [continue] ;; exclude invalid
  cnt: 0
  ;; parse number of entries
  parse line [thru "</a>" thru "(" copy cnt  to " member" (cnt: to-integer cnt ) to end]
  if cnt > 25 [  append  lb reduce [to-string lang cnt]   ] ;; only process lang with > 25 entries
]

lb:  sort/skip/compare lb 2 2   ;; sort series by entries

print reduce [ "Rank Entries Language" ]    ;; header

last: 0
rank: 0

lb: tail lb   ;; process the series backwards

until [
  lb: skip lb -2
  cnt: second lb
  if cnt <> last [
    rank: rank + 1
  ]
  print rejoin [ pad/left rank 4 "." pad/left cnt 5 " - " first lb  ]
  last: cnt
  head? lb  ;; until head reached
]


/*REXX program reads two files  and  displays a  ranked list  of Rosetta Code languages.*/
parse arg catFID lanFID outFID .                 /*obtain optional arguments from the CL*/
call init                                        /*initialize some  REXX  variables.    */
call get                                         /*obtain data from two separate files. */
call eSort #,0                                   /*sort languages along with members.   */
call tSort                                       /*  "      "     that are tied in rank.*/
call eSort #,1                                   /*  "      "     along with members.   */
call out                                         /*create the  RC_POP.OUT (output) file.*/
exit 0                                           /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
commas: parse arg _;   do jc=length(_)-3  to 1  by -3; _= insert(",",_,jc); end;  return _
s:      if arg(1)==1  then return arg(3);      return word(arg(2) 's',1)   /*pluralizer.*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
eSort: procedure expose #. @. !tr.;    arg N,p2;     h= N      /*sort: number of members*/
            do     while  h>1;         h= h % 2                /*halve number of records*/
              do i=1  for N-h;         j= i;         k= h + i  /*sort this part of list.*/
              if p2  then do  while !tR.k==!tR.j  &  @.k>@.j   /*this uses a hard swap ↓*/
                          @= @.j;  #= !tR.j;  @.j= @.k;  !tR.j= !tR.k;   @.k= @;  !tR.k= #
                          if h>=j  then leave;               j= j - h;     k= k - h
                          end   /*while !tR.k==···*/
                     else do  while #.k<#.j                    /*this uses a hard swap ↓*/
                          @= @.j;  #= #.j;    @.j= @.k;    #.j= #.k;     @.k= @;    #.k= #
                          if h>=j  then leave;               j= j - h;     k= k - h
                          end   /*while #.k<···*/
              end               /*i*/           /*hard swaps needed for embedded blanks.*/
            end                 /*while h>1*/;               return
/*──────────────────────────────────────────────────────────────────────────────────────*/
get: langs= 0;           call rdr 'languages'    /*assign languages  ───►  L.ααα        */
                         call rdr 'categories'   /*append categories ───► catHeap       */
     #= 0
           do j=1  until  catHeap==''            /*process the heap of categories.      */
           parse var catHeap cat.j (sep) catHeap /*get a category from the  catHeap.    */
           parse var cat.j  cat.j '<' "(" mems . /*untangle the strange─looking string. */
           cat.j= space(cat.j); ?=cat.j; upper ? /*remove any superfluous blanks.       */
           if ?=='' | \L.?          then iterate /*it's blank  or  it's not a language. */
           if pos(',', mems)\==0    then mems= space(translate(mems,,","), 0) /*¬commas.*/
           if \datatype(mems, 'W')  then iterate /*is the "members" number not numeric? */
           #.0= #.0 + mems                       /*bump the number of  members  found.  */
           if u.?\==0  then do;     do f=1  for #  until [email protected].f
                                    end   /*f*/
                            #.f= #.f + mems; iterate j   /*languages in different cases.*/
                            end                  /* [↑]  handle any possible duplicates.*/
           u.?= u.? + 1;       #= # + 1          /*bump a couple of counters.           */
           #.#= #.# + mems;  @.#= cat.j;  @u.#=? /*bump the counter;  assign it (upper).*/
           end   /*j*/

     !.=;        @tno= '(total) number of'       /*array holds indication of TIED langs.*/
     call tell right(commas(#),    9) @tno 'languages detected in the category file'
     call tell right(commas(langs),9) '   "       "    "     "         "     "  "  language   "
     call tell right(commas(#.0),  9) @tno 'entries (solutions) detected', , 1;    term= 0
     return                                      /*don't show any more msgs──►term. [↑] */
/*──────────────────────────────────────────────────────────────────────────────────────*/
init: sep= '█';  L.=0;  #.=0;  u.=#.;  catHeap=;  term=1;  old.= /*assign some REXX vars*/
      if catFID==''  then catFID= "RC_POP.CAT"   /*Not specified?  Then use the default.*/
      if lanFID==''  then lanFID= "RC_POP.LAN"   /* "      "         "   "   "     "    */
      if outFID==''  then outFID= "RC_POP.OUT"   /* "      "         "   "   "     "    */
      call tell center('timestamp: '  date()  time("Civil"),79,'═'), 2, 1;      return
/*──────────────────────────────────────────────────────────────────────────────────────*/
out:  w= length( commas(#) );     rank= 0        /* [↓]  show by ascending rank of lang.*/
          do t=#  by -1  for #;   rank= rank + 1 /*bump rank of a programming language. */
          call tell   right('rank:'    right(commas(!tR.t), w),  20-1)      right(!.t, 7),
                      right('('commas(#.t)  left("entr"s(#.t, 'ies', "y")')', 9), 20)  @.t
          end   /*#*/                            /* [↑]   S(···)   pluralizes a word.   */
      call tell left('', 27)  "☼  end─of─list.  ☼", 1, 2;      return    /*bottom title.*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
rdr:  arg which 2;        igAst= 1               /*ARG uppers WHICH, obtain the 1st char*/
      if which=='L'  then inFID= lanFID          /*use this fileID for the  languages.  */
      if which=='C'  then inFID= catFID          /* "    "     "    "   "   categories. */
      Uyir=  'α«ëα«»α«┐α«░α»ì/Uyir'              /*Unicode (in text)  name for  Uyir    */
      old.0= '╬£C++'     ;    new.0= "µC++"      /*Unicode ╬£C++  ───► ASCII─8: µC++    */
      old.1= 'UC++'      ;    new.1= "µC++"      /*old      UC++  ───► ASCII─8: µC++    */
      old.2= '╨£╨Ü-'     ;    new.2= "MK-"       /*Unicode ╨£╨Ü─  ───► ASCII-8: MK-     */
      old.3= 'D├⌐j├á'    ;    new.3= "Déjà"      /*Unicode ├⌐j├á  ───► ASCII─8: Déjà    */
      old.4= 'Cach├⌐'    ;    new.4= "Caché"     /*Unicode Cach├⌐ ───► ASCII─8: Caché   */
      old.5= '??-61/52'  ;    new.5= "MK-61/52"  /*somewhere past, a mis─translated: MK-*/
      old.6= 'F┼ìrmul├ª' ;    new.6= 'Fôrmulæ'   /*Unicode        ───► ASCII─8: Fôrmulæ */
      old.7= '╨£iniZinc' ;    new.7= 'MiniZinc'  /*Unicode        ───► ASCII─8: MiniZinc*/
      old.8=  Uyir       ;    new.8= 'Uyir'      /*Unicode        ───► ASCII─8: Uyir    */
      old.9= 'Perl 6'    ;    new.9= 'Raku'      /* (old name)    ───► (new name)       */

        do recs=0   while  lines(inFID) \== 0    /*read a file, a single line at a time.*/
        $= translate( linein(inFID), , '9'x)     /*handle any stray  TAB  ('09'x) chars.*/
        $$= space($);   if $$==''  then iterate  /*ignore all blank lines in the file(s)*/
                do v=0  while old.v \== ''       /*translate Unicode variations of langs*/
                if pos(old.v, $$) \==0  then $$= changestr(old.v, $$, new.v)
                end   /*v*/                      /* [↑]  handle different lang spellings*/
        if igAst  then do;  igAst= pos(' * ', $)==0;      if igAst  then iterate;      end
        if pos('RETRIEVED FROM', translate($$))\==0  then leave   /*pseudo End─Of─Data?.*/
        if which=='L'  then do;  if left($$, 1)\=="*"  then iterate  /*lang ¬legitimate?*/
                                 parse upper var   $$   '*'  $$  "<";    $$= space($$)
                                 if $$==''  then iterate;                L.$$= 1
                                 langs= langs + 1     /*bump number of languages found. */
                                 iterate
                            end                  /* [↓]  extract computer language name.*/
        if left($$, 1)=='*'  then $$= sep  ||  space( substr($$, 2) )
        catHeap= catHeap  $$                     /*append to the catHeap (CATegory) heap*/
        end   /*recs*/
      call  tell   right( commas(recs), 9)       'records read from file: '        inFID
      return
/*──────────────────────────────────────────────────────────────────────────────────────*/
tell:          do '0'arg(2);   call lineout outFID," "     ;   if term  then say ;     end
                               call lineout outFID,arg(1)  ;   if term  then say arg(1)
               do '0'arg(3);   call lineout outFID," "     ;   if term  then say ;     end
       return       /*show BEFORE blank lines (if any), message, show AFTER blank lines.*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
tSort: tied=;                            r= 0    /*add true rank (tR) ───► the entries. */
               do j=#  by -1  for #;     r= r+1;   tR= r;   !tR.j= r;   jp= j+1;   jm= j-1
               if tied==''  then pR= r;  tied=   /*handle when language rank is untied. */
               if #.j==#.jp | #.j==#.jm  then do;    !.j= '[tied]';     tied= !.j;     end
               if #.j==#.jp              then do;    tR= pR;            !tR.j= pR;     end
                                         else pR= r
               end   /*j*/;      return


# Project: Rosetta Code/Rank languages by popularity

load "stdlib.ring"
ros= download("http://rosettacode.org/wiki/Category:Programming_Languages")
pos = 1
totalros = 0
rosname = ""
rosnameold = ""
rostitle = ""
roslist = []
for n = 1 to len(ros)
        nr = searchstring(ros,'
<a href="/wiki/',pos)
        if nr = 0
           exit
        else
           pos = nr + 1
        ok
        nr = searchname(nr)
        nr = searchtitle(nr)
next
roslist = sortfirst(roslist)
roslist = reverse(roslist)

see nl
for n = 1 to len(roslist)
     see "rank: " + n + " (" + roslist[n][1] + " entries) " + roslist[n][2] + nl
next

func searchstring(str,substr,n)
       newstr=right(str,len(str)-n+1)
       nr = substr(newstr, substr)
       if nr = 0
          return 0
       else
          return n + nr -1
       ok

func count(cstring,dstring)
       sum = 0
       while substr(cstring,dstring) > 0
               sum = sum + 1
               cstring = substr(cstring,substr(cstring,dstring)+len(string(sum)))
       end
       return sum

func searchname(sn)
       nr2 = searchstring(ros,"/wiki/Category:",sn)
       nr3 = searchstring(ros,"title=",sn)
       nr4 = searchstring(ros,'">',sn)
       nr5 = searchstring(ros,"
",sn)
       rosname = substr(ros,nr2+15,nr3-nr2-17)
       rosnameold = substr(ros,nr4+2,nr5-nr4-2)
       return sn

func searchtitle(sn)
        rostitle = "rosettacode.org/wiki/Category:" + rosname
        rostitle = download(rostitle)
        nr2 = 0
        roscount = count(rostitle,"The following")
        if roscount > 0
           rp = 1
           for rc = 1 to roscount
                nr2 = searchstring(rostitle,"The following",rp)
                rp = nr2 + 1
           next
        ok
        nr3 = searchstring(rostitle,"pages are in this category",nr2)
        if nr2 > 0 and nr3 > 0
           rosnr = substr(rostitle,nr2+14,nr3-nr2-15)
           rosnr = substr(rosnr,",","")
           add(roslist,[rosnr,rosnameold])
        ok
        return sn

func sortfirst(alist)
        for n = 1 to len(alist) - 1
             for m = n + 1 to len(alist)
                  if alist[m][1] < alist[n][1]
                     swap(alist,m,n)
                  ok
                  if alist[m][1] = alist[n][1] and strcmp(alist[m][2],alist[n][2]) > 0
                     swap(alist,m,n)
                  ok
             next
        next
        return alist


require 'rosettacode'

langs = []
RosettaCode.category_members("Programming Languages") {|lang| langs << lang}

# API has trouble with long titles= values.
# To prevent skipping languages, use short slices of 20 titles.
langcount = {}
langs.each_slice(20) do |sublist|
  url = RosettaCode.get_api_url({
    "action" => "query",
    "prop" => "categoryinfo",
    "format" => "xml",
    "titles" => sublist.join("|"),
  })

  doc = REXML::Document.new open(url)
  REXML::XPath.each(doc, "//page") do |page|
    lang = page.attribute("title").value
    info = REXML::XPath.first(page, "categoryinfo")
    langcount[lang] = info.nil? ? 0 : info.attribute("pages").value.to_i
  end
end

puts Time.now
puts "There are #{langcount.length} languages"
puts "the top 25:"
langcount.sort_by {|key,val| val}.reverse[0,25].each_with_index do |(lang, count), i|
  puts "#{i+1}. #{count} - #{lang.sub(/Category:/, '')}"
end


sqliteconnect #mem, ":memory:"  ' make memory DB
#mem execute("CREATE TABLE stats(lang,cnt)")
a$	= httpGet$("http://rosettacode.org/wiki/Category:Programming_Languages")
aa$	= httpGet$("http://www.rosettacode.org/mw/index.php?title=Special:Categories&limit=5000")
i	= instr(a$,"/wiki/Category:")
while i > 0 and lang$ <> "Languages"
	j	= instr(a$,"""",i)
	lang$	= mid$(a$,i+15,j - i-15)
	ii	= instr(aa$,"Category:";lang$;"""")
	jj	= instr(aa$,"(",ii)
	kk	= instr(aa$," ",jj+1)
	if ii = 0 then cnt = 0 else cnt = val(mid$(aa$,jj+1,kk-jj))
	k	= instr(lang$,"%")     ' convert hex values to characters
	while k > 0
		lang$	= left$(lang$,k-1) + chr$(hexdec(mid$(lang$,k+1,2))) + mid$(lang$,k+3)
		k	= instr(lang$,"%")
	wend
	#mem execute("insert into stats values ('";lang$;"',";cnt;")")
	i	= instr(a$,"/wiki/Category:",i+10)
wend
html "
"
#mem execute("SELECT * FROM stats ORDER BY cnt desc") ' order list by count descending
WHILE   #mem hasanswer()
 #row = #mem #nextrow()
 rank = rank + 1
 html "
";rank;"	";#row lang$();"	";#row cnt();"
"
WEND
html "
"


import akka.actor.{Actor, ActorSystem, Props}
import scala.collection.immutable.TreeSet
import scala.xml.XML

// Reports a list with all languages recorded in the Wiki

private object Acquisition {
  val (endPoint, prefix) = ("http://rosettacode.org/mw/api.php", "Category:")
  val (maxPlaces, correction) = (50, 2)

  def convertPathArgsToURL(endPoint: String, pathArgs: Map[String, String]) = {
    pathArgs.map(argPair => argPair._1 + "=" + argPair._2)
      .mkString(endPoint + (if (pathArgs.nonEmpty) "?" else ""), "&", "")
  }

  /* The categories include a page for the language and a count of the pages
   * linked therein, this count is the data we need to scrape.
   * Reports a list with language, count pair recorded in the Wiki
   * All strings starts with the prefixes "Category:"
   */
  def mineCatos = {
    val endPoint = "http://rosettacode.org/mw/index.php"
    Concurrent.logInfo("Acquisition of categories started.")
    val categories =
      (XML.load(convertPathArgsToURL(endPoint,
        Map("title" -> "Special:Categories", "limit" -> "5000"))) \\ "ul" \ "li")
        .withFilter(p => (p \ "a" \ "@title").text.startsWith(prefix))
        .map // Create a tuple pair, eg. ("Category:Erlang", 195)
        { cat =>
          ((cat \ "a" \ "@title").text, // Takes the sibling of "a" and extracts the number
            "[0-9]+".r.findFirstIn(cat.child.drop(1).text).getOrElse("0").toInt)
        }
    Concurrent.logInfo(s"Got ${categories.size} categories..")
    categories
  }

  // The languages
  // All strings starts with the prefixes "Category:"
  def mineLangs = {
    Concurrent.logInfo("Acquisition of languages started...")
    def getLangs(first: Boolean = true, continue: String = ""): TreeSet[String] = (first, continue) match {
      case (false, "") => TreeSet[String]()
      case _ => {
        val xml = XML.load(convertPathArgsToURL(endPoint, Map(
            "action" -> "query",
            "list" -> "categorymembers",
            "cmtitle" -> (prefix + "Programming_Languages"),
            "cmlimit" -> "500",
            "rawcontinue" -> "",
            "format" -> "xml",
            "cmcontinue" -> continue)))
        getLangs(false, (xml \\ "query-continue" \ "categorymembers" \ "@cmcontinue").text) ++ (xml \\ "categorymembers" \ "cm").map(c => (c \ "@title").text)
      }
    }
    val languages = getLangs()
    Concurrent.logInfo(s"Got ${languages.size} languages..")
    languages
  }

  def joinRosettaCodeWithLanguage(catos: Seq[(String, Int)],
                                  langs: TreeSet[String]) =
    for {
      cato <- catos //Clean up the tuple pairs, eg ("Category:Erlang", 195) becomes ("Erlang", 192)
      if langs.contains(cato._1)
    } yield (cato._1.drop(prefix.length), cato._2 - correction max 0) // Correct count

  def printScrape(languages: TreeSet[String], category: Seq[(String, Int)]) {

    val join = joinRosettaCodeWithLanguage(category, languages)
    val total = join.foldLeft(0)(_ + _._2)

    Concurrent.logInfo("Data processed")

    println(f"\nTop$maxPlaces%3d Rosetta Code Languages by Popularity as ${new java.util.Date}%tF:\n")
    (join.groupBy(_._2).toSeq.sortBy(-_._1).take(maxPlaces) :+ (0, Seq(("...", 0))))
      .zipWithIndex // Group the ex aequo
      .foreach {
        case ((score, langs), rank) =>
          println(f"${rank + 1}%2d. $score%3d - ${langs.map(_._1).mkString(", ")}")
      }

    println(s"\nCross section yields ${join.size} languages, total of $total solutions")
    println(s"Resulting average is ${total / join.size} solutions per language")
  }

  def printScrape(): Unit = printScrape(mineLangs, mineCatos)
} // object Acquisition

private object Concurrent extends AppCommons {
  var (category: Option[Seq[(String, Int)]], language: Option[TreeSet[String]]) = (None, None)

  class Worker extends Actor {
    def receive = {
      case 'Catalogue => sender ! Acquisition.mineCatos
      case 'Language => sender ! Acquisition.mineLangs
    }
  }

  class Listener extends Actor {
    // Create and signal the worker actors
    context.actorOf(Props[Worker], "worker0") ! 'Catalogue
    context.actorOf(Props[Worker], "worker1") ! 'Language

    def printCompleteScape() =
      if (category.isDefined && language.isDefined) {
        Acquisition.printScrape(language.get, category.get)
        context.system.shutdown()
        appEnd()
      }

    def receive = {
      case content: TreeSet[String] =>
        language = Some(content)
        printCompleteScape()
      case content: Seq[(String, Int)] =>
        category = Some(content)
        printCompleteScape()
      case whatever => logInfo(whatever.toString)
    } // def receive
  }
} // object Concurrent

trait AppCommons {
  val execStart = System.currentTimeMillis()
  System.setProperty("http.agent", "*")

  def logInfo(info: String) {
    println(f"[Info][${System.currentTimeMillis() - execStart}%5d ms]" + info)
  }

  def appEnd() { logInfo("Run succesfully completed") }
}

// Main entry for sequential version (slower)
object GhettoParserSeq extends App with AppCommons {
  Concurrent.logInfo("Sequential version started")
  Acquisition.printScrape()
  appEnd()
}

// Entry for parallel version (faster)
object GhettoParserPar extends App {
  Concurrent.logInfo("Parallel version started")
  ActorSystem("Main").actorOf(Props[Concurrent.Listener])
}


$ include "seed7_05.s7i";
  include "gethttp.s7i";
  include "scanstri.s7i";

const type: popularityHash is hash [string] integer;
const type: rankingHash is hash [integer] array string;

const func array string: getLangs (in string: buffer) is func
  result
    var array string: langs is 0 times "";
  local
    var integer: pos is 0;
  begin
    pos := pos(buffer, "Category:");
    while pos <> 0 do
      pos +:= 9;
      langs &:= buffer[pos .. pred(pos(buffer, '"', pos))];
      pos := pos(buffer, "Category:", pos);
    end while;
  end func;

const proc: main is func
  local
    var string: categories is "";
    var popularityHash: popularity is popularityHash.value;
    var rankingHash: ranking is rankingHash.value;
    var array integer: numList is 0 times 0;
    var string: lang is "";
    var integer: pos is 0;
    var string: numStri is "";
    var integer: listIdx is 0;
    var integer: index is 0;
    var integer: place is 1;
  begin
    categories := getHttp("www.rosettacode.org/w/index.php?title=Special:Categories&limit=5000");
    for lang range getLangs(getHttp("rosettacode.org/mw/api.php?action=query&list=categorymembers&\
                                    \cmtitle=Category:Programming_Languages&cmlimit=500&format=json")) do
      pos := pos(categories, "title=\"Category:" & lang);
      if pos <> 0 then
        pos := pos(categories, "</a>", succ(pos));
        if pos <> 0 then
          pos := pos(categories, "(", succ(pos));
          if pos <> 0 then
            numStri := categories[succ(pos) len 10];
            popularity @:= [lang] integer parse getDigits(numStri);
          end if;
        end if;
      end if;
    end for;
    ranking := flip(popularity);
    numList := sort(keys(ranking));
    for listIdx range maxIdx(numList) downto minIdx(numList) do
      for key index range ranking[numList[listIdx]] do
        writeln(place lpad 3 <& ". " <& numList[listIdx] <& " - " <& ranking[numList[listIdx]][index]);
      end for;
      place +:= length(ranking[numList[listIdx]]);
    end for;
  end func;


require('MediaWiki::API')

var api = %O<MediaWiki::API>.new(
    Hash(api_url => 'http://rosettacode.org/mw/api.php')
)

var languages = []
var gcmcontinue
loop {
    var apih = api.api(
        Hash(
            action      => 'query',
            generator   => 'categorymembers',
            gcmtitle    => 'Category:Programming Languages',
            gcmlimit    => 250,
            prop        => 'categoryinfo',
            gcmcontinue => gcmcontinue,
        )
    )

    languages.append(apih{:query}{:pages}.values...)
    gcmcontinue = apih{:continue}{:gcmcontinue}
    gcmcontinue || break
}

languages.each { |lang|
    lang{:title} -= /^Category:/
    lang{:categoryinfo}{:size} := 0
}

var sorted_languages = languages.sort_by { |lang|
    -lang{:categoryinfo}{:size}
}

sorted_languages.each_kv { |i, lang|
    printf("%3d. %20s - %3d\n", i+1, lang{:title}, lang{:categoryinfo}{:size})
}


-include "url.sno"
        http.recl = "K,32767"  ;* Read next 32767 characters
                               ;*   of very long lines.

        rclangs = "http://rosettacode.org/mw/api.php?"
+          "format=xml&action=query&generator=categorymembers&"
+          "gcmtitle=Category:Programming%20Languages&"
+          "gcmlimit=500&prop=categoryinfo"

        languagepat = arb "<page" arb 'title="Category:'
+          break('"') . lang arb 'pages="' break('"') . count

        langtable = table(500, 20)

        url.open(.fin, rclangs, http.recl)          :s(read)
        output = "Cannot open rosettacode site."    :(end)

read    line = line fin                             :f(done)
get     line languagepat =                          :f(read)
        langtable<lang> = langtable<lang> + count   :(get)

done    langarray = rsort(langtable,2)              :s(write)
        output = "No languages found."              :(end)

write   n = n + 1
        output = lpad(n ". ", 5) lpad(langarray<n, 2>, 4)
+          " - " langarray<n,1>                     :s(write)

        url.close(.fin)
end


copy "http://rosettacode.org/wiki/Category:Programming_Languages" lang.html, replace
import delimited lang.html, delim("@") enc("utf-8") clear
keep if ustrpos(v1,"/wiki/Category:")
gen i = ustrpos(v1,"title=")
gen j = ustrpos(v1,char(34),i+1)
gen k = ustrpos(v1,char(34),j+1)
gen s = usubstr(v1,j,k-j+1)
keep if usubstr(s,2,9)=="Category:"
gen lang=usubstr(s,11,ustrlen(s)-11)
keep lang
save lang, replace

copy "http://rosettacode.org/mw/index.php?title=Special:Categories&limit=5000" categ.html, replace
import delimited categ.html, delim("@") enc("utf-8") clear
keep if ustrpos(v1,"/wiki/Category:") & ustrpos(v1,"member")
gen i = ustrpos(v1,"title=")
gen j = ustrpos(v1,char(34),i+1)
gen k = ustrpos(v1,char(34),j+1)
gen s = usubstr(v1,j,k-j+1)
keep if usubstr(s,2,9)=="Category:"
gen lang=usubstr(s,11,ustrlen(s)-11)
drop i j k s
gen i = ustrrpos(v1,"(")
gen j = ustrrpos(v1,")")
gen s = usubstr(v1,i,j-i+1)
gen k = ustrpos(s," ")
gen t = usubstr(s,2,k-1)
destring t, gen(count)
drop v1 i j k s t
merge 1:1 lang using lang, keep(2 3) nogen
replace count=0 if missing(count)
gsort -count lang
gen rank=1
replace rank=rank[_n-1]+(count[_n]!=count[_n-1]) in 2/l
save tasks, replace


package require Tcl 8.5
package require http

set response [http::geturl http://rosettacode.org/mw/index.php?title=Special:Categories&limit=8000]

array set ignore {
    "Basic language learning"           1
    "Encyclopedia"                      1
    "Implementations"                   1
    "Language Implementations"          1
    "Language users"                    1
    "Maintenance/OmitCategoriesCreated" 1
    "Programming Languages"             1
    "Programming Tasks"                 1
    "RCTemplates"                       1
    "Solutions by Library"              1
    "Solutions by Programming Language" 1
    "Solutions by Programming Task"     1
    "Unimplemented tasks by language"   1
    "WikiStubs"                         1
    "Examples needing attention"	1
    "Impl needed"			1
}
# need substring filter
proc filterLang {n} {
    return [expr {[string first "User" $n] > 0}]
}
# (sorry the 1 double quote in the regexp kills highlighting)
foreach line [split [http::data $response] \n] {
    if {[regexp {title..Category:([^"]+).* \((\d+) members\)} $line -> lang num]} {
        if {![info exists ignore($lang)] && ![filterLang $lang]} {
            lappend langs [list $num $lang]
        }
    }
}

foreach entry [lsort -integer -index 0 -decreasing $langs] {
    lassign $entry num lang
    puts [format "%d. %d - %s" [incr i] $num $lang]
}


$$ MODE TUSCRIPT
remotedata = REQUEST ("http://www.rosettacode.org/mw/index.php?title=Special:Categories&limit=5000")
allmembers=allnames=""
COMPILE
LOOP d=remotedata
IF (d.sw."<li>") THEN
 name=EXTRACT (d,":<<a<><%>>:"|,":<</a>>:")
 IF (name.eq."Language users") CYCLE
 IF (name.sw."Unimplemented tasks") CYCLE
 IF (name.sw."Programming") CYCLE
 IF (name.sw."Solutions") CYCLE
 IF (name.sw."Garbage") CYCLE
 IF (name.sw."Typing") CYCLE
 IF (name.sw."BASIC LANG") CYCLE
 IF (name.ew."USER") CYCLE
 IF (name.ew."tasks") CYCLE
 IF (name.ew."attention") CYCLE
 IF (name.ew."related") CYCLE
 IF (name.ct."*omit*") CYCLE
 IF (name.ct.":*Categor*:") CYCLE
 IF (name.ct.":WikiSTUBS:") CYCLE
 IF (name.ct.":Impl needed:") CYCLE
 IF (name.ct.":Implementations:") CYCLE
 IF (name.ct.":':") name = EXCHANGE (name,":'::")
 members = STRINGS (d,":><1<>>/><<> member:")
 IF (members!="") THEN
  allmembers=APPEND (allmembers,members)
  allnames  =APPEND (allnames,name)
 ENDIF
ENDIF
ENDLOOP
index      = DIGIT_INDEX (allmembers)
index      = REVERSE (index)
allmembers = INDEX_SORT  (allmembers,index)
allnames   = INDEX_SORT  (allnames,  index)
ERROR/STOP CREATE ("list",SEQ-E,-std-)
time=time(),balt=nalt=""
FILE "list" = time
LOOP n, a=allnames,b=allmembers
 IF (b==balt) THEN
   nr=nalt
 ELSE
   nalt=nr=n
 ENDIF
 content=concat (nr,". ",a," --- ",b)
 FILE "list" = CONTENT
 balt=b
ENDLOOP
ENDCOMPILE


curl 'http://rosettacode.org/mw/index.php?title=Special:Categories&limit=5000' |
sed -nre 's/^<li.*title="Category:([^"(]+)".*\(([0-9]+) members\).*/\2 - \1/p' |
sort -nr | awk '{printf "%2d. %s\n",NR,$0}'


    '''''''''''''''''''''''''''''''''''''''''''''
    ' Rosetta Code/Rank Languages by Popularity '
    '          VBScript Implementation          '
    '...........................................'

'API Links (From C Code)
URL1 = "http://www.rosettacode.org/mw/api.php?format=json&action=query&generator=categorymembers&gcmtitle=Category:Programming%20Languages&gcmlimit=500&prop=categoryinfo&rawcontinue"
URL2 = "http://www.rosettacode.org/mw/api.php?format=json&action=query&generator=categorymembers&gcmtitle=Category:Programming%20Languages&gcmlimit=500&prop=categoryinfo&gcmcontinue="

'Get Contents of the API from the Web...
Function ScrapeGoat(link)
    On Error Resume Next
    ScrapeGoat = ""
    Err.Clear
    Set objHttp = CreateObject("Msxml2.ServerXMLHTTP")
    objHttp.Open "GET", link, False
    objHttp.Send
    If objHttp.Status = 200 And Err = 0 Then ScrapeGoat = objHttp.ResponseText
    Set objHttp = Nothing
End Function

'HACK: Setup HTML for help of my partner/competitor that is better than me, JavaScript...
Set HTML = CreateObject("HtmlFile")
Set HTMLWindow = HTML.ParentWindow

    ''''''''''''''''''''
    ' Main code begins '
    '..................'

On Error Resume Next

isComplete = 0    ' 1 -> Complete Already
cntLoop = 0       ' Counts Number of Loops Done
Set outputData = CreateObject("Scripting.Dictionary")

Do
    'Scrape Data From API
    If cntLoop = 0 Then strData = ScrapeGoat(URL1) Else strData = ScrapeGoat(URL2 & gcmCont)
    If Len(strData) = 0 Then
        Set HTML = Nothing
        WScript.StdErr.WriteLine "Processing of data stopped because API query failed."
        WScript.Quit(1)
    End If

    'Parse JSON HACK
    HTMLWindow.ExecScript "var json = " & strData, "JavaScript"
    Set ObjJS = HTMLWindow.json

    Err.Clear    'Test if Query is Complete Already
    batchCompl = ObjJS.BatchComplete
    If Err.Number = 438 Then
        'Query not yet complete. Get gcmContinue instead.
        gcmCont = ObjJS.[Query-Continue].CategoryMembers.gcmContinue
    Else
        isComplete = 1    'Yes!
    End If

    'HACK #2: Put all language page ids into a JS array to be accessed by VBScript
    HTMLWindow.ExecScript "var langs=new Array(); for(var lang in json.query.pages){langs.push(lang);}" & _
                          "var nums=langs.length;", "JavaScript"
    Set arrLangs = HTMLWindow.langs
    arrLength = HTMLWindow.nums

    For i = 0 to arrLength - 1
        BuffStr = "ObjJS.Query.Pages.[" & Eval("arrLangs.[" & i & "]") & "]"
        EachStr = Eval(BuffStr & ".title")

        Err.Clear
        CntLang =  Eval(BuffStr & ".CategoryInfo.Pages")
        If InStr(EachStr, "Category:") = 1 And Err.Number = 0 Then
            outputData.Add Replace(EachStr, "Category:", "", 1, 1), CntLang
        End If
    Next

    cntLoop = cntLoop + 1
Loop While isComplete = 0
'The outputData now contains the data we need. We should now sort and print it!

'Make a 2D array with copy of outputData
arrRelease = Array()
ReDim arrRelease(UBound(outputData.Keys), 1)

outKeys = outputData.Keys
outItem = outputData.Items
For i = 0 To UBound(outKeys)
    arrRelease(i, 0) = outKeys(i)
    arrRelease(i, 1) = outItem(i)
Next

'Bubble Sort (Greatest to Least Number of Examples)
For i = 0 to UBound(arrRelease, 1)
    For j = 0 to UBound(arrRelease, 1) - 1
        If arrRelease(j, 1) < arrRelease(j + 1, 1) Then
            temp1 = arrRelease(j + 1, 0)
            temp2 = arrRelease(j + 1, 1)
            arrRelease(j + 1, 0) = arrRelease(j, 0)
            arrRelease(j + 1, 1) = arrRelease(j, 1)
            arrRelease(j, 0) = temp1
            arrRelease(j, 1) = temp2
        End If
    Next
Next

'Save contents to file instead to support Unicode Names
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set txtOut = objFSO.CreateTextFile(".\OutVBRC.txt", True, True)

txtOut.WriteLine "As of " & Now & ", RC has " & UBound(arrRelease) + 1 & " languages."
txtOut.WriteLine ""
For i = 0 to UBound(arrRelease)
    txtOut.WriteLine arrRelease(i, 1) & " Examples - " & arrRelease(i, 0)
Next

'Successfully Done :)
Set HTML = Nothing
Set objFSO = Nothing
WScript.Quit(0)


/* rc_rank_languages_by_popularity.wren */

import "./pattern" for Pattern
import "./fmt" for Fmt

var CURLOPT_URL = 10002
var CURLOPT_FOLLOWLOCATION = 52
var CURLOPT_WRITEFUNCTION = 20011
var CURLOPT_WRITEDATA = 10001

foreign class Buffer {
    construct new() {}  // C will allocate buffer of a suitable size

    foreign value       // returns buffer contents as a string
}

foreign class Curl {
    construct easyInit() {}

    foreign easySetOpt(opt, param)

    foreign easyPerform()

    foreign easyCleanup()
}

var curl = Curl.easyInit()

var getContent = Fn.new { |url|
    var buffer = Buffer.new()
    curl.easySetOpt(CURLOPT_URL, url)
    curl.easySetOpt(CURLOPT_FOLLOWLOCATION, 1)
    curl.easySetOpt(CURLOPT_WRITEFUNCTION, 0)  // write function to be supplied by C
    curl.easySetOpt(CURLOPT_WRITEDATA, buffer)
    curl.easyPerform()
    return buffer.value
}

var unescs = [
    ["_", " "],
    ["\%2B", "+"],
    ["\%C3\%A9", "é"],
    ["\%C3\%A0", "à"],
    ["\%C5\%8D", "ō"],
    ["\%C3\%A6", "æ"],
    ["\%CE\%9C", "μ"],
    ["\%D0\%9C\%D0\%9A", "МК"],
    ["\%E0\%AE\%89\%E0\%AE\%AF\%E0\%AE\%BF\%E0\%AE\%B0\%E0\%AF\%8D", "உயிர்"]
]

var unescape = Fn.new { |text|
    for (u in unescs) text = text.replace(u[0], u[1])
    return text
}

var url1 = "https://rosettacode.org/wiki/Category:Programming_Languages"
var content1 = getContent.call(url1)
var p1 = Pattern.new("<li><a href/=\"//wiki//Category:[+1^\"]\"")
var matches1 = p1.findAll(content1)
var languages = matches1.map { |l| unescape.call(l.capsText[0]) }.toList[0..-4] // the last 3 are spurious
var p2 = Pattern.new("\">[+1^<]<//a>\u200f\u200e ([/d~,#03/d] member~s)")
var url2 = "http://rosettacode.org/mw/index.php?title=Special:Categories&limit=4000"
var content2 = getContent.call(url2)
var matches2 = p2.findAll(content2)
var results = []
for (m in matches2) {
    var language = m.capsText[0]
    if (languages.contains(language)) {
        var numEntries = Num.fromString(m.capsText[1].replace(",", ""))
        results.add([language, numEntries])
    }
}
results.sort { |a, b| a[1] > b[1] }
System.print("Languages with most entries as at 4 January, 2022:")
var rank = 0
var lastScore = 0
var lastRank = 0
for (i in 0...results.count) {
    var pair = results[i]
    var eq = " "
    rank = i + 1
    if (lastScore == pair[1]) {
        eq = "="
        rank = lastRank
    } else {
        lastScore = pair[1]
        lastRank = rank
    }
    Fmt.print("$-3d$s  $-20s  $,5d", rank, eq, pair[0], pair[1])
}


var [const] CURL=Import("zklCurl"), YAJL=Import("zklYAJL")[0];

fcn getLangCounts(language){ // -->( (count,lang), ...)
   continueValue,tasks,curl := "",List(), CURL();  // "nm\0nm\0...."
   do{	// eg 5 times
      page:=curl.get(("http://rosettacode.org/mw/api.php?"
         "format=json"
	 "&action=query"
	 "&generator=categorymembers"
	 "&gcmtitle=Category:Programming%%20Languages"
	 "&gcmlimit=500"
	 "&prop=categoryinfo"
	 "&rawcontinue"		// remove warning
	 "&gcmcontinue=%s")
	 .fmt(continueValue));
      page=page[0].del(0,page[1]);  // get rid of HTML header
      json:=YAJL().write(page).close();

      json["query"]["pages"].howza(9).pump(tasks,fcn(d){ #dictionary values,only
         // { title:Category:AWK,categoryinfo:{ pages:398,size:401,... },... }
         // Gotta take care of no categoryinfo case
	 count:=d.find("categoryinfo",Dictionary).find("size",0);  // or pages?
	 if(count<300) return(Void.Skip);  // prune
         T(count,d["title"].del(0,9));     // "Category:RPL" --> "RPL"
      });

      if(continueValue=json.find("query-continue"))
      	// subcat|524558580a52455858|4331 or void
	 continueValue=continueValue["categorymembers"]["gcmcontinue"];
   }while(continueValue);
   tasks
}

langCounts:=getLangCounts()   .sort(fcn(a,b){ a[0]>b[0] });	// reverse sort

println("Most popular Rosetta Code languages as of ",Time.Date.prettyDay());
foreach n,name in ([1..15].zip(langCounts))
   { println("%2d: %3d %s".fmt(n,name.xplode())) }


V text =
‘---------- Ice and Fire ------------

fire, in end will world the say Some
ice. in say Some
desire of tasted I've what From
fire. favor who those with hold I

... elided paragraph last ...

Frost Robert -----------------------’

L(line) text.split("\n")
   print(reversed(line.split(‘ ’)).join(‘ ’))


PROC Reverse(CHAR ARRAY src,dst)
  BYTE i,j,k,beg,end

  i=1 j=src(0)
  WHILE j>0
  DO
    WHILE j>0 AND src(j)=$20
    DO j==-1 OD
    IF j=0 THEN
      EXIT
    ELSE
      end=j
    FI

    WHILE j>0 AND src(j)#$20
    DO j==-1 OD
    beg=j+1

    IF i>1 THEN
      dst(i)=$20 i==+1
    FI

    FOR k=beg TO end
    DO
      dst(i)=src(k) i==+1
    OD
  OD
  dst(0)=i-1
RETURN

PROC Test(CHAR ARRAY src)
  CHAR ARRAY dst(40)

  Reverse(src,dst)
  PrintE(dst)
RETURN

PROC Main()
  Test("---------- Ice and Fire ------------")
  Test("")
  Test("fire, in end will world the say Some")
  Test("ice. in say Some")
  Test("desire of tasted I've what From")
  Test("fire. favor who those with hold I")
  Test("")
  Test("... elided paragraph last ...")
  Test("")
  Test("Frost Robert -----------------------")
RETURN


package Simple_Parse is
   -- a very simplistic parser, useful to split a string into words

   function Next_Word(S: String; Point: in out Positive)
		     return String;
   -- a "word" is a sequence of non-space characters
   -- if S(Point .. S'Last) holds at least one word W
   -- then  Next_Word increments Point by len(W) and returns W.
   -- else  Next_Word sets Point to S'Last+1 and returns ""

end Simple_Parse;


integer j;
list l, x;
text s, t;

l = list("---------- Ice and Fire ------------",
         "",
         "fire, in end will world the say Some",
         "ice. in say Some",
         "desire of tasted I've what From",
         "fire. favor who those with hold I",
         "",
         "... elided paragraph last ...",
         "",
         "Frost Robert -----------------------");

for (, t in l) {
    file().b_affix(t).list(x, 0);
    for (j, s in x.reverse) {
        o_space(sign(j));
        o_text(s);
    }
    o_newline();
}


# returns original phrase with the order of the words reversed #
# a word is a sequence of non-blank characters                 #
PROC reverse word order = ( STRING original phrase )STRING:
     BEGIN
        STRING words reversed := "";
        STRING separator      := "";
        INT    start pos      := LWB original phrase;
        WHILE
            # skip leading spaces #
            WHILE IF start pos <= UPB original phrase
                  THEN original phrase[ start pos ] = " "
                  ELSE FALSE
                  FI
            DO start pos +:= 1
            OD;
            start pos <= UPB original phrase
        DO
            # have another word, find it #
            INT end pos := start pos;
            WHILE IF end pos <= UPB original phrase
                  THEN original phrase[ end pos ] /= " "
                  ELSE FALSE
                  FI
            DO end pos +:= 1
            OD;
            ( original phrase[ start pos : end pos - 1 ] + separator ) +=: words reversed;
            separator := " ";
            start pos := end pos + 1
        OD;
        words reversed
     END # reverse word order # ;

# reverse the words in the lines as per the task #
print( ( reverse word order ( "--------- Ice and Fire ------------ " ), newline ) );
print( ( reverse word order ( "                                    " ), newline ) );
print( ( reverse word order ( "fire, in end will world the say Some" ), newline ) );
print( ( reverse word order ( "ice. in say Some                    " ), newline ) );
print( ( reverse word order ( "desire of tasted I've what From     " ), newline ) );
print( ( reverse word order ( "fire. favor who those with hold I   " ), newline ) );
print( ( reverse word order ( "                                    " ), newline ) );
print( ( reverse word order ( "... elided paragraph last ...       " ), newline ) );
print( ( reverse word order ( "                                    " ), newline ) );
print( ( reverse word order ( "Frost Robert -----------------------" ), newline ) )


on run

    unlines(map(reverseWords, |lines|("---------- Ice and Fire ------------

fire, in end will world the say Some
ice. in say Some
desire of tasted I've what From
fire. favor who those with hold I

... elided paragraph last ...

Frost Robert -----------------------")))

end run

-- GENERIC FUNCTIONS ---------------------------------------------------------

-- reverseWords :: String -> String
on reverseWords(str)
    unwords(|reverse|(|words|(str)))
end reverseWords

-- |reverse| :: [a] -> [a]
on |reverse|(xs)
    if class of xs is text then
        (reverse of characters of xs) as text
    else
        reverse of xs
    end if
end |reverse|

-- |lines| :: Text -> [Text]
on |lines|(str)
    splitOn(linefeed, str)
end |lines|

-- |words| :: Text -> [Text]
on |words|(str)
    splitOn(space, str)
end |words|

-- ulines :: [Text] -> Text
on unlines(lstLines)
    intercalate(linefeed, lstLines)
end unlines

-- unwords :: [Text] -> Text
on unwords(lstWords)
    intercalate(space, lstWords)
end unwords

-- splitOn :: Text -> Text -> [Text]
on splitOn(strDelim, strMain)
    set {dlm, my text item delimiters} to {my text item delimiters, strDelim}
    set lstParts to text items of strMain
    set my text item delimiters to dlm
    lstParts
end splitOn

-- interCalate :: Text -> [Text] -> Text
on intercalate(strText, lstText)
    set {dlm, my text item delimiters} to {my text item delimiters, strText}
    set strJoined to lstText as text
    set my text item delimiters to dlm
    strJoined
end intercalate

-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
    tell mReturn(f)
        set lng to length of xs
        set lst to {}
        repeat with i from 1 to lng
            set end of lst to lambda(item i of xs, i, xs)
        end repeat
        return lst
    end tell
end map

-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: Handler -> Script
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property lambda : f
        end script
    end if
end mReturn


100 DATA"---------- ICE AND FIRE ------------"
110 DATA"                                    "
120 DATA"FIRE, IN END WILL WORLD THE SAY SOME"
130 DATA"ICE. IN SAY SOME                    "
140 DATA"DESIRE OF TASTED I'VE WHAT FROM     "
150 DATA"FIRE. FAVOR WHO THOSE WITH HOLD I   "
160 DATA"                                    "
170 DATA"... ELIDED PARAGRAPH LAST ...       "
180 DATA"                                    "
190 DATA"FROST ROBERT -----------------------"

200 FOR L = 1 TO 10
210     READ T$
220     I = LEN(T$)
240     IF I THEN GOSUB 300 : PRINT W$; : IF I THEN PRINT " "; : GOTO 240
250     PRINT
260 NEXT L
270 END

300 W$ = ""
310 FOR I = I TO 1 STEP -1
320     IF MID$(T$, I, 1) = " " THEN NEXT I : RETURN
330 FOR I = I TO 1 STEP -1
340     C$ = MID$(T$, I, 1)
350     IF C$ <> " " THEN  W$ = C$ + W$ : NEXT I
360 RETURN


text: {
---------- Ice and Fire ------------

fire, in end will world the say Some
ice. in say Some
desire of tasted I've what From
fire. favor who those with hold I

... elided paragraph last ...

Frost Robert -----------------------}

reversed: map split.lines text =>
    [join.with:" " reverse split.words &]

print join.with:"\n" reversed


Data := "
(Join`r`n
---------- Ice and Fire ------------

fire, in end will world the say Some
ice. in say Some
desire of tasted I've what From
fire. favor who those with hold I

... elided paragraph last ...

Frost Robert -----------------------
)"

Loop, Parse, Data, `n, `r
{
	Loop, Parse, A_LoopField, % A_Space
		Line := A_LoopField " " Line
	Output .= Line "`n", Line := ""
}
MsgBox, % RTrim(Output, "`n")


# syntax: GAWK -f REVERSE_WORDS_IN_A_STRING.AWK
BEGIN {
    text[++i] = "---------- Ice and Fire ------------"
    text[++i] = ""
    text[++i] = "fire, in end will world the say Some"
    text[++i] = "ice. in say Some"
    text[++i] = "desire of tasted I've what From"
    text[++i] = "fire. favor who those with hold I"
    text[++i] = ""
    text[++i] = "... elided paragraph last ..."
    text[++i] = ""
    text[++i] = "Frost Robert -----------------------"
    leng = i
    for (i=1; i<=leng; i++) {
      n = split(text[i],arr," ")
      for (j=n; j>0; j--) {
        printf("%s ",arr[j])
      }
      printf("\n")
    }
    exit(0)
}


PRINT REV$("---------- Ice and Fire ------------")
PRINT
PRINT REV$("fire, in end will world the say Some")
PRINT REV$("ice. in say Some                    ")
PRINT REV$("desire of tasted I've what From     ")
PRINT REV$("fire. favor who those with hold I   ")
PRINT
PRINT REV$("... elided paragraph last ...       ")
PRINT
PRINT REV$("Frost Robert -----------------------")


@echo off

::The Main Thing...
cls
echo.
call :reverse "---------- Ice and Fire ------------"
call :reverse
call :reverse "fire, in end will world the say Some"
call :reverse "ice. in say Some"
call :reverse "desire of tasted I've what From"
call :reverse "fire. favor who those with hold I"
call :reverse
call :reverse "... elided paragraph last ..."
call :reverse
call :reverse "Frost Robert -----------------------"
echo.
pause>nul
exit
::/The Main Thing...

::The Function...
:reverse
set reversed=&set word=&set str=%1
:process
for /f "tokens=1,*" %%A in (%str%) do (
	set str=%%B
	set word=%%A
)
set reversed=%word% %reversed%
set str="%str%"
if not %str%=="" goto process

echo.%reversed%
goto :EOF
::/The Function...


source = freefile
open (source, "m:\text.txt")
textEnt$ = ""
dim textSal$(size(source)*8)
linea = 0

while not eof(source)
	textEnt$ = readline(source)
	linea += 1
	textSal$[linea] = textEnt$
end while

for n = size(source) to 1 step -1
	print textSal$[n];
next n
close source


      PRINT FNreverse("---------- Ice and Fire ------------")\
      \    'FNreverse("")\
      \    'FNreverse("fire, in end will world the say Some")\
      \    'FNreverse("ice. in say Some")\
      \    'FNreverse("desire of tasted I've what From")\
      \    'FNreverse("fire. favor who those with hold I")\
      \    'FNreverse("")\
      \    'FNreverse("... elided paragraph last ...")\
      \    'FNreverse("")\
      \    'FNreverse("Frost Robert -----------------------")
      END

      DEF FNreverse(s$)
      LOCAL sp%
      sp%=INSTR(s$," ")
      IF sp% THEN =FNreverse(MID$(s$,sp%+1))+" "+LEFT$(s$,sp%-1) ELSE =s$


("---------- Ice and Fire ------------

fire, in end will world the say Some
ice. in say Some
desire of tasted I've what From
fire. favor who those with hold I

... elided paragraph last ...

Frost Robert -----------------------"
  : ?text
& ( reverse
  =   token tokens reversed
    .   :?tokens
      &   whl
        ' ( @( !arg
             : ?token (" "|\t|\r) ?arg
             )
          & !tokens !token:?tokens
          )
      & !tokens !arg:?tokens
      & :?reversed
      &   whl
        ' ( !tokens:%?token %?tokens
          & " " !token !reversed:?reversed
          )
      & !tokens !reversed:?reversed
      & str$!reversed
  )
& :?output
&   whl
  ' ( @(!text:?line \n ?text)
    & !output reverse$!line \n:?output
    )
& !output reverse$!text:?output
& out$str$!output
);


blsq ) "It is not raining"wd<-wd
"raining not is It"
blsq ) "ice. in say some"wd<-wd
"some say in ice."


#include <stdio.h>
#include <ctype.h>

void rev_print(char *s, int n)
{
        for (; *s && isspace(*s); s++);
        if (*s) {
                char *e;
                for (e = s; *e && !isspace(*e); e++);
                rev_print(e, 0);
                printf("%.*s%s", (int)(e - s), s, " " + n);
        }
        if (n) putchar('\n');
}

int main(void)
{
        char *s[] = {
                "---------- Ice and Fire ------------",
                "                                    ",
                "fire, in end will world the say Some",
                "ice. in say Some                    ",
                "desire of tasted I've what From     ",
                "fire. favor who those with hold I   ",
                "                                    ",
                "... elided paragraph last ...       ",
                "                                    ",
                "Frost Robert -----------------------",
                0
        };
        int i;
        for (i = 0; s[i]; i++) rev_print(s[i], 1);

        return 0;
}


using System;

public class ReverseWordsInString
{
    public static void Main(string[] args)
    {
        string text = @"
            ---------- Ice and Fire ------------

            fire, in end will world the say Some
            ice. in say Some
            desire of tasted I've what From
            fire. favor who those with hold I

            ... elided paragraph last ...

            Frost Robert -----------------------
            ";

        foreach (string line in text.Split(Environment.NewLine)) {
            //Splits on any whitespace, not just spaces
            string[] words = line.Split(default(char[]), StringSplitOptions.RemoveEmptyEntries);
            Array.Reverse(words);
            WriteLine(string.Join(" ", words));
        }
    }
}


#include <algorithm>
#include <functional>
#include <string>
#include <iostream>
#include <vector>

//code for a C++11 compliant compiler
template <class BidirectionalIterator, class T>
void block_reverse_cpp11(BidirectionalIterator first, BidirectionalIterator last, T const& separator) {
   std::reverse(first, last);
   auto block_last = first;
   do {
      using std::placeholders::_1;
      auto block_first = std::find_if_not(block_last, last,
         std::bind(std::equal_to<T>(),_1, separator));
      block_last = std::find(block_first, last, separator);
      std::reverse(block_first, block_last);
   } while(block_last != last);
}

//code for a C++03 compliant compiler
template <class BidirectionalIterator, class T>
void block_reverse_cpp03(BidirectionalIterator first, BidirectionalIterator last, T const& separator) {
   std::reverse(first, last);
   BidirectionalIterator block_last = first;
   do {
      BidirectionalIterator block_first = std::find_if(block_last, last,
         std::bind2nd(std::not_equal_to<T>(), separator));
      block_last = std::find(block_first, last, separator);
      std::reverse(block_first, block_last);
   } while(block_last != last);
}

int main() {
   std::string str1[] =
    {
        "---------- Ice and Fire ------------",
        "",
        "fire, in end will world the say Some",
        "ice. in say Some",
        "desire of tasted I've what From",
        "fire. favor who those with hold I",
        "",
        "... elided paragraph last ...",
        "",
        "Frost Robert -----------------------"
    };

   std::for_each(begin(str1), end(str1), [](std::string& s){
      block_reverse_cpp11(begin(s), end(s), ' ');
      std::cout << s << std::endl;
   });

   std::for_each(begin(str1), end(str1), [](std::string& s){
      block_reverse_cpp03(begin(s), end(s), ' ');
      std::cout << s << std::endl;
   });

   return 0;
}


(def poem
  "---------- Ice and Fire ------------

   fire, in end will world the say Some
   ice. in say Some
   desire of tasted I've what From
   fire. favor who those with hold I

   ... elided paragraph last ...

   Frost Robert -----------------------")

(dorun
  (map println (map #(apply str (interpose " " (reverse (re-seq #"[^\s]+" %)))) (clojure.string/split poem #"\n"))))


       program-id. rev-word.
       data division.
       working-storage section.
       1 text-block.
        2 pic x(36) value "---------- Ice and Fire ------------".
        2 pic x(36) value "                                    ".
        2 pic x(36) value "fire, in end will world the say Some".
        2 pic x(36) value "ice. in say Some                    ".
        2 pic x(36) value "desire of tasted I've what From     ".
        2 pic x(36) value "fire. favor who those with hold I   ".
        2 pic x(36) value "                                    ".
        2 pic x(36) value "... elided paragraph last ...       ".
        2 pic x(36) value "                                    ".
        2 pic x(36) value "Frost Robert -----------------------".
       1 redefines text-block.
        2 occurs 10.
         3 text-line pic x(36).
       1 text-word.
        2 wk-len binary pic 9(4).
        2 wk-word pic x(36).
       1 word-stack.
        2 occurs 10.
         3 word-entry.
          4 word-len binary pic 9(4).
          4 word pic x(36).
       1 binary.
        2 i pic 9(4).
        2 pos pic 9(4).
        2 word-stack-ptr pic 9(4).

       procedure division.
           perform varying i from 1 by 1
           until i > 10
               perform push-words
               perform pop-words
           end-perform
           stop run
           .

       push-words.
           move 1 to pos
           move 0 to word-stack-ptr
           perform until pos > 36
               unstring text-line (i) delimited by all space
               into wk-word count in wk-len
               pointer pos
               end-unstring
               add 1 to word-stack-ptr
               move text-word to word-entry (word-stack-ptr)
           end-perform
           .

       pop-words.
           perform varying word-stack-ptr from word-stack-ptr
               by -1
           until word-stack-ptr < 1
               move word-entry (word-stack-ptr) to text-word
               display wk-word (1:wk-len) space with no advancing
           end-perform
           display space
           .
       end program rev-word.


strReversed = '---------- Ice and Fire ------------\n\n
fire, in end will world the say Some\n
ice. in say Some\n
desire of tasted I\'ve what From\n
fire. favor who those with hold I\n\n
... elided paragraph last ...\n\n
Frost Robert -----------------------'

reverseString = (s) ->
  s.split('\n').map((l) -> l.split(/\s/).reverse().join ' ').join '\n'

console.log reverseString(strReversed)


(defun split-and-reverse (str)
  (labels
    ((iter (s lst)
       (let ((s2 (string-trim '(#\space) s)))
         (if s2
             (let ((word-end (position #\space s2)))
               (if (and word-end (< (1+ word-end) (length s2)))
                   (iter (subseq s2 (1+ word-end))
                         (cons (subseq s2 0 word-end) lst))
                   (cons s2 lst)))
               lst))))
  (iter str NIL)))

(defparameter *poem*
  "---------- Ice and Fire ------------

   fire, in end will world the say Some
   ice. in say Some
   desire of tasted I've what From
   fire. favor who those with hold I

   ... elided paragraph last ...

   Frost Robert -----------------------")

(with-input-from-string (s *poem*)
  (loop for line = (read-line s NIL)
        while line
        do (format t "~{~a~#[~:; ~]~}~%" (split-and-reverse line))))


void main() {
    import std.stdio, std.string, std.range, std.algorithm;

    immutable text =
"---------- Ice and Fire ------------

fire, in end will world the say Some
ice. in say Some
desire of tasted I've what From
fire. favor who those with hold I

... elided paragraph last ...

Frost Robert -----------------------";

    writefln("%(%-(%s %)\n%)",
             text.splitLines.map!(r => r.split.retro));
}


program RosettaCode_ReverseWordsInAString;

{$APPTYPE CONSOLE}

uses Classes, Types, StrUtils;

const
  TXT =
  '---------- Ice and Fire -----------'+sLineBreak+
  sLineBreak+
  'fire, in end will world the say Some'+sLineBreak+
  'ice. in say Some'+sLineBreak+
  'desire of tasted I''ve what From'+sLineBreak+
  'fire. favor who those with hold I'+sLineBreak+
  sLineBreak+
  '... elided paragraph last ...'+sLineBreak+
  sLineBreak+
  'Frost Robert -----------------------'+sLineBreak;

var
  i, w: Integer;
  d: TStringDynArray;
begin
  with TStringList.Create do
  try
    Text := TXT;
    for i := 0 to Count - 1 do
    begin
      d := SplitString(Strings[i], #32);
      Strings[i] := '';
      for w := Length(d) - 1 downto 0 do
        Strings[i] := Strings[i] + #32 + d[w];
    end;
    Writeln(Text);
  finally
    Free
  end;
  ReadLn;
end.


(define S #<<
---------- Ice and Fire ------------

fire, in end will world the say Some
ice. in say Some
desire of tasted I've what From
fire. favor who those with hold I

... elided paragraph last ...

Frost Robert -----------------------
>>#)

(for-each writeln
    (for/list ((line (string-split S "\n")))
        (string-join (reverse (string-split line " ")) " ")))


import extensions;
import system'routines;

public program()
{
    var text := new string[]{"---------- Ice and Fire ------------",
                  "",
                  "fire, in end will world the say Some",
                  "ice. in say Some",
                  "desire of tasted I've what From",
                  "fire. favor who those with hold I",
                  "",
                  "... elided paragraph last ...",
                  "",
                  "Frost Robert -----------------------"};

    text.forEach:(line)
    {
        line.splitBy:" ".sequenceReverse().forEach:(word)
        {
            console.print(word," ")
        };
        console.writeLine()
    }
}


defmodule RC do
  def reverse_words(txt) do
    txt |> String.split("\n")       # split lines
        |> Enum.map(&(              # in each line
             &1 |> String.split       # split words
                |> Enum.reverse       # reverse words
                |> Enum.join(" ")))   # rejoin words
        |> Enum.join("\n")          # rejoin lines
  end
end


reversedPoem =
  String.trim """
    ---------- Ice and Fire ------------

    fire, in end will world the say Some
    ice. in say Some
    desire of tasted I've what From
    fire. favor who those with hold I

    ... elided paragraph last ...

    Frost Robert -----------------------
    """

reverseWords string =
  string |> String.words |> List.reverse |> String.join " "

reverseLinesWords string =
  string |> String.lines |> List.map reverseWords |> String.join "\n"

poem =
  reverseLinesWords reversedPoem


(defun reverse-words (line)
  (insert
   (format "%s\n"
           (mapconcat 'identity (reverse (split-string line)) " "))))

(defun reverse-lines (lines)
  (mapcar 'reverse-words lines))

(reverse-lines
 '("---------- Ice and Fire ------------"
   ""
   "fire, in end will world the say Some"
   "ice. in say Some"
   "desire of tasted I've what From"
   "fire. favor who those with hold I"
   ""
   "... elided paragraph last ..."
   ""
   "Frost Robert ----------------------- "))


//Reverse words in a string. Nigel Galloway: July 14th., 2021
["  ---------- Ice and Fire ------------  ";
 "                                        ";
 "  fire, in end will world the say Some  ";
 "  ice. in say Some                      ";
 "  desire of tasted I've what From       ";
 "  fire. favour who those with hold I    ";
 "                                        ";
 "  ... elided paragraph last ...         ";
 "                                        ";
 "  Frost Robert -----------------------  "]|>List.map(fun n->n.Split " "|>Array.filter((<>)"")|>Array.rev|>String.concat " ")|>List.iter(printfn "%s")


USING: io sequences splitting ;
IN: rosetta-code.reverse-words

"---------- Ice and Fire ------------

fire, in end will world the say Some
ice. in say Some
desire of tasted I've what From
fire. favor who those with hold I

... elided paragraph last ...

Frost Robert -----------------------"

"\n" split [ " " split reverse " " join ] map [ print ] each


: not-empty?  dup 0 > ;
: (reverse)  parse-name not-empty? IF recurse THEN type space ;
: reverse  (reverse) cr ;

reverse ---------- Ice and Fire ------------
reverse
reverse fire, in end will world the say Some
reverse ice. in say Some
reverse desire of tasted I've what From
reverse fire. favor who those with hold I
reverse
reverse ... elided paragraph last ...
reverse
reverse Frost Robert -----------------------


 character*40 words
 character*40 reversed
 logical inblank
 ierr=0
 read (5,fmt="(a)",iostat=ierr)words
 do while (ierr.eq.0)
 inblank=.true.
 ipos=1
 do i=40,1,-1
   if(words(i:i).ne.' '.and.inblank) then
     last=i
     inblank=.false.
     end if
    if(.not.inblank.and.words(i:i).eq.' ') then
      reversed(ipos:ipos+last-i)=words(i+1:last)
      ipos=ipos+last-i+1
      inblank=.true.
      end if
     if(.not.inblank.and.i.eq.1) then
       reversed(ipos:ipos+last-1)=words(1:last)
       ipos=ipos+last
       end if
   end do
 print *,words,'=> ',reversed(1:ipos-1)
 read (5,fmt="(a)",iostat=ierr)words
 end do
 end


' FB 1.05.0 Win64

Sub split (s As String, sepList As String, result() As String, removeEmpty As Boolean = False)
  If s = "" OrElse sepList = "" Then
     Redim result(0)
     result(0) = s
     Return
  End If
  Dim As Integer i, j, count = 0, empty = 0, length
  Dim As Integer position(Len(s) + 1)
  position(0) = 0

  For i = 0 To len(s) - 1
    For j = 0 to Len(sepList) - 1
      If s[i] = sepList[j] Then
        count += 1
        position(count) = i + 1
      End If
    Next j
  Next i

  Redim result(count)
  If count  = 0 Then
    result(0) = s
    Return
  End If

  position(count + 1) = len(s) + 1

  For i = 1 To count + 1
    length = position(i) - position(i - 1) - 1
    result(i - 1 - empty) = Mid(s, position(i - 1) + 1, length)
    If removeEmpty Andalso CBool(length = 0) Then empty += 1
  Next

  If empty > 0 Then Redim Preserve result(count - empty)
End Sub

Dim s As String = "Hey you, Bub!"
Dim a() As String
split(s, " ", a(), true)
Dim reversed As String = ""
For i As Integer = UBound(a) To LBound(a) Step -1
  reversed += a(i)
  If i > LBound(a) Then reversed += " "
Next

Print "Original String = "; s
Print "Reversed String = "; reversed
Print
Print "Press any key to quit"
Sleep


lines=split["\n",
"""---------- Ice and Fire ------------

fire, in end will world the say Some
ice. in say Some
desire of tasted I've what From
fire. favor who those with hold I

 .. elided paragraph last ...

Frost Robert -----------------------"""]

for line = lines
    println[join[" ", reverse[split[%r/\s+/, line]]]]


include "NSLog.incl"

CFStringRef        frostStr
CFArrayRef         frostArr, tempArr
CFMutableStringRef mutStr
NSInteger          i, count

frostStr = @"---------- Ice and Fire ------------\n¬
\n¬
fire, in end will world the say Some\n¬
ice. in say Some\n¬
desire of tasted I've what From\n¬
fire. favor who those with hold I\n¬
\n¬
… elided paragraph last …\n¬
\n¬
Frost Robert -----------------------\n"

frostArr = fn StringComponentsSeparatedByString( frostStr, @"\n" )
count    = fn ArrayCount( frostArr )
mutStr   = fn MutableStringWithCapacity( 0 )

for i = 0 to count - 1
tempArr = fn StringComponentsSeparatedByString( frostArr[i], @" " )
tempArr = fn EnumeratorAllObjects( fn ArrayReverseObjectEnumerator( tempArr ) )
MutableStringAppendString( mutStr, fn ArrayComponentsJoinedByString( tempArr, @" " ) )
MutableStringAppendString( mutStr, @"\n" )
next

NSLog( @"%@", mutStr )

HandleEvents


Public Sub Main()
Dim sString As New String[10]                                     'Array for the input text
Dim sLine As New String[]                                         'Array of each word in a line
Dim siCount0, siCount1 As Short                                   'Counters
Dim sOutput, sReverse, sTemp As String                            'Strings

sString[0] = "---------- Ice And Fire ------------"               'Input text
sString[1] = "                                    "
sString[2] = "fire, in end will world the say Some"
sString[3] = "ice. in say Some                    "
sString[4] = "desire of tasted I've what From     "
sString[5] = "fire. favor who those with hold I   "
sString[6] = "                                    "
sString[7] = "... elided paragraph last ...       "
sString[8] = "                                    "
sString[9] = "Frost Robert -----------------------"

For siCount0 = 0 To 9                                             'To work through each line of input text
  If Trim(sString[siCount0]) = "" Then sString[siCount0] = " "    'If the line is all spaces then make it 1 space

  For Each sTemp In Split(Trim(sString[siCount0]), " ")           'Split the trimmed line by spaces
    sLine.Add(sTemp)                                              'Add each word to the sLine array
  Next

  For siCount1 = sLine.max DownTo 0                               'Loop from the last in the sLine array to 0
    sReverse &= sLine[siCount1] & " "                             'Fill sReverse with words reversed, adding a space
  Next

  sOutput &= Trim(sReverse) & gb.NewLine                          'Add the reversed words to sOutput and add a newline
  sReverse = ""                                                   'Clear sReverse
  sLine.Clear                                                     'Clear sLine array
Next

Print sOutput                                                     'Print the output

End