TG: More work done on Telecomms:

▫ Signals can now be rejected by Subspace broadcasters through a specific data[]
parameter.
▫ Improved the log browser.
▫ Log browsers and telecommunication monitors no longer require access to use.
You do need access to delete logs, however.
▫ Intercoms need power to work. They don't drain power, they just need a
constant flow of equipment power. As such, that offline intercom sprite's now
finally being put to use.

Scripting language:

▫ Sorry about all the files; they're all necessary! It's important to notice
that the basic structure of the scripting language code is not mine; I
cannibalized the base structure from some obscure BYOND project. It's pretty
well documented, and I'd say easier to browse through than atmos. Here's the
basic deal:

A compiler datum manages the relationships between the three main subsystems of
a scripting language: the Scanner, the Parser, and the Interpreter. The Scanner
splits raw text into token datums that the Parser can read. The Parser
transforms the otherwise random bits and strings into ordered AST Trees and
nodes for the Interpreter to read. The interpreter actually executes the code
and handles scope/functions/code blocks.
Revision: r3193
Author: 	 vageyenaman
This commit is contained in:
Ren Erthilo
2012-04-24 21:13:55 +01:00
parent 757b76c456
commit f8dd926ca5
14 changed files with 350 additions and 99 deletions

View File

@@ -14,7 +14,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
last_transmission last_transmission
frequency = 1459 //common chat frequency = 1459 //common chat
traitor_frequency = 0 //tune to frequency to unlock traitor supplies traitor_frequency = 0 //tune to frequency to unlock traitor supplies
canhear_range = 3 canhear_range = 3 // the range which mobs can hear this radio from
obj/item/device/radio/patch_link = null obj/item/device/radio/patch_link = null
obj/item/device/uplink/radio/traitorradio = null obj/item/device/uplink/radio/traitorradio = null
wires = WIRE_SIGNAL | WIRE_RECEIVE | WIRE_TRANSMIT wires = WIRE_SIGNAL | WIRE_RECEIVE | WIRE_TRANSMIT
@@ -63,6 +63,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
checkpower() checkpower()
/obj/item/device/radio/initialize() /obj/item/device/radio/initialize()
if(freerange) if(freerange)
if(frequency < 1200 || frequency > 1600) if(frequency < 1200 || frequency > 1600)
frequency = sanitize_frequency(frequency) frequency = sanitize_frequency(frequency)
@@ -145,6 +146,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
//..() //..()
if (usr.stat || !on) if (usr.stat || !on)
return return
if (!(issilicon(usr) || (usr.contents.Find(src) || ( in_range(src, usr) && istype(loc, /turf) )))) if (!(issilicon(usr) || (usr.contents.Find(src) || ( in_range(src, usr) && istype(loc, /turf) ))))
usr << browse(null, "window=radio") usr << browse(null, "window=radio")
return return
@@ -155,6 +157,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
if(A && target) if(A && target)
A.ai_actual_track(target) A.ai_actual_track(target)
return return
else if (href_list["faketrack"]) else if (href_list["faketrack"])
var/mob/target = locate(href_list["track"]) var/mob/target = locate(href_list["track"])
var/mob/living/silicon/ai/A = locate(href_list["track2"]) var/mob/living/silicon/ai/A = locate(href_list["track2"])
@@ -169,7 +172,9 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
usr << "Target is not on or near any active cameras on the station. We'll check again in 5 seconds (unless you use the cancel-camera verb)." usr << "Target is not on or near any active cameras on the station. We'll check again in 5 seconds (unless you use the cancel-camera verb)."
sleep(40) sleep(40)
continue continue
return return
else if (href_list["freq"]) else if (href_list["freq"])
var/new_frequency = (frequency + text2num(href_list["freq"])) var/new_frequency = (frequency + text2num(href_list["freq"]))
if (!freerange || (frequency < 1200 || frequency > 1600)) if (!freerange || (frequency < 1200 || frequency > 1600))
@@ -248,6 +253,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
return return
/obj/item/device/radio/talk_into(mob/M as mob, message, channel) /obj/item/device/radio/talk_into(mob/M as mob, message, channel)
if(!on) return // the device has to be on if(!on) return // the device has to be on
if(GLOBAL_RADIO_TYPE == 1) // NEW RADIO SYSTEMS: By Doohl if(GLOBAL_RADIO_TYPE == 1) // NEW RADIO SYSTEMS: By Doohl
@@ -424,7 +430,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
sleep(rand(10,25)) // wait a little... sleep(rand(10,25)) // wait a little...
if(signal.data["done"]) if(signal.data["done"])
//we're done here. // we're done here.
return return
// Oh my god; the comms are down or something because the signal hasn't been broadcasted yet. // Oh my god; the comms are down or something because the signal hasn't been broadcasted yet.
@@ -615,6 +621,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
R.show_message(rendered, 2) R.show_message(rendered, 2)
/obj/item/device/radio/hear_talk(mob/M as mob, msg) /obj/item/device/radio/hear_talk(mob/M as mob, msg)
if (broadcasting) if (broadcasting)
talk_into(M, msg) talk_into(M, msg)
/* /*

View File

@@ -250,6 +250,7 @@
for(var/obj/effect/speech_bubble/S in range(1, mob)) for(var/obj/effect/speech_bubble/S in range(1, mob))
if(S.parent == mob) if(S.parent == mob)
S.loc = mob.loc S.loc = mob.loc
moving = 0 moving = 0
return . return .
@@ -364,6 +365,8 @@
if((istype(turf,/turf/simulated/floor)) && (src.lastarea.has_gravity == 0)) // No one else gets a chance. if((istype(turf,/turf/simulated/floor)) && (src.lastarea.has_gravity == 0)) // No one else gets a chance.
continue continue
/* /*
if(istype(turf,/turf/simulated/floor) && (src.flags & NOGRAV)) if(istype(turf,/turf/simulated/floor) && (src.flags & NOGRAV))
continue continue

View File

@@ -52,6 +52,10 @@
message+="[id]'. " message+="[id]'. "
if(T)message+="Found '[T.value]'." if(T)message+="Found '[T.value]'."
UnterminatedComment
message="Unterminated multi-line comment statement: expected */"
DuplicateFunction DuplicateFunction
New(name, token/t) New(name, token/t)
message="Function '[name]' defined twice." message="Function '[name]' defined twice."
@@ -121,4 +125,8 @@
DivisionByZero DivisionByZero
name="DivideByZeroError" name="DivideByZeroError"
message="Division by zero attempted." message="Division by zero attempted."
MaxCPU
name="MaxComputationalUse"
message="Maximum amount of computational cycles reached (>= 1000)."

