mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +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:
@@ -34,7 +34,7 @@ constructor(var width: Int, var height: Int, var terminal: SimpleTextTerminal) {
|
||||
throw ArrayIndexOutOfBoundsException("x: $x, y; $y")
|
||||
frameBuffer[y * width + x] = ((c.toInt().and(0xFF)) + colourKey.shl(8)).toChar()
|
||||
|
||||
terminal.redraw()
|
||||
//terminal.redraw()
|
||||
}
|
||||
|
||||
fun drawBuffer(x: Int, y: Int, raw: Char): Boolean =
|
||||
@@ -42,7 +42,7 @@ constructor(var width: Int, var height: Int, var terminal: SimpleTextTerminal) {
|
||||
false
|
||||
else {
|
||||
frameBuffer[y * width + x] = raw
|
||||
terminal.redraw()
|
||||
//terminal.redraw()
|
||||
true
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ constructor(var width: Int, var height: Int, var terminal: SimpleTextTerminal) {
|
||||
val char = (other[i].toUint().shl(8) + other[i + 1].toUint()).toChar()
|
||||
frameBuffer[i.ushr(1)] = char
|
||||
}
|
||||
terminal.redraw()
|
||||
//terminal.redraw()
|
||||
}
|
||||
|
||||
fun getBackgroundColour(x: Int, y: Int): Int {
|
||||
@@ -78,7 +78,7 @@ constructor(var width: Int, var height: Int, var terminal: SimpleTextTerminal) {
|
||||
drawBuffer(x, y, 0.toChar(), background.shl(4))
|
||||
}
|
||||
}
|
||||
terminal.redraw()
|
||||
//terminal.redraw()
|
||||
}
|
||||
|
||||
fun drawFromOther(other: AAFrame) {
|
||||
@@ -88,7 +88,7 @@ constructor(var width: Int, var height: Int, var terminal: SimpleTextTerminal) {
|
||||
frameBuffer[y * width + x] = other.getRaw(x, y)!!
|
||||
}
|
||||
}
|
||||
terminal.redraw()
|
||||
//terminal.redraw()
|
||||
}
|
||||
|
||||
private fun checkOOB(x: Int, y: Int) = (x < 0 || y < 0 || x >= width || y >= height)
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
package net.torvald.spriteanimation
|
||||
|
||||
import net.torvald.terrarum.StateInGame
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import com.jme3.math.FastMath
|
||||
import net.torvald.terrarum.gameactors.ActorWithSprite
|
||||
import org.newdawn.slick.Graphics
|
||||
@@ -214,26 +212,6 @@ class SpriteAnimation(val parentActor: ActorWithSprite, val cellWidth: Int, val
|
||||
|
||||
private fun getScaledSprite(scale: Float): Image {
|
||||
val selectedImage = spriteImage!!.getSprite(currentFrame - 1, currentRow - 1)
|
||||
//Image selectedImage = sprites[currentRow - 1][currentFrame - 1];
|
||||
|
||||
// resample
|
||||
/*float nearestResampleScale = (scale > 1) ? Math.round(scale) : 1;
|
||||
float linearResampleScale = scale / nearestResampleScale;
|
||||
|
||||
// scale 1.8 -> resample in 2(nearest), then resample in 0.9(linear)
|
||||
// scale by nearestResampleScale (2, 3, ...)
|
||||
selectedImage.setFilter(Image.FILTER_NEAREST);
|
||||
Image selImgNearestScaled = selectedImage.getScaledCopy(nearestResampleScale);
|
||||
// scale by linearResampleScale (.x)
|
||||
Image selImgLinearScaled;
|
||||
if (scale % 1 > 0) {
|
||||
selImgNearestScaled.setFilter(Image.FILTER_LINEAR);
|
||||
selImgLinearScaled = selImgNearestScaled.getScaledCopy(linearResampleScale);
|
||||
return selImgLinearScaled;
|
||||
}
|
||||
else {
|
||||
return selImgNearestScaled;
|
||||
}*/
|
||||
selectedImage.filter = Image.FILTER_NEAREST
|
||||
return selectedImage.getScaledCopy(scale)
|
||||
}
|
||||
|
||||
@@ -14,16 +14,23 @@ class StateFontTester : BasicGameState() {
|
||||
|
||||
lateinit var canvas: Graphics
|
||||
|
||||
lateinit var segfont: Font
|
||||
//lateinit var segfont: Font
|
||||
|
||||
lateinit var mtfont: Font
|
||||
|
||||
override fun init(gc: GameContainer, game: StateBasedGame) {
|
||||
canvas = Graphics(1024, 1024)
|
||||
|
||||
Terrarum.gameLocale = "fiFI"
|
||||
|
||||
segfont = SpriteSheetFont(
|
||||
/*segfont = SpriteSheetFont(
|
||||
SpriteSheet("./assets/graphics/fonts/24-seg_red.tga", 22, 31),
|
||||
' '
|
||||
)*/
|
||||
|
||||
mtfont = SpriteSheetFont(
|
||||
SpriteSheet("./assets/graphics/fonts/mt-32.tga", 12, 16),
|
||||
0.toChar()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -50,9 +57,10 @@ class StateFontTester : BasicGameState() {
|
||||
}*/
|
||||
|
||||
//g.font = Terrarum.fontSmallNumbers
|
||||
g.font = segfont
|
||||
//g.font = segfont
|
||||
g.font = mtfont
|
||||
|
||||
val line = """print("Lua is copyrighted (C) 1994-2013 Lua.org, PUC-Rio")"""
|
||||
val line = " **** TERRAN BASIC V0.5 **** "
|
||||
|
||||
g.drawString(line, 10f, 10f)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class StateVTTest : BasicGameState() {
|
||||
|
||||
override fun update(container: GameContainer, game: StateBasedGame, delta: Int) {
|
||||
Terrarum.appgc.setTitle("VT — F: ${container.fps}" +
|
||||
" — M: ${Terrarum.memInUse}M / ${Terrarum.totalVMMem}M")
|
||||
" — M: ${Terrarum.memInUse}M / ${Terrarum.memXmx}M")
|
||||
vt.update(container, delta)
|
||||
computerInside.update(container, delta)
|
||||
}
|
||||
|
||||
@@ -195,8 +195,10 @@ constructor(gamename: String) : StateBasedGame(gamename) {
|
||||
private set
|
||||
|
||||
val memInUse: Long
|
||||
get() = ManagementFactory.getMemoryMXBean().heapMemoryUsage.used shr 20
|
||||
val totalVMMem: Long
|
||||
get() = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) shr 20
|
||||
val memTotal: Long
|
||||
get() = Runtime.getRuntime().totalMemory() shr 20
|
||||
val memXmx: Long
|
||||
get() = Runtime.getRuntime().maxMemory() shr 20
|
||||
|
||||
lateinit var environment: RunningEnvironment
|
||||
|
||||
@@ -18,7 +18,7 @@ object AVTracker : ConsoleCommand {
|
||||
try {
|
||||
val actorID = args[1].toInt()
|
||||
|
||||
if (Terrarum.ingame.hasActor(actorID)) {
|
||||
if (Terrarum.ingame.theGameHasActor(actorID)) {
|
||||
jPanelInstances.add(ActorValueTracker(Terrarum.ingame.getActorByID(actorID)))
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -39,8 +39,8 @@ abstract class Actor(val renderOrder: ActorOrder) : Comparable<Actor>, Runnable
|
||||
* override var referenceID: Int = generateUniqueReferenceID()
|
||||
*/
|
||||
fun generateUniqueReferenceID(): Int {
|
||||
fun checkForCollision(value: Int) =
|
||||
Terrarum.ingame.hasActor(value) ||
|
||||
fun itIsNotValid(value: Int) =
|
||||
Terrarum.ingame.theGameHasActor(value) ||
|
||||
value < ItemCodex.ITEM_COUNT_MAX ||
|
||||
value < when (renderOrder) {
|
||||
ActorOrder.BEHIND -> ItemCodex.ITEM_COUNT_MAX
|
||||
@@ -58,7 +58,7 @@ abstract class Actor(val renderOrder: ActorOrder) : Comparable<Actor>, Runnable
|
||||
var ret: Int
|
||||
do {
|
||||
ret = HQRNG().nextInt().and(0x7FFFFFFF) // set new ID
|
||||
} while (checkForCollision(ret)) // check for collision
|
||||
} while (itIsNotValid(ret)) // check for collision
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ open class ParticleBase(renderOrder: ActorOrder, maxLifeTime: Int? = null) : Run
|
||||
open val velocity = Vector2(0.0, 0.0)
|
||||
open val hitbox = Hitbox(0.0, 0.0, 0.0, 0.0)
|
||||
|
||||
open lateinit var body: Image
|
||||
open lateinit var body: Image // you might want to use SpriteAnimation
|
||||
open var glow: Image? = null
|
||||
|
||||
init {
|
||||
|
||||
@@ -66,7 +66,7 @@ class WorldTime {
|
||||
val YEAR_DAYS: Int = 365
|
||||
|
||||
fun parseTime(s: String): Int =
|
||||
if (s.length >= 4) {
|
||||
if (s.length >= 4 && s.contains('h')) {
|
||||
s.toLowerCase().substringBefore('h').toInt() * WorldTime.HOUR_SEC +
|
||||
s.toLowerCase().substringAfter('h').toInt() * WorldTime.MINUTE_SEC
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.torvald.terrarum.concurrent.ThreadParallel
|
||||
import net.torvald.terrarum.blendMul
|
||||
import net.torvald.terrarum.blendNormal
|
||||
import net.torvald.terrarum.mapdrawer.FeaturesDrawer.TILE_SIZE
|
||||
import net.torvald.terrarum.mapdrawer.LightmapRenderer.normaliseToColour
|
||||
import net.torvald.terrarum.mapdrawer.MapCamera.x
|
||||
import net.torvald.terrarum.mapdrawer.MapCamera.y
|
||||
import net.torvald.terrarum.mapdrawer.MapCamera.height
|
||||
|
||||
@@ -48,7 +48,7 @@ object TilePropCSV {
|
||||
"8"; "4";"TILE_GEM_DIAMOND" ; "33587232"; "25";"2400";"rock"; "0"; "1"; "0"; "0"; "8"; "4"; "0"; "0"; "N/A"; "0";"16"
|
||||
"8"; "5";"TILE_GEM_AMETHYST" ; "33587232"; "25";"2400";"rock"; "0"; "1"; "0"; "0"; "8"; "5"; "0"; "0"; "N/A"; "0";"16"
|
||||
"9"; "0";"TILE_SNOW" ; "33587232"; "6"; "500";"snow"; "0"; "1"; "1"; "0"; "9"; "0"; "0"; "0"; "N/A"; "0";"16"
|
||||
"9"; "1";"TILE_ICE_FRAGILE" ; "13644813"; "1"; "930";"icei"; "0"; "1"; "0"; "0"; "9"; "1"; "0"; "0"; "N/A"; "0";"16"
|
||||
"9"; "1";"TILE_ICE_FRAGILE" ; "13644813"; "1"; "930";"icei"; "0"; "1"; "0"; "0"; "9"; "1"; "0"; "0"; "N/A"; "0"; "4"
|
||||
"9"; "2";"TILE_ICE_NATURAL" ; "27289626"; "25"; "930";"icei"; "0"; "1"; "1"; "0"; "9"; "2"; "0"; "0"; "N/A"; "0"; "4"
|
||||
"9"; "3";"TILE_ICE_CLEAR_MAGICAL" ; "33587232"; "25";"3720";"icex"; "0"; "1"; "1"; "19955770"; "9"; "3"; "0"; "0"; "N/A"; "0"; "4"
|
||||
"9"; "4";"TILE_GLASS_CRUDE" ; "3146755"; "1";"2500";"glas"; "0"; "1"; "1"; "0"; "9"; "4"; "0"; "0"; "N/A"; "0";"16"
|
||||
@@ -135,7 +135,7 @@ object TilePropCSV {
|
||||
"255"; "13";"TILE_WATER" ; "27282445"; "100";"1000";"watr"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0"; "0"; "16"; "0";"16"
|
||||
"255"; "14";"TILE_WATER" ; "27282445"; "100";"1000";"watr"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0"; "0"; "16"; "0";"16"
|
||||
"255"; "15";"TILE_WATER" ; "27282445"; "100";"1000";"watr"; "1"; "0"; "0"; "0"; "N/A"; "N/A"; "0"; "0"; "16"; "0";"16"
|
||||
"256"; "0";"TILE_NULL" ; "0"; "-1";"2600";"null"; "0"; "0"; "0"; "0"; "N/A"; "N/A"; "0"; "0"; "N/A"; "0";"16"
|
||||
"256"; "0";"TILE_NULL" ;"1073741823"; "-1";"2600";"null"; "0"; "0"; "1"; "0"; "N/A"; "N/A"; "0"; "0"; "N/A"; "0";"16"
|
||||
|
||||
## Notes ##
|
||||
|
||||
|
||||
@@ -153,16 +153,19 @@ class BasicDebugInfoWindow : UICanvas {
|
||||
*/
|
||||
|
||||
g.color = GameFontBase.codeToCol["y"]
|
||||
g.drawString("${ccY}MEM ", (Terrarum.WIDTH - 15 * 8 - 2).toFloat(), 2f)
|
||||
g.drawString("${ccY}MEM ", (Terrarum.WIDTH - 21 * 8 - 2).toFloat(), 2f)
|
||||
//g.drawString("${ccY}FPS $ccG${Terrarum.appgc.fps}", (Terrarum.WIDTH - 6 * 8 - 2).toFloat(), 10f)
|
||||
g.drawString("${ccY}CPUs ${if (Terrarum.MULTITHREAD) ccG else ccR}${Terrarum.THREADS}",
|
||||
(Terrarum.WIDTH - 2 - 6*8).toFloat(), 10f)
|
||||
|
||||
g.color = GameFontBase.codeToCol["g"]
|
||||
g.drawString("${Terrarum.memInUse}M",
|
||||
(Terrarum.WIDTH - 11 * 8 - 2).toFloat(), 2f)
|
||||
g.drawString("/${Terrarum.totalVMMem}M",
|
||||
(Terrarum.WIDTH - 6 * 8 - 2).toFloat(), 2f)
|
||||
(Terrarum.WIDTH - 17 * 8 - 2).toFloat(), 2f)
|
||||
g.drawString("/${Terrarum.memTotal}M/",
|
||||
(Terrarum.WIDTH - 12 * 8 - 2).toFloat(), 2f)
|
||||
g.color = GameFontBase.codeToCol["m"]
|
||||
g.drawString("${Terrarum.memXmx}M",
|
||||
(Terrarum.WIDTH - 5 * 8 - 2).toFloat(), 2f)
|
||||
|
||||
/**
|
||||
* Bottom left
|
||||
@@ -174,7 +177,7 @@ class BasicDebugInfoWindow : UICanvas {
|
||||
(2 + 17*8).toFloat(), Terrarum.HEIGHT - 10f)
|
||||
g.drawString("${ccY}Dormant $ccG${Terrarum.ingame.actorContainerInactive.size}",
|
||||
(2 + 28*8).toFloat(), Terrarum.HEIGHT - 10f)
|
||||
g.drawString("${ccM}Particles $ccG${Terrarum.ingame.particlesContainer.elemCount}",
|
||||
g.drawString("${ccM}Particles $ccG${Terrarum.ingame.particlesActive}",
|
||||
(2 + 41*8).toFloat(), Terrarum.HEIGHT - 10f)
|
||||
}
|
||||
|
||||
|
||||
@@ -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