I have added a new program:

flx_tangle --indir=indir --outdir=outdir pattern ...

For each file matching pattern.fdoc in indir (including
subdirectories since it's a regexp) generate a corresponding
file in outdir with extension .flx, consisting of all the

@feilx
...
@

code in the fdoc file. In other words this is a mini-literate
programming tool.

The means you can write documents containing library files
or programs. 

With this program, you can generate at most one felix file
per document.

Of course more hairy algorithm could be implemented, I could
re-implement interscript in Felix .. :)

I may add a tag like:

@expect

which would also generated a filename.expect file.
This would allow all the regression tests to be coded as documents.

I would like to note I have "done a Knuth" here. I wrote this code
because I am confused about the index laws (see previous email).

To get around this I thought to write a document describing the rules,
that way I could see if the specs made sense.

But then it occurred to me that many of the index laws could be
put in a class. Well maybe not, since some of the rules are things like:

typedef fun linearise (A:TYPE) =>
  typematch A with
  | ?V ^ ?X ^ ?Y => V ^ (X * Y)
  endmatch
; 

This is your array coercion! In any case to do that I need to be able to
extract the code from the doc, at least to check it.

Now, for the boasting!

The program below is very compact.
It also uses a lot of HAIRY features of Felix
including regexp directory searching,
iterators, streams and pipelines.

You will note in particular the pipeline especially
the tangling transducer, which illustrates the utter superiority
of fthreads over callbacks.  This is a very simple state machine,
with gotos and all (and gotos are clearly the correct way to do this).

You will note very carefully that

        STATE IS MAINTAINED BY THE PROGRAM COUNTER

If you wrote it as a callback you'd need a variable to record
the state, and to do a switch on every entry to the code
for handling input in that state.

Of course this is *exactly* what the Felix compiler actually
does -- automatic control inversion from a master
routine that does reads an writes to a slave callback.

You will note very carefully that the input source, the
filter the tangler provides, and the concentrator
are all entirely independent.

They capture processing in a module called a device.

I call this ACTIVE PROGRAMMING (because devices
capture the notion of action). This is the opposite
of reactive programming associated with both
OO style and traditional functional programming.

//$ flx_tangle --inoutdir --indir=indir --outdir=outdir pattern ...
//$ processes the file indir/basename.fdoc and generates
//$ the file outdir/basename.flx by collating everything
//$ between @felix and subsequent @ command.
//$
//$ If indidr is specified and outdir is not, the outdir
//$ remains the default current directory.
//$
//$ If inoutdir is specified, indir and outdir are set
//$ to it, and indir and outdir should not be specified.
//$
//$ If no patterns are specified '.*' is used, i.e. all fdoc files
//$ in the input directory (recursively).
//$

// --- COMMAND LINE PROCESSING --------------------------------

val cmdspec : cmdspec_t = 
  ( 
    split-key-value-spec= Empty[string * string],
    multi-valued-keys-spec= Empty[string], 
    single-valued-keys-spec= list("--inoutdir","--indir","--outdir"),
    switches-spec= list("--help"), 
    short-switch-map-spec = Empty[char * string]
  )
;

proc print_help =>
  println$ "Usage: flx_tangle [--indir=indir] [--outdir=outdir] 
[--inoutdir=inoutdir] [regexp1 ...]"
;

val inargs = #System::args;

val outargs = parse-cmdline cmdspec inargs;

var keys = outargs.single-valued-keys;
if 
  ("--inoutdir" in keys) and
  (
    ("--indir" in keys) or
    ("--outdir" in keys) 
  )
do
  println$ "Cannot specify --inoutdir with --indir or --outdir";
  print_help;
  System::exit 1;
done

if "--help" in outargs.switches do
  print_help;
  System::exit 0;
done

// ----- SETUP CONTROL ARGUMENTS ------------------------
var patterns = 
  match outargs.positional with
  | _ ! (_ ! _ as tail) => tail
  | _ ! Empty => list ".*"
;

var indir = keys.get_dflt ("--indir", keys.get_dflt ("--inoutdir", ".") );
var outdir = keys.get_dflt ("--outdir",  keys.get_dflt ("--inoutdir", "."));

// --- FILE SCAN --------------------------------
for base in patterns do
  println$ "Base = " + base;
  for file in FileSystem::regfilesin(indir, base+"\\.fdoc") do
    var infile = Filename::join (indir, file);
    var outfile = Filename::join (outdir, Filename::strip_extension file 
+".flx");
    save_tangle (infile, outfile); 
  done
done

// ---- PIPELINE ---------------------------------
// Source device.
proc filesrc (file:string) (o:oschannel[string]) 
{
  var data = load file;
  for line in split(data,"\n") do write (o, line+"\n"); done
  write(o,"");
}

// Tangling transducer.
proc tangle (i:ischannel[string], o:oschannel[string]) 
{
moredoc:>
  var x = read i;
  if x == "" goto finish;
  if strip x != "@felix" goto moredoc;

morefelix:>
  x = read i;
  if x == "" goto finish;
  if x.[0] == char "@" goto moredoc;
  write(o,x);
  goto morefelix;

finish:>
  write (o,"");
}

// Concentrating transducer.
proc grab (o:oschannel[string]) (i:ischannel[string])  
{
  var s = "";
morelines:>
  var x = read i;
  if x == "" goto finish;
  s+=x;
  goto morelines;

finish:> 
  write(o,s);
}

// Pipeline controller.
proc save_tangle (infile:string, outfile:string)
{
  // Check modification times of files to
  // see if we actually need to do anything.
  var itime = FileStat::filetime infile;
  var otime = FileStat::filetime outfile; 
  if otime <= itime do

    // Run processing pipeline.
    var i,o = #mk_ioschannel_pair[string];
    spawn_fthread$ filesrc infile |-> tangle |-> grab o;
    var result  = read i;

    // If there's a non-trivial result, generate output file.
    if result != "" do
      println$ "Tangle      : " + infile + " -> " +outfile;

      // Make sure the directories in the path exist.
      Directory::mkdirs$ Filename::dirname outfile;

      // Write the file.
      var ofile = fopen_output outfile;
      write (ofile,result);
      fclose ofile;

    else
      println$ "No Code    : " + infile;
    done
  else
    println$   "Up-to-date : " + infile + " -> " +outfile;
  done 
}



--
john skaller
skal...@users.sourceforge.net
http://felix-lang.org




------------------------------------------------------------------------------
LogMeIn Rescue: Anywhere, Anytime Remote support for IT. Free Trial
Remotely access PCs and mobile devices and provide instant support
Improve your efficiency, and focus on delivering more value-add services
Discover what IT Professionals Know. Rescue delivers
http://p.sf.net/sfu/logmein_12329d2d
_______________________________________________
Felix-language mailing list
Felix-language@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/felix-language

Reply via email to