aceincl

Top-level Files of tip
Login

Top-level Files of tip

Files in the top-level directory from the latest check-in


DOI

Icon "inclusion library"

Incorporate these files into Icon programs using the $include preprocessor macro:

development repo: https://chiselapp.com/user/eschen42/repository/aceincl

mirror and release repo: https://github.com/eschen42/aceincl

Except where otherwise noted, this code is created (with others' inspiration) by Art Eschenlauer (OrcID 0000-0002-2882-0508).

The usual Icon value type abbreviations apply on this page:

with one addition:


Contents


Testing program runt.icn and working examples

Working examples, named test_*.icn, are in the tests directory:


baton.icn

baton.icn provides a serverless way to pass data between processes using five files, one for the message transmitted and the others for coordinating transfer.

Note well that nothing precludes locating the baton files on a remote filesystem to coordinate processes running on different nodes or operating systems.

procedure baton(action:s, filename:s, file:f, warn:C, wait_secs:N) : s|n|fail

Procedure baton provides four distinct modes of action:

        action    buffer    file      Cwarn   wait_secs
  baton("write",  buffer:s, file:f|C, warn:C, wait_secs:N) : s|fail
  baton("read",   buffer:s, file:f|C, warn:C, wait_secs:N) : s|fail
  baton("close",  buffer:s, file:f|C, warn:C, wait_secs:N) : s|fail
  baton("select", buffer:s, file:n|x, warn:C, wait_secs:N) : n|fail
  baton("clean",  buffer:s, file:n|x, warn:C             ) : fail

Usages:

action == ("write" | "close")

baton("write", buffer:s, file:f|C, warn:n|C, wait_secs:N) : s|fail
baton("close", buffer:s, file:f|C, warn:n|C, wait_secs:N) : s|fail

"write": Copy input from file to file named by buffer

"close": Send EOT (ASCII character 4) to file named by buffer

action == "read"

baton("read", buffer:s, file:f|C, warn:n|C, wait_secs:N) : s|fail

Copy output from file named by buffer to file

action == "select"

baton("select", buffer:s, file:n|x, warn:n|C, wait_secs:N) : n|fail

action == "clean"

baton("clean", buffer:s, file:n|x, warn:n|C) : fail

See "case two" in the example under baton_flatware below for a demonstration of use of "read" and "write" from within a program.


baton_main.icn

See below for notes regarding usage of these procedures.

procedure baton_main(args) : exit(0 | 1)

This procedure provides an implementation for main for a standalone executable to interface a baton to or from a stream, as described below.

Note that Icon does not catch SIGPIPE. Consequently, if baton("read",...) is feeding a pipe, and if the downstream process dies, then the Icon process running baton("read",...) will die as well! This is the motivation for running an output pipe from a separate process and coordiating data-passing using a baton.

Usage of baton program

baton read   bufferfile [handshake_timeout_secs]
baton write  bufferfile [handshake_timeout_secs]
baton close  bufferfile [handshake_timeout_secs]
baton select bufferfile [success_timeout_secs]
baton clean  bufferfile

  'write' reads from standard input into bufferfile in coordination with
      'read', which copies from bufferfile to standard output.
      When supplied, handshake_timeout_secs specifies number of seconds
      to wait for initial handshake.

  'close' explicitly initiates
      shutdown of 'baton read' when used independently of 'baton write';
      it is NOT required when 'baton write' is used.

  'select' returns a zero exit code only when data are
      available to read.
      When supplied, handshake_timeout_secs specifies number of seconds
      to wait for success.
      It is possible that this is not useful unless 'baton write' is not
      paired with 'baton read'.

  'clean' removes coordination files that may remain when
      read or write experiences abnormal termination.

Building baton program (if desired)

You may create a file baton_main_build.icn containing:

$define baton_main main
$include "baton_main.icn"

Then translate:

icont -u -o baton     baton_main_build.icn # on Unix-like OSs
icont -u -o baton.exe baton_main_build.icn # on MS Windows

The resulting program can be used as in the following (trivial) example:

  $include "fileDirIo.icn"
  procedure main()
    # Pass one line of data from output pipe to input pipe.
    fsend := open("baton write buffer", "wp")
    frecv := open("baton read  buffer", "rp")
    write(fsend, "hello world from " || &progname)
    write(read(frecv))
    close(fsend)
    close(frecv)
  end

However, the ordinary way to use this program is to have "baton write" stream one baton to the standard input of a process and to have "baton read" stream the standard output of a process to another baton, as demonstrated in the example for baton_flatware below.

procedure baton_flatware(args) : fail | exit(0 | 1)

This procedure facilitates creation of a "multi-entry binary"; specifically, it eliminates the necessity to create a distinct executable to use batons outside of the parent program's process.

See tests/test_baton_main.icn for examples.

This procedure diverts the control to baton_main(args) when:

Otherwise, the procedure fails.

This allows an executable program to have two behaviors:

The following exemple demonstrates invocation of baton_flatware(args) from within main(args):

  # example usage of baton_flatware; requires that sqlite3 is on PATH
  $define BATON_TRACE if &fail then write
  $define BATON_CWARN create repeat write(&errout, \@&source | "") | @&main
  $define BATON_TIMEOUT_MS 200
  $include "baton_main.icn" # implies include baton.icn and fileDirIo.icn
  $define PLUGH  "A hollow voice says \"plugh\"."
  $define PLOVER "end of SQLite result list"
  procedure main(args)
    local baton_self # string to invoke child "flatware" via baton_flatware
    local chunk      # temporary holder for a string of data
    local status     # exit status of child process, captured from Cexit
    local Cexit      # co-expression producing exit code if child "flatware"
                     #   has terminated; producing &null otherwise
    local Crecv      # co-expression to receive input from child "flatware"
    local Csend      # co-expression to send output to child "flatware"
    # handle calls to baton_main; does not return; kind of like a fork...
    baton_flatware(args)
    # path to self is platform-specific; this has been minimally tested!
    baton_self := &progname
    if not (&features == "MS Windows" | path_separator() == baton_self[1])
      then baton_self := "." || path_separator() || &progname
    baton_self ||:= " baton_main "
    # Exchange data with external process via batons.
    #   Launch process in background; if process dies, no SIGPIPE
    #   can reach us (see: https://unix.stackexchange.com/a/84828)
    #   which is good because Icon does not catch signals.
    Cexit :=
      system_nowait(
        baton_self || " read buf_in | " ||
        "sqlite3 -batch -json | " ||
        baton_self || " write buf_out"
        )
    # Set up baton for standard output of process
    Crecv := create baton("read", "buf_out", &main)
    # Set up baton for standard input of process
    # and activate baton so that it can receive a value
    @( Csend := create baton("write", "buf_in", &main) )
    # Send a command to SQLite, producing output
    ".show" @Csend
    # Send a query to SQLite, not producing any output
    "select 'plover' where 1 = 0;" @Csend
    # Send a query to SQLite to mark end-of-output
    "select 'plugh' as xyzzy;" @Csend
    # Retrieve lines until end-of-output mark (or closed pipe)
    while chunk := @Crecv
      do
        if chunk == "[{\"xyzzy\":\"plugh\"}]"
          then break write(PLUGH)
          else write(chunk)
    # Send "[]" to SQLite to mark end-of-output
    ".print '[]'" @Csend
    # Retrieve lines until end-of-output mark (or closed pipe)
    while chunk := @Crecv
      do
        if chunk == "[]"
          then break write(PLOVER)
          else write(chunk)
    # Close the output baton (has same effect as 'baton close')
    char(4) @Csend
    # Close the input baton to clean up coordination files.
    @Crecv
    # Wait (briefly) for process exit and retrieve exit code.
    every 1 to 5
      do {
        write("SQLite exit code: ", image(status := @Cexit))
        if /status
          then delay(BATON_TIMEOUT_MS)
          else break
        }
  end

You will find another example under sl3.icn.

procedure baton_crowbar() : n | stop()

Kill the program when baton_flatware is not linked (which is a programming error) to avert "infinite forking" that consumes all slots in the process table.

This approach is imperfect since one may circumvent it with

  invocable all
or with
  invocable baton_flatware


batonsys.icn

procedure baton_system() : V

baton_system(basename, cmd, inC, outC) : BatonSys (a VNom extension)

procedure VNomBatonSysCtor() : V

VNomBatonSysCtor(Original:T, Type:s, ID:s, Metatable:T, Disposable:n|x, Kind:s):BatonSys (a VNom extension)

procedure VNomBatonSysMesg() : x

VNomBatonSysMesg(args[]):x


fieldedDataFile.icn

Procedures to produce logical lines or fields from formatted data files.

record FieldedData(lines, fields)

Produce record holding two co-expression factories:

procedure FieldedDataFactory(format, filePath) : FieldedData

Produce a FieldedData record for filePath corresponding to format.

procedure csvLines(f) : C

Factory for a co-expression producing logical lines of a CSV file f.

procedure csvFields(line, sep) : C

Factory for a co-expression producing fields from a logical line of a CSV file:

procedure getCSV(typeName, csvPath, colL, sep, dflt) : L

Generate VNoms from a CSV file

procedure tabularLines(f) : C

Factory for a co-expression producing logical lines of a tab-separated values file f.

procedure tabularFields(line, sep) : C

Factory for a co-expression producing fields from a logical line of a tab-separated values file:

procedure getTabular(typeName, tsvPath, colL, sep, dflt) : L

Generate VNoms from a tab-separated values file

procedure iniLines(f) : C

Factory for a co-expression producing logical lines of an INI file f.

procedure iniFields(line) : C

Factory for a co-expression producing fields from a logical line of an INI file

procedure getIni(ini) : T (two dimensional)

Parse an INI file at path ini into a table of tables


fileDirIo.icn

Procedures to manipulate files, directores, and their paths.

procedure alterExtension(fn, old_ex, new_ex) : s1, ...

Generate modified fn, substituting new_ex for old_ex

procedure cmd_separator() : s

Produce platform-specific command separator

procedure directory_seq(name) : s1, ...

Generate name(s) that name a directory

procedure home() : s

Produce platform-specific path to the HOME directory, if available

procedure path_atoms(path) : s1, ...

Generate root, subdirectories, filename for a directory path

procedure path_constructP{exprs} : s1, ...

Generate paths from sequences of results of exprs

procedure path_parts(qualname) : s1, s2

Generate location then name from path qualname

procedure path_separator() : s

Produce platform-specific path separator

procedure prog_path_parts() : s1, s2

Generate location then name of program file.

procedure pwd() : s

Produce platform-specific path to the current directory

procedure system_nowait(command:s, title:s) : C

Run command, but do not wait for exit, producing result C

procedure tmpdir() : s

Produce platform-specific path to a tmp directory

procedure tmppath(suffix:s, len:s, dir:s) : s1, ...

Generate platform-specific temporary file path(s)

procedure which(filename:s, all:n|x) : s1, ...

Generate full path(s) for filename on PATH


iimage.icn

Procedures to transform data structures into includable Icon declarations and statements.

procedure iimage(x) : s

procedure idump(f, x[]) : (writes to \f | &errout)


jsonparse.icn

Procedures to parse and generate JSON, adapted (to support VNom tables, see vnom.icn below) from work by Carl Sturtivant (OrcID 0000-0003-1528-4504) and Gregg Townsend.

procedure json(L|T|i|n|r|s) : s

Takes data (list|table|integer|string|real|&null) and produces a JSON string defining that data. See http://json.org/. It is an error to use another type, even in substructures. To serialize other types, see codeobj.icn from the Icon Programming Library.

Works with Icon data structures constructed from tables and lists containing nulls, strings, integers and reals as well as values of those last four types. Note:

procedure jsonparse(s) : x

Takes a JSON string and produces the corresponding Icon value or structure. Tables in such a structure will have default values of null. JSON text containing true and false (booleans) will have those converted to the strings "true" and "false" respectively.

character sets

Although these routines should work with UTF-8 strings, nothing is included here to ensure that UTF-8 is correctly supported.

The characters of an Icon string are an extension of ASCII codes to 256 characters obtained by including the additional characters defined by ISO/IEC 8859-1 (also called a Latin-1 string), which are the first 256 unicode code points. However here, those characters are encoded each as a single Icon character.

Quoted strings inside JSON text are always UTF-8 encoded, with all control characters escaped using the uxxxx convention, where xxxx is a string of four hexadecimal digits indicating a unicode code point.

the VNOM preprocessor symbol

Note that (by design) conversion may not be completely symmetric when the VNOM preprocessor symbol is defined before this file is included. If VNOM is defined, then a "VNom table" (call it x) will have a "Kind" of "VNom", i.e.,

"VNom" == x[x, "Kind"]
"VNom" == vmsg(x, "kind")
The advantage of a VNom over an ordinary Icon table is that it preserves the order of the member keys in a JSON object (rather than reordering them alphabetically).


LiComboP.icn

Procedures to suspend lists combining sequences.

procedure LiP(A) : L1, ...

procedure LiFiniteP(LofC) : L1, ...

procedure nAltP(LofC) : L1, ...


lindel.icn

In-place delete or insert of a pseudo-section of L.

procedure Ldelete(L, i, j) : L

Delete indexes i to j from L (in-place), producing L

procedure Linsert(L, i, Lins) : L

Insert list Lins into L (in-place) before index i, producing L

procedure Lfind(L, x) : i1, i2, ...

Generate indices where x appears in L


rpn.icn

Procedures to embed RPN-based (Forth-like) interpreter into Icon programs; can also be run in a REPL.

This file may be used to embed RPN-scripted access to Icon procedures and operators, in a manner reminiscent of Forth.

See the following to get started:

This work was inspired by Steve Wampler's "A (small) RPN calculator" example https://sourceforge.net/p/unicon/mailman/message/6144067/ and R. G. Loeliger's Threaded interprtive languages (1981, BYTE Books) https://lccn.loc.gov/80019392.

run rpm.icn with REPL

To run this "stand-alone" in a "read, evaluate, print, loop" (REPL), I put the following in ~/bin/rpn:

LPATH=~/src/aceincl icon -P '
# run rpn.icn

# Define procedure main(args) that imports:
#   - ~/.rpn/*.rpn
#   - any .rpn files specified as arguments
# and executes standard input if either:
#   - *args = 0
#   - "-" == !args
$define RPN_MAIN 1
$include "rpn.icn"

# required by rpn.icn
$include "fileDirIo.icn"
'

then I made it executable:

chmod +x ~/bin/rpn

so that I can run it with:

~/bin/rpn

If you have rlwrap (https://github.com/hanslub42/rlwrap) installed (or built), and you change the first line above to

LPATH=~/src/aceincl rlwrap icon -P '
then you can get proper interpretation of the arrow keys in the REPL loop.


runningStats.icn

These procedures support computing summary statistics for normally distributed data using "Welford's online algorithm", porting code from Wikipedia.

ref: https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm

record welford_running(count, mean, M2)

record welford_cumulative(n, mean, variance, sampleVariance, SD, SE)

procedure welford_new()

procedure welford_add(W, x)

procedure welford_get(welford_running)


selectRecordFromListByField.icn

Procedure to produce records from a list of records (or a list of tables), matching specified criteria.

procedure selectRecordFromListByField(Lfrom, sField, Ctest) : R1, ...

procedure selectRecordFromListByFieldL(Lfrom, sFieldL, Ctest) : R1, ...


sl3.icn

Interface to exchange commands and results with sqlite3, which must be on PATH.

sl3.icn defines an interface to the sqlite3 command line interface, which is described in detail at https://sqlite.org/cli.html

No generalized process interface exists in the basic Icon implementation and library that would allow access to both the standard input and the standard output of a child process, this interface uses the baton inteface via baton.icn, baton_main.icn, and batonsys.icn to hand data back and forth between Icon and sqlite3 without resorting to such platform-specific devices such as FIFOs or named-pipes.

By default, baton files are created in the directory whose path is returned by tmpdir() in fileDirIo.icn. To override this behavior, assign a path string to the global variable g_sl3_tmpdir which is defined in sl3.icn

For convenience, if the preprocessor symbol sl3 is not defined by the preprocessor before sl3.icn is included, then it is defined as sl3Msg:

$ifndef sl3
  $define sl3msg sl3
$endif

sl3new(path, options, errC) : VNom (a SQLite3 connection)

sl3Msg(conn:SQLite3, message, arg1, arg2, arg3) : x

The signatures of sl3 messages are as follows:

  sl3(&null, "open",      path,    options, errC ) : VNom (a SQLite3 conn)
  sl3(&null, "prepare",   stmt                   ) : VNom (a prep_stmt)
  sl3(conn,  stmt:s,      parmL:L, errC          ) : n|fail
  sl3(conn,  prep_stmt:V, &null|x, errC          ) : n|fail
  sl3(conn,  "fetch"                             ) : VNom (a result row)
  sl3(conn,  "close",     errC                   ) : &null

You can find some examples with (modest) error handling in sl3tests/test_sl3_02.icn.

In lieu of a detailed description of each signature (which may be found the header of sl3.icn), here is a working example, which emphasizes several ways of passing parameters to prepared statements.

$include "fileDirIo.icn"
$include "vnom.icn"
$include "jsonparse.icn"
$include "sl3.icn"
$define ERR_OFFSET [0, -1][2]
$define DISPOSE_ON_ERROR if write(trace_reset(err_off)) then dispose(cnxn, errC)
global g_exit_code
procedure main(args)
  local cnxn, path_s, options_s, errC, chunk, err_off, prep_stmt
  # don't forget this or infinite forks will fill the process space... 
  baton_flatware(args)
  # set exit code for premature termination
  g_exit_code := -1
  # create co-expression to handle error strings
  @(errC := create while write(@ &source))
  # open sqlite and establish baton
  path_s := ":memory:"
  options_s := &null
  # open database connection
  #   signature: sl3new(path, options, errC) : VNomSQLite3
  #     === sl3(connection, "open", path, options, errC) : VNomSQLite3
  if not (cnxn := sl3new(path_s, options_s, errC))
    then stop("failed to open connection")
  err_off := ERR_OFFSET

  # execute SQL immediately, without parameters
  #   signature: sl3(conn, stmt:s, &null, errC) : n|fail
  sl3(cnxn,
    "CREATE TABLE invent(desc text, number integer, amount float);", , errC)
  DISPOSE_ON_ERROR
  # execute dot command immediately
  sl3(cnxn, ".dump", , errC)
  DISPOSE_ON_ERROR
  # show result lines, which are not rows from a SQL query
  every chunk := sl3(cnxn, "fetch") do write_ordered_values(chunk)

  # initialize first prepared statement, which has named parameters
  #   signature: sl3(conn, "prepare", stmt) : VNom (a prep_stmt)
  prep_stmt := sl3(cnxn, "prepare",
    "INSERT INTO invent VALUES(@desc, @number, @amount);")
  DISPOSE_ON_ERROR
  # set parameters for first prepared statement
  vmsg(prep_stmt, "put", "@desc", "a description")
  vmsg(prep_stmt, "put", "@number", 42)
  vmsg(prep_stmt, "put", "@amount", 3.14159)
  # execute first prepared statment
  #   signature: sl3(conn, prep_stmt:V, &null, errC ) : n|fail
  if not sl3(cnxn, prep_stmt, , errC) then {
      write("test one: execute prepared statement with named parameters failed")
      dispose(cnxn, errC)}

  # initialize second prepared statement, which has named and unnamed parameters
  #   signature: sl3(conn, "prepare", stmt) : VNom (a prep_stmt)
  prep_stmt := sl3(cnxn, "prepare", "INSERT INTO invent VALUES(?, ?3, ?2);")
  DISPOSE_ON_ERROR
  # set parameters for second prepared statement
  vmsg(prep_stmt, "put", "?1", "another description")
  vmsg(prep_stmt, "put", "?2", 1066)
  vmsg(prep_stmt, "put", "?3", 1.414214)
  # execute second prepared statment
  #   signature: sl3(conn, prep_stmt:V, &null, errC) : n|fail
  if not sl3(cnxn, prep_stmt, , errC)
    then {
      write("test two: execute prepared statement with unnamed parameters failed")
      dispose(cnxn, errC)}
  DISPOSE_ON_ERROR

  # execute SQL with implicit prepared statement and with L of unnamed params
  #   signature: sl3(conn, stmt:s, parmL:L, errC) : n|fail
  if not sl3(cnxn, "INSERT INTO invent VALUES(?, ?, ?);",
    ["foobar", 2401, 21.0/7], errC)
    then { write("test three: execute implicit prepared statement failed")
      dispose(cnxn, errC)}
  DISPOSE_ON_ERROR

  # execute SQL without params
  #   signature: sl3(conn, stmt:s, &null, errC) : n|fail
  sl3(cnxn, "select * from invent;", , errC)
  DISPOSE_ON_ERROR
  # show results from previous SQL
  write("---")
  every chunk := sl3(cnxn, "fetch") do {
    write_vnom_fields(chunk)
    write("---")}

  # set exit code for normal termination
  g_exit_code := 0
  &error := 0
  # stop sqlite3 and shut down the batons
  dispose(cnxn, errC)
end
procedure dispose(cnxn, errC)
  # stop sqlite3 and shut down the batons
  sl3(cnxn, "close", errC) | write("sl3 \"close\" failed")
  write("  batonsys disposition: ", image(cnxn["disposition"]))
  exit(g_exit_code)
end
procedure trace_reset(error_offset)
  local result
  result := ""
  if &error = 0 then fail
  if (&error < error_offset) then {
      result ||:= "There were " || error_offset - &error || " errors; "
      result ||:= "last error " || &errornumber || " - " || &errortext
      &error := error_offset
      return result
      }
  &error := error_offset
end
procedure write_vnom_fields(chunk, f)
  local i
  /f := &output
  # row fields are ordered by VNom key
  every i := vmsg(chunk, "key") do write(f, "  ", i, ": ", chunk[i])
  return
end
procedure write_ordered_values(chunk, f)
  local i
  /f := &output
  # results of commands are ordered by a discardable integer key
  every i := key(chunk) do if i ~=== chunk then write(f, "  ", chunk[i])
  return
end

which produces as output:

  PRAGMA foreign_keys=OFF;
  BEGIN TRANSACTION;
  CREATE TABLE invent(desc text, number integer, amount float);
  COMMIT;
---
  desc: a description
  number: 42
  amount: 3.14159
---
  desc: another description
  number: 1.414214
  amount: 1066.0
---
  desc: foobar
  number: 2401
  amount: 3.0
---
  batonsys disposition: 0

vnom.icn

"Nominal vector", i.e., a list whose elements may be accessed by rank (index) or name (key).

A use case for this construct might a dynamically-defined record (i.e., an ordered list of key-value pairs), such as might be used to represent a row returned by an SQL query.

Through use of a Lua-inspired "metatable", operations on this structure may be defined or extended with a few message-handler-extension functions. Note that a metatable may be shared among several VNom instances to give them identical behavior; for this reason, the copy constructor copies a reference to the metatable rather than making a copy of the metatable. Thus, behavior of the set of instances may (even dynamically) be modified by changing a single structure.

procedure vnew(Original:T, Type:s, ID:s, Metatable:T, Disposable:s, Kind:s) : V

Construct a new VNom instance.

procedure vmsg(VNom:V, Message:s, args[]) : x

Send messages to update or interrogate the VNom instance.


wora.icn

Procedure to produce a value that can be read globally but can be reset only by the co-expression that set it it initially.

procedure wora(id,del) : x (lvalue or rvalue)


Legacy Source Code Control Problems

If you are still using Git rather than the best thing since CVS, then you may need the following to recover from the mess that Git Submodule can create if you are not very careful.

Git Submodule - some practical reminders

Reference: http://openmetric.org/til/programming/git-pull-with-submodule/

Ichabod Crane and the HEAD-less Repository

Reference: https://www.loekvandenouweland.com/content/head-detached-from-origin-master.html

If a submodule is in a "detatched HEAD" state (and it is not 1789), there are two courses of action that to take, depending on whether commits require rescuing.

If there is no need to push commits to the submodule to a remote git repository, something like the following should work, e.g., to reattach origin/main from the remote for submodule aceincl:

cd aceincl
git branch -la
git checkout remotes/origin/main
git status
git pull
git status

If there are commits to preserve, the process is more involved; see the reference, the gist of which is something like:

  1. Put the commits onto a branch:
    git branch fix-detached-HEAD $(git log | sed -n -e '/^commit/{s/commit[ ]*//; p; q}; d')
  2. Get the main or master branch, as appropriate for your repo:
    git checkout main (or git checkout master)
  3. Re-establish remote tracking:
    git fetch
  4. Review changes if necessary:
    git diff fix-detached-HEAD
  5. Merge the changes to the remote-tracking non-HEAD-less branch:
    git merge fix-detached-HEAD
  6. Push changes to the remote:
    git push --set-upstream origin master
    or git push --set-upstream origin main

Deprecated files


RecTable.icn

Procedures to produce/manipulate record-like tables.

See also: vnom.icn below for another, potentially more flexible approach.

Used by fieldedDataFile.icn above because, when it was written, vnom.icn had not yet been created.

procedure RecTable(rec_name_s, rec_fields_L, rec_data_L, rec_default_x) : T

Produce a table with record-like aspects:

procedure RecTableType(x) : s1, S2, s3, ...

For RecTable, produce:

For non-RecTable, return type(x).

procedure RecTableFields(x) : s1, ...

Produce RecTable's field names.

procedure RecTableFieldsL(x) :L

Return a list of the values produced by RecTableFields(x).

procedure RecTableFieldVals(x) : s1, ...

Produce RecTable's field values.

procedure RecTableFieldValsL(x) : L

Return a list of the values produced by RecTableFieldVals(x).

procedure RecTableColTypeCheck(x, type_name, col_name, preamble) : x

Return x, except abort when x is not instance of type_name:

procedure RecTableConstructorC(rec_name_s, rec_fields_L, rec_default_x) : C

Produce a C that, when receiving a transmitted list of values (of the same length as rec_fields_L), produces a RecTable instance: