mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 02:34:00 +00:00
More work done on Telecomms:
▫ Signals can now be rejected by Subspace broadcasters through a specific data[] parameter. ▫ Improved the log browser. ▫ Log browsers and telecommunication monitors no longer require access to use. You do need access to delete logs, however. ▫ Intercoms need power to work. They don't drain power, they just need a constant flow of equipment power. As such, that offline intercom sprite's now finally being put to use. Scripting language: ▫ Sorry about all the files; they're all necessary! It's important to notice that the basic structure of the scripting language code is not mine; I cannibalized the base structure from some obscure BYOND project. It's pretty well documented, and I'd say easier to browse through than atmos. Here's the basic deal: A compiler datum manages the relationships between the three main subsystems of a scripting language: the Scanner, the Parser, and the Interpreter. The Scanner splits raw text into token datums that the Parser can read. The Parser transforms the otherwise random bits and strings into ordered AST Trees and nodes for the Interpreter to read. The interpreter actually executes the code and handles scope/functions/code blocks. git-svn-id: http://tgstation13.googlecode.com/svn/trunk@3193 316c924e-a436-60f5-8080-3fe189b3f50e
This commit is contained in:
committed by
SkyMarshal
parent
02002054de
commit
bbe42a34d8
169
code/modules/scripting/Interpreter/Evaluation.dm
Normal file
169
code/modules/scripting/Interpreter/Evaluation.dm
Normal file
@@ -0,0 +1,169 @@
|
||||
/proc/isobject(x)
|
||||
return (istype(x, /datum) || istype(x, /list) || istype(x, /savefile) || istype(x, /client) || (x==world))
|
||||
|
||||
/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
|
||||
|
||||
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())
|
||||
|
||||
|
||||
//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)
|
||||
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//
|
||||
Minus(a) return -a
|
||||
LogicalNot(a) return !a
|
||||
BitwiseNot(a) return ~a
|
||||
140
code/modules/scripting/Interpreter/Interaction.dm
Normal file
140
code/modules/scripting/Interpreter/Interaction.dm
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
File: Interpreter (Public)
|
||||
Contains methods for interacting with the interpreter.
|
||||
*/
|
||||
/*
|
||||
Class: n_Interpreter
|
||||
Procedures allowing for interaction with the script that is being run by the interpreter object.
|
||||
*/
|
||||
|
||||
/n_Interpreter
|
||||
proc
|
||||
|
||||
/*
|
||||
Proc: Load
|
||||
Loads a 'compiled' script into memory.
|
||||
|
||||
Parameters:
|
||||
program - A <GlobalBlock> object which represents the script's global scope.
|
||||
*/
|
||||
Load(node/BlockDefinition/GlobalBlock/program)
|
||||
ASSERT(program)
|
||||
src.program = program
|
||||
CreateGlobalScope()
|
||||
|
||||
/*
|
||||
Proc: Run
|
||||
Runs the script.
|
||||
*/
|
||||
Run()
|
||||
cur_recursion = 0 // reset recursion
|
||||
|
||||
ASSERT(src.program)
|
||||
RunBlock(src.program)
|
||||
|
||||
/*
|
||||
Proc: SetVar
|
||||
Defines a global variable for the duration of the next execution of a script.
|
||||
|
||||
Notes:
|
||||
This differs from <Block.SetVar()> in that variables set using this procedure only last for the session,
|
||||
while those defined from the block object persist if it is ran multiple times.
|
||||
|
||||
See Also:
|
||||
- <Block.SetVar()>
|
||||
*/
|
||||
SetVar(name, value)
|
||||
if(!istext(name))
|
||||
//CRASH("Invalid variable name")
|
||||
return
|
||||
AssignVariable(name, value)
|
||||
|
||||
/*
|
||||
Proc: SetProc
|
||||
Defines a procedure to be available to the script.
|
||||
|
||||
Parameters:
|
||||
name - The name of the procedure as exposed to the script.
|
||||
path - The typepath of a proc to be called when the function call is read by the interpreter, or, if object is specified, a string representing the procedure's name.
|
||||
object - (Optional) An object which will the be target of a function call.
|
||||
params - Only required if object is not null, a list of the names of parameters the proc takes.
|
||||
*/
|
||||
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
|
||||
/*
|
||||
Proc: VarExists
|
||||
Checks whether a global variable with the specified name exists.
|
||||
*/
|
||||
VarExists(name)
|
||||
return globalScope.variables.Find(name) //convert to 1/0 first?
|
||||
|
||||
/*
|
||||
Proc: ProcExists
|
||||
Checks whether a global function with the specified name exists.
|
||||
*/
|
||||
ProcExists(name)
|
||||
return globalScope.functions.Find(name)
|
||||
|
||||
/*
|
||||
Proc: GetVar
|
||||
Returns the value of a global variable in the script. Remember to ensure that the variable exists before calling this procedure.
|
||||
|
||||
See Also:
|
||||
- <VarExists()>
|
||||
*/
|
||||
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()>
|
||||
*/
|
||||
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]'.")
|
||||
|
||||
/*
|
||||
Event: HandleError
|
||||
Called when the interpreter throws a runtime error.
|
||||
|
||||
See Also:
|
||||
- <runtimeError>
|
||||
*/
|
||||
HandleError(runtimeError/e)
|
||||
291
code/modules/scripting/Interpreter/Interpreter.dm
Normal file
291
code/modules/scripting/Interpreter/Interpreter.dm
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
File: Interpreter (Internal)
|
||||
*/
|
||||
/*
|
||||
Class: n_Interpreter
|
||||
*/
|
||||
/*
|
||||
Macros: Status Macros
|
||||
RETURNING - Indicates that the current function is returning a value.
|
||||
BREAKING - Indicates that the current loop is being terminated.
|
||||
CONTINUING - Indicates that the rest of the current iteration of a loop is being skipped.
|
||||
*/
|
||||
#define RETURNING 1
|
||||
#define BREAKING 2
|
||||
#define CONTINUING 4
|
||||
/n_Interpreter
|
||||
var
|
||||
scope
|
||||
curScope
|
||||
globalScope
|
||||
node
|
||||
BlockDefinition/program
|
||||
statement/FunctionDefinition/curFunction
|
||||
stack
|
||||
scopes = new()
|
||||
functions = new()
|
||||
/*
|
||||
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
|
||||
|
||||
max_iterations=100 // max iterations without any kind of delay
|
||||
max_recursion=50 // max recursions without returning anything (or completing the code block)
|
||||
cur_recursion=0 // current amount of recursion
|
||||
/*
|
||||
Var: persist
|
||||
If 0, global variables will be reset after Run() finishes.
|
||||
*/
|
||||
persist=1
|
||||
paused=0
|
||||
|
||||
/*
|
||||
Constructor: New
|
||||
Calls <Load()> with the given parameters.
|
||||
*/
|
||||
New(node/BlockDefinition/GlobalBlock/program=null)
|
||||
.=..()
|
||||
if(program)Load(program)
|
||||
|
||||
proc
|
||||
/*
|
||||
Proc: RaiseError
|
||||
Raises a runtime error.
|
||||
*/
|
||||
RaiseError(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
|
||||
|
||||
CreateGlobalScope()
|
||||
scopes.Clear()
|
||||
var/scope/S = new(program, null)
|
||||
globalScope = S
|
||||
return S
|
||||
|
||||
/*
|
||||
Proc: RunBlock
|
||||
Runs each statement in a block of code.
|
||||
*/
|
||||
RunBlock(node/BlockDefinition/Block, scope/scope = null)
|
||||
var/is_global = istype(Block, /node/BlockDefinition/GlobalBlock)
|
||||
if(!is_global)
|
||||
if(scope)
|
||||
curScope = scope
|
||||
else
|
||||
CreateScope(Block)
|
||||
else
|
||||
if(!persist)
|
||||
CreateGlobalScope()
|
||||
curScope = globalScope
|
||||
|
||||
for(var/node/statement/S in Block.statements)
|
||||
while(paused) sleep(10)
|
||||
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]"))
|
||||
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.
|
||||
*/
|
||||
RunIf(node/statement/IfStatement/stmt)
|
||||
if(Eval(stmt.cond))
|
||||
RunBlock(stmt.block)
|
||||
else if(stmt.else_block)
|
||||
RunBlock(stmt.else_block)
|
||||
|
||||
/*
|
||||
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
|
||||
|
||||
/*
|
||||
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
|
||||
|
||||
/*
|
||||
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))
|
||||
|
||||
/*
|
||||
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
|
||||
|
||||
|
||||
IsVariableAccessible(name)
|
||||
var/scope/S = curScope
|
||||
while(S)
|
||||
if(S.variables.Find(name))
|
||||
return TRUE
|
||||
S = S.parent
|
||||
return FALSE
|
||||
|
||||
|
||||
/*
|
||||
Proc: AssignVariable
|
||||
Assigns a value to a variable in a specific block.
|
||||
|
||||
Parameters:
|
||||
name - The name of the variable to assign.
|
||||
value - The value to assign to it.
|
||||
S - The scope the variable resides in. If it is null, a scope with the variable already existing is found. If no scopes have a variable of the given name, the current scope is used.
|
||||
*/
|
||||
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
|
||||
|
||||
18
code/modules/scripting/Interpreter/Scope.dm
Normal file
18
code/modules/scripting/Interpreter/Scope.dm
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Class: scope
|
||||
A runtime instance of a block. Used internally by the interpreter.
|
||||
*/
|
||||
scope
|
||||
var
|
||||
scope/parent = null
|
||||
node/BlockDefinition/block
|
||||
list
|
||||
functions
|
||||
variables
|
||||
|
||||
New(node/BlockDefinition/B, scope/parent)
|
||||
src.block = B
|
||||
src.parent = parent
|
||||
src.variables = B.initial_variables.Copy()
|
||||
src.functions = B.functions.Copy()
|
||||
.=..()
|
||||
Reference in New Issue
Block a user