just updating things so that I can have a backup point...

Former-commit-id: 0a5a6d7f68ee1a96562532572c8d45fe102d3c25
Former-commit-id: a1a78f61f2fe2a8707e47633caa6cd67a829b35e
This commit is contained in:
Song Minjae
2017-01-26 16:58:06 +09:00
parent 06296983b5
commit 2203f74429
16 changed files with 1765 additions and 1226 deletions

View File

@@ -34,7 +34,7 @@ constructor(var width: Int, var height: Int, var terminal: SimpleTextTerminal) {
throw ArrayIndexOutOfBoundsException("x: $x, y; $y") throw ArrayIndexOutOfBoundsException("x: $x, y; $y")
frameBuffer[y * width + x] = ((c.toInt().and(0xFF)) + colourKey.shl(8)).toChar() 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 = fun drawBuffer(x: Int, y: Int, raw: Char): Boolean =
@@ -42,7 +42,7 @@ constructor(var width: Int, var height: Int, var terminal: SimpleTextTerminal) {
false false
else { else {
frameBuffer[y * width + x] = raw frameBuffer[y * width + x] = raw
terminal.redraw() //terminal.redraw()
true 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() val char = (other[i].toUint().shl(8) + other[i + 1].toUint()).toChar()
frameBuffer[i.ushr(1)] = char frameBuffer[i.ushr(1)] = char
} }
terminal.redraw() //terminal.redraw()
} }
fun getBackgroundColour(x: Int, y: Int): Int { 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)) drawBuffer(x, y, 0.toChar(), background.shl(4))
} }
} }
terminal.redraw() //terminal.redraw()
} }
fun drawFromOther(other: AAFrame) { 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)!! 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) private fun checkOOB(x: Int, y: Int) = (x < 0 || y < 0 || x >= width || y >= height)

View File