View File

@@ -1,7 +1,7 @@
client/verb/tcssave() client/verb/tcssave()
set hidden = 1 set hidden = 1
if(mob.machine) if(mob.machine || issilicon(mob))
if(istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || issilicon(mob))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob) if(Machine.editingcode != mob)
return return
@@ -23,8 +23,8 @@ client/verb/tcssave()
client/verb/tcscompile() client/verb/tcscompile()
set hidden = 1 set hidden = 1
if(mob.machine) if(mob.machine || issilicon(mob))
if(istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob) if(Machine.editingcode != mob)
return return
@@ -74,8 +74,8 @@ client/verb/tcscompile()
client/verb/tcsrun() client/verb/tcsrun()
set hidden = 1 set hidden = 1
if(mob.machine) if(mob.machine || issilicon(mob))
if(istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob) if(Machine.editingcode != mob)
return return
@@ -140,8 +140,8 @@ client/verb/tcsrun()
client/verb/exittcs() client/verb/exittcs()
set hidden = 1 set hidden = 1
if(mob.machine) if(mob.machine || issilicon(mob))
if(istype(mob.machine, /obj/machinery/computer/telecomms/traffic)) if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode == mob) if(Machine.editingcode == mob)
Machine.storedcode = "[winget(mob, "tcscode", "text")]" Machine.storedcode = "[winget(mob, "tcscode", "text")]"
@@ -152,8 +152,8 @@ client/verb/exittcs()
client/verb/tcsrevert() client/verb/tcsrevert()
set hidden = 1 set hidden = 1
if(mob.machine) if(mob.machine || issilicon(mob))
if(istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob) if(Machine.editingcode != mob)
return return

View File

