Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[READY] NTSL refactor #4486

Merged
merged 13 commits into from Feb 24, 2019
1 change: 1 addition & 0 deletions yogstation.dme
Expand Up @@ -3115,6 +3115,7 @@
#include "yogstation\code\modules\scripting\Interpreter\Evaluation.dm"
#include "yogstation\code\modules\scripting\Interpreter\Interaction.dm"
#include "yogstation\code\modules\scripting\Interpreter\Interpreter.dm"
#include "yogstation\code\modules\scripting\Interpreter\Objects.dm"
#include "yogstation\code\modules\scripting\Interpreter\Scope.dm"
#include "yogstation\code\modules\scripting\Parser\Expressions.dm"
#include "yogstation\code\modules\scripting\Parser\Keywords.dm"
Expand Down
17 changes: 13 additions & 4 deletions yogstation/code/modules/scripting/AST/AST Nodes.dm
Expand Up @@ -83,15 +83,24 @@
ToString()
return "operator: [name]"

/node/expression/member
var/node/expression/object
var/tmp/temp_object // so you can pre-eval it, used for function calls and assignments

/node/expression/member/dot
var/node/identifier/id

/node/expression/member/brackets
var/node/expression/index


/*
Class: FunctionCall
*/
/node/expression/FunctionCall
//Function calls can also be expressions or statements.
var
func_name
node/identifier/object
list/parameters=new
var/node/expression/function
var/list/parameters=list()

/*
Class: literal
Expand Down
10 changes: 0 additions & 10 deletions yogstation/code/modules/scripting/AST/Statements.dm
Expand Up @@ -6,16 +6,6 @@
An object representing a single instruction run by an interpreter.
*/
/node/statement
/*
Class: FunctionCall
Represents a call to a function.
*/
//
FunctionCall
var
func_name
node/identifier/object
list/parameters=new

/*
Class: FunctionDefinition
Expand Down
76 changes: 25 additions & 51 deletions yogstation/code/modules/scripting/Interpreter/Interpreter.dm
Expand Up @@ -95,6 +95,13 @@
CreateGlobalScope()
var/scope/S = new(program, null)
globalScope = S
for(var/datum/n_function/default/functype in subtypesof(/datum/n_function/default))
if(!istype(src, initial(functype.interp_type))
continue
monster860 marked this conversation as resolved.
Show resolved Hide resolved
var/datum/n_function/default/func = new functype()
globalScope.init_var(func.name, func)
for(var/alias in func.aliases)
globalScope.init_var(alias, func)
return S

/*
Expand Down Expand Up @@ -133,7 +140,8 @@
var/node/statement/VariableDeclaration/dec=S
scope.init_var(dec.var_name.id_name)
else if(istype(S, /node/statement/FunctionDefinition))
//do nothing
var/node/statement/FunctionDefinition/dec=S
scope.init_var(dec.func_name, new /datum/n_function/defined(dec, scope, src))
else if(istype(S, /node/statement/WhileLoop))
. = RunWhile(S, scope)
else if(istype(S, /node/statement/ForLoop))
Expand Down Expand Up @@ -168,57 +176,23 @@
Proc: RunFunction
Runs a function block or a proc with the arguments specified in the script.
*/
RunFunction(node/statement/FunctionCall/stmt, scope/scope)
//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(scope.recursion >= MAX_RECURSION)
AlertAdmins()
RaiseError(new/runtimeError/RecursionLimitReached())
return 0
Altoids1 marked this conversation as resolved.
Show resolved Hide resolved

