Telecomms Refactor & CodeMirror

This commit does the following:
 - A lot of shit I am really too tired to fucking write about
 - Absolute pathed telecomms scripting
 - Browser Datum traffic control
  - Absolutely lovely replacement for the fucking skin TCS window, using
    codemirror
 - CodeMirror integration for nanoUI
  - Sorta, I didn't work on this as much as I wanted to, because IT TOOK
    11 FUCKING HOURS TO GET THE BROWSER DATUM TO WORK
This commit is contained in:
Tigercat2000
2015-10-28 17:19:18 -07:00
parent f76109ca5c
commit c244a0fe15
38 changed files with 13973 additions and 2155 deletions

View File

@@ -1,169 +1,203 @@
/proc/isobject(x)
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
var/datum/D
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
RaiseError(new/datum/runtimeError/UnknownInstruction())
EvalOperator(node/expression/operator/exp)
if(istype(exp, /node/expression/operator/binary))
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())
catch
RaiseError(new/datum/runtimeError/TypeMismatch(bin.token, Eval(bin.exp), Eval(bin.exp2)))
else
try
switch(exp.type)
if(/datum/node/expression/operator/unary/Minus)
return Minus(Eval(exp.exp))
//Binary//
//Comparison operators
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 || b==null)
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
if(/datum/node/expression/operator/unary/LogicalNot)
return LogicalNot(Eval(exp.exp))
//Unary//
Minus(a) return -a
LogicalNot(a) return !a
BitwiseNot(a) return ~a
if(/datum/node/expression/operator/unary/BitwiseNot)
return BitwiseNot(Eval(exp.exp))
if(/datum/node/expression/operator/unary/group)
return Eval(exp.exp)
else
RaiseError(new/datum/runtimeError/UnknownInstruction())
catch
RaiseError(new/datum/runtimeError/TypeMismatch/unary(exp.token, Eval(exp.exp)))
//Binary//
//Comparison operators
/datum/n_Interpreter/proc/Equal(a, b)
return a == b
/datum/n_Interpreter/proc/NotEqual(a, b)
return a != b //LogicalNot(Equal(a, b))
/datum/n_Interpreter/proc/Greater(a, b)
return a > b
/datum/n_Interpreter/proc/Less(a, b)
return a < b
/datum/n_Interpreter/proc/GreaterOrEqual(a, b)
return a >= b
/datum/n_Interpreter/proc/LessOrEqual(a, b)
return a <= b
//Logical Operators
/datum/n_Interpreter/proc/LogicalAnd(a, b)
return a && b
/datum/n_Interpreter/proc/LogicalOr(a, b)
return a || b
/datum/n_Interpreter/proc/LogicalXor(a, b)
return (a || b) && !(a && b)
//Bitwise Operators
/datum/n_Interpreter/proc/BitwiseAnd(a, b)
return a & b
/datum/n_Interpreter/proc/BitwiseOr(a, b)
return a | b
/datum/n_Interpreter/proc/BitwiseXor(a, b)
return a ^ b
//Arithmetic Operators
/datum/n_Interpreter/proc/Add(a, b)
return a + b
/datum/n_Interpreter/proc/Subtract(a, b)
return a - b
/datum/n_Interpreter/proc/Divide(a, b)
if(b == 0)
RaiseError(new/datum/runtimeError/DivisionByZero())
return null
return a / b
/datum/n_Interpreter/proc/Multiply(a, b)
return a * b
/datum/n_Interpreter/proc/Modulo(a, b)
return a % b
/datum/n_Interpreter/proc/Power(a, b)
return a ** b
//Unary//
/datum/n_Interpreter/proc/Minus(a)
return -a
/datum/n_Interpreter/proc/LogicalNot(a)
return !a
/datum/n_Interpreter/proc/BitwiseNot(a)
return ~a

View File