@@ -47,6 +47,8 @@
if(!interpreter) if(!interpreter)
return return
interpreter.container = src
interpreter.SetVar("PI" , 3.141592653) // value of pi interpreter.SetVar("PI" , 3.141592653) // value of pi
interpreter.SetVar("E" , 2.718281828) // value of e interpreter.SetVar("E" , 2.718281828) // value of e
interpreter.SetVar("SQURT2" , 1.414213562) // value of the square root of 2 interpreter.SetVar("SQURT2" , 1.414213562) // value of the square root of 2
@@ -155,6 +157,25 @@
interpreter.SetProc("prob", /proc/prob_chance) interpreter.SetProc("prob", /proc/prob_chance)
interpreter.SetProc("substr", /proc/docopytext) interpreter.SetProc("substr", /proc/docopytext)
// Donkie~
// Strings
interpreter.SetProc("lower", /proc/n_lower)
interpreter.SetProc("upper", /proc/n_upper)
interpreter.SetProc("explode", /proc/string_explode)
interpreter.SetProc("repeat", /proc/n_repeat)
interpreter.SetProc("reverse", /proc/n_reverse)
interpreter.SetProc("tonum", /proc/n_str2num)
// Numbers
interpreter.SetProc("tostring", /proc/n_num2str)
interpreter.SetProc("sqrt", /proc/n_sqrt)
interpreter.SetProc("abs", /proc/n_abs)
interpreter.SetProc("floor", /proc/n_floor)
interpreter.SetProc("ceil", /proc/n_ceil)
interpreter.SetProc("round", /proc/n_round)
interpreter.SetProc("clamp", /proc/n_clamp)
interpreter.SetProc("inrange", /proc/n_inrange)
// End of Donkie~
// Run the compiled code // Run the compiled code
@@ -163,7 +184,7 @@
// Backwards-apply variables onto signal data // Backwards-apply variables onto signal data
/* sanitize EVERYTHING. fucking players can't be trusted with SHIT */ /* sanitize EVERYTHING. fucking players can't be trusted with SHIT */
signal.data["message"] = trim(copytext(sanitize(interpreter.GetVar("$content")), 1, MAX_MESSAGE_LEN)) signal.data["message"] = interpreter.GetVar("$content")
signal.frequency = interpreter.GetVar("$freq") signal.frequency = interpreter.GetVar("$freq")
var/setname = "" var/setname = ""
@@ -171,14 +192,18 @@
if(interpreter.GetVar("$source") in S.stored_names) if(interpreter.GetVar("$source") in S.stored_names)
setname = interpreter.GetVar("$source") setname = interpreter.GetVar("$source")
else else
setname = "<i>[trim(copytext(sanitize(interpreter.GetVar("$source")), 1, MAX_MESSAGE_LEN))]</i>" setname = "<i>[interpreter.GetVar("$source")]</i>"
if(signal.data["name"] != setname) if(signal.data["name"] != setname)
signal.data["realname"] = setname signal.data["realname"] = setname
signal.data["name"] = setname signal.data["name"] = setname
signal.data["job"] = trim(copytext(sanitize(interpreter.GetVar("$job")), 1, MAX_MESSAGE_LEN)) signal.data["job"] = interpreter.GetVar("$job")
signal.data["reject"] = !(interpreter.GetVar("$pass")) // set reject to the opposite of $pass signal.data["reject"] = !(interpreter.GetVar("$pass")) // set reject to the opposite of $pass
// If the message is invalid, just don't broadcast it!
if(signal.data["message"] == "" || !signal.data["message"])
signal.data["reject"] = 1
/* -- Actual language proc code -- */ /* -- Actual language proc code -- */
datum/signal datum/signal
@@ -188,7 +213,7 @@ datum/signal
if(istext(address)) if(istext(address))
var/obj/machinery/telecomms/server/S = data["server"] var/obj/machinery/telecomms/server/S = data["server"]
if(!value) if(!value && value != 0)
return S.memory[address] return S.memory[address]
else else
@@ -202,7 +227,7 @@ datum/signal
var/obj/machinery/telecomms/server/S = data["server"] var/obj/machinery/telecomms/server/S = data["server"]
var/obj/item/device/radio/hradio var/obj/item/device/radio/hradio
if(!message) if((!message || message == "") && message != 0)
message = "*beep*" message = "*beep*"
if(!source) if(!source)
source = "[html_encode(uppertext(S.id))]" source = "[html_encode(uppertext(S.id))]"
@@ -213,7 +238,7 @@ datum/signal
freq *= 10 // shift the decimal one place freq *= 10 // shift the decimal one place
if(!job) if(!job)
job = "None" job = "?"
newsign.data["mob"] = H newsign.data["mob"] = H
newsign.data["mobtype"] = H.type newsign.data["mobtype"] = H.type
@@ -222,9 +247,9 @@ datum/signal
else else
newsign.data["name"] = "<i>[html_encode(uppertext(source))]<i>" newsign.data["name"] = "<i>[html_encode(uppertext(source))]<i>"
newsign.data["realname"] = newsign.data["name"] newsign.data["realname"] = newsign.data["name"]
newsign.data["job"] = html_encode(job) newsign.data["job"] = job
newsign.data["compression"] = 0 newsign.data["compression"] = 0
newsign.data["message"] = html_encode(message) newsign.data["message"] = message
newsign.data["type"] = 2 // artificial broadcast newsign.data["type"] = 2 // artificial broadcast
if(!isnum(freq)) if(!isnum(freq))
freq = text2num(freq) freq = text2num(freq)
@@ -242,4 +267,4 @@ datum/signal
newsign.data["vmessage"] = H.voice_message newsign.data["vmessage"] = H.voice_message
newsign.data["vname"] = H.voice_name newsign.data["vname"] = H.voice_name
newsign.data["vmask"] = 0 newsign.data["vmask"] = 0
S.relay_information(newsign, "/obj/machinery/telecomms/broadcaster") // send this simple message to broadcasters S.relay_information(newsign, "/obj/machinery/telecomms/broadcaster") // send this simple message to broadcasters