var/node/statement/FunctionDefinition/def
if(!stmt.object) //A scope's function is being called, stmt.object is null
def = scope.get_function(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 = scope.get_var(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, scope)

if(!def) return

cur_recursion++ // add recursion
if(istype(def))
if(curFunction) functions.Push(curFunction)
scope = scope.push(def.block, globalScope, RESET_STATUS | RETURNING)
for(var/i=1 to def.parameters.len)
var/val
if(stmt.parameters.len>=i)
val = stmt.parameters[i]
//else
// unspecified param
scope.init_var(def.parameters[i], Eval(val, scope))
curFunction=stmt
RunBlock(def.block, scope)
//Handle return value
. = scope.return_val
scope = scope.pop(0) // keep nothing
curFunction=functions.Pop()
cur_recursion--
RunFunction(node/expression/FunctionCall/stmt, scope/scope)
var/datum/n_function/func
var/this_obj
if(istype(stmt.function, /node/expression/member))
var/node/expression/member/M = stmt.function
this_obj = M.temp_object = Eval(M.object)
func = Eval(M)
else
cur_recursion--
var/list/params=new
for(var/node/expression/P in stmt.parameters)
params+=list(Eval(P, scope))
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())
func = Eval(stmt.function)
if(!istype(func))
RaiseError(new/runtimeError/UndefinedFunction("[stmt.object.ToString()]"))
return
var/list/params = list()
for(var/node/expression/P in stmt.parameters)
params+=list(Eval(P, scope))

return func.execute(this_obj, params, scope, src)

/*
Proc: RunIf
Expand Down
49 changes: 49 additions & 0 deletions yogstation/code/modules/scripting/Interpreter/Objects.dm
@@ -0,0 +1,49 @@
/n_Interpreter/proc/get_property(object, propertyname, scope/scope)


/n_Interpreter/proc/set_property(object, propertyname, val, scope/scope)



/datum/n_function
var/name = ""

/datum/n_function/proc/execute(this_obj, list/params, scope/scope, n_Interpreter/interp)
return

/datum/n_function/defined
var/n_Interpreter/context
var/scope/closure
var/node/statement/FunctionDefinition/def

/datum/n_function/defined/New(node/statement/FunctionDefinition/D, scope/S, n_Interpreter/C)
def = D
closure = S
context = C

/datum/n_function/defined/execute(this_obj, list/params, scope/scope, n_Interpreter/interp)
if(scope.recursion >= 10)
AlertAdmins()
interp.RaiseError(new/runtimeError/RecursionLimitReached())
return 0
scope = scope.push(def.block, closure, RESET_STATUS | RETURNING)
scope.recursion++
scope.
for(var/i=1 to def.parameters.len)
var/val
if(params.len>=i)
val = params[i]
//else
// unspecified param
scope.init_var(def.parameters[i], val)
scope.init_var("src", this_obj);
RunBlock(def.block, scope)
//Handle return value
. = scope.return_val
scope = scope.pop(0) // keep nothing

/datum/n_function/default
// functions included on compilation
var/interp_type = /n_Interpreter // include this function in this kind of interpreter.
var/list/aliases // in case you want to give it multiple "names"

4 changes: 3 additions & 1 deletion yogstation/code/modules/scripting/Interpreter/Scope.dm
Expand Up @@ -11,6 +11,7 @@
var/status = 0
var/allowed_status = 0
var/recursion = 0
var/node/statement/FunctionDefinition/function
var/return_val

/scope/New(node/BlockDefinition/B, scope/parent, scope/variables_parent, allowed_status = 0)
Expand All @@ -25,7 +26,8 @@
src.functions = list()
if(parent)
src.status = parent.status
recursion = parent.recursion + 1
recursion = parent.recursion

if(allowed_status & RESET_STATUS || !parent)
src.allowed_status = allowed_status & ~RESET_STATUS
else
Expand Down
1 change: 1 addition & 0 deletions yogstation/code/modules/scripting/Parser/Expressions.dm
Expand Up @@ -194,6 +194,7 @@
NextToken()
continue
val.Push(ParseParenExpression())
else if(istype(curToken, /token/symbol) && curToken.value == ".")

else if(istype(curToken, /token/symbol)) //Operator found.
var/node/expression/operator/curOperator //Figure out whether it is unary or binary and get a new instance.
Expand Down
2 changes: 1 addition & 1 deletion yogstation/code/modules/scripting/Parser/Keywords.dm
Expand Up @@ -205,7 +205,7 @@ Represents a special statement in the code triggered by a keyword.
parser.curBlock.statements+=def
else if(parser.curToken.value=="{" && istype(parser.curToken, /token/symbol))
def.block = new
parser.curBlock.statements+=def
parser.curBlock.statements.Insert(1, def) // insert into the beginning so that all functions are defined first
parser.curBlock.functions[def.func_name]=def
parser.AddBlock(def.block)
else
Expand Down