@@ -7,9 +7,6 @@
Procedures allowing for interaction with the script that is being run by the interpreter object.
*/
/n_Interpreter
proc
/*
Proc: Load
Loads a 'compiled' script into memory.
@@ -17,126 +14,144 @@
Parameters:
program - A <GlobalBlock> object which represents the script's global scope.
*/
Load(node/BlockDefinition/GlobalBlock/program)
ASSERT(program)
src.program = program
CreateGlobalScope()
/datum/n_Interpreter/proc/Load(var/datum/node/BlockDefinition/GlobalBlock/program)
ASSERT(program)
src.program = program
CreateGlobalScope()
alertadmins = 0 // reset admin alerts
/*
Proc: Run
Runs the script.
Proc: Run
Runs the script.
*/
Run()
cur_recursion = 0 // reset recursion
cur_statements = 0 // reset CPU tracking
alertadmins = 0
/datum/n_Interpreter/proc/Run()
cur_recursion = 0 // reset recursion
cur_statements = 0 // reset CPU tracking
ASSERT(src.program)
RunBlock(src.program)
ASSERT(src.program)
RunBlock(src.program)
/*
Proc: SetVar
Defines a global variable for the duration of the next execution of a script.
Proc: SetVar
Defines a global variable for the duration of the next execution of a script.
Notes:
This differs from <Block.SetVar()> in that variables set using this procedure only last for the session,
while those defined from the block object persist if it is ran multiple times.
Notes:
This differs from <Block.SetVar()> in that variables set using this procedure only last for the session,
while those defined from the block object persist if it is ran multiple times.
See Also:
- <Block.SetVar()>
See Also:
- <Block.SetVar()>
*/
SetVar(name, value)
if(!istext(name))
//CRASH("Invalid variable name")
return
AssignVariable(name, value)
/datum/n_Interpreter/proc/SetVar(name, value)
if(!istext(name))
//CRASH("Invalid variable name")
return
AssignVariable(name, value)
/*
Proc: SetProc
Defines a procedure to be available to the script.
Proc: SetProc
Defines a procedure to be available to the script.
Parameters:
name - The name of the procedure as exposed to the script.
path - The typepath of a proc to be called when the function call is read by the interpreter, or, if object is specified, a string representing the procedure's name.
object - (Optional) An object which will the be target of a function call.
params - Only required if object is not null, a list of the names of parameters the proc takes.
Parameters:
name - The name of the procedure as exposed to the script.
path - The typepath of a proc to be called when the function call is read by the interpreter, or, if object is specified, a string representing the procedure's name.
object - (Optional) An object which will the be target of a function call.
params - Only required if object is not null, a list of the names of parameters the proc takes.
*/
SetProc(name, path, object=null, list/params=null)
if(!istext(name))
//CRASH("Invalid function name")
return
if(!object)
globalScope.functions[name] = path
else
var/node/statement/FunctionDefinition/S = new()
S.func_name = name
S.parameters = params
S.block = new()
S.block.SetVar("src", object)
var/node/expression/FunctionCall/C = new()
C.func_name = path
C.object = new("src")
for(var/p in params)
C.parameters += new/node/expression/value/variable(p)
var/node/statement/ReturnStatement/R=new()
R.value=C
S.block.statements += R
globalScope.functions[name] = S
/datum/n_Interpreter/proc/SetProc(name, path, object = null, list/params = null)
if(!istext(name))
//CRASH("Invalid function name")
return
if(!object)
globalScope.functions[name] = path
else
var/datum/node/statement/FunctionDefinition/S = new()
S.func_name = name
S.parameters = params
S.block = new()
S.block.SetVar("src", object)
var/datum/node/expression/FunctionCall/C = new()
C.func_name = path
C.object = new("src")
for(var/p in params)
C.parameters += new/datum/node/expression/value/variable(p)
var/datum/node/statement/ReturnStatement/R = new()
R.value = C
S.block.statements += R
globalScope.functions[name] = S
/*
Proc: VarExists
Checks whether a global variable with the specified name exists.
Proc: VarExists
Checks whether a global variable with the specified name exists.
*/
VarExists(name)
return globalScope.variables.Find(name) //convert to 1/0 first?
/datum/n_Interpreter/proc/VarExists(name)
return globalScope.variables.Find(name) //convert to 1/0 first?
/*
Proc: ProcExists
Checks whether a global function with the specified name exists.
Proc: ProcExists
Checks whether a global function with the specified name exists.
*/
ProcExists(name)
return globalScope.functions.Find(name)
/datum/n_Interpreter/proc/ProcExists(name)
return globalScope.functions.Find(name)
/*
Proc: GetVar
Returns the value of a global variable in the script. Remember to ensure that the variable exists before calling this procedure.
Proc: GetVar
Returns the value of a global variable in the script. Remember to ensure that the variable exists before calling this procedure.
See Also:
- <VarExists()>
See Also:
- <VarExists()>
*/
GetVar(name)
if(!VarExists(name))
//CRASH("No variable named '[name]'.")
return
var/x = globalScope.variables[name]
return Eval(x)
/datum/n_Interpreter/proc/GetVar(name)
if(!VarExists(name))
//CRASH("No variable named '[name]'.")
return
var/x = globalScope.variables[name]
return Eval(x)
/*
Proc: CallProc
Calls a global function defined in the script and, amazingly enough, returns its return value. Remember to ensure that the function
exists before calling this procedure.
See Also:
- <ProcExists()>
Proc: GetCleanVar
Returns the value of a global variable in the script and cleans it (sanitizes).
*/
CallProc(name, params[]=null)
if(!ProcExists(name))
//CRASH("No function named '[name]'.")
return
var/node/statement/FunctionDefinition/func = globalScope.functions[name]
if(istype(func))
var/node/statement/FunctionCall/stmt = new
stmt.func_name = func.func_name
stmt.parameters = params
return RunFunction(stmt)
else
return call(func)(arglist(params))
//CRASH("Unknown function type '[name]'.")
/datum/n_Interpreter/proc/GetCleanVar(name, compare)
var/x = GetVar(name)
if(istext(x) && compare && x != compare) // Was changed
x = sanitize(x)
return x
/*
Event: HandleError
Called when the interpreter throws a runtime error.
Proc: CallProc
Calls a global function defined in the script and, amazingly enough, returns its return value. Remember to ensure that the function
exists before calling this procedure.
See Also:
- <runtimeError>
See Also:
- <ProcExists()>
*/
HandleError(runtimeError/e)
/datum/n_Interpreter/proc/CallProc(name, params[]=null)
if(!ProcExists(name))
//CRASH("No function named '[name]'.")
return
var/datum/node/statement/FunctionDefinition/func = globalScope.functions[name]
if(istype(func))
var/datum/node/statement/FunctionCall/stmt = new
stmt.func_name = func.func_name
stmt.parameters = params
return RunFunction(stmt)
else
return call(func)(arglist(params))
//CRASH("Unknown function type '[name]'.")
/*
Event: HandleError
Called when the interpreter throws a runtime error.
See Also:
- <runtimeError>
*/
/datum/n_Interpreter/proc/HandleError(var/datum/runtimeError/e)