View File

@@ -53,6 +53,7 @@
if(isobject(e)) if(isobject(e))
if(istype(e, /list)) if(istype(e, /list))
chosenlist = e chosenlist = e
i = 2
else else
if(chosenlist) if(chosenlist)
chosenlist.Add(e) chosenlist.Add(e)
@@ -66,6 +67,7 @@
if(isobject(e)) if(isobject(e))
if(istype(e, /list)) if(istype(e, /list))
chosenlist = e chosenlist = e
i = 2
else else
if(chosenlist) if(chosenlist)
chosenlist.Remove(e) chosenlist.Remove(e)
@@ -101,23 +103,142 @@
if(haystack && needle) if(haystack && needle)
if(isobject(haystack)) if(isobject(haystack))
if(istype(haystack, /list)) if(istype(haystack, /list))
if(length(haystack) + 1 >= end && start > 0) if(length(haystack) >= end && start > 0)
var/list/listhaystack = haystack var/list/listhaystack = haystack
return listhaystack.Find(needle, start, end) return listhaystack.Find(needle, start, end)
else else
if(istext(haystack)) if(istext(haystack))
if(length(haystack) + 1 >= end && start > 0) if(length(haystack) >= end && start > 0)
return findtext(haystack, needle, start, end) return findtext(haystack, needle, start, end)
// Clone of copytext() // Clone of copytext()
/proc/docopytext(var/string, var/start = 1, var/end = 0) /proc/docopytext(var/string, var/start = 1, var/end = 0)
if(istext(string) && isnum(start) && isnum(end)) if(istext(string) && isnum(start) && isnum(end))
if(length(string) >= end && start > 0) if(start > 0)
return copytext(string, start, end) return copytext(string, start, end)
// Clone of length() // Clone of length()
/proc/smartlength(var/container) /proc/smartlength(var/container)
if(container) if(container)
if(istype(container, /list) || istext(container)) if(istype(container, /list) || istext(container))
return length(container) return length(container)
// BY DONKIE~
// String stuff
/proc/n_lower(var/string)
if(istext(string))
return lowertext(string)
/proc/n_upper(var/string)
if(istext(string))
return uppertext(string)
/*
//Makes a list where all indicies in a string is a seperate index in the list
// JUST A HELPER DON'T ADD TO NTSCRIPT
proc/string_tolist(var/string)
var/list/L = new/list()
var/i
for(i=1, i<=lentext(string), i++)
L.Add(copytext(string, i, i))
return L
proc/string_explode(var/string, var/separator)
if(istext(string))
if(istext(separator) && separator == "")
return string_tolist(string)
var/i
var/lasti = 1
var/list/L = new/list()
for(i=1, i<=lentext(string)+1, i++)
if(copytext(string, i, i+1) == separator) // We found a separator
L.Add(copytext(string, lasti, i))
lasti = i+1
L.Add(copytext(string, lasti, lentext(string)+1)) // Adds the last segment
return L
Just found out there was already a string explode function, did some benchmarking, and that function were a bit faster, sticking to that.
*/
proc/string_explode(var/string, var/separator)
if(istext(string) && istext(separator))
return dd_text2list(string, separator)
proc/n_repeat(var/string, var/amount)
if(istext(string) && isnum(amount))
var/i
var/newstring = ""
for(i=0, i<=amount, i++)
if(i>=1000)
break
newstring = newstring + string
return newstring
proc/n_reverse(var/string)
if(istext(string))
var/newstring = ""
var/i
for(i=lentext(string), i>0, i--)
if(i>=1000)
break
newstring = newstring + copytext(string, i, i+1)
return newstring
// I don't know if it's neccesary to make my own proc, but I think I have to to be able to check for istext.
proc/n_str2num(var/string)
if(istext(string))
return text2num(string)
// Number shit
proc/n_num2str(var/num)
if(isnum(num))
return num2text(num)
// Squareroot
proc/n_sqrt(var/num)
if(isnum(num))
return sqrt(num)
// Magnitude of num
proc/n_abs(var/num)
if(isnum(num))
return abs(num)
// Round down
proc/n_floor(var/num)
if(isnum(num))
return round(num)
// Round up
proc/n_ceil(var/num)
if(isnum(num))
return round(num)+1
// Round to nearest integer
proc/n_round(var/num)
if(isnum(num))
if(num-round(num)<0.5)
return round(num)
return n_ceil(num)
// Clamps N between min and max
proc/n_clamp(var/num, var/min=-1, var/max=1)
if(isnum(num)&&isnum(min)&&isnum(max))
if(num<=min)
return min
if(num>=max)
return max
return num
// Returns 1 if N is inbetween Min and Max
proc/n_inrange(var/num, var/min=-1, var/max=1)
if(isnum(num)&&isnum(min)&&isnum(max))
return ((min <= num) && (num <= max))
// END OF BY DONKIE :(

