Dave's Famous Original SETL Server

This page lets you play with SETL without first downloading an implementation or reading documentation.

There are three ways to use this server:

Run a canned example with a parameter.
Run a SETL program from a URL.
Run a pasted/edited program.


Canned example

Compute primes to (Enter)

URL-based service

= compile + execute

Source program URL:

Input data URL:


Direct paste/edit service

= compile + execute

Source program window:

Input data window:


Sources

More or less current, sometimes, depending:

compute-primes.cgi

#! /home/setlorg/bin/setl

const max_n = 10 ** 5;  -- maximum allowed n

if (m := getmap()) = om then stop; end if;

s := m.maxprime;
if (n := val s) /= om and n > 0 then
  n := floor n;
  n min:= max_n;
  print('Content-type: text/html');
  print();
  print('<TITLE>The primes up to '+n+'</TITLE>');
#define cmput(x) print(#x,'=',x)
  cmput({p in {2 .. n} | forall i in {2 .. floor sqrt p} | p mod i /= 0});
  if n < val s then  -- big number, or contains a fraction
    print('<p>The SETL server extends its sincerest apologies for '+
          'imposing the artificial limit of '+n+' when you asked '+
          'so nicely for '+s+'.</p>');
  end if;
else
  print('Content-type: text/html');
  print();
  print('<TITLE>No can do</TITLE>');
  print('Error - positive number required, but got '+pretty s+'.');
end if;

#include "web.setl"

setl-server.cgi

#!/bin/sh

export PATH=~/bin:$PATH

# Limit size of created files to 1MB
ulimit -f 1024

# Limit data seg size, max memory size, and virtual memory to 10MB
ulimit -d 10240
ulimit -m 10240
ulimit -v 10240

# Limit total CPU time to 10 sec.
ulimit -t 10

# Limit max user processes to 10
ulimit -u 10

# Run the "server", a SETL program
setl -3 3<<'.' >/tmp/setl.stdout$$ 2>/tmp/setl.stderr$$

const wget_cmd = 'wget -O - ';

m := getmap();

m_source := m.setl_source ? '';
m_input  := m.setl_input  ? '';
m_url    := m.setl_url    ? '';
m_data   := m.setl_data   ? '';

-- Fetch source text of user program
if not blank m_url then
  m_source := wget(m_url);
end if;

-- Compile it
if not blank m_source then
  m_object := filter('setl -c 2>&1',m_source+'\n');
  if m_object(1) /= '#' then
    printa(stderr, rehigh amp m_object);
    stop 1;
  end if;
else
  printa(stderr, 'No source (no sweat).');
  stop;
end if;

-- Fetch data for the program
if not blank m_data then
  m_input := wget(m_data);
end if;

-- Start example server, directing to stderr any output it may spew
system ('setl daytime-server.setl >&2 &');

-- Run the compiled user program
t := tmpnam();
putfile(t,m_object);
u := tmpnam();
m_output := filter('setl -t'+
                   ' --restricted'+
                   ' --maxmem=100M'+
                   ' --allow-open=localhost:1313,socket'+
                   ' --allow-open=setl.org:1313,socket'+
                   ' '+t+' 2>'+u, m_input);
unlink(t);

-- Report its stdout output
print('<pre>');
print(amp m_output);
print('</pre>');

-- Report its stderr output
putc(stderr, rehigh amp getfile u);
unlink(u);


op blank(s);
  return s('^ *$') = s;
end;

proc wget(url);
  return filter("wget -q -O - '"+escape url+"'");
end;

op rehigh(s);  -- change ANSI highlighting to HTML
  [saved_magic, magic] := [magic, false];
  gsub(s,'\033[1m','<b><i>');
  gsub(s,'\033[0m','</i></b>');
  gsub(s,'\a','');  -- get rid of BEL chars too
  magic := saved_magic;
  return s;
end op;

#include "web.setl"
#include "amp.setl"

.

# Report server output
echo 'Content-type: text/html'
echo ''
echo '<title>SETL Server output</title>'
if test -s /tmp/setl.stdout$$; then
  echo '<h3>stdout</h3>'
  # stdout output is expected to contain markup
  cat /tmp/setl.stdout$$
fi
if test -s /tmp/setl.stderr$$; then
  echo '<h3>stderr</h3>'
  # stderr output is more likely to have been inadvertent
  echo '<pre>'
  cat /tmp/setl.stderr$$
  echo '</pre>'
fi

# Remove scratch files
rm -f /tmp/setl.stdout$$ /tmp/setl.stderr$$

daytime-server.setl

-- A one-shot server to return a line with the current time and date
-- (to the millisecond) and then return input lines in hex.

const ms = 10000;  -- time limit for client to connect, else we quit

sd := open ('1313', 'server-socket');  -- listen on port 1313
if sd = om then stop 1; end if;  -- exit(1) if (e.g.) port in use

[ready] := select([{sd}], ms);  -- wait up to ms for client to connect
if sd notin ready then stop 2; end if;  -- exit(2) on timeout

fd := accept (sd);  -- accept client connection

printa (fd, fdate(tod));  -- send formatted date and time to client

while (line := getline fd) /= om loop  -- get line from client
  printa (fd, hex line);  -- reply with line in hex form
end loop;  -- exit on eof from client

web.setl

--  Please #include me in SETL programs that read from forms.

proc getmap;  -- decode form result into a SETL map
  if getenv 'REQUEST_METHOD' notin {'GET','POST'} then
    print('This script must be referenced with a METHOD of GET or POST.');
    stop 1;
  end if;
  if getenv 'CONTENT_TYPE' /= 'application/x-www-form-urlencoded' then
    print('This script can only be used to decode form results.');
    stop 1;
  end if;
  n := val getenv 'CONTENT_LENGTH';
  s := getn(stdin,n);  -- raw data
  [m, magic] := [magic, false];  -- suppress regexps
  gsub(s,'+',' ');  -- change plusses to blanks
  r := {map_pair unescape x : x in split(s,'&')};  -- decode
  magic := m;  -- restore status of regexp use
  return r;  -- that's it!
end getmap;

op escape(x);  -- convert certain characters to %HH (hex)
  return +/[if c in '.:/#?&=,;~0123456789-_'+
                    'abcdefghijklmnopqrstuvwxyz'+
                    'ABCDEFGHIJKLMNOPQRSTUVWXYZ' then c
            else '%' + hex c
            end : c in x];
end escape;

op unescape(x);  -- convert %HH hex escapes to normal chars
  r := '';
  while #x > 0 loop
    if x(1) = '%' then
      r +:= unhex x(2..3);
      x(1..3) := '';
    else
      r +:= x(1);
      x(1) := '';
    end if;
  end loop;
  return r;
end unescape;

op map_pair(x);  -- equiv. to split(x,'=') on just the first '='
  s := break(x,'=');
  return [s,x(2..1 max #x)];
end map_pair;

amp.setl

-- Useful transformation for text to be placed inside <pre>...</pre>

op amp(s);  -- expand some characters to &...;
  gsub(s,'&','\\&amp;');
  gsub(s,'<','\\&lt;');
  gsub(s,'>','\\&gt;');
  return s;
end amp;

amp

#!/bin/sh
# Handy script to apply the "amp.setl" transformation on stdin
setl '
putchar (amp getfile stdin);
#include "amp.setl"
'

       dB    bacon@cs.nyu.edu