@@ -4,8 +4,6 @@
package net.torvald.spriteanimation package net.torvald.spriteanimation
import net.torvald.terrarum.StateInGame
import net.torvald.terrarum.Terrarum
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.terrarum.gameactors.ActorWithSprite import net.torvald.terrarum.gameactors.ActorWithSprite
import org.newdawn.slick.Graphics import org.newdawn.slick.Graphics
@@ -214,26 +212,6 @@ class SpriteAnimation(val parentActor: ActorWithSprite, val cellWidth: Int, val
private fun getScaledSprite(scale: Float): Image { private fun getScaledSprite(scale: Float): Image {
val selectedImage = spriteImage!!.getSprite(currentFrame - 1, currentRow - 1) 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 selectedImage.filter = Image.FILTER_NEAREST
return selectedImage.getScaledCopy(scale) return selectedImage.getScaledCopy(scale)
} }

View File

@@ -14,16 +14,23 @@ class StateFontTester : BasicGameState() {
lateinit var canvas: Graphics lateinit var canvas: Graphics
lateinit var segfont: Font //lateinit var segfont: Font
lateinit var mtfont: Font
override fun init(gc: GameContainer, game: StateBasedGame) { override fun init(gc: GameContainer, game: StateBasedGame) {
canvas = Graphics(1024, 1024) canvas = Graphics(1024, 1024)
Terrarum.gameLocale = "fiFI" Terrarum.gameLocale = "fiFI"
segfont = SpriteSheetFont( /*segfont = SpriteSheetFont(
SpriteSheet("./assets/graphics/fonts/24-seg_red.tga", 22, 31), 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 = 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) g.drawString(line, 10f, 10f)
} }

View File

@@ -38,7 +38,7 @@ class StateVTTest : BasicGameState() {
override fun update(container: GameContainer, game: StateBasedGame, delta: Int) { override fun update(container: GameContainer, game: StateBasedGame, delta: Int) {
Terrarum.appgc.setTitle("VT — F: ${container.fps}" + 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) vt.update(container, delta)
computerInside.update(container, delta) computerInside.update(container, delta)
} }

View File

@@ -195,8 +195,10 @@ constructor(gamename: String) : StateBasedGame(gamename) {
private set private set
val memInUse: Long val memInUse: Long
get() = ManagementFactory.getMemoryMXBean().heapMemoryUsage.used shr 20 get() = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) shr 20
val totalVMMem: Long val memTotal: Long
get() = Runtime.getRuntime().totalMemory() shr 20
val memXmx: Long
get() = Runtime.getRuntime().maxMemory() shr 20 get() = Runtime.getRuntime().maxMemory() shr 20
lateinit var environment: RunningEnvironment lateinit var environment: RunningEnvironment

View File

@@ -18,7 +18,7 @@ object AVTracker : ConsoleCommand {
try { try {
val actorID = args[1].toInt() val actorID = args[1].toInt()
if (Terrarum.ingame.hasActor(actorID)) { if (Terrarum.ingame.theGameHasActor(actorID)) {
jPanelInstances.add(ActorValueTracker(Terrarum.ingame.getActorByID(actorID))) jPanelInstances.add(ActorValueTracker(Terrarum.ingame.getActorByID(actorID)))
} }
else { else {

View File

@@ -39,8 +39,8 @@ abstract class Actor(val renderOrder: ActorOrder) : Comparable<Actor>, Runnable
* override var referenceID: Int = generateUniqueReferenceID() * override var referenceID: Int = generateUniqueReferenceID()
*/ */
fun generateUniqueReferenceID(): Int { fun generateUniqueReferenceID(): Int {
fun checkForCollision(value: Int) = fun itIsNotValid(value: Int) =
Terrarum.ingame.hasActor(value) || Terrarum.ingame.theGameHasActor(value) ||
value < ItemCodex.ITEM_COUNT_MAX || value < ItemCodex.ITEM_COUNT_MAX ||
value < when (renderOrder) { value < when (renderOrder) {
ActorOrder.BEHIND -> ItemCodex.ITEM_COUNT_MAX ActorOrder.BEHIND -> ItemCodex.ITEM_COUNT_MAX
@@ -58,7 +58,7 @@ abstract class Actor(val renderOrder: ActorOrder) : Comparable<Actor>, Runnable
var ret: Int var ret: Int
do { do {
ret = HQRNG().nextInt().and(0x7FFFFFFF) // set new ID ret = HQRNG().nextInt().and(0x7FFFFFFF) // set new ID
} while (checkForCollision(ret)) // check for collision } while (itIsNotValid(ret)) // check for collision
return ret return ret
} }

View File

@@ -31,7 +31,7 @@ open class ParticleBase(renderOrder: ActorOrder, maxLifeTime: Int? = null) : Run
open val velocity = Vector2(0.0, 0.0) open val velocity = Vector2(0.0, 0.0)
open val hitbox = Hitbox(0.0, 0.0, 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 open var glow: Image? = null
init { init {

View File

@@ -66,7 +66,7 @@ class WorldTime {
val YEAR_DAYS: Int = 365 val YEAR_DAYS: Int = 365
fun parseTime(s: String): Int = 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().substringBefore('h').toInt() * WorldTime.HOUR_SEC +
s.toLowerCase().substringAfter('h').toInt() * WorldTime.MINUTE_SEC s.toLowerCase().substringAfter('h').toInt() * WorldTime.MINUTE_SEC
} }

View File

@@ -11,6 +11,7 @@ import net.torvald.terrarum.concurrent.ThreadParallel
import net.torvald.terrarum.blendMul import net.torvald.terrarum.blendMul
import net.torvald.terrarum.blendNormal import net.torvald.terrarum.blendNormal
import net.torvald.terrarum.mapdrawer.FeaturesDrawer.TILE_SIZE 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.x
import net.torvald.terrarum.mapdrawer.MapCamera.y import net.torvald.terrarum.mapdrawer.MapCamera.y
import net.torvald.terrarum.mapdrawer.MapCamera.height import net.torvald.terrarum.mapdrawer.MapCamera.height

View File

@@ -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"; "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" "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"; "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"; "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"; "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" "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"; "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"; "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" "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 ## ## Notes ##

View File

@@ -153,16 +153,19 @@ class BasicDebugInfoWindow : UICanvas {
*/ */
g.color = GameFontBase.codeToCol["y"] 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}FPS $ccG${Terrarum.appgc.fps}", (Terrarum.WIDTH - 6 * 8 - 2).toFloat(), 10f)
g.drawString("${ccY}CPUs ${if (Terrarum.MULTITHREAD) ccG else ccR}${Terrarum.THREADS}", g.drawString("${ccY}CPUs ${if (Terrarum.MULTITHREAD) ccG else ccR}${Terrarum.THREADS}",
(Terrarum.WIDTH - 2 - 6*8).toFloat(), 10f) (Terrarum.WIDTH - 2 - 6*8).toFloat(), 10f)
g.color = GameFontBase.codeToCol["g"] g.color = GameFontBase.codeToCol["g"]
g.drawString("${Terrarum.memInUse}M", g.drawString("${Terrarum.memInUse}M",
(Terrarum.WIDTH - 11 * 8 - 2).toFloat(), 2f) (Terrarum.WIDTH - 17 * 8 - 2).toFloat(), 2f)
g.drawString("/${Terrarum.totalVMMem}M", g.drawString("/${Terrarum.memTotal}M/",
(Terrarum.WIDTH - 6 * 8 - 2).toFloat(), 2f) (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 * Bottom left
@@ -174,7 +177,7 @@ class BasicDebugInfoWindow : UICanvas {
(2 + 17*8).toFloat(), Terrarum.HEIGHT - 10f) (2 + 17*8).toFloat(), Terrarum.HEIGHT - 10f)
g.drawString("${ccY}Dormant $ccG${Terrarum.ingame.actorContainerInactive.size}", g.drawString("${ccY}Dormant $ccG${Terrarum.ingame.actorContainerInactive.size}",
(2 + 28*8).toFloat(), Terrarum.HEIGHT - 10f) (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) (2 + 41*8).toFloat(), Terrarum.HEIGHT - 10f)
} }

View File

@@ -6,30 +6,40 @@ TBASIC: Simple BASIC language based on the Commodore BASIC Version 2.
How to use in your program: How to use in your program:
1. load the script by: 1. load the script by:
if you're using ComputerCraft, use: if you're using ComputerCraft, use:
os.loadAPI "TBASEXEC.lua" os.loadAPI "TBASEXEC.lua"
else, use: else, use:
require "TBASEXEC" require "TBASEXEC"
2. run: 2. run:
_TBASIC.EXEC(string of whole command) _TBASIC.EXEC(string of whole command)
]] ]]
if os and os.loadAPI then -- ComputerCraft if os and os.loadAPI then -- ComputerCraft
os.loadAPI "TBASINCL.lua" os.loadAPI "TBASINCL.lua"
else else
require "TBASINCL" require "TBASINCL"
end end
table.concat = function(t, delimeter) table.concat = function(t, delimeter)
if #t == 0 then return "" end if #t == 0 then return "" end
local outstr = t[1] local outstr = t[1]
for i = 2, #t do for i = 2, #t do
outstr = outstr..delimeter..tostring(t[i]) outstr = outstr..delimeter..tostring(t[i])
end 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 end
@@ -43,457 +53,423 @@ local programlist = {}
-- LEXER ---------------------------------------------------------------------- -- LEXER ----------------------------------------------------------------------
local function appendcommand(lineno, statement) local function appendcommand(lineno, statement)
if lineno > _TBASIC._INTPRTR.MAXLINES then if lineno > _TBASIC._INTPRTR.MAXLINES then
_TBASIC._ERROR.LINETOOBIG() _TBASIC._ERROR.LINETOOBIG()
elseif lineno < 0 then elseif lineno < 0 then
_TBASIC._ERROR.NOLINENUM() _TBASIC._ERROR.NOLINENUM()
else else
programlist[lineno] = statement programlist[lineno] = statement
end end
end end
do -- Avoid heap allocs for performance do -- Avoid heap allocs for performance
local tokens = {" ", "\t"} -- initial obvious tokens local tokens = {" ", "\t", ",", "(", ")"} -- initial obvious tokens
local longest_token_len = 0 local longest_token_len = 0
-- build 'tokens' table from list of operators from the language -- build 'tokens' table from list of operators from the language
for _, v in ipairs(_TBASIC._OPERATR) do for _, v in ipairs(_TBASIC._OPERATR) do
if not v:match("[A-Za-z]") then -- we want non-alphabetic operators as a token if not v:match("[A-Za-z]") then -- we want non-alphabetic operators as a token
table.insert(tokens, v) table.insert(tokens, v)
-- get longest_token_len, will be used for 'lookahead' -- get longest_token_len, will be used for 'lookahead'
local tokenlen = #v local tokenlen = #v
if longest_token_len < #v then if longest_token_len < #v then
longest_token_len = #v longest_token_len = #v
end end
end end
end end
-- sort them out using ther hash for binary search -- sort them out using ther hash for binary search
table.sort(tokens, function(a, b) return string.hash(a) < string.hash(b) end) table.sort(tokens, function(a, b) return string_hash(a) < string_hash(b) end)
function parsewords(line) function parsewords(line)
if line == nil then return end if line == nil then return end
----------------------- -----------------------
-- check line sanity -- -- check line sanity --
----------------------- -----------------------
-- filter for IF statement -- filter for IF statement
if line:match("[Ii][Ff]") then if line:sub(1, 2):upper() == "IF" then
-- no matching THEN -- no matching THEN
if not line:match("[Tt][Hh][Ee][Nn]") then if not line:match("[Tt][Hh][Ee][Nn]") then
_TBASIC._ERROR.NOMATCHING("IF", "THEN") _TBASIC._ERROR.NOMATCHING("IF", "THEN")
-- assignment on IF clause -- assignment on IF clause
elseif line:match("[Ii][Ff][^\n]+[Tt][Hh][Ee][Nn]"):match("[^=]=[^=]") or elseif line:match("[Ii][Ff][^\n]+[Tt][Hh][Ee][Nn]"):match("[^=+%-*/%%<>!]=[^=<>]") or
line:match("[Ii][Ff][^\n]+[Tt][Hh][Ee][Nn]"):match(":=") then line:match("[Ii][Ff][^\n]+[Tt][Hh][Ee][Nn]"):match(":=") then
_TBASIC._ERROR.ASGONIF() _TBASIC._ERROR.ASGONIF()
end end
end end
-------------------------------------------------- --------------------------------------------------
-- automatically infer and insert some commands -- -- automatically infer and insert some commands --
-------------------------------------------------- --------------------------------------------------
-- (This is starting to get dirty...) -- (This is starting to get dirty...)
-- unary minus -- unary minus
local matchobj = line:find("%-[0-9]") for matchobj in line:gmatch("%-[0-9]+") do
if matchobj then -- in this pattern, it always returns a number local newline = line:gsub(matchobj, "MINUS "..matchobj:sub(2, #matchobj))
local newline = line:sub(1, matchobj - 1) .. "MINUS " .. line:sub(matchobj + 1, #line) line = newline
line = newline end
end -- conditional for IF
-- conditional for IF -- if IF statement has no appended paren
-- if IF statement has no appended paren if line:sub(1, 2):upper() == "IF" and not line:match("[Ii][Ff][ ]*%(") then
if not line:match("[Ii][Ff][ ]*%(") then local newline = line:gsub("[Ii][Ff]", "IF ( ", 1):gsub("[Tt][Hh][Ee][Nn]", " ) THEN", 1)
local newline = line:gsub("[Ii][Ff]", "IF ( "):gsub("[Tt][Hh][Ee][Nn]", " ) THEN") line = newline
line = newline end
end -- special treatment for FOR
-- special treatment for FOR if line:sub(1, 3):upper() == "FOR" then
if line:sub(1, 3):upper() == "FOR" then if line:match("[0-9]?%.[0-9]") then -- real number used (e.g. "3.14", ".5")
if line:match("[0-9]?%.[0-9]") then -- real number used (e.g. "3.14", ".5") _TBASIC._ERROR.ILLEGALARG()
_TBASIC._ERROR.ILLEGALARG() else
else local varnameintm = line:match(" [^\n]+[ =]")
local varnameintm = line:match(" [^\n]+[ =]")
if varnameintm then if varnameintm then
local varname = varnameintm:match("[^= ]+") local varname = varnameintm:match("[^= ]+")
if varname then if varname then
local newline = line:gsub(" "..varname.."[ =]", " $"..varname.." "..varname.." = ") local newline = line:gsub(" "..varname.."[ =]", " $"..varname.." "..varname.." = ")
line = newline:gsub("= =", "=") line = newline:gsub("= =", "=")
else else
_TBASIC._ERROR.SYNTAX() _TBASIC._ERROR.SYNTAX()
end end
end end
-- basically, "FOR x x = 1 TO 10", which converts to "x x 1 10 TO = FOR", -- basically, "FOR x x = 1 TO 10", which converts to "x x 1 10 TO = FOR",
-- which is executed (in RPN) in steps of: -- which is executed (in RPN) in steps of:
-- "x x 1 10 TO = FOR" -- "x x 1 10 TO = FOR"
-- "x x (arr) = FOR" -- "x x (arr) = FOR"
-- "x FOR" -- see this part? we need extra 'x' to feed for the FOR statement to function -- "x FOR" -- see this part? we need extra 'x' to feed for the FOR statement to function
end end
end end
printdbg("parsing line", line) printdbg("parsing line", line)
lextable = {} lextable = {}
isquote = false isquote = false
quotemode = false quotemode = false
wordbuffer = "" wordbuffer = ""
local function flush() local function flush()
if (#wordbuffer > 0) then if (#wordbuffer > 0) then
table.insert(lextable, wordbuffer) table.insert(lextable, wordbuffer)
wordbuffer = "" wordbuffer = ""
end end
end end
local function append(char) local function append(char)
wordbuffer = wordbuffer..char wordbuffer = wordbuffer..char
end end
local function append_no_whitespace(char) local function append_no_whitespace(char)
if char ~= " " and char ~= "\t" then if char ~= " " and char ~= "\t" then
wordbuffer = wordbuffer..char wordbuffer = wordbuffer..char
end end
end end
-- return: lookless_count on success, nil on failure -- return: lookless_count on success, nil on failure
local function isdelimeter(string) local function isdelimeter(string)
local cmpval = function(table_elem) return string.hash(table_elem) end local cmpval = function(table_elem) return string_hash(table_elem) end
local lookless_count = #string local lookless_count = #string
local ret = nil local ret = nil
repeat repeat
ret = table.binsearch(tokens, string:sub(1, lookless_count), cmpval) ret = table.binsearch(tokens, string:sub(1, lookless_count), cmpval)
lookless_count = lookless_count - 1 lookless_count = lookless_count - 1
until ret or lookless_count < 1 until ret or lookless_count < 1
return ret and lookless_count + 1 or false return ret and lookless_count + 1 or false
end end
local i = 1 -- Lua Protip: variable in 'for' is immutable, and is different from general variable table, even if they have same name 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 while i <= #line do
local c = string.char(line:byte(i)) 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 isquote then
if c == [["]] then if c == [["]] then
flush() flush()
isquote = false isquote = false
else else
append(c) append(c)
end end
else else
if c == [["]] then if c == [["]] then
isquote = true isquote = true
append_no_whitespace("~") append_no_whitespace("~")
else else
local delimsize = isdelimeter(lookahead) -- returns nil if no matching delimeter found local delimsize = isdelimeter(lookahead) -- returns nil if no matching delimeter found
if delimsize then if delimsize then
flush() -- flush buffer flush() -- flush buffer
append_no_whitespace(lookahead:sub(1, delimsize)) append_no_whitespace(lookahead:sub(1, delimsize))
flush() -- flush this delimeter flush() -- flush this delimeter
i = i + delimsize - 1 i = i + delimsize - 1
else else
append_no_whitespace(c) append_no_whitespace(c)
end end
end end
end end
i = i + 1 i = i + 1
end end
flush() -- don't forget this! flush() -- don't forget this!
return lextable return lextable
end end
end end
local function readprogram(program) local function readprogram(program)
for line in program:gmatch("[^\n]+") do for line in program:gmatch("[^\n]+") do
lineno = line:match("[0-9]+ ", 1) lineno = line:match("[0-9]+ ", 1)
if not lineno then if not lineno then
_TBASIC._ERROR.NOLINENUM() _TBASIC._ERROR.NOLINENUM()
end end
statement = line:sub(#lineno + 1) statement = line:sub(#lineno + 1)
appendcommand(tonumber(lineno), statement) appendcommand(tonumber(lineno), statement)
end end
end end
do -- Avoid heap allocs for performance do -- Avoid heap allocs for performance
local function stackpush(t, v) local function stackpush(t, v)
t[#t + 1] = v t[#t + 1] = v
end end
local function stackpop(t) local function stackpop(t)
local v = t[#t] local v = t[#t]
t[#t] = nil t[#t] = nil
return v return v
end end
local function stackpeek(t) local function stackpeek(t)
local v = t[#t] local v = t[#t]
return v return v
end end
local function unmark(word) local function unmark(word)
if type(word) == "table" then return word end if type(word) == "table" then return word end
return word:sub(2, #word) return word:sub(2, #word)
end end
local function isoperator(word) local function isoperator(word)
if word == nil then return false end if word == nil then return false end
return word:byte(1) == 35 return word:byte(1) == 35
end end
local isvariable = _TBASIC.isvariable local isvariable = _TBASIC.isvariable
local isnumber = _TBASIC.isnumber
local isstring = _TBASIC.isstring
local function isuserfunc(word) local function isuserfunc(word)
if type(word) == "table" then return false end if type(word) == "table" then return false end
if word == nil then return false end if word == nil then return false end
return word:byte(1) == 64 return word:byte(1) == 64
end end
local function isbuiltin(word) local function isbuiltin(word)
if type(word) == "table" then return false end if type(word) == "table" then return false end
if word == nil then return false end if word == nil then return false end
return word:byte(1) == 38 return word:byte(1) == 38
end end
local function iskeyword(word) local function iskeyword(word)
if word == nil then return false end if word == nil then return false end
return isoperator(word) or isuserfunc(word) or isbuiltin(word) return isoperator(word) or isuserfunc(word) or isbuiltin(word)
end end
local function isassign(word) local function isassign(word)
if word == nil then return false end if word == nil then return false end
return word ~= "==" and word ~= ">=" and word ~= "<=" and word:byte(#word) == 61 return word ~= "==" and word ~= ">=" and word ~= "<=" and word:byte(#word) == 61
end end
local function isnoresolvevar(word) -- returns truthy value "terminate_loop" upon termination of loop; nil otherwise.
local novarresolve = {"NEXT"} 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 == "IF" then
if word:upper() == w then printdbg("--> branch statement 'IF'")
return true if not _TBASIC.__readvar(args[1]) then -- if condition 'false'
end printdbg("--> if condition 'false'", table.unpack(args))
end return "terminate_loop" -- evaluated as 'true' to Lua
else
printdbg("--> if condition 'true'", table.unpack(args))
end
end
return false printdbg("--> execword_outarg", table.unpack(args))
end result = _TBASIC.LUAFN[word][1](table.unpack(args))
local function execword(word, args) printdbg("--> result", result)
if not _TBASIC.__appexit then stackpush(execstack, result)
printdbg("--> execword", word) end
printdbg("--> inargs", table.unpack(args)) end
-- selectively resolve variable (if it's assign func, bottommost var -- target of assignation -- will not be resolved) function printdbg(...)
-- for command "NEXT": DO NOT RESOLVE, pass its name (Call by Name) local debug = false
if not isnoresolvevar(word) then if debug then print("DBG", ...) end
for i = isassign(word) and 2 or 1, #args do end
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 interpretline(line) function interpretline(line)
if not _TBASIC.__appexit then if not _TBASIC.__appexit then
--[[ --[[
impl impl
1. (normalise expr using parsewords) 1. (normalise expr using parsewords)
2. use _TBASIC.RPNPARSR to convert to RPN 2. use _TBASIC.RPNPARSR to convert to RPN
3. execute RPN op set like FORTH 3. execute RPN op set like FORTH
* "&" - internal functions * "&" - internal functions
* "@" - user-defined functions * "@" - user-defined functions
* "$" - variables (builtin constants and user-defined) -- familiar, eh? * "$" - variables (builtin constants and user-defined) -- familiar, eh?
* "#" - operators * "#" - operators
* "~" - strings * "~" - strings
* none prepended - data (number or string) * none prepended - data (number or string)
]] ]]
lextable = parsewords(line) lextable = parsewords(line)
local vararg = -13 -- magic local vararg = -13 -- magic
if lextable and lextable[1] ~= nil then if lextable and lextable[1] ~= nil then
if lextable[1]:upper() == "REM" then return nil end if lextable[1]:upper() == "REM" then return nil end
printdbg("lextable", table.concat(lextable, "|")) printdbg("lextable", table.concat(lextable, "|"))
-- execute expression -- execute expression
exprlist = _TBASIC.TORPN(lextable) -- 2 2 #+ &PRINT for "PRINT 2+2" 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 for _, word in ipairs(exprlist) do
printdbg("stack before", table.concat(execstack, " ")) printdbg("stack before", table.concat(execstack, " "))
printdbg("word", word) printdbg("word", word)
if iskeyword(word) then if iskeyword(word) then
printdbg("is keyword") printdbg("is keyword")
funcname = unmark(word) funcname = unmark(word)
args = {} args = {}
argsize = _TBASIC._GETARGS(funcname) argsize = _TBASIC._GETARGS(funcname)
printdbg("argsize", argsize) printdbg("argsize", argsize)
if not argsize then if not argsize then
_TBASIC._ERROR.DEV_UNIMPL(funcname) _TBASIC._ERROR.DEV_UNIMPL(funcname)
else else
if argsize ~= vararg then if argsize ~= vararg then
-- consume 'argsize' elements from the stack -- consume 'argsize' elements from the stack
for argcnt = argsize, 1, -1 do for argcnt = argsize, 1, -1 do
if #execstack == 0 then if #execstack == 0 then
_TBASIC._ERROR.ARGMISSING(funcname) _TBASIC._ERROR.ARGMISSING(funcname)
end end
args[argcnt] = stackpop(execstack) args[argcnt] = stackpop(execstack)
end end
else else
-- consume entire stack -- consume entire stack
local reversedargs = {} 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
printdbg("stack after", table.concat(execstack, " ")) while #execstack > 0 and
printdbg("--------") (isvariable(stackpeek(execstack)) or isnumber(stackpeek(execstack)) or
end 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 local terminate_loop = execword(funcname, args)
if #execstack > 0 then
_TBASIC._ERROR.SYNTAX() -- cannot reliably pinpoint which statement has error; use generic error if terminate_loop then
end printdbg("--> termination of loop")
end printdbg("--------")
end break
end 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 end
local function termination_condition() local function termination_condition()
return terminated or return terminated or
_TBASIC._INTPRTR.GOTOCNTR > _TBASIC._INTPRTR.GOTOLMIT or _TBASIC.__appexit or
_TBASIC.__appexit or #_TBASIC._INTPRTR.CALLSTCK > _TBASIC._INTPRTR.STACKMAX
#_TBASIC._INTPRTR.CALLSTCK > _TBASIC._INTPRTR.STACKMAX
end end
local function fetchnextcmd() local function fetchnextcmd()
cmd = nil cmd = nil
repeat repeat
_TBASIC._INTPRTR.PROGCNTR = _TBASIC._INTPRTR.PROGCNTR + 1 _TBASIC._INTPRTR.PROGCNTR = _TBASIC._INTPRTR.PROGCNTR + 1
cmd = programlist[_TBASIC._INTPRTR.PROGCNTR] cmd = programlist[_TBASIC._INTPRTR.PROGCNTR]
if _TBASIC._INTPRTR.PROGCNTR > _TBASIC._INTPRTR.MAXLINES then if _TBASIC._INTPRTR.PROGCNTR > _TBASIC._INTPRTR.MAXLINES then
terminated = true terminated = true
break break
end end
until cmd ~= nil until cmd ~= nil
if cmd ~= nil then if cmd ~= nil then
if _TBASIC._INTPRTR.TRACE then if _TBASIC._INTPRTR.TRACE then
print("PC", _TBASIC._INTPRTR.PROGCNTR) print("PC", _TBASIC._INTPRTR.PROGCNTR)
end end
return cmd return cmd
end end
end end
local function interpretall() local function interpretall()
terminated = false terminated = false
repeat repeat
interpretline(fetchnextcmd()) interpretline(fetchnextcmd())
until termination_condition() until termination_condition()
if _TBASIC._INTPRTR.GOTOCNTR > _TBASIC._INTPRTR.GOTOLMIT then
_TBASIC._ERROR.TOOLONGEXEC()
end
end end
-- END OF LEXER --------------------------------------------------------------- -- 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 _G._TBASIC.EXEC = function(cmdstring) -- you can access this interpreter with this global function
_TBASIC._INTPRTR.RESET() _TBASIC._INTPRTR.RESET()
programlist = {} programlist = {} -- wipe out previous commands from interpreter (do not delete)
readprogram(cmdstring) readprogram(cmdstring)
interpretall() interpretall()
end end
if testprogram then if testprogram then
_TBASIC._INTPRTR.RESET() _TBASIC._INTPRTR.RESET()
programlist = {} programlist = {} -- wipe out previous commands from interpreter (do not delete)
readprogram(testprogram) readprogram(testprogram)
interpretall() interpretall()
end end

View File

@@ -8,71 +8,260 @@ If no file is specified, interactive mode will be started
if os and os.loadAPI then -- ComputerCraft if os and os.loadAPI then -- ComputerCraft
os.loadAPI "TBASINCL.lua" os.loadAPI "TBASINCL.lua"
os.loadAPI "TBASEXEC.lua" os.loadAPI "TBASEXEC.lua"
else else
require "TBASINCL" require "TBASINCL"
require "TBASEXEC" require "TBASEXEC"
end end
args = {...} args = {...}
print(_G._TBASIC._VERSION) print(_G._TBASIC._HEADER)
_TBASIC.PROMPT() _TBASIC.PROMPT()
_TBASIC.SHOWLUAERROR = false _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 if args[1] then
local prog = nil local prog = nil
if fs and fs.open then -- ComputerCraft if fs and fs.open then -- ComputerCraft
local inp = assert(fs.open(args[1], "r")) local inp = assert(fs.open(args[1], "r"))
prog = inp:readAll() prog = inp:readAll()
inp:close() inp:close()
else else
local inp = assert(io.open(args[1], "r")) local inp = assert(io.open(args[1], "r"))
prog = inp:read("*all") prog = inp:read("*all")
inp:close() inp:close()
end end
_TBASIC.EXEC(prog) _TBASIC.EXEC(prog)
else 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 = {} -- TODO more elegant code than IF-ELSEIF-ELSE
local lineno = 1
while not terminate_app do
local __read = false
line = io.read()
if line:upper() == "NEW" then -- massive if-else for running command, cos implementing proper command executor is too expensive here
lines = {} if line:sub(1,6):match(linenum_match) then -- enter new command
lineno = 1 local linenum, statements = split_num_and_statements(line)
elseif line:upper() == "RUN" then lines[tonumber(linenum)] = statements
_TBASIC.EXEC(table.concat(lines, "\n")) __read = true
elseif line:upper() == "LIST" then elseif args[1] == "NEW" then
print() lines = {}
print(table.concat(lines, "\n")) elseif args[1] == "RUN" then
_TBASIC.PROMPT() _TBASIC.EXEC(concat_lines(lines))
__read = true elseif args[1] == "LIST" then -- LIST, LIST 42, LIST 10-80
elseif line:upper() == "EXIT" then if not args[2] then
terminate_app = true print(concat_lines(lines))
break else
elseif line:match("[0-9]+ ") then if args[2]:match("-") then -- ranged
table.insert(lines, line) range = {}
lineno = lineno + 1 for n in args[2]:gmatch("[^-]+") do
__read = true table.insert(range, n)
elseif #line == 0 and line:byte(1) ~= 10 and line:byte(1) ~= 13 then end
__read = true local rangestart = tonumber(range[1])
else local rangeend = tonumber(range[2])
_TBASIC.EXEC("1 "..line)
end
-- reset if not rangestart or not rangeend then
if not __read then _TBASIC._ERROR.ILLEGALARG()
_TBASIC.PROMPT() else
end print(concat_lines(lines, rangestart, rangeend))
end 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 end

View File

@@ -4,6 +4,7 @@ import net.torvald.aa.AAFrame
import net.torvald.aa.ColouredFastFont import net.torvald.aa.ColouredFastFont
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.gameactors.abs import net.torvald.terrarum.gameactors.abs
import net.torvald.terrarum.gamecontroller.Key
import net.torvald.terrarum.virtualcomputer.computer.BaseTerrarumComputer import net.torvald.terrarum.virtualcomputer.computer.BaseTerrarumComputer
import org.lwjgl.BufferUtils import org.lwjgl.BufferUtils
import org.lwjgl.openal.AL import org.lwjgl.openal.AL
@@ -106,8 +107,6 @@ open class SimpleTextTerminal(
private set private set
private var redrawSemaphore = false
override fun getColor(index: Int): Color = colours[index] override fun getColor(index: Int): Color = colours[index]
@@ -142,45 +141,40 @@ open class SimpleTextTerminal(
* pass UIcanvas to the parameter "g" * pass UIcanvas to the parameter "g"
*/ */
override fun render(gc: GameContainer, g: Graphics) { override fun render(gc: GameContainer, g: Graphics) {
// FIXME don't redraw every time it's slow
g.font = font g.font = font
// don't redraw() every fucking time, you're wasting your precious process cycle blendNormal()
if (redrawSemaphore) {
blendNormal() // black background (this is mandatory)
g.color = Color.black
// black background (this is mandatory) g.fillRect(0f, 0f, displayW.toFloat(), displayH.toFloat())
g.color = Color.black
g.fillRect(0f, 0f, displayW.toFloat(), displayH.toFloat())
// screen buffer // screen buffer
for (y in 0..height - 1) { for (y in 0..height - 1) {
for (x in 0..width - 1) { for (x in 0..width - 1) {
val ch = screenBuffer.getChar(x, y) val ch = screenBuffer.getChar(x, y)
// background // background
g.color = getColor(screenBuffer.getBackgroundColour(x, y)) g.color = getColor(screenBuffer.getBackgroundColour(x, y))
g.fillRect(fontW * x.toFloat() + borderSize, fontH * y.toFloat() + borderSize, g.fillRect(fontW * x.toFloat() + borderSize, fontH * y.toFloat() + borderSize,
fontW.toFloat(), fontH.toFloat()) fontW.toFloat(), fontH.toFloat())
// foreground // foreground
if (ch.toInt() != 0 && ch.toInt() != 32) { if (ch.toInt() != 0 && ch.toInt() != 32) {
g.color = getColor(screenBuffer.getForegroundColour(x, y)) g.color = getColor(screenBuffer.getForegroundColour(x, y))
g.drawString( g.drawString(
Character.toString(ch), Character.toString(ch),
fontW * x.toFloat() + borderSize, fontH * y.toFloat() + borderSize) fontW * x.toFloat() + borderSize, fontH * y.toFloat() + borderSize)
}
} }
} }
} }
// cursor // cursor
if (cursorBlinkOn) { if (cursorBlinkOn) {
g.color = getColor(if (cursorBlink) foreDefault else backDefault) screen colourScreen mul phosphor g.color = getColor(if (cursorBlink) foreDefault else backDefault)
g.fillRect( g.fillRect(
fontW * cursorX.toFloat() + borderSize, fontW * cursorX.toFloat() + borderSize,
@@ -189,48 +183,23 @@ open class SimpleTextTerminal(
fontH.toFloat() fontH.toFloat()
) )
} }
else {
val x = cursorX
val y = cursorY
val ch = screenBuffer.getChar(x, y)
// background // colour base
g.color = getColor(screenBuffer.getBackgroundColour(x, y)) screen colourScreen mul phosphor g.color = colourScreen
g.fillRect(fontW * x.toFloat() + borderSize, fontH * y.toFloat() + borderSize, blendScreen()
fontW.toFloat(), fontH.toFloat()) g.fillRect(0f, 0f, fontW * width.toFloat() + 2 * borderSize, fontH * height.toFloat() + 2 * borderSize)
// foreground // colour overlay
if (ch.toInt() != 0 && ch.toInt() != 32) { g.color = phosphor
g.color = getColor(screenBuffer.getForegroundColour(x, y)) screen colourScreen mul phosphor blendMul()
g.drawString( g.fillRect(0f, 0f, fontW * width.toFloat() + 2 * borderSize, fontH * height.toFloat() + 2 * borderSize)
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)
redrawSemaphore = false
}
blendNormal() blendNormal()
} }
fun redraw() {
redrawSemaphore = true
}
/** Unlike lua function, this one in Zero-based. */ /** Unlike lua function, this one in Zero-based. */
override fun setCursor(x: Int, y: Int) { override fun setCursor(x: Int, y: Int) {
cursorX = x cursorX = x
@@ -444,7 +413,7 @@ open class SimpleTextTerminal(
else if (keyPressVisible) else if (keyPressVisible)
printChar(c) printChar(c)
if (!asciiControlInUse.contains(c)) sb.append(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)
} }
} }