mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 10:21:11 +00:00
NTSL code cleanup.
This commit is contained in:
@@ -1,284 +1,299 @@
|
|||||||
/*
|
/*
|
||||||
* Holds procs designed to change one type of value, into another.
|
* Holds procs designed to change one type of value, into another.
|
||||||
* Contains:
|
* Contains:
|
||||||
* text2list & list2text
|
* text2list & list2text
|
||||||
* file2list
|
* file2list
|
||||||
* angle2dir
|
* angle2dir
|
||||||
* angle2text
|
* angle2text
|
||||||
* worldtime2text
|
* worldtime2text
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Concatenates a list of strings into a single string. A seperator may optionally be provided.
|
// Concatenates a list of strings into a single string. A seperator may optionally be provided.
|
||||||
/proc/list2text(list/ls, sep)
|
/proc/list2text(list/ls, sep)
|
||||||
if(ls && ls.len <= 1) // Early-out code for empty or singleton lists.
|
if(ls && ls.len <= 1) // Early-out code for empty or singleton lists.
|
||||||
return (ls && ls.len) ? ls[1] : ""
|
return (ls && ls.len) ? ls[1] : ""
|
||||||
|
|
||||||
var/l = ls.len // Made local for sanic speed.
|
var/l = ls.len // Made local for sanic speed.
|
||||||
var/i = 0 // Incremented every time a list index is accessed.
|
var/i = 0 // Incremented every time a list index is accessed.
|
||||||
|
|
||||||
if(sep != null)
|
if(sep != null)
|
||||||
// Macros expand to long argument lists like so: sep, ls[++i], sep, ls[++i], sep, ls[++i], etc...
|
// Macros expand to long argument lists like so: sep, ls[++i], sep, ls[++i], sep, ls[++i], etc...
|
||||||
#define S1 sep, ls[++i]
|
#define S1 sep, ls[++i]
|
||||||
#define S4 S1, S1, S1, S1
|
#define S4 S1, S1, S1, S1
|
||||||
#define S16 S4, S4, S4, S4
|
#define S16 S4, S4, S4, S4
|
||||||
#define S64 S16, S16, S16, S16
|
#define S64 S16, S16, S16, S16
|
||||||
|
|
||||||
. = "[ls[++i]]" // Make sure the initial element is converted to text.
|
. = "[ls[++i]]" // Make sure the initial element is converted to text.
|
||||||
|
|
||||||
// Having the small concatenations come before the large ones boosted speed by an average of at least 5%.
|
// Having the small concatenations come before the large ones boosted speed by an average of at least 5%.
|
||||||
if(l-1 & 0x01) // 'i' will always be 1 here.
|
if(l-1 & 0x01) // 'i' will always be 1 here.
|
||||||
. = text("[][][]", ., S1) // Append 1 element if the remaining elements are not a multiple of 2.
|
. = text("[][][]", ., S1) // Append 1 element if the remaining elements are not a multiple of 2.
|
||||||
if(l-i & 0x02)
|
if(l-i & 0x02)
|
||||||
. = text("[][][][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4.
|
. = text("[][][][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4.
|
||||||
if(l-i & 0x04)
|
if(l-i & 0x04)
|
||||||
. = text("[][][][][][][][][]", ., S4) // And so on....
|
. = text("[][][][][][][][][]", ., S4) // And so on....
|
||||||
if(l-i & 0x08)
|
if(l-i & 0x08)
|
||||||
. = text("[][][][][][][][][][][][][][][][][]", ., S4, S4)
|
. = text("[][][][][][][][][][][][][][][][][]", ., S4, S4)
|
||||||
if(l-i & 0x10)
|
if(l-i & 0x10)
|
||||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16)
|
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16)
|
||||||
if(l-i & 0x20)
|
if(l-i & 0x20)
|
||||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16)
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16)
|
||||||
if(l-i & 0x40)
|
if(l-i & 0x40)
|
||||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64)
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64)
|
||||||
while(l > i) // Chomp through the rest of the list, 128 elements at a time.
|
while(l > i) // Chomp through the rest of the list, 128 elements at a time.
|
||||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64)
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64)
|
||||||
|
|
||||||
#undef S64
|
#undef S64
|
||||||
#undef S16
|
#undef S16
|
||||||
#undef S4
|
#undef S4
|
||||||
#undef S1
|
#undef S1
|
||||||
|
|
||||||
else
|
else
|
||||||
// Macros expand to long argument lists like so: ls[++i], ls[++i], ls[++i], etc...
|
// Macros expand to long argument lists like so: ls[++i], ls[++i], ls[++i], etc...
|
||||||
#define S1 ls[++i]
|
#define S1 ls[++i]
|
||||||
#define S4 S1, S1, S1, S1
|
#define S4 S1, S1, S1, S1
|
||||||
#define S16 S4, S4, S4, S4
|
#define S16 S4, S4, S4, S4
|
||||||
#define S64 S16, S16, S16, S16
|
#define S64 S16, S16, S16, S16
|
||||||
|
|
||||||
. = "[ls[++i]]" // Make sure the initial element is converted to text.
|
. = "[ls[++i]]" // Make sure the initial element is converted to text.
|
||||||
|
|
||||||
if(l-1 & 0x01) // 'i' will always be 1 here.
|
if(l-1 & 0x01) // 'i' will always be 1 here.
|
||||||
. += "[S1]" // Append 1 element if the remaining elements are not a multiple of 2.
|
. += "[S1]" // Append 1 element if the remaining elements are not a multiple of 2.
|
||||||
if(l-i & 0x02)
|
if(l-i & 0x02)
|
||||||
. = text("[][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4.
|
. = text("[][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4.
|
||||||
if(l-i & 0x04)
|
if(l-i & 0x04)
|
||||||
. = text("[][][][][]", ., S4) // And so on...
|
. = text("[][][][][]", ., S4) // And so on...
|
||||||
if(l-i & 0x08)
|
if(l-i & 0x08)
|
||||||
. = text("[][][][][][][][][]", ., S4, S4)
|
. = text("[][][][][][][][][]", ., S4, S4)
|
||||||
if(l-i & 0x10)
|
if(l-i & 0x10)
|
||||||
. = text("[][][][][][][][][][][][][][][][][]", ., S16)
|
. = text("[][][][][][][][][][][][][][][][][]", ., S16)
|
||||||
if(l-i & 0x20)
|
if(l-i & 0x20)
|
||||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16)
|
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16)
|
||||||
if(l-i & 0x40)
|
if(l-i & 0x40)
|
||||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64)
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64)
|
||||||
while(l > i) // Chomp through the rest of the list, 128 elements at a time.
|
while(l > i) // Chomp through the rest of the list, 128 elements at a time.
|
||||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64)
|
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64)
|
||||||
|
|
||||||
#undef S64
|
#undef S64
|
||||||
#undef S16
|
#undef S16
|
||||||
#undef S4
|
#undef S4
|
||||||
#undef S1
|
#undef S1
|
||||||
|
|
||||||
//slower then list2text, but correctly processes associative lists.
|
//slower then list2text, but correctly processes associative lists.
|
||||||
proc/tg_list2text(list/list, glue=",")
|
proc/tg_list2text(list/list, glue = ",")
|
||||||
//writepanic("[__FILE__].[__LINE__] \\/proc/tg_list2text() called tick#: [world.time]")
|
//writepanic("[__FILE__].[__LINE__] \\/proc/tg_list2text() called tick#: [world.time]")
|
||||||
if(!istype(list) || !list.len)
|
if(!istype(list) || !list.len)
|
||||||
return
|
return
|
||||||
var/output
|
for(var/i=1 to list.len)
|
||||||
for(var/i=1 to list.len)
|
. += (i != 1 ? glue : null)+ \
|
||||||
output += (i!=1? glue : null)+(!isnull(list["[list[i]]"])?"[list["[list[i]]"]]":"[list[i]]")
|
(!isnull(list["[list[i]]"]) ? \
|
||||||
return output
|
"[list["[list[i]]"]]" : \
|
||||||
|
"[list[i]]")
|
||||||
// HTTP GET URL query builder thing.
|
return .
|
||||||
// list("a"="b","c"="d") -> ?a=b&c=d
|
|
||||||
/proc/buildurlquery(list/list,sep="&")
|
// Yeah, so list2text doesn't do assoc values, tg_list2text only does assoc values if they're available, and NTSL needs to stay relatively simple.
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/buildurlquery() called tick#: [world.time]")
|
/proc/vg_list2text(var/list/list, var/glue = ", ", var/assoc_glue = " = ")
|
||||||
if(!istype(list) || !list.len)
|
if(!islist(list) || !list.len)
|
||||||
return
|
return // Valid lists you nerd.
|
||||||
var/output
|
|
||||||
var/i=0
|
for(var/i = 1 to list.len)
|
||||||
var/start
|
if(isnull(list[list[i]]))
|
||||||
var/qmark="?" // God damnit byond
|
. += "[list[i]][glue]"
|
||||||
for(var/key in list)
|
else
|
||||||
start = i ? sep : qmark
|
. += "[list[i]][assoc_glue][list[list[i]]][glue]"
|
||||||
output += "[start][key]=[list[key]]"
|
|
||||||
i++
|
. = copytext(., 1, length(.) - length(glue) + 1) // Shush. (cut out the glue which is added to the end.)
|
||||||
return output
|
|
||||||
|
// HTTP GET URL query builder thing.
|
||||||
//Converts a string into a list by splitting the string at each delimiter found. (discarding the seperator)
|
// list("a"="b","c"="d") -> ?a=b&c=d
|
||||||
/proc/text2list(text, delimiter="\n")
|
/proc/buildurlquery(list/list,sep="&")
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/text2list() called tick#: [world.time]")
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/buildurlquery() called tick#: [world.time]")
|
||||||
var/delim_len = length(delimiter)
|
if(!istype(list) || !list.len)
|
||||||
if(delim_len < 1) return list(text)
|
return
|
||||||
. = list()
|
var/output
|
||||||
var/last_found = 1
|
var/i=0
|
||||||
var/found
|
var/start
|
||||||
do
|
var/qmark="?" // God damnit byond
|
||||||
found = findtext(text, delimiter, last_found, 0)
|
for(var/key in list)
|
||||||
. += copytext(text, last_found, found)
|
start = i ? sep : qmark
|
||||||
last_found = found + delim_len
|
output += "[start][key]=[list[key]]"
|
||||||
while(found)
|
i++
|
||||||
|
return output
|
||||||
//Case Sensitive!
|
|
||||||
/proc/text2listEx(text, delimiter="\n")
|
//Converts a string into a list by splitting the string at each delimiter found. (discarding the seperator)
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/text2listEx() called tick#: [world.time]")
|
/proc/text2list(text, delimiter = "\n")
|
||||||
var/delim_len = length(delimiter)
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/text2list() called tick#: [world.time]")
|
||||||
if(delim_len < 1) return list(text)
|
var/delim_len = length(delimiter)
|
||||||
. = list()
|
if(delim_len < 1) return list(text)
|
||||||
var/last_found = 1
|
. = list()
|
||||||
var/found
|
var/last_found = 1
|
||||||
do
|
var/found
|
||||||
found = findtextEx(text, delimiter, last_found, 0)
|
do
|
||||||
. += copytext(text, last_found, found)
|
found = findtext(text, delimiter, last_found, 0)
|
||||||
last_found = found + delim_len
|
. += copytext(text, last_found, found)
|
||||||
while(found)
|
last_found = found + delim_len
|
||||||
|
while(found)
|
||||||
//Splits the text of a file at seperator and returns them in a list.
|
|
||||||
/proc/file2list(filename, seperator="\n")
|
//Case Sensitive!
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/file2list() called tick#: [world.time]")
|
/proc/text2listEx(text, delimiter="\n")
|
||||||
return text2list(return_file_text(filename),seperator)
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/text2listEx() called tick#: [world.time]")
|
||||||
|
var/delim_len = length(delimiter)
|
||||||
|
if(delim_len < 1) return list(text)
|
||||||
//Turns a direction into text
|
. = list()
|
||||||
|
var/last_found = 1
|
||||||
/proc/dir2text(direction)
|
var/found
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/dir2text() called tick#: [world.time]")
|
do
|
||||||
switch(direction)
|
found = findtextEx(text, delimiter, last_found, 0)
|
||||||
if(1.0)
|
. += copytext(text, last_found, found)
|
||||||
return "north"
|
last_found = found + delim_len
|
||||||
if(2.0)
|
while(found)
|
||||||
return "south"
|
|
||||||
if(4.0)
|
//Splits the text of a file at seperator and returns them in a list.
|
||||||
return "east"
|
/proc/file2list(filename, seperator="\n")
|
||||||
if(8.0)
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/file2list() called tick#: [world.time]")
|
||||||
return "west"
|
return text2list(return_file_text(filename),seperator)
|
||||||
if(5.0)
|
|
||||||
return "northeast"
|
|
||||||
if(6.0)
|
//Turns a direction into text
|
||||||
return "southeast"
|
|
||||||
if(9.0)
|
/proc/dir2text(direction)
|
||||||
return "northwest"
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/dir2text() called tick#: [world.time]")
|
||||||
if(10.0)
|
switch(direction)
|
||||||
return "southwest"
|
if(1.0)
|
||||||
else
|
return "north"
|
||||||
return
|
if(2.0)
|
||||||
|
return "south"
|
||||||
//Turns text into proper directions
|
if(4.0)
|
||||||
/proc/text2dir(direction)
|
return "east"
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/text2dir() called tick#: [world.time]")
|
if(8.0)
|
||||||
switch(uppertext(direction))
|
return "west"
|
||||||
if("NORTH")
|
if(5.0)
|
||||||
return 1
|
return "northeast"
|
||||||
if("SOUTH")
|
if(6.0)
|
||||||
return 2
|
return "southeast"
|
||||||
if("EAST")
|
if(9.0)
|
||||||
return 4
|
return "northwest"
|
||||||
if("WEST")
|
if(10.0)
|
||||||
return 8
|
return "southwest"
|
||||||
if("NORTHEAST")
|
else
|
||||||
return 5
|
return
|
||||||
if("NORTHWEST")
|
|
||||||
return 9
|
//Turns text into proper directions
|
||||||
if("SOUTHEAST")
|
/proc/text2dir(direction)
|
||||||
return 6
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/text2dir() called tick#: [world.time]")
|
||||||
if("SOUTHWEST")
|
switch(uppertext(direction))
|
||||||
return 10
|
if("NORTH")
|
||||||
else
|
return 1
|
||||||
return
|
if("SOUTH")
|
||||||
|
return 2
|
||||||
//Converts an angle (degrees) into an ss13 direction
|
if("EAST")
|
||||||
/proc/angle2dir(var/degree)
|
return 4
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/angle2dir() called tick#: [world.time]")
|
if("WEST")
|
||||||
degree = ((degree+22.5)%365)
|
return 8
|
||||||
if(degree < 45) return NORTH
|
if("NORTHEAST")
|
||||||
if(degree < 90) return NORTHEAST
|
return 5
|
||||||
if(degree < 135) return EAST
|
if("NORTHWEST")
|
||||||
if(degree < 180) return SOUTHEAST
|
return 9
|
||||||
if(degree < 225) return SOUTH
|
if("SOUTHEAST")
|
||||||
if(degree < 270) return SOUTHWEST
|
return 6
|
||||||
if(degree < 315) return WEST
|
if("SOUTHWEST")
|
||||||
return NORTH|WEST
|
return 10
|
||||||
|
else
|
||||||
//returns the north-zero clockwise angle in degrees, given a direction
|
return
|
||||||
|
|
||||||
/proc/dir2angle(var/D)
|
//Converts an angle (degrees) into an ss13 direction
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/dir2angle() called tick#: [world.time]")
|
/proc/angle2dir(var/degree)
|
||||||
switch(D)
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/angle2dir() called tick#: [world.time]")
|
||||||
if(NORTH) return 0
|
degree = ((degree+22.5)%365)
|
||||||
if(SOUTH) return 180
|
if(degree < 45) return NORTH
|
||||||
if(EAST) return 90
|
if(degree < 90) return NORTHEAST
|
||||||
if(WEST) return 270
|
if(degree < 135) return EAST
|
||||||
if(NORTHEAST) return 45
|
if(degree < 180) return SOUTHEAST
|
||||||
if(SOUTHEAST) return 135
|
if(degree < 225) return SOUTH
|
||||||
if(NORTHWEST) return 315
|
if(degree < 270) return SOUTHWEST
|
||||||
if(SOUTHWEST) return 225
|
if(degree < 315) return WEST
|
||||||
else return null
|
return NORTH|WEST
|
||||||
|
|
||||||
//Returns the angle in english
|
//returns the north-zero clockwise angle in degrees, given a direction
|
||||||
/proc/angle2text(var/degree)
|
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/angle2text() called tick#: [world.time]")
|
/proc/dir2angle(var/D)
|
||||||
return dir2text(angle2dir(degree))
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/dir2angle() called tick#: [world.time]")
|
||||||
|
switch(D)
|
||||||
//Converts a blend_mode constant to one acceptable to icon.Blend()
|
if(NORTH) return 0
|
||||||
/proc/blendMode2iconMode(blend_mode)
|
if(SOUTH) return 180
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/blendMode2iconMode() called tick#: [world.time]")
|
if(EAST) return 90
|
||||||
switch(blend_mode)
|
if(WEST) return 270
|
||||||
if(BLEND_MULTIPLY) return ICON_MULTIPLY
|
if(NORTHEAST) return 45
|
||||||
if(BLEND_ADD) return ICON_ADD
|
if(SOUTHEAST) return 135
|
||||||
if(BLEND_SUBTRACT) return ICON_SUBTRACT
|
if(NORTHWEST) return 315
|
||||||
else return ICON_OVERLAY
|
if(SOUTHWEST) return 225
|
||||||
|
else return null
|
||||||
//Converts a rights bitfield into a string
|
|
||||||
/proc/rights2text(rights,seperator="")
|
//Returns the angle in english
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/rights2text() called tick#: [world.time]")
|
/proc/angle2text(var/degree)
|
||||||
if(rights & R_BUILDMODE) . += "[seperator]+BUILDMODE"
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/angle2text() called tick#: [world.time]")
|
||||||
if(rights & R_ADMIN) . += "[seperator]+ADMIN"
|
return dir2text(angle2dir(degree))
|
||||||
if(rights & R_BAN) . += "[seperator]+BAN"
|
|
||||||
if(rights & R_FUN) . += "[seperator]+FUN"
|
//Converts a blend_mode constant to one acceptable to icon.Blend()
|
||||||
if(rights & R_SERVER) . += "[seperator]+SERVER"
|
/proc/blendMode2iconMode(blend_mode)
|
||||||
if(rights & R_DEBUG) . += "[seperator]+DEBUG"
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/blendMode2iconMode() called tick#: [world.time]")
|
||||||
if(rights & R_POSSESS) . += "[seperator]+POSSESS"
|
switch(blend_mode)
|
||||||
if(rights & R_PERMISSIONS) . += "[seperator]+PERMISSIONS"
|
if(BLEND_MULTIPLY) return ICON_MULTIPLY
|
||||||
if(rights & R_STEALTH) . += "[seperator]+STEALTH"
|
if(BLEND_ADD) return ICON_ADD
|
||||||
if(rights & R_REJUVINATE) . += "[seperator]+REJUVINATE"
|
if(BLEND_SUBTRACT) return ICON_SUBTRACT
|
||||||
if(rights & R_VAREDIT) . += "[seperator]+VAREDIT"
|
else return ICON_OVERLAY
|
||||||
if(rights & R_SOUNDS) . += "[seperator]+SOUND"
|
|
||||||
if(rights & R_SPAWN) . += "[seperator]+SPAWN"
|
//Converts a rights bitfield into a string
|
||||||
if(rights & R_MOD) . += "[seperator]+MODERATOR"
|
/proc/rights2text(rights,seperator="")
|
||||||
if(rights & R_ADMINBUS) . += "[seperator]+ADMINBUS"
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/rights2text() called tick#: [world.time]")
|
||||||
return .
|
if(rights & R_BUILDMODE) . += "[seperator]+BUILDMODE"
|
||||||
|
if(rights & R_ADMIN) . += "[seperator]+ADMIN"
|
||||||
/proc/ui_style2icon(ui_style)
|
if(rights & R_BAN) . += "[seperator]+BAN"
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/ui_style2icon() called tick#: [world.time]")
|
if(rights & R_FUN) . += "[seperator]+FUN"
|
||||||
switch(ui_style)
|
if(rights & R_SERVER) . += "[seperator]+SERVER"
|
||||||
if("old") return 'icons/mob/screen1_old.dmi'
|
if(rights & R_DEBUG) . += "[seperator]+DEBUG"
|
||||||
if("Orange") return 'icons/mob/screen1_Orange.dmi'
|
if(rights & R_POSSESS) . += "[seperator]+POSSESS"
|
||||||
else return 'icons/mob/screen1_Midnight.dmi'
|
if(rights & R_PERMISSIONS) . += "[seperator]+PERMISSIONS"
|
||||||
|
if(rights & R_STEALTH) . += "[seperator]+STEALTH"
|
||||||
/proc/num2septext(var/theNum, var/sigFig = 7,var/sep=",") // default sigFig (1,000,000)
|
if(rights & R_REJUVINATE) . += "[seperator]+REJUVINATE"
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/num2septext() called tick#: [world.time]")
|
if(rights & R_VAREDIT) . += "[seperator]+VAREDIT"
|
||||||
var/finalNum = num2text(theNum, sigFig)
|
if(rights & R_SOUNDS) . += "[seperator]+SOUND"
|
||||||
|
if(rights & R_SPAWN) . += "[seperator]+SPAWN"
|
||||||
// Start from the end, or from the decimal point
|
if(rights & R_MOD) . += "[seperator]+MODERATOR"
|
||||||
var/end = findtextEx(finalNum, ".") || length(finalNum) + 1
|
if(rights & R_ADMINBUS) . += "[seperator]+ADMINBUS"
|
||||||
|
return .
|
||||||
// Moving towards start of string, insert comma every 3 characters
|
|
||||||
for(var/pos = end - 3, pos > 1, pos -= 3)
|
/proc/ui_style2icon(ui_style)
|
||||||
finalNum = copytext(finalNum, 1, pos) + sep + copytext(finalNum, pos)
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/ui_style2icon() called tick#: [world.time]")
|
||||||
|
switch(ui_style)
|
||||||
|
if("old") return 'icons/mob/screen1_old.dmi'
|
||||||
|
if("Orange") return 'icons/mob/screen1_Orange.dmi'
|
||||||
|
else return 'icons/mob/screen1_Midnight.dmi'
|
||||||
|
|
||||||
|
/proc/num2septext(var/theNum, var/sigFig = 7,var/sep=",") // default sigFig (1,000,000)
|
||||||
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/num2septext() called tick#: [world.time]")
|
||||||
|
var/finalNum = num2text(theNum, sigFig)
|
||||||
|
|
||||||
|
// Start from the end, or from the decimal point
|
||||||
|
var/end = findtextEx(finalNum, ".") || length(finalNum) + 1
|
||||||
|
|
||||||
|
// Moving towards start of string, insert comma every 3 characters
|
||||||
|
for(var/pos = end - 3, pos > 1, pos -= 3)
|
||||||
|
finalNum = copytext(finalNum, 1, pos) + sep + copytext(finalNum, pos)
|
||||||
|
|
||||||
return finalNum
|
return finalNum
|
||||||
@@ -12,128 +12,118 @@
|
|||||||
Articles:
|
Articles:
|
||||||
- <http://en.wikipedia.org/wiki/Abstract_syntax_tree>
|
- <http://en.wikipedia.org/wiki/Abstract_syntax_tree>
|
||||||
*/
|
*/
|
||||||
var
|
|
||||||
const
|
|
||||||
/*
|
/*
|
||||||
Constants: Operator Precedence
|
Constants: Operator Precedence
|
||||||
OOP_OR - Logical or
|
OOP_OR - Logical or
|
||||||
OOP_AND - Logical and
|
OOP_AND - Logical and
|
||||||
OOP_BIT - Bitwise operations
|
OOP_BIT - Bitwise operations
|
||||||
OOP_EQUAL - Equality checks
|
OOP_EQUAL - Equality checks
|
||||||
OOP_COMPARE - Greater than, less then, etc
|
OOP_COMPARE - Greater than, less then, etc
|
||||||
OOP_ADD - Addition and subtraction
|
OOP_ADD - Addition and subtraction
|
||||||
OOP_MULTIPLY - Multiplication and division
|
OOP_MULTIPLY - Multiplication and division
|
||||||
OOP_POW - Exponents
|
OOP_POW - Exponents
|
||||||
OOP_UNARY - Unary Operators
|
OOP_UNARY - Unary Operators
|
||||||
OOP_GROUP - Parentheses
|
OOP_GROUP - Parentheses
|
||||||
*/
|
*/
|
||||||
OOP_OR = 1 //||
|
/var/const/OOP_OR = 1 //||
|
||||||
OOP_AND = OOP_OR + 1 //&&
|
/var/const/OOP_AND = OOP_OR + 1 //&&
|
||||||
OOP_BIT = OOP_AND + 1 //&, |
|
/var/const/OOP_BIT = OOP_AND + 1 //&, |
|
||||||
OOP_EQUAL = OOP_BIT + 1 //==, !=
|
/var/const/OOP_EQUAL = OOP_BIT + 1 //==, !=
|
||||||
OOP_COMPARE = OOP_EQUAL + 1 //>, <, >=, <=
|
/var/const/OOP_COMPARE = OOP_EQUAL + 1 //>, <, >=, <=
|
||||||
OOP_ADD = OOP_COMPARE + 1 //+, -
|
/var/const/OOP_ADD = OOP_COMPARE + 1 //+, -
|
||||||
OOP_MULTIPLY= OOP_ADD + 1 //*, /, %
|
/var/const/OOP_MULTIPLY= OOP_ADD + 1 //*, /, %
|
||||||
OOP_POW = OOP_MULTIPLY+ 1 //^
|
/var/const/OOP_POW = OOP_MULTIPLY + 1 //^
|
||||||
OOP_UNARY = OOP_POW + 1 //!
|
/var/const/OOP_UNARY = OOP_POW + 1 //!
|
||||||
OOP_GROUP = OOP_UNARY + 1 //()
|
/var/const/OOP_GROUP = OOP_UNARY + 1 //()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: node
|
Class: node
|
||||||
*/
|
*/
|
||||||
/node
|
/datum/node/proc/ToString()
|
||||||
proc
|
return "[src.type]"
|
||||||
ToString()
|
|
||||||
return "[src.type]"
|
|
||||||
/*
|
/*
|
||||||
Class: identifier
|
Class: identifier
|
||||||
*/
|
*/
|
||||||
/node/identifier
|
/datum/node/identifier
|
||||||
var
|
var/id_name
|
||||||
id_name
|
|
||||||
|
|
||||||
New(id)
|
/datum/node/identifier/New(id)
|
||||||
.=..()
|
. = ..()
|
||||||
src.id_name=id
|
src.id_name = id
|
||||||
|
|
||||||
ToString()
|
/datum/node/identifier/ToString()
|
||||||
return id_name
|
return id_name
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: expression
|
Class: expression
|
||||||
*/
|
*/
|
||||||
/node/expression
|
/datum/node/expression
|
||||||
/*
|
/*
|
||||||
Class: operator
|
Class: operator
|
||||||
See <Binary Operators> and <Unary Operators> for subtypes.
|
See <Binary Operators> and <Unary Operators> for subtypes.
|
||||||
*/
|
*/
|
||||||
/node/expression/operator
|
/datum/node/expression/operator
|
||||||
var
|
var/datum/node/expression/exp
|
||||||
node/expression/exp
|
var/token = "" // Used when giving type mismatches.
|
||||||
tmp
|
var/tmp/name
|
||||||
name
|
var/tmp/precedence
|
||||||
precedence
|
|
||||||
|
|
||||||
New()
|
/datum/node/expression/operator/New()
|
||||||
.=..()
|
.=..()
|
||||||
if(!src.name) src.name="[src.type]"
|
if(!src.name)
|
||||||
|
src.name = "[src.type]"
|
||||||
|
|
||||||
ToString()
|
/datum/node/expression/operator/ToString()
|
||||||
return "operator: [name]"
|
return "operator: [name]"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: FunctionCall
|
Class: FunctionCall
|
||||||
*/
|
*/
|
||||||
/node/expression/FunctionCall
|
/datum/node/expression/FunctionCall
|
||||||
//Function calls can also be expressions or statements.
|
//Function calls can also be expressions or statements.
|
||||||
var
|
var/func_name
|
||||||
func_name
|
var/datum/node/identifier/object
|
||||||
node/identifier/object
|
var/list/parameters = list()
|
||||||
list/parameters=new
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: literal
|
Class: literal
|
||||||
*/
|
*/
|
||||||
/node/expression/value/literal
|
/datum/node/expression/value/literal
|
||||||
var
|
var/value
|
||||||
value
|
|
||||||
|
|
||||||
New(value)
|
/datum/node/expression/value/literal/New(value)
|
||||||
.=..()
|
. = ..()
|
||||||
src.value=value
|
src.value = value
|
||||||
|
|
||||||
ToString()
|
/datum/node/expression/value/literal/ToString()
|
||||||
return src.value
|
return src.value
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: variable
|
Class: variable
|
||||||
*/
|
*/
|
||||||
/node/expression/value/variable
|
/datum/node/expression/value/variable
|
||||||
var
|
var/datum/node/object //Either a node/identifier or another node/expression/value/variable which points to the object
|
||||||
node
|
var/datum/node/identifier/id
|
||||||
object //Either a node/identifier or another node/expression/value/variable which points to the object
|
|
||||||
node/identifier
|
|
||||||
id
|
|
||||||
|
|
||||||
|
|
||||||
New(ident)
|
/datum/node/expression/value/variable/New(ident)
|
||||||
.=..()
|
. = ..()
|
||||||
id=ident
|
id = ident
|
||||||
if(istext(id))id=new(id)
|
if(istext(id))
|
||||||
|
id = new(id)
|
||||||
|
|
||||||
ToString()
|
/datum/node/expression/value/variable/ToString()
|
||||||
return src.id.ToString()
|
return src.id.ToString()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: reference
|
Class: reference
|
||||||
*/
|
*/
|
||||||
/node/expression/value/reference
|
/datum/node/expression/value/reference
|
||||||
var
|
var/datum/value
|
||||||
datum/value
|
|
||||||
|
|
||||||
New(value)
|
/datum/node/expression/value/reference/New(value)
|
||||||
.=..()
|
. = ..()
|
||||||
src.value=value
|
src.value = value
|
||||||
|
|
||||||
ToString()
|
/datum/node/expression/value/reference/ToString()
|
||||||
return "ref: [src.value] ([src.value.type])"
|
return "ref: [src.value] ([src.value.type])"
|
||||||
@@ -8,13 +8,11 @@
|
|||||||
it is possible to have a function definition inside of any type of block (such as in an if statement or another function),
|
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.
|
and not just in the global scope as in many languages.
|
||||||
*/
|
*/
|
||||||
/node/BlockDefinition
|
/datum/node/BlockDefinition
|
||||||
var/list
|
var/list/statements = new
|
||||||
statements = new
|
var/list/functions = new
|
||||||
functions = new
|
var/list/initial_variables = new
|
||||||
initial_variables = new
|
|
||||||
|
|
||||||
proc
|
|
||||||
/*
|
/*
|
||||||
Proc: SetVar
|
Proc: SetVar
|
||||||
Defines a permanent variable. The variable will not be deleted when it goes out of scope.
|
Defines a permanent variable. The variable will not be deleted when it goes out of scope.
|
||||||
@@ -26,8 +24,8 @@
|
|||||||
See Also:
|
See Also:
|
||||||
- <n_Interpreter.SetVar()>
|
- <n_Interpreter.SetVar()>
|
||||||
*/
|
*/
|
||||||
SetVar(name, value)
|
/datum/node/BlockDefinition/proc/SetVar(name, value)
|
||||||
initial_variables[name]=value
|
initial_variables[name] = value
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -35,14 +33,13 @@
|
|||||||
A block object representing the global scope.
|
A block object representing the global scope.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
GlobalBlock
|
/datum/node/BlockDefinition/GlobalBlock/New()
|
||||||
New()
|
initial_variables["null"] = null
|
||||||
initial_variables["null"]=null
|
return ..()
|
||||||
return ..()
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: FunctionBlock
|
Class: FunctionBlock
|
||||||
A block representing a function body.
|
A block representing a function body.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
FunctionBlock
|
/datum/node/BlockDefinition/FunctionBlock
|
||||||
@@ -7,8 +7,8 @@
|
|||||||
Class: binary
|
Class: binary
|
||||||
Represents a binary operator in the AST. A binary operator takes two operands (ie x and y) and returns a value.
|
Represents a binary operator in the AST. A binary operator takes two operands (ie x and y) and returns a value.
|
||||||
*/
|
*/
|
||||||
/node/expression/operator/binary
|
/datum/node/expression/operator/binary
|
||||||
var/node/expression/exp2
|
var/datum/node/expression/exp2
|
||||||
|
|
||||||
////////// Comparison Operators //////////
|
////////// Comparison Operators //////////
|
||||||
/*
|
/*
|
||||||
@@ -16,159 +16,176 @@
|
|||||||
Returns true if x = y.
|
Returns true if x = y.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
Equal
|
/datum/node/expression/operator/binary/Equal
|
||||||
precedence=OOP_EQUAL
|
token = "=="
|
||||||
|
precedence = OOP_EQUAL
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: NotEqual
|
Class: NotEqual
|
||||||
Returns true if x and y aren't equal.
|
Returns true if x and y aren't equal.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
NotEqual
|
/datum/node/expression/operator/binary/NotEqual
|
||||||
precedence=OOP_EQUAL
|
token = "!="
|
||||||
|
precedence = OOP_EQUAL
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: Greater
|
Class: Greater
|
||||||
Returns true if x > y.
|
Returns true if x > y.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
Greater
|
/datum/node/expression/operator/binary/Greater
|
||||||
precedence=OOP_COMPARE
|
token = ">"
|
||||||
|
precedence = OOP_COMPARE
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: Less
|
Class: Less
|
||||||
Returns true if x < y.
|
Returns true if x < y.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
Less
|
/datum/node/expression/operator/binary/Less
|
||||||
precedence=OOP_COMPARE
|
token = "<"
|
||||||
|
precedence = OOP_COMPARE
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: GreaterOrEqual
|
Class: GreaterOrEqual
|
||||||
Returns true if x >= y.
|
Returns true if x >= y.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
GreaterOrEqual
|
/datum/node/expression/operator/binary/GreaterOrEqual
|
||||||
precedence=OOP_COMPARE
|
token = ">="
|
||||||
|
precedence = OOP_COMPARE
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: LessOrEqual
|
Class: LessOrEqual
|
||||||
Returns true if x <= y.
|
Returns true if x <= y.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
LessOrEqual
|
/datum/node/expression/operator/binary/LessOrEqual
|
||||||
precedence=OOP_COMPARE
|
token = "<="
|
||||||
|
precedence = OOP_COMPARE
|
||||||
|
|
||||||
|
|
||||||
////////// Logical Operators //////////
|
////////// Logical Operators //////////
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: LogicalAnd
|
Class: LogicalAnd
|
||||||
Returns true if x and y are true.
|
Returns true if x and y are true.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
LogicalAnd
|
/datum/node/expression/operator/binary/LogicalAnd
|
||||||
precedence=OOP_AND
|
token = "&&"
|
||||||
|
precedence = OOP_AND
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: LogicalOr
|
Class: LogicalOr
|
||||||
Returns true if x, y, or both are true.
|
Returns true if x, y, or both are true.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
LogicalOr
|
/datum/node/expression/operator/binary/LogicalOr
|
||||||
precedence=OOP_OR
|
token = "||"
|
||||||
|
precedence = OOP_OR
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: LogicalXor
|
Class: LogicalXor
|
||||||
Returns true if either x or y but not both are true.
|
Returns true if either x or y but not both are true.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
LogicalXor //Not implemented in nS
|
/datum/node/expression/operator/binary/LogicalXor //Not implemented in nS
|
||||||
precedence=OOP_OR
|
precedence = OOP_OR
|
||||||
|
|
||||||
|
|
||||||
////////// Bitwise Operators //////////
|
////////// Bitwise Operators //////////
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: BitwiseAnd
|
Class: BitwiseAnd
|
||||||
Performs a bitwise and operation.
|
Performs a bitwise and operation.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
011 & 110 = 010
|
011 & 110 = 010
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
BitwiseAnd
|
/datum/node/expression/operator/binary/BitwiseAnd
|
||||||
precedence=OOP_BIT
|
token = "&"
|
||||||
|
precedence = OOP_BIT
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: BitwiseOr
|
Class: BitwiseOr
|
||||||
Performs a bitwise or operation.
|
Performs a bitwise or operation.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
011 | 110 = 111
|
011 | 110 = 111
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
BitwiseOr
|
/datum/node/expression/operator/binary/BitwiseOr
|
||||||
precedence=OOP_BIT
|
token = "|"
|
||||||
|
precedence = OOP_BIT
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: BitwiseXor
|
Class: BitwiseXor
|
||||||
Performs a bitwise exclusive or operation.
|
Performs a bitwise exclusive or operation.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
011 xor 110 = 101
|
011 xor 110 = 101
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
BitwiseXor
|
/datum/node/expression/operator/binary/BitwiseXor
|
||||||
precedence=OOP_BIT
|
token = "`"
|
||||||
|
precedence = OOP_BIT
|
||||||
|
|
||||||
|
|
||||||
////////// Arithmetic Operators //////////
|
////////// Arithmetic Operators //////////
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: Add
|
Class: Add
|
||||||
Returns the sum of x and y.
|
Returns the sum of x and y.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
Add
|
/datum/node/expression/operator/binary/Add
|
||||||
precedence=OOP_ADD
|
token = "+"
|
||||||
|
precedence = OOP_ADD
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: Subtract
|
Class: Subtract
|
||||||
Returns the difference of x and y.
|
Returns the difference of x and y.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
Subtract
|
/datum/node/expression/operator/binary/Subtract
|
||||||
precedence=OOP_ADD
|
token = "-"
|
||||||
|
precedence = OOP_ADD
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: Multiply
|
Class: Multiply
|
||||||
Returns the product of x and y.
|
Returns the product of x and y.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
Multiply
|
/datum/node/expression/operator/binary/Multiply
|
||||||
precedence=OOP_MULTIPLY
|
token = "*"
|
||||||
|
precedence = OOP_MULTIPLY
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: Divide
|
Class: Divide
|
||||||
Returns the quotient of x and y.
|
Returns the quotient of x and y.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
Divide
|
/datum/node/expression/operator/binary/Divide
|
||||||
precedence=OOP_MULTIPLY
|
token = "/"
|
||||||
|
precedence = OOP_MULTIPLY
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: Power
|
Class: Power
|
||||||
Returns x raised to the power of y.
|
Returns x raised to the power of y.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
Power
|
/datum/node/expression/operator/binary/Power
|
||||||
precedence=OOP_POW
|
token = "^"
|
||||||
|
precedence = OOP_POW
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: Modulo
|
Class: Modulo
|
||||||
Returns the remainder of x / y.
|
Returns the remainder of x / y.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
Modulo
|
/datum/node/expression/operator/binary/Modulo
|
||||||
precedence=OOP_MULTIPLY
|
token = "%"
|
||||||
|
precedence = OOP_MULTIPLY
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
Class: unary
|
Class: unary
|
||||||
Represents a unary operator in the AST. Unary operators take a single operand (referred to as x below) and return a value.
|
Represents a unary operator in the AST. Unary operators take a single operand (referred to as x below) and return a value.
|
||||||
*/
|
*/
|
||||||
/node/expression/operator/unary
|
/datum/node/expression/operator/unary
|
||||||
precedence=OOP_UNARY
|
precedence = OOP_UNARY
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: LogicalNot
|
Class: LogicalNot
|
||||||
@@ -16,8 +16,8 @@
|
|||||||
!true = false and !false = true
|
!true = false and !false = true
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
LogicalNot
|
/datum/node/expression/operator/unary/LogicalNot
|
||||||
name="logical not"
|
name = "logical not"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: BitwiseNot
|
Class: BitwiseNot
|
||||||
@@ -27,25 +27,25 @@
|
|||||||
~10 (decimal 2) = 01 (decimal 1).
|
~10 (decimal 2) = 01 (decimal 1).
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
BitwiseNot
|
/datum/node/expression/operator/unary/BitwiseNot
|
||||||
name="bitwise not"
|
name = "bitwise not"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: Minus
|
Class: Minus
|
||||||
Returns -x.
|
Returns -x.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
Minus
|
/datum/node/expression/operator/unary/Minus
|
||||||
name="minus"
|
name = "minus"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: group
|
Class: group
|
||||||
A special unary operator representing a value in parentheses.
|
A special unary operator representing a value in parentheses.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
group
|
/datum/node/expression/operator/unary/group
|
||||||
precedence=OOP_GROUP
|
precedence = OOP_GROUP
|
||||||
|
|
||||||
New(node/expression/exp)
|
/datum/node/expression/operator/unary/New(var/datum/node/expression/exp)
|
||||||
src.exp=exp
|
src.exp = exp
|
||||||
return ..()
|
return ..()
|
||||||
|
|||||||
@@ -5,122 +5,106 @@
|
|||||||
Class: statement
|
Class: statement
|
||||||
An object representing a single instruction run by an interpreter.
|
An object representing a single instruction run by an interpreter.
|
||||||
*/
|
*/
|
||||||
/node/statement
|
/datum/node/statement
|
||||||
/*
|
/*
|
||||||
Class: FunctionCall
|
Class: FunctionCall
|
||||||
Represents a call to a function.
|
Represents a call to a function.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
FunctionCall
|
/datum/node/statement/FunctionCall
|
||||||
var
|
var/func_name
|
||||||
func_name
|
var/datum/node/identifier/object
|
||||||
node/identifier/object
|
var/list/parameters = new
|
||||||
list/parameters=new
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: FunctionDefinition
|
Class: FunctionDefinition
|
||||||
Defines a function.
|
Defines a function.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
FunctionDefinition
|
/datum/node/statement/FunctionDefinition
|
||||||
var
|
var/func_name
|
||||||
func_name
|
var/list/parameters = new
|
||||||
list/parameters=new
|
var/datum/node/BlockDefinition/FunctionBlock/block
|
||||||
node/BlockDefinition/FunctionBlock/block
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: VariableAssignment
|
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.
|
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:
|
Notes:
|
||||||
If a variable with the same name exists in a higher block, the value will be assigned to it. If not,
|
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>.
|
a new variable is created in the current block. To force creation of a new variable, use <VariableDeclaration>.
|
||||||
|
|
||||||
See Also:
|
See Also:
|
||||||
- <VariableDeclaration>
|
- <VariableDeclaration>
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
VariableAssignment
|
/datum/node/statement/VariableAssignment
|
||||||
var
|
var/datum/node/identifier/object
|
||||||
node
|
var/datum/node/identifier/var_name
|
||||||
identifier
|
var/datum/node/expression/value
|
||||||
object
|
|
||||||
var_name
|
|
||||||
expression/value
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: VariableDeclaration
|
Class: VariableDeclaration
|
||||||
Intializes a local variable to a null value.
|
Intializes a local variable to a null value.
|
||||||
|
|
||||||
See Also:
|
See Also:
|
||||||
- <VariableAssignment>
|
- <VariableAssignment>
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
VariableDeclaration
|
/datum/node/statement/VariableDeclaration
|
||||||
var
|
var/datum/node/identifier/object
|
||||||
node
|
var/datum/node/identifier/var_name
|
||||||
identifier
|
|
||||||
object
|
|
||||||
var_name
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: IfStatement
|
Class: IfStatement
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
IfStatement
|
/datum/node/statement/IfStatement
|
||||||
var
|
var/skip = 0
|
||||||
skip = 0
|
var/datum/node/BlockDefinition/block
|
||||||
node
|
var/datum/node/BlockDefinition/else_block //may be null
|
||||||
BlockDefinition
|
var/datum/node/expression/cond
|
||||||
block
|
var/datum/node/statement/else_if
|
||||||
else_block //may be null
|
|
||||||
expression/cond
|
|
||||||
statement/else_if
|
|
||||||
|
|
||||||
ElseIf
|
/datum/node/statement/IfStatement/ElseIf
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: WhileLoop
|
Class: WhileLoop
|
||||||
Loops while a given condition is true.
|
Loops while a given condition is true.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
WhileLoop
|
/datum/node/statement/WhileLoop
|
||||||
var
|
var/datum/node/BlockDefinition/block
|
||||||
node
|
var/datum/node/expression/cond
|
||||||
BlockDefinition/block
|
|
||||||
expression/cond
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: ForLoop
|
Class: ForLoop
|
||||||
Loops while test is true, initializing a variable, increasing the variable
|
Loops while test is true, initializing a variable, increasing the variable
|
||||||
*/
|
*/
|
||||||
ForLoop
|
/datum/node/statement/ForLoop
|
||||||
var
|
var/datum/node/BlockDefinition/block
|
||||||
node
|
var/datum/node/expression/test
|
||||||
BlockDefinition/block
|
var/datum/node/expression/init
|
||||||
expression/test
|
var/datum/node/expression/increment
|
||||||
expression/init
|
|
||||||
expression/increment
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: BreakStatement
|
Class: BreakStatement
|
||||||
Ends a loop.
|
Ends a loop.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
BreakStatement
|
/datum/node/statement/BreakStatement
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: ContinueStatement
|
Class: ContinueStatement
|
||||||
Skips to the next iteration of a loop.
|
Skips to the next iteration of a loop.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
ContinueStatement
|
/datum/node/statement/ContinueStatement
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: ReturnStatement
|
Class: ReturnStatement
|
||||||
Ends the function and returns a value.
|
Ends the function and returns a value.
|
||||||
*/
|
*/
|
||||||
//
|
//
|
||||||
ReturnStatement
|
/datum/node/statement/ReturnStatement
|
||||||
var
|
var/datum/node/expression/value
|
||||||
node/expression/value
|
|
||||||
@@ -5,137 +5,165 @@
|
|||||||
Class: scriptError
|
Class: scriptError
|
||||||
An error scanning or parsing the source code.
|
An error scanning or parsing the source code.
|
||||||
*/
|
*/
|
||||||
/scriptError
|
/datum/scriptError
|
||||||
var
|
|
||||||
/*
|
/*
|
||||||
Var: message
|
Var: message
|
||||||
A message describing the problem.
|
A message describing the problem.
|
||||||
*/
|
*/
|
||||||
message
|
var/message
|
||||||
New(msg=null)
|
|
||||||
if(msg)message=msg
|
|
||||||
|
|
||||||
BadToken
|
/datum/scriptError/New(msg = null)
|
||||||
message="Unexpected token: "
|
if(msg)message = msg
|
||||||
var/token/token
|
|
||||||
New(token/t)
|
|
||||||
token=t
|
|
||||||
if(t&&t.line) message="[t.line]: [message]"
|
|
||||||
if(istype(t))message+="[t.value]"
|
|
||||||
else message+="[t]"
|
|
||||||
|
|
||||||
InvalidID
|
/datum/scriptError/BadToken
|
||||||
parent_type=/scriptError/BadToken
|
message = "Unexpected token: "
|
||||||
message="Invalid identifier name: "
|
var/datum/token/token
|
||||||
|
|
||||||
ReservedWord
|
/datum/scriptError/BadToken/New(datum/token/t)
|
||||||
parent_type=/scriptError/BadToken
|
token = t
|
||||||
message="Identifer using reserved word: "
|
if(t && t.line)
|
||||||
|
message = "[t.line]: [message]"
|
||||||
|
|
||||||
BadNumber
|
if(istype(t))
|
||||||
parent_type=/scriptError/BadToken
|
message += "[t.value]"
|
||||||
message = "Bad number: "
|
|
||||||
|
|
||||||
BadReturn
|
else
|
||||||
var/token/token
|
message += "[t]"
|
||||||
message = "Unexpected return statement outside of a function."
|
|
||||||
New(token/t)
|
|
||||||
src.token=t
|
|
||||||
|
|
||||||
EndOfFile
|
/datum/scriptError/InvalidID
|
||||||
message = "Unexpected end of file."
|
parent_type = /datum/scriptError/BadToken
|
||||||
|
message = "Invalid identifier name: "
|
||||||
|
|
||||||
ExpectedToken
|
/datum/scriptError/ReservedWord
|
||||||
message="Expected: '"
|
parent_type = /datum/scriptError/BadToken
|
||||||
New(id, token/T)
|
message = "Identifer using reserved word: "
|
||||||
if(T && T.line) message="[T.line]: [message]"
|
|
||||||
message+="[id]'. "
|
/datum/scriptError/BadNumber
|
||||||
if(T)message+="Found '[T.value]'."
|
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]'."
|
||||||
|
|
||||||
|
|
||||||
UnterminatedComment
|
/datum/scriptError/UnterminatedComment
|
||||||
message="Unterminated multi-line comment statement: expected */"
|
message = "Unterminated multi-line comment statement: expected */"
|
||||||
|
|
||||||
DuplicateFunction
|
/datum/scriptError/DuplicateFunction/New(name, datum/token/t)
|
||||||
New(name, token/t)
|
message = "Function '[name]' defined twice."
|
||||||
message="Function '[name]' defined twice."
|
|
||||||
|
|
||||||
ParameterFunction
|
/datum/scriptError/ParameterFunction
|
||||||
message = "You cannot use a function inside a parameter."
|
message = "You cannot use a function inside a parameter."
|
||||||
|
|
||||||
New(token/t)
|
/datum/scriptError/ParameterFunction/New(datum/token/t)
|
||||||
var/line = "?"
|
var/line = "?"
|
||||||
if(t)
|
if(t)
|
||||||
line = t.line
|
line = t.line
|
||||||
message = "[line]: [message]"
|
message = "[line]: [message]"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: runtimeError
|
Class: runtimeError
|
||||||
An error thrown by the interpreter in running the script.
|
An error thrown by the interpreter in running the script.
|
||||||
*/
|
*/
|
||||||
/runtimeError
|
/datum/runtimeError
|
||||||
var
|
var/name
|
||||||
name
|
|
||||||
/*
|
/*
|
||||||
Var: message
|
Var: message
|
||||||
A basic description as to what went wrong.
|
A basic description as to what went wrong.
|
||||||
*/
|
*/
|
||||||
message
|
var/message
|
||||||
stack/stack
|
var/datum/stack/stack
|
||||||
|
|
||||||
proc
|
|
||||||
/*
|
/*
|
||||||
Proc: ToString
|
Proc: ToString
|
||||||
Returns a description of the error suitable for showing to the user.
|
Returns a description of the error suitable for showing to the user.
|
||||||
*/
|
*/
|
||||||
ToString()
|
/datum/runtimeError/proc/ToString()
|
||||||
. = "[name]: [message]"
|
. = "[name]: [message]"
|
||||||
if(!stack.Top()) return
|
if(!stack.Top())
|
||||||
.+="\nStack:"
|
return
|
||||||
while(stack.Top())
|
|
||||||
var/node/statement/FunctionCall/stmt=stack.Pop()
|
|
||||||
. += "\n\t [stmt.func_name]()"
|
|
||||||
|
|
||||||
TypeMismatch
|
. += "\nStack:"
|
||||||
name="TypeMismatchError"
|
while(stack.Top())
|
||||||
New(op, a, b)
|
var/datum/node/statement/FunctionCall/stmt = stack.Pop()
|
||||||
message="Type mismatch: '[a]' [op] '[b]'"
|
. += "\n\t [stmt.func_name]()"
|
||||||
|
|
||||||
UnexpectedReturn
|
/datum/runtimeError/TypeMismatch
|
||||||
name="UnexpectedReturnError"
|
name = "TypeMismatchError"
|
||||||
message="Unexpected return statement."
|
|
||||||
|
|
||||||
UnknownInstruction
|
/datum/runtimeError/TypeMismatch/New(op, a, b)
|
||||||
name="UnknownInstructionError"
|
message = "Type mismatch: '[a]' [op] '[b]'"
|
||||||
message="Unknown instruction type. This may be due to incompatible compiler and interpreter versions or a lack of implementation."
|
|
||||||
|
|
||||||
UndefinedVariable
|
/datum/runtimeError/TypeMismatch/unary/New(op, a)
|
||||||
name="UndefinedVariableError"
|
message = "Type mismatch: [op]'[a]'"
|
||||||
New(variable)
|
|
||||||
message="Variable '[variable]' has not been declared."
|
/datum/runtimeError/TypeMismatch/New(op, a, b)
|
||||||
|
message = "Type mismatch: '[a]' [op] '[b]'"
|
||||||
|
|
||||||
|
/datum/runtimeError/UnexpectedReturn
|
||||||
|
name = "UnexpectedReturnError"
|
||||||
|
message = "Unexpected return statement."
|
||||||
|
|
||||||
UndefinedFunction
|
/datum/runtimeError/UnknownInstruction
|
||||||
name="UndefinedFunctionError"
|
name = "UnknownInstructionError"
|
||||||
New(function)
|
message = "Unknown instruction type. This may be due to incompatible compiler and interpreter versions or a lack of implementation."
|
||||||
message="Function '[function]()' has not been defined."
|
|
||||||
|
|
||||||
DuplicateVariableDeclaration
|
/datum/runtimeError/UndefinedVariable
|
||||||
name="DuplicateVariableError"
|
name = "UndefinedVariableError"
|
||||||
New(variable)
|
|
||||||
message="Variable '[variable]' was already declared."
|
|
||||||
|
|
||||||
IterationLimitReached
|
/datum/runtimeError/UndefinedVariable/New(variable)
|
||||||
name="MaxIterationError"
|
message = "Variable '[variable]' has not been declared."
|
||||||
message="A loop has reached its maximum number of iterations."
|
|
||||||
|
|
||||||
RecursionLimitReached
|
/datum/runtimeError/UndefinedFunction
|
||||||
name="MaxRecursionError"
|
name = "UndefinedFunctionError"
|
||||||
message="The maximum amount of recursion has been reached."
|
|
||||||
|
|
||||||
DivisionByZero
|
/datum/runtimeError/UndefinedFunction/New(function)
|
||||||
name="DivideByZeroError"
|
message = "Function '[function]()' has not been defined."
|
||||||
message="Division by zero attempted."
|
|
||||||
|
|
||||||
MaxCPU
|
/datum/runtimeError/DuplicateVariableDeclaration
|
||||||
name="MaxComputationalUse"
|
name = "DuplicateVariableError"
|
||||||
message="Maximum amount of computational cycles reached (>= 1000)."
|
|
||||||
|
/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,4 +1,4 @@
|
|||||||
client/verb/tcssave()
|
/client/verb/tcssave()
|
||||||
set hidden = 1
|
set hidden = 1
|
||||||
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\client/verb/tcssave() called tick#: [world.time]")
|
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\client/verb/tcssave() called tick#: [world.time]")
|
||||||
if(mob.machine || issilicon(mob))
|
if(mob.machine || issilicon(mob))
|
||||||
@@ -9,7 +9,7 @@ client/verb/tcssave()
|
|||||||
|
|
||||||
if(Machine.SelectedServer)
|
if(Machine.SelectedServer)
|
||||||
var/obj/machinery/telecomms/server/Server = Machine.SelectedServer
|
var/obj/machinery/telecomms/server/Server = Machine.SelectedServer
|
||||||
var/tcscode=winget(src, "tcscode", "text")
|
var/tcscode = winget(src, "tcscode", "text")
|
||||||
Server.setcode( tcscode ) // this actually saves the code from input to the server
|
Server.setcode( tcscode ) // this actually saves the code from input to the server
|
||||||
src << output(null, "tcserror") // clear the errors
|
src << output(null, "tcserror") // clear the errors
|
||||||
else
|
else
|
||||||
@@ -23,7 +23,7 @@ client/verb/tcssave()
|
|||||||
src << output("<font color = red>Failed to save: Unable to locate machine. (Back up your code before exiting the window!)</font color>", "tcserror")
|
src << output("<font color = red>Failed to save: Unable to locate machine. (Back up your code before exiting the window!)</font color>", "tcserror")
|
||||||
|
|
||||||
|
|
||||||
client/verb/tcscompile()
|
/client/verb/tcscompile()
|
||||||
set hidden = 1
|
set hidden = 1
|
||||||
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\client/verb/tcscompile() called tick#: [world.time]")
|
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\client/verb/tcscompile() called tick#: [world.time]")
|
||||||
if(mob.machine || issilicon(mob))
|
if(mob.machine || issilicon(mob))
|
||||||
@@ -47,7 +47,7 @@ client/verb/tcscompile()
|
|||||||
|
|
||||||
if(compileerrors.len)
|
if(compileerrors.len)
|
||||||
src << output("<b>Compile Errors</b>", "tcserror")
|
src << output("<b>Compile Errors</b>", "tcserror")
|
||||||
for(var/scriptError/e in compileerrors)
|
for(var/datum/scriptError/e in compileerrors)
|
||||||
src << output("<font color = red>\t>[e.message]</font color>", "tcserror")
|
src << output("<font color = red>\t>[e.message]</font color>", "tcserror")
|
||||||
src << output("([compileerrors.len] errors)", "tcserror")
|
src << output("([compileerrors.len] errors)", "tcserror")
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ client/verb/tcscompile()
|
|||||||
if(M.client)
|
if(M.client)
|
||||||
M << output(null, "tcserror")
|
M << output(null, "tcserror")
|
||||||
M << output("<b>Compile Errors</b>", "tcserror")
|
M << output("<b>Compile Errors</b>", "tcserror")
|
||||||
for(var/scriptError/e in compileerrors)
|
for(var/datum/scriptError/e in compileerrors)
|
||||||
M << output("<font color = red>\t>[e.message]</font color>", "tcserror")
|
M << output("<font color = red>\t>[e.message]</font color>", "tcserror")
|
||||||
M << output("([compileerrors.len] errors)", "tcserror")
|
M << output("([compileerrors.len] errors)", "tcserror")
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ client/verb/tcscompile()
|
|||||||
src << output(null, "tcserror")
|
src << output(null, "tcserror")
|
||||||
src << output("<font color = red>Failed to compile: Unable to locate machine. (Back up your code before exiting the window!)</font color>", "tcserror")
|
src << output("<font color = red>Failed to compile: Unable to locate machine. (Back up your code before exiting the window!)</font color>", "tcserror")
|
||||||
|
|
||||||
client/verb/tcsrun()
|
/client/verb/tcsrun()
|
||||||
set hidden = 1
|
set hidden = 1
|
||||||
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\client/verb/tcsrun() called tick#: [world.time]")
|
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\client/verb/tcsrun() called tick#: [world.time]")
|
||||||
if(mob.machine || issilicon(mob))
|
if(mob.machine || issilicon(mob))
|
||||||
@@ -117,7 +117,7 @@ client/verb/tcsrun()
|
|||||||
src << output("<font color = red>Failed to run: Unable to locate machine. (Back up your code before exiting the window!)</font color>", "tcserror")
|
src << output("<font color = red>Failed to run: Unable to locate machine. (Back up your code before exiting the window!)</font color>", "tcserror")
|
||||||
|
|
||||||
|
|
||||||
client/verb/exittcs()
|
/client/verb/exittcs()
|
||||||
set hidden = 1
|
set hidden = 1
|
||||||
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\client/verb/exittcs() called tick#: [world.time]")
|
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\client/verb/exittcs() called tick#: [world.time]")
|
||||||
if(mob.machine || issilicon(mob))
|
if(mob.machine || issilicon(mob))
|
||||||
@@ -130,7 +130,7 @@ client/verb/exittcs()
|
|||||||
if(mob in Machine.viewingcode)
|
if(mob in Machine.viewingcode)
|
||||||
Machine.viewingcode.Remove(mob)
|
Machine.viewingcode.Remove(mob)
|
||||||
|
|
||||||
client/verb/tcsrevert()
|
/client/verb/tcsrevert()
|
||||||
set hidden = 1
|
set hidden = 1
|
||||||
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\client/verb/tcsrevert() called tick#: [world.time]")
|
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\client/verb/tcsrevert() called tick#: [world.time]")
|
||||||
if(mob.machine || issilicon(mob))
|
if(mob.machine || issilicon(mob))
|
||||||
@@ -160,7 +160,7 @@ client/verb/tcsrevert()
|
|||||||
src << output("<font color = red>Failed to revert: Unable to locate machine.</font color>", "tcserror")
|
src << output("<font color = red>Failed to revert: Unable to locate machine.</font color>", "tcserror")
|
||||||
|
|
||||||
|
|
||||||
client/verb/tcsclearmem()
|
/client/verb/tcsclearmem()
|
||||||
set hidden = 1
|
set hidden = 1
|
||||||
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\client/verb/tcsclearmem() called tick#: [world.time]")
|
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\client/verb/tcsclearmem() called tick#: [world.time]")
|
||||||
if(mob.machine || issilicon(mob))
|
if(mob.machine || issilicon(mob))
|
||||||
|
|||||||
@@ -1,353 +1,360 @@
|
|||||||
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33
|
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33
|
||||||
|
|
||||||
|
|
||||||
/* --- Traffic Control Scripting Language --- */
|
/* --- Traffic Control Scripting Language --- */
|
||||||
// Nanotrasen TCS Language - Made by Doohl
|
// Nanotrasen TCS Language - Made by Doohl
|
||||||
|
|
||||||
/n_Interpreter/TCS_Interpreter
|
/datum/n_Interpreter/TCS_Interpreter
|
||||||
var/datum/TCS_Compiler/Compiler
|
var/datum/TCS_Compiler/Compiler
|
||||||
|
|
||||||
HandleError(runtimeError/e)
|
/datum/n_Interpreter/TCS_Interpreter/HandleError(datum/runtimeError/e)
|
||||||
Compiler.Holder.add_entry(e.ToString(), "Execution Error")
|
Compiler.Holder.add_entry(e.ToString(), "Execution Error")
|
||||||
|
|
||||||
GC()
|
/datum/n_Interpreter/TCS_Interpreter/GC()
|
||||||
..()
|
..()
|
||||||
Compiler = null
|
Compiler = null
|
||||||
|
|
||||||
|
/datum/TCS_Compiler
|
||||||
/datum/TCS_Compiler
|
var/datum/n_Interpreter/TCS_Interpreter/interpreter
|
||||||
|
var/obj/machinery/telecomms/server/Holder // the server that is running the code
|
||||||
var/n_Interpreter/TCS_Interpreter/interpreter
|
var/ready = 1 // 1 if ready to run code
|
||||||
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 -- */
|
||||||
|
|
||||||
/* -- Set ourselves to Garbage Collect -- */
|
/datum/TCS_Compiler/proc/GC()
|
||||||
|
Holder = null
|
||||||
proc/GC()
|
if(interpreter)
|
||||||
|
interpreter.GC()
|
||||||
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\proc/GC() called tick#: [world.time]")
|
|
||||||
|
|
||||||
Holder = null
|
/* -- Compile a raw block of text -- */
|
||||||
if(interpreter)
|
|
||||||
interpreter.GC()
|
/datum/TCS_Compiler/proc/Compile(code as message)
|
||||||
|
var/datum/n_scriptOptions/nS_Options/options = new()
|
||||||
|
var/datum/n_Scanner/nS_Scanner/scanner = new(code, options)
|
||||||
/* -- Compile a raw block of text -- */
|
var/list/tokens = scanner.Scan()
|
||||||
|
var/datum/n_Parser/nS_Parser/parser = new(tokens, options)
|
||||||
proc/Compile(code as message)
|
var/datum/node/BlockDefinition/GlobalBlock/program = parser.Parse()
|
||||||
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\proc/Compile() called tick#: [world.time]")
|
|
||||||
var/n_scriptOptions/nS_Options/options = new()
|
var/list/returnerrors = list()
|
||||||
var/n_Scanner/nS_Scanner/scanner = new(code, options)
|
|
||||||
var/list/tokens = scanner.Scan()
|
returnerrors += scanner.errors
|
||||||
var/n_Parser/nS_Parser/parser = new(tokens, options)
|
returnerrors += parser.errors
|
||||||
var/node/BlockDefinition/GlobalBlock/program = parser.Parse()
|
|
||||||
|
if(returnerrors.len)
|
||||||
var/list/returnerrors = list()
|
return returnerrors
|
||||||
|
|
||||||
returnerrors += scanner.errors
|
interpreter = new(program)
|
||||||
returnerrors += parser.errors
|
interpreter.persist = 1
|
||||||
|
interpreter.Compiler= src
|
||||||
if(returnerrors.len)
|
|
||||||
return returnerrors
|
return returnerrors
|
||||||
|
|
||||||
interpreter = new(program)
|
/* -- Execute the compiled code -- */
|
||||||
interpreter.persist = 1
|
|
||||||
interpreter.Compiler= src
|
/datum/TCS_Compiler/proc/Run(var/datum/signal/signal)
|
||||||
|
if(!ready)
|
||||||
return returnerrors
|
return
|
||||||
|
|
||||||
/* -- Execute the compiled code -- */
|
if(!interpreter)
|
||||||
|
return
|
||||||
proc/Run(var/datum/signal/signal)
|
|
||||||
|
interpreter.container = src
|
||||||
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\proc/Run() called tick#: [world.time]")
|
|
||||||
|
interpreter.SetVar("PI", 3.141592653) // value of pi
|
||||||
if(!ready)
|
interpreter.SetVar("E", 2.718281828) // value of e
|
||||||
return
|
interpreter.SetVar("SQURT2", 1.414213562) // value of the square root of 2
|
||||||
|
interpreter.SetVar("FALSE", 0) // boolean shortcut to 0
|
||||||
if(!interpreter)
|
interpreter.SetVar("false", 0) // boolean shortcut to 0
|
||||||
return
|
interpreter.SetVar("TRUE", 1) // boolean shortcut to 1
|
||||||
|
interpreter.SetVar("true", 1) // boolean shortcut to 1
|
||||||
interpreter.container = src
|
|
||||||
|
interpreter.SetVar("NORTH", NORTH) // NORTH (1)
|
||||||
interpreter.SetVar("PI", 3.141592653) // value of pi
|
interpreter.SetVar("SOUTH", SOUTH) // SOUTH (2)
|
||||||
interpreter.SetVar("E", 2.718281828) // value of e
|
interpreter.SetVar("EAST", EAST) // EAST (4)
|
||||||
interpreter.SetVar("SQURT2", 1.414213562) // value of the square root of 2
|
interpreter.SetVar("WEST", WEST) // WEST (8)
|
||||||
interpreter.SetVar("FALSE", 0) // boolean shortcut to 0
|
|
||||||
interpreter.SetVar("false", 0) // boolean shortcut to 0
|
// Channel macros
|
||||||
interpreter.SetVar("TRUE", 1) // boolean shortcut to 1
|
interpreter.SetVar("$common", 1459)
|
||||||
interpreter.SetVar("true", 1) // boolean shortcut to 1
|
interpreter.SetVar("$science", 1351)
|
||||||
|
interpreter.SetVar("$command", 1353)
|
||||||
interpreter.SetVar("NORTH", NORTH) // NORTH (1)
|
interpreter.SetVar("$medical", 1355)
|
||||||
interpreter.SetVar("SOUTH", SOUTH) // SOUTH (2)
|
interpreter.SetVar("$engineering", 1357)
|
||||||
interpreter.SetVar("EAST", EAST) // EAST (4)
|
interpreter.SetVar("$security", 1359)
|
||||||
interpreter.SetVar("WEST", WEST) // WEST (8)
|
interpreter.SetVar("$supply", 1347)
|
||||||
|
|
||||||
// Channel macros
|
// Signal data
|
||||||
interpreter.SetVar("$common", 1459)
|
|
||||||
interpreter.SetVar("$science", 1351)
|
interpreter.SetVar("$content", signal.data["message"])
|
||||||
interpreter.SetVar("$command", 1353)
|
interpreter.SetVar("$freq", signal.frequency)
|
||||||
interpreter.SetVar("$medical", 1355)
|
interpreter.SetVar("$source", signal.data["name"])
|
||||||
interpreter.SetVar("$engineering", 1357)
|
interpreter.SetVar("$job", signal.data["job"])
|
||||||
interpreter.SetVar("$security", 1359)
|
interpreter.SetVar("$sign", signal)
|
||||||
interpreter.SetVar("$supply", 1347)
|
interpreter.SetVar("$pass", !(signal.data["reject"])) // if the signal isn't rejected, pass = 1; if the signal IS rejected, pass = 0
|
||||||
|
|
||||||
// Signal data
|
// Set up the script procs
|
||||||
|
|
||||||
interpreter.SetVar("$content", signal.data["message"])
|
/*
|
||||||
interpreter.SetVar("$freq", signal.frequency)
|
-> Send another signal to a server
|
||||||
interpreter.SetVar("$source", signal.data["name"])
|
@format: broadcast(content, frequency, source, job)
|
||||||
interpreter.SetVar("$job", signal.data["job"])
|
|
||||||
interpreter.SetVar("$sign", signal)
|
@param content: Message to broadcast
|
||||||
interpreter.SetVar("$pass", !(signal.data["reject"])) // if the signal isn't rejected, pass = 1; if the signal IS rejected, pass = 0
|
@param frequency: Frequency to broadcast to
|
||||||
|
@param source: The name of the source you wish to imitate. Must be stored in stored_names list.
|
||||||
// Set up the script procs
|
@param job: The name of the job.
|
||||||
|
*/
|
||||||
/*
|
interpreter.SetProc("broadcast", "tcombroadcast", signal, list("message", "freq", "source", "job"))
|
||||||
-> Send another signal to a server
|
|
||||||
@format: broadcast(content, frequency, source, job)
|
/*
|
||||||
|
-> Send a code signal.
|
||||||
@param content: Message to broadcast
|
@format: signal(frequency, code)
|
||||||
@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 frequency: Frequency to send the signal to
|
||||||
@param job: The name of the job.
|
@param code: Encryption code to send the signal with
|
||||||
*/
|
*/
|
||||||
interpreter.SetProc("broadcast", "tcombroadcast", signal, list("message", "freq", "source", "job"))
|
interpreter.SetProc("signal", "signaler", signal, list("freq", "code"))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
-> Send a code signal.
|
-> Store a value permanently to the server machine (not the actual game hosting machine, the ingame machine)
|
||||||
@format: signal(frequency, code)
|
@format: mem(address, value)
|
||||||
|
|
||||||
@param frequency: Frequency to send the signal to
|
@param address: The memory address (string index) to store a value to
|
||||||
@param code: Encryption code to send the signal with
|
@param value: The value to store to the memory address
|
||||||
*/
|
*/
|
||||||
interpreter.SetProc("signal", "signaler", signal, list("freq", "code"))
|
interpreter.SetProc("mem", "mem", signal, list("address", "value"))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
-> Store a value permanently to the server machine (not the actual game hosting machine, the ingame machine)
|
-> Delay code for a given amount of deciseconds
|
||||||
@format: mem(address, value)
|
@format: sleep(time)
|
||||||
|
|
||||||
@param address: The memory address (string index) to store a value to
|
@param time: time to sleep in deciseconds (1/10th second)
|
||||||
@param value: The value to store to the memory address
|
*/
|
||||||
*/
|
interpreter.SetProc("sleep", /proc/delay)
|
||||||
interpreter.SetProc("mem", "mem", signal, list("address", "value"))
|
|
||||||
|
/*
|
||||||
/*
|
-> Replaces a string with another string
|
||||||
-> Delay code for a given amount of deciseconds
|
@format: replace(string, substring, replacestring)
|
||||||
@format: sleep(time)
|
|
||||||
|
@param string: the string to search for substrings (best used with $content$ constant)
|
||||||
@param time: time to sleep in deciseconds (1/10th second)
|
@param substring: the substring to search for
|
||||||
*/
|
@param replacestring: the string to replace the substring with
|
||||||
interpreter.SetProc("sleep", /proc/delay)
|
|
||||||
|
*/
|
||||||
/*
|
interpreter.SetProc("replace", /proc/replacetext)
|
||||||
-> Replaces a string with another string
|
|
||||||
@format: replace(string, substring, replacestring)
|
/*
|
||||||
|
-> Locates an element/substring inside of a list or string
|
||||||
@param string: the string to search for substrings (best used with $content$ constant)
|
@format: find(haystack, needle, start = 1, end = 0)
|
||||||
@param substring: the substring to search for
|
|
||||||
@param replacestring: the string to replace the substring with
|
@param haystack: the container to search
|
||||||
|
@param needle: the element to search for
|
||||||
*/
|
@param start: the position to start in
|
||||||
interpreter.SetProc("replace", /proc/string_replacetext)
|
@param end: the position to end in
|
||||||
|
|
||||||
/*
|
*/
|
||||||
-> Locates an element/substring inside of a list or string
|
interpreter.SetProc("find", /proc/smartfind)
|
||||||
@format: find(haystack, needle, start = 1, end = 0)
|
|
||||||
|
/*
|
||||||
@param haystack: the container to search
|
-> Finds the length of a string or list
|
||||||
@param needle: the element to search for
|
@format: length(container)
|
||||||
@param start: the position to start in
|
|
||||||
@param end: the position to end in
|
@param container: the list or container to measure
|
||||||
|
|
||||||
*/
|
*/
|
||||||
interpreter.SetProc("find", /proc/smartfind)
|
interpreter.SetProc("length", /proc/smartlength)
|
||||||
|
|
||||||
/*
|
/* -- Clone functions, carried from default BYOND procs --- */
|
||||||
-> Finds the length of a string or list
|
|
||||||
@format: length(container)
|
// vector namespace
|
||||||
|
interpreter.SetProc("vector", /proc/n_list)
|
||||||
@param container: the list or container to measure
|
interpreter.SetProc("at", /proc/n_listpos)
|
||||||
|
interpreter.SetProc("copy", /proc/n_listcopy)
|
||||||
*/
|
interpreter.SetProc("push_back", /proc/n_listadd)
|
||||||
interpreter.SetProc("length", /proc/smartlength)
|
interpreter.SetProc("remove", /proc/n_listremove)
|
||||||
|
interpreter.SetProc("cut", /proc/n_listcut)
|
||||||
/* -- Clone functions, carried from default BYOND procs --- */
|
interpreter.SetProc("swap", /proc/n_listswap)
|
||||||
|
interpreter.SetProc("insert", /proc/n_listinsert)
|
||||||
// vector namespace
|
|
||||||
interpreter.SetProc("vector", /proc/n_list)
|
interpreter.SetProc("pick", /proc/n_pick)
|
||||||
interpreter.SetProc("at", /proc/n_listpos)
|
interpreter.SetProc("prob", /proc/prob_chance)
|
||||||
interpreter.SetProc("copy", /proc/n_listcopy)
|
interpreter.SetProc("substr", /proc/docopytext)
|
||||||
interpreter.SetProc("push_back", /proc/n_listadd)
|
|
||||||
interpreter.SetProc("remove", /proc/n_listremove)
|
interpreter.SetProc("shuffle", /proc/shuffle)
|
||||||
interpreter.SetProc("cut", /proc/n_listcut)
|
interpreter.SetProc("uniquevector", /proc/uniquelist)
|
||||||
interpreter.SetProc("swap", /proc/n_listswap)
|
|
||||||
interpreter.SetProc("insert", /proc/n_listinsert)
|
interpreter.SetProc("text2vector", /proc/text2list)
|
||||||
|
interpreter.SetProc("text2vectorEx",/proc/text2listEx)
|
||||||
interpreter.SetProc("pick", /proc/n_pick)
|
interpreter.SetProc("vector2text", /proc/vg_list2text)
|
||||||
interpreter.SetProc("prob", /proc/prob_chance)
|
|
||||||
interpreter.SetProc("substr", /proc/docopytext)
|
// Donkie~
|
||||||
|
// Strings
|
||||||
// Donkie~
|
interpreter.SetProc("lower", /proc/n_lower)
|
||||||
// Strings
|
interpreter.SetProc("upper", /proc/n_upper)
|
||||||
interpreter.SetProc("lower", /proc/n_lower)
|
interpreter.SetProc("explode", /proc/string_explode)
|
||||||
interpreter.SetProc("upper", /proc/n_upper)
|
interpreter.SetProc("repeat", /proc/n_repeat)
|
||||||
interpreter.SetProc("explode", /proc/string_explode)
|
interpreter.SetProc("reverse", /proc/reverse_text)
|
||||||
interpreter.SetProc("repeat", /proc/n_repeat)
|
interpreter.SetProc("tonum", /proc/n_str2num)
|
||||||
interpreter.SetProc("reverse", /proc/n_reverse)
|
interpreter.SetProc("capitalize", /proc/capitalize)
|
||||||
interpreter.SetProc("tonum", /proc/n_str2num)
|
interpreter.SetProc("replacetextEx",/proc/replacetextEx)
|
||||||
|
|
||||||
// Numbers
|
// Numbers
|
||||||
interpreter.SetProc("tostring", /proc/n_num2str)
|
interpreter.SetProc("tostring", /proc/n_num2str)
|
||||||
interpreter.SetProc("sqrt", /proc/n_sqrt)
|
interpreter.SetProc("sqrt", /proc/n_sqrt)
|
||||||
interpreter.SetProc("abs", /proc/n_abs)
|
interpreter.SetProc("abs", /proc/n_abs)
|
||||||
interpreter.SetProc("floor", /proc/n_floor)
|
interpreter.SetProc("floor", /proc/Floor)
|
||||||
interpreter.SetProc("ceil", /proc/n_ceil)
|
interpreter.SetProc("ceil", /proc/Ceiling)
|
||||||
interpreter.SetProc("round", /proc/n_round)
|
interpreter.SetProc("round", /proc/n_round)
|
||||||
interpreter.SetProc("clamp", /proc/n_clamp)
|
interpreter.SetProc("clamp", /proc/n_clamp)
|
||||||
interpreter.SetProc("inrange", /proc/n_inrange)
|
interpreter.SetProc("inrange", /proc/IsInRange)
|
||||||
interpreter.SetProc("rand", /proc/rand_chance)
|
interpreter.SetProc("rand", /proc/rand_chance)
|
||||||
// End of Donkie~
|
interpreter.SetProc("arctan", /proc/Atan2)
|
||||||
|
interpreter.SetProc("lcm", /proc/Lcm)
|
||||||
// Time
|
interpreter.SetProc("gcd", /proc/Gcd)
|
||||||
interpreter.SetProc("time", /proc/time)
|
interpreter.SetProc("mean", /proc/Mean)
|
||||||
interpreter.SetProc("timestamp", /proc/timestamp)
|
interpreter.SetProc("root", /proc/Root)
|
||||||
|
interpreter.SetProc("sin", /proc/n_sin)
|
||||||
// Run the compiled code
|
interpreter.SetProc("cos", /proc/n_cos)
|
||||||
interpreter.Run()
|
interpreter.SetProc("arcsin", /proc/n_asin)
|
||||||
|
interpreter.SetProc("arccos", /proc/n_acos)
|
||||||
// Backwards-apply variables onto signal data
|
interpreter.SetProc("tan", /proc/Tan)
|
||||||
/* sanitize EVERYTHING. fucking players can't be trusted with SHIT */
|
interpreter.SetProc("csc", /proc/Csc)
|
||||||
|
interpreter.SetProc("cot", /proc/Cot)
|
||||||
signal.data["message"] = interpreter.GetCleanVar("$content", signal.data["message"])
|
interpreter.SetProc("sec", /proc/Sec)
|
||||||
signal.frequency = interpreter.GetCleanVar("$freq", signal.frequency)
|
interpreter.SetProc("todegrees", /proc/ToDegrees)
|
||||||
|
interpreter.SetProc("toradians", /proc/ToRadians)
|
||||||
var/setname = interpreter.GetCleanVar("$source", signal.data["name"])
|
interpreter.SetProc("lerp", /proc/Lerp)
|
||||||
|
interpreter.SetProc("max", /proc/n_max)
|
||||||
if(signal.data["name"] != setname)
|
interpreter.SetProc("min", /proc/n_min)
|
||||||
signal.data["realname"] = setname
|
|
||||||
signal.data["name"] = setname
|
// End of Donkie~
|
||||||
signal.data["job"] = interpreter.GetCleanVar("$job", signal.data["job"])
|
|
||||||
signal.data["reject"] = !(interpreter.GetCleanVar("$pass")) // set reject to the opposite of $pass
|
// Time
|
||||||
|
interpreter.SetProc("time", /proc/time)
|
||||||
// If the message is invalid, just don't broadcast it!
|
interpreter.SetProc("timestamp", /proc/timestamp)
|
||||||
if(signal.data["message"] == "" || !signal.data["message"])
|
|
||||||
signal.data["reject"] = 1
|
// Run the compiled code
|
||||||
|
interpreter.Run()
|
||||||
/* -- Actual language proc code -- */
|
|
||||||
|
// Backwards-apply variables onto signal data
|
||||||
var/const/SIGNAL_COOLDOWN = 20 // 2 seconds
|
/* sanitize EVERYTHING. fucking players can't be trusted with SHIT */
|
||||||
|
|
||||||
datum/signal
|
signal.data["message"] = interpreter.GetCleanVar("$content", signal.data["message"])
|
||||||
|
signal.frequency = interpreter.GetCleanVar("$freq", signal.frequency)
|
||||||
proc/mem(var/address, var/value)
|
|
||||||
|
var/setname = interpreter.GetCleanVar("$source", signal.data["name"])
|
||||||
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\proc/mem() called tick#: [world.time]")
|
|
||||||
|
if(signal.data["name"] != setname)
|
||||||
if(istext(address))
|
signal.data["realname"] = setname
|
||||||
var/obj/machinery/telecomms/server/S = data["server"]
|
signal.data["name"] = setname
|
||||||
|
signal.data["job"] = interpreter.GetCleanVar("$job", signal.data["job"])
|
||||||
if(!value && value != 0)
|
signal.data["reject"] = !(interpreter.GetCleanVar("$pass")) // set reject to the opposite of $pass
|
||||||
return S.memory[address]
|
|
||||||
|
// If the message is invalid, just don't broadcast it!
|
||||||
else
|
if(signal.data["message"] == "" || !signal.data["message"])
|
||||||
S.memory[address] = value
|
signal.data["reject"] = 1
|
||||||
|
|
||||||
|
/* -- Actual language proc code -- */
|
||||||
proc/signaler(var/freq = 1459, var/code = 30)
|
|
||||||
|
/var/const/SIGNAL_COOLDOWN = 20 // 2 seconds
|
||||||
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\proc/signaler() called tick#: [world.time]")
|
|
||||||
|
/datum/signal/proc/mem(var/address, var/value)
|
||||||
if(isnum(freq) && isnum(code))
|
if(istext(address))
|
||||||
|
var/obj/machinery/telecomms/server/S = data["server"]
|
||||||
var/obj/machinery/telecomms/server/S = data["server"]
|
|
||||||
|
if(!value && value != 0)
|
||||||
if(S.last_signal + SIGNAL_COOLDOWN > world.timeofday && S.last_signal < MIDNIGHT_ROLLOVER)
|
return S.memory[address]
|
||||||
return
|
|
||||||
S.last_signal = world.timeofday
|
else
|
||||||
|
S.memory[address] = value
|
||||||
var/datum/radio_frequency/connection = radio_controller.return_frequency(freq)
|
|
||||||
|
/datum/signal/proc/signaler(var/freq = 1459, var/code = 30)
|
||||||
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
|
if(isnum(freq) && isnum(code))
|
||||||
freq *= 10 // shift the decimal one place
|
|
||||||
|
var/obj/machinery/telecomms/server/S = data["server"]
|
||||||
freq = sanitize_frequency(freq)
|
|
||||||
|
if(S.last_signal + SIGNAL_COOLDOWN > world.timeofday && S.last_signal < MIDNIGHT_ROLLOVER)
|
||||||
code = round(code)
|
return
|
||||||
code = Clamp(code, 0, 100)
|
S.last_signal = world.timeofday
|
||||||
|
|
||||||
var/datum/signal/signal = getFromPool(/datum/signal)
|
var/datum/radio_frequency/connection = radio_controller.return_frequency(freq)
|
||||||
signal.source = S
|
|
||||||
signal.encryption = code
|
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
|
||||||
signal.data["message"] = "ACTIVATE"
|
freq *= 10 // shift the decimal one place
|
||||||
|
|
||||||
connection.post_signal(S, signal)
|
freq = sanitize_frequency(freq)
|
||||||
|
|
||||||
var/time = time2text(world.realtime,"hh:mm:ss")
|
code = round(code)
|
||||||
lastsignalers.Add("[time] <B>:</B> [S.id] sent a signal command, which was triggered by NTSL.<B>:</B> [format_frequency(freq)]/[code]")
|
code = Clamp(code, 0, 100)
|
||||||
|
|
||||||
|
var/datum/signal/signal = getFromPool(/datum/signal)
|
||||||
proc/tcombroadcast(var/message, var/freq, var/source, var/job)
|
signal.source = S
|
||||||
|
signal.encryption = code
|
||||||
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\proc/tcombroadcast() called tick#: [world.time]")
|
signal.data["message"] = "ACTIVATE"
|
||||||
|
|
||||||
var/datum/signal/newsign = getFromPool(/datum/signal)
|
connection.post_signal(S, signal)
|
||||||
var/obj/machinery/telecomms/server/S = data["server"]
|
|
||||||
var/obj/item/device/radio/hradio = S.server_radio
|
var/time = time2text(world.realtime,"hh:mm:ss")
|
||||||
|
lastsignalers.Add("[time] <B>:</B> [S.id] sent a signal command, which was triggered by NTSL.<B>:</B> [format_frequency(freq)]/[code]")
|
||||||
if(!hradio)
|
|
||||||
error("[src] has no radio.")
|
|
||||||
return
|
/datum/signal/proc/tcombroadcast(var/message, var/freq, var/source, var/job)
|
||||||
|
var/datum/signal/newsign = getFromPool(/datum/signal)
|
||||||
if((!message || message == "") && message != 0)
|
var/obj/machinery/telecomms/server/S = data["server"]
|
||||||
message = "*beep*"
|
var/obj/item/device/radio/hradio = S.server_radio
|
||||||
if(!source)
|
|
||||||
source = "[html_encode(uppertext(S.id))]"
|
if(!hradio)
|
||||||
hradio = new // sets the hradio as a radio intercom
|
error("[src] has no radio.")
|
||||||
if(!freq || (!isnum(freq) && text2num(freq) == null))
|
return
|
||||||
freq = 1459
|
|
||||||
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
|
if((!message || message == "") && message != 0)
|
||||||
freq *= 10 // shift the decimal one place
|
message = "*beep*"
|
||||||
|
if(!source)
|
||||||
if(!job)
|
source = "[html_encode(uppertext(S.id))]"
|
||||||
job = "Unknown"
|
hradio = new // sets the hradio as a radio intercom
|
||||||
|
if(!freq || (!isnum(freq) && text2num(freq) == null))
|
||||||
//SAY REWRITE RELATED CODE.
|
freq = 1459
|
||||||
//This code is a little hacky, but it *should* work. Even though it'll result in a virtual speaker referencing another virtual speaker. vOv
|
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
|
||||||
var/atom/movable/virtualspeaker/virt = getFromPool(/atom/movable/virtualspeaker, null)
|
freq *= 10 // shift the decimal one place
|
||||||
virt.name = source
|
|
||||||
virt.job = job
|
if(!job)
|
||||||
virt.faketrack = 1
|
job = "Unknown"
|
||||||
//END SAY REWRITE RELATED CODE.
|
|
||||||
|
//SAY REWRITE RELATED CODE.
|
||||||
|
//This code is a little hacky, but it *should* work. Even though it'll result in a virtual speaker referencing another virtual speaker. vOv
|
||||||
newsign.data["mob"] = virt
|
var/atom/movable/virtualspeaker/virt = getFromPool(/atom/movable/virtualspeaker, null)
|
||||||
newsign.data["mobtype"] = /mob/living/carbon/human
|
virt.name = source
|
||||||
newsign.data["name"] = source
|
virt.job = job
|
||||||
newsign.data["realname"] = newsign.data["name"]
|
virt.faketrack = 1
|
||||||
newsign.data["job"] = "[job]"
|
//END SAY REWRITE RELATED CODE.
|
||||||
newsign.data["compression"] = 0
|
|
||||||
newsign.data["message"] = message
|
|
||||||
newsign.data["type"] = 2 // artificial broadcast
|
newsign.data["mob"] = virt
|
||||||
if(!isnum(freq))
|
newsign.data["mobtype"] = /mob/living/carbon/human
|
||||||
freq = text2num(freq)
|
newsign.data["name"] = source
|
||||||
newsign.frequency = freq
|
newsign.data["realname"] = newsign.data["name"]
|
||||||
|
newsign.data["job"] = "[job]"
|
||||||
var/datum/radio_frequency/connection = radio_controller.return_frequency(freq)
|
newsign.data["compression"] = 0
|
||||||
newsign.data["connection"] = connection
|
newsign.data["message"] = message
|
||||||
|
newsign.data["type"] = 2 // artificial broadcast
|
||||||
|
if(!isnum(freq))
|
||||||
newsign.data["radio"] = hradio
|
freq = text2num(freq)
|
||||||
newsign.data["vmessage"] = message
|
newsign.frequency = freq
|
||||||
newsign.data["vname"] = source
|
|
||||||
newsign.data["vmask"] = 0
|
var/datum/radio_frequency/connection = radio_controller.return_frequency(freq)
|
||||||
newsign.data["level"] = data["level"]
|
newsign.data["connection"] = connection
|
||||||
|
|
||||||
newsign.sanitize_data()
|
|
||||||
|
newsign.data["radio"] = hradio
|
||||||
var/pass = S.relay_information(newsign, "/obj/machinery/telecomms/hub")
|
newsign.data["vmessage"] = message
|
||||||
if(!pass)
|
newsign.data["vname"] = source
|
||||||
S.relay_information(newsign, "/obj/machinery/telecomms/broadcaster") // send this simple message to broadcasters
|
newsign.data["vmask"] = 0
|
||||||
|
newsign.data["level"] = data["level"]
|
||||||
spawn(50)
|
|
||||||
returnToPool(virt)
|
newsign.sanitize_data()
|
||||||
|
|
||||||
|
var/pass = S.relay_information(newsign, "/obj/machinery/telecomms/hub")
|
||||||
|
if(!pass)
|
||||||
|
S.relay_information(newsign, "/obj/machinery/telecomms/broadcaster") // send this simple message to broadcasters
|
||||||
|
|
||||||
|
spawn(50)
|
||||||
|
returnToPool(virt)
|
||||||
|
|||||||
@@ -158,46 +158,11 @@
|
|||||||
|
|
||||||
/proc/time()
|
/proc/time()
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/time() called tick#: [world.time]")
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/time() called tick#: [world.time]")
|
||||||
return world.timeofday
|
return world.time + (12 HOURS)
|
||||||
|
|
||||||
/proc/timestamp(var/format = "hh:mm:ss") // Get the game time in text
|
/proc/timestamp(var/format = "hh:mm:ss") // Get the game time in text
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/timestamp() called tick#: [world.time]")
|
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/timestamp() called tick#: [world.time]")
|
||||||
return time2text(world.time + 432000, format)
|
return time2text(world.time + (10 HOURS), format) // Yes, 10, not 12 hours, for some reason time2text() is being moronic (T-thanks BYOND), and it's adding 2 hours to this, I don't even know either.
|
||||||
|
|
||||||
/*
|
|
||||||
//Makes a list where all indicies in a string is a seperate index in the list
|
|
||||||
// JUST A HELPER DON'T ADD TO NTSCRIPT
|
|
||||||
proc/string_tolist(var/string)
|
|
||||||
//writepanic("[__FILE__].[__LINE__] \\/proc/string_tolist() called tick#: [world.time]")
|
|
||||||
var/list/L = new/list()
|
|
||||||
|
|
||||||
var/i
|
|
||||||
for(i=1, i<=length(string), i++)
|
|
||||||
L.Add(copytext(string, i, i))
|
|
||||||
|
|
||||||
return L
|
|
||||||
|
|
||||||
proc/string_explode(var/string, var/separator)
|
|
||||||
//writepanic("[__FILE__].[__LINE__] \\/proc/string_explode() called tick#: [world.time]")
|
|
||||||
if(istext(string))
|
|
||||||
if(istext(separator) && separator == "")
|
|
||||||
return string_tolist(string)
|
|
||||||
var/i
|
|
||||||
var/lasti = 1
|
|
||||||
var/list/L = new/list()
|
|
||||||
|
|
||||||
for(i=1, i<=length(string)+1, i++)
|
|
||||||
if(copytext(string, i, i+1) == separator) // We found a separator
|
|
||||||
L.Add(copytext(string, lasti, i))
|
|
||||||
lasti = i+1
|
|
||||||
|
|
||||||
L.Add(copytext(string, lasti, length(string)+1)) // Adds the last segment
|
|
||||||
|
|
||||||
return L
|
|
||||||
|
|
||||||
Just found out there was already a string explode function, did some benchmarking, and that function were a bit faster, sticking to that.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
proc/string_explode(var/string, var/separator = "")
|
proc/string_explode(var/string, var/separator = "")
|
||||||
//writepanic("[__FILE__].[__LINE__] \\/proc/string_explode() called tick#: [world.time]")
|
//writepanic("[__FILE__].[__LINE__] \\/proc/string_explode() called tick#: [world.time]")
|
||||||
@@ -218,24 +183,17 @@ proc/n_repeat(var/string, var/amount)
|
|||||||
|
|
||||||
return newstring
|
return newstring
|
||||||
|
|
||||||
proc/n_reverse(var/string)
|
|
||||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_reverse() called tick#: [world.time]")
|
|
||||||
if(istext(string))
|
|
||||||
var/newstring = ""
|
|
||||||
var/i
|
|
||||||
for(i=length(string), i>0, i--)
|
|
||||||
if(i>=1000)
|
|
||||||
break
|
|
||||||
newstring = newstring + copytext(string, i, i+1)
|
|
||||||
|
|
||||||
return newstring
|
|
||||||
|
|
||||||
// I don't know if it's neccesary to make my own proc, but I think I have to to be able to check for istext.
|
// 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)
|
proc/n_str2num(var/string)
|
||||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_str2num() called tick#: [world.time]")
|
//writepanic("[__FILE__].[__LINE__] \\/proc/n_str2num() called tick#: [world.time]")
|
||||||
if(istext(string))
|
if(istext(string))
|
||||||
return text2num(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
|
// Number shit
|
||||||
proc/n_num2str(var/num)
|
proc/n_num2str(var/num)
|
||||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_num2str() called tick#: [world.time]")
|
//writepanic("[__FILE__].[__LINE__] \\/proc/n_num2str() called tick#: [world.time]")
|
||||||
@@ -274,89 +232,23 @@ proc/n_round(var/num)
|
|||||||
return round(num)
|
return round(num)
|
||||||
return n_ceil(num)
|
return n_ceil(num)
|
||||||
|
|
||||||
// Clamps N between min and max
|
|
||||||
proc/n_clamp(var/num, var/min=-1, var/max=1)
|
|
||||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_clamp() called tick#: [world.time]")
|
|
||||||
if(isnum(num)&&isnum(min)&&isnum(max))
|
|
||||||
if(num<=min)
|
|
||||||
return min
|
|
||||||
if(num>=max)
|
|
||||||
return max
|
|
||||||
return num
|
|
||||||
|
|
||||||
// Returns 1 if N is inbetween Min and Max
|
|
||||||
proc/n_inrange(var/num, var/min=-1, var/max=1)
|
|
||||||
//writepanic("[__FILE__].[__LINE__] \\/proc/n_inrange() called tick#: [world.time]")
|
|
||||||
if(isnum(num)&&isnum(min)&&isnum(max))
|
|
||||||
return ((min <= num) && (num <= max))
|
|
||||||
// END OF BY DONKIE :(
|
// END OF BY DONKIE :(
|
||||||
|
|
||||||
// Non-recursive
|
/proc/n_sin(var/const/x)
|
||||||
// Imported from Mono string.ReplaceUnchecked
|
return sin(x)
|
||||||
/*
|
|
||||||
/proc/string_replacetext(var/haystack,var/a,var/b)
|
|
||||||
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\/proc/string_replacetext() called tick#: [world.time]")
|
|
||||||
if(istext(haystack)&&istext(a)&&istext(b))
|
|
||||||
var/i = 1
|
|
||||||
var/lenh=length(haystack)
|
|
||||||
var/lena=length(a)
|
|
||||||
//var/lenb=length(b)
|
|
||||||
var/count = 0
|
|
||||||
var/list/dat = list()
|
|
||||||
while (i < lenh)
|
|
||||||
var/found = findtext(haystack, a, i, 0)
|
|
||||||
//diary << "findtext([haystack], [a], [i], 0)=[found]"
|
|
||||||
if (found == 0) // Not found
|
|
||||||
break
|
|
||||||
else
|
|
||||||
if (count < SCRIPT_MAX_REPLACEMENTS_ALLOWED)
|
|
||||||
dat+=found
|
|
||||||
count+=1
|
|
||||||
else
|
|
||||||
//diary << "Script found [a] [count] times, aborted"
|
|
||||||
break
|
|
||||||
//diary << "Found [a] at [found]! Moving up..."
|
|
||||||
i = found + lena
|
|
||||||
if (count == 0)
|
|
||||||
return haystack
|
|
||||||
//var/nlen = lenh + ((lenb - lena) * count)
|
|
||||||
var/buf = copytext(haystack,1,dat[1]) // Prefill
|
|
||||||
var/lastReadPos = 0
|
|
||||||
for (i = 1, i <= count, i++)
|
|
||||||
var/precopy = dat[i] - lastReadPos-1
|
|
||||||
//internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
|
|
||||||
//fixed (char* dest = target, src = source)
|
|
||||||
//CharCopy (dest + targetIndex, src + sourceIndex, count);
|
|
||||||
//CharCopy (dest + curPos, source + lastReadPos, precopy);
|
|
||||||
buf+=copytext(haystack,lastReadPos,precopy)
|
|
||||||
diary << "buf+=copytext([haystack],[lastReadPos],[precopy])"
|
|
||||||
diary<<"[buf]"
|
|
||||||
lastReadPos = dat[i] + lena
|
|
||||||
//CharCopy (dest + curPos, replace, newValue.length);
|
|
||||||
buf+=b
|
|
||||||
diary<<"[buf]"
|
|
||||||
buf+=copytext(haystack,lastReadPos, 0)
|
|
||||||
return buf
|
|
||||||
*/
|
|
||||||
|
|
||||||
/proc/string_replacetext(text, find, replacement)
|
/proc/n_cos(var/const/x)
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/string_replacetext() called tick#: [world.time]")
|
return cos(x)
|
||||||
if(istext(text) && istext(find) && istext(replacement))
|
|
||||||
var/find_len = length(find)
|
|
||||||
if(find_len < 1) return text
|
|
||||||
. = ""
|
|
||||||
var/last_found = 1
|
|
||||||
var/count = 0
|
|
||||||
while(1)
|
|
||||||
count += 1
|
|
||||||
if(count > SCRIPT_MAX_REPLACEMENTS_ALLOWED)
|
|
||||||
break
|
|
||||||
var/found = findtext(text, find, last_found, 0)
|
|
||||||
. += copytext(text, last_found, found)
|
|
||||||
if(found)
|
|
||||||
. += replacement
|
|
||||||
last_found = found + find_len
|
|
||||||
continue
|
|
||||||
return
|
|
||||||
|
|
||||||
#undef SCRIPT_MAX_REPLACEMENTS_ALLOWED
|
/proc/n_asin(var/const/x)
|
||||||
|
return arcsin(x)
|
||||||
|
|
||||||
|
/proc/n_acos(var/const/x)
|
||||||
|
return arccos(x)
|
||||||
|
|
||||||
|
|
||||||
|
/proc/n_max(...)
|
||||||
|
return max(arglist(args))
|
||||||
|
|
||||||
|
/proc/n_min(...)
|
||||||
|
return min(arglist(args))
|
||||||
@@ -1,170 +1,203 @@
|
|||||||
/proc/isobject(x)
|
/proc/isobject(x)
|
||||||
//writepanic("[__FILE__].[__LINE__] (no type)([usr ? usr.ckey : ""]) \\/proc/isobject() called tick#: [world.time]")
|
return (istype(x, /datum) || istype(x, /list) || istype(x, /savefile) || istype(x, /client) || (x == world))
|
||||||
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))
|
||||||
|
|
||||||
/n_Interpreter
|
|
||||||
proc
|
|
||||||
Eval(node/expression/exp)
|
|
||||||
if(istype(exp, /node/expression/FunctionCall))
|
|
||||||
return RunFunction(exp)
|
|
||||||
else if(istype(exp, /node/expression/operator))
|
|
||||||
return EvalOperator(exp)
|
|
||||||
else if(istype(exp, /node/expression/value/literal))
|
|
||||||
var/node/expression/value/literal/lit=exp
|
|
||||||
return lit.value
|
|
||||||
else if(istype(exp, /node/expression/value/reference))
|
|
||||||
var/node/expression/value/reference/ref=exp
|
|
||||||
return ref.value
|
|
||||||
else if(istype(exp, /node/expression/value/variable))
|
|
||||||
var/node/expression/value/variable/v=exp
|
|
||||||
if(!v.object)
|
|
||||||
return Eval(GetVariable(v.id.id_name))
|
|
||||||
else
|
else
|
||||||
var/datum/D
|
RaiseError(new/datum/runtimeError/UnknownInstruction())
|
||||||
if(istype(v.object, /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/runtimeError/UndefinedVariable("[v.object.ToString()].[v.id.id_name]"))
|
|
||||||
return null
|
|
||||||
return Eval(D.vars[v.id.id_name])
|
|
||||||
else if(istype(exp, /node/expression))
|
|
||||||
RaiseError(new/runtimeError/UnknownInstruction())
|
|
||||||
else
|
|
||||||
return exp
|
|
||||||
|
|
||||||
EvalOperator(node/expression/operator/exp)
|
catch
|
||||||
if(istype(exp, /node/expression/operator/binary))
|
RaiseError(new/datum/runtimeError/TypeMismatch(bin.token, Eval(bin.exp), Eval(bin.exp2)))
|
||||||
var/node/expression/operator/binary/bin=exp
|
|
||||||
switch(bin.type)
|
|
||||||
if(/node/expression/operator/binary/Equal)
|
|
||||||
return Equal(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/NotEqual)
|
|
||||||
return NotEqual(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/Greater)
|
|
||||||
return Greater(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/Less)
|
|
||||||
return Less(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/GreaterOrEqual)
|
|
||||||
return GreaterOrEqual(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/LessOrEqual)
|
|
||||||
return LessOrEqual(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/LogicalAnd)
|
|
||||||
return LogicalAnd(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/LogicalOr)
|
|
||||||
return LogicalOr(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/LogicalXor)
|
|
||||||
return LogicalXor(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/BitwiseAnd)
|
|
||||||
return BitwiseAnd(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/BitwiseOr)
|
|
||||||
return BitwiseOr(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/BitwiseXor)
|
|
||||||
return BitwiseXor(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/Add)
|
|
||||||
return Add(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/Subtract)
|
|
||||||
return Subtract(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/Multiply)
|
|
||||||
return Multiply(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/Divide)
|
|
||||||
return Divide(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/Power)
|
|
||||||
return Power(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
if(/node/expression/operator/binary/Modulo)
|
|
||||||
return Modulo(Eval(bin.exp), Eval(bin.exp2))
|
|
||||||
else
|
|
||||||
RaiseError(new/runtimeError/UnknownInstruction())
|
|
||||||
else
|
|
||||||
switch(exp.type)
|
|
||||||
if(/node/expression/operator/unary/Minus)
|
|
||||||
return Minus(Eval(exp.exp))
|
|
||||||
if(/node/expression/operator/unary/LogicalNot)
|
|
||||||
return LogicalNot(Eval(exp.exp))
|
|
||||||
if(/node/expression/operator/unary/BitwiseNot)
|
|
||||||
return BitwiseNot(Eval(exp.exp))
|
|
||||||
if(/node/expression/operator/unary/group)
|
|
||||||
return Eval(exp.exp)
|
|
||||||
else
|
|
||||||
RaiseError(new/runtimeError/UnknownInstruction())
|
|
||||||
|
|
||||||
|
else
|
||||||
|
try
|
||||||
|
switch(exp.type)
|
||||||
|
if(/datum/node/expression/operator/unary/Minus)
|
||||||
|
return Minus(Eval(exp.exp))
|
||||||
|
|
||||||
//Binary//
|
if(/datum/node/expression/operator/unary/LogicalNot)
|
||||||
//Comparison operators
|
return LogicalNot(Eval(exp.exp))
|
||||||
Equal(a, b) return a==b
|
|
||||||
NotEqual(a, b) return a!=b //LogicalNot(Equal(a, b))
|
|
||||||
Greater(a, b) return a>b
|
|
||||||
Less(a, b) return a<b
|
|
||||||
GreaterOrEqual(a, b)return a>=b
|
|
||||||
LessOrEqual(a, b) return a<=b
|
|
||||||
//Logical Operators
|
|
||||||
LogicalAnd(a, b) return a&&b
|
|
||||||
LogicalOr(a, b) return a||b
|
|
||||||
LogicalXor(a, b) return (a||b) && !(a&&b)
|
|
||||||
//Bitwise Operators
|
|
||||||
BitwiseAnd(a, b) return a&b
|
|
||||||
BitwiseOr(a, b) return a|b
|
|
||||||
BitwiseXor(a, b) return a^b
|
|
||||||
//Arithmetic Operators
|
|
||||||
Add(a, b)
|
|
||||||
if(istext(a)&&!istext(b)) b="[b]"
|
|
||||||
else if(istext(b)&&!istext(a)) a="[a]"
|
|
||||||
if(isobject(a) && !isobject(b))
|
|
||||||
RaiseError(new/runtimeError/TypeMismatch("+", a, b))
|
|
||||||
return null
|
|
||||||
else if(isobject(b) && !isobject(a))
|
|
||||||
RaiseError(new/runtimeError/TypeMismatch("+", a, b))
|
|
||||||
return null
|
|
||||||
return a+b
|
|
||||||
Subtract(a, b)
|
|
||||||
if(isobject(a) && !isobject(b))
|
|
||||||
RaiseError(new/runtimeError/TypeMismatch("-", a, b))
|
|
||||||
return null
|
|
||||||
else if(isobject(b) && !isobject(a))
|
|
||||||
RaiseError(new/runtimeError/TypeMismatch("-", a, b))
|
|
||||||
return null
|
|
||||||
return a-b
|
|
||||||
Divide(a, b)
|
|
||||||
if(isobject(a) && !isobject(b))
|
|
||||||
RaiseError(new/runtimeError/TypeMismatch("/", a, b))
|
|
||||||
return null
|
|
||||||
else if(isobject(b) && !isobject(a))
|
|
||||||
RaiseError(new/runtimeError/TypeMismatch("/", a, b))
|
|
||||||
return null
|
|
||||||
if(b==0)
|
|
||||||
RaiseError(new/runtimeError/DivisionByZero())
|
|
||||||
return null
|
|
||||||
return a/b
|
|
||||||
Multiply(a, b)
|
|
||||||
if(isobject(a) && !isobject(b))
|
|
||||||
RaiseError(new/runtimeError/TypeMismatch("*", a, b))
|
|
||||||
return null
|
|
||||||
else if(isobject(b) && !isobject(a))
|
|
||||||
RaiseError(new/runtimeError/TypeMismatch("*", a, b))
|
|
||||||
return null
|
|
||||||
return a*b
|
|
||||||
Modulo(a, b)
|
|
||||||
if(isobject(a) && !isobject(b))
|
|
||||||
RaiseError(new/runtimeError/TypeMismatch("%", a, b))
|
|
||||||
return null
|
|
||||||
else if(isobject(b) && !isobject(a))
|
|
||||||
RaiseError(new/runtimeError/TypeMismatch("%", a, b))
|
|
||||||
return null
|
|
||||||
return a%b
|
|
||||||
Power(a, b)
|
|
||||||
if(isobject(a) && !isobject(b))
|
|
||||||
RaiseError(new/runtimeError/TypeMismatch("**", a, b))
|
|
||||||
return null
|
|
||||||
else if(isobject(b) && !isobject(a))
|
|
||||||
RaiseError(new/runtimeError/TypeMismatch("**", a, b))
|
|
||||||
return null
|
|
||||||
return a**b
|
|
||||||
|
|
||||||
//Unary//
|
if(/datum/node/expression/operator/unary/BitwiseNot)
|
||||||
Minus(a) return -a
|
return BitwiseNot(Eval(exp.exp))
|
||||||
LogicalNot(a) return !a
|
|
||||||
BitwiseNot(a) return ~a
|
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
|
||||||
|
|||||||
@@ -7,9 +7,6 @@
|
|||||||
Procedures allowing for interaction with the script that is being run by the interpreter object.
|
Procedures allowing for interaction with the script that is being run by the interpreter object.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/n_Interpreter
|
|
||||||
proc
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: Load
|
Proc: Load
|
||||||
Loads a 'compiled' script into memory.
|
Loads a 'compiled' script into memory.
|
||||||
@@ -17,137 +14,144 @@
|
|||||||
Parameters:
|
Parameters:
|
||||||
program - A <GlobalBlock> object which represents the script's global scope.
|
program - A <GlobalBlock> object which represents the script's global scope.
|
||||||
*/
|
*/
|
||||||
Load(node/BlockDefinition/GlobalBlock/program)
|
/datum/n_Interpreter/proc/Load(var/datum/node/BlockDefinition/GlobalBlock/program)
|
||||||
ASSERT(program)
|
ASSERT(program)
|
||||||
src.program = program
|
src.program = program
|
||||||
CreateGlobalScope()
|
CreateGlobalScope()
|
||||||
alertadmins = 0 // reset admin alerts
|
alertadmins = 0 // reset admin alerts
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: Run
|
Proc: Run
|
||||||
Runs the script.
|
Runs the script.
|
||||||
*/
|
*/
|
||||||
Run()
|
/datum/n_Interpreter/proc/Run()
|
||||||
cur_recursion = 0 // reset recursion
|
cur_recursion = 0 // reset recursion
|
||||||
cur_statements = 0 // reset CPU tracking
|
cur_statements = 0 // reset CPU tracking
|
||||||
|
|
||||||
ASSERT(src.program)
|
ASSERT(src.program)
|
||||||
RunBlock(src.program)
|
RunBlock(src.program)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: SetVar
|
Proc: SetVar
|
||||||
Defines a global variable for the duration of the next execution of a script.
|
Defines a global variable for the duration of the next execution of a script.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
This differs from <Block.SetVar()> in that variables set using this procedure only last for the session,
|
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.
|
while those defined from the block object persist if it is ran multiple times.
|
||||||
|
|
||||||
See Also:
|
See Also:
|
||||||
- <Block.SetVar()>
|
- <Block.SetVar()>
|
||||||
*/
|
*/
|
||||||
SetVar(name, value)
|
/datum/n_Interpreter/proc/SetVar(name, value)
|
||||||
if(!istext(name))
|
if(!istext(name))
|
||||||
//CRASH("Invalid variable name")
|
//CRASH("Invalid variable name")
|
||||||
return
|
return
|
||||||
AssignVariable(name, value)
|
|
||||||
|
AssignVariable(name, value)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: SetProc
|
Proc: SetProc
|
||||||
Defines a procedure to be available to the script.
|
Defines a procedure to be available to the script.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
name - The name of the procedure as exposed to the script.
|
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.
|
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.
|
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.
|
params - Only required if object is not null, a list of the names of parameters the proc takes.
|
||||||
*/
|
*/
|
||||||
SetProc(name, path, object=null, list/params=null)
|
/datum/n_Interpreter/proc/SetProc(name, path, object = null, list/params = null)
|
||||||
if(!istext(name))
|
if(!istext(name))
|
||||||
//CRASH("Invalid function name")
|
//CRASH("Invalid function name")
|
||||||
return
|
return
|
||||||
if(!object)
|
|
||||||
globalScope.functions[name] = path
|
if(!object)
|
||||||
else
|
globalScope.functions[name] = path
|
||||||
var/node/statement/FunctionDefinition/S = new()
|
|
||||||
S.func_name = name
|
else
|
||||||
S.parameters = params
|
var/datum/node/statement/FunctionDefinition/S = new()
|
||||||
S.block = new()
|
S.func_name = name
|
||||||
S.block.SetVar("src", object)
|
S.parameters = params
|
||||||
var/node/expression/FunctionCall/C = new()
|
S.block = new()
|
||||||
C.func_name = path
|
S.block.SetVar("src", object)
|
||||||
C.object = new("src")
|
var/datum/node/expression/FunctionCall/C = new()
|
||||||
for(var/p in params)
|
C.func_name = path
|
||||||
C.parameters += new/node/expression/value/variable(p)
|
C.object = new("src")
|
||||||
var/node/statement/ReturnStatement/R=new()
|
for(var/p in params)
|
||||||
R.value=C
|
C.parameters += new/datum/node/expression/value/variable(p)
|
||||||
S.block.statements += R
|
|
||||||
globalScope.functions[name] = S
|
var/datum/node/statement/ReturnStatement/R = new()
|
||||||
|
R.value = C
|
||||||
|
S.block.statements += R
|
||||||
|
globalScope.functions[name] = S
|
||||||
/*
|
/*
|
||||||
Proc: VarExists
|
Proc: VarExists
|
||||||
Checks whether a global variable with the specified name exists.
|
Checks whether a global variable with the specified name exists.
|
||||||
*/
|
*/
|
||||||
VarExists(name)
|
/datum/n_Interpreter/proc/VarExists(name)
|
||||||
return globalScope.variables.Find(name) //convert to 1/0 first?
|
return globalScope.variables.Find(name) //convert to 1/0 first?
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: ProcExists
|
Proc: ProcExists
|
||||||
Checks whether a global function with the specified name exists.
|
Checks whether a global function with the specified name exists.
|
||||||
*/
|
*/
|
||||||
ProcExists(name)
|
/datum/n_Interpreter/proc/ProcExists(name)
|
||||||
return globalScope.functions.Find(name)
|
return globalScope.functions.Find(name)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: GetVar
|
Proc: GetVar
|
||||||
Returns the value of a global variable in the script. Remember to ensure that the variable exists before calling this procedure.
|
Returns the value of a global variable in the script. Remember to ensure that the variable exists before calling this procedure.
|
||||||
|
|
||||||
See Also:
|
See Also:
|
||||||
- <VarExists()>
|
- <VarExists()>
|
||||||
*/
|
*/
|
||||||
GetVar(name)
|
/datum/n_Interpreter/proc/GetVar(name)
|
||||||
if(!VarExists(name))
|
if(!VarExists(name))
|
||||||
//CRASH("No variable named '[name]'.")
|
//CRASH("No variable named '[name]'.")
|
||||||
return
|
return
|
||||||
var/x = globalScope.variables[name]
|
var/x = globalScope.variables[name]
|
||||||
return Eval(x)
|
return Eval(x)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: GetCleanVar
|
Proc: GetCleanVar
|
||||||
Returns the value of a global variable in the script and cleans it (sanitizes).
|
Returns the value of a global variable in the script and cleans it (sanitizes).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
GetCleanVar(name, compare)
|
/datum/n_Interpreter/proc/GetCleanVar(name, compare)
|
||||||
var/x = GetVar(name)
|
var/x = GetVar(name)
|
||||||
if(istext(x) && compare && x != compare) // Was changed
|
if(istext(x) && compare && x != compare) // Was changed
|
||||||
x = sanitize(x)
|
x = sanitize(x)
|
||||||
return x
|
return x
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: CallProc
|
Proc: CallProc
|
||||||
Calls a global function defined in the script and, amazingly enough, returns its return value. Remember to ensure that the function
|
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.
|
exists before calling this procedure.
|
||||||
|
|
||||||
See Also:
|
See Also:
|
||||||
- <ProcExists()>
|
- <ProcExists()>
|
||||||
*/
|
*/
|
||||||
CallProc(name, params[]=null)
|
/datum/n_Interpreter/proc/CallProc(name, params[]=null)
|
||||||
if(!ProcExists(name))
|
if(!ProcExists(name))
|
||||||
//CRASH("No function named '[name]'.")
|
//CRASH("No function named '[name]'.")
|
||||||
return
|
return
|
||||||
var/node/statement/FunctionDefinition/func = globalScope.functions[name]
|
|
||||||
if(istype(func))
|
var/datum/node/statement/FunctionDefinition/func = globalScope.functions[name]
|
||||||
var/node/statement/FunctionCall/stmt = new
|
|
||||||
stmt.func_name = func.func_name
|
if(istype(func))
|
||||||
stmt.parameters = params
|
var/datum/node/statement/FunctionCall/stmt = new
|
||||||
return RunFunction(stmt)
|
stmt.func_name = func.func_name
|
||||||
else
|
stmt.parameters = params
|
||||||
return call(func)(arglist(params))
|
return RunFunction(stmt)
|
||||||
//CRASH("Unknown function type '[name]'.")
|
|
||||||
|
else
|
||||||
|
return call(func)(arglist(params))
|
||||||
|
//CRASH("Unknown function type '[name]'.")
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Event: HandleError
|
Event: HandleError
|
||||||
Called when the interpreter throws a runtime error.
|
Called when the interpreter throws a runtime error.
|
||||||
|
|
||||||
See Also:
|
See Also:
|
||||||
- <runtimeError>
|
- <runtimeError>
|
||||||
*/
|
*/
|
||||||
HandleError(runtimeError/e)
|
/datum/n_Interpreter/proc/HandleError(var/datum/runtimeError/e)
|
||||||
|
|||||||
@@ -13,328 +13,366 @@
|
|||||||
#define RETURNING 1
|
#define RETURNING 1
|
||||||
#define BREAKING 2
|
#define BREAKING 2
|
||||||
#define CONTINUING 4
|
#define CONTINUING 4
|
||||||
/n_Interpreter
|
|
||||||
var
|
|
||||||
scope
|
|
||||||
curScope
|
|
||||||
globalScope
|
|
||||||
node
|
|
||||||
BlockDefinition/program
|
|
||||||
statement/FunctionDefinition/curFunction
|
|
||||||
stack
|
|
||||||
scopes = new()
|
|
||||||
functions = new()
|
|
||||||
|
|
||||||
datum/container // associated container for interpeter
|
/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
|
Var: status
|
||||||
A variable indicating that the rest of the current block should be skipped. This may be set to any combination of <Status Macros>.
|
A variable indicating that the rest of the current block should be skipped. This may be set to any combination of <Status Macros>.
|
||||||
*/
|
*/
|
||||||
status=0
|
var/status = 0
|
||||||
returnVal
|
var/returnVal
|
||||||
|
|
||||||
max_statements=900 // maximum amount of statements that can be called in one execution. this is to prevent massive crashes and exploitation
|
var/max_statements = 900 // maximum amount of statements that can be called in one execution. this is to prevent massive crashes and exploitation
|
||||||
cur_statements=0 // current amount of statements called
|
var/cur_statements = 0 // current amount of statements called
|
||||||
alertadmins=0 // set to 1 if the admins shouldn't be notified of anymore issues
|
var/alertadmins = 0 // set to 1 if the admins shouldn't be notified of anymore issues
|
||||||
max_iterations=100 // max number of uninterrupted loops possible
|
var/max_iterations = 100 // max number of uninterrupted loops possible
|
||||||
max_recursion=10 // max recursions without returning anything (or completing the code block)
|
var/max_recursion = 10 // max recursions without returning anything (or completing the code block)
|
||||||
cur_recursion=0 // current amount of recursion
|
var/cur_recursion = 0 // current amount of recursion
|
||||||
/*
|
/*
|
||||||
Var: persist
|
Var: persist
|
||||||
If 0, global variables will be reset after Run() finishes.
|
If 0, global variables will be reset after Run() finishes.
|
||||||
*/
|
*/
|
||||||
persist=1
|
var/persist = 1
|
||||||
paused=0
|
var/paused = 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Constructor: New
|
Constructor: New
|
||||||
Calls <Load()> with the given parameters.
|
Calls <Load()> with the given parameters.
|
||||||
*/
|
*/
|
||||||
New(node/BlockDefinition/GlobalBlock/program=null)
|
/datum/n_Interpreter/New(datum/node/BlockDefinition/GlobalBlock/program = null)
|
||||||
.=..()
|
. = ..()
|
||||||
if(program)Load(program)
|
if(program)
|
||||||
|
Load(program)
|
||||||
proc
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Set ourselves to Garbage Collect
|
Set ourselves to Garbage Collect
|
||||||
*/
|
*/
|
||||||
GC()
|
/datum/n_Interpreter/proc/GC()
|
||||||
..()
|
..()
|
||||||
container = null
|
container = null
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: RaiseError
|
Proc: RaiseError
|
||||||
Raises a runtime error.
|
Raises a runtime error.
|
||||||
*/
|
*/
|
||||||
RaiseError(runtimeError/e)
|
/datum/n_Interpreter/proc/RaiseError(datum/runtimeError/e)
|
||||||
e.stack=functions.Copy()
|
e.stack = functions.Copy()
|
||||||
e.stack.Push(curFunction)
|
e.stack.Push(curFunction)
|
||||||
src.HandleError(e)
|
src.HandleError(e)
|
||||||
|
|
||||||
CreateScope(node/BlockDefinition/B)
|
/datum/n_Interpreter/proc/CreateScope(datum/node/BlockDefinition/B)
|
||||||
var/scope/S = new(B, curScope)
|
var/datum/scope/S = new(B, curScope)
|
||||||
scopes.Push(curScope)
|
scopes.Push(curScope)
|
||||||
curScope = S
|
curScope = S
|
||||||
return S
|
return S
|
||||||
|
|
||||||
CreateGlobalScope()
|
/datum/n_Interpreter/proc/CreateGlobalScope()
|
||||||
scopes.Clear()
|
scopes.Clear()
|
||||||
var/scope/S = new(program, null)
|
var/datum/scope/S = new(program, null)
|
||||||
globalScope = S
|
globalScope = S
|
||||||
return S
|
return S
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: AlertAdmins
|
Proc: AlertAdmins
|
||||||
Alerts the admins of a script that is bad.
|
Alerts the admins of a script that is bad.
|
||||||
*/
|
*/
|
||||||
AlertAdmins()
|
/datum/n_Interpreter/proc/AlertAdmins()
|
||||||
if(container && !alertadmins)
|
if(container && !alertadmins)
|
||||||
if(istype(container, /datum/TCS_Compiler))
|
if(istype(container, /datum/TCS_Compiler))
|
||||||
var/datum/TCS_Compiler/Compiler = container
|
var/datum/TCS_Compiler/Compiler = container
|
||||||
var/obj/machinery/telecomms/server/Holder = Compiler.Holder
|
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])."
|
var/message = "Potential crash-inducing NTSL script detected at telecommunications server [Compiler.Holder] ([Holder.x], [Holder.y], [Holder.z])."
|
||||||
|
|
||||||
alertadmins = 1
|
alertadmins = 1
|
||||||
message_admins(message, 1)
|
message_admins(message, 1)
|
||||||
/*
|
/*
|
||||||
Proc: RunBlock
|
Proc: RunBlock
|
||||||
Runs each statement in a block of code.
|
Runs each statement in a block of code.
|
||||||
*/
|
*/
|
||||||
RunBlock(node/BlockDefinition/Block, scope/scope = null)
|
/datum/n_Interpreter/proc/RunBlock(var/datum/node/BlockDefinition/Block, var/datum/scope/scope = null)
|
||||||
var/is_global = istype(Block, /node/BlockDefinition/GlobalBlock)
|
var/is_global = istype(Block, /datum/node/BlockDefinition/GlobalBlock)
|
||||||
if(!is_global)
|
if(!is_global)
|
||||||
if(scope)
|
if(scope)
|
||||||
curScope = scope
|
curScope = scope
|
||||||
else
|
else
|
||||||
CreateScope(Block)
|
CreateScope(Block)
|
||||||
else
|
else
|
||||||
if(!persist)
|
if(!persist)
|
||||||
CreateGlobalScope()
|
CreateGlobalScope()
|
||||||
curScope = globalScope
|
|
||||||
|
|
||||||
if(cur_statements < max_statements)
|
curScope = globalScope
|
||||||
|
|
||||||
for(var/node/statement/S in Block.statements)
|
if(cur_statements < max_statements)
|
||||||
while(paused) sleep(10)
|
for(var/datum/node/statement/S in Block.statements)
|
||||||
|
while(paused) sleep(10)
|
||||||
|
|
||||||
cur_statements++
|
cur_statements++
|
||||||
if(cur_statements >= max_statements)
|
if(cur_statements >= max_statements)
|
||||||
RaiseError(new/runtimeError/MaxCPU())
|
RaiseError(new/datum/runtimeError/MaxCPU())
|
||||||
AlertAdmins()
|
|
||||||
break
|
|
||||||
|
|
||||||
if(istype(S, /node/statement/VariableAssignment))
|
|
||||||
var/node/statement/VariableAssignment/stmt = S
|
|
||||||
var/name = stmt.var_name.id_name
|
|
||||||
if(!stmt.object)
|
|
||||||
// Below we assign the variable first to null if it doesn't already exist.
|
|
||||||
// This is necessary for assignments like +=, and when the variable is used in a function
|
|
||||||
// If the variable already exists in a different block, then AssignVariable will automatically use that one.
|
|
||||||
if(!IsVariableAccessible(name))
|
|
||||||
AssignVariable(name, null)
|
|
||||||
AssignVariable(name, Eval(stmt.value))
|
|
||||||
else
|
|
||||||
var/datum/D = Eval(GetVariable(stmt.object.id_name))
|
|
||||||
if(!D) return
|
|
||||||
D.vars[stmt.var_name.id_name] = Eval(stmt.value)
|
|
||||||
else if(istype(S, /node/statement/VariableDeclaration))
|
|
||||||
//VariableDeclaration nodes are used to forcibly declare a local variable so that one in a higher scope isn't used by default.
|
|
||||||
var/node/statement/VariableDeclaration/dec=S
|
|
||||||
if(!dec.object)
|
|
||||||
AssignVariable(dec.var_name.id_name, null, curScope)
|
|
||||||
else
|
|
||||||
var/datum/D = Eval(GetVariable(dec.object.id_name))
|
|
||||||
if(!D) return
|
|
||||||
D.vars[dec.var_name.id_name] = null
|
|
||||||
else if(istype(S, /node/statement/FunctionCall))
|
|
||||||
RunFunction(S)
|
|
||||||
else if(istype(S, /node/statement/FunctionDefinition))
|
|
||||||
//do nothing
|
|
||||||
else if(istype(S, /node/statement/WhileLoop))
|
|
||||||
RunWhile(S)
|
|
||||||
else if(istype(S, /node/statement/IfStatement))
|
|
||||||
RunIf(S)
|
|
||||||
else if(istype(S, /node/statement/ReturnStatement))
|
|
||||||
if(!curFunction)
|
|
||||||
RaiseError(new/runtimeError/UnexpectedReturn())
|
|
||||||
continue
|
|
||||||
status |= RETURNING
|
|
||||||
returnVal=Eval(S:value)
|
|
||||||
break
|
|
||||||
else if(istype(S, /node/statement/BreakStatement))
|
|
||||||
status |= BREAKING
|
|
||||||
break
|
|
||||||
else if(istype(S, /node/statement/ContinueStatement))
|
|
||||||
status |= CONTINUING
|
|
||||||
break
|
|
||||||
else
|
|
||||||
RaiseError(new/runtimeError/UnknownInstruction())
|
|
||||||
if(status)
|
|
||||||
break
|
|
||||||
|
|
||||||
curScope = scopes.Pop()
|
|
||||||
|
|
||||||
/*
|
|
||||||
Proc: RunFunction
|
|
||||||
Runs a function block or a proc with the arguments specified in the script.
|
|
||||||
*/
|
|
||||||
RunFunction(node/statement/FunctionCall/stmt)
|
|
||||||
//Note that anywhere /node/statement/FunctionCall/stmt is used so may /node/expression/FunctionCall
|
|
||||||
|
|
||||||
// If recursion gets too high (max 50 nested functions) throw an error
|
|
||||||
if(cur_recursion >= max_recursion)
|
|
||||||
AlertAdmins()
|
AlertAdmins()
|
||||||
RaiseError(new/runtimeError/RecursionLimitReached())
|
break
|
||||||
return 0
|
|
||||||
|
|
||||||
var/node/statement/FunctionDefinition/def
|
if(istype(S, /datum/node/statement/VariableAssignment))
|
||||||
if(!stmt.object) //A scope's function is being called, stmt.object is null
|
var/datum/node/statement/VariableAssignment/stmt = S
|
||||||
def = GetFunction(stmt.func_name)
|
var/name = stmt.var_name.id_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
|
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)
|
||||||
|
|
||||||
cur_recursion++ // add recursion
|
AssignVariable(name, Eval(stmt.value))
|
||||||
if(istype(def))
|
else
|
||||||
if(curFunction) functions.Push(curFunction)
|
var/datum/D = Eval(GetVariable(stmt.object.id_name))
|
||||||
var/scope/S = CreateScope(def.block)
|
if(!D)
|
||||||
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/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/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/runtimeError/UndefinedFunction("[stmt.object.id_name].[stmt.func_name]"))
|
|
||||||
return
|
return
|
||||||
return call(def, stmt.func_name)(arglist(params))
|
|
||||||
else //def is a path to a global proc
|
D.vars[stmt.var_name.id_name] = Eval(stmt.value)
|
||||||
return call(def)(arglist(params))
|
|
||||||
|
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())
|
||||||
|
|
||||||
|
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
|
//else
|
||||||
// RaiseError(new/runtimeError/UnknownInstruction())
|
// 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
|
Proc: RunIf
|
||||||
Checks a condition and runs either the if block or else block.
|
Checks a condition and runs either the if block or else block.
|
||||||
*/
|
*/
|
||||||
RunIf(node/statement/IfStatement/stmt)
|
/datum/n_Interpreter/proc/RunIf(var/datum/node/statement/IfStatement/stmt)
|
||||||
if(!stmt.skip)
|
if(!stmt.skip)
|
||||||
if(Eval(stmt.cond))
|
if(Eval(stmt.cond))
|
||||||
RunBlock(stmt.block)
|
RunBlock(stmt.block)
|
||||||
// Loop through the if else chain and tell them to be skipped.
|
// Loop through the if else chain and tell them to be skipped.
|
||||||
var/node/statement/IfStatement/i = stmt.else_if
|
var/datum/node/statement/IfStatement/i = stmt.else_if
|
||||||
var/fail_safe = 800
|
var/fail_safe = 800
|
||||||
while(i && fail_safe)
|
|
||||||
fail_safe -= 1
|
|
||||||
i.skip = 1
|
|
||||||
i = i.else_if
|
|
||||||
|
|
||||||
else if(stmt.else_block)
|
while(i && fail_safe)
|
||||||
RunBlock(stmt.else_block)
|
fail_safe -= 1
|
||||||
// We don't need to skip you anymore.
|
i.skip = 1
|
||||||
stmt.skip = 0
|
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
|
Proc: RunWhile
|
||||||
Runs a while loop.
|
Runs a while loop.
|
||||||
*/
|
*/
|
||||||
RunWhile(node/statement/WhileLoop/stmt)
|
|
||||||
var/i=1
|
/datum/n_Interpreter/proc/RunWhile(var/datum/node/statement/WhileLoop/stmt)
|
||||||
while(Eval(stmt.cond) && Iterate(stmt.block, i++))
|
var/i = 1
|
||||||
continue
|
while(Eval(stmt.cond) && Iterate(stmt.block, i++))
|
||||||
status &= ~BREAKING
|
continue
|
||||||
|
|
||||||
|
status &= ~BREAKING
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc:Iterate
|
Proc:Iterate
|
||||||
Runs a single iteration of a loop. Returns a value indicating whether or not to continue looping.
|
Runs a single iteration of a loop. Returns a value indicating whether or not to continue looping.
|
||||||
*/
|
*/
|
||||||
Iterate(node/BlockDefinition/block, count)
|
|
||||||
RunBlock(block)
|
/datum/n_Interpreter/proc/Iterate(var/datum/node/BlockDefinition/block, count)
|
||||||
if(max_iterations > 0 && count >= max_iterations)
|
RunBlock(block)
|
||||||
RaiseError(new/runtimeError/IterationLimitReached())
|
if(max_iterations > 0 && count >= max_iterations)
|
||||||
return 0
|
RaiseError(new/datum/runtimeError/IterationLimitReached())
|
||||||
if(status & (BREAKING|RETURNING))
|
return 0
|
||||||
return 0
|
|
||||||
status &= ~CONTINUING
|
if(status & (BREAKING|RETURNING))
|
||||||
return 1
|
return 0
|
||||||
|
|
||||||
|
status &= ~CONTINUING
|
||||||
|
return 1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: GetFunction
|
Proc: GetFunction
|
||||||
Finds a function in an accessible scope with the given name. Returns a <FunctionDefinition>.
|
Finds a function in an accessible scope with the given name. Returns a <FunctionDefinition>.
|
||||||
*/
|
*/
|
||||||
GetFunction(name)
|
|
||||||
var/scope/S = curScope
|
/datum/n_Interpreter/proc/GetFunction(name)
|
||||||
while(S)
|
var/datum/scope/S = curScope
|
||||||
if(S.functions.Find(name))
|
while(S)
|
||||||
return S.functions[name]
|
if(S.functions.Find(name))
|
||||||
S = S.parent
|
return S.functions[name]
|
||||||
RaiseError(new/runtimeError/UndefinedFunction(name))
|
S = S.parent
|
||||||
|
|
||||||
|
RaiseError(new/datum/runtimeError/UndefinedFunction(name))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: GetVariable
|
Proc: GetVariable
|
||||||
Finds a variable in an accessible scope and returns its value.
|
Finds a variable in an accessible scope and returns its value.
|
||||||
*/
|
*/
|
||||||
GetVariable(name)
|
|
||||||
var/scope/S = curScope
|
|
||||||
while(S)
|
|
||||||
if(S.variables.Find(name))
|
|
||||||
return S.variables[name]
|
|
||||||
S = S.parent
|
|
||||||
RaiseError(new/runtimeError/UndefinedVariable(name))
|
|
||||||
|
|
||||||
GetVariableScope(name) //needed for when you reassign a variable in a higher scope
|
/datum/n_Interpreter/proc/GetVariable(name)
|
||||||
var/scope/S = curScope
|
var/datum/scope/S = curScope
|
||||||
while(S)
|
while(S)
|
||||||
if(S.variables.Find(name))
|
if(S.variables.Find(name))
|
||||||
return S
|
return S.variables[name]
|
||||||
S = S.parent
|
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
|
||||||
|
|
||||||
|
|
||||||
IsVariableAccessible(name)
|
/datum/n_Interpreter/proc/IsVariableAccessible(name)
|
||||||
var/scope/S = curScope
|
var/datum/scope/S = curScope
|
||||||
while(S)
|
while(S)
|
||||||
if(S.variables.Find(name))
|
if(S.variables.Find(name))
|
||||||
return TRUE
|
return TRUE
|
||||||
S = S.parent
|
S = S.parent
|
||||||
return FALSE
|
|
||||||
|
return FALSE
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: AssignVariable
|
Proc: AssignVariable
|
||||||
Assigns a value to a variable in a specific block.
|
Assigns a value to a variable in a specific block.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
name - The name of the variable to assign.
|
name - The name of the variable to assign.
|
||||||
value - The value to assign to it.
|
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.
|
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.
|
||||||
*/
|
*/
|
||||||
AssignVariable(name, node/expression/value, scope/S=null)
|
|
||||||
if(!S) S = GetVariableScope(name)
|
/datum/n_Interpreter/proc/AssignVariable(name, datum/node/expression/value, var/datum/scope/S = null)
|
||||||
if(!S) S = curScope
|
if(!S) S = GetVariableScope(name)
|
||||||
if(!S) S = globalScope
|
if(!S) S = curScope
|
||||||
ASSERT(istype(S))
|
if(!S) S = globalScope
|
||||||
if(istext(value) || isnum(value) || isnull(value)) value = new/node/expression/value/literal(value)
|
|
||||||
else if(!istype(value) && isobject(value)) value = new/node/expression/value/reference(value)
|
ASSERT(istype(S))
|
||||||
//TODO: check for invalid name
|
if(istext(value) || isnum(value) || isnull(value)) value = new/datum/node/expression/value/literal(value)
|
||||||
S.variables["[name]"] = value
|
else if(!istype(value) && isobject(value)) value = new/datum/node/expression/value/reference(value)
|
||||||
|
//TODO: check for invalid name
|
||||||
|
S.variables["[name]"] = value
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,15 @@
|
|||||||
Class: scope
|
Class: scope
|
||||||
A runtime instance of a block. Used internally by the interpreter.
|
A runtime instance of a block. Used internally by the interpreter.
|
||||||
*/
|
*/
|
||||||
scope
|
/datum/scope
|
||||||
var
|
var/datum/scope/parent = null
|
||||||
scope/parent = null
|
var/datum/node/BlockDefinition/block
|
||||||
node/BlockDefinition/block
|
var/list/functions
|
||||||
list
|
var/list/variables
|
||||||
functions
|
|
||||||
variables
|
|
||||||
|
|
||||||
New(node/BlockDefinition/B, scope/parent)
|
/datum/scope/New(var/datum/node/BlockDefinition/B, var/datum/scope/parent)
|
||||||
src.block = B
|
src.block = B
|
||||||
src.parent = parent
|
src.parent = parent
|
||||||
src.variables = B.initial_variables.Copy()
|
src.variables = B.initial_variables.Copy()
|
||||||
src.functions = B.functions.Copy()
|
src.functions = B.functions.Copy()
|
||||||
.=..()
|
. = ..()
|
||||||
|
|||||||
@@ -1,82 +1,121 @@
|
|||||||
/*
|
/*
|
||||||
File: Options
|
File: Options
|
||||||
*/
|
*/
|
||||||
var/const //Ascii values of characters
|
// Ascii values of characters
|
||||||
ascii_A =65
|
/var/const/ascii_A = 65
|
||||||
ascii_Z =90
|
/var/const/ascii_Z = 90
|
||||||
ascii_a =97
|
/var/const/ascii_a = 97
|
||||||
ascii_z =122
|
/var/const/ascii_z = 122
|
||||||
ascii_DOLLAR = 36 // $
|
/var/const/ascii_DOLLAR = 36 // $
|
||||||
ascii_ZERO=48
|
/var/const/ascii_ZERO = 48
|
||||||
ascii_NINE=57
|
/var/const/ascii_NINE = 57
|
||||||
ascii_UNDERSCORE=95 // _
|
/var/const/ascii_UNDERSCORE = 95 // _
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: n_scriptOptions
|
Class: n_scriptOptions
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
proc
|
if(!isnum(char))
|
||||||
CanStartID(char) //returns true if the character can start a variable, function, or keyword name (by default letters or an underscore)
|
char = text2ascii(char)
|
||||||
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
|
|
||||||
|
|
||||||
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)
|
return (char in ascii_A to ascii_Z) || (char in ascii_a to ascii_z) || char == ascii_UNDERSCORE || char == ascii_DOLLAR
|
||||||
if(!isnum(char))char=text2ascii(char)
|
|
||||||
return CanStartID(char) || IsDigit(char)
|
|
||||||
|
|
||||||
IsDigit(char)
|
/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)
|
if(!isnum(char))
|
||||||
return char in ascii_ZERO to ascii_NINE
|
char = text2ascii(char)
|
||||||
|
|
||||||
IsValidID(id) //returns true if all the characters in the string are okay to be in an identifier name
|
return CanStartID(char) || IsDigit(char)
|
||||||
if(!CanStartID(id)) //don't need to grab first char in id, since text2ascii does it automatically
|
|
||||||
return 0
|
/datum/n_scriptOptions/proc/IsDigit(char)
|
||||||
if(length(id)==1) return 1
|
if(!isnum(char))
|
||||||
for(var/i=2 to length(id))
|
char = text2ascii(char)
|
||||||
if(!IsValidIDChar(copytext(id, i, i+1)))
|
|
||||||
return 0
|
return char in ascii_ZERO to ascii_NINE
|
||||||
return 1
|
|
||||||
|
/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
|
||||||
|
|
||||||
|
if(length(id) == 1)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
for(var/i=2 to length(id))
|
||||||
|
if(!IsValidIDChar(copytext(id, i, i + 1)))
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: nS_Options
|
Class: nS_Options
|
||||||
An implementation of <n_scriptOptions> for the n_Script language.
|
An implementation of <n_scriptOptions> for the n_Script language.
|
||||||
*/
|
*/
|
||||||
nS_Options
|
/datum/n_scriptOptions/nS_Options
|
||||||
var
|
var/list/symbols = list(
|
||||||
list
|
"(",
|
||||||
symbols = list("(", ")", "\[", "]", ";", ",", "{", "}") //scanner - Characters that can be in symbols
|
")",
|
||||||
|
"\[",
|
||||||
|
"]",
|
||||||
|
";",
|
||||||
|
",",
|
||||||
|
"{",
|
||||||
|
"}"
|
||||||
|
) //scanner - Characters that can be in symbols
|
||||||
/*
|
/*
|
||||||
Var: keywords
|
Var: keywords
|
||||||
An associative list used by the parser to parse keywords. Indices are strings which will trigger the keyword when parsed and the
|
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.
|
associated values are <nS_Keyword> types of which the <n_Keyword.Parse()> proc will be called.
|
||||||
*/
|
*/
|
||||||
keywords = list("if" = /n_Keyword/nS_Keyword/kwIf, "else" = /n_Keyword/nS_Keyword/kwElse, "elseif" = /n_Keyword/nS_Keyword/kwElseIf, \
|
var/list/keywords = list(
|
||||||
"while" = /n_Keyword/nS_Keyword/kwWhile, "break" = /n_Keyword/nS_Keyword/kwBreak, \
|
"if" = /datum/n_Keyword/nS_Keyword/kwIf,
|
||||||
"continue" = /n_Keyword/nS_Keyword/kwContinue, \
|
"else" = /datum/n_Keyword/nS_Keyword/kwElse,
|
||||||
"return" = /n_Keyword/nS_Keyword/kwReturn, "def" = /n_Keyword/nS_Keyword/kwDef)
|
"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
|
||||||
|
)
|
||||||
|
|
||||||
list
|
var/list/assign_operators = list(
|
||||||
assign_operators=list("=" = null, "&=" = "&",
|
"=" = null,
|
||||||
"|=" = "|", "`=" = "`",
|
"&=" = "&",
|
||||||
"+=" = "+", "-=" = "-",
|
"|=" = "|",
|
||||||
"*=" = "*", "/=" = "/",
|
"`=" = "`",
|
||||||
"^=" = "^",
|
"+=" = "+",
|
||||||
"%=" = "%")
|
"-=" = "-",
|
||||||
|
"*=" = "*",
|
||||||
|
"/=" = "/",
|
||||||
|
"^=" = "^",
|
||||||
|
"%=" = "%"
|
||||||
|
)
|
||||||
|
|
||||||
unary_operators =list("!" = /node/expression/operator/unary/LogicalNot, "~" = /node/expression/operator/unary/BitwiseNot,
|
var/list/unary_operators = list(
|
||||||
"-" = /node/expression/operator/unary/Minus)
|
"!" = /datum/node/expression/operator/unary/LogicalNot,
|
||||||
|
"~" = /datum/node/expression/operator/unary/BitwiseNot,
|
||||||
|
"-" = /datum/node/expression/operator/unary/Minus
|
||||||
|
)
|
||||||
|
|
||||||
binary_operators=list("==" = /node/expression/operator/binary/Equal, "!=" = /node/expression/operator/binary/NotEqual,
|
var/list/binary_operators = list(
|
||||||
">" = /node/expression/operator/binary/Greater, "<" = /node/expression/operator/binary/Less,
|
"==" = /datum/node/expression/operator/binary/Equal,
|
||||||
">=" = /node/expression/operator/binary/GreaterOrEqual,"<=" = /node/expression/operator/binary/LessOrEqual,
|
"!=" = /datum/node/expression/operator/binary/NotEqual,
|
||||||
"&&" = /node/expression/operator/binary/LogicalAnd, "||" = /node/expression/operator/binary/LogicalOr,
|
">" = /datum/node/expression/operator/binary/Greater,
|
||||||
"&" = /node/expression/operator/binary/BitwiseAnd, "|" = /node/expression/operator/binary/BitwiseOr,
|
"<" = /datum/node/expression/operator/binary/Less,
|
||||||
"`" = /node/expression/operator/binary/BitwiseXor, "+" = /node/expression/operator/binary/Add,
|
">=" = /datum/node/expression/operator/binary/GreaterOrEqual,
|
||||||
"-" = /node/expression/operator/binary/Subtract, "*" = /node/expression/operator/binary/Multiply,
|
"<=" = /datum/node/expression/operator/binary/LessOrEqual,
|
||||||
"/" = /node/expression/operator/binary/Divide, "^" = /node/expression/operator/binary/Power,
|
"&&" = /datum/node/expression/operator/binary/LogicalAnd,
|
||||||
"%" = /node/expression/operator/binary/Modulo)
|
"||" = /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
|
||||||
|
)
|
||||||
|
|
||||||
New()
|
/datum/n_scriptOptions/nS_Options/New()
|
||||||
.=..()
|
. = ..()
|
||||||
for(var/O in assign_operators+binary_operators+unary_operators)
|
for(var/O in assign_operators + binary_operators + unary_operators)
|
||||||
if(!symbols.Find(O)) symbols+=O
|
if(!symbols.Find(O))
|
||||||
|
symbols += O
|
||||||
@@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Macros: Expression Macros
|
Macros: Expression Macros
|
||||||
OPERATOR - A value indicating the parser currently expects a binary operator.
|
OPERATOR - A value indicating the parser currently expects a binary operator.
|
||||||
VALUE - A value indicating the parser currently expects a value.
|
VALUE - A value indicating the parser currently expects a value.
|
||||||
SHIFT - Tells the parser to push the current operator onto the stack.
|
SHIFT - Tells the parser to push the current operator onto the stack.
|
||||||
REDUCE - Tells the parser to reduce the stack.
|
REDUCE - Tells the parser to reduce the stack.
|
||||||
*/
|
*/
|
||||||
#define OPERATOR 1
|
#define OPERATOR 1
|
||||||
#define VALUE 2
|
#define VALUE 2
|
||||||
@@ -18,63 +18,71 @@
|
|||||||
/*
|
/*
|
||||||
Class: nS_Parser
|
Class: nS_Parser
|
||||||
*/
|
*/
|
||||||
/n_Parser/nS_Parser
|
/datum/n_Parser/nS_Parser
|
||||||
var
|
|
||||||
/*
|
/*
|
||||||
Var: expecting
|
Var: expecting
|
||||||
A variable which keeps track of whether an operator or value is expected. It should be either <OPERATOR> or <VALUE>. See <ParseExpression()>
|
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.
|
for more information.
|
||||||
*/
|
*/
|
||||||
expecting=VALUE
|
var/expecting = VALUE
|
||||||
|
|
||||||
proc
|
|
||||||
/*
|
/*
|
||||||
Proc: Precedence
|
Proc: Precedence
|
||||||
Compares two operators, decides which is higher in the order of operations, and returns <SHIFT> or <REDUCE>.
|
Compares two operators, decides which is higher in the order of operations, and returns <SHIFT> or <REDUCE>.
|
||||||
*/
|
*/
|
||||||
Precedence(node/expression/operator/top, node/expression/operator/input)
|
/datum/n_Parser/nS_Parser/proc/Precedence(var/datum/node/expression/operator/top, var/datum/node/expression/operator/input)
|
||||||
if(istype(top))
|
if(istype(top))
|
||||||
top=top.precedence
|
top = top.precedence
|
||||||
if(istype(input))
|
|
||||||
input=input:precedence
|
if(istype(input))
|
||||||
if(top>=input)
|
|
||||||
return REDUCE
|
input = input:precedence
|
||||||
return SHIFT
|
|
||||||
|
if(top >= input)
|
||||||
|
return REDUCE
|
||||||
|
|
||||||
|
return SHIFT
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: GetExpression
|
Proc: GetExpression
|
||||||
Takes a token expected to represent a value and returns an <expression> node.
|
Takes a token expected to represent a value and returns an <expression> node.
|
||||||
*/
|
*/
|
||||||
GetExpression(token/T)
|
/datum/n_Parser/nS_Parser/proc/GetExpression(var/datum/token/T)
|
||||||
if(!T) return
|
if(!T)
|
||||||
if(istype(T, /node/expression))
|
return
|
||||||
return T
|
|
||||||
switch(T.type)
|
|
||||||
if(/token/word)
|
|
||||||
return new/node/expression/value/variable(T.value)
|
|
||||||
if(/token/accessor)
|
|
||||||
var
|
|
||||||
token/accessor/A=T
|
|
||||||
node/expression/value/variable/E//=new(A.member)
|
|
||||||
stack/S=new()
|
|
||||||
while(istype(A.object, /token/accessor))
|
|
||||||
S.Push(A)
|
|
||||||
A=A.object
|
|
||||||
ASSERT(istext(A.object))
|
|
||||||
|
|
||||||
while(A)
|
if(istype(T, /datum/node/expression))
|
||||||
var/node/expression/value/variable/V=new()
|
return T
|
||||||
V.id=new(A.member)
|
|
||||||
if(E)
|
|
||||||
V.object=E
|
|
||||||
else
|
|
||||||
V.object=new/node/identifier(A.object)
|
|
||||||
E=V
|
|
||||||
A=S.Pop()
|
|
||||||
return E
|
|
||||||
|
|
||||||
if(/token/number, /token/string)
|
switch(T.type)
|
||||||
return new/node/expression/value/literal(T.value)
|
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
|
Proc: GetOperator
|
||||||
@@ -91,15 +99,26 @@
|
|||||||
- <GetBinaryOperator()>
|
- <GetBinaryOperator()>
|
||||||
- <GetUnaryOperator()>
|
- <GetUnaryOperator()>
|
||||||
*/
|
*/
|
||||||
GetOperator(O, type=/node/expression/operator, L[])
|
/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, type))
|
||||||
if(istype(O, /token)) O=O:value //sets O to text
|
return O //O is already the desired type
|
||||||
if(istext(O)) //sets O to path
|
|
||||||
if(L.Find(O)) O=L[O]
|
if(istype(O, /datum/token))
|
||||||
else return null
|
O = O:value //sets O to text
|
||||||
if(ispath(O))O=new O //catches path from last check
|
|
||||||
else return null //Unknown type
|
if(istext(O)) //sets O to path
|
||||||
return O
|
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
|
Proc: GetBinaryOperator
|
||||||
@@ -110,8 +129,8 @@
|
|||||||
- <GetOperator()>
|
- <GetOperator()>
|
||||||
- <GetUnaryOperator()>
|
- <GetUnaryOperator()>
|
||||||
*/
|
*/
|
||||||
GetBinaryOperator(O)
|
/datum/n_Parser/nS_Parser/proc/GetBinaryOperator(O)
|
||||||
return GetOperator(O, /node/expression/operator/binary, options.binary_operators)
|
return GetOperator(O, /datum/node/expression/operator/binary, options.binary_operators)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: GetUnaryOperator
|
Proc: GetUnaryOperator
|
||||||
@@ -122,30 +141,34 @@
|
|||||||
- <GetOperator()>
|
- <GetOperator()>
|
||||||
- <GetBinaryOperator()>
|
- <GetBinaryOperator()>
|
||||||
*/
|
*/
|
||||||
GetUnaryOperator(O)
|
/datum/n_Parser/nS_Parser/proc/GetUnaryOperator(O)
|
||||||
return GetOperator(O, /node/expression/operator/unary, options.unary_operators)
|
return GetOperator(O, /datum/node/expression/operator/unary, options.unary_operators)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: Reduce
|
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
|
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.
|
of the val stack.
|
||||||
*/
|
*/
|
||||||
Reduce(stack/opr, stack/val)
|
/datum/n_Parser/nS_Parser/proc/Reduce(var/datum/stack/opr, var/datum/stack/val)
|
||||||
var/node/expression/operator/O=opr.Pop()
|
var/datum/node/expression/operator/O = opr.Pop()
|
||||||
if(!O) return
|
if(!O)
|
||||||
if(!istype(O))
|
return
|
||||||
errors+=new/scriptError("Error reducing expression - invalid operator.")
|
|
||||||
return
|
if(!istype(O))
|
||||||
//Take O and assign its operands, popping one or two values from the val stack
|
errors += new/datum/scriptError("Error reducing expression - invalid operator.")
|
||||||
//depending on whether O is a binary or unary operator.
|
return
|
||||||
if(istype(O, /node/expression/operator/binary))
|
|
||||||
var/node/expression/operator/binary/B=O
|
//Take O and assign its operands, popping one or two values from the val stack
|
||||||
B.exp2=val.Pop()
|
//depending on whether O is a binary or unary operator.
|
||||||
B.exp =val.Pop()
|
if(istype(O, /datum/node/expression/operator/binary))
|
||||||
val.Push(B)
|
var/datum/node/expression/operator/binary/B=O
|
||||||
else
|
B.exp2 = val.Pop()
|
||||||
O.exp=val.Pop()
|
B.exp = val.Pop()
|
||||||
val.Push(O)
|
val.Push(B)
|
||||||
|
|
||||||
|
else
|
||||||
|
O.exp=val.Pop()
|
||||||
|
val.Push(O)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: EndOfExpression
|
Proc: EndOfExpression
|
||||||
@@ -154,14 +177,14 @@
|
|||||||
Parameters:
|
Parameters:
|
||||||
end - A list of values to compare the current token to.
|
end - A list of values to compare the current token to.
|
||||||
*/
|
*/
|
||||||
EndOfExpression(end[])
|
/datum/n_Parser/nS_Parser/proc/EndOfExpression(end[])
|
||||||
if(!curToken)
|
if(!curToken)
|
||||||
return 1
|
return 1
|
||||||
if(istype(curToken, /token/symbol) && end.Find(curToken.value))
|
if(istype(curToken, /datum/token/symbol) && end.Find(curToken.value))
|
||||||
return 1
|
return 1
|
||||||
if(istype(curToken, /token/end) && end.Find(/token/end))
|
if(istype(curToken, /datum/token/end) && end.Find(/datum/token/end))
|
||||||
return 1
|
return 1
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: ParseExpression
|
Proc: ParseExpression
|
||||||
@@ -180,106 +203,114 @@
|
|||||||
- <ParseParenExpression()>
|
- <ParseParenExpression()>
|
||||||
- <ParseParamExpression()>
|
- <ParseParamExpression()>
|
||||||
*/
|
*/
|
||||||
ParseExpression(list/end=list(/token/end), list/ErrChars=list("{", "}"), check_functions = 0)
|
|
||||||
var/stack
|
|
||||||
opr=new
|
|
||||||
val=new
|
|
||||||
src.expecting=VALUE
|
|
||||||
var/loop = 0
|
|
||||||
for()
|
|
||||||
loop++
|
|
||||||
if(loop > 800)
|
|
||||||
errors+=new/scriptError("Too many nested tokens.")
|
|
||||||
return
|
|
||||||
|
|
||||||
if(EndOfExpression(end))
|
/datum/n_Parser/nS_Parser/proc/ParseExpression(var/list/end = list(/datum/token/end), list/ErrChars = list("{", "}"), check_functions = 0)
|
||||||
break
|
var/datum/stack/opr = new
|
||||||
if(istype(curToken, /token/symbol) && ErrChars.Find(curToken.value))
|
var/datum/stack/val = new
|
||||||
errors+=new/scriptError/BadToken(curToken)
|
src.expecting = VALUE
|
||||||
break
|
var/loop = 0
|
||||||
|
for()
|
||||||
|
loop++
|
||||||
|
if(loop > 800)
|
||||||
|
errors += new/datum/scriptError("Too many nested tokens.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if(EndOfExpression(end))
|
||||||
|
break
|
||||||
|
|
||||||
if(index>tokens.len) //End of File
|
if(istype(curToken, /datum/token/symbol) && ErrChars.Find(curToken.value))
|
||||||
errors+=new/scriptError/EndOfFile()
|
errors += new/datum/scriptError/BadToken(curToken)
|
||||||
break
|
break
|
||||||
var/token/ntok
|
|
||||||
if(index+1<=tokens.len)
|
|
||||||
ntok=tokens[index+1]
|
|
||||||
|
|
||||||
if(istype(curToken, /token/symbol) && curToken.value=="(") //Parse parentheses expression
|
if(index > tokens.len) //End of File
|
||||||
if(expecting!=VALUE)
|
errors += new/datum/scriptError/EndOfFile()
|
||||||
errors+=new/scriptError/ExpectedToken("operator", curToken)
|
break
|
||||||
NextToken()
|
|
||||||
continue
|
|
||||||
val.Push(ParseParenExpression())
|
|
||||||
|
|
||||||
else if(istype(curToken, /token/symbol)) //Operator found.
|
var/datum/token/ntok
|
||||||
var/node/expression/operator/curOperator //Figure out whether it is unary or binary and get a new instance.
|
if(index + 1 <= tokens.len)
|
||||||
if(src.expecting==OPERATOR)
|
ntok = tokens[index + 1]
|
||||||
curOperator=GetBinaryOperator(curToken)
|
|
||||||
if(!curOperator)
|
|
||||||
errors+=new/scriptError/ExpectedToken("operator", curToken)
|
|
||||||
NextToken()
|
|
||||||
continue
|
|
||||||
else
|
|
||||||
curOperator=GetUnaryOperator(curToken)
|
|
||||||
if(!curOperator) //given symbol isn't a unary operator
|
|
||||||
errors+=new/scriptError/ExpectedToken("expression", curToken)
|
|
||||||
NextToken()
|
|
||||||
continue
|
|
||||||
|
|
||||||
if(opr.Top() && Precedence(opr.Top(), curOperator)==REDUCE) //Check order of operations and reduce if necessary
|
if(istype(curToken, /datum/token/symbol) && curToken.value == "(") //Parse parentheses expression
|
||||||
Reduce(opr, val)
|
if(expecting != VALUE)
|
||||||
continue
|
errors += new/datum/scriptError/ExpectedToken("operator", curToken)
|
||||||
opr.Push(curOperator)
|
NextToken()
|
||||||
src.expecting=VALUE
|
continue
|
||||||
|
|
||||||
else if(ntok && ntok.value=="(" && istype(ntok, /token/symbol)\
|
val.Push(ParseParenExpression())
|
||||||
&& istype(curToken, /token/word)) //Parse function call
|
|
||||||
|
|
||||||
if(!check_functions)
|
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.
|
||||||
var/token/preToken=curToken
|
if(src.expecting == OPERATOR)
|
||||||
var/old_expect=src.expecting
|
curOperator = GetBinaryOperator(curToken)
|
||||||
var/fex=ParseFunctionExpression()
|
if(!curOperator)
|
||||||
if(old_expect!=VALUE)
|
errors += new/datum/scriptError/ExpectedToken("operator", curToken)
|
||||||
errors+=new/scriptError/ExpectedToken("operator", preToken)
|
NextToken()
|
||||||
NextToken()
|
continue
|
||||||
continue
|
else
|
||||||
val.Push(fex)
|
curOperator = GetUnaryOperator(curToken)
|
||||||
else
|
if(!curOperator) //given symbol isn't a unary operator
|
||||||
errors+=new/scriptError/ParameterFunction(curToken)
|
errors += new/datum/scriptError/ExpectedToken("expression", curToken)
|
||||||
break
|
|
||||||
|
|
||||||
else if(istype(curToken, /token/keyword)) //inline keywords
|
|
||||||
var/n_Keyword/kw=options.keywords[curToken.value]
|
|
||||||
kw=new kw(inline=1)
|
|
||||||
if(kw)
|
|
||||||
if(!kw.Parse(src))
|
|
||||||
return
|
|
||||||
else
|
|
||||||
errors+=new/scriptError/BadToken(curToken)
|
|
||||||
|
|
||||||
else if(istype(curToken, /token/end)) //semicolon found where it wasn't expected
|
|
||||||
errors+=new/scriptError/BadToken(curToken)
|
|
||||||
NextToken()
|
NextToken()
|
||||||
continue
|
continue
|
||||||
else
|
|
||||||
if(expecting!=VALUE)
|
|
||||||
errors+=new/scriptError/ExpectedToken("operator", curToken)
|
|
||||||
NextToken()
|
|
||||||
continue
|
|
||||||
val.Push(GetExpression(curToken))
|
|
||||||
src.expecting=OPERATOR
|
|
||||||
|
|
||||||
|
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()
|
NextToken()
|
||||||
|
continue
|
||||||
|
|
||||||
while(opr.Top()) Reduce(opr, val) //Reduce the value stack completely
|
val.Push(GetExpression(curToken))
|
||||||
.=val.Pop() //Return what should be the last value on the stack
|
src.expecting = OPERATOR
|
||||||
if(val.Top()) //
|
|
||||||
var/node/N=val.Pop()
|
NextToken()
|
||||||
errors+=new/scriptError("Error parsing expression. Unexpected value left on stack: [N.ToString()].")
|
|
||||||
return null
|
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
|
Proc: ParseFunctionExpression
|
||||||
@@ -288,29 +319,33 @@
|
|||||||
See Also:
|
See Also:
|
||||||
- <ParseExpression()>
|
- <ParseExpression()>
|
||||||
*/
|
*/
|
||||||
ParseFunctionExpression()
|
|
||||||
var/node/expression/FunctionCall/exp=new
|
|
||||||
exp.func_name=curToken.value
|
|
||||||
NextToken() //skip function name
|
|
||||||
NextToken() //skip open parenthesis, already found
|
|
||||||
var/loops = 0
|
|
||||||
|
|
||||||
for()
|
/datum/n_Parser/nS_Parser/proc/ParseFunctionExpression()
|
||||||
loops++
|
var/datum/node/expression/FunctionCall/exp = new
|
||||||
if(loops>=800)
|
exp.func_name = curToken.value
|
||||||
errors += new/scriptError("Too many nested expressions.")
|
NextToken() //skip function name
|
||||||
break
|
NextToken() //skip open parenthesis, already found
|
||||||
//CRASH("Something TERRIBLE has gone wrong in ParseFunctionExpression ;__;")
|
var/loops = 0
|
||||||
|
|
||||||
if(istype(curToken, /token/symbol) && curToken.value==")")
|
for()
|
||||||
return exp
|
loops++
|
||||||
exp.parameters+=ParseParamExpression()
|
if(loops >= 800)
|
||||||
if(errors.len)
|
errors += new/datum/scriptError("Too many nested expressions.")
|
||||||
return exp
|
break
|
||||||
if(curToken.value==","&&istype(curToken, /token/symbol))NextToken() //skip comma
|
//CRASH("Something TERRIBLE has gone wrong in ParseFunctionExpression ;__;")
|
||||||
if(istype(curToken, /token/end)) //Prevents infinite loop...
|
|
||||||
errors+=new/scriptError/ExpectedToken(")")
|
if(istype(curToken, /datum/token/symbol) && curToken.value == ")")
|
||||||
return exp
|
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
|
Proc: ParseParenExpression
|
||||||
@@ -319,10 +354,10 @@
|
|||||||
See Also:
|
See Also:
|
||||||
- <ParseExpression()>
|
- <ParseExpression()>
|
||||||
*/
|
*/
|
||||||
ParseParenExpression()
|
/datum/n_Parser/nS_Parser/proc/ParseParenExpression()
|
||||||
if(!CheckToken("(", /token/symbol))
|
if(!CheckToken("(", /datum/token/symbol))
|
||||||
return
|
return
|
||||||
return new/node/expression/operator/unary/group(ParseExpression(list(")")))
|
return new/datum/node/expression/operator/unary/group(ParseExpression(list(")")))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: ParseParamExpression
|
Proc: ParseParamExpression
|
||||||
@@ -331,6 +366,6 @@
|
|||||||
See Also:
|
See Also:
|
||||||
- <ParseExpression()>
|
- <ParseExpression()>
|
||||||
*/
|
*/
|
||||||
ParseParamExpression(var/check_functions = 0)
|
/datum/n_Parser/nS_Parser/proc/ParseParamExpression(var/check_functions = 0)
|
||||||
var/cf = check_functions
|
var/cf = check_functions
|
||||||
return ParseExpression(list(",", ")"), check_functions = cf)
|
return ParseExpression(list(",", ")"), check_functions = cf)
|
||||||
@@ -12,17 +12,17 @@ var/const/KW_WARN = 3 //Warning
|
|||||||
var/const/Class: n_Keyword
|
var/const/Class: n_Keyword
|
||||||
var/const/Represents a special statement in the code triggered by a keyword.
|
var/const/Represents a special statement in the code triggered by a keyword.
|
||||||
*/
|
*/
|
||||||
/n_Keyword
|
/datum/n_Keyword
|
||||||
New(inline=0)
|
|
||||||
src.inline=inline
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Var: inline
|
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).
|
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
|
var/inline
|
||||||
|
|
||||||
|
/datum/n_Keyword/New(inline = 0)
|
||||||
|
src.inline = inline
|
||||||
|
return ..()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: Parse
|
Proc: Parse
|
||||||
Called when the parser finds a keyword in the code.
|
Called when the parser finds a keyword in the code.
|
||||||
@@ -31,9 +31,8 @@ var/const/Represents a special statement in the code triggered by a keyword.
|
|||||||
parser - The parser that created this object. You can use the parameter to manipulate the parser in order to add statements and blocks
|
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.
|
to its AST.
|
||||||
*/
|
*/
|
||||||
proc/Parse(n_Parser/parser)
|
/datum/n_Keyword/proc/Parse(var/datum/n_Parser/parser)
|
||||||
//writepanic("[__FILE__].[__LINE__] ([src.type])([usr ? usr.ckey : ""]) \\proc/Parse() called tick#: [world.time]")
|
return
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: nS_Keyword
|
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
|
A keyword in n_Script. By default these include return, if, else, while, and def. To enable or disable a keyword, change the
|
||||||
@@ -43,150 +42,163 @@ var/const/Represents a special statement in the code triggered by a keyword.
|
|||||||
When a parser is expecting a new statement, and a keyword listed in <nS_Options.keywords> is found, it will call the keyword's
|
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.
|
<n_Keyword.Parse()> proc.
|
||||||
*/
|
*/
|
||||||
//
|
/datum/n_Keyword/nS_Keyword/New(var/inline = 0)
|
||||||
nS_Keyword
|
if(inline)
|
||||||
New(inline=0)
|
del src
|
||||||
if(inline)
|
|
||||||
del src
|
|
||||||
|
|
||||||
kwReturn
|
/datum/n_Keyword/nS_Keyword/kwReturn/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||||
Parse(n_Parser/nS_Parser/parser)
|
. = KW_PASS
|
||||||
.=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.
|
||||||
if(istype(parser.curBlock, /node/BlockDefinition/GlobalBlock)) // Exit out of the program by setting the tokens list size to the same as index.
|
parser.tokens.len = parser.index
|
||||||
parser.tokens.len = parser.index
|
return
|
||||||
return
|
|
||||||
var/node/statement/ReturnStatement/stmt=new
|
|
||||||
parser.NextToken() //skip 'return' token
|
|
||||||
stmt.value=parser.ParseExpression()
|
|
||||||
parser.curBlock.statements+=stmt
|
|
||||||
|
|
||||||
kwIf
|
var/datum/node/statement/ReturnStatement/stmt = new
|
||||||
Parse(n_Parser/nS_Parser/parser)
|
parser.NextToken() //skip 'return' token
|
||||||
.=KW_PASS
|
stmt.value = parser.ParseExpression()
|
||||||
var/node/statement/IfStatement/stmt=new
|
parser.curBlock.statements += stmt
|
||||||
parser.NextToken() //skip 'if' token
|
|
||||||
stmt.cond=parser.ParseParenExpression()
|
|
||||||
if(!parser.CheckToken(")", /token/symbol))
|
|
||||||
return KW_FAIL
|
|
||||||
if(!parser.CheckToken("{", /token/symbol, skip=0)) //Token needs to be preserved for parse loop, so skip=0
|
|
||||||
return KW_ERR
|
|
||||||
parser.curBlock.statements+=stmt
|
|
||||||
stmt.block=new
|
|
||||||
parser.AddBlock(stmt.block)
|
|
||||||
|
|
||||||
kwElseIf
|
/datum/n_Keyword/nS_Keyword/kwIf/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||||
Parse(n_Parser/nS_Parser/parser)
|
. = KW_PASS
|
||||||
.=KW_PASS
|
var/datum/node/statement/IfStatement/stmt = new
|
||||||
var/list/L=parser.curBlock.statements
|
parser.NextToken() //skip 'if' token
|
||||||
var/node/statement/IfStatement/ifstmt
|
stmt.cond = parser.ParseParenExpression()
|
||||||
|
if(!parser.CheckToken(")", /datum/token/symbol))
|
||||||
|
return KW_FAIL
|
||||||
|
|
||||||
if(L && L.len)
|
if(!parser.CheckToken("{", /datum/token/symbol, skip=0)) //Token needs to be preserved for parse loop, so skip=0
|
||||||
ifstmt = L[L.len] //Get the last statement in the current block
|
return KW_ERR
|
||||||
if(!ifstmt || !istype(ifstmt) || ifstmt.else_if)
|
|
||||||
parser.errors += new/scriptError/ExpectedToken("if statement", parser.curToken)
|
|
||||||
return KW_FAIL
|
|
||||||
|
|
||||||
var/node/statement/IfStatement/ElseIf/stmt = new
|
parser.curBlock.statements += stmt
|
||||||
parser.NextToken() //skip 'if' token
|
stmt.block = new
|
||||||
stmt.cond = parser.ParseParenExpression()
|
parser.AddBlock(stmt.block)
|
||||||
if(!parser.CheckToken(")", /token/symbol))
|
|
||||||
return KW_FAIL
|
|
||||||
if(!parser.CheckToken("{", /token/symbol, skip=0)) //Token needs to be preserved for parse loop, so skip=0
|
|
||||||
return KW_ERR
|
|
||||||
parser.curBlock.statements+=stmt
|
|
||||||
stmt.block=new
|
|
||||||
ifstmt.else_if = stmt
|
|
||||||
parser.AddBlock(stmt.block)
|
|
||||||
|
|
||||||
|
/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
|
||||||
|
|
||||||
kwElse
|
if(L && L.len)
|
||||||
Parse(n_Parser/nS_Parser/parser)
|
ifstmt = L[L.len] //Get the last statement in the current block
|
||||||
.=KW_PASS
|
|
||||||
var/list/L=parser.curBlock.statements
|
|
||||||
var/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
|
|
||||||
parser.errors+=new/scriptError/ExpectedToken("if statement",parser.curToken)
|
|
||||||
return KW_FAIL
|
|
||||||
parser.NextToken() //skip 'else' token
|
|
||||||
if(!parser.CheckToken("{", /token/symbol, skip=0))
|
|
||||||
return KW_ERR
|
|
||||||
stmt.else_block=new()
|
|
||||||
parser.AddBlock(stmt.else_block)
|
|
||||||
|
|
||||||
kwWhile
|
if(!ifstmt || !istype(ifstmt) || ifstmt.else_if)
|
||||||
Parse(n_Parser/nS_Parser/parser)
|
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...
|
||||||
.=KW_PASS
|
parser.errors += new/datum/scriptError/ExpectedToken("if statement", parser.curToken)
|
||||||
var/node/statement/WhileLoop/stmt=new
|
return KW_FAIL
|
||||||
parser.NextToken() //skip 'while' token
|
|
||||||
stmt.cond=parser.ParseParenExpression()
|
|
||||||
if(!parser.CheckToken(")", /token/symbol))
|
|
||||||
return KW_FAIL
|
|
||||||
if(!parser.CheckToken("{", /token/symbol, skip=0))
|
|
||||||
return KW_ERR
|
|
||||||
parser.curBlock.statements+=stmt
|
|
||||||
stmt.block=new
|
|
||||||
parser.AddBlock(stmt.block)
|
|
||||||
|
|
||||||
kwBreak
|
var/datum/node/statement/IfStatement/ElseIf/stmt = new
|
||||||
Parse(n_Parser/nS_Parser/parser)
|
parser.NextToken() //skip 'if' token
|
||||||
.=KW_PASS
|
stmt.cond = parser.ParseParenExpression()
|
||||||
if(istype(parser.curBlock, /node/BlockDefinition/GlobalBlock))
|
if(!parser.CheckToken(")", /datum/token/symbol))
|
||||||
parser.errors+=new/scriptError/BadToken(parser.curToken)
|
return KW_FAIL
|
||||||
. = KW_WARN
|
|
||||||
var/node/statement/BreakStatement/stmt=new
|
|
||||||
parser.NextToken() //skip 'break' token
|
|
||||||
parser.curBlock.statements+=stmt
|
|
||||||
|
|
||||||
kwContinue
|
if(!parser.CheckToken("{", /datum/token/symbol, skip = 0)) //Token needs to be preserved for parse loop, so skip=0
|
||||||
Parse(n_Parser/nS_Parser/parser)
|
return KW_ERR
|
||||||
.=KW_PASS
|
|
||||||
if(istype(parser.curBlock, /node/BlockDefinition/GlobalBlock))
|
|
||||||
parser.errors+=new/scriptError/BadToken(parser.curToken)
|
|
||||||
. = KW_WARN
|
|
||||||
var/node/statement/ContinueStatement/stmt=new
|
|
||||||
parser.NextToken() //skip 'break' token
|
|
||||||
parser.curBlock.statements+=stmt
|
|
||||||
|
|
||||||
kwDef
|
parser.curBlock.statements += stmt
|
||||||
Parse(n_Parser/nS_Parser/parser)
|
stmt.block = new
|
||||||
.=KW_PASS
|
ifstmt.else_if = stmt
|
||||||
var/node/statement/FunctionDefinition/def=new
|
parser.AddBlock(stmt.block)
|
||||||
parser.NextToken() //skip 'def' token
|
|
||||||
if(!parser.options.IsValidID(parser.curToken.value))
|
|
||||||
parser.errors+=new/scriptError/InvalidID(parser.curToken)
|
|
||||||
return KW_FAIL
|
|
||||||
def.func_name=parser.curToken.value
|
|
||||||
parser.NextToken()
|
|
||||||
if(!parser.CheckToken("(", /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, /token/symbol))
|
|
||||||
switch(parser.curToken.value)
|
|
||||||
if(",")
|
|
||||||
parser.NextToken()
|
|
||||||
if(")")
|
|
||||||
break
|
|
||||||
else
|
|
||||||
parser.errors+=new/scriptError/BadToken(parser.curToken)
|
|
||||||
return KW_ERR
|
|
||||||
|
|
||||||
else if(istype(parser.curToken, /token/word))
|
/datum/n_Keyword/nS_Keyword/kwElse/Parse(var/datum/n_Parser/nS_Parser/parser)
|
||||||
def.parameters+=parser.curToken.value
|
. = KW_PASS
|
||||||
parser.NextToken()
|
var/list/L = parser.curBlock.statements
|
||||||
else
|
var/datum/node/statement/IfStatement/stmt
|
||||||
parser.errors+=new/scriptError/InvalidID(parser.curToken)
|
|
||||||
return KW_ERR
|
|
||||||
if(!parser.CheckToken(")", /token/symbol))
|
|
||||||
return KW_FAIL
|
|
||||||
|
|
||||||
if(istype(parser.curToken, /token/end)) //Function prototype
|
if(L && L.len)
|
||||||
parser.curBlock.statements+=def
|
stmt = L[L.len] //Get the last statement in the current block
|
||||||
else if(parser.curToken.value=="{" && istype(parser.curToken, /token/symbol))
|
|
||||||
def.block = new
|
if(!stmt || !istype(stmt) || stmt.else_block) //Ensure that it is an if statement
|
||||||
parser.curBlock.statements+=def
|
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.curBlock.functions[def.func_name]=def
|
parser.errors += new/datum/scriptError/ExpectedToken("if statement", parser.curToken)
|
||||||
parser.AddBlock(def.block)
|
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
|
else
|
||||||
parser.errors+=new/scriptError/BadToken(parser.curToken)
|
parser.errors += new/datum/scriptError/BadToken(parser.curToken)
|
||||||
return KW_FAIL
|
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
|
||||||
|
|||||||
@@ -5,19 +5,17 @@
|
|||||||
Class: n_Parser
|
Class: n_Parser
|
||||||
An object that reads tokens and produces an AST (abstract syntax tree).
|
An object that reads tokens and produces an AST (abstract syntax tree).
|
||||||
*/
|
*/
|
||||||
/n_Parser
|
/datum/n_Parser
|
||||||
var
|
|
||||||
/*
|
/*
|
||||||
Var: index
|
Var: index
|
||||||
The parser's current position in the token's list.
|
The parser's current position in the token's list.
|
||||||
*/
|
*/
|
||||||
index = 1
|
var/index = 1
|
||||||
list
|
|
||||||
/*
|
/*
|
||||||
Var: tokens
|
Var: tokens
|
||||||
A list of tokens in the source code generated by a scanner.
|
A list of tokens in the source code generated by a scanner.
|
||||||
*/
|
*/
|
||||||
tokens = new
|
var/list/tokens = new
|
||||||
/*
|
/*
|
||||||
Var: errors
|
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.
|
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.
|
||||||
@@ -25,48 +23,44 @@
|
|||||||
See Also:
|
See Also:
|
||||||
- <scriptError>
|
- <scriptError>
|
||||||
*/
|
*/
|
||||||
errors = new
|
var/list/errors = new
|
||||||
/*
|
/*
|
||||||
Var: warnings
|
Var: warnings
|
||||||
A list of non-fatal problems in the script.
|
A list of non-fatal problems in the script.
|
||||||
*/
|
*/
|
||||||
warnings = new
|
var/list/warnings = new
|
||||||
token
|
|
||||||
/*
|
/*
|
||||||
Var: curToken
|
Var: curToken
|
||||||
The token at <index> in <tokens>.
|
The token at <index> in <tokens>.
|
||||||
*/
|
*/
|
||||||
curToken
|
var/datum/token/curToken
|
||||||
stack
|
var/datum/stack/blocks = new
|
||||||
blocks=new
|
var/datum/node/BlockDefinition/GlobalBlock/global_block = new
|
||||||
node/BlockDefinition
|
var/datum/node/BlockDefinition/GlobalBlock/curBlock
|
||||||
GlobalBlock/global_block=new
|
|
||||||
curBlock
|
|
||||||
|
|
||||||
proc
|
|
||||||
/*
|
/*
|
||||||
Proc: Parse
|
Proc: Parse
|
||||||
Reads the tokens and returns the AST's <GlobalBlock> node. Be sure to populate the tokens list before calling this procedure.
|
Reads the tokens and returns the AST's <GlobalBlock> node. Be sure to populate the tokens list before calling this procedure.
|
||||||
*/
|
*/
|
||||||
Parse()
|
/datum/n_Parser/proc/Parse()
|
||||||
|
return
|
||||||
/*
|
/*
|
||||||
Proc: NextToken
|
Proc: NextToken
|
||||||
Sets <curToken> to the next token in the <tokens> list, or null if there are no more tokens.
|
Sets <curToken> to the next token in the <tokens> list, or null if there are no more tokens.
|
||||||
*/
|
*/
|
||||||
NextToken()
|
/datum/n_Parser/proc/NextToken()
|
||||||
if(index>=tokens.len)
|
if(index >= tokens.len)
|
||||||
curToken=null
|
curToken = null
|
||||||
else
|
else
|
||||||
curToken=tokens[++index]
|
curToken = tokens[++index]
|
||||||
return curToken
|
return curToken
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: nS_Parser
|
Class: nS_Parser
|
||||||
An implmentation of a parser for n_Script.
|
An implmentation of a parser for n_Script.
|
||||||
*/
|
*/
|
||||||
/n_Parser/nS_Parser
|
/datum/n_Parser/nS_Parser
|
||||||
var/n_scriptOptions/nS_Options/options
|
var/datum/n_scriptOptions/nS_Options/options
|
||||||
/*
|
/*
|
||||||
Constructor: New
|
Constructor: New
|
||||||
|
|
||||||
@@ -74,117 +68,138 @@
|
|||||||
tokens - A list of tokens to parse.
|
tokens - A list of tokens to parse.
|
||||||
options - An object used for configuration.
|
options - An object used for configuration.
|
||||||
*/
|
*/
|
||||||
New(tokens[], n_scriptOptions/options)
|
/datum/n_Parser/nS_Parser/New(tokens[], var/datum/n_scriptOptions/options)
|
||||||
src.tokens=tokens
|
src.tokens = tokens
|
||||||
src.options=options
|
src.options = options
|
||||||
curBlock=global_block
|
curBlock = global_block
|
||||||
return ..()
|
return ..()
|
||||||
|
|
||||||
Parse()
|
/datum/n_Parser/nS_Parser/Parse()
|
||||||
ASSERT(tokens)
|
ASSERT(tokens)
|
||||||
for(,src.index<=src.tokens.len, src.index++)
|
for(,src.index <= src.tokens.len, src.index++)
|
||||||
curToken=tokens[index]
|
curToken = tokens[index]
|
||||||
switch(curToken.type)
|
switch(curToken.type)
|
||||||
if(/token/keyword)
|
if(/datum/token/keyword)
|
||||||
var/n_Keyword/kw=options.keywords[curToken.value]
|
var/datum/n_Keyword/kw = options.keywords[curToken.value]
|
||||||
kw=new kw()
|
kw = new kw()
|
||||||
if(kw)
|
if(kw)
|
||||||
if(!kw.Parse(src))
|
if(!kw.Parse(src))
|
||||||
return
|
return
|
||||||
if(/token/word)
|
|
||||||
var/token/ntok
|
if(/datum/token/word)
|
||||||
if(index+1>tokens.len)
|
var/datum/token/ntok
|
||||||
errors+=new/scriptError/BadToken(curToken)
|
if(index + 1 > tokens.len)
|
||||||
continue
|
errors += new/datum/scriptError/BadToken(curToken)
|
||||||
ntok=tokens[index+1]
|
|
||||||
if(!istype(ntok, /token/symbol))
|
|
||||||
errors+=new/scriptError/BadToken(ntok)
|
|
||||||
continue
|
|
||||||
if(ntok.value=="(")
|
|
||||||
ParseFunctionStatement()
|
|
||||||
else if(options.assign_operators.Find(ntok.value))
|
|
||||||
ParseAssignment()
|
|
||||||
else
|
|
||||||
errors+=new/scriptError/BadToken(ntok)
|
|
||||||
continue
|
|
||||||
if(!istype(curToken, /token/end))
|
|
||||||
errors+=new/scriptError/ExpectedToken(";", curToken)
|
|
||||||
continue
|
|
||||||
if(/token/symbol)
|
|
||||||
if(curToken.value=="}")
|
|
||||||
if(!EndBlock())
|
|
||||||
errors+=new/scriptError/BadToken(curToken)
|
|
||||||
continue
|
|
||||||
else
|
|
||||||
errors+=new/scriptError/BadToken(curToken)
|
|
||||||
continue
|
|
||||||
if(/token/end)
|
|
||||||
warnings+=new/scriptError/BadToken(curToken)
|
|
||||||
continue
|
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
|
else
|
||||||
errors+=new/scriptError/BadToken(curToken)
|
errors += new/datum/scriptError/BadToken(ntok)
|
||||||
return
|
continue
|
||||||
return global_block
|
|
||||||
|
|
||||||
proc
|
if(!istype(curToken, /datum/token/end))
|
||||||
CheckToken(val, type, err=1, skip=1)
|
errors += new/datum/scriptError/ExpectedToken(";", curToken)
|
||||||
if(curToken.value!=val || !istype(curToken,type))
|
continue
|
||||||
if(err)
|
|
||||||
errors+=new/scriptError/ExpectedToken(val, curToken)
|
|
||||||
return 0
|
|
||||||
if(skip)NextToken()
|
|
||||||
return 1
|
|
||||||
|
|
||||||
AddBlock(node/BlockDefinition/B)
|
if(/datum/token/symbol)
|
||||||
blocks.Push(curBlock)
|
if(curToken.value == "}")
|
||||||
curBlock=B
|
if(!EndBlock())
|
||||||
|
errors += new/datum/scriptError/BadToken(curToken)
|
||||||
|
continue
|
||||||
|
|
||||||
EndBlock()
|
else
|
||||||
if(curBlock==global_block) return 0
|
errors += new/datum/scriptError/BadToken(curToken)
|
||||||
curBlock=blocks.Pop()
|
continue
|
||||||
return 1
|
|
||||||
|
if(/datum/token/end)
|
||||||
|
warnings += new/datum/scriptError/BadToken(curToken)
|
||||||
|
continue
|
||||||
|
|
||||||
ParseAssignment()
|
|
||||||
var/name=curToken.value
|
|
||||||
if(!options.IsValidID(name))
|
|
||||||
errors+=new/scriptError/InvalidID(curToken)
|
|
||||||
return
|
|
||||||
NextToken()
|
|
||||||
var/t=options.binary_operators[options.assign_operators[curToken.value]]
|
|
||||||
var/node/statement/VariableAssignment/stmt=new()
|
|
||||||
stmt.var_name=new(name)
|
|
||||||
NextToken()
|
|
||||||
if(t)
|
|
||||||
stmt.value=new t()
|
|
||||||
stmt.value:exp=new/node/expression/value/variable(stmt.var_name)
|
|
||||||
stmt.value:exp2=ParseExpression()
|
|
||||||
else
|
else
|
||||||
stmt.value=ParseExpression()
|
errors += new/datum/scriptError/BadToken(curToken)
|
||||||
curBlock.statements+=stmt
|
|
||||||
|
|
||||||
ParseFunctionStatement()
|
|
||||||
if(!istype(curToken, /token/word))
|
|
||||||
errors+=new/scriptError("Bad identifier in function call.")
|
|
||||||
return
|
return
|
||||||
var/node/statement/FunctionCall/stmt=new
|
|
||||||
stmt.func_name=curToken.value
|
|
||||||
NextToken() //skip function name
|
|
||||||
if(!CheckToken("(", /token/symbol)) //Check for and skip open parenthesis
|
|
||||||
return
|
|
||||||
var/loops = 0
|
|
||||||
for()
|
|
||||||
loops++
|
|
||||||
if(loops>=800)
|
|
||||||
errors +=new/scriptError("Cannot find ending params.")
|
|
||||||
return
|
|
||||||
|
|
||||||
if(!curToken)
|
return global_block
|
||||||
errors+=new/scriptError/EndOfFile()
|
|
||||||
return
|
/datum/n_Parser/nS_Parser/proc/CheckToken(val, type, err = 1, skip = 1)
|
||||||
if(istype(curToken, /token/symbol) && curToken.value==")")
|
if(curToken.value != val || !istype(curToken, type))
|
||||||
curBlock.statements+=stmt
|
if(err)
|
||||||
NextToken() //Skip close parenthesis
|
errors += new/datum/scriptError/ExpectedToken(val, curToken)
|
||||||
return
|
return 0
|
||||||
var/node/expression/P=ParseParamExpression(check_functions = 1)
|
if(skip)
|
||||||
stmt.parameters+=P
|
NextToken()
|
||||||
if(istype(curToken, /token/symbol) && curToken.value==",") 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()
|
||||||
|
|||||||
@@ -5,10 +5,8 @@
|
|||||||
Class: n_Scanner
|
Class: n_Scanner
|
||||||
An object responsible for breaking up source code into tokens for use by the parser.
|
An object responsible for breaking up source code into tokens for use by the parser.
|
||||||
*/
|
*/
|
||||||
/n_Scanner
|
/datum/n_Scanner
|
||||||
var
|
var/code
|
||||||
code
|
|
||||||
list
|
|
||||||
/*
|
/*
|
||||||
Var: errors
|
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.
|
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.
|
||||||
@@ -16,54 +14,50 @@
|
|||||||
See Also:
|
See Also:
|
||||||
- <scriptError>
|
- <scriptError>
|
||||||
*/
|
*/
|
||||||
errors = new
|
var/list/errors = new
|
||||||
/*
|
/*
|
||||||
Var: warnings
|
Var: warnings
|
||||||
A list of non-fatal problems in the source code found by the scanner.
|
A list of non-fatal problems in the source code found by the scanner.
|
||||||
*/
|
*/
|
||||||
warnings = new
|
var/list/warnings = new
|
||||||
|
|
||||||
proc
|
|
||||||
/*
|
/*
|
||||||
Proc: LoadCode
|
Proc: LoadCode
|
||||||
Loads source code.
|
Loads source code.
|
||||||
*/
|
*/
|
||||||
LoadCode(c)
|
/datum/n_Scanner/proc/LoadCode(var/c)
|
||||||
code=c
|
code=c
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: LoadCodeFromFile
|
Proc: LoadCodeFromFile
|
||||||
Gets the code from a file and calls <LoadCode()>.
|
Gets the code from a file and calls <LoadCode()>.
|
||||||
*/
|
*/
|
||||||
LoadCodeFromFile(f)
|
/datum/n_Scanner/proc/LoadCodeFromFile(var/f)
|
||||||
LoadCode(file2text(f))
|
LoadCode(file2text(f))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: Scan
|
Proc: Scan
|
||||||
Runs the scanner and returns the resulting list of tokens. Ensure that <LoadCode()> has been called first.
|
Runs the scanner and returns the resulting list of tokens. Ensure that <LoadCode()> has been called first.
|
||||||
*/
|
*/
|
||||||
Scan()
|
/datum/n_Scanner/proc/Scan()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: nS_Scanner
|
Class: nS_Scanner
|
||||||
A scanner implementation for n_Script.
|
A scanner implementation for n_Script.
|
||||||
*/
|
*/
|
||||||
/n_Scanner/nS_Scanner
|
/datum/n_Scanner/nS_Scanner
|
||||||
|
|
||||||
var
|
|
||||||
/*
|
/*
|
||||||
Variable: codepos
|
Variable: codepos
|
||||||
The scanner's position in the source code.
|
The scanner's position in the source code.
|
||||||
*/
|
*/
|
||||||
codepos = 1
|
var/codepos = 1
|
||||||
line = 1
|
var/line = 1
|
||||||
linepos = 0 //column=codepos-linepos
|
var/linepos = 0 //column=codepos-linepos
|
||||||
n_scriptOptions/nS_Options/options
|
var/datum/n_scriptOptions/nS_Options/options
|
||||||
|
|
||||||
commenting = 0
|
var/commenting = 0
|
||||||
// 1: single-line
|
// 1: single-line
|
||||||
// 2: multi-line
|
// 2: multi-line
|
||||||
list
|
|
||||||
/*
|
/*
|
||||||
Variable: ignore
|
Variable: ignore
|
||||||
A list of characters that are ignored by the scanner.
|
A list of characters that are ignored by the scanner.
|
||||||
@@ -71,7 +65,7 @@
|
|||||||
Default Value:
|
Default Value:
|
||||||
Whitespace
|
Whitespace
|
||||||
*/
|
*/
|
||||||
ignore = list(" ", "\t", "\n") //Don't add tokens for whitespace
|
var/list/ignore = list(" ", "\t", "\n") //Don't add tokens for whitespace
|
||||||
/*
|
/*
|
||||||
Variable: end_stmt
|
Variable: end_stmt
|
||||||
A list of characters that end a statement. Each item may only be one character long.
|
A list of characters that end a statement. Each item may only be one character long.
|
||||||
@@ -79,7 +73,7 @@
|
|||||||
Default Value:
|
Default Value:
|
||||||
Semicolon
|
Semicolon
|
||||||
*/
|
*/
|
||||||
end_stmt = list(";")
|
var/list/end_stmt = list(";")
|
||||||
/*
|
/*
|
||||||
Variable: string_delim
|
Variable: string_delim
|
||||||
A list of characters that can start and end strings.
|
A list of characters that can start and end strings.
|
||||||
@@ -87,12 +81,12 @@
|
|||||||
Default Value:
|
Default Value:
|
||||||
Double and single quotes.
|
Double and single quotes.
|
||||||
*/
|
*/
|
||||||
string_delim = list("\"", "'")
|
var/list/string_delim = list("\"", "'")
|
||||||
/*
|
/*
|
||||||
Variable: delim
|
Variable: delim
|
||||||
A list of characters that denote the start of a new token. This list is automatically populated.
|
A list of characters that denote the start of a new token. This list is automatically populated.
|
||||||
*/
|
*/
|
||||||
delim = new
|
var/list/delim = new
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Macro: COL
|
Macro: COL
|
||||||
@@ -106,45 +100,52 @@
|
|||||||
code - The source code to tokenize.
|
code - The source code to tokenize.
|
||||||
options - An <nS_Options> object used to configure the scanner.
|
options - An <nS_Options> object used to configure the scanner.
|
||||||
*/
|
*/
|
||||||
New(code, n_scriptOptions/nS_Options/options)
|
/datum/n_Scanner/nS_Scanner/New(var/code, var/datum/n_scriptOptions/nS_Options/options)
|
||||||
.=..()
|
. = ..()
|
||||||
ignore+= ascii2text(13) //Carriage return
|
ignore += ascii2text(13) //Carriage return
|
||||||
delim += ignore + options.symbols + end_stmt + string_delim
|
delim += ignore + options.symbols + end_stmt + string_delim
|
||||||
src.options=options
|
src.options = options
|
||||||
LoadCode(code)
|
LoadCode(code)
|
||||||
|
|
||||||
Scan() //Creates a list of tokens from source code
|
/datum/n_Scanner/nS_Scanner/Scan() //Creates a list of tokens from source code
|
||||||
var/list/tokens=new
|
var/list/tokens = new
|
||||||
for(, src.codepos<=length(code), src.codepos++)
|
for(, src.codepos <= length(code), src.codepos++)
|
||||||
|
|
||||||
var/char=copytext(code, codepos, codepos+1)
|
var/char = copytext(code, codepos, codepos + 1)
|
||||||
if(char=="\n")
|
var/nextchar = copytext(code, codepos + 1, codepos + 2)
|
||||||
line++
|
if(char == "\n")
|
||||||
linepos=codepos
|
line++
|
||||||
|
linepos = codepos
|
||||||
|
|
||||||
if(ignore.Find(char))
|
if(ignore.Find(char))
|
||||||
continue
|
continue
|
||||||
else if(char == "/")
|
|
||||||
ReadComment()
|
else if(char == "/" && (nextchar == "*" || nextchar == "/"))
|
||||||
else if(end_stmt.Find(char))
|
ReadComment()
|
||||||
tokens+=new /token/end(char, line, COL)
|
|
||||||
else if(string_delim.Find(char))
|
else if(end_stmt.Find(char))
|
||||||
codepos++ //skip string delimiter
|
tokens += new/datum/token/end(char, line, COL)
|
||||||
tokens+=ReadString(char)
|
|
||||||
else if(options.CanStartID(char))
|
else if(string_delim.Find(char))
|
||||||
tokens+=ReadWord()
|
codepos++ //skip string delimiter
|
||||||
else if(options.IsDigit(char))
|
tokens += ReadString(char)
|
||||||
tokens+=ReadNumber()
|
|
||||||
else if(options.symbols.Find(char))
|
else if(options.CanStartID(char))
|
||||||
tokens+=ReadSymbol()
|
tokens += ReadWord()
|
||||||
|
|
||||||
|
else if(options.IsDigit(char))
|
||||||
|
tokens += ReadNumber()
|
||||||
|
|
||||||
|
else if(options.symbols.Find(char))
|
||||||
|
tokens += ReadSymbol()
|
||||||
|
|
||||||
|
|
||||||
codepos=initial(codepos)
|
codepos = initial(codepos)
|
||||||
line=initial(line)
|
line = initial(line)
|
||||||
linepos=initial(linepos)
|
linepos = initial(linepos)
|
||||||
return tokens
|
return tokens
|
||||||
|
|
||||||
|
|
||||||
proc
|
|
||||||
/*
|
/*
|
||||||
Proc: ReadString
|
Proc: ReadString
|
||||||
Reads a string in the source code into a token.
|
Reads a string in the source code into a token.
|
||||||
@@ -152,136 +153,139 @@
|
|||||||
Parameters:
|
Parameters:
|
||||||
start - The character used to start the string.
|
start - The character used to start the string.
|
||||||
*/
|
*/
|
||||||
ReadString(start)
|
/datum/n_Scanner/nS_Scanner/proc/ReadString(start)
|
||||||
var
|
var/buf
|
||||||
buf
|
for(, codepos <= length(code), codepos++)//codepos to length(code))
|
||||||
for(, codepos <= length(code), codepos++)//codepos to length(code))
|
var/char = copytext(code, codepos, codepos + 1)
|
||||||
var/char=copytext(code, codepos, codepos+1)
|
switch(char)
|
||||||
|
if("\\") //Backslash (\) encountered in string
|
||||||
|
codepos++ //Skip next character in string, since it was escaped by a backslash
|
||||||
|
char = copytext(code, codepos, codepos+1)
|
||||||
switch(char)
|
switch(char)
|
||||||
if("\\") //Backslash (\) encountered in string
|
if("\\") //Double backslash
|
||||||
codepos++ //Skip next character in string, since it was escaped by a backslash
|
buf += "\\"
|
||||||
char=copytext(code, codepos, codepos+1)
|
if("n") //\n Newline
|
||||||
switch(char)
|
buf += "\n"
|
||||||
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/token/string(buf, line, COL)
|
|
||||||
errors+=new/scriptError("Unterminated string. Newline reached.", .)
|
|
||||||
line++
|
|
||||||
linepos=codepos
|
|
||||||
break
|
|
||||||
else
|
else
|
||||||
if(char==start) //string delimiter found, end string
|
if(char == start) //\" Doublequote
|
||||||
break
|
buf += start
|
||||||
else
|
else //Unknown escaped text
|
||||||
buf+=char //Just a normal character in a string
|
buf += char
|
||||||
if(!.) return new/token/string(buf, line, COL)
|
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
|
Proc: ReadWord
|
||||||
Reads characters separated by an item in <delim> into a token.
|
Reads characters separated by an item in <delim> into a token.
|
||||||
*/
|
*/
|
||||||
ReadWord()
|
/datum/n_Scanner/nS_Scanner/proc/ReadWord()
|
||||||
var
|
var/char = copytext(code, codepos, codepos + 1)
|
||||||
char=copytext(code, codepos, codepos+1)
|
var/buf
|
||||||
buf
|
|
||||||
while(!delim.Find(char) && codepos<=length(code))
|
while(!delim.Find(char) && codepos <= length(code))
|
||||||
buf+=char
|
buf += char
|
||||||
char=copytext(code, ++codepos, codepos+1)
|
char = copytext(code, ++codepos, codepos + 1)
|
||||||
codepos-- //allow main Scan() proc to read the delimiter
|
codepos-- //allow main Scan() proc to read the delimiter
|
||||||
if(options.keywords.Find(buf))
|
if(options.keywords.Find(buf))
|
||||||
return new /token/keyword(buf, line, COL)
|
return new/datum/token/keyword(buf, line, COL)
|
||||||
else
|
else
|
||||||
return new /token/word(buf, line, COL)
|
return new/datum/token/word(buf, line, COL)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: ReadSymbol
|
Proc: ReadSymbol
|
||||||
Reads a symbol into a token.
|
Reads a symbol into a token.
|
||||||
*/
|
*/
|
||||||
ReadSymbol()
|
/datum/n_Scanner/nS_Scanner/proc/ReadSymbol()
|
||||||
var
|
var/char=copytext(code, codepos, codepos + 1)
|
||||||
char=copytext(code, codepos, codepos+1)
|
var/buf
|
||||||
buf
|
|
||||||
|
|
||||||
while(options.symbols.Find(buf+char))
|
while(options.symbols.Find(buf + char))
|
||||||
buf+=char
|
buf += char
|
||||||
if(++codepos>length(code)) break
|
if(++codepos > length(code)) break
|
||||||
char=copytext(code, codepos, codepos+1)
|
char = copytext(code, codepos, codepos + 1)
|
||||||
|
|
||||||
codepos-- //allow main Scan() proc to read the next character
|
codepos-- //allow main Scan() proc to read the next character
|
||||||
return new /token/symbol(buf, line, COL)
|
return new /datum/token/symbol(buf, line, COL)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: ReadNumber
|
Proc: ReadNumber
|
||||||
Reads a number into a token.
|
Reads a number into a token.
|
||||||
*/
|
*/
|
||||||
ReadNumber()
|
/datum/n_Scanner/nS_Scanner/proc/ReadNumber()
|
||||||
var
|
var/char = copytext(code, codepos, codepos + 1)
|
||||||
char=copytext(code, codepos, codepos+1)
|
var/buf
|
||||||
buf
|
var/dec = 0
|
||||||
dec=0
|
|
||||||
|
|
||||||
while(options.IsDigit(char) || (char=="." && !dec))
|
while(options.IsDigit(char) || (char == "." && !dec))
|
||||||
if(char==".") dec=1
|
if(char == ".")
|
||||||
buf+=char
|
dec = 1
|
||||||
codepos++
|
|
||||||
char=copytext(code, codepos, codepos+1)
|
buf += char
|
||||||
var/token/number/T=new(buf, line, COL)
|
codepos++
|
||||||
if(isnull(text2num(buf)))
|
char = copytext(code, codepos, codepos + 1)
|
||||||
errors+=new/scriptError("Bad number: ", T)
|
|
||||||
T.value=0
|
var/datum/token/number/T = new(buf, line, COL)
|
||||||
codepos-- //allow main Scan() proc to read the next character
|
if(isnull(text2num(buf)))
|
||||||
return T
|
errors += new/datum/scriptError("Bad number: ", T)
|
||||||
|
T.value = 0
|
||||||
|
|
||||||
|
codepos-- //allow main Scan() proc to read the next character
|
||||||
|
return T
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proc: ReadComment
|
Proc: ReadComment
|
||||||
Reads a comment and outputs the type of comment
|
Reads a comment and outputs the type of comment
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ReadComment()
|
/datum/n_Scanner/nS_Scanner/proc/ReadComment()
|
||||||
var
|
var/char = copytext(code, codepos, codepos + 1)
|
||||||
char=copytext(code, codepos, codepos+1)
|
var/nextchar = copytext(code, codepos + 1, codepos + 2)
|
||||||
nextchar=copytext(code, codepos+1, codepos+2)
|
var/charstring = char + nextchar
|
||||||
charstring = char+nextchar
|
var/comm = 1
|
||||||
comm = 1
|
// 1: single-line comment
|
||||||
// 1: single-line comment
|
// 2: multi-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 > length(code))
|
||||||
|
break
|
||||||
|
|
||||||
|
if(expectedend) // ending statement expected...
|
||||||
|
char = copytext(code, codepos, codepos + 1)
|
||||||
|
if(char == "/") // ending statement found - beak the comment
|
||||||
|
comm = 0
|
||||||
|
break
|
||||||
|
|
||||||
|
if(comm == 2)
|
||||||
|
// multi-line comments are broken by ending statements
|
||||||
|
char = copytext(code, codepos, codepos + 1)
|
||||||
|
if(char == "*")
|
||||||
|
expectedend = 1
|
||||||
|
continue
|
||||||
|
else
|
||||||
|
char = copytext(code, codepos, codepos + 1)
|
||||||
|
if(char == "\n")
|
||||||
|
comm = 0
|
||||||
|
break
|
||||||
|
|
||||||
|
if(expectedend)
|
||||||
expectedend = 0
|
expectedend = 0
|
||||||
|
|
||||||
if(charstring == "//" || charstring == "/*")
|
if(comm == 2)
|
||||||
if(charstring == "/*")
|
errors += new/datum/scriptError/UnterminatedComment()
|
||||||
comm = 2 // starts a multi-line comment
|
|
||||||
|
|
||||||
while(comm)
|
|
||||||
if(++codepos>length(code)) break
|
|
||||||
|
|
||||||
if(expectedend) // ending statement expected...
|
|
||||||
char = copytext(code, codepos, codepos+1)
|
|
||||||
if(char == "/") // ending statement found - beak the comment
|
|
||||||
comm = 0
|
|
||||||
break
|
|
||||||
|
|
||||||
if(comm == 2)
|
|
||||||
// multi-line comments are broken by ending statements
|
|
||||||
char = copytext(code, codepos, codepos+1)
|
|
||||||
if(char == "*")
|
|
||||||
expectedend = 1
|
|
||||||
continue
|
|
||||||
else
|
|
||||||
char = copytext(code, codepos, codepos+1)
|
|
||||||
if(char == "\n")
|
|
||||||
comm = 0
|
|
||||||
break
|
|
||||||
|
|
||||||
if(expectedend) expectedend = 0
|
|
||||||
|
|
||||||
if(comm == 2)
|
|
||||||
errors+=new/scriptError/UnterminatedComment()
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,40 @@
|
|||||||
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Class: Token
|
Class: Token
|
||||||
Represents an entity and position in the source code.
|
Represents an entity and position in the source code.
|
||||||
*/
|
*/
|
||||||
/token
|
/datum/token
|
||||||
var/value
|
var/value
|
||||||
var/line
|
var/line
|
||||||
var/column
|
var/column
|
||||||
|
|
||||||
New(v, l=0, c=0)
|
/datum/token/New(v, l = 0, c = 0)
|
||||||
value=v
|
value = v
|
||||||
line=l
|
line = l
|
||||||
column=c
|
column = c
|
||||||
|
|
||||||
string
|
/datum/token/string
|
||||||
symbol
|
|
||||||
word
|
|
||||||
keyword
|
|
||||||
number
|
|
||||||
New()
|
|
||||||
.=..()
|
|
||||||
if(!isnum(value))
|
|
||||||
value=text2num(value)
|
|
||||||
ASSERT(!isnull(value))
|
|
||||||
accessor
|
|
||||||
var/object
|
|
||||||
var/member
|
|
||||||
|
|
||||||
New(object, member, l=0, c=0)
|
/datum/token/symbol
|
||||||
src.object=object
|
|
||||||
src.member=member
|
|
||||||
src.value="[object].[member]" //for debugging only
|
|
||||||
src.line=l
|
|
||||||
src.column=c
|
|
||||||
|
|
||||||
end
|
/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,23 +1,26 @@
|
|||||||
/stack
|
/datum/stack
|
||||||
var/list
|
var/list/contents = list()
|
||||||
contents=new
|
|
||||||
proc
|
|
||||||
Push(value)
|
|
||||||
contents+=value
|
|
||||||
|
|
||||||
Pop()
|
/datum/stack/proc/Push(value)
|
||||||
if(!contents.len) return null
|
contents += value
|
||||||
. = contents[contents.len]
|
|
||||||
contents.len--
|
|
||||||
|
|
||||||
Top() //returns the item on the top of the stack without removing it
|
/datum/stack/proc/Pop()
|
||||||
if(!contents.len) return null
|
if(!contents.len)
|
||||||
return contents[contents.len]
|
return null
|
||||||
|
|
||||||
Copy()
|
. = contents[contents.len]
|
||||||
var/stack/S=new()
|
contents.len--
|
||||||
S.contents=src.contents.Copy()
|
|
||||||
return S
|
|
||||||
|
|
||||||
Clear()
|
/datum/stack/proc/Top() //returns the item on the top of the stack without removing it
|
||||||
contents.len = 0
|
if(!contents.len)
|
||||||
|
return null
|
||||||
|
|
||||||
|
return contents[contents.len]
|
||||||
|
|
||||||
|
/datum/stack/proc/Copy()
|
||||||
|
var/datum/stack/S = new()
|
||||||
|
S.contents = src.contents.Copy()
|
||||||
|
return S
|
||||||
|
|
||||||
|
/datum/stack/proc/Clear()
|
||||||
|
contents.Cut()
|
||||||
Reference in New Issue
Block a user