init upload
This commit is contained in:
@@ -0,0 +1,39 @@
|
|||||||
|
# LVTL-X: a compact and fair VTL-2 port to POSIX AWK
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
LVTL-X is a port of the [VTL-2](https://rosettacode.org/wiki/Category:VTL-2) retro programming language interpreter to the POSIX AWK runtime. Unlike the previous effort, LVTL-W, that only worked in BusyBox and GAWK, this version works in any POSIX-compliant environment. On top of that, LVTL-X is extremely compact and takes under 100 SLOC of portable and readable AWK code.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The easiest way is to run the `./lvtl` shell wrapper, either stand-alone or with a program file path to preload. You can also specify a non-default AWK interpreter to use with the `AWK` environment variable for the wrapper to use.
|
||||||
|
|
||||||
|
Alternatively, you can run the LVTL-X script directly like this:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
POSIXLY_CORRECT=1 awk -f lvtl-x.awk -- [/path/to/your/vtl/program.vtl] -
|
||||||
|
```
|
||||||
|
|
||||||
|
If running without a wrapper and specifying a program to preload, don't forget to put another `-` after it.
|
||||||
|
|
||||||
|
## Differences from the Altair original
|
||||||
|
|
||||||
|
LVTL-X stands out from other LVTL versions by being closer to the original VTL-2, but still has some important differences that shouldn't be ignored:
|
||||||
|
|
||||||
|
- both LF and CRLF are accepted (but not saved into RAM)
|
||||||
|
- only LFs are printed instead of CRLFs
|
||||||
|
- maximum line length is not enforced
|
||||||
|
- all whitespace after the line number is fully ignored
|
||||||
|
- any whitespace within expressions is also insignificant
|
||||||
|
- parentheses are NOT auto-closed at the end of the statement
|
||||||
|
- only 26 characters (A-Z) are valid generic variable names
|
||||||
|
- supports all standard VTL-2 binary operators: +, -, *, /, =, >, <
|
||||||
|
- jumps to itself (like 10 #=10) are prohibited and counted as nops
|
||||||
|
- no file I/O (only the option to preload VTL code from a file)
|
||||||
|
- self-modifying programs won't work correctly
|
||||||
|
|
||||||
|
Nevertheless, any program that's valid on LVTL-X will run on the original VTL-2/02 implementations too.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
Created by Luxferre in 2026, released into the public domain with no warranties.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# LVTL-X wrapper/loader
|
||||||
|
[ -z "$AWK" ] && AWK=awk
|
||||||
|
POSIXLY_CORRECT=1 $AWK -f lvtl-x.awk -- "$1" -
|
||||||
+122
@@ -0,0 +1,122 @@
|
|||||||
|
# LVTL-X: port of VTL-2 to POSIX AWK
|
||||||
|
#
|
||||||
|
# Usage: POSIXLY_CORRECT=1 awk -f lvtl-x.awk [prog.vtl -]
|
||||||
|
# (don't forget the - after the program or LVTL will exit upon preloading)
|
||||||
|
#
|
||||||
|
# Any program valid for LVTL-X will run on the original VTL-2 and VTL02 too
|
||||||
|
#
|
||||||
|
# Differences from the original VTL-2 for Altair:
|
||||||
|
# - both LF and CRLF are accepted (but not saved into RAM)
|
||||||
|
# - only LFs are printed instead of CRLFs
|
||||||
|
# - maximum line length is not enforced
|
||||||
|
# - all whitespace after the line number is fully ignored
|
||||||
|
# - any whitespace within expressions is also insignificant
|
||||||
|
# - parentheses are NOT auto-closed at the end of the statement
|
||||||
|
# - only 26 characters (A-Z) are valid generic variable names
|
||||||
|
# - supports all standard VTL-2 binary operators: +, -, *, /, =, >, <
|
||||||
|
# - jumps to itself (like 10 #=10) are prohibited and counted as nops
|
||||||
|
# - no file I/O (only the option to preload VTL code from a file)
|
||||||
|
# - self-modifying programs won't work correctly
|
||||||
|
#
|
||||||
|
# Created by Luxferre in 2026 (based on LVTL-W from 2023), released into public domain
|
||||||
|
|
||||||
|
function err(m) {print "Fatal error: " m; exit 1}
|
||||||
|
|
||||||
|
# read a generic/system variable
|
||||||
|
function getvar(varname, c, cmd) {
|
||||||
|
if(varname == "$") {
|
||||||
|
cmd = "dd bs=1 count=1 2>/dev/null | od -An -t u1"
|
||||||
|
c = (cmd | getline c) > 0 ? int(c) : 0
|
||||||
|
close(cmd); return c
|
||||||
|
}
|
||||||
|
if(varname == "?") { getline c; return evalexpr(c) }
|
||||||
|
if(varname == "'") return int(rand()*65536)
|
||||||
|
return (varname in VARS) ? VARS[varname] : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# set a generic/system variable
|
||||||
|
function setvar(varname, value, qi) {
|
||||||
|
if(varname == "$") printf("%c", value % 256)
|
||||||
|
else if(varname == "?") {
|
||||||
|
if(VARS["\""]) {
|
||||||
|
qi = index(value, "\"")
|
||||||
|
printf("%s%s", substr(value, 1, qi - 1), substr(value, qi+1, 1) == ";" ? "" : "\n")
|
||||||
|
VARS["\""] = 0
|
||||||
|
} else printf("%d", value % 65536)
|
||||||
|
} else if(varname in VARS) {
|
||||||
|
value %= 65536; if(varname == "#" && value > 0) VARS["!"] = VARS["#"] + 1
|
||||||
|
VARS[varname] = value
|
||||||
|
} else err("invalid variable name!")
|
||||||
|
}
|
||||||
|
|
||||||
|
# strict LTR evaluator
|
||||||
|
function evalexpr(expr, tkn, i, l, opex, acc, cop, oprnd, subs) {
|
||||||
|
opex = 1; l = length(expr)
|
||||||
|
for(i=1; i<=l; i++) {
|
||||||
|
tkn = substr(expr, i, 1)
|
||||||
|
if(tkn == ")") break
|
||||||
|
if(tkn == " " || tkn == "\t") continue
|
||||||
|
if(opex = 1 - opex) {
|
||||||
|
if(index("+-*/=<>&!#", tkn)) cop = tkn
|
||||||
|
else err("unexpected binary operator at " VARS["#"])
|
||||||
|
} else {
|
||||||
|
if(tkn == "\"") { VARS[tkn] = 1; return substr(expr, i+1) }
|
||||||
|
if(tkn == ":" || tkn == "(") {
|
||||||
|
subs = evalexpr(substr(expr, i+1)); oprnd = (tkn == ":") ? SCRATCH[subs] : subs
|
||||||
|
i += PROCCHARS
|
||||||
|
} else if(index("0123456789", tkn)) {
|
||||||
|
oprnd = int(substr(expr, i)); i += length(oprnd) - 1
|
||||||
|
} else oprnd = getvar(tkn)
|
||||||
|
oprnd %= 65536
|
||||||
|
if(cop == 0) acc = oprnd
|
||||||
|
else if(cop == "+") acc += oprnd
|
||||||
|
else if(cop == "-") acc -= oprnd
|
||||||
|
else if(cop == "*") acc *= oprnd
|
||||||
|
else if(cop == "/" && oprnd) { VARS["%"] = acc % oprnd; acc = int(acc/oprnd) }
|
||||||
|
else if(cop == "=") acc = (acc == oprnd)
|
||||||
|
else if(cop == ">") acc = (acc >= oprnd)
|
||||||
|
else if(cop == "<") acc = (acc < oprnd)
|
||||||
|
acc = (acc % 65536 + 65536) % 65536
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PROCCHARS = i; return acc
|
||||||
|
}
|
||||||
|
|
||||||
|
# run the statement (iterative implementation)
|
||||||
|
function exec_stmt(stmt, ln, ei, lhs, rhs, vn, lcache) {
|
||||||
|
while(stmt != "") {
|
||||||
|
if(!(ei = index(stmt, "="))) return
|
||||||
|
if((lhs = substr(stmt, 1, ei - 1)) == "" || (rhs = substr(stmt, ei + 1)) == "") return
|
||||||
|
ei = evalexpr(rhs); vn = substr(lhs, 1, 1)
|
||||||
|
if(vn == ":") SCRATCH[evalexpr(substr(lhs, 2))] = ei
|
||||||
|
else setvar(vn, ei)
|
||||||
|
lcache = ln; ln = VARS["#"]
|
||||||
|
if((!ln && lcache) || (ln > 0 && ln == lcache)) ln = lcache + 1
|
||||||
|
if(ln > 0) {
|
||||||
|
for(ei=ln; ei<65536; ei++) if(ei in PROGLINES) {ln = ei; break}
|
||||||
|
VARS["#"] = ln = (ei >= 65536) ? 0 : ln; stmt = PROGLINES[ln]
|
||||||
|
} else stmt = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
srand()
|
||||||
|
for(i=65; i<=90; i++) VARS[sprintf("%c", i)] = 0
|
||||||
|
VARS["!"] = VARS["\""] = VARS["#"] = VARS["%"] = VARS["&"] = 0
|
||||||
|
VARS["*"] = 65535
|
||||||
|
print "LVTL-X by Luxferre\nPress Ctrl+C to exit\n\nOK"
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ln = int($1) % 65536; if(NF > 1) $1 = ""
|
||||||
|
gsub(/^[[:blank:]]+|[[:blank:]]+$/, "")
|
||||||
|
if(ln > 0) {
|
||||||
|
if($0 == "" || $0 == ln) delete PROGLINES[ln]
|
||||||
|
else PROGLINES[ln] = toupper($0)
|
||||||
|
} else {
|
||||||
|
if($0 == "0") {
|
||||||
|
for(i=1; i<65536; i++) if(i in PROGLINES) print i, PROGLINES[i]
|
||||||
|
} else exec_stmt(toupper($0), 0)
|
||||||
|
print "\nOK"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user