View File

@@ -28,6 +28,8 @@
*/ */
Run() Run()
cur_recursion = 0 // reset recursion cur_recursion = 0 // reset recursion
cur_statements = 0 // reset CPU tracking
alertadmins = 0
ASSERT(src.program) ASSERT(src.program)
RunBlock(src.program) RunBlock(src.program)

View File

@@ -24,6 +24,8 @@
stack stack
scopes = new() scopes = new()
functions = new() functions = new()
datum/container // associated container for interpeter
/* /*
Var: status Var: status
A variable indicating that the rest of the current block should be skipped. This may be set to any combination of <Status Macros>. A variable indicating that the rest of the current block should be skipped. This may be set to any combination of <Status Macros>.
@@ -31,10 +33,12 @@
status=0 status=0
returnVal returnVal
max_iterations=100 // max iterations without any kind of delay max_statements=1000 // maximum amount of statements that can be called in one execution. this is to prevent massive crashes and exploitation
cur_iterations=0 // current iteration cur_statements=0 // current amount of statements called
max_recursion=50 // max recursions without returning anything (or completing the code block) alertadmins=0 // set to 1 if the admins shouldn't be notified of anymore issues
cur_recursion=0 // current amount of recursion max_iterations=100 // max number of uninterrupted loops possible
max_recursion=50 // max recursions without returning anything (or completing the code block)
cur_recursion=0 // current amount of recursion
/* /*
Var: persist Var: persist
If 0, global variables will be reset after Run() finishes. If 0, global variables will be reset after Run() finishes.
@@ -88,56 +92,74 @@
CreateGlobalScope() CreateGlobalScope()
curScope = globalScope curScope = globalScope
for(var/node/statement/S in Block.statements) if(cur_statements < max_statements)
while(paused) sleep(10)
if(istype(S, /node/statement/VariableAssignment)) for(var/node/statement/S in Block.statements)
var/node/statement/VariableAssignment/stmt = S while(paused) sleep(10)
var/name = stmt.var_name.id_name
if(!stmt.object) cur_statements++
// Below we assign the variable first to null if it doesn't already exist. if(cur_statements >= max_statements)
// This is necessary for assignments like +=, and when the variable is used in a function RaiseError(new/runtimeError/MaxCPU())
// If the variable already exists in a different block, then AssignVariable will automatically use that one.
if(!IsVariableAccessible(name)) if(container && !alertadmins)
AssignVariable(name, null) if(istype(container, /datum/TCS_Compiler))
AssignVariable(name, Eval(stmt.value)) var/datum/TCS_Compiler/Compiler = container
var/obj/machinery/telecomms/server/Holder = Compiler.Holder
var/message = "Potential crash-inducing NTSL script detected at telecommunications server [Compiler.Holder] ([Holder.x], [Holder.y], [Holder.z])."
alertadmins = 1
message_admins(message, 1)
break
if(istype(S, /node/statement/VariableAssignment))
var/node/statement/VariableAssignment/stmt = S
var/name = stmt.var_name.id_name
if(!stmt.object)
// Below we assign the variable first to null if it doesn't already exist.
// This is necessary for assignments like +=, and when the variable is used in a function
// If the variable already exists in a different block, then AssignVariable will automatically use that one.
if(!IsVariableAccessible(name))
AssignVariable(name, null)
AssignVariable(name, Eval(stmt.value))
else
var/datum/D = Eval(GetVariable(stmt.object.id_name))
if(!D) return
D.vars[stmt.var_name.id_name] = Eval(stmt.value)
else if(istype(S, /node/statement/VariableDeclaration))
//VariableDeclaration nodes are used to forcibly declare a local variable so that one in a higher scope isn't used by default.
var/node/statement/VariableDeclaration/dec=S
if(!dec.object)
AssignVariable(dec.var_name.id_name, null, curScope)
else
var/datum/D = Eval(GetVariable(dec.object.id_name))
if(!D) return
D.vars[dec.var_name.id_name] = null
else if(istype(S, /node/statement/FunctionCall))
RunFunction(S)
else if(istype(S, /node/statement/FunctionDefinition))
//do nothing
else if(istype(S, /node/statement/WhileLoop))
RunWhile(S)
else if(istype(S, /node/statement/IfStatement))
RunIf(S)
else if(istype(S, /node/statement/ReturnStatement))
if(!curFunction)
RaiseError(new/runtimeError/UnexpectedReturn())
continue
status |= RETURNING
returnVal=Eval(S:value)
break
else if(istype(S, /node/statement/BreakStatement))
status |= BREAKING
break
else if(istype(S, /node/statement/ContinueStatement))
status |= CONTINUING
break
else else
var/datum/D = Eval(GetVariable(stmt.object.id_name)) RaiseError(new/runtimeError/UnknownInstruction())
if(!D) return if(status)
D.vars[stmt.var_name.id_name] = Eval(stmt.value) break
else if(istype(S, /node/statement/VariableDeclaration))
//VariableDeclaration nodes are used to forcibly declare a local variable so that one in a higher scope isn't used by default.
var/node/statement/VariableDeclaration/dec=S
if(!dec.object)
AssignVariable(dec.var_name.id_name, null, curScope)
else
var/datum/D = Eval(GetVariable(dec.object.id_name))
if(!D) return
D.vars[dec.var_name.id_name] = null
else if(istype(S, /node/statement/FunctionCall))
RunFunction(S)
else if(istype(S, /node/statement/FunctionDefinition))
//do nothing
else if(istype(S, /node/statement/WhileLoop))
RunWhile(S)
else if(istype(S, /node/statement/IfStatement))
RunIf(S)
else if(istype(S, /node/statement/ReturnStatement))
if(!curFunction)
RaiseError(new/runtimeError/UnexpectedReturn())
continue
status |= RETURNING
returnVal=Eval(S:value)
break
else if(istype(S, /node/statement/BreakStatement))
status |= BREAKING
break
else if(istype(S, /node/statement/ContinueStatement))
status |= CONTINUING
break
else
RaiseError(new/runtimeError/UnknownInstruction())
if(status)
break
curScope = scopes.Pop() curScope = scopes.Pop()
/* /*
@@ -212,14 +234,9 @@
*/ */
RunWhile(node/statement/WhileLoop/stmt) RunWhile(node/statement/WhileLoop/stmt)
var/i=1 var/i=1
if(!cur_iterations)
cur_iterations = 1
while(Eval(stmt.cond) && Iterate(stmt.block, i++)) while(Eval(stmt.cond) && Iterate(stmt.block, i++))
cur_iterations++
continue continue
status &= ~BREAKING status &= ~BREAKING
cur_iterations -= i
if(cur_iterations <= 0) cur_iterations = 0
/* /*
Proc:Iterate Proc:Iterate
@@ -227,7 +244,7 @@
*/ */
Iterate(node/BlockDefinition/block, count) Iterate(node/BlockDefinition/block, count)
RunBlock(block) RunBlock(block)
if(max_iterations > 0 && (count >= max_iterations || cur_iterations + 1 >= max_iterations)) if(max_iterations > 0 && count >= max_iterations)
RaiseError(new/runtimeError/IterationLimitReached()) RaiseError(new/runtimeError/IterationLimitReached())
return 0 return 0
if(status & (BREAKING|RETURNING)) if(status & (BREAKING|RETURNING))
@@ -294,3 +311,4 @@
else if(!istype(value) && isobject(value)) value = new/node/expression/value/reference(value) else if(!istype(value) && isobject(value)) value = new/node/expression/value/reference(value)
//TODO: check for invalid name //TODO: check for invalid name
S.variables["[name]"] = value S.variables["[name]"] = value

