# 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; sub(/\r$/, "", 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" } { sub(/\r$/, "") 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" } }