Update NTSL from /tg/.

This commit is contained in:
Rob Nelson
2013-08-14 15:42:23 -07:00
parent 3fab835417
commit 153871400a
16 changed files with 603 additions and 361 deletions

View File

@@ -69,11 +69,15 @@
//
IfStatement
var
skip = 0
node
BlockDefinition
block
else_block //may be null
expression/cond
statement/else_if
ElseIf
/*
Class: WhileLoop

View File

@@ -60,6 +60,15 @@
New(name, token/t)
message="Function '[name]' defined twice."
ParameterFunction
message = "You cannot use a function inside a parameter."
New(token/t)
var/line = "?"
if(t)
line = t.line
message = "[line]: [message]"
/*
Class: runtimeError
An error thrown by the interpreter in running the script.

View File

@@ -1,7 +1,7 @@
client/verb/tcssave()
set hidden = 1
if(mob.machine || issilicon(mob))
if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || issilicon(mob))
if(telecomms_check(mob))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob)
return
@@ -9,9 +9,6 @@ client/verb/tcssave()
if(Machine.SelectedServer)
var/obj/machinery/telecomms/server/Server = Machine.SelectedServer
var/tcscode=winget(src, "tcscode", "text")
var/msg="[mob.name] is adding script to server [Server]: [tcscode]"
diary << msg
message_admins("[mob.name] has uploaded a NTLS script to [Machine.SelectedServer] ([mob.x],[mob.y],[mob.z] - <A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[mob.x];Y=[mob.y];Z=[mob.z]'>JMP</a>)",0,1)
Server.setcode( tcscode ) // this actually saves the code from input to the server
src << output(null, "tcserror") // clear the errors
else
@@ -28,7 +25,7 @@ client/verb/tcssave()
client/verb/tcscompile()
set hidden = 1
if(mob.machine || issilicon(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) ))
if(telecomms_check(mob))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob)
return
@@ -36,35 +33,40 @@ client/verb/tcscompile()
if(Machine.SelectedServer)
var/obj/machinery/telecomms/server/Server = Machine.SelectedServer
Server.setcode( winget(src, "tcscode", "text") ) // save code first
var/list/compileerrors = Server.compile() // then compile the code!
// Output all the compile-time errors
src << output(null, "tcserror")
spawn(0)
// Output all the compile-time errors
src << output(null, "tcserror")
src << output("<font color = black>Please wait, compiling...</font>", "tcserror")
if(compileerrors.len)
src << output("<b>Compile Errors</b>", "tcserror")
for(var/scriptError/e in compileerrors)
src << output("<font color = red>\t>[e.message]</font color>", "tcserror")
src << output("([compileerrors.len] errors)", "tcserror")
var/list/compileerrors = Server.compile(mob) // then compile the code!
if(!telecomms_check(mob))
return
// Output compile errors to all other people viewing the code too
for(var/mob/M in Machine.viewingcode)
if(M.client)
M << output(null, "tcserror")
M << output("<b>Compile Errors</b>", "tcserror")
for(var/scriptError/e in compileerrors)
M << output("<font color = red>\t>[e.message]</font color>", "tcserror")
M << output("([compileerrors.len] errors)", "tcserror")
if(compileerrors.len)
src << output("<b>Compile Errors</b>", "tcserror")
for(var/scriptError/e in compileerrors)
src << output("<font color = red>\t>[e.message]</font color>", "tcserror")
src << output("([compileerrors.len] errors)", "tcserror")
// Output compile errors to all other people viewing the code too
for(var/mob/M in Machine.viewingcode)
if(M.client)
M << output(null, "tcserror")
M << output("<b>Compile Errors</b>", "tcserror")
for(var/scriptError/e in compileerrors)
M << output("<font color = red>\t>[e.message]</font color>", "tcserror")
M << output("([compileerrors.len] errors)", "tcserror")
else
src << output("<font color = blue>TCS compilation successful!</font color>", "tcserror")
src << output("(0 errors)", "tcserror")
else
src << output("<font color = blue>TCS compilation successful!</font color>", "tcserror")
src << output("(0 errors)", "tcserror")
for(var/mob/M in Machine.viewingcode)
if(M.client)
M << output("<font color = blue>TCS compilation successful!</font color>", "tcserror")
M << output("(0 errors)", "tcserror")
for(var/mob/M in Machine.viewingcode)
if(M.client)
M << output("<font color = blue>TCS compilation successful!</font color>", "tcserror")
M << output("(0 errors)", "tcserror")
else
src << output(null, "tcserror")
@@ -79,56 +81,26 @@ client/verb/tcscompile()
client/verb/tcsrun()
set hidden = 1
if(mob.machine || issilicon(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) ))
if(telecomms_check(mob))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob)
return
if(Machine.SelectedServer)
var/obj/machinery/telecomms/server/Server = Machine.SelectedServer
Server.setcode( winget(src, "tcscode", "text") ) // save code first
var/list/compileerrors = Server.compile() // then compile the code!
// Output all the compile-time errors
src << output(null, "tcserror")
if(compileerrors.len)
src << output("<b>Compile Errors</b>", "tcserror")
for(var/scriptError/e in compileerrors)
src << output("<font color = red>\t>[e.message]</font color>", "tcserror")
src << output("([compileerrors.len] errors)", "tcserror")
// Output compile errors to all other people viewing the code too
for(var/mob/M in Machine.viewingcode)
if(M.client)
M << output(null, "tcserror")
M << output("<b>Compile Errors</b>", "tcserror")
for(var/scriptError/e in compileerrors)
M << output("<font color = red>\t>[e.message]</font color>", "tcserror")
M << output("([compileerrors.len] errors)", "tcserror")
var/datum/signal/signal = new()
signal.data["message"] = ""
if(Server.freq_listening.len > 0)
signal.frequency = Server.freq_listening[1]
else
// Finally, we run the code!
src << output("<font color = blue>TCS compilation successful! Code executed.</font color>", "tcserror")
src << output("(0 errors)", "tcserror")
signal.frequency = 1459
signal.data["name"] = ""
signal.data["job"] = ""
signal.data["reject"] = 0
signal.data["server"] = Server
for(var/mob/M in Machine.viewingcode)
if(M.client)
M << output("<font color = blue>TCS compilation successful!</font color>", "tcserror")
M << output("(0 errors)", "tcserror")
var/datum/signal/signal = new()
signal.data["message"] = ""
if(Server.freq_listening.len > 0)
signal.frequency = Server.freq_listening[1]
else
signal.frequency = 1459
signal.data["name"] = ""
signal.data["job"] = ""
signal.data["reject"] = 0
signal.data["server"] = Server
Server.Compiler.Run(signal)
Server.Compiler.Run(signal)
else
@@ -145,7 +117,7 @@ client/verb/tcsrun()
client/verb/exittcs()
set hidden = 1
if(mob.machine || issilicon(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) ))
if(telecomms_check(mob))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode == mob)
Machine.storedcode = "[winget(mob, "tcscode", "text")]"
@@ -157,7 +129,7 @@ client/verb/exittcs()
client/verb/tcsrevert()
set hidden = 1
if(mob.machine || issilicon(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) ))
if(telecomms_check(mob))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob)
return
@@ -186,7 +158,7 @@ client/verb/tcsrevert()
client/verb/tcsclearmem()
set hidden = 1
if(mob.machine || issilicon(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) ))
if(telecomms_check(mob))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob)
return
@@ -208,4 +180,9 @@ client/verb/tcsclearmem()
src << output("<font color = red>Failed to clear memory: Unable to locate machine.</font color>", "tcserror")
else
src << output(null, "tcserror")
src << output("<font color = red>Failed to clear memory: Unable to locate machine.</font color>", "tcserror")
src << output("<font color = red>Failed to clear memory: Unable to locate machine.</font color>", "tcserror")
/proc/telecomms_check(var/mob/mob)
if(mob && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && in_range(mob.machine, mob) || issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic))
return 1
return 0

View File

@@ -10,11 +10,26 @@
HandleError(runtimeError/e)
Compiler.Holder.add_entry(e.ToString(), "Execution Error")
GC()
..()
Compiler = null
/datum/TCS_Compiler
var/n_Interpreter/TCS_Interpreter/interpreter
var/obj/machinery/telecomms/server/Holder // the server that is running the code
var/ready = 1 // 1 if ready to run code
/* -- Set ourselves to Garbage Collect -- */
proc/GC()
Holder = null
if(interpreter)
interpreter.GC()
/* -- Compile a raw block of text -- */
proc/Compile(code as message)
@@ -54,7 +69,9 @@
interpreter.SetVar("E" , 2.718281828) // value of e
interpreter.SetVar("SQURT2" , 1.414213562) // value of the square root of 2
interpreter.SetVar("FALSE" , 0) // boolean shortcut to 0
interpreter.SetVar("false" , 0) // boolean shortcut to 0
interpreter.SetVar("TRUE" , 1) // boolean shortcut to 1
interpreter.SetVar("true" , 1) // boolean shortcut to 1
interpreter.SetVar("NORTH" , NORTH) // NORTH (1)
interpreter.SetVar("SOUTH" , SOUTH) // SOUTH (2)
@@ -93,13 +110,13 @@
interpreter.SetProc("broadcast", "tcombroadcast", signal, list("message", "freq", "source", "job"))
/*
-> Send a signal used by signallers.
-> Send a code signal.
@format: signal(frequency, code)
@param frequency: Frequency to broadcast to
@param code: Code to send
@param frequency: Frequency to send the signal to
@param code: Encryption code to send the signal with
*/
interpreter.SetProc("signal", /proc/ntsl_send_signal)
interpreter.SetProc("signal", "signaler", signal, list("freq", "code"))
/*
-> Store a value permanently to the server machine (not the actual game hosting machine, the ingame machine)
@@ -184,8 +201,12 @@
interpreter.SetProc("round", /proc/n_round)
interpreter.SetProc("clamp", /proc/n_clamp)
interpreter.SetProc("inrange", /proc/n_inrange)
interpreter.SetProc("rand", /proc/rand_chance)
// End of Donkie~
// Time
interpreter.SetProc("time", /proc/time)
interpreter.SetProc("timestamp", /proc/timestamp)
// Run the compiled code
interpreter.Run()
@@ -193,21 +214,16 @@
// Backwards-apply variables onto signal data
/* sanitize EVERYTHING. fucking players can't be trusted with SHIT */
signal.data["message"] = interpreter.GetVar("$content")
signal.frequency = interpreter.GetVar("$freq")
signal.data["message"] = interpreter.GetCleanVar("$content", signal.data["message"])
signal.frequency = interpreter.GetCleanVar("$freq", signal.frequency)
var/setname = ""
var/obj/machinery/telecomms/server/S = signal.data["server"]
if(interpreter.GetVar("$source") in S.stored_names)
setname = interpreter.GetVar("$source")
else
setname = "<i>[interpreter.GetVar("$source")]</i>"
var/setname = interpreter.GetCleanVar("$source", signal.data["name"])
if(signal.data["name"] != setname)
signal.data["realname"] = setname
signal.data["name"] = setname
signal.data["job"] = interpreter.GetVar("$job")
signal.data["reject"] = !(interpreter.GetVar("$pass")) // set reject to the opposite of $pass
signal.data["job"] = interpreter.GetCleanVar("$job", signal.data["job"])
signal.data["reject"] = !(interpreter.GetCleanVar("$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"])
@@ -215,6 +231,8 @@
/* -- Actual language proc code -- */
var/const/SIGNAL_COOLDOWN = 20 // 2 seconds
datum/signal
proc/mem(var/address, var/value)
@@ -229,6 +247,37 @@ datum/signal
S.memory[address] = value
proc/signaler(var/freq = 1459, var/code = 30)
if(isnum(freq) && isnum(code))
var/obj/machinery/telecomms/server/S = data["server"]
if(S.last_signal + SIGNAL_COOLDOWN > world.timeofday && S.last_signal < MIDNIGHT_ROLLOVER)
return
S.last_signal = world.timeofday
var/datum/radio_frequency/connection = radio_controller.return_frequency(freq)
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
freq *= 10 // shift the decimal one place
freq = sanitize_frequency(freq)
code = round(code)
code = Clamp(code, 0, 100)
var/datum/signal/signal = new
signal.source = S
signal.encryption = code
signal.data["message"] = "ACTIVATE"
connection.post_signal(S, signal)
var/time = time2text(world.realtime,"hh:mm:ss")
lastsignalers.Add("[time] <B>:</B> [S.id] sent a signal command, which was triggered by NTSL.<B>:</B> [format_frequency(freq)]/[code]")
proc/tcombroadcast(var/message, var/freq, var/source, var/job)
var/datum/signal/newsign = new
@@ -244,7 +293,7 @@ datum/signal
if(!source)
source = "[html_encode(uppertext(S.id))]"
hradio = new // sets the hradio as a radio intercom
if(!freq)
if(!freq || (!isnum(freq) && text2num(freq) == null))
freq = 1459
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
freq *= 10 // shift the decimal one place
@@ -254,12 +303,9 @@ datum/signal
newsign.data["mob"] = null
newsign.data["mobtype"] = /mob/living/carbon/human
if(source in S.stored_names)
newsign.data["name"] = source
else
newsign.data["name"] = "<i>[html_encode(uppertext(source))]<i>"
newsign.data["name"] = source
newsign.data["realname"] = newsign.data["name"]
newsign.data["job"] = job
newsign.data["job"] = "[job]"
newsign.data["compression"] = 0
newsign.data["message"] = message
newsign.data["type"] = 2 // artificial broadcast
@@ -277,6 +323,8 @@ datum/signal
newsign.data["vmask"] = 0
newsign.data["level"] = list()
newsign.sanitize_data()
var/pass = S.relay_information(newsign, "/obj/machinery/telecomms/hub")
if(!pass)
S.relay_information(newsign, "/obj/machinery/telecomms/broadcaster") // send this simple message to broadcasters