View File

@@ -276,7 +276,13 @@
exp.func_name=curToken.value exp.func_name=curToken.value
NextToken() //skip function name NextToken() //skip function name
NextToken() //skip open parenthesis, already found NextToken() //skip open parenthesis, already found
var/loops = 0
for() for()
loops++
if(loops>=1000)
CRASH("Something TERRIBLE has gone wrong in ParseFunctionExpression ;__;")
if(istype(curToken, /token/symbol) && curToken.value==")") if(istype(curToken, /token/symbol) && curToken.value==")")
return exp return exp
exp.parameters+=ParseParamExpression() exp.parameters+=ParseParamExpression()

View File

@@ -171,7 +171,12 @@
NextToken() //skip function name NextToken() //skip function name
if(!CheckToken("(", /token/symbol)) //Check for and skip open parenthesis if(!CheckToken("(", /token/symbol)) //Check for and skip open parenthesis
return return
var/loops = 0
for() for()
loops++
if(loops>=6000)
CRASH("Something TERRIBLE has gone wrong in ParseFunctionStatement ;__;")
if(!curToken) if(!curToken)
errors+=new/scriptError/EndOfFile() errors+=new/scriptError/EndOfFile()
return return

View File

@@ -59,6 +59,10 @@
line = 1 line = 1
linepos = 0 //column=codepos-linepos linepos = 0 //column=codepos-linepos
n_scriptOptions/nS_Options/options n_scriptOptions/nS_Options/options
commenting = 0
// 1: single-line
// 2: multi-line
list list
/* /*
Variable: ignore Variable: ignore
@@ -112,12 +116,16 @@
Scan() //Creates a list of tokens from source code Scan() //Creates a list of tokens from source code
var/list/tokens=new var/list/tokens=new
for(, src.codepos<=lentext(code), src.codepos++) for(, src.codepos<=lentext(code), src.codepos++)
var/char=copytext(code, codepos, codepos+1) var/char=copytext(code, codepos, codepos+1)
if(char=="\n") if(char=="\n")
line++ line++
linepos=codepos linepos=codepos
if(ignore.Find(char)) if(ignore.Find(char))
continue continue
else if(char == "/")
ReadComment()
else if(end_stmt.Find(char)) else if(end_stmt.Find(char))
tokens+=new /token/end(char, line, COL) tokens+=new /token/end(char, line, COL)
else if(string_delim.Find(char)) else if(string_delim.Find(char))
@@ -129,6 +137,8 @@
tokens+=ReadNumber() tokens+=ReadNumber()
else if(options.symbols.Find(char)) else if(options.symbols.Find(char))
tokens+=ReadSymbol() tokens+=ReadSymbol()
codepos=initial(codepos) codepos=initial(codepos)
line=initial(line) line=initial(line)
linepos=initial(linepos) linepos=initial(linepos)
@@ -228,4 +238,50 @@
errors+=new/scriptError("Bad number: ", T) errors+=new/scriptError("Bad number: ", T)
T.value=0 T.value=0
codepos-- //allow main Scan() proc to read the next character codepos-- //allow main Scan() proc to read the next character
return T return T
/*
Proc: ReadComment
Reads a comment and outputs the type of comment
*/
ReadComment()
var
char=copytext(code, codepos, codepos+1)
nextchar=copytext(code, codepos+1, codepos+2)
charstring = char+nextchar
comm = 1
// 1: single-line comment
// 2: multi-line comment
expectedend = 0
if(charstring == "//" || charstring == "/*")
if(charstring == "/*")
comm = 2 // starts a multi-line comment
while(comm)
if(++codepos>lentext(code)) break
if(expectedend) // ending statement expected...
char = copytext(code, codepos, codepos+1)
if(char == "/") // ending statement found - beak the comment
comm = 0
break
if(comm == 2)
// multi-line comments are broken by ending statements
char = copytext(code, codepos, codepos+1)
if(char == "*")
expectedend = 1
continue
else
char = copytext(code, codepos, codepos+1)
if(char == "\n")
comm = 0
break
if(expectedend) expectedend = 0
if(comm == 2)
errors+=new/scriptError/UnterminatedComment()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

