mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-22 04:24:20 +01:00
@@ -4321,7 +4321,7 @@
|
||||
"bFe" = (/obj/item/radio/intercom{frequency = 1459; name = "Station Intercom (General)"; pixel_y = -25},/obj/machinery/camera{c_tag = "Arrivals - Middle Arm - Far"; dir = 1; network = list("SS13")},/obj/effect/decal/warning_stripes/south,/turf/simulated/floor/plasteel,/area/hallway/secondary/entry{name = "Arrivals"})
|
||||
"bFf" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4; level = 1},/obj/machinery/computer/monitor{name = "Grid Power Monitoring Computer"},/obj/item/radio/intercom{frequency = 1459; name = "Station Intercom (General)"; pixel_x = 5; pixel_y = -26},/turf/simulated/floor/plasteel{icon_state = "dark"},/area/turret_protected/tcomeast{name = "\improper MiniSat East Wing"})
|
||||
"bFg" = (/obj/structure/window/reinforced{dir = 1},/obj/structure/window/reinforced{dir = 4},/turf/space,/area/construction/hallway{name = "\improper MiniSat Exterior"})
|
||||
"bFh" = (/obj/machinery/computer/telecomms/traffic{network = "tcommsat"},/turf/simulated/floor/plasteel{icon_state = "grimy"},/area/tcommsat/computer{name = "\improper Telecoms Control Room"})
|
||||
"bFh" = (/obj/machinery/computer/telecomms/traffic,/turf/simulated/floor/plasteel{icon_state = "grimy"},/area/tcommsat/computer{name = "\improper Telecoms Control Room"})
|
||||
"bFi" = (/obj/machinery/atmospherics/unary/vent_pump{dir = 4; on = 1},/obj/structure/chair/office/dark{dir = 1},/turf/simulated/floor/plasteel{icon_state = "grimy"},/area/tcommsat/computer{name = "\improper Telecoms Control Room"})
|
||||
"bFj" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/turf/simulated/floor/plasteel{icon_state = "grimy"},/area/tcommsat/computer{name = "\improper Telecoms Control Room"})
|
||||
"bFk" = (/obj/structure/transit_tube{icon_state = "E-W-Pass"},/obj/structure/lattice,/turf/space,/area/space)
|
||||
@@ -8504,7 +8504,7 @@
|
||||
"dhF" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/obj/machinery/door/airlock/maintenance{name = "storage room"; req_access = "12"},/turf/simulated/floor/plating,/area/maintenance/aft{name = "Aft Maintenance"})
|
||||
"dhG" = (/obj/item/seeds/watermelon,/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/obj/effect/decal/cleanable/dirt,/turf/simulated/floor/plating,/area/maintenance/aft{name = "Aft Maintenance"})
|
||||
"dhI" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 9},/mob/living/simple_animal/bot/cleanbot{name = "Mopfficer Sweepsky"; on = 0},/turf/simulated/floor/plating,/area/maintenance/fore)
|
||||
|
||||
|
||||
(1,1,1) = {"
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
GLOBAL_DATUM_INIT(ntsl2_config, /datum/ntsl2_configuration, new())
|
||||
// Custom Implementations for NTSL2
|
||||
/* NTSL2 Configuration Datum
|
||||
* This is an abstract handler for the configuration loadout. It's set up like this both for ease of transfering in and out of the UI
|
||||
* as well as allowing users to save and load configurations.
|
||||
*/
|
||||
/datum/ntsl2_configuration
|
||||
/* Simple Toggles */
|
||||
var/toggle_activated = TRUE
|
||||
var/toggle_jobs = FALSE
|
||||
var/toggle_timecode = FALSE
|
||||
// Hack section
|
||||
var/toggle_gibberish = FALSE
|
||||
var/toggle_honk = FALSE
|
||||
|
||||
/* Strings */
|
||||
var/setting_language = null
|
||||
|
||||
/* Tables */
|
||||
var/list/regex = list()
|
||||
|
||||
/* Arrays */
|
||||
var/list/firewall = list()
|
||||
|
||||
/* Meta stuff */
|
||||
// These variables requires the source computer to be hacked in order to change
|
||||
var/list/requires_unlock = list(
|
||||
"firewall" = TRUE,
|
||||
"toggle_gibberish" = TRUE,
|
||||
"toggle_honk" = TRUE,
|
||||
)
|
||||
|
||||
// This is used to sanitize topic data
|
||||
var/list/tables = list("regex")
|
||||
var/list/arrays = list("firewall")
|
||||
|
||||
// This tells the datum what is safe to serialize and what's not. It also applies to deserialization.
|
||||
var/list/to_serialize = list(
|
||||
"toggle_activated",
|
||||
"toggle_jobs",
|
||||
"toggle_timecode",
|
||||
"toggle_gibberish",
|
||||
"toggle_honk",
|
||||
"setting_language",
|
||||
"regex",
|
||||
"firewall"
|
||||
)
|
||||
|
||||
// This is used for sanitization.
|
||||
var/list/serialize_sanitize = list(
|
||||
"toggle_activated" = "bool",
|
||||
"toggle_jobs" = "bool",
|
||||
"toggle_timecode" = "bool",
|
||||
"toggle_gibberish" = "bool",
|
||||
"toggle_honk" = "bool",
|
||||
"setting_language" = "string",
|
||||
"regex" = "table",
|
||||
"firewall" = "array"
|
||||
)
|
||||
|
||||
// Used to determine what languages are allowable for conversion. Generated during runtime.
|
||||
var/list/valid_languages = list("--DISABLE--")
|
||||
|
||||
/datum/ntsl2_configuration/proc/reset()
|
||||
/* Simple Toggles */
|
||||
toggle_activated = initial(toggle_activated)
|
||||
toggle_jobs = initial(toggle_jobs)
|
||||
toggle_timecode = initial(toggle_timecode)
|
||||
// Hack section
|
||||
toggle_gibberish = initial(toggle_gibberish)
|
||||
toggle_honk = initial(toggle_honk)
|
||||
|
||||
/* Strings */
|
||||
setting_language = initial(setting_language)
|
||||
|
||||
/* Tables */
|
||||
regex = list()
|
||||
|
||||
/* Arrays */
|
||||
firewall = list()
|
||||
|
||||
|
||||
/datum/ntsl2_configuration/proc/update_languages()
|
||||
for(var/language in GLOB.all_languages)
|
||||
var/datum/language/L = GLOB.all_languages[language]
|
||||
if(L.flags & HIVEMIND)
|
||||
continue
|
||||
valid_languages[language] = TRUE
|
||||
|
||||
// I'd use serialize() but it's used by another system. This converts the configuration into a JSON string.
|
||||
/datum/ntsl2_configuration/proc/ntsl_serialize()
|
||||
. = list()
|
||||
for(var/variable in to_serialize)
|
||||
.[variable] = vars[variable]
|
||||
. = json_encode(.)
|
||||
|
||||
// This loads a configuration from a JSON string.
|
||||
// Fucking broken as shit, someone help me fix this.
|
||||
/datum/ntsl2_configuration/proc/ntsl_deserialize(text, obj/machinery/computer/telecomms/traffic/source)
|
||||
var/list/var_list = json_decode(text)
|
||||
for(var/variable in var_list)
|
||||
if(variable in to_serialize) // Don't just accept any random vars jesus christ!
|
||||
if(requires_unlock[variable] && (source && !source.unlocked))
|
||||
continue
|
||||
var/sanitize_method = serialize_sanitize[variable]
|
||||
var/variable_value = var_list[variable]
|
||||
variable_value = sanitize(variable_value, sanitize_method)
|
||||
if(variable_value)
|
||||
vars[variable] = variable_value
|
||||
|
||||
// Sanitizing user input. Don't blindly trust the JSON.
|
||||
/datum/ntsl2_configuration/proc/sanitize(variable, sanitize_method)
|
||||
if(!sanitize_method)
|
||||
return null
|
||||
|
||||
switch(sanitize_method)
|
||||
if("bool")
|
||||
return variable ? TRUE : FALSE
|
||||
if("table", "array")
|
||||
if(!islist(variable))
|
||||
return list()
|
||||
// Insert html filtering for the regexes here if you're boring
|
||||
return sanitize(variable)
|
||||
if("string")
|
||||
return "[variable]"
|
||||
|
||||
return variable
|
||||
|
||||
// Primary signal modification. This is where all of the variables behavior are actually implemented.
|
||||
/datum/ntsl2_configuration/proc/modify_signal(datum/signal/signal)
|
||||
// Servers are deliberately turned off. Mark every signal as rejected.
|
||||
if(!toggle_activated)
|
||||
signal.data["reject"] = TRUE
|
||||
return
|
||||
|
||||
// Firewall
|
||||
// This must happen before anything else modifies the signal ["name"].
|
||||
if(islist(firewall) && firewall.len > 0)
|
||||
if(firewall.Find(signal.data["name"]))
|
||||
signal.data["reject"] = 1
|
||||
|
||||
// These two stack properly.
|
||||
// Simple job indicator switch.
|
||||
if(toggle_jobs)
|
||||
var/new_name = signal.data["name"] + " ([signal.data["job"]]) "
|
||||
signal.data["name"] = new_name
|
||||
signal.data["realname"] = new_name // this is required because the broadcaster uses this directly if the speaker doesn't have a voice changer on
|
||||
|
||||
// Add the current station time like a time code.
|
||||
if(toggle_timecode)
|
||||
var/new_name = "\[[station_time_timestamp()]] " + signal.data["name"]
|
||||
signal.data["name"] = new_name
|
||||
signal.data["realname"] = new_name // this is required because the broadcaster uses this directly if the speaker doesn't have a voice changer on
|
||||
|
||||
// Hacks!
|
||||
// Censor dat shit like nobody's business
|
||||
if(toggle_gibberish)
|
||||
signal.data["message"] = Gibberish(signal.data["message"], 80)
|
||||
|
||||
// Replace everything with HONK!
|
||||
if(toggle_honk)
|
||||
var/list/split = splittext(signal.data["message"], " ")
|
||||
var/honklength = split.len
|
||||
var/new_message = ""
|
||||
for(var/i in 1 to honklength)
|
||||
new_message += pick("HoNK!", "HONK", "HOOOoONK", "HONKHONK!", "HoNnnkKK!!!", "HOOOOOOOOOOONK!!!!11!", "henk!")
|
||||
signal.data["message"] = new_message
|
||||
|
||||
|
||||
// Language Conversion
|
||||
if(setting_language && valid_languages[setting_language])
|
||||
if(setting_language == "--DISABLE--")
|
||||
setting_language = null
|
||||
else
|
||||
signal.data["language"] = GLOB.all_languages[setting_language]
|
||||
|
||||
// Regex replacements
|
||||
if(islist(regex) && regex.len > 0)
|
||||
var/original = signal.data["message"]
|
||||
var/new_message = original
|
||||
for(var/reg in regex)
|
||||
var/replacePattern = pencode_to_html(regex[reg])
|
||||
var/regex/start = new(reg, "gi")
|
||||
new_message = start.Replace(original, replacePattern)
|
||||
signal.data["message"] = new_message
|
||||
|
||||
// Make sure the message is valid after we tinkered with it, otherwise reject it
|
||||
if(signal.data["message"] == "" || !signal.data["message"])
|
||||
signal.data["reject"] = 1
|
||||
|
||||
/datum/ntsl2_configuration/Topic(mob/user, href_list, window_id, obj/machinery/computer/telecomms/traffic/source)
|
||||
// Toggles
|
||||
if(href_list["toggle"])
|
||||
var/var_to_toggle = href_list["toggle"]
|
||||
if(requires_unlock[var_to_toggle] && !source.unlocked)
|
||||
return
|
||||
if(!(var_to_toggle in to_serialize))
|
||||
return
|
||||
vars[var_to_toggle] = !vars[var_to_toggle]
|
||||
log_action(user, "toggled NTSL2 variable [var_to_toggle] [vars[var_to_toggle] ? "on" : "off"]")
|
||||
|
||||
// Strings
|
||||
if(href_list["setting_language"])
|
||||
var/new_language = input(user, "Pick a language to convert messages to.", "Language Conversion") as null|anything in valid_languages
|
||||
if(!new_language)
|
||||
return
|
||||
if(new_language == "--DISABLE--")
|
||||
setting_language = null
|
||||
to_chat(user, "<span class='notice'>Language conversion disabled.</span>")
|
||||
else
|
||||
setting_language = new_language
|
||||
to_chat(user, "<span class='notice'>Messages will now be converted to [new_language].</span>")
|
||||
|
||||
log_action(user, new_language == "--DISABLE--" ? "disabled NTSL2 language conversion" : "set NTSL2 language conversion to [new_language]", TRUE)
|
||||
|
||||
// Tables
|
||||
if(href_list["create_row"])
|
||||
if(href_list["table"] && href_list["table"] in tables)
|
||||
if(requires_unlock[href_list["table"]] && !source.unlocked)
|
||||
return
|
||||
var/new_key = input(user, "Provide a key for the new row.", "New Row") as text|null
|
||||
if(!new_key)
|
||||
return
|
||||
var/new_value = input(user, "Provide a new value for the key [new_key]", "New Row") as text|null
|
||||
if(new_value == null)
|
||||
return
|
||||
var/list/table = vars[href_list["table"]]
|
||||
table[new_key] = new_value
|
||||
to_chat(user, "<span class='notice'>Added row [new_key] -> [new_value].</span>")
|
||||
log_action(user, "updated [href_list["table"]] - new row [new_key] -> [new_value]")
|
||||
|
||||
if(href_list["delete_row"])
|
||||
if(href_list["table"] && href_list["table"] in tables)
|
||||
if(requires_unlock[href_list["table"]] && !source.unlocked)
|
||||
return
|
||||
var/list/table = vars[href_list["table"]]
|
||||
table.Remove(href_list["delete_row"])
|
||||
to_chat(user, "<span class='warning'>Removed row [href_list["delete_row"]] from [href_list["table"]]</span>")
|
||||
log_action(user, "updated [href_list["table"]] - removed row [href_list["delete_row"]]")
|
||||
|
||||
// Arrays
|
||||
if(href_list["create_item"])
|
||||
if(href_list["array"] && href_list["array"] in arrays)
|
||||
if(requires_unlock[href_list["array"]] && !source.unlocked)
|
||||
return
|
||||
var/new_value = input(user, "Provide a value for the new index.", "New Index") as text|null
|
||||
if(new_value == null)
|
||||
return
|
||||
var/list/array = vars[href_list["array"]]
|
||||
array.Add(new_value)
|
||||
to_chat(user, "<span class='notice'>Added row [new_value].</span>")
|
||||
log_action(user, "updated [href_list["array"]] - new value [new_value]")
|
||||
|
||||
if(href_list["delete_item"])
|
||||
if(href_list["array"] && href_list["array"] in arrays)
|
||||
if(requires_unlock[href_list["array"]] && !source.unlocked)
|
||||
return
|
||||
var/list/array = vars[href_list["array"]]
|
||||
array.Remove(href_list["delete_item"])
|
||||
to_chat(user, "<span class='warning'>Removed [href_list["delete_item"]] from [href_list["array"]]</span>")
|
||||
log_action(user, "updated [href_list["array"]] - removed [href_list["delete_item"]]")
|
||||
|
||||
// Spit out the serialized config to the user
|
||||
if(href_list["save_config"])
|
||||
user << browse(ntsl_serialize(), "window=save_ntsl2")
|
||||
|
||||
if(href_list["load_config"])
|
||||
var/json = input(user, "Provide configuration JSON below.", "Load Config", ntsl_serialize()) as message
|
||||
ntsl_deserialize(json, source)
|
||||
log_action(user, "has uploaded a NTSL2 JSON configuration: [json]", TRUE)
|
||||
|
||||
user << output(list2params(list(ntsl_serialize())), "[window_id].browser:updateConfig")
|
||||
|
||||
/datum/ntsl2_configuration/proc/log_action(user, msg, adminmsg = FALSE)
|
||||
log_game("NTSL2: [key_name(user)] [msg]")
|
||||
log_investigate("[key_name(user)] [msg]", "ntsl")
|
||||
if(adminmsg)
|
||||
message_admins("[key_name_admin(user)] [msg]")
|
||||
|
||||
/* Asset datum for the UI */
|
||||
/datum/asset/simple/ntsl2
|
||||
assets = list(
|
||||
"bundle.css" = 'html/ntsl2/dist/bundle.css',
|
||||
"bundle.js" = 'html/ntsl2/dist/bundle.js',
|
||||
"tab_home.html" = 'html/ntsl2/dist/tab_home.html',
|
||||
"tab_hack.html" = 'html/ntsl2/dist/tab_hack.html',
|
||||
"tab_filtering.html" = 'html/ntsl2/dist/tab_filtering.html',
|
||||
"tab_firewall.html" = 'html/ntsl2/dist/tab_firewall.html',
|
||||
"tab_regex.html" = 'html/ntsl2/dist/tab_regex.html',
|
||||
"uiTitleFluff.png" = 'html/ntsl2/dist/uiTitleFluff.png'
|
||||
)
|
||||
|
||||
/* Custom subtype of /datum/browser that behaves as we want for our project */
|
||||
/datum/browser/ntsl2
|
||||
var/initial_config // Initial NTSL2 configuration
|
||||
|
||||
/datum/browser/ntsl2/New(nuser, nwindow_id, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null, ntsl2_config)
|
||||
. = ..()
|
||||
initial_config = ntsl2_config
|
||||
// Prevent all stylesheets from being added, we have our own CSS that's bundled with gulp
|
||||
/datum/browser/ntsl2/add_stylesheet()
|
||||
return
|
||||
// No header, we're running a fully complete .html file
|
||||
/datum/browser/ntsl2/get_header()
|
||||
return
|
||||
// We inject a little code at the bottom of the file, similar to NanoUI, but more limited.
|
||||
// This code is used for delivering live updates of config changes & allowing the UI to provide Topic data.
|
||||
/datum/browser/ntsl2/get_footer()
|
||||
var/byondSrc = "byond://?src=[ref.UID()];"
|
||||
var/dat = "<script type='text/javascript'>"
|
||||
dat += "window.byondSrc = '[byondSrc]';"
|
||||
dat += "window.originalConfig = '[html_encode(initial_config)]';"
|
||||
dat += "window.updateConfig = function(config) { window.config = JSON.parse(config); window.reload_tab() };"
|
||||
dat += "</script>"
|
||||
return dat
|
||||
@@ -435,37 +435,25 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
|
||||
var/logs = 0 // number of logs
|
||||
var/totaltraffic = 0 // gigabytes (if > 1024, divide by 1024 -> terrabytes)
|
||||
|
||||
var/list/memory = list() // stored memory
|
||||
var/list/rawcode = list() // the code to compile (list of characters)
|
||||
var/datum/TCS_Compiler/Compiler // the compiler that compiles and runs the code
|
||||
var/autoruncode = 0 // 1 if the code is set to run every time a signal is picked up
|
||||
|
||||
var/encryption = "null" // encryption key: ie "password"
|
||||
var/salt = "null" // encryption salt: ie "123comsat"
|
||||
// would add up to md5("password123comsat")
|
||||
var/language = "human"
|
||||
var/obj/item/radio/headset/server_radio = null
|
||||
|
||||
/obj/machinery/telecomms/server/New()
|
||||
/obj/machinery/telecomms/server/Initialize()
|
||||
..()
|
||||
Compiler = new()
|
||||
Compiler.Holder = src
|
||||
server_radio = new()
|
||||
|
||||
/obj/machinery/telecomms/server/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from)
|
||||
|
||||
if(signal.data["message"])
|
||||
|
||||
if(is_freq_listening(signal))
|
||||
|
||||
if(traffic > 0)
|
||||
totaltraffic += traffic // add current traffic to total traffic
|
||||
|
||||
//Is this a test signal? Bypass logging
|
||||
if(signal.data["type"] != 4)
|
||||
|
||||
// If signal has a message and appropriate frequency
|
||||
|
||||
update_logs()
|
||||
|
||||
var/datum/comm_log_entry/log = new
|
||||
@@ -508,23 +496,14 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
|
||||
var/identifier = num2text( rand(-1000,1000) + world.time )
|
||||
log.name = "data packet ([md5(identifier)])"
|
||||
|
||||
if(Compiler && autoruncode)
|
||||
Compiler.Run(signal) // execute the code
|
||||
// Run NTSL2 against the signal
|
||||
if(GLOB.ntsl2_config)
|
||||
GLOB.ntsl2_config.modify_signal(signal)
|
||||
|
||||
var/can_send = relay_information(signal, "/obj/machinery/telecomms/hub")
|
||||
if(!can_send)
|
||||
relay_information(signal, "/obj/machinery/telecomms/broadcaster")
|
||||
|
||||
|
||||
/obj/machinery/telecomms/server/proc/setcode(var/list/code)
|
||||
if(istype(code))
|
||||
rawcode = code
|
||||
|
||||
/obj/machinery/telecomms/server/proc/compile(mob/user as mob)
|
||||
if(Compiler)
|
||||
admin_log(user)
|
||||
return Compiler.Compile(rawcode)
|
||||
|
||||
/obj/machinery/telecomms/server/proc/update_logs()
|
||||
// start deleting the very first log entry
|
||||
if(logs >= 400)
|
||||
@@ -544,16 +523,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
|
||||
log_entries.Add(log)
|
||||
update_logs()
|
||||
|
||||
/obj/machinery/telecomms/server/proc/admin_log(var/mob/mob)
|
||||
var/msg="[key_name(mob)] has compiled a script to server [src]:"
|
||||
log_game("NTSL: [msg]")
|
||||
log_game("NTSL: [rawcode.Join("")]")
|
||||
src.investigate_log("[msg]<br>[rawcode.Join("")]", "ntsl")
|
||||
if(length(rawcode)) // Let's not bother the admins for empty code.
|
||||
message_admins("[key_name_admin(mob)] has compiled and uploaded a NTSL script to [src.id] (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</a>)")
|
||||
|
||||
// Simple log entry datum
|
||||
|
||||
/datum/comm_log_entry
|
||||
var/parameters = list() // carbon-copy to signal.data[]
|
||||
var/name = "data packet (#)"
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
/obj/machinery/computer/telecomms/traffic
|
||||
name = "telecommunications traffic control"
|
||||
|
||||
var/screen = 0 // the screen number:
|
||||
var/allservers = 0 // Are all servers being edited at the same time?
|
||||
var/list/servers = list() // the servers located by the computer
|
||||
var/mob/editingcode
|
||||
var/mob/lasteditor
|
||||
var/obj/machinery/telecomms/server/SelectedServer
|
||||
|
||||
var/compiling_errors = ""
|
||||
|
||||
var/network = "NULL" // the network to probe
|
||||
var/temp = "" // temporary feedback messages
|
||||
|
||||
var/storedcode = "" // code stored
|
||||
|
||||
light_color = LIGHT_COLOR_DARKGREEN
|
||||
|
||||
req_access = list(access_tcomsat)
|
||||
circuit = /obj/item/circuitboard/comm_traffic
|
||||
|
||||
// NTSL2
|
||||
var/window_id // mostly used to let the configuration datum update the user's UI
|
||||
var/unlocked = FALSE
|
||||
|
||||
/obj/machinery/computer/telecomms/traffic/attackby(obj/item/I, mob/user)
|
||||
if(ismultitool(I))
|
||||
unlocked = !unlocked
|
||||
to_chat(user, "<span class='notice'>This computer is now [unlocked ? "<span class='good'>Unlocked</span>" : "<span class='bad'>Locked</span>"]. \
|
||||
Reopen the UI to see the difference.</span>")
|
||||
return
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/computer/telecomms/traffic/attack_hand(mob/user)
|
||||
interact(user)
|
||||
|
||||
@@ -27,154 +25,19 @@
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
return 0
|
||||
|
||||
user.set_machine(src)
|
||||
if(GLOB.ntsl2_config.valid_languages.len == 1)
|
||||
GLOB.ntsl2_config.update_languages() // this is silly but it has to be done because ntsl2 inits before languages do
|
||||
|
||||
if(!editingcode || editingcode && editingcode.machine != src)
|
||||
editingcode = user
|
||||
lasteditor = user
|
||||
|
||||
var/dat = ""
|
||||
|
||||
dat += "<center><b>Telecommunications Traffic Control</b></center>"
|
||||
|
||||
if(screen == 0) //main menu
|
||||
dat += "<br>[temp]</br>"
|
||||
dat += "<br>Current Network: <a href='?src=[UID()];network=1'>[network]</a><br>"
|
||||
if(servers.len)
|
||||
dat += "<br>Detected Telecommunication Servers:<ul>"
|
||||
for(var/obj/machinery/telecomms/T in servers)
|
||||
dat += "<li><a href='?src=[UID()];viewserver=[T.id]'>\ref[T] [T.name]</a> ([T.id])</li>"
|
||||
dat += "<li><a href='?src=[UID()];viewserver=all'>Modify All Detected Servers</a></li>"
|
||||
dat += "</ul>"
|
||||
|
||||
dat += "<br><a href='?src=[UID()];operation=release'>\[Flush Buffer\]</a>"
|
||||
|
||||
else
|
||||
dat += "<br>No servers detected. Scan for servers: <a href='?src=[UID()];operation=scan'>\[Scan\]</a>"
|
||||
|
||||
if(screen == 1) //server menu
|
||||
dat += "<br>[temp]<br>"
|
||||
dat += "<center><a href='?src=[UID()];operation=mainmenu'>\[Main Menu\]</a> <a href='?src=[UID()];operation=refresh'>\[Refresh\]</a></center>"
|
||||
dat += "<br>Current Network: [network]"
|
||||
if(allservers)
|
||||
dat += "<br>Selected Server: All Servers<br><br>"
|
||||
else
|
||||
dat += "<br>Selected Server: [SelectedServer.id]<br><br>"
|
||||
dat += "<br><a href='?src=[UID()];operation=editcode'>\[Edit Code\]</a>"
|
||||
dat += "<br>Signal Execution: "
|
||||
if(allservers)
|
||||
dat += "<a href='?src=[UID()];operation=runon'>ALWAYS</a> <a href='?src=[UID()];operation=runoff'>NEVER</a>"
|
||||
else
|
||||
if(SelectedServer.autoruncode)
|
||||
dat += "<a href='?src=[UID()];operation=togglerun'>ALWAYS</a>"
|
||||
else
|
||||
dat += "<a href='?src=[UID()];operation=togglerun'>NEVER</a>"
|
||||
|
||||
if(screen == 2) //code editor
|
||||
if(editingcode == user)
|
||||
dat += {"<br>[temp]<br>
|
||||
<center>
|
||||
<a href='?src=[UID()];operation=codeback'>\[Back\]</a>
|
||||
<a href='?src=[UID()];operation=mainmenu'>\[Main Menu\]</a>
|
||||
<a href='?src=[UID()];operation=refresh'>\[Refresh\]</a>
|
||||
</center>
|
||||
|
||||
<style type="text/css">
|
||||
.CodeMirror {
|
||||
height: auto;
|
||||
}
|
||||
button {
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
background: #40628a;
|
||||
border: 1px solid #161616;
|
||||
padding: 1px 4px 1px 4px;
|
||||
margin: 0 2px 0 0;
|
||||
cursor:default;
|
||||
}
|
||||
button:hover {
|
||||
color: #40628a;
|
||||
background: #ffffff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="statusDisplay">
|
||||
<textarea id="fSubmit" name="cMirror">[storedcode]</textarea>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var cMirror_fSubmit = CodeMirror.fromTextArea(document.getElementById("fSubmit"),
|
||||
{
|
||||
lineNumbers: true,
|
||||
indentUnit: 4,
|
||||
indentWithTabs: true,
|
||||
mode: "NTSL",
|
||||
theme: "lesser-dark",
|
||||
viewportMargin: Infinity
|
||||
}
|
||||
);
|
||||
|
||||
function compileCode() {
|
||||
var codeText = cMirror_fSubmit.getValue();
|
||||
document.getElementById("cMirrorPost").value = codeText;
|
||||
document.getElementById("cMirrorPostList").value = JSON.stringify(codeText.split(''));
|
||||
document.getElementById("theform").submit();
|
||||
}
|
||||
|
||||
function clearCode() {
|
||||
window.location = "byond://?src=[UID()];choice=Clear;";
|
||||
}
|
||||
</script>
|
||||
<a href="javascript:compileCode()">Compile</a>
|
||||
<a href="javascript:clearCode()">Clear</a>
|
||||
|
||||
<div class="item">
|
||||
[compiling_errors]
|
||||
</div>
|
||||
|
||||
<form action="byond://" method="POST" id="theform">
|
||||
<input type="hidden" name="choice" value="Compile">
|
||||
<input type="hidden" name="src" value="[UID()]">
|
||||
<input type="hidden" id="cMirrorPost" name="cMirror" value="">
|
||||
<input type="hidden" id="cMirrorPostList" name="cMirrorList" value="">
|
||||
</form>
|
||||
"}
|
||||
else
|
||||
dat += {"<br>[temp]<br>
|
||||
<center><a href='?src=[UID()];operation=refresh'>\[Refresh\]</a></center>
|
||||
<div class="item" style="width:80%">
|
||||
<textarea id="fSubmit" name="cMirror">
|
||||
[storedcode]
|
||||
</textarea>
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("fSubmit"),
|
||||
{
|
||||
lineNumbers: true,
|
||||
indentUnit: 4,
|
||||
indentWithTabs: true,
|
||||
mode: "NTSL",
|
||||
theme: "lesser-dark",
|
||||
viewportMargin: Infinity
|
||||
}
|
||||
);
|
||||
</script>
|
||||
</div>
|
||||
<div class="item">
|
||||
[compiling_errors]
|
||||
</div>
|
||||
"} //anything typed will be overridden anyways by the one who is editing the code
|
||||
|
||||
var/datum/browser/popup = new(user, "traffic_control", "Telecommunication Traffic Control", 700, 500)
|
||||
//codemirror
|
||||
popup.add_script("codemirror-compressed", 'nano/codemirror/codemirror-compressed.js') // A custom minified JavaScript file of CodeMirror, with the following plugins: CSS Mode, NTSL Mode, CSS-hint addon, Search addon, Sublime Keymap.
|
||||
popup.add_stylesheet("codemirror", 'nano/codemirror/codemirror.css') // A CSS sheet containing the basic stylings and formatting information for CodeMirror.
|
||||
popup.add_stylesheet("lesser-dark", 'nano/codemirror/lesser-dark.css') // A theme for CodeMirror to use, which closely resembles the rest of the NanoUI style.
|
||||
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
temp = ""
|
||||
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/ntsl2)
|
||||
assets.send(user)
|
||||
|
||||
var/dat = file2text("html/ntsl2/dist/index.html")
|
||||
if(unlocked)
|
||||
dat += "\n<script type='text/javascript'>window.secretsunlocked = true;</script>"
|
||||
var/datum/browser/ntsl2/nt_browser = new(user, name, "NTSL2", 720, 480, src, GLOB.ntsl2_config.ntsl_serialize())
|
||||
nt_browser.set_content(dat)
|
||||
nt_browser.open()
|
||||
window_id = nt_browser.window_id
|
||||
|
||||
/obj/machinery/computer/telecomms/traffic/Topic(href, href_list)
|
||||
if(..())
|
||||
@@ -184,209 +47,4 @@
|
||||
if(!istype(user) || !user.client)
|
||||
return 0
|
||||
|
||||
if(user != editingcode)
|
||||
return 0
|
||||
|
||||
var/code = href_list["cMirror"]
|
||||
if(code)
|
||||
storedcode = code
|
||||
|
||||
var/list/codelist = href_list["cMirrorList"]
|
||||
if(istext(codelist))
|
||||
codelist = json_decode(codelist)
|
||||
|
||||
add_fingerprint(user)
|
||||
user.set_machine(src)
|
||||
|
||||
if(!allowed(user) && !emagged && !user.can_admin_interact())
|
||||
to_chat(user, "<span class='danger'>Access Denied.</span>")
|
||||
return 0
|
||||
|
||||
switch(href_list["choice"])
|
||||
if("Compile")
|
||||
if(!istype(codelist))
|
||||
return 0
|
||||
if(user != editingcode)
|
||||
return 0 //only one editor
|
||||
|
||||
if(SelectedServer)
|
||||
var/obj/machinery/telecomms/server/Server = SelectedServer
|
||||
Server.setcode(codelist)
|
||||
|
||||
spawn(0)
|
||||
// Output all the compile-time errors
|
||||
compiling_errors = "<font color = black>Please wait, compiling...</font>"
|
||||
updateUsrDialog()
|
||||
|
||||
var/list/compileerrors = Server.compile(user) // then compile the code!
|
||||
if(!telecomms_check(user))
|
||||
return
|
||||
|
||||
if(compileerrors.len)
|
||||
compiling_errors = "<b>Compile Errors</b><br>"
|
||||
for(var/datum/scriptError/e in compileerrors)
|
||||
compiling_errors += "<font color = red>\t>[e.message]</font></br>"
|
||||
compiling_errors += "([compileerrors.len] errors)"
|
||||
|
||||
|
||||
else
|
||||
compiling_errors = "<font color='#4266DB'>TCS compilation successful!</font><br>"
|
||||
compiling_errors += "(0 errors)"
|
||||
|
||||
updateUsrDialog()
|
||||
if(allservers)
|
||||
spawn(0)
|
||||
compiling_errors = "<font color = black>Please wait, compiling...</font>"
|
||||
updateUsrDialog()
|
||||
|
||||
for(var/obj/machinery/telecomms/server/Server in servers)
|
||||
Server.setcode(codelist)
|
||||
var/list/compileerrors = Server.compile(user)
|
||||
if(!telecomms_check(user))
|
||||
return
|
||||
if(compileerrors.len)
|
||||
compiling_errors = "<b>Compile Errors</b><br>"
|
||||
for(var/datum/scriptError/e in compileerrors)
|
||||
compiling_errors += "<font color = red>\t>[e.message]</font></br>"
|
||||
compiling_errors += "([compileerrors.len] errors)"
|
||||
updateUsrDialog()
|
||||
return // If there are errors, don't waste time compiling on other servers
|
||||
compiling_errors = "<font color='#4266DB'>TCS compilation successful!</font><br>"
|
||||
compiling_errors += "(0 errors)"
|
||||
updateUsrDialog()
|
||||
|
||||
if("Clear")
|
||||
if(!telecomms_check(user) || user != editingcode)
|
||||
return 0
|
||||
|
||||
if(SelectedServer)
|
||||
var/obj/machinery/telecomms/server/Server = SelectedServer
|
||||
Server.memory = list() // clear the memory
|
||||
|
||||
compiling_errors = "<font color='#4266DB'>Server Memory Cleared!</font>"
|
||||
storedcode = null
|
||||
updateUsrDialog()
|
||||
if(allservers)
|
||||
for(var/obj/machinery/telecomms/server/Server in servers)
|
||||
Server.memory = list() // clear the memory
|
||||
|
||||
compiling_errors = "<font color='#4266DB'>Server Memory Cleared!</font>"
|
||||
storedcode = null
|
||||
updateUsrDialog()
|
||||
|
||||
if(href_list["viewserver"])
|
||||
screen = 1
|
||||
if(href_list["viewserver"] == "all")
|
||||
allservers = 1
|
||||
else
|
||||
allservers = 0
|
||||
for(var/obj/machinery/telecomms/T in servers)
|
||||
if(T.id == href_list["viewserver"])
|
||||
SelectedServer = T
|
||||
break
|
||||
|
||||
if(href_list["operation"])
|
||||
switch(href_list["operation"])
|
||||
|
||||
if("release")
|
||||
servers = list()
|
||||
screen = 0
|
||||
|
||||
if("mainmenu")
|
||||
screen = 0
|
||||
|
||||
if("codeback")
|
||||
if(allservers || SelectedServer)
|
||||
screen = 1
|
||||
|
||||
if("scan")
|
||||
if(servers.len > 0)
|
||||
temp = "<font color = #D70B00>- FAILED: CANNOT PROBE WHEN BUFFER FULL -</font>"
|
||||
|
||||
else
|
||||
for(var/obj/machinery/telecomms/server/T in range(25, src))
|
||||
if(T.network == network)
|
||||
servers.Add(T)
|
||||
|
||||
if(!servers.len)
|
||||
temp = "<font color = #D70B00>- FAILED: UNABLE TO LOCATE SERVERS IN \[[network]\] -</font>"
|
||||
else
|
||||
temp = "<font color = #336699>- [servers.len] SERVERS PROBED & BUFFERED -</font>"
|
||||
|
||||
screen = 0
|
||||
|
||||
if("editcode")
|
||||
screen = 2
|
||||
|
||||
if("togglerun")
|
||||
SelectedServer.autoruncode = !(SelectedServer.autoruncode)
|
||||
if("runon")
|
||||
for(var/obj/machinery/telecomms/server/Server in servers)
|
||||
Server.autoruncode = 1
|
||||
if("runoff")
|
||||
for(var/obj/machinery/telecomms/server/Server in servers)
|
||||
Server.autoruncode = 0
|
||||
|
||||
if(href_list["network"])
|
||||
|
||||
var/newnet = input(user, "Which network do you want to view?", "Comm Monitor", network) as null|text
|
||||
|
||||
if(newnet && canAccess(user))
|
||||
if(length(newnet) > 15)
|
||||
temp = "<font color = #D70B00>- FAILED: NETWORK TAG STRING TOO LENGHTLY -</font>"
|
||||
|
||||
else
|
||||
|
||||
network = newnet
|
||||
screen = 0
|
||||
servers = list()
|
||||
temp = "<font color = #336699>- NEW NETWORK TAG SET IN ADDRESS \[[network]\] -</font>"
|
||||
|
||||
updateUsrDialog()
|
||||
|
||||
/obj/machinery/computer/telecomms/traffic/attackby(var/obj/item/D as obj, var/mob/user as mob, params)
|
||||
if(istype(D, /obj/item/screwdriver))
|
||||
playsound(get_turf(src), D.usesound, 50, 1)
|
||||
if(do_after(user, 20 * D.toolspeed, target = src))
|
||||
if(src.stat & BROKEN)
|
||||
to_chat(user, "<span class='notice'>The broken glass falls out.</span>")
|
||||
var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
|
||||
new /obj/item/shard(loc)
|
||||
var/obj/item/circuitboard/comm_traffic/M = new /obj/item/circuitboard/comm_traffic( A )
|
||||
for(var/obj/C in src)
|
||||
C.loc = src.loc
|
||||
A.circuit = M
|
||||
A.state = 3
|
||||
A.icon_state = "3"
|
||||
A.anchored = 1
|
||||
qdel(src)
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You disconnect the monitor.</span>")
|
||||
var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
|
||||
var/obj/item/circuitboard/comm_traffic/M = new /obj/item/circuitboard/comm_traffic( A )
|
||||
for(var/obj/C in src)
|
||||
C.loc = src.loc
|
||||
A.circuit = M
|
||||
A.state = 4
|
||||
A.icon_state = "4"
|
||||
A.anchored = 1
|
||||
qdel(src)
|
||||
src.updateUsrDialog()
|
||||
return
|
||||
|
||||
/obj/machinery/computer/telecomms/traffic/emag_act(user as mob)
|
||||
if(!emagged)
|
||||
playsound(get_turf(src), 'sound/effects/sparks4.ogg', 75, 1)
|
||||
emagged = 1
|
||||
to_chat(user, "<span class='notice'>You you disable the security protocols</span>")
|
||||
|
||||
/obj/machinery/computer/telecomms/traffic/proc/canAccess(var/mob/user)
|
||||
if(issilicon(user) || in_range(user, src))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/machinery/computer/telecomms/traffic/proc/telecomms_check(var/mob/mob)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/telecomms_check() called tick#: [world.time]")
|
||||
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
|
||||
GLOB.ntsl2_config.Topic(user, href_list, window_id, src)
|
||||
@@ -133,7 +133,7 @@
|
||||
return
|
||||
|
||||
// Crossed didn't like people lying down.
|
||||
if(isobject(AM))
|
||||
if(isatom(AM))
|
||||
AM.loc = src.loc
|
||||
do_transform_mime(AM)
|
||||
else
|
||||
@@ -221,7 +221,7 @@
|
||||
AM.loc = src.loc
|
||||
irradiate(AM)
|
||||
|
||||
else if(isobject(AM))
|
||||
else if(isatom(AM))
|
||||
AM.loc = src.loc
|
||||
scan(AM)
|
||||
|
||||
|
||||
@@ -26,6 +26,13 @@
|
||||
investigate_log_subjects |= subject
|
||||
F << "<small>[time_stamp()] \ref[src] ([x],[y],[z])</small> || [src] [message]<br>"
|
||||
|
||||
/proc/log_investigate(message, subject)
|
||||
if(!message) return
|
||||
var/F = investigate_subject2file(subject)
|
||||
if(!F) return
|
||||
investigate_log_subjects |= subject
|
||||
F << "<small>[time_stamp()] || [message]<br>"
|
||||
|
||||
//ADMINVERBS
|
||||
/client/proc/investigate_show( subject in investigate_log_subjects )
|
||||
set name = "Investigate"
|
||||
|
||||
@@ -720,7 +720,7 @@ var/list/admin_verbs_ticket = list(
|
||||
var/list/Lines = file2list("config/admins.txt")
|
||||
for(var/line in Lines)
|
||||
var/list/splitline = splittext(line, " - ")
|
||||
if(n_lower(splitline[1]) == ckey)
|
||||
if(lowertext(splitline[1]) == ckey)
|
||||
if(splitline.len >= 2)
|
||||
rank = ckeyEx(splitline[2])
|
||||
break
|
||||
|
||||
@@ -937,25 +937,20 @@ Traitors and the like can also be revived with the previous role mostly intact.
|
||||
|
||||
/client/proc/reset_all_tcs()
|
||||
set category = "Admin"
|
||||
set name = "Reset Telecomms Scripts"
|
||||
set desc = "Blanks all telecomms scripts from all telecomms servers"
|
||||
set name = "Reset NTSL2 Configuration"
|
||||
set desc = "Resets NTSL2 to the default configuration."
|
||||
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
|
||||
var/confirm = alert(src, "You sure you want to blank all NTSL scripts?", "Confirm", "Yes", "No")
|
||||
var/confirm = alert(src, "You sure you want to reset NTSL2?", "Confirm", "Yes", "No")
|
||||
if(confirm != "Yes")
|
||||
return
|
||||
|
||||
for(var/obj/machinery/telecomms/server/S in telecomms_list)
|
||||
var/datum/TCS_Compiler/C = S.Compiler
|
||||
S.rawcode = ""
|
||||
C.Compile("")
|
||||
for(var/obj/machinery/computer/telecomms/traffic/T in GLOB.machines)
|
||||
T.storedcode = ""
|
||||
log_admin("[key_name(usr)] blanked all telecomms scripts.")
|
||||
message_admins("[key_name_admin(usr)] blanked all telecomms scripts.")
|
||||
feedback_add_details("admin_verb","RAT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
GLOB.ntsl2_config.reset()
|
||||
log_admin("[key_name(usr)] reset NTSL2 scripts.")
|
||||
message_admins("[key_name_admin(usr)] reset NTSL2 scripts.")
|
||||
feedback_add_details("admin_verb","RAT2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/list_ssds()
|
||||
set category = "Admin"
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
slots_list += "paranormal: [paranormal_slots]"
|
||||
if(cyborg_slots > 0)
|
||||
slots_list += "cyborg: [cyborg_slots]"
|
||||
var slot_text = list_implode(slots_list, ", ")
|
||||
var/slot_text = jointext(slots_list, ", ")
|
||||
notify_ghosts("An ERT is being dispatched. Open positions: [slot_text]")
|
||||
message_admins("[key_name_admin(usr)] dispatched a [ert_type] ERT. Slots: [slot_text]", 1)
|
||||
log_admin("[key_name(usr)] dispatched a [ert_type] ERT. Slots: [slot_text]")
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
File: AST Nodes
|
||||
An abstract syntax tree (AST) is a representation of source code in a computer-friendly format. It is composed of nodes,
|
||||
each of which represents a certain part of the source code. For example, an <IfStatement> node represents an if statement in the
|
||||
script's source code. Because it is a representation of the source code in memory, it is independent of any specific scripting language.
|
||||
This allows a script in any language for which a parser exists to be run by the interpreter.
|
||||
|
||||
The AST is produced by an <n_Parser> object. It consists of a <GlobalBlock> with an arbitrary amount of statements. These statements are
|
||||
run in order by an <n_Interpreter> object. A statement may in turn run another block (such as an if statement might if its condition is
|
||||
met).
|
||||
|
||||
Articles:
|
||||
- <http://en.wikipedia.org/wiki/Abstract_syntax_tree>
|
||||
*/
|
||||
/*
|
||||
Constants: Operator Precedence
|
||||
OOP_OR - Logical or
|
||||
OOP_AND - Logical and
|
||||
OOP_BIT - Bitwise operations
|
||||
OOP_EQUAL - Equality checks
|
||||
OOP_COMPARE - Greater than, less then, etc
|
||||
OOP_ADD - Addition and subtraction
|
||||
OOP_MULTIPLY - Multiplication and division
|
||||
OOP_POW - Exponents
|
||||
OOP_UNARY - Unary Operators
|
||||
OOP_GROUP - Parentheses
|
||||
*/
|
||||
/var/const/OOP_OR = 1 //||
|
||||
/var/const/OOP_AND = OOP_OR + 1 //&&
|
||||
/var/const/OOP_BIT = OOP_AND + 1 //&, |
|
||||
/var/const/OOP_EQUAL = OOP_BIT + 1 //==, !=
|
||||
/var/const/OOP_COMPARE = OOP_EQUAL + 1 //>, <, >=, <=
|
||||
/var/const/OOP_ADD = OOP_COMPARE + 1 //+, -
|
||||
/var/const/OOP_MULTIPLY= OOP_ADD + 1 //*, /, %
|
||||
/var/const/OOP_POW = OOP_MULTIPLY + 1 //^
|
||||
/var/const/OOP_UNARY = OOP_POW + 1 //!
|
||||
/var/const/OOP_GROUP = OOP_UNARY + 1 //()
|
||||
|
||||
/*
|
||||
Class: node
|
||||
*/
|
||||
/datum/node/proc/ToString()
|
||||
return "[src.type]"
|
||||
/*
|
||||
Class: identifier
|
||||
*/
|
||||
/datum/node/identifier
|
||||
var/id_name
|
||||
|
||||
/datum/node/identifier/New(id)
|
||||
. = ..()
|
||||
src.id_name = id
|
||||
|
||||
/datum/node/identifier/ToString()
|
||||
return id_name
|
||||
|
||||
/*
|
||||
Class: expression
|
||||
*/
|
||||
/datum/node/expression
|
||||
/*
|
||||
Class: operator
|
||||
See <Binary Operators> and <Unary Operators> for subtypes.
|
||||
*/
|
||||
/datum/node/expression/operator
|
||||
var/datum/node/expression/exp
|
||||
var/token = "" // Used when giving type mismatches.
|
||||
var/tmp/name
|
||||
var/tmp/precedence
|
||||
|
||||
/datum/node/expression/operator/New()
|
||||
.=..()
|
||||
if(!src.name)
|
||||
src.name = "[src.type]"
|
||||
|
||||
/datum/node/expression/operator/ToString()
|
||||
return "operator: [name]"
|
||||
|
||||
/*
|
||||
Class: FunctionCall
|
||||
*/
|
||||
/datum/node/expression/FunctionCall
|
||||
//Function calls can also be expressions or statements.
|
||||
var/func_name
|
||||
var/datum/node/identifier/object
|
||||
var/list/parameters = list()
|
||||
|
||||
/*
|
||||
Class: literal
|
||||
*/
|
||||
/datum/node/expression/value/literal
|
||||
var/value
|
||||
|
||||
/datum/node/expression/value/literal/New(value)
|
||||
. = ..()
|
||||
src.value = value
|
||||
|
||||
/datum/node/expression/value/literal/ToString()
|
||||
return src.value
|
||||
|
||||
/*
|
||||
Class: variable
|
||||
*/
|
||||
/datum/node/expression/value/variable
|
||||
var/datum/node/object //Either a node/identifier or another node/expression/value/variable which points to the object
|
||||
var/datum/node/identifier/id
|
||||
|
||||
|
||||
/datum/node/expression/value/variable/New(ident)
|
||||
. = ..()
|
||||
id = ident
|
||||
if(istext(id))
|
||||
id = new(id)
|
||||
|
||||
/datum/node/expression/value/variable/ToString()
|
||||
return src.id.ToString()
|
||||
|
||||
/*
|
||||
Class: reference
|
||||
*/
|
||||
/datum/node/expression/value/reference
|
||||
var/datum/value
|
||||
|
||||
/datum/node/expression/value/reference/New(value)
|
||||
. = ..()
|
||||
src.value = value
|
||||
|
||||
/datum/node/expression/value/reference/ToString()
|
||||
return "ref: [src.value] ([src.value.type])"
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
File: Block Types
|
||||
*/
|
||||
/*
|
||||
Class: BlockDefinition
|
||||
An object representing a set of actions to perform independently from the rest of the script. Blocks are basically just
|
||||
lists of statements to execute which also contain some local variables and methods. Note that since functions are local to a block,
|
||||
it is possible to have a function definition inside of any type of block (such as in an if statement or another function),
|
||||
and not just in the global scope as in many languages.
|
||||
*/
|
||||
/datum/node/BlockDefinition
|
||||
var/list/statements = new
|
||||
var/list/functions = new
|
||||
var/list/initial_variables = new
|
||||
|
||||
/*
|
||||
Proc: SetVar
|
||||
Defines a permanent variable. The variable will not be deleted when it goes out of scope.
|
||||
|
||||
Notes:
|
||||
Since all pre-existing temporary variables are deleted, it is not generally desirable to use this proc after the interpreter has been instantiated.
|
||||
Instead, use <n_Interpreter.SetVar()>.
|
||||
|
||||
See Also:
|
||||
- <n_Interpreter.SetVar()>
|
||||
*/
|
||||
/datum/node/BlockDefinition/proc/SetVar(name, value)
|
||||
initial_variables[name] = value
|
||||
|
||||
|
||||
/*
|
||||
Class: GlobalBlock
|
||||
A block object representing the global scope.
|
||||
*/
|
||||
//
|
||||
/datum/node/BlockDefinition/GlobalBlock/New()
|
||||
initial_variables["null"] = null
|
||||
return ..()
|
||||
|
||||
/*
|
||||
Class: FunctionBlock
|
||||
A block representing a function body.
|
||||
*/
|
||||
//
|
||||
/datum/node/BlockDefinition/FunctionBlock
|
||||
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
File: Binary Operators
|
||||
*/
|
||||
/*
|
||||
Class: binary
|
||||
Represents a binary operator in the AST. A binary operator takes two operands (ie x and y) and returns a value.
|
||||
*/
|
||||
/datum/node/expression/operator/binary
|
||||
var/datum/node/expression/exp2
|
||||
|
||||
////////// Comparison Operators //////////
|
||||
/*
|
||||
Class: Equal
|
||||
Returns true if x = y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Equal
|
||||
token = "=="
|
||||
precedence = OOP_EQUAL
|
||||
|
||||
/*
|
||||
Class: NotEqual
|
||||
Returns true if x and y aren't equal.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/NotEqual
|
||||
token = "!="
|
||||
precedence = OOP_EQUAL
|
||||
|
||||
/*
|
||||
Class: Greater
|
||||
Returns true if x > y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Greater
|
||||
token = ">"
|
||||
precedence = OOP_COMPARE
|
||||
|
||||
/*
|
||||
Class: Less
|
||||
Returns true if x < y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Less
|
||||
token = "<"
|
||||
precedence = OOP_COMPARE
|
||||
|
||||
/*
|
||||
Class: GreaterOrEqual
|
||||
Returns true if x >= y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/GreaterOrEqual
|
||||
token = ">="
|
||||
precedence = OOP_COMPARE
|
||||
|
||||
/*
|
||||
Class: LessOrEqual
|
||||
Returns true if x <= y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/LessOrEqual
|
||||
token = "<="
|
||||
precedence = OOP_COMPARE
|
||||
|
||||
|
||||
////////// Logical Operators //////////
|
||||
|
||||
/*
|
||||
Class: LogicalAnd
|
||||
Returns true if x and y are true.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/LogicalAnd
|
||||
token = "&&"
|
||||
precedence = OOP_AND
|
||||
|
||||
/*
|
||||
Class: LogicalOr
|
||||
Returns true if x, y, or both are true.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/LogicalOr
|
||||
token = "||"
|
||||
precedence = OOP_OR
|
||||
|
||||
/*
|
||||
Class: LogicalXor
|
||||
Returns true if either x or y but not both are true.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/LogicalXor //Not implemented in nS
|
||||
precedence = OOP_OR
|
||||
|
||||
|
||||
////////// Bitwise Operators //////////
|
||||
|
||||
/*
|
||||
Class: BitwiseAnd
|
||||
Performs a bitwise and operation.
|
||||
|
||||
Example:
|
||||
011 & 110 = 010
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/BitwiseAnd
|
||||
token = "&"
|
||||
precedence = OOP_BIT
|
||||
|
||||
/*
|
||||
Class: BitwiseOr
|
||||
Performs a bitwise or operation.
|
||||
|
||||
Example:
|
||||
011 | 110 = 111
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/BitwiseOr
|
||||
token = "|"
|
||||
precedence = OOP_BIT
|
||||
|
||||
/*
|
||||
Class: BitwiseXor
|
||||
Performs a bitwise exclusive or operation.
|
||||
|
||||
Example:
|
||||
011 xor 110 = 101
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/BitwiseXor
|
||||
token = "`"
|
||||
precedence = OOP_BIT
|
||||
|
||||
|
||||
////////// Arithmetic Operators //////////
|
||||
|
||||
/*
|
||||
Class: Add
|
||||
Returns the sum of x and y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Add
|
||||
token = "+"
|
||||
precedence = OOP_ADD
|
||||
|
||||
/*
|
||||
Class: Subtract
|
||||
Returns the difference of x and y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Subtract
|
||||
token = "-"
|
||||
precedence = OOP_ADD
|
||||
|
||||
/*
|
||||
Class: Multiply
|
||||
Returns the product of x and y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Multiply
|
||||
token = "*"
|
||||
precedence = OOP_MULTIPLY
|
||||
|
||||
/*
|
||||
Class: Divide
|
||||
Returns the quotient of x and y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Divide
|
||||
token = "/"
|
||||
precedence = OOP_MULTIPLY
|
||||
|
||||
/*
|
||||
Class: Power
|
||||
Returns x raised to the power of y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Power
|
||||
token = "^"
|
||||
precedence = OOP_POW
|
||||
|
||||
/*
|
||||
Class: Modulo
|
||||
Returns the remainder of x / y.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/binary/Modulo
|
||||
token = "%"
|
||||
precedence = OOP_MULTIPLY
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
File: Unary Operators
|
||||
*/
|
||||
/*
|
||||
Class: unary
|
||||
Represents a unary operator in the AST. Unary operators take a single operand (referred to as x below) and return a value.
|
||||
*/
|
||||
/datum/node/expression/operator/unary
|
||||
precedence = OOP_UNARY
|
||||
|
||||
/*
|
||||
Class: LogicalNot
|
||||
Returns !x.
|
||||
|
||||
Example:
|
||||
!true = false and !false = true
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/unary/LogicalNot
|
||||
name = "logical not"
|
||||
|
||||
/*
|
||||
Class: BitwiseNot
|
||||
Returns the value of a bitwise not operation performed on x.
|
||||
|
||||
Example:
|
||||
~10 (decimal 2) = 01 (decimal 1).
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/unary/BitwiseNot
|
||||
name = "bitwise not"
|
||||
|
||||
/*
|
||||
Class: Minus
|
||||
Returns -x.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/unary/Minus
|
||||
name = "minus"
|
||||
|
||||
/*
|
||||
Class: group
|
||||
A special unary operator representing a value in parentheses.
|
||||
*/
|
||||
//
|
||||
/datum/node/expression/operator/unary/group
|
||||
precedence = OOP_GROUP
|
||||
|
||||
/datum/node/expression/operator/unary/New(var/datum/node/expression/exp)
|
||||
src.exp = exp
|
||||
return ..()
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
File: Statement Types
|
||||
*/
|
||||
/*
|
||||
Class: statement
|
||||
An object representing a single instruction run by an interpreter.
|
||||
*/
|
||||
/datum/node/statement
|
||||
/*
|
||||
Class: FunctionCall
|
||||
Represents a call to a function.
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/FunctionCall
|
||||
var/func_name
|
||||
var/datum/node/identifier/object
|
||||
var/list/parameters = new
|
||||
|
||||
/*
|
||||
Class: FunctionDefinition
|
||||
Defines a function.
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/FunctionDefinition
|
||||
var/func_name
|
||||
var/list/parameters = new
|
||||
var/datum/node/BlockDefinition/FunctionBlock/block
|
||||
|
||||
/*
|
||||
Class: VariableAssignment
|
||||
Sets a variable in an accessible scope to the given value if one exists, otherwise initializes a new local variable to the given value.
|
||||
|
||||
Notes:
|
||||
If a variable with the same name exists in a higher block, the value will be assigned to it. If not,
|
||||
a new variable is created in the current block. To force creation of a new variable, use <VariableDeclaration>.
|
||||
|
||||
See Also:
|
||||
- <VariableDeclaration>
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/VariableAssignment
|
||||
var/datum/node/identifier/object
|
||||
var/datum/node/identifier/var_name
|
||||
var/datum/node/expression/value
|
||||
|
||||
/*
|
||||
Class: VariableDeclaration
|
||||
Intializes a local variable to a null value.
|
||||
|
||||
See Also:
|
||||
- <VariableAssignment>
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/VariableDeclaration
|
||||
var/datum/node/identifier/object
|
||||
var/datum/node/identifier/var_name
|
||||
|
||||
/*
|
||||
Class: IfStatement
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/IfStatement
|
||||
var/skip = 0
|
||||
var/datum/node/BlockDefinition/block
|
||||
var/datum/node/BlockDefinition/else_block //may be null
|
||||
var/datum/node/expression/cond
|
||||
var/datum/node/statement/else_if
|
||||
|
||||
/datum/node/statement/IfStatement/ElseIf
|
||||
|
||||
/*
|
||||
Class: WhileLoop
|
||||
Loops while a given condition is true.
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/WhileLoop
|
||||
var/datum/node/BlockDefinition/block
|
||||
var/datum/node/expression/cond
|
||||
|
||||
/*
|
||||
Class: ForLoop
|
||||
Loops while test is true, initializing a variable, increasing the variable
|
||||
*/
|
||||
/datum/node/statement/ForLoop
|
||||
var/datum/node/BlockDefinition/block
|
||||
var/datum/node/expression/test
|
||||
var/datum/node/expression/init
|
||||
var/datum/node/expression/increment
|
||||
|
||||
/*
|
||||
Class: BreakStatement
|
||||
Ends a loop.
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/BreakStatement
|
||||
|
||||
/*
|
||||
Class: ContinueStatement
|
||||
Skips to the next iteration of a loop.
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/ContinueStatement
|
||||
|
||||
/*
|
||||
Class: ReturnStatement
|
||||
Ends the function and returns a value.
|
||||
*/
|
||||
//
|
||||
/datum/node/statement/ReturnStatement
|
||||
var/datum/node/expression/value
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
File: Errors
|
||||
*/
|
||||
/*
|
||||
Class: scriptError
|
||||
An error scanning or parsing the source code.
|
||||
*/
|
||||
/datum/scriptError
|
||||
/*
|
||||
Var: message
|
||||
A message describing the problem.
|
||||
*/
|
||||
var/message
|
||||
|
||||
/datum/scriptError/New(msg = null)
|
||||
if(msg)message = msg
|
||||
|
||||
/datum/scriptError/BadToken
|
||||
message = "Unexpected token: "
|
||||
var/datum/token/token
|
||||
|
||||
/datum/scriptError/BadToken/New(datum/token/t)
|
||||
token = t
|
||||
if(t && t.line)
|
||||
message = "[t.line]: [message]"
|
||||
|
||||
if(istype(t))
|
||||
message += "[t.value]"
|
||||
|
||||
else
|
||||
message += "[t]"
|
||||
|
||||
/datum/scriptError/InvalidID
|
||||
parent_type = /datum/scriptError/BadToken
|
||||
message = "Invalid identifier name: "
|
||||
|
||||
/datum/scriptError/ReservedWord
|
||||
parent_type = /datum/scriptError/BadToken
|
||||
message = "Identifer using reserved word: "
|
||||
|
||||
/datum/scriptError/BadNumber
|
||||
parent_type = /datum/scriptError/BadToken
|
||||
message = "Bad number: "
|
||||
|
||||
/datum/scriptError/BadReturn
|
||||
var/datum/token/token
|
||||
message = "Unexpected return statement outside of a function."
|
||||
|
||||
/datum/scriptError/BadReturn/New(datum/token/t)
|
||||
src.token = t
|
||||
|
||||
/datum/scriptError/EndOfFile
|
||||
message = "Unexpected end of file."
|
||||
|
||||
/datum/scriptError/ExpectedToken
|
||||
message = "Expected: '"
|
||||
|
||||
/datum/scriptError/ExpectedToken/New(id, datum/token/T)
|
||||
if(T && T.line)
|
||||
message = "[T.line]: [message]"
|
||||
|
||||
message += "[id]'. "
|
||||
|
||||
if(T)
|
||||
message += "Found '[T.value]'."
|
||||
|
||||
|
||||
/datum/scriptError/UnterminatedComment
|
||||
message = "Unterminated multi-line comment statement: expected */"
|
||||
|
||||
/datum/scriptError/DuplicateFunction/New(name, datum/token/t)
|
||||
message = "Function '[name]' defined twice."
|
||||
|
||||
/datum/scriptError/ParameterFunction
|
||||
message = "You cannot use a function inside a parameter."
|
||||
|
||||
/datum/scriptError/ParameterFunction/New(datum/token/t)
|
||||
var/line = "?"
|
||||
if(t)
|
||||
line = t.line
|
||||
message = "[line]: [message]"
|
||||
|
||||
/*
|
||||
Class: runtimeError
|
||||
An error thrown by the interpreter in running the script.
|
||||
*/
|
||||
/datum/runtimeError
|
||||
var/name
|
||||
/*
|
||||
Var: message
|
||||
A basic description as to what went wrong.
|
||||
*/
|
||||
var/message
|
||||
var/datum/stack/stack
|
||||
/*
|
||||
Proc: ToString
|
||||
Returns a description of the error suitable for showing to the user.
|
||||
*/
|
||||
/datum/runtimeError/proc/ToString()
|
||||
. = "[name]: [message]"
|
||||
if(!stack.Top())
|
||||
return
|
||||
|
||||
. += "\nStack:"
|
||||
while(stack.Top())
|
||||
var/datum/node/statement/FunctionCall/stmt = stack.Pop()
|
||||
. += "\n\t [stmt.func_name]()"
|
||||
|
||||
/datum/runtimeError/TypeMismatch
|
||||
name = "TypeMismatchError"
|
||||
|
||||
/datum/runtimeError/TypeMismatch/New(op, a, b)
|
||||
message = "Type mismatch: '[a]' [op] '[b]'"
|
||||
|
||||
/datum/runtimeError/TypeMismatch/unary/New(op, a)
|
||||
message = "Type mismatch: [op]'[a]'"
|
||||
|
||||
/datum/runtimeError/TypeMismatch/New(op, a, b)
|
||||
message = "Type mismatch: '[a]' [op] '[b]'"
|
||||
|
||||
/datum/runtimeError/UnexpectedReturn
|
||||
name = "UnexpectedReturnError"
|
||||
message = "Unexpected return statement."
|
||||
|
||||
/datum/runtimeError/UnknownInstruction
|
||||
name = "UnknownInstructionError"
|
||||
message = "Unknown instruction type. This may be due to incompatible compiler and interpreter versions or a lack of implementation."
|
||||
|
||||
/datum/runtimeError/UndefinedVariable
|
||||
name = "UndefinedVariableError"
|
||||
|
||||
/datum/runtimeError/UndefinedVariable/New(variable)
|
||||
message = "Variable '[variable]' has not been declared."
|
||||
|
||||
/datum/runtimeError/UndefinedFunction
|
||||
name = "UndefinedFunctionError"
|
||||
|
||||
/datum/runtimeError/UndefinedFunction/New(function)
|
||||
message = "Function '[function]()' has not been defined."
|
||||
|
||||
/datum/runtimeError/DuplicateVariableDeclaration
|
||||
name = "DuplicateVariableError"
|
||||
|
||||
/datum/runtimeError/DuplicateVariableDeclaration/New(variable)
|
||||
message="Variable '[variable]' was already declared."
|
||||
|
||||
/datum/runtimeError/IterationLimitReached
|
||||
name = "MaxIterationError"
|
||||
message = "A loop has reached its maximum number of iterations."
|
||||
|
||||
/datum/runtimeError/RecursionLimitReached
|
||||
name = "MaxRecursionError"
|
||||
message = "The maximum amount of recursion has been reached."
|
||||
|
||||
/datum/runtimeError/DivisionByZero
|
||||
name = "DivideByZeroError"
|
||||
message = "Division by zero attempted."
|
||||
|
||||
/datum/runtimeError/MaxCPU
|
||||
name = "MaxComputationalUse"
|
||||
message = "Maximum amount of computational cycles reached (>= 1000)."
|
||||
|
||||
/datum/runtimeError/VectorLimit
|
||||
name = "VectorSizeOverflow"
|
||||
message = "Maximum vector size reached"
|
||||
|
||||
/datum/runtimeError/StringLimit
|
||||
name = "StringSizeOverflow"
|
||||
message = "Maximum string size reached"
|
||||
@@ -1,352 +0,0 @@
|
||||
/* --- Traffic Control Scripting Language --- */
|
||||
// Nanotrasen TCS Language - Made by Doohl
|
||||
|
||||
/datum/n_Interpreter/TCS_Interpreter
|
||||
var/datum/TCS_Compiler/Compiler
|
||||
|
||||
/datum/n_Interpreter/TCS_Interpreter/HandleError(datum/runtimeError/e)
|
||||
Compiler.Holder.add_entry(e.ToString(), "Execution Error")
|
||||
|
||||
/datum/n_Interpreter/TCS_Interpreter/GC()
|
||||
..()
|
||||
Compiler = null
|
||||
|
||||
/datum/TCS_Compiler
|
||||
var/datum/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 -- */
|
||||
|
||||
/datum/TCS_Compiler/proc/GC()
|
||||
Holder = null
|
||||
if(interpreter)
|
||||
interpreter.GC()
|
||||
|
||||
|
||||
//temp
|
||||
/datum/TCS_Compiler
|
||||
var/datum/n_scriptOptions/nS_Options/options
|
||||
var/datum/n_Scanner/nS_Scanner/scanner
|
||||
var/list/tokens
|
||||
var/datum/n_Parser/nS_Parser/parser
|
||||
var/datum/node/BlockDefinition/GlobalBlock/program
|
||||
|
||||
/* -- Compile a raw block of text -- */
|
||||
|
||||
/datum/TCS_Compiler/proc/Compile(list/code)
|
||||
options = new()
|
||||
scanner = new(code, options)
|
||||
tokens = scanner.Scan()
|
||||
parser = new(tokens, options)
|
||||
program = parser.Parse()
|
||||
|
||||
var/list/returnerrors = list()
|
||||
|
||||
returnerrors += scanner.errors
|
||||
returnerrors += parser.errors
|
||||
|
||||
if(returnerrors.len)
|
||||
return returnerrors
|
||||
|
||||
interpreter = new(program)
|
||||
interpreter.persist = 1
|
||||
interpreter.Compiler= src
|
||||
|
||||
return returnerrors
|
||||
|
||||
/* -- Execute the compiled code -- */
|
||||
|
||||
/datum/TCS_Compiler/proc/Run(var/datum/signal/signal)
|
||||
if(!ready)
|
||||
return
|
||||
|
||||
if(!interpreter)
|
||||
return
|
||||
|
||||
interpreter.container = src
|
||||
|
||||
interpreter.CreateGlobalScope() // Reset the variables.
|
||||
interpreter.curScope = interpreter.globalScope
|
||||
|
||||
interpreter.SetVar("PI", 3.141592653) // value of pi
|
||||
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)
|
||||
interpreter.SetVar("EAST", EAST) // EAST (4)
|
||||
interpreter.SetVar("WEST", WEST) // WEST (8)
|
||||
|
||||
// Channel macros
|
||||
interpreter.SetVar("$common", PUB_FREQ)
|
||||
interpreter.SetVar("$science", SCI_FREQ)
|
||||
interpreter.SetVar("$command", COMM_FREQ)
|
||||
interpreter.SetVar("$medical", MED_FREQ)
|
||||
interpreter.SetVar("$engineering", ENG_FREQ)
|
||||
interpreter.SetVar("$security", SEC_FREQ)
|
||||
interpreter.SetVar("$supply", SUP_FREQ)
|
||||
interpreter.SetVar("$service", SRV_FREQ)
|
||||
|
||||
// Signal data
|
||||
|
||||
interpreter.SetVar("$content", signal.data["message"])
|
||||
interpreter.SetVar("$freq", signal.frequency)
|
||||
interpreter.SetVar("$source", signal.data["name"])
|
||||
interpreter.SetVar("$job", signal.data["job"])
|
||||
interpreter.SetVar("$sign", signal)
|
||||
interpreter.SetVar("$pass", !(signal.data["reject"])) // if the signal isn't rejected, pass = 1; if the signal IS rejected, pass = 0
|
||||
|
||||
var/datum/language/speaking = signal.data["language"]
|
||||
if(speaking)
|
||||
interpreter.SetVar("$language", speaking.name)
|
||||
else
|
||||
interpreter.SetVar("$language", "Unknown")
|
||||
|
||||
// Set up the script procs
|
||||
|
||||
/*
|
||||
-> Send another signal to a server
|
||||
@format: broadcast(content, frequency, source, job)
|
||||
|
||||
@param content: Message to broadcast
|
||||
@param frequency: Frequency to broadcast to
|
||||
@param source: The name of the source you wish to imitate. Must be stored in stored_names list.
|
||||
@param job: The name of the job.
|
||||
*/
|
||||
interpreter.SetProc("broadcast", "tcombroadcast", signal, list("message", "freq", "source", "job"))
|
||||
|
||||
/*
|
||||
-> Store a value permanently to the server machine (not the actual game hosting machine, the ingame machine)
|
||||
@format: mem(address, value)
|
||||
|
||||
@param address: The memory address (string index) to store a value to
|
||||
@param value: The value to store to the memory address
|
||||
*/
|
||||
interpreter.SetProc("mem", "mem", signal, list("address", "value"))
|
||||
|
||||
/*
|
||||
-> Delay code for a given amount of deciseconds
|
||||
@format: sleep(time)
|
||||
|
||||
@param time: time to sleep in deciseconds (1/10th second)
|
||||
*/
|
||||
interpreter.SetProc("sleep", "delay", signal, list("time"))
|
||||
|
||||
/*
|
||||
-> Replaces a string with another string
|
||||
@format: replace(string, substring, replacestring)
|
||||
|
||||
@param string: the string to search for substrings (best used with $content$ constant)
|
||||
@param substring: the substring to search for
|
||||
@param replacestring: the string to replace the substring with
|
||||
|
||||
*/
|
||||
interpreter.SetProc("replace", /proc/n_replacetext)
|
||||
|
||||
/*
|
||||
-> Locates an element/substring inside of a list or string
|
||||
@format: find(haystack, needle, start = 1, end = 0)
|
||||
|
||||
@param haystack: the container to search
|
||||
@param needle: the element to search for
|
||||
@param start: the position to start in
|
||||
@param end: the position to end in
|
||||
|
||||
*/
|
||||
interpreter.SetProc("find", /proc/smartfind)
|
||||
|
||||
/*
|
||||
-> Finds the length of a string or list
|
||||
@format: length(container)
|
||||
|
||||
@param container: the list or container to measure
|
||||
|
||||
*/
|
||||
interpreter.SetProc("length", /proc/smartlength)
|
||||
|
||||
/* -- Clone functions, carried from default BYOND procs --- */
|
||||
|
||||
// vector namespace
|
||||
interpreter.SetProc("vector", /proc/n_list)
|
||||
interpreter.SetProc("at", /proc/n_listpos)
|
||||
interpreter.SetProc("copy", /proc/n_listcopy)
|
||||
interpreter.SetProc("push_back", /proc/n_listadd)
|
||||
interpreter.SetProc("remove", /proc/n_listremove)
|
||||
interpreter.SetProc("cut", /proc/n_listcut)
|
||||
interpreter.SetProc("swap", /proc/n_listswap)
|
||||
interpreter.SetProc("insert", /proc/n_listinsert)
|
||||
|
||||
interpreter.SetProc("pick", /proc/n_pick)
|
||||
interpreter.SetProc("prob", /proc/prob_chance)
|
||||
interpreter.SetProc("substr", /proc/docopytext)
|
||||
|
||||
interpreter.SetProc("shuffle", /proc/shuffle)
|
||||
interpreter.SetProc("uniquevector", /proc/uniquelist)
|
||||
|
||||
interpreter.SetProc("text2vector", /proc/n_splittext)
|
||||
interpreter.SetProc("vector2text", /proc/n_jointext)
|
||||
|
||||
// Donkie~
|
||||
// Strings
|
||||
interpreter.SetProc("lower", /proc/n_lower)
|
||||
interpreter.SetProc("upper", /proc/n_upper)
|
||||
interpreter.SetProc("explode", /proc/string_explode)
|
||||
interpreter.SetProc("implode", /proc/n_jointext)
|
||||
interpreter.SetProc("repeat", /proc/n_repeat)
|
||||
interpreter.SetProc("reverse", /proc/reverse_text)
|
||||
interpreter.SetProc("tonum", /proc/n_str2num)
|
||||
interpreter.SetProc("capitalize", /proc/capitalize)
|
||||
interpreter.SetProc("replacetextEx",/proc/n_replacetextEx)
|
||||
|
||||
// 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_ceiling)
|
||||
interpreter.SetProc("round", /proc/n_round)
|
||||
interpreter.SetProc("clamp", /proc/n_clamp)
|
||||
interpreter.SetProc("inrange", /proc/n_isInRange)
|
||||
interpreter.SetProc("rand", /proc/rand_chance)
|
||||
interpreter.SetProc("arctan", /proc/Atan2)
|
||||
interpreter.SetProc("lcm", /proc/n_lcm)
|
||||
interpreter.SetProc("gcd", /proc/Gcd)
|
||||
interpreter.SetProc("mean", /proc/Mean)
|
||||
interpreter.SetProc("root", /proc/n_root)
|
||||
interpreter.SetProc("sin", /proc/n_sin)
|
||||
interpreter.SetProc("cos", /proc/n_cos)
|
||||
interpreter.SetProc("arcsin", /proc/n_asin)
|
||||
interpreter.SetProc("arccos", /proc/n_acos)
|
||||
interpreter.SetProc("tan", /proc/n_tan)
|
||||
interpreter.SetProc("csc", /proc/n_csc)
|
||||
interpreter.SetProc("cot", /proc/n_cot)
|
||||
interpreter.SetProc("sec", /proc/n_sec)
|
||||
interpreter.SetProc("todegrees", /proc/n_toDegrees)
|
||||
interpreter.SetProc("toradians", /proc/n_toRadians)
|
||||
interpreter.SetProc("lerp", /proc/Lerp)
|
||||
interpreter.SetProc("max", /proc/n_max)
|
||||
interpreter.SetProc("min", /proc/n_min)
|
||||
|
||||
// End of Donkie~
|
||||
|
||||
// Time
|
||||
interpreter.SetProc("time", /proc/time)
|
||||
interpreter.SetProc("timestamp", /proc/gameTimestamp)
|
||||
|
||||
// Run the compiled code
|
||||
interpreter.Run()
|
||||
|
||||
// Backwards-apply variables onto signal data
|
||||
/* sanitize EVERYTHING. fucking players can't be trusted with SHIT */
|
||||
|
||||
/* Okay, so, the original 'sanitizing' code... did fucking nothing. Then PJB fixed it, which means no HTML.
|
||||
But I like HTML, so back to no sanitizing.*/
|
||||
|
||||
var/message = interpreter.GetVar("$content")
|
||||
var/regex/bannedTags = new ("(<script|<iframe|<video|<audio|<embed|<svg)", "i")
|
||||
if(bannedTags.Find(message)) //uh oh
|
||||
message_admins("Warning: Current Telecomms script contains banned html. Stripping message.")
|
||||
log_admin("Warning: Current Telecomms script contains banned html. Stripping message.")
|
||||
message = interpreter.GetCleanVar("$content", signal.data["message"])
|
||||
|
||||
signal.data["message"] = message
|
||||
signal.frequency = interpreter.GetCleanVar("$freq", signal.frequency)
|
||||
|
||||
var/setname = interpreter.GetVar("$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["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"])
|
||||
signal.data["reject"] = 1
|
||||
|
||||
/* -- Actual language proc code -- */
|
||||
/datum/signal/proc/delay(var/time)
|
||||
var/obj/machinery/telecomms/server/S = data["server"]
|
||||
var/datum/n_Interpreter/TCS_Interpreter/interpreter = S.Compiler.interpreter
|
||||
// Backup the scope
|
||||
var/datum/scope/globalScope = interpreter.globalScope
|
||||
var/datum/scope/curScope = interpreter.curScope
|
||||
var/list/scopes = interpreter.scopes.stack.Copy()
|
||||
var/list/functions = interpreter.functions.stack.Copy()
|
||||
var/datum/node/statement/FunctionDefinition/curFunction = interpreter.curFunction
|
||||
var/status = interpreter.status
|
||||
var/returnVal = interpreter.returnVal
|
||||
// Sleep
|
||||
sleep(time)
|
||||
// Restore the scope
|
||||
interpreter.returnVal = returnVal
|
||||
interpreter.status = status
|
||||
interpreter.curFunction = curFunction
|
||||
interpreter.functions.stack = functions
|
||||
interpreter.scopes.stack = scopes
|
||||
interpreter.curScope = curScope
|
||||
interpreter.globalScope = globalScope
|
||||
|
||||
/datum/signal/proc/mem(var/address, var/value)
|
||||
if(istext(address))
|
||||
var/obj/machinery/telecomms/server/S = data["server"]
|
||||
|
||||
if(!value && value != 0)
|
||||
return S.memory[address]
|
||||
|
||||
else
|
||||
S.memory[address] = value
|
||||
|
||||
/datum/signal/proc/tcombroadcast(var/message, var/freq, var/source, var/job)
|
||||
var/datum/signal/newsign = new
|
||||
var/obj/machinery/telecomms/server/S = data["server"]
|
||||
var/obj/item/radio/hradio = S.server_radio
|
||||
|
||||
if(!hradio)
|
||||
error("[src] has no radio.")
|
||||
return
|
||||
|
||||
if((!message || message == "") && message != 0)
|
||||
message = "*beep*"
|
||||
if(!source)
|
||||
source = "[html_encode(uppertext(S.id))]"
|
||||
hradio = new // sets the hradio as a radio intercom
|
||||
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
|
||||
|
||||
if(!job)
|
||||
job = "?"
|
||||
|
||||
newsign.data["mob"] = null
|
||||
newsign.data["mobtype"] = /mob/living/carbon/human
|
||||
newsign.data["race"] = "Automated Signal"
|
||||
newsign.data["name"] = source
|
||||
newsign.data["realname"] = newsign.data["name"]
|
||||
newsign.data["job"] = "[job]"
|
||||
newsign.data["compression"] = 0
|
||||
newsign.data["message"] = message
|
||||
newsign.data["type"] = 2 // artificial broadcast
|
||||
if(!isnum(freq))
|
||||
freq = text2num(freq)
|
||||
newsign.frequency = freq
|
||||
|
||||
var/datum/radio_frequency/connection = radio_controller.return_frequency(freq)
|
||||
newsign.data["connection"] = connection
|
||||
|
||||
|
||||
newsign.data["radio"] = hradio
|
||||
newsign.data["vmessage"] = message
|
||||
newsign.data["vname"] = source
|
||||
newsign.data["vmask"] = 0
|
||||
newsign.data["level"] = data["level"]
|
||||
|
||||
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
|
||||
@@ -1,305 +0,0 @@
|
||||
// Script -> BYOND code procs
|
||||
#define SCRIPT_MAX_REPLACEMENTS_ALLOWED 200
|
||||
// --- List operations (lists known as vectors in n_script) ---
|
||||
|
||||
// Clone of list()
|
||||
/proc/n_list()
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_list() called tick#: [world.time]")
|
||||
var/list/returnlist = list()
|
||||
for(var/e in args)
|
||||
returnlist.Add(e)
|
||||
return returnlist
|
||||
|
||||
// Clone of pick()
|
||||
/proc/n_pick()
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_pick() called tick#: [world.time]")
|
||||
var/list/finalpick = list()
|
||||
for(var/e in args)
|
||||
if(isobject(e))
|
||||
if(istype(e, /list))
|
||||
var/list/sublist = e
|
||||
for(var/sube in sublist)
|
||||
finalpick.Add(sube)
|
||||
continue
|
||||
finalpick.Add(e)
|
||||
|
||||
return pick(finalpick)
|
||||
|
||||
// Clone of list[]
|
||||
/proc/n_listpos(var/list/L, var/pos, var/value)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_listpos() called tick#: [world.time]")
|
||||
if(!istype(L, /list)) return
|
||||
if(isnum(pos))
|
||||
if(!value)
|
||||
if(L.len >= pos && !(pos > L.len))
|
||||
return L[pos]
|
||||
else
|
||||
if(L.len >= pos && !(pos > L.len))
|
||||
L[pos] = value
|
||||
else if(istext(pos))
|
||||
if(!value)
|
||||
return L[pos]
|
||||
else
|
||||
L[pos] = value
|
||||
|
||||
// Clone of list.Copy()
|
||||
/proc/n_listcopy(var/list/L, var/start, var/end)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_listcopy() called tick#: [world.time]")
|
||||
if(!istype(L, /list)) return
|
||||
return L.Copy(start, end)
|
||||
|
||||
// Clone of list.Add()
|
||||
/proc/n_listadd()
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_listadd() called tick#: [world.time]")
|
||||
var/list/chosenlist
|
||||
var/i = 1
|
||||
for(var/e in args)
|
||||
if(i == 1)
|
||||
if(isobject(e))
|
||||
if(istype(e, /list))
|
||||
chosenlist = e
|
||||
i = 2
|
||||
else
|
||||
if(chosenlist)
|
||||
chosenlist.Add(e)
|
||||
|
||||
// Clone of list.Remove()
|
||||
/proc/n_listremove()
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_listremove() called tick#: [world.time]")
|
||||
var/list/chosenlist
|
||||
var/i = 1
|
||||
for(var/e in args)
|
||||
if(i == 1)
|
||||
if(isobject(e))
|
||||
if(istype(e, /list))
|
||||
chosenlist = e
|
||||
i = 2
|
||||
else
|
||||
if(chosenlist)
|
||||
chosenlist.Remove(e)
|
||||
|
||||
// Clone of list.len = 0
|
||||
/proc/n_listcut(var/list/L, var/start, var/end)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_listcut() called tick#: [world.time]")
|
||||
if(!istype(L, /list)) return
|
||||
return L.Cut(start, end)
|
||||
|
||||
// Clone of list.Swap()
|
||||
/proc/n_listswap(var/list/L, var/firstindex, var/secondindex)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_listswap() called tick#: [world.time]")
|
||||
if(!istype(L, /list)) return
|
||||
if(L.len >= secondindex && L.len >= firstindex)
|
||||
return L.Swap(firstindex, secondindex)
|
||||
|
||||
// Clone of list.Insert()
|
||||
/proc/n_listinsert(var/list/L, var/index, var/element)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_listinsert() called tick#: [world.time]")
|
||||
if(!istype(L, /list)) return
|
||||
return L.Insert(index, element)
|
||||
|
||||
// --- Miscellaneous functions ---
|
||||
|
||||
// Clone of rand()
|
||||
/proc/rand_chance(var/low = 0, var/high)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/rand_chance() called tick#: [world.time]")
|
||||
return rand(low, high)
|
||||
|
||||
// Clone of prob()
|
||||
/proc/prob_chance(var/chance)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/prob_chance() called tick#: [world.time]")
|
||||
return prob(chance)
|
||||
|
||||
// Merge of list.Find() and findtext()
|
||||
/proc/smartfind(var/haystack, var/needle, var/start = 1, var/end = 0)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/smartfind() called tick#: [world.time]")
|
||||
if(haystack && needle)
|
||||
if(isobject(haystack))
|
||||
if(istype(haystack, /list))
|
||||
if(length(haystack) >= end && start > 0)
|
||||
var/list/listhaystack = haystack
|
||||
return listhaystack.Find(needle, start, end)
|
||||
|
||||
else
|
||||
if(istext(haystack))
|
||||
if(length(haystack) >= end && start > 0)
|
||||
return findtext(haystack, needle, start, end)
|
||||
|
||||
// Clone of copytext()
|
||||
/proc/docopytext(var/string, var/start = 1, var/end = 0)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/docopytext() called tick#: [world.time]")
|
||||
if(istext(string) && isnum(start) && isnum(end))
|
||||
if(start > 0)
|
||||
return copytext(string, start, end)
|
||||
|
||||
// Clone of length()
|
||||
/proc/smartlength(var/container)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/smartlength() called tick#: [world.time]")
|
||||
if(container)
|
||||
if(istype(container, /list) || istext(container))
|
||||
return length(container)
|
||||
return 0
|
||||
|
||||
// BY DONKIE~
|
||||
// String stuff
|
||||
/proc/n_lower(var/string)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_lower() called tick#: [world.time]")
|
||||
if(istext(string))
|
||||
return lowertext(string)
|
||||
|
||||
/proc/n_upper(var/string)
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/n_upper() called tick#: [world.time]")
|
||||
if(istext(string))
|
||||
return uppertext(string)
|
||||
|
||||
/proc/time()
|
||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/time() called tick#: [world.time]")
|
||||
return world.time + (12 HOURS)
|
||||
|
||||
proc/string_explode(var/string, var/separator = "")
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/string_explode() called tick#: [world.time]")
|
||||
if(istext(string) && (istext(separator) || isnull(separator)))
|
||||
return splittext(string, separator)
|
||||
|
||||
//Converts a list to a string
|
||||
/proc/list_implode(var/list/li, var/separator)
|
||||
if(istype(li) && (istext(separator) || isnull(separator)))
|
||||
return jointext(li, separator)
|
||||
|
||||
proc/n_repeat(var/string, var/amount)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_repeat() called tick#: [world.time]")
|
||||
if(istext(string) && isnum(amount))
|
||||
var/i
|
||||
var/newstring = ""
|
||||
if(length(newstring)*amount >=1000)
|
||||
return
|
||||
for(i=0, i<=amount, i++)
|
||||
if(i>=1000)
|
||||
break
|
||||
newstring = newstring + string
|
||||
|
||||
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)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_str2num() called tick#: [world.time]")
|
||||
if(istext(string))
|
||||
return text2num(string)
|
||||
|
||||
// Clamps N between min and max
|
||||
/proc/n_clamp(var/num, var/min = 0, var/max = 1)
|
||||
if(isnum(num) && isnum(min) && isnum(max))
|
||||
return Clamp(num, min, max)
|
||||
|
||||
// Number shit
|
||||
proc/n_num2str(var/num)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_num2str() called tick#: [world.time]")
|
||||
if(isnum(num))
|
||||
return num2text(num)
|
||||
|
||||
// Squareroot
|
||||
proc/n_sqrt(var/num)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_sqrt() called tick#: [world.time]")
|
||||
if(isnum(num))
|
||||
return sqrt(num)
|
||||
|
||||
// Magnitude of num
|
||||
proc/n_abs(var/num)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_abs() called tick#: [world.time]")
|
||||
if(isnum(num))
|
||||
return abs(num)
|
||||
|
||||
// Round down
|
||||
proc/n_floor(var/num)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_floor() called tick#: [world.time]")
|
||||
if(isnum(num))
|
||||
return Floor(num)
|
||||
|
||||
// Round up
|
||||
proc/n_ceiling(var/num)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_ceil() called tick#: [world.time]")
|
||||
if(isnum(num))
|
||||
return Ceiling(num)
|
||||
|
||||
// Round to nearest integer
|
||||
proc/n_round(var/num)
|
||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_round() called tick#: [world.time]")
|
||||
if(isnum(num))
|
||||
if(num-round(num)<0.5)
|
||||
return round(num)
|
||||
return Ceiling(num)
|
||||
|
||||
// END OF BY DONKIE :(
|
||||
|
||||
/proc/n_sin(var/const/x)
|
||||
if(isnum(x))
|
||||
return sin(x)
|
||||
|
||||
/proc/n_cos(var/const/x)
|
||||
if(isnum(x))
|
||||
return cos(x)
|
||||
|
||||
/proc/n_tan(var/const/x)
|
||||
if(isnum(x))
|
||||
return Tan(x)
|
||||
|
||||
/proc/n_csc(var/const/x)
|
||||
if(isnum(x))
|
||||
return Csc(x)
|
||||
|
||||
/proc/n_cot(var/const/x)
|
||||
if(isnum(x))
|
||||
return Cot(x)
|
||||
|
||||
/proc/n_sec(var/const/x)
|
||||
if(isnum(x))
|
||||
return Sec(x)
|
||||
|
||||
/proc/n_asin(var/const/x)
|
||||
if(isnum(x))
|
||||
return arcsin(x)
|
||||
|
||||
/proc/n_acos(var/const/x)
|
||||
if(isnum(x))
|
||||
return arccos(x)
|
||||
|
||||
/proc/n_isInRange(var/const/x, var/const/min, var/const/max)
|
||||
if(isnum(x) && isnum(min) && isnum(max))
|
||||
return IsInRange(x, min, max)
|
||||
|
||||
/proc/n_lcm(var/const/a, var/const/b)
|
||||
if(isnum(a) && isnum(b))
|
||||
return Lcm(a, b)
|
||||
|
||||
/proc/n_root(var/const/n, var/const/x)
|
||||
if(isnum(n) && isnum(x))
|
||||
return Root(n, x)
|
||||
|
||||
/proc/n_toDegrees(var/const/x)
|
||||
if(isnum(x))
|
||||
return ToDegrees(x)
|
||||
|
||||
/proc/n_toRadians(var/const/x)
|
||||
if(isnum(x))
|
||||
return ToRadians(x)
|
||||
|
||||
/proc/n_max(...)
|
||||
return max(arglist(args))
|
||||
|
||||
/proc/n_min(...)
|
||||
return min(arglist(args))
|
||||
|
||||
/proc/n_log(var/num)
|
||||
if(isnum(num) && 0 < num)
|
||||
return log(num)
|
||||
|
||||
/proc/n_replacetext(text, r, with)
|
||||
return replacetext(text, r, with)
|
||||
|
||||
/proc/n_replacetextEx(text, r, with)
|
||||
return replacetextEx(text, r, with)
|
||||
|
||||
/proc/n_jointext(list, glue)
|
||||
return jointext(list, glue)
|
||||
|
||||
/proc/n_splittext(text, delim)
|
||||
return splittext(text, delim)
|
||||
@@ -1,203 +0,0 @@
|
||||
/proc/isobject(x)
|
||||
return (istype(x, /datum) || istype(x, /list) || istype(x, /savefile) || istype(x, /client) || (x == world))
|
||||
|
||||
/datum/n_Interpreter/proc/Eval(datum/node/expression/exp)
|
||||
if(istype(exp, /datum/node/expression/FunctionCall))
|
||||
return RunFunction(exp)
|
||||
|
||||
else if(istype(exp, /datum/node/expression/operator))
|
||||
return EvalOperator(exp)
|
||||
|
||||
else if(istype(exp, /datum/node/expression/value/literal))
|
||||
var/datum/node/expression/value/literal/lit = exp
|
||||
return lit.value
|
||||
|
||||
else if(istype(exp, /datum/node/expression/value/reference))
|
||||
var/datum/node/expression/value/reference/ref = exp
|
||||
return ref.value
|
||||
|
||||
else if(istype(exp, /datum/node/expression/value/variable))
|
||||
var/datum/node/expression/value/variable/v = exp
|
||||
if(!v.object)
|
||||
return Eval(GetVariable(v.id.id_name))
|
||||
else
|
||||
var/datum/D
|
||||
if(istype(v.object, /datum/node/identifier))
|
||||
D = GetVariable(v.object:id_name)
|
||||
else
|
||||
D = v.object
|
||||
|
||||
D = Eval(D)
|
||||
if(!isobject(D))
|
||||
return null
|
||||
|
||||
if(!D.vars.Find(v.id.id_name))
|
||||
RaiseError(new/datum/runtimeError/UndefinedVariable("[v.object.ToString()].[v.id.id_name]"))
|
||||
return null
|
||||
|
||||
return Eval(D.vars[v.id.id_name])
|
||||
|
||||
else if(istype(exp, /datum/node/expression))
|
||||
RaiseError(new/datum/runtimeError/UnknownInstruction())
|
||||
|
||||
else
|
||||
return exp
|
||||
|
||||
/datum/n_Interpreter/proc/EvalOperator(datum/node/expression/operator/exp)
|
||||
if(istype(exp, /datum/node/expression/operator/binary))
|
||||
var/datum/node/expression/operator/binary/bin = exp
|
||||
try // This way we can forgo sanity in the actual evaluation (other than divide by 0).
|
||||
switch(bin.type)
|
||||
if(/datum/node/expression/operator/binary/Equal)
|
||||
return Equal(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/NotEqual)
|
||||
return NotEqual(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Greater)
|
||||
return Greater(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Less)
|
||||
return Less(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/GreaterOrEqual)
|
||||
return GreaterOrEqual(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/LessOrEqual)
|
||||
return LessOrEqual(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/LogicalAnd)
|
||||
return LogicalAnd(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/LogicalOr)
|
||||
return LogicalOr(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/LogicalXor)
|
||||
return LogicalXor(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/BitwiseAnd)
|
||||
return BitwiseAnd(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/BitwiseOr)
|
||||
return BitwiseOr(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/BitwiseXor)
|
||||
return BitwiseXor(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Add)
|
||||
return Add(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Subtract)
|
||||
return Subtract(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Multiply)
|
||||
return Multiply(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Divide)
|
||||
return Divide(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Power)
|
||||
return Power(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
if(/datum/node/expression/operator/binary/Modulo)
|
||||
return Modulo(Eval(bin.exp), Eval(bin.exp2))
|
||||
|
||||
else
|
||||
RaiseError(new/datum/runtimeError/UnknownInstruction())
|
||||
|
||||
catch
|
||||
RaiseError(new/datum/runtimeError/TypeMismatch(bin.token, Eval(bin.exp), Eval(bin.exp2)))
|
||||
|
||||
else
|
||||
try
|
||||
switch(exp.type)
|
||||
if(/datum/node/expression/operator/unary/Minus)
|
||||
return Minus(Eval(exp.exp))
|
||||
|
||||
if(/datum/node/expression/operator/unary/LogicalNot)
|
||||
return LogicalNot(Eval(exp.exp))
|
||||
|
||||
if(/datum/node/expression/operator/unary/BitwiseNot)
|
||||
return BitwiseNot(Eval(exp.exp))
|
||||
|
||||
if(/datum/node/expression/operator/unary/group)
|
||||
return Eval(exp.exp)
|
||||
|
||||
else
|
||||
RaiseError(new/datum/runtimeError/UnknownInstruction())
|
||||
catch
|
||||
RaiseError(new/datum/runtimeError/TypeMismatch/unary(exp.token, Eval(exp.exp)))
|
||||
|
||||
//Binary//
|
||||
//Comparison operators
|
||||
/datum/n_Interpreter/proc/Equal(a, b)
|
||||
return a == b
|
||||
|
||||
/datum/n_Interpreter/proc/NotEqual(a, b)
|
||||
return a != b //LogicalNot(Equal(a, b))
|
||||
|
||||
/datum/n_Interpreter/proc/Greater(a, b)
|
||||
return a > b
|
||||
|
||||
/datum/n_Interpreter/proc/Less(a, b)
|
||||
return a < b
|
||||
|
||||
/datum/n_Interpreter/proc/GreaterOrEqual(a, b)
|
||||
return a >= b
|
||||
|
||||
/datum/n_Interpreter/proc/LessOrEqual(a, b)
|
||||
return a <= b
|
||||
|
||||
//Logical Operators
|
||||
/datum/n_Interpreter/proc/LogicalAnd(a, b)
|
||||
return a && b
|
||||
|
||||
/datum/n_Interpreter/proc/LogicalOr(a, b)
|
||||
return a || b
|
||||
|
||||
/datum/n_Interpreter/proc/LogicalXor(a, b)
|
||||
return (a || b) && !(a && b)
|
||||
|
||||
//Bitwise Operators
|
||||
/datum/n_Interpreter/proc/BitwiseAnd(a, b)
|
||||
return a & b
|
||||
|
||||
/datum/n_Interpreter/proc/BitwiseOr(a, b)
|
||||
return a | b
|
||||
|
||||
/datum/n_Interpreter/proc/BitwiseXor(a, b)
|
||||
return a ^ b
|
||||
|
||||
//Arithmetic Operators
|
||||
/datum/n_Interpreter/proc/Add(a, b)
|
||||
return a + b
|
||||
|
||||
/datum/n_Interpreter/proc/Subtract(a, b)
|
||||
return a - b
|
||||
|
||||
/datum/n_Interpreter/proc/Divide(a, b)
|
||||
if(b == 0)
|
||||
RaiseError(new/datum/runtimeError/DivisionByZero())
|
||||
return null
|
||||
|
||||
return a / b
|
||||
|
||||
/datum/n_Interpreter/proc/Multiply(a, b)
|
||||
return a * b
|
||||
|
||||
/datum/n_Interpreter/proc/Modulo(a, b)
|
||||
|
||||
return a % b
|
||||
|
||||
/datum/n_Interpreter/proc/Power(a, b)
|
||||
return a ** b
|
||||
|
||||
//Unary//
|
||||
/datum/n_Interpreter/proc/Minus(a)
|
||||
return -a
|
||||
|
||||
/datum/n_Interpreter/proc/LogicalNot(a)
|
||||
return !a
|
||||
|
||||
/datum/n_Interpreter/proc/BitwiseNot(a)
|
||||
return ~a
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
File: Interpreter (Public)
|
||||
Contains methods for interacting with the interpreter.
|
||||
*/
|
||||
/*
|
||||
Class: n_Interpreter
|
||||
Procedures allowing for interaction with the script that is being run by the interpreter object.
|
||||
*/
|
||||
|
||||
/*
|
||||
Proc: Load
|
||||
Loads a 'compiled' script into memory.
|
||||
|
||||
Parameters:
|
||||
program - A <GlobalBlock> object which represents the script's global scope.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/Load(var/datum/node/BlockDefinition/GlobalBlock/program)
|
||||
ASSERT(program)
|
||||
src.program = program
|
||||
CreateGlobalScope()
|
||||
alertadmins = 0 // reset admin alerts
|
||||
|
||||
/*
|
||||
Proc: Run
|
||||
Runs the script.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/Run()
|
||||
cur_recursion = 0 // reset recursion
|
||||
cur_statements = 0 // reset CPU tracking
|
||||
|
||||
ASSERT(src.program)
|
||||
RunBlock(src.program)
|
||||
|
||||
/*
|
||||
Proc: SetVar
|
||||
Defines a global variable for the duration of the next execution of a script.
|
||||
|
||||
Notes:
|
||||
This differs from <Block.SetVar()> in that variables set using this procedure only last for the session,
|
||||
while those defined from the block object persist if it is ran multiple times.
|
||||
|
||||
See Also:
|
||||
- <Block.SetVar()>
|
||||
*/
|
||||
/datum/n_Interpreter/proc/SetVar(name, value)
|
||||
if(!istext(name))
|
||||
//CRASH("Invalid variable name")
|
||||
return
|
||||
|
||||
AssignVariable(name, value)
|
||||
|
||||
/*
|
||||
Proc: SetProc
|
||||
Defines a procedure to be available to the script.
|
||||
|
||||
Parameters:
|
||||
name - The name of the procedure as exposed to the script.
|
||||
path - The typepath of a proc to be called when the function call is read by the interpreter, or, if object is specified, a string representing the procedure's name.
|
||||
object - (Optional) An object which will the be target of a function call.
|
||||
params - Only required if object is not null, a list of the names of parameters the proc takes.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/SetProc(name, path, object = null, list/params = null)
|
||||
if(!istext(name))
|
||||
//CRASH("Invalid function name")
|
||||
return
|
||||
|
||||
if(!object)
|
||||
globalScope.functions[name] = path
|
||||
|
||||
else
|
||||
var/datum/node/statement/FunctionDefinition/S = new()
|
||||
S.func_name = name
|
||||
S.parameters = params
|
||||
S.block = new()
|
||||
S.block.SetVar("src", object)
|
||||
var/datum/node/expression/FunctionCall/C = new()
|
||||
C.func_name = path
|
||||
C.object = new("src")
|
||||
for(var/p in params)
|
||||
C.parameters += new/datum/node/expression/value/variable(p)
|
||||
|
||||
var/datum/node/statement/ReturnStatement/R = new()
|
||||
R.value = C
|
||||
S.block.statements += R
|
||||
globalScope.functions[name] = S
|
||||
/*
|
||||
Proc: VarExists
|
||||
Checks whether a global variable with the specified name exists.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/VarExists(name)
|
||||
return globalScope.variables.Find(name) //convert to 1/0 first?
|
||||
|
||||
/*
|
||||
Proc: ProcExists
|
||||
Checks whether a global function with the specified name exists.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/ProcExists(name)
|
||||
return globalScope.functions.Find(name)
|
||||
|
||||
/*
|
||||
Proc: GetVar
|
||||
Returns the value of a global variable in the script. Remember to ensure that the variable exists before calling this procedure.
|
||||
|
||||
See Also:
|
||||
- <VarExists()>
|
||||
*/
|
||||
/datum/n_Interpreter/proc/GetVar(name)
|
||||
if(!VarExists(name))
|
||||
//CRASH("No variable named '[name]'.")
|
||||
return
|
||||
var/x = globalScope.variables[name]
|
||||
return Eval(x)
|
||||
|
||||
/*
|
||||
Proc: GetCleanVar
|
||||
Returns the value of a global variable in the script and cleans it (sanitizes).
|
||||
*/
|
||||
|
||||
/datum/n_Interpreter/proc/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
|
||||
exists before calling this procedure.
|
||||
|
||||
See Also:
|
||||
- <ProcExists()>
|
||||
*/
|
||||
/datum/n_Interpreter/proc/CallProc(name, params[]=null)
|
||||
if(!ProcExists(name))
|
||||
//CRASH("No function named '[name]'.")
|
||||
return
|
||||
|
||||
var/datum/node/statement/FunctionDefinition/func = globalScope.functions[name]
|
||||
|
||||
if(istype(func))
|
||||
var/datum/node/statement/FunctionCall/stmt = new
|
||||
stmt.func_name = func.func_name
|
||||
stmt.parameters = params
|
||||
return RunFunction(stmt)
|
||||
|
||||
else
|
||||
return call(func)(arglist(params))
|
||||
//CRASH("Unknown function type '[name]'.")
|
||||
|
||||
/*
|
||||
Event: HandleError
|
||||
Called when the interpreter throws a runtime error.
|
||||
|
||||
See Also:
|
||||
- <runtimeError>
|
||||
*/
|
||||
/datum/n_Interpreter/proc/HandleError(var/datum/runtimeError/e)
|
||||
@@ -1,380 +0,0 @@
|
||||
/*
|
||||
File: Interpreter (Internal)
|
||||
*/
|
||||
/*
|
||||
Class: n_Interpreter
|
||||
*/
|
||||
/*
|
||||
Macros: Status Macros
|
||||
RETURNING - Indicates that the current function is returning a value.
|
||||
BREAKING - Indicates that the current loop is being terminated.
|
||||
CONTINUING - Indicates that the rest of the current iteration of a loop is being skipped.
|
||||
*/
|
||||
#define RETURNING 1
|
||||
#define BREAKING 2
|
||||
#define CONTINUING 4
|
||||
|
||||
/datum/n_Interpreter
|
||||
var/datum/scope/curScope
|
||||
var/datum/scope/globalScope
|
||||
|
||||
var/datum/node/BlockDefinition/program
|
||||
var/datum/node/statement/FunctionDefinition/curFunction
|
||||
var/datum/stack/scopes = new()
|
||||
var/datum/stack/functions = new()
|
||||
|
||||
var/datum/container // associated container for interpeter
|
||||
/*
|
||||
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>.
|
||||
*/
|
||||
var/status = 0
|
||||
var/returnVal
|
||||
|
||||
var/max_statements = 900 // maximum amount of statements that can be called in one execution. this is to prevent massive crashes and exploitation
|
||||
var/cur_statements = 0 // current amount of statements called
|
||||
var/alertadmins = 0 // set to 1 if the admins shouldn't be notified of anymore issues
|
||||
var/max_iterations = 100 // max number of uninterrupted loops possible
|
||||
var/max_recursion = 10 // max recursions without returning anything (or completing the code block)
|
||||
var/cur_recursion = 0 // current amount of recursion
|
||||
/*
|
||||
Var: persist
|
||||
If 0, global variables will be reset after Run() finishes.
|
||||
*/
|
||||
var/persist = 1
|
||||
var/paused = 0
|
||||
|
||||
/*
|
||||
Constructor: New
|
||||
Calls <Load()> with the given parameters.
|
||||
*/
|
||||
/datum/n_Interpreter/New(datum/node/BlockDefinition/GlobalBlock/program = null)
|
||||
. = ..()
|
||||
if(program)
|
||||
Load(program)
|
||||
|
||||
/*
|
||||
Set ourselves to Garbage Collect
|
||||
*/
|
||||
/datum/n_Interpreter/proc/GC()
|
||||
..()
|
||||
container = null
|
||||
|
||||
/*
|
||||
Proc: RaiseError
|
||||
Raises a runtime error.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/RaiseError(datum/runtimeError/e)
|
||||
e.stack = functions.Copy()
|
||||
e.stack.Push(curFunction)
|
||||
src.HandleError(e)
|
||||
|
||||
/datum/n_Interpreter/proc/CreateScope(datum/node/BlockDefinition/B)
|
||||
var/datum/scope/S = new(B, curScope)
|
||||
scopes.Push(curScope)
|
||||
curScope = S
|
||||
return S
|
||||
|
||||
/datum/n_Interpreter/proc/CreateGlobalScope()
|
||||
scopes.Clear()
|
||||
var/datum/scope/S = new(program, null)
|
||||
globalScope = S
|
||||
return S
|
||||
|
||||
/*
|
||||
Proc: AlertAdmins
|
||||
Alerts the admins of a script that is bad.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/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.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/RunBlock(var/datum/node/BlockDefinition/Block, var/datum/scope/scope = null)
|
||||
var/is_global = istype(Block, /datum/node/BlockDefinition/GlobalBlock)
|
||||
if(!is_global)
|
||||
if(scope)
|
||||
curScope = scope
|
||||
else
|
||||
CreateScope(Block)
|
||||
else
|
||||
if(!persist)
|
||||
CreateGlobalScope()
|
||||
|
||||
curScope = globalScope
|
||||
|
||||
if(cur_statements < max_statements)
|
||||
for(var/datum/node/statement/S in Block.statements)
|
||||
while(paused) sleep(10)
|
||||
|
||||
cur_statements++
|
||||
if(cur_statements >= max_statements)
|
||||
RaiseError(new/datum/runtimeError/MaxCPU())
|
||||
AlertAdmins()
|
||||
break
|
||||
|
||||
if(istype(S, /datum/node/statement/VariableAssignment))
|
||||
var/datum/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, /datum/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/datum/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, /datum/node/statement/FunctionCall))
|
||||
RunFunction(S)
|
||||
|
||||
else if(istype(S, /datum/node/statement/FunctionDefinition))
|
||||
//do nothing
|
||||
|
||||
else if(istype(S, /datum/node/statement/WhileLoop))
|
||||
RunWhile(S)
|
||||
|
||||
else if(istype(S, /datum/node/statement/IfStatement))
|
||||
RunIf(S)
|
||||
|
||||
else if(istype(S, /datum/node/statement/ReturnStatement))
|
||||
if(!curFunction)
|
||||
RaiseError(new/datum/runtimeError/UnexpectedReturn())
|
||||
continue
|
||||
|
||||
status |= RETURNING
|
||||
returnVal = Eval(S:value)
|
||||
break
|
||||
|
||||
else if(istype(S, /datum/node/statement/BreakStatement))
|
||||
status |= BREAKING
|
||||
break
|
||||
|
||||
else if(istype(S, /datum/node/statement/ContinueStatement))
|
||||
status |= CONTINUING
|
||||
break
|
||||
|
||||
else
|
||||
RaiseError(new/datum/runtimeError/UnknownInstruction())
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
if(status)
|
||||
break
|
||||
|
||||
curScope = scopes.Pop()
|
||||
|
||||
/*
|
||||
Proc: RunFunction
|
||||
Runs a function block or a proc with the arguments specified in the script.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/RunFunction(var/datum/node/statement/FunctionCall/stmt)
|
||||
//Note that anywhere /datum/node/statement/FunctionCall/stmt is used so may /datum/node/expression/FunctionCall
|
||||
|
||||
// If recursion gets too high (max 50 nested functions) throw an error
|
||||
if(cur_recursion >= max_recursion)
|
||||
AlertAdmins()
|
||||
RaiseError(new/datum/runtimeError/RecursionLimitReached())
|
||||
return 0
|
||||
|
||||
var/datum/node/statement/FunctionDefinition/def
|
||||
if(!stmt.object) //A scope's function is being called, stmt.object is null
|
||||
def = GetFunction(stmt.func_name)
|
||||
|
||||
else if(istype(stmt.object)) //A method of an object exposed as a variable is being called, stmt.object is a /node/identifier
|
||||
var/O = GetVariable(stmt.object.id_name) //Gets a reference to the object which is the target of the function call.
|
||||
if(!O) return //Error already thrown in GetVariable()
|
||||
def = Eval(O)
|
||||
|
||||
if(!def)
|
||||
return
|
||||
|
||||
cur_recursion++ // add recursion
|
||||
if(istype(def))
|
||||
if(curFunction) functions.Push(curFunction)
|
||||
var/datum/scope/S = CreateScope(def.block)
|
||||
|
||||
for(var/i = 1 to def.parameters.len)
|
||||
var/val
|
||||
if(stmt.parameters.len >= i)
|
||||
val = stmt.parameters[i]
|
||||
//else
|
||||
// unspecified param
|
||||
AssignVariable(def.parameters[i], new/datum/node/expression/value/literal(Eval(val)), S)
|
||||
|
||||
curFunction = stmt
|
||||
RunBlock(def.block, S)
|
||||
|
||||
//Handle return value
|
||||
. = returnVal
|
||||
status &= ~RETURNING
|
||||
returnVal = null
|
||||
curFunction = functions.Pop()
|
||||
cur_recursion--
|
||||
|
||||
else
|
||||
cur_recursion--
|
||||
var/list/params = new
|
||||
for(var/datum/node/expression/P in stmt.parameters)
|
||||
params += list(Eval(P))
|
||||
|
||||
if(isobject(def)) //def is an object which is the target of a function call
|
||||
if(!hascall(def, stmt.func_name))
|
||||
RaiseError(new/datum/runtimeError/UndefinedFunction("[stmt.object.id_name].[stmt.func_name]"))
|
||||
return
|
||||
|
||||
return call(def, stmt.func_name)(arglist(params))
|
||||
|
||||
else //def is a path to a global proc
|
||||
return call(def)(arglist(params))
|
||||
//else
|
||||
// RaiseError(new/runtimeError/UnknownInstruction())
|
||||
|
||||
/*
|
||||
Proc: RunIf
|
||||
Checks a condition and runs either the if block or else block.
|
||||
*/
|
||||
/datum/n_Interpreter/proc/RunIf(var/datum/node/statement/IfStatement/stmt)
|
||||
if(!stmt.skip)
|
||||
if(Eval(stmt.cond))
|
||||
RunBlock(stmt.block)
|
||||
// Loop through the if else chain and tell them to be skipped.
|
||||
var/datum/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
|
||||
Runs a while loop.
|
||||
*/
|
||||
|
||||
/datum/n_Interpreter/proc/RunWhile(var/datum/node/statement/WhileLoop/stmt)
|
||||
var/i = 1
|
||||
while(Eval(stmt.cond) && Iterate(stmt.block, i++))
|
||||
continue
|
||||
|
||||
status &= ~BREAKING
|
||||
|
||||
/*
|
||||
Proc:Iterate
|
||||
Runs a single iteration of a loop. Returns a value indicating whether or not to continue looping.
|
||||
*/
|
||||
|
||||
/datum/n_Interpreter/proc/Iterate(var/datum/node/BlockDefinition/block, count)
|
||||
RunBlock(block)
|
||||
if(max_iterations > 0 && count >= max_iterations)
|
||||
RaiseError(new/datum/runtimeError/IterationLimitReached())
|
||||
return 0
|
||||
|
||||
if(status & (BREAKING|RETURNING))
|
||||
return 0
|
||||
|
||||
status &= ~CONTINUING
|
||||
return 1
|
||||
|
||||
/*
|
||||
Proc: GetFunction
|
||||
Finds a function in an accessible scope with the given name. Returns a <FunctionDefinition>.
|
||||
*/
|
||||
|
||||
/datum/n_Interpreter/proc/GetFunction(name)
|
||||
var/datum/scope/S = curScope
|
||||
while(S)
|
||||
if(S.functions.Find(name))
|
||||
return S.functions[name]
|
||||
S = S.parent
|
||||
|
||||
RaiseError(new/datum/runtimeError/UndefinedFunction(name))
|
||||
|
||||
/*
|
||||
Proc: GetVariable
|
||||
Finds a variable in an accessible scope and returns its value.
|
||||
*/
|
||||
|
||||
/datum/n_Interpreter/proc/GetVariable(name)
|
||||
var/datum/scope/S = curScope
|
||||
while(S)
|
||||
if(S.variables.Find(name))
|
||||
return S.variables[name]
|
||||
S = S.parent
|
||||
|
||||
RaiseError(new/datum/runtimeError/UndefinedVariable(name))
|
||||
|
||||
/datum/n_Interpreter/proc/GetVariableScope(name) //needed for when you reassign a variable in a higher scope
|
||||
var/datum/scope/S = curScope
|
||||
while(S)
|
||||
if(S.variables.Find(name))
|
||||
return S
|
||||
|
||||
S = S.parent
|
||||
|
||||
|
||||
/datum/n_Interpreter/proc/IsVariableAccessible(name)
|
||||
var/datum/scope/S = curScope
|
||||
while(S)
|
||||
if(S.variables.Find(name))
|
||||
return TRUE
|
||||
S = S.parent
|
||||
|
||||
return FALSE
|
||||
|
||||
|
||||
/*
|
||||
Proc: AssignVariable
|
||||
Assigns a value to a variable in a specific block.
|
||||
|
||||
Parameters:
|
||||
name - The name of the variable to assign.
|
||||
value - The value to assign to it.
|
||||
S - The scope the variable resides in. If it is null, a scope with the variable already existing is found. If no scopes have a variable of the given name, the current scope is used.
|
||||
*/
|
||||
|
||||
/datum/n_Interpreter/proc/AssignVariable(name, datum/node/expression/value, var/datum/scope/S = null)
|
||||
if(!S) S = GetVariableScope(name)
|
||||
if(!S) S = curScope
|
||||
if(!S) S = globalScope
|
||||
|
||||
ASSERT(istype(S))
|
||||
if(istext(value) || isnum(value) || isnull(value)) value = new/datum/node/expression/value/literal(value)
|
||||
else if(!istype(value) && isobject(value)) value = new/datum/node/expression/value/reference(value)
|
||||
//TODO: check for invalid name
|
||||
S.variables["[name]"] = value
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
Class: scope
|
||||
A runtime instance of a block. Used internally by the interpreter.
|
||||
*/
|
||||
/datum/scope
|
||||
var/datum/scope/parent = null
|
||||
var/datum/node/BlockDefinition/block
|
||||
var/list/functions
|
||||
var/list/variables
|
||||
|
||||
/datum/scope/New(var/datum/node/BlockDefinition/B, var/datum/scope/parent)
|
||||
src.block = B
|
||||
src.parent = parent
|
||||
src.variables = B.initial_variables.Copy()
|
||||
src.functions = B.functions.Copy()
|
||||
. = ..()
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
File: Options
|
||||
*/
|
||||
// Ascii values of characters
|
||||
/var/const/ascii_A = 65
|
||||
/var/const/ascii_Z = 90
|
||||
/var/const/ascii_a = 97
|
||||
/var/const/ascii_z = 122
|
||||
/var/const/ascii_DOLLAR = 36 // $
|
||||
/var/const/ascii_ZERO = 48
|
||||
/var/const/ascii_NINE = 57
|
||||
/var/const/ascii_UNDERSCORE = 95 // _
|
||||
|
||||
/*
|
||||
Class: n_scriptOptions
|
||||
*/
|
||||
/datum/n_scriptOptions/proc/CanStartID(char) //returns true if the character can start a variable, function, or keyword name (by default letters or an underscore)
|
||||
if(!isnum(char))
|
||||
char = text2ascii(char)
|
||||
|
||||
return (char in ascii_A to ascii_Z) || (char in ascii_a to ascii_z) || char == ascii_UNDERSCORE || char == ascii_DOLLAR
|
||||
|
||||
/datum/n_scriptOptions/proc/IsValidIDChar(char) //returns true if the character can be in the body of a variable, function, or keyword name (by default letters, numbers, and underscore)
|
||||
if(!isnum(char))
|
||||
char = text2ascii(char)
|
||||
|
||||
return CanStartID(char) || IsDigit(char)
|
||||
|
||||
/datum/n_scriptOptions/proc/IsDigit(char)
|
||||
if(!isnum(char))
|
||||
char = text2ascii(char)
|
||||
|
||||
return char in ascii_ZERO to ascii_NINE
|
||||
|
||||
/datum/n_scriptOptions/proc/IsValidID(id) //returns true if all the characters in the string are okay to be in an identifier name
|
||||
if(!CanStartID(id)) //don't need to grab first char in id, since text2ascii does it automatically
|
||||
return 0
|
||||
|
||||
var/list/charmap = string2charlist(id)
|
||||
if(charmap.len == 1)
|
||||
return 1
|
||||
|
||||
for(var/i = 2 to charmap.len)
|
||||
if(!IsValidIDChar(charmap[i]))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/*
|
||||
Class: nS_Options
|
||||
An implementation of <n_scriptOptions> for the n_Script language.
|
||||
*/
|
||||
/datum/n_scriptOptions/nS_Options
|
||||
var/list/symbols = list(
|
||||
"(",
|
||||
")",
|
||||
"\[",
|
||||
"]",
|
||||
";",
|
||||
",",
|
||||
"{",
|
||||
"}"
|
||||
) //scanner - Characters that can be in symbols
|
||||
/*
|
||||
Var: keywords
|
||||
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.
|
||||
*/
|
||||
var/list/keywords = list(
|
||||
"if" = /datum/n_Keyword/nS_Keyword/kwIf,
|
||||
"else" = /datum/n_Keyword/nS_Keyword/kwElse,
|
||||
"elseif" = /datum/n_Keyword/nS_Keyword/kwElseIf,
|
||||
"while" = /datum/n_Keyword/nS_Keyword/kwWhile,
|
||||
"break" = /datum/n_Keyword/nS_Keyword/kwBreak,
|
||||
"continue" = /datum/n_Keyword/nS_Keyword/kwContinue,
|
||||
"return" = /datum/n_Keyword/nS_Keyword/kwReturn,
|
||||
"def" = /datum/n_Keyword/nS_Keyword/kwDef
|
||||
)
|
||||
|
||||
var/list/assign_operators = list(
|
||||
"=" = null,
|
||||
"&=" = "&",
|
||||
"|=" = "|",
|
||||
"`=" = "`",
|
||||
"+=" = "+",
|
||||
"-=" = "-",
|
||||
"*=" = "*",
|
||||
"/=" = "/",
|
||||
"^=" = "^",
|
||||
"%=" = "%"
|
||||
)
|
||||
|
||||
var/list/unary_operators = list(
|
||||
"!" = /datum/node/expression/operator/unary/LogicalNot,
|
||||
"~" = /datum/node/expression/operator/unary/BitwiseNot,
|
||||
"-" = /datum/node/expression/operator/unary/Minus
|
||||
)
|
||||
|
||||
var/list/binary_operators = list(
|
||||
"==" = /datum/node/expression/operator/binary/Equal,
|
||||
"!=" = /datum/node/expression/operator/binary/NotEqual,
|
||||
">" = /datum/node/expression/operator/binary/Greater,
|
||||
"<" = /datum/node/expression/operator/binary/Less,
|
||||
">=" = /datum/node/expression/operator/binary/GreaterOrEqual,
|
||||
"<=" = /datum/node/expression/operator/binary/LessOrEqual,
|
||||
"&&" = /datum/node/expression/operator/binary/LogicalAnd,
|
||||
"||" = /datum/node/expression/operator/binary/LogicalOr,
|
||||
"&" = /datum/node/expression/operator/binary/BitwiseAnd,
|
||||
"|" = /datum/node/expression/operator/binary/BitwiseOr,
|
||||
"`" = /datum/node/expression/operator/binary/BitwiseXor,
|
||||
"+" = /datum/node/expression/operator/binary/Add,
|
||||
"-" = /datum/node/expression/operator/binary/Subtract,
|
||||
"*" = /datum/node/expression/operator/binary/Multiply,
|
||||
"/" = /datum/node/expression/operator/binary/Divide,
|
||||
"^" = /datum/node/expression/operator/binary/Power,
|
||||
"%" = /datum/node/expression/operator/binary/Modulo
|
||||
)
|
||||
|
||||
/datum/n_scriptOptions/nS_Options/New()
|
||||
. = ..()
|
||||
for(var/O in assign_operators + binary_operators + unary_operators)
|
||||
if(!symbols.Find(O))
|
||||
symbols += O
|
||||
@@ -1,371 +0,0 @@
|
||||
/*
|
||||
File: Expressions
|
||||
Procedures for parsing expressions.
|
||||
*/
|
||||
|
||||
/*
|
||||
Macros: Expression Macros
|
||||
OPERATOR - A value indicating the parser currently expects a binary operator.
|
||||
VALUE - A value indicating the parser currently expects a value.
|
||||
SHIFT - Tells the parser to push the current operator onto the stack.
|
||||
REDUCE - Tells the parser to reduce the stack.
|
||||
*/
|
||||
#define OPERATOR 1
|
||||
#define VALUE 2
|
||||
#define SHIFT 0
|
||||
#define REDUCE 1
|
||||
|
||||
/*
|
||||
Class: nS_Parser
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser
|
||||
/*
|
||||
Var: expecting
|
||||
A variable which keeps track of whether an operator or value is expected. It should be either <OPERATOR> or <VALUE>. See <ParseExpression()>
|
||||
for more information.
|
||||
*/
|
||||
var/expecting = VALUE
|
||||
|
||||
/*
|
||||
Proc: Precedence
|
||||
Compares two operators, decides which is higher in the order of operations, and returns <SHIFT> or <REDUCE>.
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/Precedence(var/datum/node/expression/operator/top, var/datum/node/expression/operator/input)
|
||||
if(istype(top))
|
||||
top = top.precedence
|
||||
|
||||
if(istype(input))
|
||||
|
||||
input = input:precedence
|
||||
|
||||
if(top >= input)
|
||||
return REDUCE
|
||||
|
||||
return SHIFT
|
||||
|
||||
/*
|
||||
Proc: GetExpression
|
||||
Takes a token expected to represent a value and returns an <expression> node.
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/GetExpression(var/datum/token/T)
|
||||
if(!T)
|
||||
return
|
||||
|
||||
if(istype(T, /datum/node/expression))
|
||||
return T
|
||||
|
||||
switch(T.type)
|
||||
if(/datum/token/word)
|
||||
return new/datum/node/expression/value/variable(T.value)
|
||||
|
||||
if(/datum/token/accessor)
|
||||
var/datum/token/accessor/A = T
|
||||
var/datum/node/expression/value/variable/E// = new(A.member)
|
||||
var/datum/stack/S = new()
|
||||
|
||||
while(istype(A.object, /datum/token/accessor))
|
||||
S.Push(A)
|
||||
A = A.object
|
||||
|
||||
ASSERT(istext(A.object))
|
||||
|
||||
while(A)
|
||||
var/datum/node/expression/value/variable/V = new()
|
||||
V.id = new(A.member)
|
||||
if(E)
|
||||
V.object = E
|
||||
else
|
||||
V.object = new/datum/node/identifier(A.object)
|
||||
|
||||
E = V
|
||||
A = S.Pop()
|
||||
return E
|
||||
|
||||
if(/datum/token/number, /datum/token/string)
|
||||
return new/datum/node/expression/value/literal(T.value)
|
||||
|
||||
/*
|
||||
Proc: GetOperator
|
||||
Gets a path related to a token or string and returns an instance of the given type. This is used to get an instance of either a binary or unary
|
||||
operator from a token.
|
||||
|
||||
Parameters:
|
||||
O - The input value. If this is a token, O is reset to the token's value.
|
||||
When O is a string and is in L, its associated value is used as the path to instantiate.
|
||||
type - The desired type of the returned object.
|
||||
L - The list in which to search for O.
|
||||
|
||||
See Also:
|
||||
- <GetBinaryOperator()>
|
||||
- <GetUnaryOperator()>
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/GetOperator(O, type = /datum/node/expression/operator, L[])
|
||||
if(istype(O, type))
|
||||
return O //O is already the desired type
|
||||
|
||||
if(istype(O, /datum/token))
|
||||
O = O:value //sets O to text
|
||||
|
||||
if(istext(O)) //sets O to path
|
||||
if(L.Find(O))
|
||||
O = L[O]
|
||||
else
|
||||
return null
|
||||
|
||||
if(ispath(O))
|
||||
O = new O //catches path from last check
|
||||
|
||||
else
|
||||
return null //Unknown type
|
||||
|
||||
return O
|
||||
|
||||
/*
|
||||
Proc: GetBinaryOperator
|
||||
Uses <GetOperator()> to search for an instance of a binary operator type with which the given string is associated. For example, if
|
||||
O is set to "+", an <Add> node is returned.
|
||||
|
||||
See Also:
|
||||
- <GetOperator()>
|
||||
- <GetUnaryOperator()>
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/GetBinaryOperator(O)
|
||||
return GetOperator(O, /datum/node/expression/operator/binary, options.binary_operators)
|
||||
|
||||
/*
|
||||
Proc: GetUnaryOperator
|
||||
Uses <GetOperator()> to search for an instance of a unary operator type with which the given string is associated. For example, if
|
||||
O is set to "!", a <LogicalNot> node is returned.
|
||||
|
||||
See Also:
|
||||
- <GetOperator()>
|
||||
- <GetBinaryOperator()>
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/GetUnaryOperator(O)
|
||||
return GetOperator(O, /datum/node/expression/operator/unary, options.unary_operators)
|
||||
|
||||
/*
|
||||
Proc: Reduce
|
||||
Takes the operator on top of the opr stack and assigns its operand(s). Then this proc pushes the value of that operation to the top
|
||||
of the val stack.
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/Reduce(var/datum/stack/opr, var/datum/stack/val)
|
||||
var/datum/node/expression/operator/O = opr.Pop()
|
||||
if(!O)
|
||||
return
|
||||
|
||||
if(!istype(O))
|
||||
errors += new/datum/scriptError("Error reducing expression - invalid operator.")
|
||||
return
|
||||
|
||||
//Take O and assign its operands, popping one or two values from the val stack
|
||||
//depending on whether O is a binary or unary operator.
|
||||
if(istype(O, /datum/node/expression/operator/binary))
|
||||
var/datum/node/expression/operator/binary/B=O
|
||||
B.exp2 = val.Pop()
|
||||
B.exp = val.Pop()
|
||||
val.Push(B)
|
||||
|
||||
else
|
||||
O.exp=val.Pop()
|
||||
val.Push(O)
|
||||
|
||||
/*
|
||||
Proc: EndOfExpression
|
||||
Returns true if the current token represents the end of an expression.
|
||||
|
||||
Parameters:
|
||||
end - A list of values to compare the current token to.
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/EndOfExpression(end[])
|
||||
if(!curToken)
|
||||
return 1
|
||||
if(istype(curToken, /datum/token/symbol) && end.Find(curToken.value))
|
||||
return 1
|
||||
if(istype(curToken, /datum/token/end) && end.Find(/datum/token/end))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/*
|
||||
Proc: ParseExpression
|
||||
Uses the Shunting-yard algorithm to parse expressions.
|
||||
|
||||
Notes:
|
||||
- When an opening parenthesis is found, then <ParseParenExpression()> is called to handle it.
|
||||
- The <expecting> variable helps distinguish unary operators from binary operators (for cases like the - operator, which can be either).
|
||||
|
||||
Articles:
|
||||
- <http://epaperpress.com/oper/>
|
||||
- <http://en.wikipedia.org/wiki/Shunting-yard_algorithm>
|
||||
|
||||
See Also:
|
||||
- <ParseFunctionExpression()>
|
||||
- <ParseParenExpression()>
|
||||
- <ParseParamExpression()>
|
||||
*/
|
||||
|
||||
/datum/n_Parser/nS_Parser/proc/ParseExpression(var/list/end = list(/datum/token/end), list/ErrChars = list("{", "}"), check_functions = 0)
|
||||
var/datum/stack/opr = new
|
||||
var/datum/stack/val = new
|
||||
src.expecting = VALUE
|
||||
var/loop = 0
|
||||
for()
|
||||
loop++
|
||||
if(loop > 800)
|
||||
errors += new/datum/scriptError("Too many nested tokens.")
|
||||
return
|
||||
|
||||
if(EndOfExpression(end))
|
||||
break
|
||||
|
||||
if(istype(curToken, /datum/token/symbol) && ErrChars.Find(curToken.value))
|
||||
errors += new/datum/scriptError/BadToken(curToken)
|
||||
break
|
||||
|
||||
if(index > tokens.len) //End of File
|
||||
errors += new/datum/scriptError/EndOfFile()
|
||||
break
|
||||
|
||||
var/datum/token/ntok
|
||||
if(index + 1 <= tokens.len)
|
||||
ntok = tokens[index + 1]
|
||||
|
||||
if(istype(curToken, /datum/token/symbol) && curToken.value == "(") //Parse parentheses expression
|
||||
if(expecting != VALUE)
|
||||
errors += new/datum/scriptError/ExpectedToken("operator", curToken)
|
||||
NextToken()
|
||||
continue
|
||||
|
||||
val.Push(ParseParenExpression())
|
||||
|
||||
else if(istype(curToken, /datum/token/symbol)) //Operator found.
|
||||
var/datum/node/expression/operator/curOperator //Figure out whether it is unary or binary and get a new instance.
|
||||
if(src.expecting == OPERATOR)
|
||||
curOperator = GetBinaryOperator(curToken)
|
||||
if(!curOperator)
|
||||
errors += new/datum/scriptError/ExpectedToken("operator", curToken)
|
||||
NextToken()
|
||||
continue
|
||||
else
|
||||
curOperator = GetUnaryOperator(curToken)
|
||||
if(!curOperator) //given symbol isn't a unary operator
|
||||
errors += new/datum/scriptError/ExpectedToken("expression", curToken)
|
||||
NextToken()
|
||||
continue
|
||||
|
||||
if(opr.Top() && Precedence(opr.Top(), curOperator) == REDUCE) //Check order of operations and reduce if necessary
|
||||
Reduce(opr, val)
|
||||
continue
|
||||
|
||||
opr.Push(curOperator)
|
||||
src.expecting = VALUE
|
||||
|
||||
else if(ntok && ntok.value == "(" && istype(ntok, /datum/token/symbol)\
|
||||
&& istype(curToken, /datum/token/word)) //Parse function call
|
||||
|
||||
if(!check_functions)
|
||||
|
||||
var/datum/token/preToken = curToken
|
||||
var/old_expect = src.expecting
|
||||
var/fex = ParseFunctionExpression()
|
||||
if(old_expect != VALUE)
|
||||
errors += new/datum/scriptError/ExpectedToken("operator", preToken)
|
||||
NextToken()
|
||||
continue
|
||||
|
||||
val.Push(fex)
|
||||
else
|
||||
errors += new/datum/scriptError/ParameterFunction(curToken)
|
||||
break
|
||||
|
||||
else if(istype(curToken, /datum/token/keyword)) //inline keywords
|
||||
var/datum/n_Keyword/kw = options.keywords[curToken.value]
|
||||
kw = new kw(inline=1)
|
||||
if(kw)
|
||||
if(!kw.Parse(src))
|
||||
return
|
||||
else
|
||||
errors += new/datum/scriptError/BadToken(curToken)
|
||||
|
||||
else if(istype(curToken, /datum/token/end)) //semicolon found where it wasn't expected
|
||||
errors += new/datum/scriptError/BadToken(curToken)
|
||||
NextToken()
|
||||
continue
|
||||
else
|
||||
if(expecting != VALUE)
|
||||
errors += new/datum/scriptError/ExpectedToken("operator", curToken)
|
||||
NextToken()
|
||||
continue
|
||||
|
||||
val.Push(GetExpression(curToken))
|
||||
src.expecting = OPERATOR
|
||||
|
||||
NextToken()
|
||||
|
||||
while(opr.Top())
|
||||
Reduce(opr, val)
|
||||
//Reduce the value stack completely
|
||||
. = val.Pop() //Return what should be the last value on the stack
|
||||
|
||||
if(val.Top()) //
|
||||
var/datum/node/N = val.Pop()
|
||||
errors += new/datum/scriptError("Error parsing expression. Unexpected value left on stack: [N.ToString()].")
|
||||
return null
|
||||
|
||||
/*
|
||||
Proc: ParseFunctionExpression
|
||||
Parses a function call inside of an expression.
|
||||
|
||||
See Also:
|
||||
- <ParseExpression()>
|
||||
*/
|
||||
|
||||
/datum/n_Parser/nS_Parser/proc/ParseFunctionExpression()
|
||||
var/datum/node/expression/FunctionCall/exp = new
|
||||
exp.func_name = curToken.value
|
||||
NextToken() //skip function name
|
||||
NextToken() //skip open parenthesis, already found
|
||||
var/loops = 0
|
||||
|
||||
for()
|
||||
loops++
|
||||
if(loops >= 800)
|
||||
errors += new/datum/scriptError("Too many nested expressions.")
|
||||
break
|
||||
//CRASH("Something TERRIBLE has gone wrong in ParseFunctionExpression ;__;")
|
||||
|
||||
if(istype(curToken, /datum/token/symbol) && curToken.value == ")")
|
||||
return exp
|
||||
exp.parameters += ParseParamExpression()
|
||||
if(errors.len)
|
||||
return exp
|
||||
|
||||
if(curToken.value == "," && istype(curToken, /datum/token/symbol))
|
||||
NextToken() //skip comma
|
||||
|
||||
if(istype(curToken, /datum/token/end)) //Prevents infinite loop...
|
||||
errors += new/datum/scriptError/ExpectedToken(")")
|
||||
return exp
|
||||
|
||||
/*
|
||||
Proc: ParseParenExpression
|
||||
Parses an expression that ends with a close parenthesis. This is used for parsing expressions inside of parentheses.
|
||||
|
||||
See Also:
|
||||
- <ParseExpression()>
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/ParseParenExpression()
|
||||
if(!CheckToken("(", /datum/token/symbol))
|
||||
return
|
||||
return new/datum/node/expression/operator/unary/group(ParseExpression(list(")")))
|
||||
|
||||
/*
|
||||
Proc: ParseParamExpression
|
||||
Parses an expression that ends with either a comma or close parenthesis. This is used for parsing the parameters passed to a function call.
|
||||
|
||||
See Also:
|
||||
- <ParseExpression()>
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/proc/ParseParamExpression(var/check_functions = 0)
|
||||
var/cf = check_functions
|
||||
return ParseExpression(list(",", ")"), check_functions = cf)
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
File: Keywords
|
||||
*/
|
||||
var/const/KW_FAIL = 0 //Fatal error; stop parsing entire script.
|
||||
var/const/KW_PASS = 1 //OK
|
||||
var/const/KW_ERR = 2 //Non-fatal error, keyword couldn't be handled properly. Ignore keyword but continue on.
|
||||
var/const/KW_WARN = 3 //Warning
|
||||
|
||||
/*
|
||||
var/const/Class: n_Keyword
|
||||
var/const/Represents a special statement in the code triggered by a keyword.
|
||||
*/
|
||||
/datum/n_Keyword
|
||||
/*
|
||||
Var: inline
|
||||
1 if the keyword is in an expression (e.g. the new keyword in many languages), 0 otherwise (such as the if and else keywords).
|
||||
*/
|
||||
var/inline
|
||||
|
||||
/datum/n_Keyword/New(inline = 0)
|
||||
src.inline = inline
|
||||
return ..()
|
||||
|
||||
/*
|
||||
Proc: Parse
|
||||
Called when the parser finds a keyword in the code.
|
||||
|
||||
Parameters:
|
||||
parser - The parser that created this object. You can use the parameter to manipulate the parser in order to add statements and blocks
|
||||
to its AST.
|
||||
*/
|
||||
/datum/n_Keyword/proc/Parse(var/datum/n_Parser/parser)
|
||||
return
|
||||
/*
|
||||
Class: nS_Keyword
|
||||
A keyword in n_Script. By default these include return, if, else, while, and def. To enable or disable a keyword, change the
|
||||
<nS_Options.keywords> list.
|
||||
|
||||
Behavior:
|
||||
When a parser is expecting a new statement, and a keyword listed in <nS_Options.keywords> is found, it will call the keyword's
|
||||
<n_Keyword.Parse()> proc.
|
||||
*/
|
||||
/datum/n_Keyword/nS_Keyword/New(var/inline = 0)
|
||||
if(inline)
|
||||
del src
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwReturn/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
if(istype(parser.curBlock, /datum/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/datum/node/statement/ReturnStatement/stmt = new
|
||||
parser.NextToken() //skip 'return' token
|
||||
stmt.value = parser.ParseExpression()
|
||||
parser.curBlock.statements += stmt
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwIf/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
var/datum/node/statement/IfStatement/stmt = new
|
||||
parser.NextToken() //skip 'if' token
|
||||
stmt.cond = parser.ParseParenExpression()
|
||||
if(!parser.CheckToken(")", /datum/token/symbol))
|
||||
return KW_FAIL
|
||||
|
||||
if(!parser.CheckToken("{", /datum/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
|
||||
parser.AddBlock(stmt.block)
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwElseIf/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
var/list/L = parser.curBlock.statements
|
||||
var/datum/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)
|
||||
to_chat(usr, "NTSL: ELSE IF FAILED: [!ifstmt], [!istype(ifstmt)], [!istype(ifstmt) || ifstmt.else_if]")// Usr is unsafe as SHIT but JUST incase I forget this debug line like the fucking asset cache...
|
||||
|
||||
parser.errors += new/datum/scriptError/ExpectedToken("if statement", parser.curToken)
|
||||
return KW_FAIL
|
||||
|
||||
var/datum/node/statement/IfStatement/ElseIf/stmt = new
|
||||
parser.NextToken() //skip 'if' token
|
||||
stmt.cond = parser.ParseParenExpression()
|
||||
if(!parser.CheckToken(")", /datum/token/symbol))
|
||||
return KW_FAIL
|
||||
|
||||
if(!parser.CheckToken("{", /datum/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)
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwElse/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
var/list/L = parser.curBlock.statements
|
||||
var/datum/node/statement/IfStatement/stmt
|
||||
|
||||
if(L && L.len)
|
||||
stmt = L[L.len] //Get the last statement in the current block
|
||||
|
||||
if(!stmt || !istype(stmt) || stmt.else_block) //Ensure that it is an if statement
|
||||
to_chat(usr, "NTSL: ELSE IF FAILED: [!stmt], [!istype(stmt)], [!istype(stmt) || stmt.else_block]")// Usr is unsafe as SHIT but JUST incase I forget this debug line like the fucking asset cache...
|
||||
|
||||
parser.errors += new/datum/scriptError/ExpectedToken("if statement", parser.curToken)
|
||||
return KW_FAIL
|
||||
|
||||
parser.NextToken() //skip 'else' token
|
||||
if(!parser.CheckToken("{", /datum/token/symbol, skip = 0))
|
||||
return KW_ERR
|
||||
|
||||
stmt.else_block = new()
|
||||
parser.AddBlock(stmt.else_block)
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwWhile/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
var/datum/node/statement/WhileLoop/stmt = new
|
||||
parser.NextToken() //skip 'while' token
|
||||
stmt.cond = parser.ParseParenExpression()
|
||||
if(!parser.CheckToken(")", /datum/token/symbol))
|
||||
return KW_FAIL
|
||||
|
||||
if(!parser.CheckToken("{", /datum/token/symbol, skip=0))
|
||||
return KW_ERR
|
||||
|
||||
parser.curBlock.statements += stmt
|
||||
stmt.block = new
|
||||
parser.AddBlock(stmt.block)
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwBreak/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
if(istype(parser.curBlock, /datum/node/BlockDefinition/GlobalBlock))
|
||||
parser.errors += new/datum/scriptError/BadToken(parser.curToken)
|
||||
. = KW_WARN
|
||||
|
||||
var/datum/node/statement/BreakStatement/stmt = new
|
||||
parser.NextToken() //skip 'break' token
|
||||
parser.curBlock.statements += stmt
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwContinue/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
if(istype(parser.curBlock, /datum/node/BlockDefinition/GlobalBlock))
|
||||
parser.errors += new/datum/scriptError/BadToken(parser.curToken)
|
||||
. = KW_WARN
|
||||
|
||||
var/datum/node/statement/ContinueStatement/stmt = new
|
||||
parser.NextToken() //skip 'break' token
|
||||
parser.curBlock.statements += stmt
|
||||
|
||||
/datum/n_Keyword/nS_Keyword/kwDef/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||
. = KW_PASS
|
||||
var/datum/node/statement/FunctionDefinition/def = new
|
||||
parser.NextToken() //skip 'def' token
|
||||
|
||||
if(!parser.options.IsValidID(parser.curToken.value))
|
||||
parser.errors += new/datum/scriptError/InvalidID(parser.curToken)
|
||||
return KW_FAIL
|
||||
|
||||
def.func_name = parser.curToken.value
|
||||
parser.NextToken()
|
||||
|
||||
if(!parser.CheckToken("(", /datum/token/symbol))
|
||||
return KW_FAIL
|
||||
|
||||
for() //for now parameters can be separated by whitespace - they don't need a comma in between
|
||||
if(istype(parser.curToken, /datum/token/symbol))
|
||||
switch(parser.curToken.value)
|
||||
if(",")
|
||||
parser.NextToken()
|
||||
if(")")
|
||||
break
|
||||
else
|
||||
parser.errors += new/datum/scriptError/BadToken(parser.curToken)
|
||||
return KW_ERR
|
||||
|
||||
else if(istype(parser.curToken, /datum/token/word))
|
||||
def.parameters += parser.curToken.value
|
||||
parser.NextToken()
|
||||
|
||||
else
|
||||
parser.errors += new/datum/scriptError/InvalidID(parser.curToken)
|
||||
return KW_ERR
|
||||
|
||||
if(!parser.CheckToken(")", /datum/token/symbol))
|
||||
return KW_FAIL
|
||||
|
||||
if(istype(parser.curToken, /datum/token/end)) //Function prototype
|
||||
parser.curBlock.statements += def
|
||||
|
||||
else if(parser.curToken.value == "{" && istype(parser.curToken, /datum/token/symbol))
|
||||
def.block = new
|
||||
parser.curBlock.statements += def
|
||||
parser.curBlock.functions[def.func_name] = def
|
||||
parser.AddBlock(def.block)
|
||||
else
|
||||
parser.errors += new/datum/scriptError/BadToken(parser.curToken)
|
||||
return KW_FAIL
|
||||
@@ -1,205 +0,0 @@
|
||||
/*
|
||||
File: Parser
|
||||
*/
|
||||
/*
|
||||
Class: n_Parser
|
||||
An object that reads tokens and produces an AST (abstract syntax tree).
|
||||
*/
|
||||
/datum/n_Parser
|
||||
/*
|
||||
Var: index
|
||||
The parser's current position in the token's list.
|
||||
*/
|
||||
var/index = 1
|
||||
/*
|
||||
Var: tokens
|
||||
A list of tokens in the source code generated by a scanner.
|
||||
*/
|
||||
var/list/tokens = new
|
||||
/*
|
||||
Var: errors
|
||||
A list of fatal errors found by the parser. If there are any items in this list, then it is not safe to run the returned AST.
|
||||
|
||||
See Also:
|
||||
- <scriptError>
|
||||
*/
|
||||
var/list/errors = new
|
||||
/*
|
||||
Var: warnings
|
||||
A list of non-fatal problems in the script.
|
||||
*/
|
||||
var/list/warnings = new
|
||||
/*
|
||||
Var: curToken
|
||||
The token at <index> in <tokens>.
|
||||
*/
|
||||
var/datum/token/curToken
|
||||
var/datum/stack/blocks = new
|
||||
var/datum/node/BlockDefinition/GlobalBlock/global_block = new
|
||||
var/datum/node/BlockDefinition/GlobalBlock/curBlock
|
||||
|
||||
/*
|
||||
Proc: Parse
|
||||
Reads the tokens and returns the AST's <GlobalBlock> node. Be sure to populate the tokens list before calling this procedure.
|
||||
*/
|
||||
/datum/n_Parser/proc/Parse()
|
||||
return
|
||||
/*
|
||||
Proc: NextToken
|
||||
Sets <curToken> to the next token in the <tokens> list, or null if there are no more tokens.
|
||||
*/
|
||||
/datum/n_Parser/proc/NextToken()
|
||||
if(index >= tokens.len)
|
||||
curToken = null
|
||||
else
|
||||
curToken = tokens[++index]
|
||||
return curToken
|
||||
|
||||
/*
|
||||
Class: nS_Parser
|
||||
An implmentation of a parser for n_Script.
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser
|
||||
var/datum/n_scriptOptions/nS_Options/options
|
||||
/*
|
||||
Constructor: New
|
||||
|
||||
Parameters:
|
||||
tokens - A list of tokens to parse.
|
||||
options - An object used for configuration.
|
||||
*/
|
||||
/datum/n_Parser/nS_Parser/New(tokens[], var/datum/n_scriptOptions/options)
|
||||
src.tokens = tokens
|
||||
src.options = options
|
||||
curBlock = global_block
|
||||
return ..()
|
||||
|
||||
/datum/n_Parser/nS_Parser/Parse()
|
||||
ASSERT(tokens)
|
||||
for(,src.index <= src.tokens.len, src.index++)
|
||||
curToken = tokens[index]
|
||||
switch(curToken.type)
|
||||
if(/datum/token/keyword)
|
||||
var/datum/n_Keyword/kw = options.keywords[curToken.value]
|
||||
kw = new kw()
|
||||
if(kw)
|
||||
if(!kw.Parse(src))
|
||||
return
|
||||
|
||||
if(/datum/token/word)
|
||||
var/datum/token/ntok
|
||||
if(index + 1 > tokens.len)
|
||||
errors += new/datum/scriptError/BadToken(curToken)
|
||||
continue
|
||||
|
||||
ntok = tokens[index + 1]
|
||||
|
||||
if(!istype(ntok, /datum/token/symbol))
|
||||
errors += new/datum/scriptError/BadToken(ntok)
|
||||
continue
|
||||
|
||||
if(ntok.value == "(")
|
||||
ParseFunctionStatement()
|
||||
|
||||
else if(options.assign_operators.Find(ntok.value))
|
||||
ParseAssignment()
|
||||
|
||||
else
|
||||
errors += new/datum/scriptError/BadToken(ntok)
|
||||
continue
|
||||
|
||||
if(!istype(curToken, /datum/token/end))
|
||||
errors += new/datum/scriptError/ExpectedToken(";", curToken)
|
||||
continue
|
||||
|
||||
if(/datum/token/symbol)
|
||||
if(curToken.value == "}")
|
||||
if(!EndBlock())
|
||||
errors += new/datum/scriptError/BadToken(curToken)
|
||||
continue
|
||||
|
||||
else
|
||||
errors += new/datum/scriptError/BadToken(curToken)
|
||||
continue
|
||||
|
||||
if(/datum/token/end)
|
||||
warnings += new/datum/scriptError/BadToken(curToken)
|
||||
continue
|
||||
|
||||
else
|
||||
errors += new/datum/scriptError/BadToken(curToken)
|
||||
return
|
||||
|
||||
return global_block
|
||||
|
||||
/datum/n_Parser/nS_Parser/proc/CheckToken(val, type, err = 1, skip = 1)
|
||||
if(curToken.value != val || !istype(curToken, type))
|
||||
if(err)
|
||||
errors += new/datum/scriptError/ExpectedToken(val, curToken)
|
||||
return 0
|
||||
if(skip)
|
||||
NextToken()
|
||||
|
||||
return 1
|
||||
|
||||
/datum/n_Parser/nS_Parser/proc/AddBlock(var/datum/node/BlockDefinition/B)
|
||||
blocks.Push(curBlock)
|
||||
curBlock = B
|
||||
|
||||
/datum/n_Parser/nS_Parser/proc/EndBlock()
|
||||
if(curBlock == global_block)
|
||||
return 0
|
||||
|
||||
curBlock = blocks.Pop()
|
||||
return 1
|
||||
|
||||
/datum/n_Parser/nS_Parser/proc/ParseAssignment()
|
||||
var/name = curToken.value
|
||||
if(!options.IsValidID(name))
|
||||
errors += new/datum/scriptError/InvalidID(curToken)
|
||||
return
|
||||
|
||||
NextToken()
|
||||
var/t = options.binary_operators[options.assign_operators[curToken.value]]
|
||||
var/datum/node/statement/VariableAssignment/stmt = new()
|
||||
stmt.var_name = new(name)
|
||||
NextToken()
|
||||
|
||||
if(t)
|
||||
stmt.value = new t()
|
||||
stmt.value:exp = new/datum/node/expression/value/variable(stmt.var_name)
|
||||
stmt.value:exp2 = ParseExpression()
|
||||
else
|
||||
stmt.value = ParseExpression()
|
||||
|
||||
curBlock.statements += stmt
|
||||
|
||||
/datum/n_Parser/nS_Parser/proc/ParseFunctionStatement()
|
||||
if(!istype(curToken, /datum/token/word))
|
||||
errors += new/datum/scriptError("Bad identifier in function call.")
|
||||
return
|
||||
|
||||
var/datum/node/statement/FunctionCall/stmt=new
|
||||
stmt.func_name = curToken.value
|
||||
NextToken() //skip function name
|
||||
if(!CheckToken("(", /datum/token/symbol)) //Check for and skip open parenthesis
|
||||
return
|
||||
var/loops = 0
|
||||
for()
|
||||
loops++
|
||||
if(loops >= 800)
|
||||
errors +=new/datum/scriptError("Cannot find ending params.")
|
||||
return
|
||||
|
||||
if(!curToken)
|
||||
errors+=new/datum/scriptError/EndOfFile()
|
||||
return
|
||||
if(istype(curToken, /datum/token/symbol) && curToken.value == ")")
|
||||
curBlock.statements += stmt
|
||||
NextToken() //Skip close parenthesis
|
||||
return
|
||||
|
||||
var/datum/node/expression/P = ParseParamExpression(check_functions = 1)
|
||||
stmt.parameters += P
|
||||
if(istype(curToken, /datum/token/symbol) && curToken.value == ",")
|
||||
NextToken()
|
||||
@@ -1,293 +0,0 @@
|
||||
/*
|
||||
File: Scanner
|
||||
*/
|
||||
/*
|
||||
Class: n_Scanner
|
||||
An object responsible for breaking up source code into tokens for use by the parser.
|
||||
*/
|
||||
/datum/n_Scanner
|
||||
var/list/code
|
||||
/*
|
||||
Var: errors
|
||||
A list of fatal errors found by the scanner. If there are any items in this list, then it is not safe to parse the returned tokens.
|
||||
|
||||
See Also:
|
||||
- <scriptError>
|
||||
*/
|
||||
var/list/errors = new
|
||||
/*
|
||||
Var: warnings
|
||||
A list of non-fatal problems in the source code found by the scanner.
|
||||
*/
|
||||
var/list/warnings = new
|
||||
|
||||
/*
|
||||
Proc: LoadCode
|
||||
Loads source code.
|
||||
*/
|
||||
/datum/n_Scanner/proc/LoadCode(var/list/c)
|
||||
code=c
|
||||
|
||||
/*
|
||||
Proc: LoadCodeFromFile
|
||||
Gets the code from a file and calls <LoadCode()>.
|
||||
*/
|
||||
/datum/n_Scanner/proc/LoadCodeFromFile(var/f)
|
||||
LoadCode(file2text(f))
|
||||
|
||||
/*
|
||||
Proc: Scan
|
||||
Runs the scanner and returns the resulting list of tokens. Ensure that <LoadCode()> has been called first.
|
||||
*/
|
||||
/datum/n_Scanner/proc/Scan()
|
||||
|
||||
/*
|
||||
Class: nS_Scanner
|
||||
A scanner implementation for n_Script.
|
||||
*/
|
||||
/datum/n_Scanner/nS_Scanner
|
||||
/*
|
||||
Variable: codepos
|
||||
The scanner's position in the source code.
|
||||
*/
|
||||
var/codepos = 1
|
||||
var/line = 1
|
||||
var/linepos = 0 //column=codepos-linepos
|
||||
var/datum/n_scriptOptions/nS_Options/options
|
||||
|
||||
var/commenting = 0
|
||||
// 1: single-line
|
||||
// 2: multi-line
|
||||
/*
|
||||
Variable: ignore
|
||||
A list of characters that are ignored by the scanner.
|
||||
|
||||
Default Value:
|
||||
Whitespace
|
||||
*/
|
||||
var/list/ignore = list(" ", "\t", "\n") //Don't add tokens for whitespace
|
||||
/*
|
||||
Variable: end_stmt
|
||||
A list of characters that end a statement. Each item may only be one character long.
|
||||
|
||||
Default Value:
|
||||
Semicolon
|
||||
*/
|
||||
var/list/end_stmt = list(";")
|
||||
/*
|
||||
Variable: string_delim
|
||||
A list of characters that can start and end strings.
|
||||
|
||||
Default Value:
|
||||
Double and single quotes.
|
||||
*/
|
||||
var/list/string_delim = list("\"", "'")
|
||||
/*
|
||||
Variable: delim
|
||||
A list of characters that denote the start of a new token. This list is automatically populated.
|
||||
*/
|
||||
var/list/delim = new
|
||||
|
||||
/*
|
||||
Macro: COL
|
||||
The current column number.
|
||||
*/
|
||||
#define COL codepos-linepos
|
||||
|
||||
/*
|
||||
Constructor: New
|
||||
Parameters:
|
||||
code - The source code to tokenize.
|
||||
options - An <nS_Options> object used to configure the scanner.
|
||||
*/
|
||||
/datum/n_Scanner/nS_Scanner/New(var/list/c, var/datum/n_scriptOptions/nS_Options/options)
|
||||
. = ..()
|
||||
ignore += ascii2text(13) //Carriage return
|
||||
delim += ignore + options.symbols + end_stmt + string_delim
|
||||
src.options = options
|
||||
LoadCode(c)
|
||||
|
||||
/datum/n_Scanner/nS_Scanner/Scan() //Creates a list of tokens from source code
|
||||
var/list/tokens = new
|
||||
for(, src.codepos <= code.len, src.codepos++)
|
||||
var/char = code[codepos]
|
||||
var/nextchar = TCOMMS_SAFE_INDEX(code, codepos + 1)
|
||||
if(char == "\n")
|
||||
line++
|
||||
linepos = codepos
|
||||
|
||||
if(char in ignore)
|
||||
continue
|
||||
|
||||
else if(char == "/" && (nextchar == "*" || nextchar == "/"))
|
||||
ReadComment()
|
||||
|
||||
else if(end_stmt.Find(char))
|
||||
tokens += new/datum/token/end(char, line, COL)
|
||||
|
||||
else if(string_delim.Find(char))
|
||||
codepos++ //skip string delimiter
|
||||
tokens += ReadString(char)
|
||||
|
||||
else if(options.CanStartID(char))
|
||||
tokens += ReadWord()
|
||||
|
||||
else if(options.IsDigit(char))
|
||||
tokens += ReadNumber()
|
||||
|
||||
else if(options.symbols.Find(char))
|
||||
tokens += ReadSymbol()
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
codepos = initial(codepos)
|
||||
line = initial(line)
|
||||
linepos = initial(linepos)
|
||||
return tokens
|
||||
|
||||
|
||||
/*
|
||||
Proc: ReadString
|
||||
Reads a string in the source code into a token.
|
||||
|
||||
Parameters:
|
||||
start - The character used to start the string.
|
||||
*/
|
||||
/datum/n_Scanner/nS_Scanner/proc/ReadString(start)
|
||||
var/buf
|
||||
for(, codepos <= code.len, codepos++)//codepos to length(code))
|
||||
var/char = code[codepos]
|
||||
switch(char)
|
||||
if("\\") //Backslash (\) encountered in string
|
||||
codepos++ //Skip next character in string, since it was escaped by a backslash
|
||||
char = TCOMMS_SAFE_INDEX(code, codepos)
|
||||
switch(char)
|
||||
if("\\") //Double backslash
|
||||
buf += "\\"
|
||||
if("n") //\n Newline
|
||||
buf += "\n"
|
||||
else
|
||||
if(char == start) //\" Doublequote
|
||||
buf += start
|
||||
else //Unknown escaped text
|
||||
buf += char
|
||||
if("\n")
|
||||
. = new/datum/token/string(buf, line, COL)
|
||||
errors += new/datum/scriptError("Unterminated string. Newline reached.", .)
|
||||
line++
|
||||
linepos = codepos
|
||||
break
|
||||
else
|
||||
if(char == start) //string delimiter found, end string
|
||||
break
|
||||
else
|
||||
buf += char //Just a normal character in a string
|
||||
if(!.)
|
||||
return new/datum/token/string(buf, line, COL)
|
||||
|
||||
/*
|
||||
Proc: ReadWord
|
||||
Reads characters separated by an item in <delim> into a token.
|
||||
*/
|
||||
/datum/n_Scanner/nS_Scanner/proc/ReadWord()
|
||||
var/char = code[codepos]
|
||||
var/buf
|
||||
|
||||
while(!delim.Find(char))
|
||||
buf += char
|
||||
if(++codepos > code.len) break
|
||||
char = code[codepos]
|
||||
|
||||
codepos-- //allow main Scan() proc to read the delimiter
|
||||
if(options.keywords.Find(buf))
|
||||
return new/datum/token/keyword(buf, line, COL)
|
||||
else
|
||||
return new/datum/token/word(buf, line, COL)
|
||||
|
||||
/*
|
||||
Proc: ReadSymbol
|
||||
Reads a symbol into a token.
|
||||
*/
|
||||
/datum/n_Scanner/nS_Scanner/proc/ReadSymbol()
|
||||
var/char = code[codepos]
|
||||
var/buf
|
||||
|
||||
while(options.symbols.Find(buf + char))
|
||||
buf += char
|
||||
if(++codepos > code.len) break
|
||||
char = code[codepos]
|
||||
|
||||
codepos-- //allow main Scan() proc to read the next character
|
||||
return new /datum/token/symbol(buf, line, COL)
|
||||
|
||||
/*
|
||||
Proc: ReadNumber
|
||||
Reads a number into a token.
|
||||
*/
|
||||
/datum/n_Scanner/nS_Scanner/proc/ReadNumber()
|
||||
var/char = code[codepos]
|
||||
var/buf
|
||||
var/dec = 0
|
||||
|
||||
while(options.IsDigit(char) || (char == "." && !dec))
|
||||
if(char == ".")
|
||||
dec = 1
|
||||
|
||||
buf += char
|
||||
codepos++
|
||||
char = TCOMMS_SAFE_INDEX(code, codepos)
|
||||
|
||||
var/datum/token/number/T = new(buf, line, COL)
|
||||
if(isnull(text2num(buf)))
|
||||
errors += new/datum/scriptError("Bad number: ", T)
|
||||
T.value = 0
|
||||
|
||||
codepos-- //allow main Scan() proc to read the next character
|
||||
return T
|
||||
|
||||
/*
|
||||
Proc: ReadComment
|
||||
Reads a comment and outputs the type of comment
|
||||
*/
|
||||
|
||||
/datum/n_Scanner/nS_Scanner/proc/ReadComment()
|
||||
var/char = code[codepos]
|
||||
var/nextchar = TCOMMS_SAFE_INDEX(code, codepos + 1)
|
||||
var/charstring = char + nextchar
|
||||
var/comm = 1
|
||||
// 1: single-line comment
|
||||
// 2: multi-line comment
|
||||
var/expectedend = 0
|
||||
|
||||
if(charstring == "//" || charstring == "/*")
|
||||
if(charstring == "/*")
|
||||
comm = 2 // starts a multi-line comment
|
||||
|
||||
while(comm)
|
||||
if(++codepos > code.len)
|
||||
break
|
||||
|
||||
if(expectedend) // ending statement expected...
|
||||
char = code[codepos]
|
||||
if(char == "/") // ending statement found - beak the comment
|
||||
comm = 0
|
||||
break
|
||||
|
||||
if(comm == 2)
|
||||
// multi-line comments are broken by ending statements
|
||||
char = code[codepos]
|
||||
if(char == "*")
|
||||
expectedend = 1
|
||||
continue
|
||||
else
|
||||
char = code[codepos]
|
||||
if(char == "\n")
|
||||
comm = 0
|
||||
break
|
||||
|
||||
if(expectedend)
|
||||
expectedend = 0
|
||||
|
||||
if(comm == 2)
|
||||
errors += new/datum/scriptError/UnterminatedComment()
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
Class: Token
|
||||
Represents an entity and position in the source code.
|
||||
*/
|
||||
/datum/token
|
||||
var/value
|
||||
var/line
|
||||
var/column
|
||||
|
||||
/datum/token/New(v, l = 0, c = 0)
|
||||
value = v
|
||||
line = l
|
||||
column = c
|
||||
|
||||
/datum/token/string
|
||||
|
||||
/datum/token/symbol
|
||||
|
||||
/datum/token/word
|
||||
|
||||
/datum/token/keyword
|
||||
|
||||
/datum/token/number/New()
|
||||
. = ..()
|
||||
if(!isnum(value))
|
||||
value = text2num(value)
|
||||
ASSERT(!isnull(value))
|
||||
|
||||
/datum/token/accessor
|
||||
var/object
|
||||
var/member
|
||||
|
||||
/datum/token/accessor/New(object, member, l = 0, c = 0)
|
||||
src.object = object
|
||||
src.member = member
|
||||
src.value = "[object].[member]" //for debugging only
|
||||
src.line = l
|
||||
src.column = c
|
||||
|
||||
/datum/token/end
|
||||
@@ -1 +0,0 @@
|
||||
#define TCOMMS_SAFE_INDEX(list, index) list.len > index ? list[index] : null
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
Vendored
+154
@@ -0,0 +1,154 @@
|
||||
/* Global */
|
||||
html, body {
|
||||
background-color: #272727;
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Navbar */
|
||||
#navbar {
|
||||
background: #383838;
|
||||
border-bottom: 2px solid #161616;
|
||||
list-style-type: none;
|
||||
height: 28px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#navbar > div {
|
||||
background: #40628a;
|
||||
border-radius: 5px 5px 0 0;
|
||||
color: #ffffff;
|
||||
display: block;
|
||||
height: 80%;
|
||||
float: left;
|
||||
text-decoration: none;
|
||||
padding: 0 7px 0 5px;
|
||||
margin: 6px 0 0 5px;
|
||||
}
|
||||
|
||||
#navbar > div:hover {
|
||||
background: #ffffff;
|
||||
color: #40628a;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#navbar > img {
|
||||
float: left;
|
||||
margin: 2px 10px 2px 0;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
#navbar > .title {
|
||||
color: #a7a7a7;
|
||||
float: right;
|
||||
background: none;
|
||||
font-size: 1.5em;
|
||||
font-family: Helvetica;
|
||||
text-decoration: bold;
|
||||
padding: 0;
|
||||
padding-right: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#navbar > .title:hover {
|
||||
background: none;
|
||||
color: #a7a7a7;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.btnActive {
|
||||
background: #2f943c !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* Content */
|
||||
#content {
|
||||
min-height: 75%;
|
||||
line-height: 1.5;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
.link, .linkOn, .linkOff {
|
||||
background: #40628a;
|
||||
border: 1px solid #161616;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
display: inline;
|
||||
margin: 0 2px 0px 0;
|
||||
min-width: 15px;
|
||||
padding: 0px 4px 0px 4px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.linkOn, .linkOn:link, .linkOn:visited, .linkOn:active, .linkOn:hover
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #2f943c;
|
||||
border-color: #24722e;
|
||||
}
|
||||
|
||||
.linkOff, .linkOff:link, .linkOff:visited, .linkOff:active, .linkOff:hover
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #999999;
|
||||
border-color: #666666;
|
||||
}
|
||||
|
||||
.linkActive:hover {
|
||||
background: #507aac;
|
||||
}
|
||||
|
||||
.linkActive:active {
|
||||
background: #2f943c;
|
||||
}
|
||||
|
||||
/* Config Tables */
|
||||
.tblConfig td {
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.tables {
|
||||
border: 1px solid black;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tables th {
|
||||
border-bottom: 2px solid black;
|
||||
}
|
||||
|
||||
.tables th, .tables td {
|
||||
border-right: 2px dotted black;
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
height: 15px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
/* Hacking stuff. */
|
||||
.hack {
|
||||
color: #fff;
|
||||
text-shadow: 1px 1px #300;
|
||||
font-size: 1.12em;
|
||||
}
|
||||
.hackBtn {
|
||||
background-color: #a00 !important;
|
||||
}
|
||||
.hackBtn:hover {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
.hackBtn:hover .hack {
|
||||
color: #000 !important;
|
||||
}
|
||||
Vendored
+10907
File diff suppressed because one or more lines are too long
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>NTSL</title>
|
||||
<script type="text/javascript" src="bundle.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="bundle.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="navbar">
|
||||
<img src="uiTitleFluff.png">
|
||||
<div class="navBtn" data-tab="home">Home</div>
|
||||
<div class="navBtn" data-tab="filtering">Filtering</div>
|
||||
<div class="navBtn" data-tab="regex">Regex</div>
|
||||
<div class="navBtn" style="display:none" data-locked="1" data-tab="firewall">Firewall</div>
|
||||
<div class="navBtn hackBtn" style="display:none" data-locked="1" data-tab="hack"><span class='hack'>1337</span></div>
|
||||
<div class="title">NTTC</div>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<!-- Javascript loads this -->
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<activeLink data-href="save_config=1">Save Config</activeLink>
|
||||
<activeLink data-href="load_config=1">Load Config</activeLink>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
<div>
|
||||
<h2>Filtering Settings</h2>
|
||||
<table class="tblConfig">
|
||||
<tr>
|
||||
<td>Announce Jobs?</td>
|
||||
<td><activeLink data-sconfig="toggle_jobs" data-href="toggle=toggle_jobs"></activeLink></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Announce Timecodes?</td>
|
||||
<td><activeLink data-sconfig="toggle_timecode" data-href="toggle=toggle_timecode"></activeLink></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Language Conversion?</td>
|
||||
<td><activeLink data-aconfig="setting_language" data-href="setting_language=1"></activeLink></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
<div>
|
||||
<h2>FIREWALL</h2>
|
||||
<arrayBoat data-config="firewall" data-header="Blocked User|Delete Entry"></arrayBoat>
|
||||
</div>
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
<div>
|
||||
<h2>1337 HAaCCkEr MeNU!</h2>
|
||||
<table class="tblConfig">
|
||||
<tr>
|
||||
<td>H*lp Tr%%ait0r i* $c/e#ce</td>
|
||||
<td><activeLink data-sconfig="toggle_gibberish" data-href="toggle=toggle_gibberish"></activeLink></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>HoNKKk!!11!</td>
|
||||
<td><activeLink data-sconfig="toggle_honk" data-href="toggle=toggle_honk"></activeLink></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<style type="text/css">
|
||||
html, body {
|
||||
background: #a00;
|
||||
}
|
||||
|
||||
#navbar {
|
||||
background: #400;
|
||||
}
|
||||
</style>
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
<div>
|
||||
<h2>Home</h2>
|
||||
<table class="tblConfig">
|
||||
<tr>
|
||||
<td>Telecomms Activated?</td>
|
||||
<td><activeLink data-sconfig="toggle_activated" data-href="toggle=toggle_activated;"></activeLink></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
<div>
|
||||
<h2>Regex</h2>
|
||||
<listBoat data-config="regex" data-header="From|To|Del"></listBoat>
|
||||
</div>
|
||||
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 670 B |
@@ -0,0 +1,106 @@
|
||||
/* Dependencies */
|
||||
|
||||
// Regular node dependencies
|
||||
var child_process = require("child_process");
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
|
||||
// Main compilers
|
||||
var browserify = require("browserify");
|
||||
var gulp = require("gulp");
|
||||
|
||||
// Extras
|
||||
var del = require("del");
|
||||
var concat = require("gulp-concat");
|
||||
var flatten = require("gulp-flatten");
|
||||
var rename = require("gulp-rename");
|
||||
var buffer = require("vinyl-buffer");
|
||||
var source = require("vinyl-source-stream");
|
||||
|
||||
/* Configuration */
|
||||
const watch_targets = [
|
||||
"./src/index.js",
|
||||
"./src/css/*",
|
||||
"./src/js/*",
|
||||
"./src/html/**/*"
|
||||
]
|
||||
|
||||
const output = {
|
||||
"css": "bundle.css",
|
||||
"dest": "./dist/",
|
||||
"js": "bundle.js"
|
||||
};
|
||||
|
||||
/* Clean out the dist directory to get rid of any old build artifacts. */
|
||||
gulp.task("clean", function () {
|
||||
del(output.dest + "*");
|
||||
});
|
||||
|
||||
/* This uses browserify to compress every single require() into a single javascript file. */
|
||||
gulp.task("browserify", ["clean"], function () {
|
||||
var bundleStream = browserify("./src/index.js").bundle();
|
||||
|
||||
bundleStream
|
||||
.pipe(source("index.js"))
|
||||
.pipe(rename("bundle.js"))
|
||||
.pipe(buffer())
|
||||
.pipe(gulp.dest(output.dest))
|
||||
});
|
||||
|
||||
|
||||
/* CSS concatenation and output. */
|
||||
gulp.task("css", ["clean"], function () {
|
||||
gulp.src("./src/css/*.css")
|
||||
.pipe(concat(output.css))
|
||||
.pipe(buffer())
|
||||
.pipe(gulp.dest(output.dest))
|
||||
});
|
||||
|
||||
/* Basically just copy-paste the html files into the dest, but flatten the directory structure. */
|
||||
gulp.task("etc", ["clean"], function () {
|
||||
gulp.src("./src/html/**/*.html")
|
||||
.pipe(flatten())
|
||||
.pipe(buffer())
|
||||
.pipe(gulp.dest(output.dest));
|
||||
gulp.src("./src/img/*")
|
||||
.pipe(buffer())
|
||||
.pipe(gulp.dest(output.dest));
|
||||
});
|
||||
|
||||
// This runs all of the other defined tasks on "gulp"
|
||||
gulp.task("default", ["css", "etc", "browserify"])
|
||||
|
||||
// Autoreload
|
||||
gulp.task("reload", ["default"], function() {
|
||||
child_process.exec("reload.bat", function (err, stdout) {
|
||||
if (err)
|
||||
throw err;
|
||||
|
||||
var byond_cache = path.join(stdout.trim(), "BYOND", "cache");
|
||||
var files = fs.readdirSync(byond_cache);
|
||||
for (let file of files) {
|
||||
let filepath = path.join(byond_cache, file);
|
||||
let stats = fs.statSync(filepath)
|
||||
if (file.startsWith("tmp") && stats.isDirectory()) {
|
||||
var tmpFiles = fs.readdirSync(filepath);
|
||||
if (tmpFiles.includes("bundle.js")) {
|
||||
setTimeout(function () {transfer_files(filepath)}, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function transfer_files(target) {
|
||||
console.log("transfer_files")
|
||||
let filesToTransfer = fs.readdirSync(path.join(".", "dist"));
|
||||
for (let file of filesToTransfer) {
|
||||
console.log(file, path.join(target, file))
|
||||
let filepath = path.join(".", "dist", file);
|
||||
fs.createReadStream(filepath).pipe(fs.createWriteStream(path.join(target, file)))
|
||||
}
|
||||
}
|
||||
|
||||
gulp.task("watch", function () {
|
||||
gulp.watch(watch_targets, ["reload"]);
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "ntsl2",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Tigercat2000",
|
||||
"license": "AGPL-3.0-only",
|
||||
"dependencies": {
|
||||
"del": "^3.0.0",
|
||||
"gulp-flatten": "^0.4.0",
|
||||
"he": "^1.2.0",
|
||||
"jquery": "^3.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"browserify": "^16.2.3",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-rename": "^1.4.0",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
@echo off
|
||||
|
||||
for /f "tokens=3* delims= " %%a in (
|
||||
'reg query "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" /v "Personal"'
|
||||
) do (
|
||||
set documents=%%a
|
||||
)
|
||||
|
||||
echo %documents%
|
||||
@@ -0,0 +1,154 @@
|
||||
/* Global */
|
||||
html, body {
|
||||
background-color: #272727;
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Navbar */
|
||||
#navbar {
|
||||
background: #383838;
|
||||
border-bottom: 2px solid #161616;
|
||||
list-style-type: none;
|
||||
height: 28px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#navbar > div {
|
||||
background: #40628a;
|
||||
border-radius: 5px 5px 0 0;
|
||||
color: #ffffff;
|
||||
display: block;
|
||||
height: 80%;
|
||||
float: left;
|
||||
text-decoration: none;
|
||||
padding: 0 7px 0 5px;
|
||||
margin: 6px 0 0 5px;
|
||||
}
|
||||
|
||||
#navbar > div:hover {
|
||||
background: #ffffff;
|
||||
color: #40628a;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#navbar > img {
|
||||
float: left;
|
||||
margin: 2px 10px 2px 0;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
#navbar > .title {
|
||||
color: #a7a7a7;
|
||||
float: right;
|
||||
background: none;
|
||||
font-size: 1.5em;
|
||||
font-family: Helvetica;
|
||||
text-decoration: bold;
|
||||
padding: 0;
|
||||
padding-right: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#navbar > .title:hover {
|
||||
background: none;
|
||||
color: #a7a7a7;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.btnActive {
|
||||
background: #2f943c !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* Content */
|
||||
#content {
|
||||
min-height: 75%;
|
||||
line-height: 1.5;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
.link, .linkOn, .linkOff {
|
||||
background: #40628a;
|
||||
border: 1px solid #161616;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
display: inline;
|
||||
margin: 0 2px 0px 0;
|
||||
min-width: 15px;
|
||||
padding: 0px 4px 0px 4px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.linkOn, .linkOn:link, .linkOn:visited, .linkOn:active, .linkOn:hover
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #2f943c;
|
||||
border-color: #24722e;
|
||||
}
|
||||
|
||||
.linkOff, .linkOff:link, .linkOff:visited, .linkOff:active, .linkOff:hover
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #999999;
|
||||
border-color: #666666;
|
||||
}
|
||||
|
||||
.linkActive:hover {
|
||||
background: #507aac;
|
||||
}
|
||||
|
||||
.linkActive:active {
|
||||
background: #2f943c;
|
||||
}
|
||||
|
||||
/* Config Tables */
|
||||
.tblConfig td {
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.tables {
|
||||
border: 1px solid black;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tables th {
|
||||
border-bottom: 2px solid black;
|
||||
}
|
||||
|
||||
.tables th, .tables td {
|
||||
border-right: 2px dotted black;
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
height: 15px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
/* Hacking stuff. */
|
||||
.hack {
|
||||
color: #fff;
|
||||
text-shadow: 1px 1px #300;
|
||||
font-size: 1.12em;
|
||||
}
|
||||
.hackBtn {
|
||||
background-color: #a00 !important;
|
||||
}
|
||||
.hackBtn:hover {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
.hackBtn:hover .hack {
|
||||
color: #000 !important;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>NTSL</title>
|
||||
<script type="text/javascript" src="bundle.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="bundle.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="navbar">
|
||||
<img src="uiTitleFluff.png">
|
||||
<div class="navBtn" data-tab="home">Home</div>
|
||||
<div class="navBtn" data-tab="filtering">Filtering</div>
|
||||
<div class="navBtn" data-tab="regex">Regex</div>
|
||||
<div class="navBtn" style="display:none" data-locked="1" data-tab="firewall">Firewall</div>
|
||||
<div class="navBtn hackBtn" style="display:none" data-locked="1" data-tab="hack"><span class='hack'>1337</span></div>
|
||||
<div class="title">NTTC</div>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<!-- Javascript loads this -->
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<activeLink data-href="save_config=1">Save Config</activeLink>
|
||||
<activeLink data-href="load_config=1">Load Config</activeLink>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,17 @@
|
||||
<div>
|
||||
<h2>Filtering Settings</h2>
|
||||
<table class="tblConfig">
|
||||
<tr>
|
||||
<td>Announce Jobs?</td>
|
||||
<td><activeLink data-sconfig="toggle_jobs" data-href="toggle=toggle_jobs"></activeLink></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Announce Timecodes?</td>
|
||||
<td><activeLink data-sconfig="toggle_timecode" data-href="toggle=toggle_timecode"></activeLink></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Language Conversion?</td>
|
||||
<td><activeLink data-aconfig="setting_language" data-href="setting_language=1"></activeLink></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<div>
|
||||
<h2>FIREWALL</h2>
|
||||
<arrayBoat data-config="firewall" data-header="Blocked User|Delete Entry"></arrayBoat>
|
||||
</div>
|
||||
@@ -0,0 +1,22 @@
|
||||
<div>
|
||||
<h2>1337 HAaCCkEr MeNU!</h2>
|
||||
<table class="tblConfig">
|
||||
<tr>
|
||||
<td>H*lp Tr%%ait0r i* $c/e#ce</td>
|
||||
<td><activeLink data-sconfig="toggle_gibberish" data-href="toggle=toggle_gibberish"></activeLink></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>HoNKKk!!11!</td>
|
||||
<td><activeLink data-sconfig="toggle_honk" data-href="toggle=toggle_honk"></activeLink></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<style type="text/css">
|
||||
html, body {
|
||||
background: #a00;
|
||||
}
|
||||
|
||||
#navbar {
|
||||
background: #400;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,9 @@
|
||||
<div>
|
||||
<h2>Home</h2>
|
||||
<table class="tblConfig">
|
||||
<tr>
|
||||
<td>Telecomms Activated?</td>
|
||||
<td><activeLink data-sconfig="toggle_activated" data-href="toggle=toggle_activated;"></activeLink></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<div>
|
||||
<h2>Regex</h2>
|
||||
<listBoat data-config="regex" data-header="From|To|Del"></listBoat>
|
||||
</div>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 670 B |
@@ -0,0 +1 @@
|
||||
var main = require("./js/main.js")
|
||||
@@ -0,0 +1,14 @@
|
||||
var $ = require("jquery");
|
||||
|
||||
module.exports.reload = function () {
|
||||
$(".linkActive")
|
||||
.off("click")
|
||||
.on("click", function (event) {
|
||||
event.preventDefault();
|
||||
var href = $(this).data("href");
|
||||
if (href) {
|
||||
href = window.byondSrc + href;
|
||||
window.location.href = href;
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
var $ = require("jquery");
|
||||
var he = require("he");
|
||||
var templater = require("./templater.js")
|
||||
|
||||
$(document).ready(function () {
|
||||
if(window.originalConfig) {
|
||||
window.config = JSON.parse(he.decode(window.originalConfig))
|
||||
}
|
||||
|
||||
var current_tab = "home";
|
||||
|
||||
function tab(tabname) {
|
||||
console.log(tabname)
|
||||
loadtab(tabname);
|
||||
}
|
||||
|
||||
function loadtab(tabname) {
|
||||
current_tab = tabname;
|
||||
$.when($.ajax({
|
||||
url: "tab_" + tabname + ".html",
|
||||
cache: false,
|
||||
dataType: 'html'}))
|
||||
.done(function (tabdata) {
|
||||
$("#content").html(tabdata);
|
||||
templater.parseCurrentPage();
|
||||
});
|
||||
}
|
||||
|
||||
window.reload_tab = function() {
|
||||
loadtab(current_tab)
|
||||
}
|
||||
|
||||
loadtab("home");
|
||||
$(".navBtn[data-tab='home']").addClass("btnActive");
|
||||
|
||||
$(".navBtn").click(function () {
|
||||
$(".navBtn").removeClass("btnActive")
|
||||
var toSwitch = $(this).data("tab");
|
||||
$(this).addClass("btnActive");
|
||||
tab(toSwitch);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,145 @@
|
||||
var $ = require("jquery");
|
||||
|
||||
function createLink(href, text) {
|
||||
let $link = $("<div>");
|
||||
$link.addClass("link linkActive");
|
||||
$link.data("href", href);
|
||||
$link.attr("unselectable", "on")
|
||||
$link.text(text);
|
||||
return $link;
|
||||
}
|
||||
|
||||
function link_onClick(event) {
|
||||
event.preventDefault();
|
||||
let href = $(this).data("href")
|
||||
if (href) {
|
||||
href = window.byondSrc + href;
|
||||
window.location.href = href;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.parseCurrentPage = function () {
|
||||
$("listBoat").each(function () {
|
||||
var config_attr = $(this).data("config");
|
||||
if (!config_attr)
|
||||
return;
|
||||
|
||||
let th = $(this).data("header");
|
||||
|
||||
let dataList = config[config_attr];
|
||||
$(this).replaceWith("<table class=\"tables\" id=\"WORK\">");
|
||||
|
||||
if (th) {
|
||||
let assembledString = "<tr>";
|
||||
let split = th.split("|");
|
||||
for (let header in split) {
|
||||
assembledString += "<th>";
|
||||
assembledString += split[header];
|
||||
assembledString += "</th>";
|
||||
}
|
||||
assembledString += "</tr>";
|
||||
$("#WORK").append(assembledString);
|
||||
}
|
||||
|
||||
for (let key in dataList) {
|
||||
let value = dataList[key];
|
||||
let $tr = $("<tr>");
|
||||
$tr.append($("<td>").text(key));
|
||||
$tr.append($("<td>").text(value));
|
||||
let $td = $("<td>");
|
||||
$td.append(createLink('table='+config_attr+';delete_row='+key, "X"))
|
||||
$tr.append($td);
|
||||
$("#WORK").append($tr);
|
||||
}
|
||||
|
||||
$(createLink('table='+config_attr+';create_row=1', "New")).insertAfter("#WORK").click(link_onClick);
|
||||
$("#WORK .link").click(link_onClick);
|
||||
$("#WORK").attr("id", null);
|
||||
});
|
||||
|
||||
$("arrayBoat").each(function () {
|
||||
var config_attr = $(this).data("config");
|
||||
if (!config_attr)
|
||||
return;
|
||||
|
||||
let th = $(this).data("header");
|
||||
|
||||
let dataList = config[config_attr];
|
||||
$(this).replaceWith("<table class=\"tables\" id=\"WORK\">");
|
||||
|
||||
if (th) {
|
||||
let assembledString = "<tr>";
|
||||
let split = th.split("|");
|
||||
for (let header in split) {
|
||||
assembledString += "<th>";
|
||||
assembledString += split[header];
|
||||
assembledString += "</th>";
|
||||
}
|
||||
assembledString += "</tr>";
|
||||
$("#WORK").append(assembledString);
|
||||
}
|
||||
|
||||
for (var i = 0; i < dataList.length; i++) {
|
||||
let value = dataList[i];
|
||||
let $tr = $("<tr>");
|
||||
$tr.append($("<td>").text(value));
|
||||
let $td = $("<td>");
|
||||
$td.append(createLink('array='+config_attr+';delete_item='+value, "X"))
|
||||
$tr.append($td);
|
||||
$("#WORK").append($tr);
|
||||
}
|
||||
|
||||
$(createLink('array='+config_attr+';create_item=1', "New")).insertAfter("#WORK").click(link_onClick);
|
||||
$("#WORK .link").click(link_onClick);
|
||||
$("#WORK").attr("id", null);
|
||||
});
|
||||
|
||||
$("activeLink").each(function () {
|
||||
let href = $(this).data("href");
|
||||
let simple_config = $(this).data("sconfig");
|
||||
let advanced_config = $(this).data("aconfig");
|
||||
let internalText = $(this).text();
|
||||
|
||||
$(this).replaceWith("<div id=\"WORK\">")
|
||||
$("#WORK")
|
||||
.addClass("link linkActive")
|
||||
.data("href", href)
|
||||
.text(internalText)
|
||||
.on("click", link_onClick)
|
||||
.attr("unselectable", "on")
|
||||
|
||||
if (simple_config) {
|
||||
if (window.config[simple_config]) {
|
||||
$("#WORK")
|
||||
.addClass("linkOn")
|
||||
.text("Enabled")
|
||||
} else {
|
||||
$("#WORK")
|
||||
.text("Disabled")
|
||||
}
|
||||
}
|
||||
|
||||
if (advanced_config) {
|
||||
if (window.config[advanced_config])
|
||||
$("#WORK").text(window.config[advanced_config])
|
||||
else
|
||||
$("#WORK").text("Unset")
|
||||
}
|
||||
|
||||
$("#WORK").attr("id", null);
|
||||
});
|
||||
|
||||
if (!window.secretsunlocked) {
|
||||
$("div[data-locked='1']").hide()
|
||||
} else {
|
||||
$("div[data-locked='1']").show()
|
||||
}
|
||||
|
||||
$("configRead").each(function () {
|
||||
let seeked = $(this).data("config");
|
||||
$(this).replaceWith("<span id=\"WORK\">")
|
||||
$("#WORK")
|
||||
.text(window.config[seeked])
|
||||
.attr("id", null);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "ntsl2-src",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Tigercat2000",
|
||||
"license": "AGPL-3.0-only",
|
||||
"dependencies": {
|
||||
"he": "^1.1.1",
|
||||
"jquery": "^3.3.1"
|
||||
}
|
||||
}
|
||||
+1
-19
@@ -689,6 +689,7 @@
|
||||
#include "code\game\machinery\telecomms\broadcaster.dm"
|
||||
#include "code\game\machinery\telecomms\logbrowser.dm"
|
||||
#include "code\game\machinery\telecomms\machine_interactions.dm"
|
||||
#include "code\game\machinery\telecomms\ntsl2.dm"
|
||||
#include "code\game\machinery\telecomms\presets.dm"
|
||||
#include "code\game\machinery\telecomms\telecomunications.dm"
|
||||
#include "code\game\machinery\telecomms\telemonitor.dm"
|
||||
@@ -2219,25 +2220,6 @@
|
||||
#include "code\modules\response_team\ert.dm"
|
||||
#include "code\modules\response_team\ert_outfits.dm"
|
||||
#include "code\modules\ruins\ruin_areas.dm"
|
||||
#include "code\modules\scripting\__defines.dm"
|
||||
#include "code\modules\scripting\Errors.dm"
|
||||
#include "code\modules\scripting\Options.dm"
|
||||
#include "code\modules\scripting\AST\AST Nodes.dm"
|
||||
#include "code\modules\scripting\AST\Blocks.dm"
|
||||
#include "code\modules\scripting\AST\Statements.dm"
|
||||
#include "code\modules\scripting\AST\Operators\Binary Operators.dm"
|
||||
#include "code\modules\scripting\AST\Operators\Unary Operators.dm"
|
||||
#include "code\modules\scripting\Implementations\_Logic.dm"
|
||||
#include "code\modules\scripting\Implementations\Telecomms.dm"
|
||||
#include "code\modules\scripting\Interpreter\Evaluation.dm"
|
||||
#include "code\modules\scripting\Interpreter\Interaction.dm"
|
||||
#include "code\modules\scripting\Interpreter\Interpreter.dm"
|
||||
#include "code\modules\scripting\Interpreter\Scope.dm"
|
||||
#include "code\modules\scripting\Parser\Expressions.dm"
|
||||
#include "code\modules\scripting\Parser\Keywords.dm"
|
||||
#include "code\modules\scripting\Parser\Parser.dm"
|
||||
#include "code\modules\scripting\Scanner\Scanner.dm"
|
||||
#include "code\modules\scripting\Scanner\Tokens.dm"
|
||||
#include "code\modules\security_levels\keycard authentication.dm"
|
||||
#include "code\modules\security_levels\security levels.dm"
|
||||
#include "code\modules\shuttle\assault_pod.dm"
|
||||
|
||||
Reference in New Issue
Block a user