View File

@@ -94,6 +94,10 @@
/proc/delay(var/time)
sleep(time)
// Clone of rand()
/proc/rand_chance(var/low = 0, var/high)
return rand(low, high)
// Clone of prob()
/proc/prob_chance(var/chance)
return prob(chance)
@@ -123,6 +127,7 @@
if(container)
if(istype(container, /list) || istext(container))
return length(container)
return 0
// BY DONKIE~
// String stuff
@@ -134,6 +139,12 @@
if(istext(string))
return uppertext(string)
/proc/time()
return world.timeofday
/proc/timestamp(var/format = "hh:mm:ss") // Get the game time in text
return time2text(world.time + 432000, format)
/*
//Makes a list where all indicies in a string is a seperate index in the list
// JUST A HELPER DON'T ADD TO NTSCRIPT
@@ -165,8 +176,10 @@ proc/string_explode(var/string, var/separator)
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))
proc/string_explode(var/string, var/separator = "")
if(istext(string) && (istext(separator) || isnull(separator)))
return text2list(string, separator)
proc/n_repeat(var/string, var/amount)
@@ -247,15 +260,18 @@ proc/n_inrange(var/num, var/min=-1, var/max=1)
// Non-recursive
// Imported from Mono string.ReplaceUnchecked
/*
/proc/string_replacetext(var/haystack,var/a,var/b)
if(istext(haystack)&&istext(a)&&istext(b))
var/i = 1
var/lenh=lentext(haystack)
var/lena=lentext(a)
//var/lenb=lentext(b)
var/count = 0
var/list/dat = list()
while (i < lenh)
var/found = findtext(haystack, a, i, 0)
//diary << "findtext([haystack], [a], [i], 0)=[found]"
if (found == 0) // Not found
break
else
@@ -263,50 +279,49 @@ proc/n_inrange(var/num, var/min=-1, var/max=1)
dat+=found
count+=1
else
//diary << "Script found [a] [count] times, aborted"
break
//diary << "Found [a] at [found]! Moving up..."
i = found + lena
if (count == 0)
return haystack
//var/nlen = lenh + ((lenb - lena) * count)
var/buf = copytext(haystack,1,dat[1]) // Prefill
var/lastReadPos = 0
for (i = 1, i <= count, i++)
var/precopy = dat[i] - lastReadPos-1
//internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
//fixed (char* dest = target, src = source)
//CharCopy (dest + targetIndex, src + sourceIndex, count);
//CharCopy (dest + curPos, source + lastReadPos, precopy);
buf+=copytext(haystack,lastReadPos,precopy)
diary << "buf+=copytext([haystack],[lastReadPos],[precopy])"
diary<<"[buf]"
lastReadPos = dat[i] + lena
//CharCopy (dest + curPos, replace, newValue.length);
buf+=b
diary<<"[buf]"
buf+=copytext(haystack,lastReadPos, 0)
return buf
*/
/proc/ntsl_send_signal(var/freq,var/code)
if(isnum(freq)&&isnum(code))
if(!radio_controller)
sleep(20)
if(!radio_controller)
/proc/string_replacetext(text, find, replacement)
if(istext(text) && istext(find) && istext(replacement))
var/find_len = length(find)
if(find_len < 1) return text
. = ""
var/last_found = 1
var/count = 0
while(1)
count += 1
if(count > SCRIPT_MAX_REPLACEMENTS_ALLOWED)
break
var/found = findtext(text, find, last_found, 0)
. += copytext(text, last_found, found)
if(found)
. += replacement
last_found = found + find_len
continue
return
// Sanitize frequency
var/new_frequency = freq
if(new_frequency < 1200 || new_frequency > 1600)
new_frequency = sanitize_frequency(new_frequency)
// Sanitize code
code = round(code)
code = min(100, code)
code = max(1, code)
// Since we're not an object, we can't do this.
/*
radio_controller.remove_object(src, frequency)
frequency = new_frequency
radio_connection = radio_controller.add_object(src, frequency, RADIO_CHAT)
*/
// Therefore, we do this hacky bit of crap instead.
var/datum/radio_frequency/radio_connection = radio_controller.return_frequency(freq)
// Go go gadget
var/datum/signal/signal = new
signal.source = null // Oh god I hope this works.
signal.encryption = code
signal.data["message"] = "ACTIVATE"
radio_connection.post_signal(null, signal)
#undef SCRIPT_MAX_REPLACEMENTS_ALLOWED