@@ -331,7 +331,7 @@ menu "menu"
window "Telecomms IDE" window "Telecomms IDE"
elem "Telecomms IDE" elem "Telecomms IDE"
type = MAIN type = MAIN
pos = 281,0 pos = 303,13
size = 652x582 size = 652x582
anchor1 = none anchor1 = none
anchor2 = none anchor2 = none
@@ -372,8 +372,8 @@ window "Telecomms IDE"
type = BUTTON type = BUTTON
pos = 180,464 pos = 180,464
size = 60x20 size = 60x20
anchor1 = none anchor1 = 28,80
anchor2 = none anchor2 = 37,83
font-family = "" font-family = ""
font-size = 0 font-size = 0
font-style = "" font-style = ""
@@ -400,8 +400,8 @@ window "Telecomms IDE"
type = BUTTON type = BUTTON
pos = 120,464 pos = 120,464
size = 60x20 size = 60x20
anchor1 = none anchor1 = 18,80
anchor2 = none anchor2 = 28,83
font-family = "" font-family = ""
font-size = 0 font-size = 0
font-style = "" font-style = ""
@@ -428,8 +428,8 @@ window "Telecomms IDE"
type = OUTPUT type = OUTPUT
pos = 0,488 pos = 0,488
size = 648x94 size = 648x94
anchor1 = none anchor1 = 0,84
anchor2 = none anchor2 = 99,100
font-family = "sans-serif" font-family = "sans-serif"
font-size = 9 font-size = 9
font-style = "" font-style = ""
@@ -454,8 +454,8 @@ window "Telecomms IDE"
type = BUTTON type = BUTTON
pos = 60,464 pos = 60,464
size = 60x20 size = 60x20
anchor1 = none anchor1 = 9,80
anchor2 = none anchor2 = 18,83
font-family = "" font-family = ""
font-size = 0 font-size = 0
font-style = "" font-style = ""
@@ -482,8 +482,8 @@ window "Telecomms IDE"
type = BUTTON type = BUTTON
pos = 0,464 pos = 0,464
size = 60x20 size = 60x20
anchor1 = none anchor1 = 0,80
anchor2 = none anchor2 = 9,83
font-family = "" font-family = ""
font-size = 0 font-size = 0
font-style = "" font-style = ""
@@ -510,8 +510,8 @@ window "Telecomms IDE"
type = INPUT type = INPUT
pos = 0,0 pos = 0,0
size = 652x464 size = 652x464
anchor1 = none anchor1 = 0,0
anchor2 = none anchor2 = 100,80
font-family = "Courier" font-family = "Courier"
font-size = 10 font-size = 10
font-style = "" font-style = ""