NTSL code cleanup.

This commit is contained in:
PJB3005
2015-10-11 22:13:17 +02:00
parent 8a541b54f8
commit 1446346171
21 changed files with 2506 additions and 2393 deletions

View File

@@ -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

View File

@@ -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])"

View File

@@ -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

View File

@@ -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

View File

@@ -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 ..()

View File

@@ -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

View File

@@ -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"

View File

@@ -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))

View File

@@ -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)

View File

@@ -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))

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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()
.=..() . = ..()

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View File

@@ -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()