View File

@@ -13,302 +13,366 @@
#define RETURNING 1
#define BREAKING 2
#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
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
returnVal
var/status = 0
var/returnVal
max_statements=1000 // maximum amount of statements that can be called in one execution. this is to prevent massive crashes and exploitation
cur_statements=0 // current amount of statements called
alertadmins=0 // set to 1 if the admins shouldn't be notified of anymore issues
max_iterations=100 // max number of uninterrupted loops possible
max_recursion=50 // max recursions without returning anything (or completing the code block)
cur_recursion=0 // current amount of recursion
var/max_statements = 900 // maximum amount of statements that can be called in one execution. this is to prevent massive crashes and exploitation
var/cur_statements = 0 // current amount of statements called
var/alertadmins = 0 // set to 1 if the admins shouldn't be notified of anymore issues
var/max_iterations = 100 // max number of uninterrupted loops possible
var/max_recursion = 10 // max recursions without returning anything (or completing the code block)
var/cur_recursion = 0 // current amount of recursion
/*
Var: persist
If 0, global variables will be reset after Run() finishes.
*/
persist=1
paused=0
var/persist = 1
var/paused = 0
/*
Constructor: New
Calls <Load()> with the given parameters.
*/
New(node/BlockDefinition/GlobalBlock/program=null)
.=..()
if(program)Load(program)
/datum/n_Interpreter/New(datum/node/BlockDefinition/GlobalBlock/program = null)
. = ..()
if(program)
Load(program)
/*
Set ourselves to Garbage Collect
*/
/datum/n_Interpreter/proc/GC()
..()
container = null
proc
/*
Proc: RaiseError
Raises a runtime error.
*/
RaiseError(runtimeError/e)
e.stack=functions.Copy()
e.stack.Push(curFunction)
src.HandleError(e)
/datum/n_Interpreter/proc/RaiseError(datum/runtimeError/e)
e.stack = functions.Copy()
e.stack.Push(curFunction)
src.HandleError(e)
CreateScope(node/BlockDefinition/B)
var/scope/S = new(B, curScope)
scopes.Push(curScope)
curScope = S
return S
/datum/n_Interpreter/proc/CreateScope(datum/node/BlockDefinition/B)
var/datum/scope/S = new(B, curScope)
scopes.Push(curScope)
curScope = S
return S
CreateGlobalScope()
scopes.Clear()
var/scope/S = new(program, null)
globalScope = S
return S
/datum/n_Interpreter/proc/CreateGlobalScope()
scopes.Clear()
var/datum/scope/S = new(program, null)
globalScope = S
return S
/*
Proc: RunBlock
Runs each statement in a block of code.
Proc: AlertAdmins
Alerts the admins of a script that is bad.
*/
RunBlock(node/BlockDefinition/Block, scope/scope = null)
var/is_global = istype(Block, /node/BlockDefinition/GlobalBlock)
if(!is_global)
if(scope)
curScope = scope
/datum/n_Interpreter/proc/AlertAdmins()
if(container && !alertadmins)
if(istype(container, /datum/TCS_Compiler))
var/datum/TCS_Compiler/Compiler = container
var/obj/machinery/telecomms/server/Holder = Compiler.Holder
var/message = "Potential crash-inducing NTSL script detected at telecommunications server [Compiler.Holder] ([Holder.x], [Holder.y], [Holder.z])."
alertadmins = 1
message_admins(message, 1)
/*
Proc: RunBlock
Runs each statement in a block of code.
*/
/datum/n_Interpreter/proc/RunBlock(var/datum/node/BlockDefinition/Block, var/datum/scope/scope = null)
var/is_global = istype(Block, /datum/node/BlockDefinition/GlobalBlock)
if(!is_global)
if(scope)
curScope = scope
else
CreateScope(Block)
else
if(!persist)
CreateGlobalScope()
curScope = globalScope
if(cur_statements < max_statements)
for(var/datum/node/statement/S in Block.statements)
while(paused) sleep(10)
cur_statements++
if(cur_statements >= max_statements)
RaiseError(new/datum/runtimeError/MaxCPU())
AlertAdmins()
break
if(istype(S, /datum/node/statement/VariableAssignment))
var/datum/node/statement/VariableAssignment/stmt = S
var/name = stmt.var_name.id_name
if(!stmt.object)
// Below we assign the variable first to null if it doesn't already exist.
// This is necessary for assignments like +=, and when the variable is used in a function
// If the variable already exists in a different block, then AssignVariable will automatically use that one.
if(!IsVariableAccessible(name))
AssignVariable(name, null)
AssignVariable(name, Eval(stmt.value))
else
CreateScope(Block)
else
if(!persist)
CreateGlobalScope()
curScope = globalScope
if(cur_statements < max_statements)
for(var/node/statement/S in Block.statements)
while(paused) sleep(10)
cur_statements++
if(cur_statements >= max_statements)
RaiseError(new/runtimeError/MaxCPU())
if(container && !alertadmins)
if(istype(container, /datum/TCS_Compiler))
var/datum/TCS_Compiler/Compiler = container
var/obj/machinery/telecomms/server/Holder = Compiler.Holder
var/message = "Potential crash-inducing NTSL script detected at telecommunications server [Compiler.Holder] ([Holder.x], [Holder.y], [Holder.z])."
alertadmins = 1
message_admins(message, 1)
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)
RaiseError(new/runtimeError/RecursionLimitReached())
return 0
var/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/scope/S = CreateScope(def.block)
for(var/i=1 to def.parameters.len)
var/val
if(stmt.parameters.len>=i)
val = stmt.parameters[i]
//else
// unspecified param
AssignVariable(def.parameters[i], new/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]"))
var/datum/D = Eval(GetVariable(stmt.object.id_name))
if(!D)
return
return call(def, stmt.func_name)(arglist(params))
else //def is a path to a global proc
return call(def)(arglist(params))
D.vars[stmt.var_name.id_name] = Eval(stmt.value)
else if(istype(S, /datum/node/statement/VariableDeclaration))
//VariableDeclaration nodes are used to forcibly declare a local variable so that one in a higher scope isn't used by default.
var/datum/node/statement/VariableDeclaration/dec=S
if(!dec.object)
AssignVariable(dec.var_name.id_name, null, curScope)
else
var/datum/D = Eval(GetVariable(dec.object.id_name))
if(!D)
return
D.vars[dec.var_name.id_name] = null
else if(istype(S, /datum/node/statement/FunctionCall))
RunFunction(S)
else if(istype(S, /datum/node/statement/FunctionDefinition))
//do nothing
else if(istype(S, /datum/node/statement/WhileLoop))
RunWhile(S)
else if(istype(S, /datum/node/statement/IfStatement))
RunIf(S)
else if(istype(S, /datum/node/statement/ReturnStatement))
if(!curFunction)
RaiseError(new/datum/runtimeError/UnexpectedReturn())
continue
status |= RETURNING
returnVal = Eval(S:value)
break
else if(istype(S, /datum/node/statement/BreakStatement))
status |= BREAKING
break
else if(istype(S, /datum/node/statement/ContinueStatement))
status |= CONTINUING
break
else
RaiseError(new/datum/runtimeError/UnknownInstruction())
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
// 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
Checks a condition and runs either the if block or else block.
Proc: RunIf
Checks a condition and runs either the if block or else block.
*/
RunIf(node/statement/IfStatement/stmt)
if(Eval(stmt.cond))
RunBlock(stmt.block)
else if(stmt.else_block)
RunBlock(stmt.else_block)
/datum/n_Interpreter/proc/RunIf(var/datum/node/statement/IfStatement/stmt)
if(!stmt.skip)
if(Eval(stmt.cond))
RunBlock(stmt.block)
// Loop through the if else chain and tell them to be skipped.
var/datum/node/statement/IfStatement/i = stmt.else_if
var/fail_safe = 800
while(i && fail_safe)
fail_safe -= 1
i.skip = 1
i = i.else_if
else if(stmt.else_block)
RunBlock(stmt.else_block)
// We don't need to skip you anymore.
stmt.skip = 0
/*
Proc: RunWhile
Runs a while loop.
Proc: RunWhile
Runs a while loop.
*/
RunWhile(node/statement/WhileLoop/stmt)
var/i=1
while(Eval(stmt.cond) && Iterate(stmt.block, i++))
continue
status &= ~BREAKING
/datum/n_Interpreter/proc/RunWhile(var/datum/node/statement/WhileLoop/stmt)
var/i = 1
while(Eval(stmt.cond) && Iterate(stmt.block, i++))
continue
status &= ~BREAKING
/*
Proc:Iterate
Runs a single iteration of a loop. Returns a value indicating whether or not to continue looping.
Proc:Iterate
Runs a single iteration of a loop. Returns a value indicating whether or not to continue looping.
*/
Iterate(node/BlockDefinition/block, count)
RunBlock(block)
if(max_iterations > 0 && count >= max_iterations)
RaiseError(new/runtimeError/IterationLimitReached())
return 0
if(status & (BREAKING|RETURNING))
return 0
status &= ~CONTINUING
return 1
/datum/n_Interpreter/proc/Iterate(var/datum/node/BlockDefinition/block, count)
RunBlock(block)
if(max_iterations > 0 && count >= max_iterations)
RaiseError(new/datum/runtimeError/IterationLimitReached())
return 0
if(status & (BREAKING|RETURNING))
return 0
status &= ~CONTINUING
return 1
/*
Proc: GetFunction
Finds a function in an accessible scope with the given name. Returns a <FunctionDefinition>.
Proc: GetFunction
Finds a function in an accessible scope with the given name. Returns a <FunctionDefinition>.
*/
GetFunction(name)
var/scope/S = curScope
while(S)
if(S.functions.Find(name))
return S.functions[name]
S = S.parent
RaiseError(new/runtimeError/UndefinedFunction(name))
/datum/n_Interpreter/proc/GetFunction(name)
var/datum/scope/S = curScope
while(S)
if(S.functions.Find(name))
return S.functions[name]
S = S.parent
RaiseError(new/datum/runtimeError/UndefinedFunction(name))
/*
Proc: GetVariable
Finds a variable in an accessible scope and returns its value.
Proc: GetVariable
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
var/scope/S = curScope
while(S)
if(S.variables.Find(name))
return S
S = S.parent
/datum/n_Interpreter/proc/GetVariable(name)
var/datum/scope/S = curScope
while(S)
if(S.variables.Find(name))
return S.variables[name]
S = S.parent
RaiseError(new/datum/runtimeError/UndefinedVariable(name))
/datum/n_Interpreter/proc/GetVariableScope(name) //needed for when you reassign a variable in a higher scope
var/datum/scope/S = curScope
while(S)
if(S.variables.Find(name))
return S
S = S.parent
IsVariableAccessible(name)
var/scope/S = curScope
while(S)
if(S.variables.Find(name))
return TRUE
S = S.parent
return FALSE
/datum/n_Interpreter/proc/IsVariableAccessible(name)
var/datum/scope/S = curScope
while(S)
if(S.variables.Find(name))
return TRUE
S = S.parent
return FALSE
/*
Proc: AssignVariable
Assigns a value to a variable in a specific block.
Proc: AssignVariable
Assigns a value to a variable in a specific block.
Parameters:
name - The name of the variable to assign.
value - The value to assign to it.
S - The scope the variable resides in. If it is null, a scope with the variable already existing is found. If no scopes have a variable of the given name, the current scope is used.
Parameters:
name - The name of the variable to assign.
value - The value to assign to it.
S - The scope the variable resides in. If it is null, a scope with the variable already existing is found. If no scopes have a variable of the given name, the current scope is used.
*/
AssignVariable(name, node/expression/value, scope/S=null)
if(!S) S = GetVariableScope(name)
if(!S) S = curScope
if(!S) S = globalScope
ASSERT(istype(S))
if(istext(value) || isnum(value) || isnull(value)) value = new/node/expression/value/literal(value)
else if(!istype(value) && isobject(value)) value = new/node/expression/value/reference(value)
//TODO: check for invalid name
S.variables["[name]"] = value
/datum/n_Interpreter/proc/AssignVariable(name, datum/node/expression/value, var/datum/scope/S = null)
if(!S) S = GetVariableScope(name)
if(!S) S = curScope
if(!S) S = globalScope
ASSERT(istype(S))
if(istext(value) || isnum(value) || isnull(value)) value = new/datum/node/expression/value/literal(value)
else if(!istype(value) && isobject(value)) value = new/datum/node/expression/value/reference(value)
//TODO: check for invalid name
S.variables["[name]"] = value

View File

@@ -2,17 +2,15 @@
Class: scope
A runtime instance of a block. Used internally by the interpreter.
*/
scope
var
scope/parent = null
node/BlockDefinition/block
list
functions
variables
/datum/scope
var/datum/scope/parent = null
var/datum/node/BlockDefinition/block
var/list/functions
var/list/variables
New(node/BlockDefinition/B, scope/parent)
src.block = B
src.parent = parent
src.variables = B.initial_variables.Copy()
src.functions = B.functions.Copy()
.=..()
/datum/scope/New(var/datum/node/BlockDefinition/B, var/datum/scope/parent)
src.block = B
src.parent = parent
src.variables = B.initial_variables.Copy()
src.functions = B.functions.Copy()
. = ..()