mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-16 08:36:07 +09:00
just updating things so that I can have a backup point...
Former-commit-id: 0a5a6d7f68ee1a96562532572c8d45fe102d3c25 Former-commit-id: a1a78f61f2fe2a8707e47633caa6cd67a829b35e
This commit is contained in:
@@ -6,30 +6,40 @@ TBASIC: Simple BASIC language based on the Commodore BASIC Version 2.
|
||||
|
||||
How to use in your program:
|
||||
|
||||
1. load the script by:
|
||||
if you're using ComputerCraft, use:
|
||||
os.loadAPI "TBASEXEC.lua"
|
||||
else, use:
|
||||
require "TBASEXEC"
|
||||
1. load the script by:
|
||||
if you're using ComputerCraft, use:
|
||||
os.loadAPI "TBASEXEC.lua"
|
||||
else, use:
|
||||
require "TBASEXEC"
|
||||
|
||||
2. run:
|
||||
_TBASIC.EXEC(string of whole command)
|
||||
2. run:
|
||||
_TBASIC.EXEC(string of whole command)
|
||||
]]
|
||||
|
||||
if os and os.loadAPI then -- ComputerCraft
|
||||
os.loadAPI "TBASINCL.lua"
|
||||
os.loadAPI "TBASINCL.lua"
|
||||
else
|
||||
require "TBASINCL"
|
||||
require "TBASINCL"
|
||||
end
|
||||
|
||||
table.concat = function(t, delimeter)
|
||||
if #t == 0 then return "" end
|
||||
local outstr = t[1]
|
||||
for i = 2, #t do
|
||||
outstr = outstr..delimeter..tostring(t[i])
|
||||
end
|
||||
if #t == 0 then return "" end
|
||||
local outstr = t[1]
|
||||
for i = 2, #t do
|
||||
outstr = outstr..delimeter..tostring(t[i])
|
||||
end
|
||||
|
||||
return outstr
|
||||
return outstr
|
||||
end
|
||||
|
||||
-- Copy from TBASINCL; looks like OpenComputers has a bug...
|
||||
function string_hash(str)
|
||||
local hash = 2166136261
|
||||
for i = 1, #str do
|
||||
hash = hash * 16777619
|
||||
hash = bit.bxor(hash, str:byte(i))
|
||||
end
|
||||
return hash
|
||||
end
|
||||
|
||||
|
||||
@@ -43,457 +53,423 @@ local programlist = {}
|
||||
-- LEXER ----------------------------------------------------------------------
|
||||
|
||||
local function appendcommand(lineno, statement)
|
||||
if lineno > _TBASIC._INTPRTR.MAXLINES then
|
||||
_TBASIC._ERROR.LINETOOBIG()
|
||||
elseif lineno < 0 then
|
||||
_TBASIC._ERROR.NOLINENUM()
|
||||
else
|
||||
programlist[lineno] = statement
|
||||
end
|
||||
if lineno > _TBASIC._INTPRTR.MAXLINES then
|
||||
_TBASIC._ERROR.LINETOOBIG()
|
||||
elseif lineno < 0 then
|
||||
_TBASIC._ERROR.NOLINENUM()
|
||||
else
|
||||
programlist[lineno] = statement
|
||||
end
|
||||
end
|
||||
|
||||
do -- Avoid heap allocs for performance
|
||||
local tokens = {" ", "\t"} -- initial obvious tokens
|
||||
local longest_token_len = 0
|
||||
-- build 'tokens' table from list of operators from the language
|
||||
for _, v in ipairs(_TBASIC._OPERATR) do
|
||||
if not v:match("[A-Za-z]") then -- we want non-alphabetic operators as a token
|
||||
table.insert(tokens, v)
|
||||
-- get longest_token_len, will be used for 'lookahead'
|
||||
local tokenlen = #v
|
||||
if longest_token_len < #v then
|
||||
longest_token_len = #v
|
||||
end
|
||||
end
|
||||
end
|
||||
-- sort them out using ther hash for binary search
|
||||
table.sort(tokens, function(a, b) return string.hash(a) < string.hash(b) end)
|
||||
local tokens = {" ", "\t", ",", "(", ")"} -- initial obvious tokens
|
||||
local longest_token_len = 0
|
||||
-- build 'tokens' table from list of operators from the language
|
||||
for _, v in ipairs(_TBASIC._OPERATR) do
|
||||
if not v:match("[A-Za-z]") then -- we want non-alphabetic operators as a token
|
||||
table.insert(tokens, v)
|
||||
-- get longest_token_len, will be used for 'lookahead'
|
||||
local tokenlen = #v
|
||||
if longest_token_len < #v then
|
||||
longest_token_len = #v
|
||||
end
|
||||
end
|
||||
end
|
||||
-- sort them out using ther hash for binary search
|
||||
table.sort(tokens, function(a, b) return string_hash(a) < string_hash(b) end)
|
||||
|
||||
|
||||
function parsewords(line)
|
||||
if line == nil then return end
|
||||
function parsewords(line)
|
||||
if line == nil then return end
|
||||
|
||||
-----------------------
|
||||
-- check line sanity --
|
||||
-----------------------
|
||||
-----------------------
|
||||
-- check line sanity --
|
||||
-----------------------
|
||||
|
||||
-- filter for IF statement
|
||||
if line:match("[Ii][Ff]") then
|
||||
-- no matching THEN
|
||||
if not line:match("[Tt][Hh][Ee][Nn]") then
|
||||
_TBASIC._ERROR.NOMATCHING("IF", "THEN")
|
||||
-- assignment on IF clause
|
||||
elseif line:match("[Ii][Ff][^\n]+[Tt][Hh][Ee][Nn]"):match("[^=]=[^=]") or
|
||||
line:match("[Ii][Ff][^\n]+[Tt][Hh][Ee][Nn]"):match(":=") then
|
||||
_TBASIC._ERROR.ASGONIF()
|
||||
end
|
||||
end
|
||||
-- filter for IF statement
|
||||
if line:sub(1, 2):upper() == "IF" then
|
||||
-- no matching THEN
|
||||
if not line:match("[Tt][Hh][Ee][Nn]") then
|
||||
_TBASIC._ERROR.NOMATCHING("IF", "THEN")
|
||||
-- assignment on IF clause
|
||||
elseif line:match("[Ii][Ff][^\n]+[Tt][Hh][Ee][Nn]"):match("[^=+%-*/%%<>!]=[^=<>]") or
|
||||
line:match("[Ii][Ff][^\n]+[Tt][Hh][Ee][Nn]"):match(":=") then
|
||||
_TBASIC._ERROR.ASGONIF()
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------
|
||||
-- automatically infer and insert some commands --
|
||||
--------------------------------------------------
|
||||
-- (This is starting to get dirty...)
|
||||
--------------------------------------------------
|
||||
-- automatically infer and insert some commands --
|
||||
--------------------------------------------------
|
||||
-- (This is starting to get dirty...)
|
||||
|
||||
-- unary minus
|
||||
local matchobj = line:find("%-[0-9]")
|
||||
if matchobj then -- in this pattern, it always returns a number
|
||||
local newline = line:sub(1, matchobj - 1) .. "MINUS " .. line:sub(matchobj + 1, #line)
|
||||
line = newline
|
||||
end
|
||||
-- conditional for IF
|
||||
-- if IF statement has no appended paren
|
||||
if not line:match("[Ii][Ff][ ]*%(") then
|
||||
local newline = line:gsub("[Ii][Ff]", "IF ( "):gsub("[Tt][Hh][Ee][Nn]", " ) THEN")
|
||||
line = newline
|
||||
end
|
||||
-- special treatment for FOR
|
||||
if line:sub(1, 3):upper() == "FOR" then
|
||||
if line:match("[0-9]?%.[0-9]") then -- real number used (e.g. "3.14", ".5")
|
||||
_TBASIC._ERROR.ILLEGALARG()
|
||||
else
|
||||
local varnameintm = line:match(" [^\n]+[ =]")
|
||||
-- unary minus
|
||||
for matchobj in line:gmatch("%-[0-9]+") do
|
||||
local newline = line:gsub(matchobj, "MINUS "..matchobj:sub(2, #matchobj))
|
||||
line = newline
|
||||
end
|
||||
-- conditional for IF
|
||||
-- if IF statement has no appended paren
|
||||
if line:sub(1, 2):upper() == "IF" and not line:match("[Ii][Ff][ ]*%(") then
|
||||
local newline = line:gsub("[Ii][Ff]", "IF ( ", 1):gsub("[Tt][Hh][Ee][Nn]", " ) THEN", 1)
|
||||
line = newline
|
||||
end
|
||||
-- special treatment for FOR
|
||||
if line:sub(1, 3):upper() == "FOR" then
|
||||
if line:match("[0-9]?%.[0-9]") then -- real number used (e.g. "3.14", ".5")
|
||||
_TBASIC._ERROR.ILLEGALARG()
|
||||
else
|
||||
local varnameintm = line:match(" [^\n]+[ =]")
|
||||
|
||||
if varnameintm then
|
||||
local varname = varnameintm:match("[^= ]+")
|
||||
if varname then
|
||||
local newline = line:gsub(" "..varname.."[ =]", " $"..varname.." "..varname.." = ")
|
||||
line = newline:gsub("= =", "=")
|
||||
else
|
||||
_TBASIC._ERROR.SYNTAX()
|
||||
end
|
||||
end
|
||||
-- basically, "FOR x x = 1 TO 10", which converts to "x x 1 10 TO = FOR",
|
||||
-- which is executed (in RPN) in steps of:
|
||||
-- "x x 1 10 TO = FOR"
|
||||
-- "x x (arr) = FOR"
|
||||
-- "x FOR" -- see this part? we need extra 'x' to feed for the FOR statement to function
|
||||
end
|
||||
end
|
||||
if varnameintm then
|
||||
local varname = varnameintm:match("[^= ]+")
|
||||
if varname then
|
||||
local newline = line:gsub(" "..varname.."[ =]", " $"..varname.." "..varname.." = ")
|
||||
line = newline:gsub("= =", "=")
|
||||
else
|
||||
_TBASIC._ERROR.SYNTAX()
|
||||
end
|
||||
end
|
||||
-- basically, "FOR x x = 1 TO 10", which converts to "x x 1 10 TO = FOR",
|
||||
-- which is executed (in RPN) in steps of:
|
||||
-- "x x 1 10 TO = FOR"
|
||||
-- "x x (arr) = FOR"
|
||||
-- "x FOR" -- see this part? we need extra 'x' to feed for the FOR statement to function
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
printdbg("parsing line", line)
|
||||
printdbg("parsing line", line)
|
||||
|
||||
|
||||
|
||||
lextable = {}
|
||||
isquote = false
|
||||
quotemode = false
|
||||
wordbuffer = ""
|
||||
local function flush()
|
||||
if (#wordbuffer > 0) then
|
||||
table.insert(lextable, wordbuffer)
|
||||
wordbuffer = ""
|
||||
end
|
||||
end
|
||||
local function append(char)
|
||||
wordbuffer = wordbuffer..char
|
||||
end
|
||||
local function append_no_whitespace(char)
|
||||
if char ~= " " and char ~= "\t" then
|
||||
wordbuffer = wordbuffer..char
|
||||
end
|
||||
end
|
||||
lextable = {}
|
||||
isquote = false
|
||||
quotemode = false
|
||||
wordbuffer = ""
|
||||
local function flush()
|
||||
if (#wordbuffer > 0) then
|
||||
table.insert(lextable, wordbuffer)
|
||||
wordbuffer = ""
|
||||
end
|
||||
end
|
||||
local function append(char)
|
||||
wordbuffer = wordbuffer..char
|
||||
end
|
||||
local function append_no_whitespace(char)
|
||||
if char ~= " " and char ~= "\t" then
|
||||
wordbuffer = wordbuffer..char
|
||||
end
|
||||
end
|
||||
|
||||
-- return: lookless_count on success, nil on failure
|
||||
local function isdelimeter(string)
|
||||
local cmpval = function(table_elem) return string.hash(table_elem) end
|
||||
local lookless_count = #string
|
||||
local ret = nil
|
||||
repeat
|
||||
ret = table.binsearch(tokens, string:sub(1, lookless_count), cmpval)
|
||||
lookless_count = lookless_count - 1
|
||||
until ret or lookless_count < 1
|
||||
return ret and lookless_count + 1 or false
|
||||
end
|
||||
-- return: lookless_count on success, nil on failure
|
||||
local function isdelimeter(string)
|
||||
local cmpval = function(table_elem) return string_hash(table_elem) end
|
||||
local lookless_count = #string
|
||||
local ret = nil
|
||||
repeat
|
||||
ret = table.binsearch(tokens, string:sub(1, lookless_count), cmpval)
|
||||
lookless_count = lookless_count - 1
|
||||
until ret or lookless_count < 1
|
||||
return ret and lookless_count + 1 or false
|
||||
end
|
||||
|
||||
local i = 1 -- Lua Protip: variable in 'for' is immutable, and is different from general variable table, even if they have same name
|
||||
while i <= #line do
|
||||
local c = string.char(line:byte(i))
|
||||
local i = 1 -- Lua Protip: variable in 'for' is immutable, and is different from general variable table, even if they have same name
|
||||
while i <= #line do
|
||||
local c = string.char(line:byte(i))
|
||||
|
||||
local lookahead = line:sub(i, i+longest_token_len)
|
||||
local lookahead = line:sub(i, i+longest_token_len)
|
||||
|
||||
if isquote then
|
||||
if c == [["]] then
|
||||
flush()
|
||||
isquote = false
|
||||
else
|
||||
append(c)
|
||||
end
|
||||
else
|
||||
if c == [["]] then
|
||||
isquote = true
|
||||
append_no_whitespace("~")
|
||||
else
|
||||
local delimsize = isdelimeter(lookahead) -- returns nil if no matching delimeter found
|
||||
if delimsize then
|
||||
flush() -- flush buffer
|
||||
append_no_whitespace(lookahead:sub(1, delimsize))
|
||||
flush() -- flush this delimeter
|
||||
i = i + delimsize - 1
|
||||
else
|
||||
append_no_whitespace(c)
|
||||
end
|
||||
end
|
||||
end
|
||||
if isquote then
|
||||
if c == [["]] then
|
||||
flush()
|
||||
isquote = false
|
||||
else
|
||||
append(c)
|
||||
end
|
||||
else
|
||||
if c == [["]] then
|
||||
isquote = true
|
||||
append_no_whitespace("~")
|
||||
else
|
||||
local delimsize = isdelimeter(lookahead) -- returns nil if no matching delimeter found
|
||||
if delimsize then
|
||||
flush() -- flush buffer
|
||||
append_no_whitespace(lookahead:sub(1, delimsize))
|
||||
flush() -- flush this delimeter
|
||||
i = i + delimsize - 1
|
||||
else
|
||||
append_no_whitespace(c)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
flush() -- don't forget this!
|
||||
i = i + 1
|
||||
end
|
||||
flush() -- don't forget this!
|
||||
|
||||
|
||||
return lextable
|
||||
end
|
||||
return lextable
|
||||
end
|
||||
end
|
||||
|
||||
local function readprogram(program)
|
||||
for line in program:gmatch("[^\n]+") do
|
||||
lineno = line:match("[0-9]+ ", 1)
|
||||
|
||||
if not lineno then
|
||||
_TBASIC._ERROR.NOLINENUM()
|
||||
end
|
||||
for line in program:gmatch("[^\n]+") do
|
||||
lineno = line:match("[0-9]+ ", 1)
|
||||
|
||||
if not lineno then
|
||||
_TBASIC._ERROR.NOLINENUM()
|
||||
end
|
||||
|
||||
statement = line:sub(#lineno + 1)
|
||||
statement = line:sub(#lineno + 1)
|
||||
|
||||
appendcommand(tonumber(lineno), statement)
|
||||
end
|
||||
appendcommand(tonumber(lineno), statement)
|
||||
end
|
||||
end
|
||||
|
||||
do -- Avoid heap allocs for performance
|
||||
local function stackpush(t, v)
|
||||
t[#t + 1] = v
|
||||
end
|
||||
local function stackpush(t, v)
|
||||
t[#t + 1] = v
|
||||
end
|
||||
|
||||
local function stackpop(t)
|
||||
local v = t[#t]
|
||||
t[#t] = nil
|
||||
return v
|
||||
end
|
||||
local function stackpop(t)
|
||||
local v = t[#t]
|
||||
t[#t] = nil
|
||||
return v
|
||||
end
|
||||
|
||||
local function stackpeek(t)
|
||||
local v = t[#t]
|
||||
return v
|
||||
end
|
||||
local function stackpeek(t)
|
||||
local v = t[#t]
|
||||
return v
|
||||
end
|
||||
|
||||
local function unmark(word)
|
||||
if type(word) == "table" then return word end
|
||||
return word:sub(2, #word)
|
||||
end
|
||||
local function unmark(word)
|
||||
if type(word) == "table" then return word end
|
||||
return word:sub(2, #word)
|
||||
end
|
||||
|
||||
local function isoperator(word)
|
||||
if word == nil then return false end
|
||||
return word:byte(1) == 35
|
||||
end
|
||||
local function isoperator(word)
|
||||
if word == nil then return false end
|
||||
return word:byte(1) == 35
|
||||
end
|
||||
|
||||
local isvariable = _TBASIC.isvariable
|
||||
local isvariable = _TBASIC.isvariable
|
||||
local isnumber = _TBASIC.isnumber
|
||||
local isstring = _TBASIC.isstring
|
||||
|
||||
local function isuserfunc(word)
|
||||
if type(word) == "table" then return false end
|
||||
if word == nil then return false end
|
||||
return word:byte(1) == 64
|
||||
end
|
||||
local function isuserfunc(word)
|
||||
if type(word) == "table" then return false end
|
||||
if word == nil then return false end
|
||||
return word:byte(1) == 64
|
||||
end
|
||||
|
||||
local function isbuiltin(word)
|
||||
if type(word) == "table" then return false end
|
||||
if word == nil then return false end
|
||||
return word:byte(1) == 38
|
||||
end
|
||||
local function isbuiltin(word)
|
||||
if type(word) == "table" then return false end
|
||||
if word == nil then return false end
|
||||
return word:byte(1) == 38
|
||||
end
|
||||
|
||||
local function iskeyword(word)
|
||||
if word == nil then return false end
|
||||
return isoperator(word) or isuserfunc(word) or isbuiltin(word)
|
||||
end
|
||||
local function iskeyword(word)
|
||||
if word == nil then return false end
|
||||
return isoperator(word) or isuserfunc(word) or isbuiltin(word)
|
||||
end
|
||||
|
||||
local function isassign(word)
|
||||
if word == nil then return false end
|
||||
return word ~= "==" and word ~= ">=" and word ~= "<=" and word:byte(#word) == 61
|
||||
end
|
||||
local function isassign(word)
|
||||
if word == nil then return false end
|
||||
return word ~= "==" and word ~= ">=" and word ~= "<=" and word:byte(#word) == 61
|
||||
end
|
||||
|
||||
local function isnoresolvevar(word)
|
||||
local novarresolve = {"NEXT"}
|
||||
-- returns truthy value "terminate_loop" upon termination of loop; nil otherwise.
|
||||
local function execword(word, args)
|
||||
if not _TBASIC.__appexit then
|
||||
printdbg("--> execword", word)
|
||||
printdbg("--> execword_args", table.unpack(args))
|
||||
|
||||
for _, w in ipairs(novarresolve) do -- linear search, because the array is small
|
||||
if word:upper() == w then
|
||||
return true
|
||||
end
|
||||
end
|
||||
if word == "IF" then
|
||||
printdbg("--> branch statement 'IF'")
|
||||
if not _TBASIC.__readvar(args[1]) then -- if condition 'false'
|
||||
printdbg("--> if condition 'false'", table.unpack(args))
|
||||
return "terminate_loop" -- evaluated as 'true' to Lua
|
||||
else
|
||||
printdbg("--> if condition 'true'", table.unpack(args))
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
printdbg("--> execword_outarg", table.unpack(args))
|
||||
result = _TBASIC.LUAFN[word][1](table.unpack(args))
|
||||
|
||||
local function execword(word, args)
|
||||
if not _TBASIC.__appexit then
|
||||
printdbg("--> execword", word)
|
||||
printdbg("--> inargs", table.unpack(args))
|
||||
printdbg("--> result", result)
|
||||
stackpush(execstack, result)
|
||||
end
|
||||
end
|
||||
|
||||
-- selectively resolve variable (if it's assign func, bottommost var -- target of assignation -- will not be resolved)
|
||||
-- for command "NEXT": DO NOT RESOLVE, pass its name (Call by Name)
|
||||
if not isnoresolvevar(word) then
|
||||
for i = isassign(word) and 2 or 1, #args do
|
||||
arg = args[i]
|
||||
|
||||
printdbg("--> resolvevar arg", arg)
|
||||
|
||||
if isvariable(arg) then
|
||||
var = unmark(arg)
|
||||
|
||||
if type(var) ~= "table" then
|
||||
value = _TBASIC._INTPRTR.CNSTANTS[var:upper()] -- try for pre-def
|
||||
|
||||
if value == nil then
|
||||
value = _TBASIC._INTPRTR.VARTABLE[var:upper()] -- try for user-def
|
||||
end
|
||||
|
||||
if value == nil then
|
||||
_TBASIC._ERROR.NULVAR(var)
|
||||
end
|
||||
|
||||
args[i] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if word == "IF" then
|
||||
printdbg("--> branch statement 'IF'")
|
||||
if not args[1] then -- if condition 'false'
|
||||
printdbg("--> if condition 'false'", table.unpack(args))
|
||||
return "terminate_loop" -- evaluated as 'true' to Lua
|
||||
else
|
||||
printdbg("--> if condition 'true'", table.unpack(args))
|
||||
end
|
||||
end
|
||||
|
||||
printdbg("--> execword outarg", table.unpack(args))
|
||||
result = _TBASIC.LUAFN[word][1](table.unpack(args))
|
||||
|
||||
printdbg("--> result", result)
|
||||
stackpush(execstack, result)
|
||||
end
|
||||
end
|
||||
|
||||
function printdbg(...)
|
||||
local debug = false
|
||||
if debug then print("DBG", ...) end
|
||||
end
|
||||
function printdbg(...)
|
||||
local debug = false
|
||||
if debug then print("DBG", ...) end
|
||||
end
|
||||
|
||||
|
||||
function interpretline(line)
|
||||
if not _TBASIC.__appexit then
|
||||
--[[
|
||||
impl
|
||||
function interpretline(line)
|
||||
if not _TBASIC.__appexit then
|
||||
--[[
|
||||
impl
|
||||
|
||||
1. (normalise expr using parsewords)
|
||||
2. use _TBASIC.RPNPARSR to convert to RPN
|
||||
3. execute RPN op set like FORTH
|
||||
1. (normalise expr using parsewords)
|
||||
2. use _TBASIC.RPNPARSR to convert to RPN
|
||||
3. execute RPN op set like FORTH
|
||||
|
||||
* "&" - internal functions
|
||||
* "@" - user-defined functions
|
||||
* "$" - variables (builtin constants and user-defined) -- familiar, eh?
|
||||
* "#" - operators
|
||||
* "~" - strings
|
||||
* none prepended - data (number or string)
|
||||
]]
|
||||
* "&" - internal functions
|
||||
* "@" - user-defined functions
|
||||
* "$" - variables (builtin constants and user-defined) -- familiar, eh?
|
||||
* "#" - operators
|
||||
* "~" - strings
|
||||
* none prepended - data (number or string)
|
||||
]]
|
||||
|
||||
lextable = parsewords(line)
|
||||
local vararg = -13 -- magic
|
||||
lextable = parsewords(line)
|
||||
local vararg = -13 -- magic
|
||||
|
||||
|
||||
if lextable and lextable[1] ~= nil then
|
||||
if lextable[1]:upper() == "REM" then return nil end
|
||||
if lextable and lextable[1] ~= nil then
|
||||
if lextable[1]:upper() == "REM" then return nil end
|
||||
|
||||
printdbg("lextable", table.concat(lextable, "|"))
|
||||
printdbg("lextable", table.concat(lextable, "|"))
|
||||
|
||||
-- execute expression
|
||||
exprlist = _TBASIC.TORPN(lextable) -- 2 2 #+ &PRINT for "PRINT 2+2"
|
||||
-- execute expression
|
||||
exprlist = _TBASIC.TORPN(lextable) -- 2 2 #+ &PRINT for "PRINT 2+2"
|
||||
|
||||
printdbg("trying to exec", table.concat(exprlist, " "), "\n--------")
|
||||
printdbg("trying to exec", table.concat(exprlist, " "), "\n--------")
|
||||
|
||||
execstack = {}
|
||||
execstack = {}
|
||||
|
||||
for _, word in ipairs(exprlist) do
|
||||
printdbg("stack before", table.concat(execstack, " "))
|
||||
printdbg("word", word)
|
||||
for _, word in ipairs(exprlist) do
|
||||
printdbg("stack before", table.concat(execstack, " "))
|
||||
printdbg("word", word)
|
||||
|
||||
if iskeyword(word) then
|
||||
printdbg("is keyword")
|
||||
if iskeyword(word) then
|
||||
printdbg("is keyword")
|
||||
|
||||
funcname = unmark(word)
|
||||
args = {}
|
||||
argsize = _TBASIC._GETARGS(funcname)
|
||||
funcname = unmark(word)
|
||||
args = {}
|
||||
argsize = _TBASIC._GETARGS(funcname)
|
||||
|
||||
printdbg("argsize", argsize)
|
||||
printdbg("argsize", argsize)
|
||||
|
||||
if not argsize then
|
||||
_TBASIC._ERROR.DEV_UNIMPL(funcname)
|
||||
else
|
||||
if argsize ~= vararg then
|
||||
-- consume 'argsize' elements from the stack
|
||||
for argcnt = argsize, 1, -1 do
|
||||
if #execstack == 0 then
|
||||
_TBASIC._ERROR.ARGMISSING(funcname)
|
||||
end
|
||||
args[argcnt] = stackpop(execstack)
|
||||
end
|
||||
else
|
||||
-- consume entire stack
|
||||
local reversedargs = {}
|
||||
while #execstack > 0 and isvariable(stackpeek(execstack)) do
|
||||
stackpush(reversedargs, stackpop(execstack))
|
||||
end
|
||||
-- reverse 'args'
|
||||
while #reversedargs > 0 do
|
||||
stackpush(args, stackpop(reversedargs))
|
||||
end
|
||||
end
|
||||
local terminate_loop = execword(funcname, args)
|
||||
if terminate_loop then
|
||||
printdbg("--> termination of loop")
|
||||
printdbg("--------")
|
||||
break
|
||||
end
|
||||
end
|
||||
elseif isvariable(word) then
|
||||
printdbg("is variable")
|
||||
stackpush(execstack, word) -- push raw variable ($ sign retained)
|
||||
else
|
||||
printdbg("is data")
|
||||
stackpush(execstack, word) -- push number or string
|
||||
end
|
||||
if not argsize then
|
||||
_TBASIC._ERROR.DEV_UNIMPL(funcname)
|
||||
else
|
||||
if argsize ~= vararg then
|
||||
-- consume 'argsize' elements from the stack
|
||||
for argcnt = argsize, 1, -1 do
|
||||
if #execstack == 0 then
|
||||
_TBASIC._ERROR.ARGMISSING(funcname)
|
||||
end
|
||||
args[argcnt] = stackpop(execstack)
|
||||
end
|
||||
else
|
||||
-- consume entire stack
|
||||
local reversedargs = {}
|
||||
|
||||
printdbg("stack after", table.concat(execstack, " "))
|
||||
printdbg("--------")
|
||||
end
|
||||
while #execstack > 0 and
|
||||
(isvariable(stackpeek(execstack)) or isnumber(stackpeek(execstack)) or
|
||||
isstring(stackpeek(execstack)))
|
||||
do
|
||||
stackpush(reversedargs, stackpop(execstack))
|
||||
end
|
||||
-- reverse 'args'
|
||||
while #reversedargs > 0 do
|
||||
stackpush(args, stackpop(reversedargs))
|
||||
end
|
||||
end
|
||||
|
||||
-- if execstack is not empty, something is wrong
|
||||
if #execstack > 0 then
|
||||
_TBASIC._ERROR.SYNTAX() -- cannot reliably pinpoint which statement has error; use generic error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local terminate_loop = execword(funcname, args)
|
||||
|
||||
if terminate_loop then
|
||||
printdbg("--> termination of loop")
|
||||
printdbg("--------")
|
||||
break
|
||||
end
|
||||
end
|
||||
elseif isvariable(word) then
|
||||
printdbg("is variable")
|
||||
stackpush(execstack, word) -- push raw variable ($ sign retained)
|
||||
else
|
||||
printdbg("is data")
|
||||
stackpush(execstack, word) -- push number or string
|
||||
end
|
||||
|
||||
printdbg("stack after", table.concat(execstack, " "))
|
||||
printdbg("--------")
|
||||
end
|
||||
|
||||
-- if execstack is not empty, something is wrong
|
||||
if #execstack > 0 then
|
||||
_TBASIC._ERROR.SYNTAX() -- cannot reliably pinpoint which statement has error; use generic error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function termination_condition()
|
||||
return terminated or
|
||||
_TBASIC._INTPRTR.GOTOCNTR > _TBASIC._INTPRTR.GOTOLMIT or
|
||||
_TBASIC.__appexit or
|
||||
#_TBASIC._INTPRTR.CALLSTCK > _TBASIC._INTPRTR.STACKMAX
|
||||
return terminated or
|
||||
_TBASIC.__appexit or
|
||||
#_TBASIC._INTPRTR.CALLSTCK > _TBASIC._INTPRTR.STACKMAX
|
||||
end
|
||||
|
||||
local function fetchnextcmd()
|
||||
cmd = nil
|
||||
repeat
|
||||
_TBASIC._INTPRTR.PROGCNTR = _TBASIC._INTPRTR.PROGCNTR + 1
|
||||
cmd = programlist[_TBASIC._INTPRTR.PROGCNTR]
|
||||
cmd = nil
|
||||
repeat
|
||||
_TBASIC._INTPRTR.PROGCNTR = _TBASIC._INTPRTR.PROGCNTR + 1
|
||||
cmd = programlist[_TBASIC._INTPRTR.PROGCNTR]
|
||||
|
||||
if _TBASIC._INTPRTR.PROGCNTR > _TBASIC._INTPRTR.MAXLINES then
|
||||
terminated = true
|
||||
break
|
||||
end
|
||||
until cmd ~= nil
|
||||
if _TBASIC._INTPRTR.PROGCNTR > _TBASIC._INTPRTR.MAXLINES then
|
||||
terminated = true
|
||||
break
|
||||
end
|
||||
until cmd ~= nil
|
||||
|
||||
if cmd ~= nil then
|
||||
if _TBASIC._INTPRTR.TRACE then
|
||||
print("PC", _TBASIC._INTPRTR.PROGCNTR)
|
||||
end
|
||||
if cmd ~= nil then
|
||||
if _TBASIC._INTPRTR.TRACE then
|
||||
print("PC", _TBASIC._INTPRTR.PROGCNTR)
|
||||
end
|
||||
|
||||
return cmd
|
||||
end
|
||||
return cmd
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function interpretall()
|
||||
|
||||
terminated = false
|
||||
terminated = false
|
||||
|
||||
repeat
|
||||
interpretline(fetchnextcmd())
|
||||
until termination_condition()
|
||||
|
||||
if _TBASIC._INTPRTR.GOTOCNTR > _TBASIC._INTPRTR.GOTOLMIT then
|
||||
_TBASIC._ERROR.TOOLONGEXEC()
|
||||
end
|
||||
repeat
|
||||
interpretline(fetchnextcmd())
|
||||
until termination_condition()
|
||||
end
|
||||
|
||||
-- END OF LEXER ---------------------------------------------------------------
|
||||
|
||||
-- _TBASIC.SHOWLUAERROR = false -- commented; let the shell handle it
|
||||
|
||||
local testprogram = nil
|
||||
|
||||
_G._TBASIC.EXEC = function(cmdstring) -- you can access this interpreter with this global function
|
||||
_TBASIC._INTPRTR.RESET()
|
||||
programlist = {}
|
||||
readprogram(cmdstring)
|
||||
interpretall()
|
||||
_TBASIC._INTPRTR.RESET()
|
||||
programlist = {} -- wipe out previous commands from interpreter (do not delete)
|
||||
readprogram(cmdstring)
|
||||
interpretall()
|
||||
end
|
||||
|
||||
|
||||
if testprogram then
|
||||
_TBASIC._INTPRTR.RESET()
|
||||
programlist = {}
|
||||
readprogram(testprogram)
|
||||
interpretall()
|
||||
_TBASIC._INTPRTR.RESET()
|
||||
programlist = {} -- wipe out previous commands from interpreter (do not delete)
|
||||
readprogram(testprogram)
|
||||
interpretall()
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -8,71 +8,260 @@ If no file is specified, interactive mode will be started
|
||||
|
||||
|
||||
if os and os.loadAPI then -- ComputerCraft
|
||||
os.loadAPI "TBASINCL.lua"
|
||||
os.loadAPI "TBASEXEC.lua"
|
||||
os.loadAPI "TBASINCL.lua"
|
||||
os.loadAPI "TBASEXEC.lua"
|
||||
else
|
||||
require "TBASINCL"
|
||||
require "TBASEXEC"
|
||||
require "TBASINCL"
|
||||
require "TBASEXEC"
|
||||
end
|
||||
|
||||
args = {...}
|
||||
|
||||
print(_G._TBASIC._VERSION)
|
||||
print(_G._TBASIC._HEADER)
|
||||
_TBASIC.PROMPT()
|
||||
_TBASIC.SHOWLUAERROR = false
|
||||
|
||||
|
||||
local function concat_lines(lines, startindex, endindex)
|
||||
local out = ""
|
||||
for i = startindex or 1, endindex or _TBASIC._INTPRTR.MAXLINES do
|
||||
if lines[i] ~= nil then
|
||||
out = out.."\n"..tostring(i).." "..lines[i]
|
||||
end
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
|
||||
if args[1] then
|
||||
local prog = nil
|
||||
if fs and fs.open then -- ComputerCraft
|
||||
local inp = assert(fs.open(args[1], "r"))
|
||||
prog = inp:readAll()
|
||||
inp:close()
|
||||
else
|
||||
local inp = assert(io.open(args[1], "r"))
|
||||
prog = inp:read("*all")
|
||||
inp:close()
|
||||
end
|
||||
local prog = nil
|
||||
if fs and fs.open then -- ComputerCraft
|
||||
local inp = assert(fs.open(args[1], "r"))
|
||||
prog = inp:readAll()
|
||||
inp:close()
|
||||
else
|
||||
local inp = assert(io.open(args[1], "r"))
|
||||
prog = inp:read("*all")
|
||||
inp:close()
|
||||
end
|
||||
|
||||
_TBASIC.EXEC(prog)
|
||||
_TBASIC.EXEC(prog)
|
||||
else
|
||||
local terminate_app = false
|
||||
local terminate_app = false
|
||||
|
||||
local ptn_nums = "[0-9]+"
|
||||
local renum_targets = {"GOTO[ ]+"..ptn_nums, "GOSUB[ ]+"..ptn_nums }
|
||||
|
||||
local lines = {}
|
||||
|
||||
local linenum_match = "[0-9]+ "
|
||||
|
||||
local get_linenum = function(line) return line:sub(1,6):match(linenum_match, 1) end -- line:sub(1,6) limits max linumber to be 99999
|
||||
local split_num_and_statements = function(line)
|
||||
local linenum = get_linenum(line)
|
||||
local statements = line:sub(#linenum + 1)
|
||||
return tonumber(linenum), statements
|
||||
end
|
||||
|
||||
while not terminate_app do
|
||||
local __read = false
|
||||
line = io.read()
|
||||
|
||||
-- tokenise line by " "
|
||||
args = {}
|
||||
for word in line:gmatch("[^ ]+") do
|
||||
table.insert(args, word:upper())
|
||||
end
|
||||
|
||||
|
||||
local lines = {}
|
||||
local lineno = 1
|
||||
while not terminate_app do
|
||||
local __read = false
|
||||
line = io.read()
|
||||
-- TODO more elegant code than IF-ELSEIF-ELSE
|
||||
|
||||
if line:upper() == "NEW" then
|
||||
lines = {}
|
||||
lineno = 1
|
||||
elseif line:upper() == "RUN" then
|
||||
_TBASIC.EXEC(table.concat(lines, "\n"))
|
||||
elseif line:upper() == "LIST" then
|
||||
print()
|
||||
print(table.concat(lines, "\n"))
|
||||
_TBASIC.PROMPT()
|
||||
__read = true
|
||||
elseif line:upper() == "EXIT" then
|
||||
terminate_app = true
|
||||
break
|
||||
elseif line:match("[0-9]+ ") then
|
||||
table.insert(lines, line)
|
||||
lineno = lineno + 1
|
||||
__read = true
|
||||
elseif #line == 0 and line:byte(1) ~= 10 and line:byte(1) ~= 13 then
|
||||
__read = true
|
||||
else
|
||||
_TBASIC.EXEC("1 "..line)
|
||||
end
|
||||
-- massive if-else for running command, cos implementing proper command executor is too expensive here
|
||||
if line:sub(1,6):match(linenum_match) then -- enter new command
|
||||
local linenum, statements = split_num_and_statements(line)
|
||||
lines[tonumber(linenum)] = statements
|
||||
__read = true
|
||||
elseif args[1] == "NEW" then
|
||||
lines = {}
|
||||
elseif args[1] == "RUN" then
|
||||
_TBASIC.EXEC(concat_lines(lines))
|
||||
elseif args[1] == "LIST" then -- LIST, LIST 42, LIST 10-80
|
||||
if not args[2] then
|
||||
print(concat_lines(lines))
|
||||
else
|
||||
if args[2]:match("-") then -- ranged
|
||||
range = {}
|
||||
for n in args[2]:gmatch("[^-]+") do
|
||||
table.insert(range, n)
|
||||
end
|
||||
local rangestart = tonumber(range[1])
|
||||
local rangeend = tonumber(range[2])
|
||||
|
||||
-- reset
|
||||
if not __read then
|
||||
_TBASIC.PROMPT()
|
||||
end
|
||||
end
|
||||
if not rangestart or not rangeend then
|
||||
_TBASIC._ERROR.ILLEGALARG()
|
||||
else
|
||||
print(concat_lines(lines, rangestart, rangeend))
|
||||
end
|
||||
else
|
||||
local linenum = tonumber(args[2])
|
||||
if not linenum then
|
||||
_TBASIC._ERROR.ILLEGALARG()
|
||||
else
|
||||
print(concat_lines(lines, linenum, linenum))
|
||||
end
|
||||
end
|
||||
end
|
||||
_TBASIC.PROMPT()
|
||||
__read = true
|
||||
elseif args[1] == "DELETE" then -- DELETE 30, DELETE 454-650
|
||||
if not args[2] then
|
||||
_TBASIC._ERROR.ILLEGALARG()
|
||||
else
|
||||
if args[2]:match("-") then -- ranged
|
||||
range = {}
|
||||
for n in args[2]:gmatch("[^-]+") do
|
||||
table.insert(range, n)
|
||||
end
|
||||
local rangestart = tonumber(range[1])
|
||||
local rangeend = tonumber(range[2])
|
||||
|
||||
if not rangestart or not rangeend then
|
||||
_TBASIC._ERROR.ILLEGALARG()
|
||||
else
|
||||
for i = rangestart, rangeend do
|
||||
lines[i] = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
local linenum = tonumber(args[2])
|
||||
if not linenum then
|
||||
_TBASIC._ERROR.ILLEGALARG()
|
||||
else
|
||||
lines[linenum] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif args[1] == "EXIT" then
|
||||
terminate_app = true
|
||||
break
|
||||
elseif args[1] == "SAVE" then
|
||||
local status, err = pcall(function()
|
||||
if fs and fs.open then -- computercraft
|
||||
local file = fs.open(args[2], "w")
|
||||
file.write(concat_lines(lines))
|
||||
file.close()
|
||||
else
|
||||
local file = assert(io.open(args[2], "w"))
|
||||
file:write(concat_lines(lines))
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
)
|
||||
if err then
|
||||
if _TBASIC.SHOWLUAERROR then
|
||||
print(err)
|
||||
end
|
||||
_TBASIC._ERROR.IOERR()
|
||||
else
|
||||
print("FILE SAVED")
|
||||
end
|
||||
elseif args[1] == "LOAD" then
|
||||
local status, err = pcall(function()
|
||||
lines = {}
|
||||
if fs and fs.open then -- computercraft
|
||||
local file = fs.open(args[2], "r")
|
||||
local data = file.readAll("*all")
|
||||
for dataline in data:gmatch("[^\n]+") do
|
||||
if #dataline > 0 then
|
||||
local linenum, statements = split_num_and_statements(dataline)
|
||||
lines[linenum] = statements
|
||||
end
|
||||
end
|
||||
file.close()
|
||||
else
|
||||
local file = assert(io.open(args[2], "r"))
|
||||
local data = file:read("*all")
|
||||
for dataline in data:gmatch("[^\n]+") do
|
||||
if #dataline > 0 then
|
||||
local linenum, statements = split_num_and_statements(dataline)
|
||||
lines[linenum] = statements
|
||||
end
|
||||
end
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
)
|
||||
if err then
|
||||
if _TBASIC.SHOWLUAERROR then
|
||||
error(err)
|
||||
end
|
||||
_TBASIC._ERROR.IOERR()
|
||||
else
|
||||
print("FILE LOADED")
|
||||
end
|
||||
elseif args[1] == "RENUM" then
|
||||
local statement_table = {}
|
||||
local renumbering_table = {}
|
||||
local new_linenum_counter = 10
|
||||
-- first, get the list of commands, without line number indexing
|
||||
for i = 1, _TBASIC._INTPRTR.MAXLINES do
|
||||
if lines[i] ~= nil then
|
||||
--table.insert(statement_table, lines[i])
|
||||
statement_table[new_linenum_counter] = lines[i]
|
||||
renumbering_table[i] = new_linenum_counter
|
||||
|
||||
-- test
|
||||
--print("old line", i, "new line", new_linenum_counter)
|
||||
|
||||
new_linenum_counter = new_linenum_counter + 10
|
||||
end
|
||||
end
|
||||
-- copy statement_table into lines table
|
||||
lines = statement_table
|
||||
|
||||
-- re-number GOTO and GOSUB line numbers
|
||||
local line_counter = 0 -- loop counter
|
||||
for line_pc = 0, _TBASIC._INTPRTR.MAXLINES do
|
||||
local line = lines[line_pc]
|
||||
if line then
|
||||
line_counter = line_counter + 1
|
||||
|
||||
-- replace
|
||||
-- extract a <- "GOTO 320"
|
||||
-- extract n_from from a (320), make n_to from it
|
||||
-- make new string b <- "GOTO "..n_to
|
||||
for _, match_string in ipairs(renum_targets) do
|
||||
local match = line:match(match_string)
|
||||
if match then
|
||||
local matching_statement = match:gsub("[ ]+"..ptn_nums, "")
|
||||
local target_line_old = tonumber(match:match(ptn_nums))
|
||||
local target_line_new = renumbering_table[target_line_old]
|
||||
|
||||
local gsub_from = match
|
||||
local gsub_to = matching_statement.." "..target_line_new
|
||||
|
||||
-- test
|
||||
--print("matching_statement", matching_statement, "target_line_old", target_line_old, "target_line_new", target_line_new)
|
||||
--print("gsub_from", gsub_from, "gsub_to", gsub_to)
|
||||
|
||||
-- substitute
|
||||
lines[line_pc] = line:gsub(gsub_from, gsub_to)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif #line == 0 and line:byte(1) ~= 10 and line:byte(1) ~= 13 then
|
||||
__read = true
|
||||
else
|
||||
_TBASIC.EXEC("1 "..line) -- execute command right away
|
||||
end
|
||||
|
||||
-- reset
|
||||
if not __read then
|
||||
_TBASIC.PROMPT()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@ import net.torvald.aa.AAFrame
|
||||
import net.torvald.aa.ColouredFastFont
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.gameactors.abs
|
||||
import net.torvald.terrarum.gamecontroller.Key
|
||||
import net.torvald.terrarum.virtualcomputer.computer.BaseTerrarumComputer
|
||||
import org.lwjgl.BufferUtils
|
||||
import org.lwjgl.openal.AL
|
||||
@@ -106,8 +107,6 @@ open class SimpleTextTerminal(
|
||||
private set
|
||||
|
||||
|
||||
private var redrawSemaphore = false
|
||||
|
||||
|
||||
override fun getColor(index: Int): Color = colours[index]
|
||||
|
||||
@@ -142,45 +141,40 @@ open class SimpleTextTerminal(
|
||||
* pass UIcanvas to the parameter "g"
|
||||
*/
|
||||
override fun render(gc: GameContainer, g: Graphics) {
|
||||
// FIXME don't redraw every time it's slow
|
||||
g.font = font
|
||||
|
||||
|
||||
// don't redraw() every fucking time, you're wasting your precious process cycle
|
||||
if (redrawSemaphore) {
|
||||
blendNormal()
|
||||
|
||||
blendNormal()
|
||||
|
||||
// black background (this is mandatory)
|
||||
g.color = Color.black
|
||||
g.fillRect(0f, 0f, displayW.toFloat(), displayH.toFloat())
|
||||
// black background (this is mandatory)
|
||||
g.color = Color.black
|
||||
g.fillRect(0f, 0f, displayW.toFloat(), displayH.toFloat())
|
||||
|
||||
|
||||
// screen buffer
|
||||
for (y in 0..height - 1) {
|
||||
for (x in 0..width - 1) {
|
||||
val ch = screenBuffer.getChar(x, y)
|
||||
// screen buffer
|
||||
for (y in 0..height - 1) {
|
||||
for (x in 0..width - 1) {
|
||||
val ch = screenBuffer.getChar(x, y)
|
||||
|
||||
// background
|
||||
g.color = getColor(screenBuffer.getBackgroundColour(x, y))
|
||||
g.fillRect(fontW * x.toFloat() + borderSize, fontH * y.toFloat() + borderSize,
|
||||
fontW.toFloat(), fontH.toFloat())
|
||||
// background
|
||||
g.color = getColor(screenBuffer.getBackgroundColour(x, y))
|
||||
g.fillRect(fontW * x.toFloat() + borderSize, fontH * y.toFloat() + borderSize,
|
||||
fontW.toFloat(), fontH.toFloat())
|
||||
|
||||
// foreground
|
||||
if (ch.toInt() != 0 && ch.toInt() != 32) {
|
||||
g.color = getColor(screenBuffer.getForegroundColour(x, y))
|
||||
g.drawString(
|
||||
Character.toString(ch),
|
||||
fontW * x.toFloat() + borderSize, fontH * y.toFloat() + borderSize)
|
||||
}
|
||||
// foreground
|
||||
if (ch.toInt() != 0 && ch.toInt() != 32) {
|
||||
g.color = getColor(screenBuffer.getForegroundColour(x, y))
|
||||
g.drawString(
|
||||
Character.toString(ch),
|
||||
fontW * x.toFloat() + borderSize, fontH * y.toFloat() + borderSize)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// cursor
|
||||
if (cursorBlinkOn) {
|
||||
g.color = getColor(if (cursorBlink) foreDefault else backDefault) screen colourScreen mul phosphor
|
||||
g.color = getColor(if (cursorBlink) foreDefault else backDefault)
|
||||
|
||||
g.fillRect(
|
||||
fontW * cursorX.toFloat() + borderSize,
|
||||
@@ -189,48 +183,23 @@ open class SimpleTextTerminal(
|
||||
fontH.toFloat()
|
||||
)
|
||||
}
|
||||
else {
|
||||
val x = cursorX
|
||||
val y = cursorY
|
||||
val ch = screenBuffer.getChar(x, y)
|
||||
|
||||
// background
|
||||
g.color = getColor(screenBuffer.getBackgroundColour(x, y)) screen colourScreen mul phosphor
|
||||
g.fillRect(fontW * x.toFloat() + borderSize, fontH * y.toFloat() + borderSize,
|
||||
fontW.toFloat(), fontH.toFloat())
|
||||
// colour base
|
||||
g.color = colourScreen
|
||||
blendScreen()
|
||||
g.fillRect(0f, 0f, fontW * width.toFloat() + 2 * borderSize, fontH * height.toFloat() + 2 * borderSize)
|
||||
|
||||
// foreground
|
||||
if (ch.toInt() != 0 && ch.toInt() != 32) {
|
||||
g.color = getColor(screenBuffer.getForegroundColour(x, y)) screen colourScreen mul phosphor
|
||||
g.drawString(
|
||||
Character.toString(ch),
|
||||
fontW * x.toFloat() + borderSize, fontH * y.toFloat() + borderSize)
|
||||
}
|
||||
}
|
||||
|
||||
if (redrawSemaphore) {
|
||||
// colour base
|
||||
g.color = colourScreen
|
||||
blendScreen()
|
||||
g.fillRect(0f, 0f, fontW * width.toFloat() + 2 * borderSize, fontH * height.toFloat() + 2 * borderSize)
|
||||
|
||||
// colour overlay
|
||||
g.color = phosphor
|
||||
blendMul()
|
||||
g.fillRect(0f, 0f, fontW * width.toFloat() + 2 * borderSize, fontH * height.toFloat() + 2 * borderSize)
|
||||
// colour overlay
|
||||
g.color = phosphor
|
||||
blendMul()
|
||||
g.fillRect(0f, 0f, fontW * width.toFloat() + 2 * borderSize, fontH * height.toFloat() + 2 * borderSize)
|
||||
|
||||
|
||||
redrawSemaphore = false
|
||||
}
|
||||
|
||||
blendNormal()
|
||||
|
||||
}
|
||||
|
||||
fun redraw() {
|
||||
redrawSemaphore = true
|
||||
}
|
||||
|
||||
/** Unlike lua function, this one in Zero-based. */
|
||||
override fun setCursor(x: Int, y: Int) {
|
||||
cursorX = x
|
||||
@@ -444,7 +413,7 @@ open class SimpleTextTerminal(
|
||||
else if (keyPressVisible)
|
||||
printChar(c)
|
||||
if (!asciiControlInUse.contains(c)) sb.append(c)
|
||||
else if (c == ASCII_DEL && sb.length > 0) sb.deleteCharAt(sb.length - 1)
|
||||
else if (key == Key.BACKSPACE && sb.isNotEmpty()) sb.deleteCharAt(sb.length - 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user