View File

@@ -21,6 +21,7 @@
ASSERT(program)
src.program = program
CreateGlobalScope()
alertadmins = 0 // reset admin alerts
/*
Proc: Run
@@ -29,7 +30,6 @@
Run()
cur_recursion = 0 // reset recursion
cur_statements = 0 // reset CPU tracking
alertadmins = 0
ASSERT(src.program)
RunBlock(src.program)
@@ -110,6 +110,17 @@
var/x = globalScope.variables[name]
return Eval(x)
/*
Proc: GetCleanVar
Returns the value of a global variable in the script and cleans it (sanitizes).
*/
GetCleanVar(name, compare)
var/x = GetVar(name)
if(istext(x) && compare && x != compare) // Was changed
x = sanitize(x)
return x
/*
Proc: CallProc
Calls a global function defined in the script and, amazingly enough, returns its return value. Remember to ensure that the function

View File

@@ -33,11 +33,11 @@
status=0
returnVal
max_statements=1000 // maximum amount of statements that can be called in one execution. this is to prevent massive crashes and exploitation
max_statements=900 // maximum amount of statements that can be called in one execution. this is to prevent massive crashes and exploitation
cur_statements=0 // current amount of statements called
alertadmins=0 // set to 1 if the admins shouldn't be notified of anymore issues
max_iterations=100 // max number of uninterrupted loops possible
max_recursion=50 // max recursions without returning anything (or completing the code block)
max_recursion=10 // max recursions without returning anything (or completing the code block)
cur_recursion=0 // current amount of recursion
/*
Var: persist
@@ -55,6 +55,14 @@
if(program)Load(program)
proc
/*
Set ourselves to Garbage Collect
*/
GC()
..()
container = null
/*
Proc: RaiseError
Raises a runtime error.
@@ -76,6 +84,19 @@
globalScope = S
return S
/*
Proc: AlertAdmins
Alerts the admins of a script that is bad.
*/
AlertAdmins()
if(container && !alertadmins)
if(istype(container, /datum/TCS_Compiler))
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)
/*
Proc: RunBlock
Runs each statement in a block of code.
@@ -100,15 +121,7 @@
cur_statements++
if(cur_statements >= max_statements)
RaiseError(new/runtimeError/MaxCPU())
if(container && !alertadmins)
if(istype(container, /datum/TCS_Compiler))
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)
AlertAdmins()
break
if(istype(S, /node/statement/VariableAssignment))
@@ -171,6 +184,7 @@
// If recursion gets too high (max 50 nested functions) throw an error
if(cur_recursion >= max_recursion)
AlertAdmins()
RaiseError(new/runtimeError/RecursionLimitReached())
return 0
@@ -223,10 +237,21 @@
Checks a condition and runs either the if block or else block.
*/
RunIf(node/statement/IfStatement/stmt)
if(Eval(stmt.cond))
RunBlock(stmt.block)
else if(stmt.else_block)
RunBlock(stmt.else_block)
if(!stmt.skip)
if(Eval(stmt.cond))
RunBlock(stmt.block)
// Loop through the if else chain and tell them to be skipped.
var/node/statement/IfStatement/i = stmt.else_if
var/fail_safe = 800
while(i && fail_safe)
fail_safe -= 1
i.skip = 1
i = i.else_if
else if(stmt.else_block)
RunBlock(stmt.else_block)
// We don't need to skip you anymore.
stmt.skip = 0
/*
Proc: RunWhile
@@ -312,3 +337,4 @@
//TODO: check for invalid name
S.variables["[name]"] = value

View File

@@ -50,7 +50,7 @@ n_scriptOptions
An associative list used by the parser to parse keywords. Indices are strings which will trigger the keyword when parsed and the
associated values are <nS_Keyword> types of which the <n_Keyword.Parse()> proc will be called.
*/
keywords = list("if" = /n_Keyword/nS_Keyword/kwIf, "else" = /n_Keyword/nS_Keyword/kwElse, \
keywords = list("if" = /n_Keyword/nS_Keyword/kwIf, "else" = /n_Keyword/nS_Keyword/kwElse, "elseif" = /n_Keyword/nS_Keyword/kwElseIf, \
"while" = /n_Keyword/nS_Keyword/kwWhile, "break" = /n_Keyword/nS_Keyword/kwBreak, \
"continue" = /n_Keyword/nS_Keyword/kwContinue, \
"return" = /n_Keyword/nS_Keyword/kwReturn, "def" = /n_Keyword/nS_Keyword/kwDef)

View File

@@ -180,12 +180,18 @@
- <ParseParenExpression()>
- <ParseParamExpression()>
*/
ParseExpression(list/end=list(/token/end), list/ErrChars=list("{", "}"))
ParseExpression(list/end=list(/token/end), list/ErrChars=list("{", "}"), check_functions = 0)
var/stack
opr=new
val=new
src.expecting=VALUE
var/loop = 0
for()
loop++
if(loop > 800)
errors+=new/scriptError("Too many nested tokens.")
return
if(EndOfExpression(end))
break
if(istype(curToken, /token/symbol) && ErrChars.Find(curToken.value))
@@ -206,6 +212,7 @@
NextToken()
continue
val.Push(ParseParenExpression())
else if(istype(curToken, /token/symbol)) //Operator found.
var/node/expression/operator/curOperator //Figure out whether it is unary or binary and get a new instance.
if(src.expecting==OPERATOR)
@@ -226,16 +233,24 @@
continue
opr.Push(curOperator)
src.expecting=VALUE
else if(ntok && ntok.value=="(" && istype(ntok, /token/symbol)\
&& istype(curToken, /token/word)) //Parse function call
var/token/preToken=curToken
var/old_expect=src.expecting
var/fex=ParseFunctionExpression()
if(old_expect!=VALUE)
errors+=new/scriptError/ExpectedToken("operator", preToken)
NextToken()
continue
val.Push(fex)
if(!check_functions)
var/token/preToken=curToken
var/old_expect=src.expecting
var/fex=ParseFunctionExpression()
if(old_expect!=VALUE)
errors+=new/scriptError/ExpectedToken("operator", preToken)
NextToken()
continue
val.Push(fex)
else
errors+=new/scriptError/ParameterFunction(curToken)
break
else if(istype(curToken, /token/keyword)) //inline keywords
var/n_Keyword/kw=options.keywords[curToken.value]
kw=new kw(inline=1)
@@ -244,6 +259,7 @@
return
else
errors+=new/scriptError/BadToken(curToken)
else if(istype(curToken, /token/end)) //semicolon found where it wasn't expected
errors+=new/scriptError/BadToken(curToken)
NextToken()
@@ -255,6 +271,7 @@
continue
val.Push(GetExpression(curToken))
src.expecting=OPERATOR
NextToken()
while(opr.Top()) Reduce(opr, val) //Reduce the value stack completely
@@ -280,12 +297,16 @@
for()
loops++
if(loops>=1000)
CRASH("Something TERRIBLE has gone wrong in ParseFunctionExpression ;__;")
if(loops>=800)
errors += new/scriptError("Too many nested expressions.")
break
//CRASH("Something TERRIBLE has gone wrong in ParseFunctionExpression ;__;")
if(istype(curToken, /token/symbol) && curToken.value==")")
return exp
exp.parameters+=ParseParamExpression()
if(errors.len)
return exp
if(curToken.value==","&&istype(curToken, /token/symbol))NextToken() //skip comma
if(istype(curToken, /token/end)) //Prevents infinite loop...
errors+=new/scriptError/ExpectedToken(")")
@@ -310,5 +331,6 @@
See Also:
- <ParseExpression()>
*/
ParseParamExpression()
return ParseExpression(list(",", ")"))
ParseParamExpression(var/check_functions = 0)
var/cf = check_functions
return ParseExpression(list(",", ")"), check_functions = cf)

View File

@@ -51,9 +51,9 @@ var/const/Represents a special statement in the code triggered by a keyword.
kwReturn
Parse(n_Parser/nS_Parser/parser)
.=KW_PASS
if(istype(parser.curBlock, /node/BlockDefinition/GlobalBlock))
parser.errors+=new/scriptError/BadReturn(parser.curToken)
. = KW_WARN
if(istype(parser.curBlock, /node/BlockDefinition/GlobalBlock)) // Exit out of the program by setting the tokens list size to the same as index.
parser.tokens.len = parser.index
return
var/node/statement/ReturnStatement/stmt=new
parser.NextToken() //skip 'return' token
stmt.value=parser.ParseExpression()
@@ -73,6 +73,31 @@ var/const/Represents a special statement in the code triggered by a keyword.
stmt.block=new
parser.AddBlock(stmt.block)
kwElseIf
Parse(n_Parser/nS_Parser/parser)
.=KW_PASS
var/list/L=parser.curBlock.statements
var/node/statement/IfStatement/ifstmt
if(L && L.len)
ifstmt = L[L.len] //Get the last statement in the current block
if(!ifstmt || !istype(ifstmt) || ifstmt.else_if)
parser.errors += new/scriptError/ExpectedToken("if statement", parser.curToken)
return KW_FAIL
var/node/statement/IfStatement/ElseIf/stmt = new
parser.NextToken() //skip 'if' token
stmt.cond = parser.ParseParenExpression()
if(!parser.CheckToken(")", /token/symbol))
return KW_FAIL
if(!parser.CheckToken("{", /token/symbol, skip=0)) //Token needs to be preserved for parse loop, so skip=0
return KW_ERR
parser.curBlock.statements+=stmt
stmt.block=new
ifstmt.else_if = stmt
parser.AddBlock(stmt.block)
kwElse
Parse(n_Parser/nS_Parser/parser)
.=KW_PASS

View File

@@ -174,8 +174,9 @@
var/loops = 0
for()
loops++
if(loops>=6000)
CRASH("Something TERRIBLE has gone wrong in ParseFunctionStatement ;__;")
if(loops>=800)
errors +=new/scriptError("Cannot find ending params.")
return
if(!curToken)
errors+=new/scriptError/EndOfFile()
@@ -184,6 +185,6 @@
curBlock.statements+=stmt
NextToken() //Skip close parenthesis
return
var/node/expression/P=ParseParamExpression()
var/node/expression/P=ParseParamExpression(check_functions = 1)
stmt.parameters+=P
if(istype(curToken, /token/symbol) && curToken.value==",") NextToken()