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