intrinsics - mvx-lang/mvx GitHub Wiki
LEN(s) |
length |
TRIM(s) |
strip leading/trailing blanks, squeeze runs |
TRIM(s, c[, opt]) |
trim character c; opt = L/T/B/A/R (default R, both ends + squeeze) |
STR(s, n) |
s repeated n times |
SPACE(n) |
n spaces |
CHAR(n) / SEQ(s)
|
code to character and back |
FIELD(s, delim, n {, cnt}) |
n-th delimited field (cnt fields with delimiters) |
INDEX(s, sub, occ) |
position of the occ-th occurrence, 0 if none |
s MATCHES pat |
pattern test (operator, 1/0); MATCH is a synonym |
MATCHFIELD(s, pat, n) |
the substring matched by pattern component n |
COUNT(s, sub) |
occurrences of sub |
DCOUNT(s, delim) |
delimited-field count |
NUM(x) |
1 if x is numeric ("" counts) |
s[start, len] |
substring (operator) |
CHANGE(s, old, new) |
replace every occurrence of old with new |
EREPLACE(s, old, new) / SWAP(s, old, new)
|
same as CHANGE |
CONVERT(from, to, s) |
translate each char: from[i] -> to[i], deleted when to is shorter |
TRIMB(s) / TRIMF(s)
|
strip trailing / leading blanks |
ALPHA(s) |
1 if s is non-empty and all letters |
QUOTE(s) / DQUOTE(s) / SQUOTE(s)
|
wrap in double / double / single quotes |
FMT(x, mask) |
format: L/R justify, fill # space * star % zero, width β FMT(N, "R%8")
|
A pattern for MATCHES/MATCHFIELD is a run of tokens that must
consume the whole string: nN/nA/nX (exactly n numeric / alphabetic
/ any characters), 0N/0A/0X (zero or more of that class), a quoted
literal ('-' or "/"), or any other character taken literally. Value
marks (@VM) in the pattern give alternatives β the string matches if it
matches any one. So "01-234" MATCHES "2N'-'3N" is true, and
"3N":@VM:"3A" matches three digits or three letters.
EXTRACT(A, a{,v{,s}}) |
same as A<a,v,s>
|
REPLACE(A, a{,v{,s}}, x) |
copy with element replaced |
INSERT(A, a{,v{,s}}, x) |
copy with element inserted |
DELETE(A, a{,v{,s}}) |
copy with element removed |
SUM(A) |
reduce the lowest delimiter level by addition (keeps higher structure) |
MAXIMUM(A) / MINIMUM(A)
|
largest / smallest numeric field at any level |
System constants: @AM/@FM (attribute mark), @VM (value mark),
@SM/@SVM (subvalue mark).
INT(x) |
truncate toward zero |
ABS(x), SQRT(x)
|
as expected |
MOD(a, b) |
modulo, sign of b |
PWR(x, y) |
x to the power y (also x ^ y) |
LN(x), EXP(x)
|
natural log and its inverse |
SIN(d), COS(d), TAN(d)
|
trigonometry, argument in degrees |
ATAN(x) |
arctangent, result in degrees |
RND(n) |
random 0 .. n-1 |
DATE() |
internal date (day 0 = 31 DEC 1967), local |
TIME() |
seconds since midnight, local |
SYSTEM(12) |
milliseconds since midnight |
OCONV(x, code) |
internal β external |
ICONV(x, code) |
external β internal |
Conversion codes: D{y}{sep} dates (OCONV(0,"D") β 31 DEC 1967,
D2/ β 12/31/67), MT{S} times (MTS β 12:34:56), MD{n}[,][$]
masked decimal (OCONV(1234567,"MD2,$") β $12,345.67), MCU,
MCL, MCT case. STATUS() after a conversion: 0 ok, 1 bad input,
2 bad code.
SENTENCE() |
the TCL command line that invoked this program |
ENV(name) |
environment variable, "" if unset |
STATUS() |
last conversion status |
SYSTEM(2) / SYSTEM(3)
|
terminal width / depth (live) |
SYSTEM(11) |
1 when a select list is active |
LOGTO(acct) |
change account; 1 moved, 0 refused, reason in STATUS()
|
@LEVEL |
how many programs are above this one; 0 when none |
LOGTO takes the account's directory, the same thing mvx -a and the LOGTO
verb take. It leaves the old account before entering the new one β closing its
files, dropping its locks and giving its connections back β and forgets
everything resolved per account: the VOC chain, linked packages, and the
subroutine libraries under LIB/.
The program continues either way, which is what UniData and UniVerse both do. What it held is gone, so re-opening is the program's own business:
IF LOGTO(DIVISION) THEN
GOSUB OPEN.THE.FILES ;* the handles from before are stale
END ELSE
BEGIN CASE
CASE STATUS() = 1 ; PRINT "no such account: ":DIVISION
CASE STATUS() = 2 ; PRINT "commit or abort first"
END CASE
ENDA file variable held across a LOGTO says it is stale rather than reading a
handle that has gone. EXECUTE "LOGTO ..." reaches the same place, so code
ported from another MV system keeps working.
@LEVEL is 0 for a program run from the prompt or straight from Unix, and
1 for one an EXECUTE reached β the same numbering UniData and UniVerse use.
Use it when a routine must not talk to a terminal it does not own:
IF @LEVEL THEN
RETURN ;* something is above me β ask nobody
END
CRT "Press RETURN " :
INPUT ANYTHINGThis is a different question from @USER.TYPE, which says whether there is
a terminal β a program three EXECUTEs deep still has one. An account's
LOGIN wants both: it may be entered directly, or fired by a LOGTO from
inside a running program whose screen and keyboard it would otherwise take
over.
KEYIN({ms}) |
one decoded keystroke; with ms, "" on timeout |
MOUSE() |
last click as col : row : button : event β see the terminal chapter |
@(col, row) |
cursor positioning string |
@(-1..-18) |
screen codes β see the terminal chapter |
COLOR(fg {, bg}) |
colour codes β see the terminal chapter |
For moving records to and from the outside world (editors, git, diff).
OSREAD(path) |
whole OS file as a string; STATUS() 1 on failure |
OSWRITE(data, path) |
write; returns 1/0 |
OSDELETE(path) |
remove; returns 1/0 |
TMPNAM() |
a fresh temp path |
EDITFILE(path) |
run the external editor (unrestricted tier) |
Export a record as attribute-per-line text and back:
LF = CHAR(10)
X = OSWRITE(CHANGE(REC, @AM, LF), PATH) record -> text file
REC = CHANGE(OSREAD(PATH), LF, @AM) text file -> record
CREATEFILE(name {, "DIR"}) |
create a file (data + dictionary) |
DELETEFILE(name) |
remove a file |
FILELIST() |
the account's files, name @VM type per attribute |
INDEXBUILD(fvar, item) |
(re)build an index; returns record count |
INDEXDROP(fvar, item) |
drop an index |
INDEXSELECT(fvar, item, value) |
form the select list from an index |
MSGSEND(target, text {, class {, payload}}) |
send; returns ports reached, 0 none live, -1 refused, -2 no registry |
MSGPENDING() |
messages waiting, -1 when no registry is running |
MSGREAD() |
the next message as a record, "" when the inbox is empty |
MSGMODE({m}) |
ON, OFF or DEFER; returns the previous mode |
MSGDROPPED() |
messages lost to inbox overflow since logon |
MSGWHO({scope}) |
the session roster: one session per attribute, port VM user VM account VM host VM line VM logged-on; empty when no registry is running |
MSGSTATUS() |
state VM transport VM port VM prefix; state is up or unavailable
|
A message reads as an ordinary MV record β 1 version, 2 class + flags,
3β6 the sender's port, user, account and host, 7 sent, 8 text, 9 the
payload. The payload is the attachment: file VM id VM program VM mode, with
positions 5 and beyond free for an application. See
Messaging. SEND and MESSAGE are
statement spellings of MSGSEND and MSGMODE
(Statements).
| COMPILE(mode, src, out {, options}) | spawn the compiler (developer tier); mode "c", "exe", "shared"; options "NODEBUG", "STRIP", or both |
| DOCTAG(tag) | inside an I-type dictionary item: extract "@tag value" from a record's comments |
A mapping says how a record's attributes and associations become structured
fields; it is an ordinary MV string (an @AM list of
name<VM>attr<VM>conv<VM>type<VM>assoc fields) β hand-buildable, built
field-by-field with MAPFIELD, or derived from a file's dictionary with
MAPSPEC. JSONENCODE/JSONDECODE are built into the runtime β they were
a package once, and are not one now, so they need no package and no
configuration; MAPSPEC/MAPFIELD and the mapping engine live there too.
MAPFIELD(name, attr {, conv {, type {, assoc}}}) |
one mapping field; append with SPEC<-1> = MAPFIELD(...). Empty type is derived from conv (MDβNUMERIC, DβDATE, MTβTIME) |
MAPSPEC(file) |
derive a mapping from file's dictionary (every D-item β a field) |
JSONENCODE(rec, mapping) |
the record as a JSON object: single attrs β keys, each association β an array of objects; NUMERIC unquoted, empty β null/""
|
JSONDECODE(json, mapping) |
the inverse β map JSON back to a record (mapped attributes only) |
SPEC = ""
SPEC<-1> = MAPFIELD("customer", 1)
SPEC<-1> = MAPFIELD("price", 7, "MD2", "", "items") ;* type -> NUMERIC
REC = JSONDECODE(INBOUND, SPEC) ;* consume JSON on the fly
PRINT JSONENCODE(REC, MAPSPEC("ORDERS")) ;* or map from a file's dict