mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2025-12-14 12:12:37 +00:00
Linter diagnostics + bans non-var relative pathing (#8150)
* Linter diagnostics + bans non-var relative pathing * Enable DreamChecker Annotations * make it executable * update hashFiles * oops * tries to fix it... again * trying again * path * repath * fix perms * fixes weird capitalisation issue
This commit is contained in:
@@ -41,23 +41,20 @@ var
|
||||
/*
|
||||
Class: node
|
||||
*/
|
||||
/node
|
||||
proc
|
||||
ToString()
|
||||
return "[src.type]"
|
||||
/node/proc/ToString()
|
||||
return "[src.type]"
|
||||
/*
|
||||
Class: identifier
|
||||
*/
|
||||
/node/identifier
|
||||
var
|
||||
id_name
|
||||
var/id_name
|
||||
|
||||
New(id)
|
||||
.=..()
|
||||
src.id_name=id
|
||||
/node/identifier/New(id)
|
||||
.=..()
|
||||
src.id_name=id
|
||||
|
||||
ToString()
|
||||
return id_name
|
||||
/node/identifier/ToString()
|
||||
return id_name
|
||||
|
||||
/*
|
||||
Class: expression
|
||||
@@ -68,72 +65,64 @@ var
|
||||
See <Binary Operators> and <Unary Operators> for subtypes.
|
||||
*/
|
||||
/node/expression/operator
|
||||
var
|
||||
node/expression/exp
|
||||
tmp
|
||||
name
|
||||
precedence
|
||||
var/node/expression/exp
|
||||
var/tmp/name
|
||||
var/tmp/precedence
|
||||
|
||||
New()
|
||||
.=..()
|
||||
if(!src.name) src.name="[src.type]"
|
||||
/node/expression/operator/New()
|
||||
.=..()
|
||||
if(!src.name) src.name="[src.type]"
|
||||
|
||||
ToString()
|
||||
return "operator: [name]"
|
||||
/node/expression/operator/ToString()
|
||||
return "operator: [name]"
|
||||
|
||||
/*
|
||||
Class: FunctionCall
|
||||
*/
|
||||
/node/expression/FunctionCall
|
||||
//Function calls can also be expressions or statements.
|
||||
var
|
||||
func_name
|
||||
node/identifier/object
|
||||
list/parameters=new
|
||||
var/func_name
|
||||
var/node/identifier/object
|
||||
var/list/parameters = list()
|
||||
|
||||
/*
|
||||
Class: literal
|
||||
*/
|
||||
/node/expression/value/literal
|
||||
var
|
||||
value
|
||||
var/value
|
||||
|
||||
New(value)
|
||||
.=..()
|
||||
src.value=value
|
||||
/node/expression/value/literal/New(value)
|
||||
.=..()
|
||||
src.value=value
|
||||
|
||||
ToString()
|
||||
return src.value
|
||||
/node/expression/value/literal/ToString()
|
||||
return src.value
|
||||
|
||||
/*
|
||||
Class: variable
|
||||
*/
|
||||
/node/expression/value/variable
|
||||
var
|
||||
node
|
||||
object //Either a node/identifier or another node/expression/value/variable which points to the object
|
||||
node/identifier
|
||||
id
|
||||
var/node/object //Either a node/identifier or another node/expression/value/variable which points to the object
|
||||
var/node/identifier/id
|
||||
|
||||
|
||||
New(ident)
|
||||
.=..()
|
||||
id=ident
|
||||
if(istext(id))id=new(id)
|
||||
/node/expression/value/variable/New(ident)
|
||||
.=..()
|
||||
id=ident
|
||||
if(istext(id))id=new(id)
|
||||
|
||||
ToString()
|
||||
return src.id.ToString()
|
||||
/node/expression/value/variable/ToString()
|
||||
return src.id.ToString()
|
||||
|
||||
/*
|
||||
Class: reference
|
||||
*/
|
||||
/node/expression/value/reference
|
||||
var
|
||||
datum/value
|
||||
var/datum/value
|
||||
|
||||
New(value)
|
||||
.=..()
|
||||
src.value=value
|
||||
/node/expression/value/reference/New(value)
|
||||
.=..()
|
||||
src.value=value
|
||||
|
||||
ToString()
|
||||
return "ref: [src.value] ([src.value.type])"
|
||||
/node/expression/value/reference/ToString()
|
||||
return "ref: [src.value] ([src.value.type])"
|
||||
@@ -9,12 +9,10 @@
|
||||
and not just in the global scope as in many languages.
|
||||
*/
|
||||
/node/BlockDefinition
|
||||
var/list
|
||||
statements = new
|
||||
functions = new
|
||||
initial_variables = new
|
||||
var/list/statements = list()
|
||||
var/list/functions = list()
|
||||
var/list/initial_variables = list()
|
||||
|
||||
proc
|
||||
/*
|
||||
Proc: SetVar
|
||||
Defines a permanent variable. The variable will not be deleted when it goes out of scope.
|
||||
@@ -26,8 +24,8 @@
|
||||
See Also:
|
||||
- <n_Interpreter.SetVar()>
|
||||
*/
|
||||
SetVar(name, value)
|
||||
initial_variables[name]=value
|
||||
/node/BlockDefinition/proc/SetVar(name, value)
|
||||
initial_variables[name]=value
|
||||
|
||||
|
||||
/*
|
||||
@@ -35,14 +33,13 @@
|
||||
A block object representing the global scope.
|
||||
*/
|
||||
//
|
||||
GlobalBlock
|
||||
New()
|
||||
initial_variables["null"]=null
|
||||
return ..()
|
||||
/node/BlockDefinition/GlobalBlock/New()
|
||||
initial_variables["null"]=null
|
||||
return ..()
|
||||
|
||||
/*
|
||||
Class: FunctionBlock
|
||||
A block representing a function body.
|
||||
*/
|
||||
//
|
||||
FunctionBlock
|
||||
/node/BlockDefinition/FunctionBlock
|
||||
@@ -16,48 +16,48 @@
|
||||
Returns true if x = y.
|
||||
*/
|
||||
//
|
||||
Equal
|
||||
precedence=OOP_EQUAL
|
||||
/node/expression/operator/binary/Equal
|
||||
precedence=OOP_EQUAL
|
||||
|
||||
/*
|
||||
Class: NotEqual
|
||||
Returns true if x and y aren't equal.
|
||||
*/
|
||||
//
|
||||
NotEqual
|
||||
precedence=OOP_EQUAL
|
||||
/node/expression/operator/binary/NotEqual
|
||||
precedence=OOP_EQUAL
|
||||
|
||||
/*
|
||||
Class: Greater
|
||||
Returns true if x > y.
|
||||
*/
|
||||
//
|
||||
Greater
|
||||
precedence=OOP_COMPARE
|
||||
/node/expression/operator/binary/Greater
|
||||
precedence=OOP_COMPARE
|
||||
|
||||
/*
|
||||
Class: Less
|
||||
Returns true if x < y.
|
||||
*/
|
||||
//
|
||||
Less
|
||||
precedence=OOP_COMPARE
|
||||
/node/expression/operator/binary/Less
|
||||
precedence=OOP_COMPARE
|
||||
|
||||
/*
|
||||
Class: GreaterOrEqual
|
||||
Returns true if x >= y.
|
||||
*/
|
||||
//
|
||||
GreaterOrEqual
|
||||
precedence=OOP_COMPARE
|
||||
/node/expression/operator/binary/GreaterOrEqual
|
||||
precedence=OOP_COMPARE
|
||||
|
||||
/*
|
||||
Class: LessOrEqual
|
||||
Returns true if x <= y.
|
||||
*/
|
||||
//
|
||||
LessOrEqual
|
||||
precedence=OOP_COMPARE
|
||||
/node/expression/operator/binary/LessOrEqual
|
||||
precedence=OOP_COMPARE
|
||||
|
||||
|
||||
////////// Logical Operators //////////
|
||||
@@ -67,24 +67,24 @@
|
||||
Returns true if x and y are true.
|
||||
*/
|
||||
//
|
||||
LogicalAnd
|
||||
precedence=OOP_AND
|
||||
/node/expression/operator/binary/LogicalAnd
|
||||
precedence=OOP_AND
|
||||
|
||||
/*
|
||||
Class: LogicalOr
|
||||
Returns true if x, y, or both are true.
|
||||
*/
|
||||
//
|
||||
LogicalOr
|
||||
precedence=OOP_OR
|
||||
/node/expression/operator/binary/LogicalOr
|
||||
precedence=OOP_OR
|
||||
|
||||
/*
|
||||
Class: LogicalXor
|
||||
Returns true if either x or y but not both are true.
|
||||
*/
|
||||
//
|
||||
LogicalXor //Not implemented in nS
|
||||
precedence=OOP_OR
|
||||
/node/expression/operator/binary/LogicalXor //Not implemented in nS
|
||||
precedence=OOP_OR
|
||||
|
||||
|
||||
////////// Bitwise Operators //////////
|
||||
@@ -97,8 +97,8 @@
|
||||
011 & 110 = 010
|
||||
*/
|
||||
//
|
||||
BitwiseAnd
|
||||
precedence=OOP_BIT
|
||||
/node/expression/operator/binary/BitwiseAnd
|
||||
precedence=OOP_BIT
|
||||
|
||||
/*
|
||||
Class: BitwiseOr
|
||||
@@ -108,8 +108,8 @@
|
||||
011 | 110 = 111
|
||||
*/
|
||||
//
|
||||
BitwiseOr
|
||||
precedence=OOP_BIT
|
||||
/node/expression/operator/binary/BitwiseOr
|
||||
precedence=OOP_BIT
|
||||
|
||||
/*
|
||||
Class: BitwiseXor
|
||||
@@ -119,8 +119,8 @@
|
||||
011 xor 110 = 101
|
||||
*/
|
||||
//
|
||||
BitwiseXor
|
||||
precedence=OOP_BIT
|
||||
/node/expression/operator/binary/BitwiseXor
|
||||
precedence=OOP_BIT
|
||||
|
||||
|
||||
////////// Arithmetic Operators //////////
|
||||
@@ -130,45 +130,45 @@
|
||||
Returns the sum of x and y.
|
||||
*/
|
||||
//
|
||||
Add
|
||||
precedence=OOP_ADD
|
||||
/node/expression/operator/binary/Add
|
||||
precedence=OOP_ADD
|
||||
|
||||
/*
|
||||
Class: Subtract
|
||||
Returns the difference of x and y.
|
||||
*/
|
||||
//
|
||||
Subtract
|
||||
precedence=OOP_ADD
|
||||
/node/expression/operator/binary/Subtract
|
||||
precedence=OOP_ADD
|
||||
|
||||
/*
|
||||
Class: Multiply
|
||||
Returns the product of x and y.
|
||||
*/
|
||||
//
|
||||
Multiply
|
||||
precedence=OOP_MULTIPLY
|
||||
/node/expression/operator/binary/Multiply
|
||||
precedence=OOP_MULTIPLY
|
||||
|
||||
/*
|
||||
Class: Divide
|
||||
Returns the quotient of x and y.
|
||||
*/
|
||||
//
|
||||
Divide
|
||||
precedence=OOP_MULTIPLY
|
||||
/node/expression/operator/binary/Divide
|
||||
precedence=OOP_MULTIPLY
|
||||
|
||||
/*
|
||||
Class: Power
|
||||
Returns x raised to the power of y.
|
||||
*/
|
||||
//
|
||||
Power
|
||||
precedence=OOP_POW
|
||||
/node/expression/operator/binary/Power
|
||||
precedence=OOP_POW
|
||||
|
||||
/*
|
||||
Class: Modulo
|
||||
Returns the remainder of x / y.
|
||||
*/
|
||||
//
|
||||
Modulo
|
||||
precedence=OOP_MULTIPLY
|
||||
/node/expression/operator/binary/Modulo
|
||||
precedence=OOP_MULTIPLY
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
!true = false and !false = true
|
||||
*/
|
||||
//
|
||||
LogicalNot
|
||||
name="logical not"
|
||||
/node/expression/operator/unary/LogicalNot
|
||||
name="logical not"
|
||||
|
||||
/*
|
||||
Class: BitwiseNot
|
||||
@@ -27,25 +27,25 @@
|
||||
~10 (decimal 2) = 01 (decimal 1).
|
||||
*/
|
||||
//
|
||||
BitwiseNot
|
||||
name="bitwise not"
|
||||
/node/expression/operator/unary/BitwiseNot
|
||||
name="bitwise not"
|
||||
|
||||
/*
|
||||
Class: Minus
|
||||
Returns -x.
|
||||
*/
|
||||
//
|
||||
Minus
|
||||
name="minus"
|
||||
/node/expression/operator/unary/Minus
|
||||
name="minus"
|
||||
|
||||
/*
|
||||
Class: group
|
||||
A special unary operator representing a value in parentheses.
|
||||
*/
|
||||
//
|
||||
group
|
||||
precedence=OOP_GROUP
|
||||
/node/expression/operator/unary/group
|
||||
precedence=OOP_GROUP
|
||||
|
||||
New(node/expression/exp)
|
||||
src.exp=exp
|
||||
return ..()
|
||||
/node/expression/operator/unary/New(node/expression/exp)
|
||||
src.exp=exp
|
||||
return ..()
|
||||
|
||||
@@ -11,22 +11,20 @@
|
||||
Represents a call to a function.
|
||||
*/
|
||||
//
|
||||
FunctionCall
|
||||
var
|
||||
func_name
|
||||
node/identifier/object
|
||||
list/parameters=new
|
||||
/node/statement/FunctionCall
|
||||
var/func_name
|
||||
var/node/identifier/object
|
||||
var/list/parameters=list()
|
||||
|
||||
/*
|
||||
Class: FunctionDefinition
|
||||
Defines a function.
|
||||
*/
|
||||
//
|
||||
FunctionDefinition
|
||||
var
|
||||
func_name
|
||||
list/parameters=new
|
||||
node/BlockDefinition/FunctionBlock/block
|
||||
/node/statement/FunctionDefinition
|
||||
var/func_name
|
||||
var/list/parameters=list()
|
||||
var/node/BlockDefinition/FunctionBlock/block
|
||||
|
||||
/*
|
||||
Class: VariableAssignment
|
||||
@@ -40,13 +38,10 @@
|
||||
- <VariableDeclaration>
|
||||
*/
|
||||
//
|
||||
VariableAssignment
|
||||
var
|
||||
node
|
||||
identifier
|
||||
object
|
||||
var_name
|
||||
expression/value
|
||||
/node/statement/VariableAssignment
|
||||
var/node/identifier/object
|
||||
var/node/identifier/var_name
|
||||
var/node/expression/value
|
||||
|
||||
/*
|
||||
Class: VariableDeclaration
|
||||
@@ -56,67 +51,56 @@
|
||||
- <VariableAssignment>
|
||||
*/
|
||||
//
|
||||
VariableDeclaration
|
||||
var
|
||||
node
|
||||
identifier
|
||||
object
|
||||
var_name
|
||||
/node/statement/VariableDeclaration
|
||||
var/node/identifier/object
|
||||
var/node/identifier/var_name
|
||||
|
||||
/*
|
||||
Class: IfStatement
|
||||
*/
|
||||
//
|
||||
IfStatement
|
||||
var
|
||||
node
|
||||
BlockDefinition
|
||||
block
|
||||
else_block //may be null
|
||||
expression/cond
|
||||
/node/statement/IfStatement
|
||||
var/node/BlockDefinition/block
|
||||
var/node/BlockDefinition/else_block // may be null
|
||||
var/node/expression/cond
|
||||
|
||||
/*
|
||||
Class: WhileLoop
|
||||
Loops while a given condition is true.
|
||||
*/
|
||||
//
|
||||
WhileLoop
|
||||
var
|
||||
node
|
||||
BlockDefinition/block
|
||||
expression/cond
|
||||
/node/statement/WhileLoop
|
||||
var/node/BlockDefinition/block
|
||||
var/node/expression/cond
|
||||
|
||||
/*
|
||||
Class: ForLoop
|
||||
Loops while test is true, initializing a variable, increasing the variable
|
||||
*/
|
||||
ForLoop
|
||||
var
|
||||
node
|
||||
BlockDefinition/block
|
||||
expression/test
|
||||
expression/init
|
||||
expression/increment
|
||||
/node/statement/ForLoop
|
||||
var/node/BlockDefinition/block
|
||||
var/node/expression/test
|
||||
var/node/expression/init
|
||||
var/node/expression/increment
|
||||
|
||||
/*
|
||||
Class: BreakStatement
|
||||
Ends a loop.
|
||||
*/
|
||||
//
|
||||
BreakStatement
|
||||
/node/statement/BreakStatement
|
||||
|
||||
/*
|
||||
Class: ContinueStatement
|
||||
Skips to the next iteration of a loop.
|
||||
*/
|
||||
//
|
||||
ContinueStatement
|
||||
/node/statement/ContinueStatement
|
||||
|
||||
/*
|
||||
Class: ReturnStatement
|
||||
Ends the function and returns a value.
|
||||
*/
|
||||
//
|
||||
ReturnStatement
|
||||
var
|
||||
node/expression/value
|
||||
/node/statement/ReturnStatement
|
||||
var/node/expression/value
|
||||
@@ -6,127 +6,117 @@
|
||||
An error scanning or parsing the source code.
|
||||
*/
|
||||
/scriptError
|
||||
var
|
||||
/*
|
||||
Var: message
|
||||
A message describing the problem.
|
||||
*/
|
||||
message
|
||||
New(msg=null)
|
||||
if(msg)message=msg
|
||||
var/message /// A message describing the problem.
|
||||
/scriptError/New(msg=null)
|
||||
if(msg)message=msg
|
||||
|
||||
BadToken
|
||||
message="Unexpected token: "
|
||||
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]"
|
||||
/scriptError/BadToken
|
||||
message="Unexpected token: "
|
||||
var/token/token
|
||||
/scriptError/BadToken/New(token/t)
|
||||
token=t
|
||||
if(t&&t.line) message="[t.line]: [message]"
|
||||
if(istype(t))message+="[t.value]"
|
||||
else message+="[t]"
|
||||
|
||||
InvalidID
|
||||
parent_type=/scriptError/BadToken
|
||||
message="Invalid identifier name: "
|
||||
/scriptError/InvalidID
|
||||
parent_type=/scriptError/BadToken
|
||||
message="Invalid identifier name: "
|
||||
|
||||
ReservedWord
|
||||
parent_type=/scriptError/BadToken
|
||||
message="Identifer using reserved word: "
|
||||
/scriptError/ReservedWord
|
||||
parent_type=/scriptError/BadToken
|
||||
message="Identifer using reserved word: "
|
||||
|
||||
BadNumber
|
||||
parent_type=/scriptError/BadToken
|
||||
message = "Bad number: "
|
||||
/scriptError/BadNumber
|
||||
parent_type=/scriptError/BadToken
|
||||
message = "Bad number: "
|
||||
|
||||
BadReturn
|
||||
var/token/token
|
||||
message = "Unexpected return statement outside of a function."
|
||||
New(token/t)
|
||||
src.token=t
|
||||
/scriptError/BadReturn
|
||||
var/token/token
|
||||
message = "Unexpected return statement outside of a function."
|
||||
/scriptError/BadReturn/New(token/t)
|
||||
src.token=t
|
||||
|
||||
EndOfFile
|
||||
message = "Unexpected end of file."
|
||||
/scriptError/EndOfFile
|
||||
message = "Unexpected end of file."
|
||||
|
||||
ExpectedToken
|
||||
message="Expected: '"
|
||||
New(id, token/T)
|
||||
if(T && T.line) message="[T.line]: [message]"
|
||||
message+="[id]'. "
|
||||
if(T)message+="Found '[T.value]'."
|
||||
/scriptError/ExpectedToken
|
||||
message="Expected: '"
|
||||
/scriptError/ExpectedToken/New(id, token/T)
|
||||
if(T && T.line) message="[T.line]: [message]"
|
||||
message+="[id]'. "
|
||||
if(T)message+="Found '[T.value]'."
|
||||
|
||||
|
||||
UnterminatedComment
|
||||
message="Unterminated multi-line comment statement: expected */"
|
||||
/scriptError/UnterminatedComment
|
||||
message="Unterminated multi-line comment statement: expected */"
|
||||
|
||||
DuplicateFunction
|
||||
New(name, token/t)
|
||||
message="Function '[name]' defined twice."
|
||||
/scriptError/DuplicateFunction
|
||||
message="Function defined twice."
|
||||
/scriptError/DuplicateFunction/New(name, token/t)
|
||||
message="Function '[name]' defined twice."
|
||||
|
||||
/*
|
||||
Class: runtimeError
|
||||
An error thrown by the interpreter in running the script.
|
||||
*/
|
||||
/runtimeError
|
||||
var
|
||||
name
|
||||
/*
|
||||
Var: message
|
||||
A basic description as to what went wrong.
|
||||
*/
|
||||
message
|
||||
stack/stack
|
||||
var/name
|
||||
var/message /// A basic description as to what went wrong.
|
||||
var/stack/stack
|
||||
|
||||
proc
|
||||
/*
|
||||
Proc: ToString
|
||||
Returns a description of the error suitable for showing to the user.
|
||||
*/
|
||||
ToString()
|
||||
. = "[name]: [message]"
|
||||
if(!stack.Top()) return
|
||||
.+="\nStack:"
|
||||
while(stack.Top())
|
||||
var/node/statement/FunctionCall/stmt=stack.Pop()
|
||||
. += "\n\t [stmt.func_name]()"
|
||||
/**
|
||||
* Proc: ToString
|
||||
* Returns a description of the error suitable for showing to the user.
|
||||
*/
|
||||
/runtimeError/proc/ToString()
|
||||
. = "[name]: [message]"
|
||||
if(!stack.Top()) return
|
||||
.+="\nStack:"
|
||||
while(stack.Top())
|
||||
var/node/statement/FunctionCall/stmt=stack.Pop()
|
||||
. += "\n\t [stmt.func_name]()"
|
||||
|
||||
TypeMismatch
|
||||
name="TypeMismatchError"
|
||||
New(op, a, b)
|
||||
message="Type mismatch: '[a]' [op] '[b]'"
|
||||
/runtimeError/TypeMismatch
|
||||
name="TypeMismatchError"
|
||||
/runtimeError/TypeMismatch/New(op, a, b)
|
||||
message="Type mismatch: '[a]' [op] '[b]'"
|
||||
|
||||
UnexpectedReturn
|
||||
name="UnexpectedReturnError"
|
||||
message="Unexpected return statement."
|
||||
/runtimeError/UnexpectedReturn
|
||||
name="UnexpectedReturnError"
|
||||
message="Unexpected return statement."
|
||||
|
||||
UnknownInstruction
|
||||
name="UnknownInstructionError"
|
||||
message="Unknown instruction type. This may be due to incompatible compiler and interpreter versions or a lack of implementation."
|
||||
/runtimeError/UnknownInstruction
|
||||
name="UnknownInstructionError"
|
||||
message="Unknown instruction type. This may be due to incompatible compiler and interpreter versions or a lack of implementation."
|
||||
|
||||
UndefinedVariable
|
||||
name="UndefinedVariableError"
|
||||
New(variable)
|
||||
message="Variable '[variable]' has not been declared."
|
||||
/runtimeError/UndefinedVariable
|
||||
name="UndefinedVariableError"
|
||||
/runtimeError/UndefinedVariable/New(variable)
|
||||
message="Variable '[variable]' has not been declared."
|
||||
|
||||
UndefinedFunction
|
||||
name="UndefinedFunctionError"
|
||||
New(function)
|
||||
message="Function '[function]()' has not been defined."
|
||||
/runtimeError/UndefinedFunction
|
||||
name="UndefinedFunctionError"
|
||||
/runtimeError/UndefinedFunction/New(function)
|
||||
message="Function '[function]()' has not been defined."
|
||||
|
||||
DuplicateVariableDeclaration
|
||||
name="DuplicateVariableError"
|
||||
New(variable)
|
||||
message="Variable '[variable]' was already declared."
|
||||
/runtimeError/DuplicateVariableDeclaration
|
||||
name="DuplicateVariableError"
|
||||
/runtimeError/DuplicateVariableDeclaration/New(variable)
|
||||
message="Variable '[variable]' was already declared."
|
||||
|
||||
IterationLimitReached
|
||||
name="MaxIterationError"
|
||||
message="A loop has reached its maximum number of iterations."
|
||||
/runtimeError/IterationLimitReached
|
||||
name="MaxIterationError"
|
||||
message="A loop has reached its maximum number of iterations."
|
||||
|
||||
RecursionLimitReached
|
||||
name="MaxRecursionError"
|
||||
message="The maximum amount of recursion has been reached."
|
||||
/runtimeError/RecursionLimitReached
|
||||
name="MaxRecursionError"
|
||||
message="The maximum amount of recursion has been reached."
|
||||
|
||||
DivisionByZero
|
||||
name="DivideByZeroError"
|
||||
message="Division by zero attempted."
|
||||
/runtimeError/DivisionByZero
|
||||
name="DivideByZeroError"
|
||||
message="Division by zero attempted."
|
||||
|
||||
MaxCPU
|
||||
name="MaxComputationalUse"
|
||||
message="Maximum amount of computational cycles reached (>= 1000)."
|
||||
/runtimeError/MaxCPU
|
||||
name="MaxComputationalUse"
|
||||
message="Maximum amount of computational cycles reached (>= 1000)."
|
||||
@@ -1,4 +1,4 @@
|
||||
client/verb/tcssave()
|
||||
/client/verb/tcssave()
|
||||
set hidden = 1
|
||||
if(mob.machine || issilicon(mob))
|
||||
if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && (mob.machine in view(1, mob))) || issilicon(mob))
|
||||
@@ -25,7 +25,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>", "tcserror")
|
||||
|
||||
|
||||
client/verb/tcscompile()
|
||||
/client/verb/tcscompile()
|
||||
set hidden = 1
|
||||
if(mob.machine || issilicon(mob))
|
||||
if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && (mob.machine in view(1, mob))) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
|
||||
@@ -76,7 +76,7 @@ client/verb/tcscompile()
|
||||
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>", "tcserror")
|
||||
|
||||
client/verb/tcsrun()
|
||||
/client/verb/tcsrun()
|
||||
set hidden = 1
|
||||
if(mob.machine || issilicon(mob))
|
||||
if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && (mob.machine in view(1, mob))) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
|
||||
@@ -142,7 +142,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>", "tcserror")
|
||||
|
||||
|
||||
client/verb/exittcs()
|
||||
/client/verb/exittcs()
|
||||
set hidden = 1
|
||||
if(mob.machine || issilicon(mob))
|
||||
if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && (mob.machine in view(1, mob))) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
|
||||
@@ -154,7 +154,7 @@ client/verb/exittcs()
|
||||
if(mob in Machine.viewingcode)
|
||||
Machine.viewingcode.Remove(mob)
|
||||
|
||||
client/verb/tcsrevert()
|
||||
/client/verb/tcsrevert()
|
||||
set hidden = 1
|
||||
if(mob.machine || issilicon(mob))
|
||||
if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && (mob.machine in view(1, mob))) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
|
||||
@@ -183,7 +183,7 @@ client/verb/tcsrevert()
|
||||
src << output("<font color = red>Failed to revert: Unable to locate machine.</font>", "tcserror")
|
||||
|
||||
|
||||
client/verb/tcsclearmem()
|
||||
/client/verb/tcsclearmem()
|
||||
set hidden = 1
|
||||
if(mob.machine || issilicon(mob))
|
||||
if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && (mob.machine in view(1, mob))) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
|
||||
|
||||
@@ -7,269 +7,275 @@
|
||||
/n_Interpreter/TCS_Interpreter
|
||||
var/datum/TCS_Compiler/Compiler
|
||||
|
||||
HandleError(runtimeError/e)
|
||||
Compiler.Holder.add_entry(e.ToString(), "Execution Error")
|
||||
/n_Interpreter/TCS_Interpreter/HandleError(runtimeError/e)
|
||||
Compiler.Holder.add_entry(e.ToString(), "Execution Error")
|
||||
|
||||
/datum/TCS_Compiler
|
||||
var/n_Interpreter/TCS_Interpreter/interpreter
|
||||
var/obj/machinery/telecomms/server/Holder // the server that is running the code
|
||||
var/ready = 1 // 1 if ready to run code
|
||||
|
||||
/* -- Compile a raw block of text -- */
|
||||
/** Proc: Compile
|
||||
* Compile a raw block of text into a program
|
||||
* Returns: List of errors
|
||||
*/
|
||||
|
||||
proc/Compile(code as message)
|
||||
var/n_scriptOptions/nS_Options/options = new()
|
||||
var/n_Scanner/nS_Scanner/scanner = new(code, options)
|
||||
var/list/tokens = scanner.Scan()
|
||||
var/n_Parser/nS_Parser/parser = new(tokens, options)
|
||||
var/node/BlockDefinition/GlobalBlock/program = parser.Parse()
|
||||
/datum/TCS_Compiler/proc/Compile(code as message)
|
||||
var/n_scriptOptions/nS_Options/options = new()
|
||||
var/n_Scanner/nS_Scanner/scanner = new(code, options)
|
||||
var/list/tokens = scanner.Scan()
|
||||
var/n_Parser/nS_Parser/parser = new(tokens, options)
|
||||
var/node/BlockDefinition/GlobalBlock/program = parser.Parse()
|
||||
|
||||
var/list/returnerrors = list()
|
||||
var/list/returnerrors = list()
|
||||
|
||||
returnerrors += scanner.errors
|
||||
returnerrors += parser.errors
|
||||
|
||||
if(returnerrors.len)
|
||||
return returnerrors
|
||||
|
||||
interpreter = new(program)
|
||||
interpreter.persist = 1
|
||||
interpreter.Compiler= src
|
||||
returnerrors += scanner.errors
|
||||
returnerrors += parser.errors
|
||||
|
||||
if(returnerrors.len)
|
||||
return returnerrors
|
||||
|
||||
interpreter = new(program)
|
||||
interpreter.persist = 1
|
||||
interpreter.Compiler= src
|
||||
|
||||
return returnerrors
|
||||
|
||||
/* -- Execute the compiled code -- */
|
||||
/** Proc: Run
|
||||
* Executes the compiled code.
|
||||
* Arguments:
|
||||
* var/datum/signal/signal - a telecomms signal
|
||||
* Returns: None
|
||||
*/
|
||||
/datum/TCS_Compiler/proc/Run(var/datum/signal/signal)
|
||||
|
||||
proc/Run(var/datum/signal/signal)
|
||||
if(!ready)
|
||||
return
|
||||
|
||||
if(!ready)
|
||||
return
|
||||
if(!interpreter)
|
||||
return
|
||||
|
||||
if(!interpreter)
|
||||
return
|
||||
interpreter.container = src
|
||||
|
||||
interpreter.container = src
|
||||
interpreter.SetVar("PI" , 3.141592653) // value of pi
|
||||
interpreter.SetVar("E" , 2.718281828) // value of e
|
||||
interpreter.SetVar("SQURT2" , 1.414213562) // value of the square root of 2
|
||||
interpreter.SetVar("FALSE" , 0) // boolean shortcut to 0
|
||||
interpreter.SetVar("TRUE" , 1) // boolean shortcut to 1
|
||||
|
||||
interpreter.SetVar("PI" , 3.141592653) // value of pi
|
||||
interpreter.SetVar("E" , 2.718281828) // value of e
|
||||
interpreter.SetVar("SQURT2" , 1.414213562) // value of the square root of 2
|
||||
interpreter.SetVar("FALSE" , 0) // boolean shortcut to 0
|
||||
interpreter.SetVar("TRUE" , 1) // boolean shortcut to 1
|
||||
interpreter.SetVar("NORTH" , NORTH) // NORTH (1)
|
||||
interpreter.SetVar("SOUTH" , SOUTH) // SOUTH (2)
|
||||
interpreter.SetVar("EAST" , EAST) // EAST (4)
|
||||
interpreter.SetVar("WEST" , WEST) // WEST (8)
|
||||
|
||||
interpreter.SetVar("NORTH" , NORTH) // NORTH (1)
|
||||
interpreter.SetVar("SOUTH" , SOUTH) // SOUTH (2)
|
||||
interpreter.SetVar("EAST" , EAST) // EAST (4)
|
||||
interpreter.SetVar("WEST" , WEST) // WEST (8)
|
||||
// Channel macros
|
||||
interpreter.SetVar("$common", PUB_FREQ)
|
||||
interpreter.SetVar("$science", SCI_FREQ)
|
||||
interpreter.SetVar("$command", COMM_FREQ)
|
||||
interpreter.SetVar("$medical", MED_FREQ)
|
||||
interpreter.SetVar("$engineering",ENG_FREQ)
|
||||
interpreter.SetVar("$security", SEC_FREQ)
|
||||
interpreter.SetVar("$supply", SUP_FREQ)
|
||||
interpreter.SetVar("$explorer", EXP_FREQ)
|
||||
|
||||
// Channel macros
|
||||
interpreter.SetVar("$common", PUB_FREQ)
|
||||
interpreter.SetVar("$science", SCI_FREQ)
|
||||
interpreter.SetVar("$command", COMM_FREQ)
|
||||
interpreter.SetVar("$medical", MED_FREQ)
|
||||
interpreter.SetVar("$engineering",ENG_FREQ)
|
||||
interpreter.SetVar("$security", SEC_FREQ)
|
||||
interpreter.SetVar("$supply", SUP_FREQ)
|
||||
interpreter.SetVar("$explorer", EXP_FREQ)
|
||||
// Signal data
|
||||
|
||||
// Signal data
|
||||
interpreter.SetVar("$content", signal.data["message"])
|
||||
interpreter.SetVar("$freq" , signal.frequency)
|
||||
interpreter.SetVar("$source" , signal.data["name"])
|
||||
interpreter.SetVar("$job" , signal.data["job"])
|
||||
interpreter.SetVar("$sign" , signal)
|
||||
interpreter.SetVar("$pass" , !(signal.data["reject"])) // if the signal isn't rejected, pass = 1; if the signal IS rejected, pass = 0
|
||||
|
||||
interpreter.SetVar("$content", signal.data["message"])
|
||||
interpreter.SetVar("$freq" , signal.frequency)
|
||||
interpreter.SetVar("$source" , signal.data["name"])
|
||||
interpreter.SetVar("$job" , signal.data["job"])
|
||||
interpreter.SetVar("$sign" , signal)
|
||||
interpreter.SetVar("$pass" , !(signal.data["reject"])) // if the signal isn't rejected, pass = 1; if the signal IS rejected, pass = 0
|
||||
// Set up the script procs
|
||||
|
||||
// Set up the script procs
|
||||
/*
|
||||
-> Send another signal to a server
|
||||
@format: broadcast(content, frequency, source, job)
|
||||
|
||||
/*
|
||||
-> Send another signal to a server
|
||||
@format: broadcast(content, frequency, source, job)
|
||||
@param content: Message to broadcast
|
||||
@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 job: The name of the job.
|
||||
*/
|
||||
interpreter.SetProc("broadcast", "tcombroadcast", signal, list("message", "freq", "source", "job"))
|
||||
|
||||
@param content: Message to broadcast
|
||||
@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 job: The name of the job.
|
||||
*/
|
||||
interpreter.SetProc("broadcast", "tcombroadcast", signal, list("message", "freq", "source", "job"))
|
||||
/*
|
||||
-> Store a value permanently to the server machine (not the actual game hosting machine, the ingame machine)
|
||||
@format: mem(address, value)
|
||||
|
||||
/*
|
||||
-> Store a value permanently to the server machine (not the actual game hosting machine, the ingame machine)
|
||||
@format: mem(address, value)
|
||||
@param address: The memory address (string index) to store a value to
|
||||
@param value: The value to store to the memory address
|
||||
*/
|
||||
interpreter.SetProc("mem", "mem", signal, list("address", "value"))
|
||||
|
||||
@param address: The memory address (string index) to store a value to
|
||||
@param value: The value to store to the memory address
|
||||
*/
|
||||
interpreter.SetProc("mem", "mem", signal, list("address", "value"))
|
||||
/*
|
||||
-> Delay code for a given amount of deciseconds
|
||||
@format: sleep(time)
|
||||
|
||||
/*
|
||||
-> Delay code for a given amount of deciseconds
|
||||
@format: sleep(time)
|
||||
@param time: time to sleep in deciseconds (1/10th second)
|
||||
*/
|
||||
interpreter.SetProc("sleep", /proc/delay)
|
||||
|
||||
@param time: time to sleep in deciseconds (1/10th second)
|
||||
*/
|
||||
interpreter.SetProc("sleep", /proc/delay)
|
||||
/*
|
||||
-> Replaces a string with another string
|
||||
@format: replace(string, substring, replacestring)
|
||||
|
||||
/*
|
||||
-> Replaces a string with another string
|
||||
@format: replace(string, substring, replacestring)
|
||||
@param string: the string to search for substrings (best used with $content$ constant)
|
||||
@param substring: the substring to search for
|
||||
@param replacestring: the string to replace the substring with
|
||||
|
||||
@param string: the string to search for substrings (best used with $content$ constant)
|
||||
@param substring: the substring to search for
|
||||
@param replacestring: the string to replace the substring with
|
||||
*/
|
||||
interpreter.SetProc("replace", /proc/string_replacetext)
|
||||
|
||||
*/
|
||||
interpreter.SetProc("replace", /proc/string_replacetext)
|
||||
/*
|
||||
-> Locates an element/substring inside of a list or string
|
||||
@format: find(haystack, needle, start = 1, end = 0)
|
||||
|
||||
/*
|
||||
-> Locates an element/substring inside of a list or string
|
||||
@format: find(haystack, needle, start = 1, end = 0)
|
||||
@param haystack: the container to search
|
||||
@param needle: the element to search for
|
||||
@param start: the position to start in
|
||||
@param end: the position to end in
|
||||
|
||||
@param haystack: the container to search
|
||||
@param needle: the element to search for
|
||||
@param start: the position to start in
|
||||
@param end: the position to end in
|
||||
*/
|
||||
interpreter.SetProc("find", /proc/smartfind)
|
||||
|
||||
*/
|
||||
interpreter.SetProc("find", /proc/smartfind)
|
||||
/*
|
||||
-> Finds the length of a string or list
|
||||
@format: length(container)
|
||||
|
||||
/*
|
||||
-> Finds the length of a string or list
|
||||
@format: length(container)
|
||||
@param container: the list or container to measure
|
||||
|
||||
@param container: the list or container to measure
|
||||
*/
|
||||
interpreter.SetProc("length", /proc/smartlength)
|
||||
|
||||
*/
|
||||
interpreter.SetProc("length", /proc/smartlength)
|
||||
/* -- Clone functions, carried from default BYOND procs --- */
|
||||
|
||||
/* -- Clone functions, carried from default BYOND procs --- */
|
||||
// vector namespace
|
||||
interpreter.SetProc("vector", /proc/n_list)
|
||||
interpreter.SetProc("at", /proc/n_listpos)
|
||||
interpreter.SetProc("copy", /proc/n_listcopy)
|
||||
interpreter.SetProc("push_back", /proc/n_listadd)
|
||||
interpreter.SetProc("remove", /proc/n_listremove)
|
||||
interpreter.SetProc("cut", /proc/n_listcut)
|
||||
interpreter.SetProc("swap", /proc/n_listswap)
|
||||
interpreter.SetProc("insert", /proc/n_listinsert)
|
||||
|
||||
// vector namespace
|
||||
interpreter.SetProc("vector", /proc/n_list)
|
||||
interpreter.SetProc("at", /proc/n_listpos)
|
||||
interpreter.SetProc("copy", /proc/n_listcopy)
|
||||
interpreter.SetProc("push_back", /proc/n_listadd)
|
||||
interpreter.SetProc("remove", /proc/n_listremove)
|
||||
interpreter.SetProc("cut", /proc/n_listcut)
|
||||
interpreter.SetProc("swap", /proc/n_listswap)
|
||||
interpreter.SetProc("insert", /proc/n_listinsert)
|
||||
interpreter.SetProc("pick", /proc/n_pick)
|
||||
interpreter.SetProc("prob", /proc/prob_chance)
|
||||
interpreter.SetProc("substr", /proc/docopytext)
|
||||
|
||||
interpreter.SetProc("pick", /proc/n_pick)
|
||||
interpreter.SetProc("prob", /proc/prob_chance)
|
||||
interpreter.SetProc("substr", /proc/docopytext)
|
||||
// Donkie~
|
||||
// Strings
|
||||
interpreter.SetProc("lower", /proc/n_lower)
|
||||
interpreter.SetProc("upper", /proc/n_upper)
|
||||
interpreter.SetProc("explode", /proc/string_explode)
|
||||
interpreter.SetProc("repeat", /proc/n_repeat)
|
||||
interpreter.SetProc("reverse", /proc/n_reverse)
|
||||
interpreter.SetProc("tonum", /proc/n_str2num)
|
||||
|
||||
// Donkie~
|
||||
// Strings
|
||||
interpreter.SetProc("lower", /proc/n_lower)
|
||||
interpreter.SetProc("upper", /proc/n_upper)
|
||||
interpreter.SetProc("explode", /proc/string_explode)
|
||||
interpreter.SetProc("repeat", /proc/n_repeat)
|
||||
interpreter.SetProc("reverse", /proc/n_reverse)
|
||||
interpreter.SetProc("tonum", /proc/n_str2num)
|
||||
|
||||
// Numbers
|
||||
interpreter.SetProc("tostring", /proc/n_num2str)
|
||||
interpreter.SetProc("sqrt", /proc/n_sqrt)
|
||||
interpreter.SetProc("abs", /proc/n_abs)
|
||||
interpreter.SetProc("floor", /proc/n_floor)
|
||||
interpreter.SetProc("ceil", /proc/n_ceil)
|
||||
interpreter.SetProc("round", /proc/n_round)
|
||||
interpreter.SetProc("clamp", /proc/n_clamp)
|
||||
interpreter.SetProc("inrange", /proc/n_inrange)
|
||||
// End of Donkie~
|
||||
// Numbers
|
||||
interpreter.SetProc("tostring", /proc/n_num2str)
|
||||
interpreter.SetProc("sqrt", /proc/n_sqrt)
|
||||
interpreter.SetProc("abs", /proc/n_abs)
|
||||
interpreter.SetProc("floor", /proc/n_floor)
|
||||
interpreter.SetProc("ceil", /proc/n_ceil)
|
||||
interpreter.SetProc("round", /proc/n_round)
|
||||
interpreter.SetProc("clamp", /proc/n_clamp)
|
||||
interpreter.SetProc("inrange", /proc/n_inrange)
|
||||
// End of Donkie~
|
||||
|
||||
|
||||
// Run the compiled code
|
||||
interpreter.Run()
|
||||
// Run the compiled code
|
||||
interpreter.Run()
|
||||
|
||||
// Backwards-apply variables onto signal data
|
||||
/* sanitize EVERYTHING. fucking players can't be trusted with SHIT */
|
||||
// Backwards-apply variables onto signal data
|
||||
/* sanitize EVERYTHING. fucking players can't be trusted with SHIT */
|
||||
|
||||
signal.data["message"] = interpreter.GetVar("$content")
|
||||
signal.frequency = interpreter.GetVar("$freq")
|
||||
signal.data["message"] = interpreter.GetVar("$content")
|
||||
signal.frequency = interpreter.GetVar("$freq")
|
||||
|
||||
var/setname = ""
|
||||
var/obj/machinery/telecomms/server/S = signal.data["server"]
|
||||
if(interpreter.GetVar("$source") in S.stored_names)
|
||||
setname = interpreter.GetVar("$source")
|
||||
else
|
||||
setname = "<i>[interpreter.GetVar("$source")]</i>"
|
||||
var/setname = ""
|
||||
var/obj/machinery/telecomms/server/S = signal.data["server"]
|
||||
if(interpreter.GetVar("$source") in S.stored_names)
|
||||
setname = interpreter.GetVar("$source")
|
||||
else
|
||||
setname = "<i>[interpreter.GetVar("$source")]</i>"
|
||||
|
||||
if(signal.data["name"] != setname)
|
||||
signal.data["realname"] = setname
|
||||
signal.data["name"] = setname
|
||||
signal.data["job"] = interpreter.GetVar("$job")
|
||||
signal.data["reject"] = !(interpreter.GetVar("$pass")) // set reject to the opposite of $pass
|
||||
if(signal.data["name"] != setname)
|
||||
signal.data["realname"] = setname
|
||||
signal.data["name"] = setname
|
||||
signal.data["job"] = interpreter.GetVar("$job")
|
||||
signal.data["reject"] = !(interpreter.GetVar("$pass")) // set reject to the opposite of $pass
|
||||
|
||||
// If the message is invalid, just don't broadcast it!
|
||||
if(signal.data["message"] == "" || !signal.data["message"])
|
||||
signal.data["reject"] = 1
|
||||
// If the message is invalid, just don't broadcast it!
|
||||
if(signal.data["message"] == "" || !signal.data["message"])
|
||||
signal.data["reject"] = 1
|
||||
|
||||
/* -- Actual language proc code -- */
|
||||
|
||||
datum/signal
|
||||
/datum/signal/proc/mem(var/address, var/value)
|
||||
|
||||
proc/mem(var/address, var/value)
|
||||
|
||||
if(istext(address))
|
||||
var/obj/machinery/telecomms/server/S = data["server"]
|
||||
|
||||
if(!value && value != 0)
|
||||
return S.memory[address]
|
||||
|
||||
else
|
||||
S.memory[address] = value
|
||||
|
||||
|
||||
proc/tcombroadcast(var/message, var/freq, var/source, var/job)
|
||||
|
||||
var/datum/signal/newsign = new
|
||||
if(istext(address))
|
||||
var/obj/machinery/telecomms/server/S = data["server"]
|
||||
var/obj/item/device/radio/hradio = S.server_radio
|
||||
|
||||
if(!hradio)
|
||||
error("[src] has no radio.")
|
||||
return
|
||||
if(!value && value != 0)
|
||||
return S.memory[address]
|
||||
|
||||
if((!message || message == "") && message != 0)
|
||||
message = "*beep*"
|
||||
if(!source)
|
||||
source = "[html_encode(uppertext(S.id))]"
|
||||
hradio = new // sets the hradio as a radio intercom
|
||||
if(!freq)
|
||||
freq = PUB_FREQ
|
||||
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
|
||||
freq *= 10 // shift the decimal one place
|
||||
|
||||
if(!job)
|
||||
job = "?"
|
||||
|
||||
newsign.data["mob"] = null
|
||||
newsign.data["mobtype"] = /mob/living/carbon/human
|
||||
if(source in S.stored_names)
|
||||
newsign.data["name"] = source
|
||||
else
|
||||
newsign.data["name"] = "<i>[html_encode(uppertext(source))]</i>"
|
||||
newsign.data["realname"] = newsign.data["name"]
|
||||
newsign.data["job"] = job
|
||||
newsign.data["compression"] = 0
|
||||
newsign.data["message"] = message
|
||||
newsign.data["type"] = 2 // artificial broadcast
|
||||
if(!isnum(freq))
|
||||
freq = text2num(freq)
|
||||
newsign.frequency = freq
|
||||
|
||||
var/datum/radio_frequency/connection = radio_controller.return_frequency(freq)
|
||||
newsign.data["connection"] = connection
|
||||
S.memory[address] = value
|
||||
|
||||
|
||||
newsign.data["radio"] = hradio
|
||||
newsign.data["vmessage"] = message
|
||||
newsign.data["vname"] = source
|
||||
newsign.data["vmask"] = 0
|
||||
newsign.data["level"] = list()
|
||||
/datum/signal/proc/tcombroadcast(var/message, var/freq, var/source, var/job)
|
||||
|
||||
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
|
||||
var/datum/signal/newsign = new
|
||||
var/obj/machinery/telecomms/server/S = data["server"]
|
||||
var/obj/item/device/radio/hradio = S.server_radio
|
||||
|
||||
if(!hradio)
|
||||
error("[src] has no radio.")
|
||||
return
|
||||
|
||||
if((!message || message == "") && message != 0)
|
||||
message = "*beep*"
|
||||
if(!source)
|
||||
source = "[html_encode(uppertext(S.id))]"
|
||||
hradio = new // sets the hradio as a radio intercom
|
||||
if(!freq)
|
||||
freq = PUB_FREQ
|
||||
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
|
||||
freq *= 10 // shift the decimal one place
|
||||
|
||||
if(!job)
|
||||
job = "?"
|
||||
|
||||
newsign.data["mob"] = null
|
||||
newsign.data["mobtype"] = /mob/living/carbon/human
|
||||
if(source in S.stored_names)
|
||||
newsign.data["name"] = source
|
||||
else
|
||||
newsign.data["name"] = "<i>[html_encode(uppertext(source))]</i>"
|
||||
newsign.data["realname"] = newsign.data["name"]
|
||||
newsign.data["job"] = job
|
||||
newsign.data["compression"] = 0
|
||||
newsign.data["message"] = message
|
||||
newsign.data["type"] = 2 // artificial broadcast
|
||||
if(!isnum(freq))
|
||||
freq = text2num(freq)
|
||||
newsign.frequency = freq
|
||||
|
||||
var/datum/radio_frequency/connection = radio_controller.return_frequency(freq)
|
||||
newsign.data["connection"] = connection
|
||||
|
||||
|
||||
newsign.data["radio"] = hradio
|
||||
newsign.data["vmessage"] = message
|
||||
newsign.data["vname"] = source
|
||||
newsign.data["vmask"] = 0
|
||||
newsign.data["level"] = list()
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
/*
|
||||
//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)
|
||||
/proc/string_tolist(var/string)
|
||||
var/list/L = new/list()
|
||||
|
||||
var/i
|
||||
@@ -146,7 +146,7 @@ proc/string_tolist(var/string)
|
||||
|
||||
return L
|
||||
|
||||
proc/string_explode(var/string, var/separator)
|
||||
/proc/string_explode(var/string, var/separator)
|
||||
if(istext(string))
|
||||
if(istext(separator) && separator == "")
|
||||
return string_tolist(string)
|
||||
@@ -165,11 +165,11 @@ proc/string_explode(var/string, var/separator)
|
||||
|
||||
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)
|
||||
if(istext(string) && istext(separator))
|
||||
return splittext(string, separator)
|
||||
|
||||
proc/n_repeat(var/string, var/amount)
|
||||
/proc/n_repeat(var/string, var/amount)
|
||||
if(istext(string) && isnum(amount))
|
||||
var/i
|
||||
var/newstring = ""
|
||||
@@ -182,7 +182,7 @@ proc/n_repeat(var/string, var/amount)
|
||||
|
||||
return newstring
|
||||
|
||||
proc/n_reverse(var/string)
|
||||
/proc/n_reverse(var/string)
|
||||
if(istext(string))
|
||||
var/newstring = ""
|
||||
var/i
|
||||
@@ -194,44 +194,44 @@ proc/n_reverse(var/string)
|
||||
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.
|
||||
proc/n_str2num(var/string)
|
||||
/proc/n_str2num(var/string)
|
||||
if(istext(string))
|
||||
return text2num(string)
|
||||
|
||||
// Number shit
|
||||
proc/n_num2str(var/num)
|
||||
/proc/n_num2str(var/num)
|
||||
if(isnum(num))
|
||||
return num2text(num)
|
||||
|
||||
// Squareroot
|
||||
proc/n_sqrt(var/num)
|
||||
/proc/n_sqrt(var/num)
|
||||
if(isnum(num))
|
||||
return sqrt(num)
|
||||
|
||||
// Magnitude of num
|
||||
proc/n_abs(var/num)
|
||||
/proc/n_abs(var/num)
|
||||
if(isnum(num))
|
||||
return abs(num)
|
||||
|
||||
// Round down
|
||||
proc/n_floor(var/num)
|
||||
/proc/n_floor(var/num)
|
||||
if(isnum(num))
|
||||
return round(num)
|
||||
|
||||
// Round up
|
||||
proc/n_ceil(var/num)
|
||||
/proc/n_ceil(var/num)
|
||||
if(isnum(num))
|
||||
return round(num)+1
|
||||
|
||||
// Round to nearest integer
|
||||
proc/n_round(var/num)
|
||||
/proc/n_round(var/num)
|
||||
if(isnum(num))
|
||||
if(num-round(num)<0.5)
|
||||
return round(num)
|
||||
return n_ceil(num)
|
||||
|
||||
// Clamps N between min and max
|
||||
proc/n_clamp(var/num, var/min=-1, var/max=1)
|
||||
/proc/n_clamp(var/num, var/min=-1, var/max=1)
|
||||
if(isnum(num)&&isnum(min)&&isnum(max))
|
||||
if(num<=min)
|
||||
return min
|
||||
@@ -240,7 +240,7 @@ proc/n_clamp(var/num, var/min=-1, var/max=1)
|
||||
return num
|
||||
|
||||
// Returns 1 if N is inbetween Min and Max
|
||||
proc/n_inrange(var/num, var/min=-1, var/max=1)
|
||||
/proc/n_inrange(var/num, var/min=-1, var/max=1)
|
||||
if(isnum(num)&&isnum(min)&&isnum(max))
|
||||
return ((min <= num) && (num <= max))
|
||||
// END OF BY DONKIE :(
|
||||
|
||||
@@ -1,169 +1,167 @@
|
||||
/proc/isobject(x)
|
||||
return (istype(x, /datum) || istype(x, /list) || istype(x, /savefile) || istype(x, /client) || (x==world))
|
||||
return !(isnum(x) || istext(x))
|
||||
|
||||
/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))
|
||||
/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
|
||||
|
||||
/n_Interpreter/proc/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
|
||||
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())
|
||||
return
|
||||
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
|
||||
//Binary//
|
||||
//Comparison operators
|
||||
/n_Interpreter/proc/Equal(a, b) return a==b
|
||||
/n_Interpreter/proc/NotEqual(a, b) return a!=b //LogicalNot(Equal(a, b))
|
||||
/n_Interpreter/proc/Greater(a, b) return a>b
|
||||
/n_Interpreter/proc/Less(a, b) return a<b
|
||||
/n_Interpreter/proc/GreaterOrEqual(a, b) return a>=b
|
||||
/n_Interpreter/proc/LessOrEqual(a, b) return a<=b
|
||||
//Logical Operators
|
||||
/n_Interpreter/proc/LogicalAnd(a, b) return a&&b
|
||||
/n_Interpreter/proc/LogicalOr(a, b) return a||b
|
||||
/n_Interpreter/proc/LogicalXor(a, b) return (a||b) && !(a&&b)
|
||||
//Bitwise Operators
|
||||
/n_Interpreter/proc/BitwiseAnd(a, b) return a&b
|
||||
/n_Interpreter/proc/BitwiseOr(a, b) return a|b
|
||||
/n_Interpreter/proc/BitwiseXor(a, b) return a^b
|
||||
//Arithmetic Operators
|
||||
/n_Interpreter/proc/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
|
||||
/n_Interpreter/proc/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
|
||||
/n_Interpreter/proc/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
|
||||
/n_Interpreter/proc/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
|
||||
/n_Interpreter/proc/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
|
||||
/n_Interpreter/proc/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
|
||||
//Unary//
|
||||
/n_Interpreter/proc/Minus(a) return -a
|
||||
/n_Interpreter/proc/LogicalNot(a) return !a
|
||||
/n_Interpreter/proc/BitwiseNot(a) return ~a
|
||||
@@ -6,9 +6,7 @@
|
||||
Class: n_Interpreter
|
||||
Procedures allowing for interaction with the script that is being run by the interpreter object.
|
||||
*/
|
||||
|
||||
/n_Interpreter
|
||||
proc
|
||||
|
||||
/*
|
||||
Proc: Load
|
||||
@@ -17,22 +15,22 @@
|
||||
Parameters:
|
||||
program - A <GlobalBlock> object which represents the script's global scope.
|
||||
*/
|
||||
Load(node/BlockDefinition/GlobalBlock/program)
|
||||
ASSERT(program)
|
||||
src.program = program
|
||||
CreateGlobalScope()
|
||||
/n_Interpreter/proc/Load(node/BlockDefinition/GlobalBlock/program)
|
||||
ASSERT(program)
|
||||
src.program = program
|
||||
CreateGlobalScope()
|
||||
|
||||
/*
|
||||
Proc: Run
|
||||
Runs the script.
|
||||
*/
|
||||
Run()
|
||||
cur_recursion = 0 // reset recursion
|
||||
cur_statements = 0 // reset CPU tracking
|
||||
alertadmins = 0
|
||||
/n_Interpreter/proc/Run()
|
||||
cur_recursion = 0 // reset recursion
|
||||
cur_statements = 0 // reset CPU tracking
|
||||
alertadmins = 0
|
||||
|
||||
ASSERT(src.program)
|
||||
RunBlock(src.program)
|
||||
ASSERT(src.program)
|
||||
RunBlock(src.program)
|
||||
|
||||
/*
|
||||
Proc: SetVar
|
||||
@@ -45,11 +43,11 @@
|
||||
See Also:
|
||||
- <Block.SetVar()>
|
||||
*/
|
||||
SetVar(name, value)
|
||||
if(!istext(name))
|
||||
//CRASH("Invalid variable name")
|
||||
return
|
||||
AssignVariable(name, value)
|
||||
/n_Interpreter/proc/SetVar(name, value)
|
||||
if(!istext(name))
|
||||
//CRASH("Invalid variable name")
|
||||
return
|
||||
AssignVariable(name, value)
|
||||
|
||||
/*
|
||||
Proc: SetProc
|
||||
@@ -61,40 +59,40 @@
|
||||
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
|
||||
/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
|
||||
return
|
||||
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?
|
||||
/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.
|
||||
*/
|
||||
ProcExists(name)
|
||||
return globalScope.functions.Find(name)
|
||||
/n_Interpreter/proc/ProcExists(name)
|
||||
return globalScope.functions.Find(name)
|
||||
|
||||
/*
|
||||
Proc: GetVar
|
||||
@@ -103,12 +101,12 @@
|
||||
See Also:
|
||||
- <VarExists()>
|
||||
*/
|
||||
GetVar(name)
|
||||
if(!VarExists(name))
|
||||
//CRASH("No variable named '[name]'.")
|
||||
return
|
||||
var/x = globalScope.variables[name]
|
||||
return Eval(x)
|
||||
/n_Interpreter/proc/GetVar(name)
|
||||
if(!VarExists(name))
|
||||
//CRASH("No variable named '[name]'.")
|
||||
return
|
||||
var/x = globalScope.variables[name]
|
||||
return Eval(x)
|
||||
|
||||
/*
|
||||
Proc: CallProc
|
||||
@@ -118,19 +116,19 @@
|
||||
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]'.")
|
||||
/n_Interpreter/proc/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
|
||||
@@ -139,4 +137,4 @@
|
||||
See Also:
|
||||
- <runtimeError>
|
||||
*/
|
||||
HandleError(runtimeError/e)
|
||||
/n_Interpreter/proc/HandleError(runtimeError/e)
|
||||
@@ -14,301 +14,296 @@
|
||||
#define BREAKING 2
|
||||
#define CONTINUING 4
|
||||
/n_Interpreter
|
||||
var
|
||||
scope
|
||||
curScope
|
||||
globalScope
|
||||
node
|
||||
BlockDefinition/program
|
||||
statement/FunctionDefinition/curFunction
|
||||
stack
|
||||
scopes = new()
|
||||
functions = new()
|
||||
var/scope/curScope
|
||||
var/scope/globalScope
|
||||
var/node/BlockDefinition/program
|
||||
var/node/statement/FunctionDefinition/curFunction
|
||||
var/stack/scopes = new()
|
||||
var/stack/functions = new()
|
||||
|
||||
datum/container // associated container for interpeter
|
||||
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=1000 // 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=50 // 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)
|
||||
/n_Interpreter/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)
|
||||
/n_Interpreter/proc/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
|
||||
/n_Interpreter/proc/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
|
||||
/n_Interpreter/proc/CreateGlobalScope()
|
||||
scopes.Clear()
|
||||
var/scope/S = new(program, null)
|
||||
globalScope = S
|
||||
return S
|
||||
|
||||
/*
|
||||
Proc: RunBlock
|
||||
Runs each statement in a block of code.
|
||||
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
|
||||
/n_Interpreter/proc/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
|
||||
|
||||
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
|
||||
CreateScope(Block)
|
||||
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
|
||||
if(!persist)
|
||||
CreateGlobalScope()
|
||||
curScope = globalScope
|
||||
RaiseError(new/runtimeError/UnknownInstruction())
|
||||
if(status)
|
||||
break
|
||||
|
||||
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()
|
||||
curScope = scopes.Pop()
|
||||
|
||||
/*
|
||||
Proc: RunFunction
|
||||
Runs a function block or a proc with the arguments specified in the script.
|
||||
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
|
||||
/n_Interpreter/proc/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
|
||||
// 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)
|
||||
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
|
||||
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))
|
||||
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
|
||||
// RaiseError(new/runtimeError/UnknownInstruction())
|
||||
// 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.
|
||||
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)
|
||||
/n_Interpreter/proc/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.
|
||||
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
|
||||
/n_Interpreter/proc/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.
|
||||
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
|
||||
/n_Interpreter/proc/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>.
|
||||
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))
|
||||
/n_Interpreter/proc/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.
|
||||
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))
|
||||
/n_Interpreter/proc/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
|
||||
/n_Interpreter/proc/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
|
||||
/n_Interpreter/proc/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.
|
||||
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
|
||||
/n_Interpreter/proc/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
|
||||
|
||||
|
||||
@@ -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
|
||||
/scope/
|
||||
var/scope/parent = null
|
||||
var/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()
|
||||
.=..()
|
||||
/scope/New(node/BlockDefinition/B, scope/parent)
|
||||
src.block = B
|
||||
src.parent = parent
|
||||
src.variables = B.initial_variables.Copy()
|
||||
src.functions = B.functions.Copy()
|
||||
.=..()
|
||||
@@ -14,69 +14,87 @@ var/const //Ascii values of characters
|
||||
/*
|
||||
Class: n_scriptOptions
|
||||
*/
|
||||
n_scriptOptions
|
||||
proc
|
||||
CanStartID(char) //returns true if the character can start a variable, function, or keyword name (by default letters or an underscore)
|
||||
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
|
||||
/n_scriptOptions/proc/CanStartID(char) //returns true if the character can start a variable, function, or keyword name (by default letters or an underscore)
|
||||
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)
|
||||
if(!isnum(char))char=text2ascii(char)
|
||||
return CanStartID(char) || IsDigit(char)
|
||||
/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)
|
||||
return CanStartID(char) || IsDigit(char)
|
||||
|
||||
IsDigit(char)
|
||||
if(!isnum(char))char=text2ascii(char)
|
||||
return char in ascii_ZERO to ascii_NINE
|
||||
/n_scriptOptions/proc/IsDigit(char)
|
||||
if(!isnum(char))char=text2ascii(char)
|
||||
return char in ascii_ZERO to ascii_NINE
|
||||
|
||||
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
|
||||
/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
|
||||
An implementation of <n_scriptOptions> for the n_Script language.
|
||||
*/
|
||||
nS_Options
|
||||
var
|
||||
list
|
||||
symbols = list("(", ")", "\[", "]", ";", ",", "{", "}") //scanner - Characters that can be in symbols
|
||||
/n_scriptOptions/nS_Options
|
||||
var/list/symbols = list("(", ")", "\[", "]", ";", ",", "{", "}") //scanner - Characters that can be in symbols
|
||||
/*
|
||||
Var: keywords
|
||||
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.
|
||||
Var: keywords
|
||||
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.
|
||||
*/
|
||||
keywords = list("if" = /n_Keyword/nS_Keyword/kwIf, "else" = /n_Keyword/nS_Keyword/kwElse, \
|
||||
"while" = /n_Keyword/nS_Keyword/kwWhile, "break" = /n_Keyword/nS_Keyword/kwBreak, \
|
||||
"continue" = /n_Keyword/nS_Keyword/kwContinue, \
|
||||
"return" = /n_Keyword/nS_Keyword/kwReturn, "def" = /n_Keyword/nS_Keyword/kwDef)
|
||||
var/list/keywords = list(
|
||||
"if" = /n_Keyword/nS_Keyword/kwIf,
|
||||
"else" = /n_Keyword/nS_Keyword/kwElse,
|
||||
"while" = /n_Keyword/nS_Keyword/kwWhile,
|
||||
"break" = /n_Keyword/nS_Keyword/kwBreak,
|
||||
"continue" = /n_Keyword/nS_Keyword/kwContinue,
|
||||
"return" = /n_Keyword/nS_Keyword/kwReturn,
|
||||
"def" = /n_Keyword/nS_Keyword/kwDef
|
||||
)
|
||||
|
||||
list
|
||||
assign_operators=list("=" = null, "&=" = "&",
|
||||
"|=" = "|", "`=" = "`",
|
||||
"+=" = "+", "-=" = "-",
|
||||
"*=" = "*", "/=" = "/",
|
||||
"^=" = "^",
|
||||
"%=" = "%")
|
||||
var/list/assign_operators = list(
|
||||
"=" = null,
|
||||
"&=" = "&",
|
||||
"|=" = "|",
|
||||
"`=" = "`",
|
||||
"+=" = "+",
|
||||
"-=" = "-",
|
||||
"*=" = "*",
|
||||
"/=" = "/",
|
||||
"^=" = "^",
|
||||
"%=" = "%"
|
||||
)
|
||||
|
||||
unary_operators =list("!" = /node/expression/operator/unary/LogicalNot, "~" = /node/expression/operator/unary/BitwiseNot,
|
||||
"-" = /node/expression/operator/unary/Minus)
|
||||
var/list/unary_operators =list(
|
||||
"!" = /node/expression/operator/unary/LogicalNot,
|
||||
"~" = /node/expression/operator/unary/BitwiseNot,
|
||||
"-" = /node/expression/operator/unary/Minus
|
||||
)
|
||||
|
||||
binary_operators=list("==" = /node/expression/operator/binary/Equal, "!=" = /node/expression/operator/binary/NotEqual,
|
||||
">" = /node/expression/operator/binary/Greater, "<" = /node/expression/operator/binary/Less,
|
||||
">=" = /node/expression/operator/binary/GreaterOrEqual,"<=" = /node/expression/operator/binary/LessOrEqual,
|
||||
"&&" = /node/expression/operator/binary/LogicalAnd, "||" = /node/expression/operator/binary/LogicalOr,
|
||||
"&" = /node/expression/operator/binary/BitwiseAnd, "|" = /node/expression/operator/binary/BitwiseOr,
|
||||
"`" = /node/expression/operator/binary/BitwiseXor, "+" = /node/expression/operator/binary/Add,
|
||||
"-" = /node/expression/operator/binary/Subtract, "*" = /node/expression/operator/binary/Multiply,
|
||||
"/" = /node/expression/operator/binary/Divide, "^" = /node/expression/operator/binary/Power,
|
||||
"%" = /node/expression/operator/binary/Modulo)
|
||||
var/list/binary_operators=list(
|
||||
"==" = /node/expression/operator/binary/Equal,
|
||||
"!=" = /node/expression/operator/binary/NotEqual,
|
||||
">" = /node/expression/operator/binary/Greater,
|
||||
"<" = /node/expression/operator/binary/Less,
|
||||
">=" = /node/expression/operator/binary/GreaterOrEqual,
|
||||
"<=" = /node/expression/operator/binary/LessOrEqual,
|
||||
"&&" = /node/expression/operator/binary/LogicalAnd,
|
||||
"||" = /node/expression/operator/binary/LogicalOr,
|
||||
"&" = /node/expression/operator/binary/BitwiseAnd,
|
||||
"|" = /node/expression/operator/binary/BitwiseOr,
|
||||
"`" = /node/expression/operator/binary/BitwiseXor,
|
||||
"+" = /node/expression/operator/binary/Add,
|
||||
"-" = /node/expression/operator/binary/Subtract,
|
||||
"*" = /node/expression/operator/binary/Multiply,
|
||||
"/" = /node/expression/operator/binary/Divide,
|
||||
"^" = /node/expression/operator/binary/Power,
|
||||
"%" = /node/expression/operator/binary/Modulo)
|
||||
|
||||
New()
|
||||
.=..()
|
||||
for(var/O in assign_operators+binary_operators+unary_operators)
|
||||
if(!symbols.Find(O)) symbols+=O
|
||||
/n_scriptOptions/nS_Options/New()
|
||||
.=..()
|
||||
for(var/O in assign_operators+binary_operators+unary_operators)
|
||||
if(!symbols.Find(O)) symbols+=O
|
||||
@@ -19,294 +19,292 @@
|
||||
Class: nS_Parser
|
||||
*/
|
||||
/n_Parser/nS_Parser
|
||||
var
|
||||
/*
|
||||
Var: expecting
|
||||
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.
|
||||
*/
|
||||
expecting=VALUE
|
||||
var/expecting=VALUE
|
||||
|
||||
proc
|
||||
/*
|
||||
Proc: Precedence
|
||||
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)
|
||||
if(istype(top))
|
||||
top=top.precedence
|
||||
if(istype(input))
|
||||
input=input:precedence
|
||||
if(top>=input)
|
||||
return REDUCE
|
||||
return SHIFT
|
||||
/n_Parser/nS_Parser/proc/Precedence(node/expression/operator/top, node/expression/operator/input)
|
||||
if(istype(top))
|
||||
top=top.precedence
|
||||
if(istype(input))
|
||||
input=input:precedence
|
||||
if(top>=input)
|
||||
return REDUCE
|
||||
return SHIFT
|
||||
|
||||
/*
|
||||
Proc: GetExpression
|
||||
Takes a token expected to represent a value and returns an <expression> node.
|
||||
Proc: GetExpression
|
||||
Takes a token expected to represent a value and returns an <expression> node.
|
||||
*/
|
||||
GetExpression(token/T)
|
||||
if(!T) return
|
||||
if(istype(T, /node/expression))
|
||||
return T
|
||||
switch(T.type)
|
||||
if(/token/word)
|
||||
return new/node/expression/value/variable(T.value)
|
||||
if(/token/accessor)
|
||||
var/token/accessor/A=T
|
||||
var/node/expression/value/variable/E//=new(A.member)
|
||||
var/stack/S=new()
|
||||
while(istype(A.object, /token/accessor))
|
||||
S.Push(A)
|
||||
A=A.object
|
||||
ASSERT(istext(A.object))
|
||||
/n_Parser/nS_Parser/proc/GetExpression(token/T)
|
||||
if(!T) return
|
||||
if(istype(T, /node/expression))
|
||||
return T
|
||||
switch(T.type)
|
||||
if(/token/word)
|
||||
return new/node/expression/value/variable(T.value)
|
||||
if(/token/accessor)
|
||||
var/token/accessor/A=T
|
||||
var/node/expression/value/variable/E//=new(A.member)
|
||||
var/stack/S=new()
|
||||
while(istype(A.object, /token/accessor))
|
||||
S.Push(A)
|
||||
A=A.object
|
||||
ASSERT(istext(A.object))
|
||||
|
||||
while(A)
|
||||
var/node/expression/value/variable/V=new()
|
||||
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
|
||||
while(A)
|
||||
var/node/expression/value/variable/V=new()
|
||||
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)
|
||||
return new/node/expression/value/literal(T.value)
|
||||
if(/token/number, /token/string)
|
||||
return new/node/expression/value/literal(T.value)
|
||||
|
||||
/*
|
||||
Proc: GetOperator
|
||||
Gets a path related to a token or string and returns an instance of the given type. This is used to get an instance of either a binary or unary
|
||||
operator from a token.
|
||||
Proc: GetOperator
|
||||
Gets a path related to a token or string and returns an instance of the given type. This is used to get an instance of either a binary or unary
|
||||
operator from a token.
|
||||
|
||||
Parameters:
|
||||
O - The input value. If this is a token, O is reset to the token's value.
|
||||
When O is a string and is in L, its associated value is used as the path to instantiate.
|
||||
type - The desired type of the returned object.
|
||||
L - The list in which to search for O.
|
||||
Parameters:
|
||||
O - The input value. If this is a token, O is reset to the token's value.
|
||||
When O is a string and is in L, its associated value is used as the path to instantiate.
|
||||
type - The desired type of the returned object.
|
||||
L - The list in which to search for O.
|
||||
|
||||
See Also:
|
||||
- <GetBinaryOperator()>
|
||||
- <GetUnaryOperator()>
|
||||
See Also:
|
||||
- <GetBinaryOperator()>
|
||||
- <GetUnaryOperator()>
|
||||
*/
|
||||
GetOperator(O, type=/node/expression/operator, L[])
|
||||
if(istype(O, type)) return O //O is already the desired type
|
||||
if(istype(O, /token)) O=O:value //sets O to text
|
||||
if(istext(O)) //sets O to path
|
||||
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
|
||||
/n_Parser/nS_Parser/proc/GetOperator(O, type=/node/expression/operator, L[])
|
||||
if(istype(O, type)) return O //O is already the desired type
|
||||
if(istype(O, /token)) O=O:value //sets O to text
|
||||
if(istext(O)) //sets O to path
|
||||
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
|
||||
Uses <GetOperator()> to search for an instance of a binary operator type with which the given string is associated. For example, if
|
||||
O is set to "+", an <Add> node is returned.
|
||||
Proc: GetBinaryOperator
|
||||
Uses <GetOperator()> to search for an instance of a binary operator type with which the given string is associated. For example, if
|
||||
O is set to "+", an <Add> node is returned.
|
||||
|
||||
See Also:
|
||||
- <GetOperator()>
|
||||
- <GetUnaryOperator()>
|
||||
See Also:
|
||||
- <GetOperator()>
|
||||
- <GetUnaryOperator()>
|
||||
*/
|
||||
GetBinaryOperator(O)
|
||||
return GetOperator(O, /node/expression/operator/binary, options.binary_operators)
|
||||
/n_Parser/nS_Parser/proc/GetBinaryOperator(O)
|
||||
return GetOperator(O, /node/expression/operator/binary, options.binary_operators)
|
||||
|
||||
/*
|
||||
Proc: GetUnaryOperator
|
||||
Uses <GetOperator()> to search for an instance of a unary operator type with which the given string is associated. For example, if
|
||||
O is set to "!", a <LogicalNot> node is returned.
|
||||
Proc: GetUnaryOperator
|
||||
Uses <GetOperator()> to search for an instance of a unary operator type with which the given string is associated. For example, if
|
||||
O is set to "!", a <LogicalNot> node is returned.
|
||||
|
||||
See Also:
|
||||
- <GetOperator()>
|
||||
- <GetBinaryOperator()>
|
||||
See Also:
|
||||
- <GetOperator()>
|
||||
- <GetBinaryOperator()>
|
||||
*/
|
||||
GetUnaryOperator(O)
|
||||
return GetOperator(O, /node/expression/operator/unary, options.unary_operators)
|
||||
/n_Parser/nS_Parser/proc/GetUnaryOperator(O)
|
||||
return GetOperator(O, /node/expression/operator/unary, options.unary_operators)
|
||||
|
||||
/*
|
||||
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
|
||||
of the val stack.
|
||||
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
|
||||
of the val stack.
|
||||
*/
|
||||
Reduce(stack/opr, stack/val)
|
||||
var/node/expression/operator/O=opr.Pop()
|
||||
if(!O) return
|
||||
if(!istype(O))
|
||||
errors+=new/scriptError("Error reducing expression - invalid operator.")
|
||||
return
|
||||
//Take O and assign its operands, popping one or two values from the val stack
|
||||
//depending on whether O is a binary or unary operator.
|
||||
if(istype(O, /node/expression/operator/binary))
|
||||
var/node/expression/operator/binary/B=O
|
||||
B.exp2=val.Pop()
|
||||
B.exp =val.Pop()
|
||||
val.Push(B)
|
||||
else
|
||||
O.exp=val.Pop()
|
||||
val.Push(O)
|
||||
/n_Parser/nS_Parser/proc/Reduce(stack/opr, stack/val)
|
||||
var/node/expression/operator/O=opr.Pop()
|
||||
if(!O) return
|
||||
if(!istype(O))
|
||||
errors+=new/scriptError("Error reducing expression - invalid operator.")
|
||||
return
|
||||
//Take O and assign its operands, popping one or two values from the val stack
|
||||
//depending on whether O is a binary or unary operator.
|
||||
if(istype(O, /node/expression/operator/binary))
|
||||
var/node/expression/operator/binary/B=O
|
||||
B.exp2=val.Pop()
|
||||
B.exp =val.Pop()
|
||||
val.Push(B)
|
||||
else
|
||||
O.exp=val.Pop()
|
||||
val.Push(O)
|
||||
|
||||
/*
|
||||
Proc: EndOfExpression
|
||||
Returns true if the current token represents the end of an expression.
|
||||
Proc: EndOfExpression
|
||||
Returns true if the current token represents the end of an expression.
|
||||
|
||||
Parameters:
|
||||
end - A list of values to compare the current token to.
|
||||
Parameters:
|
||||
end - A list of values to compare the current token to.
|
||||
*/
|
||||
EndOfExpression(end[])
|
||||
if(!curToken)
|
||||
return 1
|
||||
if(istype(curToken, /token/symbol) && end.Find(curToken.value))
|
||||
return 1
|
||||
if(istype(curToken, /token/end) && end.Find(/token/end))
|
||||
return 1
|
||||
return 0
|
||||
/n_Parser/nS_Parser/proc/EndOfExpression(end[])
|
||||
if(!curToken)
|
||||
return 1
|
||||
if(istype(curToken, /token/symbol) && end.Find(curToken.value))
|
||||
return 1
|
||||
if(istype(curToken, /token/end) && end.Find(/token/end))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/*
|
||||
Proc: ParseExpression
|
||||
Uses the Shunting-yard algorithm to parse expressions.
|
||||
Proc: ParseExpression
|
||||
Uses the Shunting-yard algorithm to parse expressions.
|
||||
|
||||
Notes:
|
||||
- When an opening parenthesis is found, then <ParseParenExpression()> is called to handle it.
|
||||
- The <expecting> variable helps distinguish unary operators from binary operators (for cases like the - operator, which can be either).
|
||||
Notes:
|
||||
- When an opening parenthesis is found, then <ParseParenExpression()> is called to handle it.
|
||||
- The <expecting> variable helps distinguish unary operators from binary operators (for cases like the - operator, which can be either).
|
||||
|
||||
Articles:
|
||||
- <http://epaperpress.com/oper/>
|
||||
- <http://en.wikipedia.org/wiki/Shunting-yard_algorithm>
|
||||
Articles:
|
||||
- <http://epaperpress.com/oper/>
|
||||
- <http://en.wikipedia.org/wiki/Shunting-yard_algorithm>
|
||||
|
||||
See Also:
|
||||
- <ParseFunctionExpression()>
|
||||
- <ParseParenExpression()>
|
||||
- <ParseParamExpression()>
|
||||
See Also:
|
||||
- <ParseFunctionExpression()>
|
||||
- <ParseParenExpression()>
|
||||
- <ParseParamExpression()>
|
||||
*/
|
||||
ParseExpression(list/end=list(/token/end), list/ErrChars=list("{", "}"))
|
||||
var/stack/opr=new
|
||||
var/stack/val=new
|
||||
src.expecting=VALUE
|
||||
while(TRUE)
|
||||
if(EndOfExpression(end))
|
||||
break
|
||||
if(istype(curToken, /token/symbol) && ErrChars.Find(curToken.value))
|
||||
errors+=new/scriptError/BadToken(curToken)
|
||||
break
|
||||
/n_Parser/nS_Parser/proc/ParseExpression(list/end=list(/token/end), list/ErrChars=list("{", "}"))
|
||||
var/stack/opr=new
|
||||
var/stack/val=new
|
||||
src.expecting=VALUE
|
||||
while(TRUE)
|
||||
if(EndOfExpression(end))
|
||||
break
|
||||
if(istype(curToken, /token/symbol) && ErrChars.Find(curToken.value))
|
||||
errors+=new/scriptError/BadToken(curToken)
|
||||
break
|
||||
|
||||
|
||||
if(index>tokens.len) //End of File
|
||||
errors+=new/scriptError/EndOfFile()
|
||||
break
|
||||
var/token/ntok
|
||||
if(index+1<=tokens.len)
|
||||
ntok=tokens[index+1]
|
||||
if(index>tokens.len) //End of File
|
||||
errors+=new/scriptError/EndOfFile()
|
||||
break
|
||||
var/token/ntok
|
||||
if(index+1<=tokens.len)
|
||||
ntok=tokens[index+1]
|
||||
|
||||
if(istype(curToken, /token/symbol) && curToken.value=="(") //Parse parentheses expression
|
||||
if(expecting!=VALUE)
|
||||
errors+=new/scriptError/ExpectedToken("operator", curToken)
|
||||
NextToken()
|
||||
continue
|
||||
val.Push(ParseParenExpression())
|
||||
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.
|
||||
if(src.expecting==OPERATOR)
|
||||
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
|
||||
Reduce(opr, val)
|
||||
continue
|
||||
opr.Push(curOperator)
|
||||
src.expecting=VALUE
|
||||
else if(ntok && ntok.value=="(" && istype(ntok, /token/symbol)\
|
||||
&& istype(curToken, /token/word)) //Parse function call
|
||||
var/token/preToken=curToken
|
||||
var/old_expect=src.expecting
|
||||
var/fex=ParseFunctionExpression()
|
||||
if(old_expect!=VALUE)
|
||||
errors+=new/scriptError/ExpectedToken("operator", preToken)
|
||||
NextToken()
|
||||
continue
|
||||
val.Push(fex)
|
||||
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)
|
||||
if(istype(curToken, /token/symbol) && curToken.value=="(") //Parse parentheses expression
|
||||
if(expecting!=VALUE)
|
||||
errors+=new/scriptError/ExpectedToken("operator", curToken)
|
||||
NextToken()
|
||||
continue
|
||||
val.Push(ParseParenExpression())
|
||||
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.
|
||||
if(src.expecting==OPERATOR)
|
||||
curOperator=GetBinaryOperator(curToken)
|
||||
if(!curOperator)
|
||||
errors+=new/scriptError/ExpectedToken("operator", curToken)
|
||||
NextToken()
|
||||
continue
|
||||
else
|
||||
if(expecting!=VALUE)
|
||||
errors+=new/scriptError/ExpectedToken("operator", curToken)
|
||||
NextToken()
|
||||
continue
|
||||
val.Push(GetExpression(curToken))
|
||||
src.expecting=OPERATOR
|
||||
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
|
||||
Reduce(opr, val)
|
||||
continue
|
||||
opr.Push(curOperator)
|
||||
src.expecting=VALUE
|
||||
else if(ntok && ntok.value=="(" && istype(ntok, /token/symbol)\
|
||||
&& istype(curToken, /token/word)) //Parse function call
|
||||
var/token/preToken=curToken
|
||||
var/old_expect=src.expecting
|
||||
var/fex=ParseFunctionExpression()
|
||||
if(old_expect!=VALUE)
|
||||
errors+=new/scriptError/ExpectedToken("operator", preToken)
|
||||
NextToken()
|
||||
continue
|
||||
val.Push(fex)
|
||||
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()
|
||||
continue
|
||||
else
|
||||
if(expecting!=VALUE)
|
||||
errors+=new/scriptError/ExpectedToken("operator", curToken)
|
||||
NextToken()
|
||||
continue
|
||||
val.Push(GetExpression(curToken))
|
||||
src.expecting=OPERATOR
|
||||
NextToken()
|
||||
|
||||
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/node/N=val.Pop()
|
||||
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/node/N=val.Pop()
|
||||
errors+=new/scriptError("Error parsing expression. Unexpected value left on stack: [N.ToString()].")
|
||||
return null
|
||||
|
||||
/*
|
||||
Proc: ParseFunctionExpression
|
||||
Parses a function call inside of an expression.
|
||||
Proc: ParseFunctionExpression
|
||||
Parses a function call inside of an expression.
|
||||
|
||||
See Also:
|
||||
- <ParseExpression()>
|
||||
See Also:
|
||||
- <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
|
||||
/n_Parser/nS_Parser/proc/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
|
||||
|
||||
while(TRUE)
|
||||
loops++
|
||||
if(loops>=1000)
|
||||
CRASH("Something TERRIBLE has gone wrong in ParseFunctionExpression ;__;")
|
||||
while(TRUE)
|
||||
loops++
|
||||
if(loops>=1000)
|
||||
CRASH("Something TERRIBLE has gone wrong in ParseFunctionExpression ;__;")
|
||||
|
||||
if(istype(curToken, /token/symbol) && curToken.value==")")
|
||||
return exp
|
||||
exp.parameters+=ParseParamExpression()
|
||||
if(curToken.value==","&&istype(curToken, /token/symbol))NextToken() //skip comma
|
||||
if(istype(curToken, /token/end)) //Prevents infinite loop...
|
||||
errors+=new/scriptError/ExpectedToken(")")
|
||||
return exp
|
||||
if(istype(curToken, /token/symbol) && curToken.value==")")
|
||||
return exp
|
||||
exp.parameters+=ParseParamExpression()
|
||||
if(curToken.value==","&&istype(curToken, /token/symbol))NextToken() //skip comma
|
||||
if(istype(curToken, /token/end)) //Prevents infinite loop...
|
||||
errors+=new/scriptError/ExpectedToken(")")
|
||||
return exp
|
||||
|
||||
/*
|
||||
Proc: ParseParenExpression
|
||||
Parses an expression that ends with a close parenthesis. This is used for parsing expressions inside of parentheses.
|
||||
Proc: ParseParenExpression
|
||||
Parses an expression that ends with a close parenthesis. This is used for parsing expressions inside of parentheses.
|
||||
|
||||
See Also:
|
||||
- <ParseExpression()>
|
||||
See Also:
|
||||
- <ParseExpression()>
|
||||
*/
|
||||
ParseParenExpression()
|
||||
if(!CheckToken("(", /token/symbol))
|
||||
return
|
||||
return new/node/expression/operator/unary/group(ParseExpression(list(")")))
|
||||
/n_Parser/nS_Parser/proc/ParseParenExpression()
|
||||
if(!CheckToken("(", /token/symbol))
|
||||
return
|
||||
return new/node/expression/operator/unary/group(ParseExpression(list(")")))
|
||||
|
||||
/*
|
||||
Proc: ParseParamExpression
|
||||
Parses an expression that ends with either a comma or close parenthesis. This is used for parsing the parameters passed to a function call.
|
||||
Proc: ParseParamExpression
|
||||
Parses an expression that ends with either a comma or close parenthesis. This is used for parsing the parameters passed to a function call.
|
||||
|
||||
See Also:
|
||||
- <ParseExpression()>
|
||||
See Also:
|
||||
- <ParseExpression()>
|
||||
*/
|
||||
ParseParamExpression()
|
||||
return ParseExpression(list(",", ")"))
|
||||
/n_Parser/nS_Parser/proc/ParseParamExpression()
|
||||
return ParseExpression(list(",", ")"))
|
||||
@@ -12,16 +12,16 @@ var/const/KW_WARN = 3 //Warning
|
||||
var/const/Class: n_Keyword
|
||||
var/const/Represents a special statement in the code triggered by a keyword.
|
||||
*/
|
||||
/n_Keyword
|
||||
New(inline=0)
|
||||
src.inline=inline
|
||||
return ..()
|
||||
|
||||
/*
|
||||
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).
|
||||
*/
|
||||
/n_Keyword
|
||||
var/inline
|
||||
/n_Keyword/New(inline=FALSE)
|
||||
src.inline=inline
|
||||
return ..()
|
||||
|
||||
/*
|
||||
Proc: Parse
|
||||
@@ -31,7 +31,7 @@ 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
|
||||
to its AST.
|
||||
*/
|
||||
proc/Parse(n_Parser/parser)
|
||||
/n_Keyword/proc/Parse(n_Parser/parser)
|
||||
|
||||
/*
|
||||
Class: nS_Keyword
|
||||
@@ -43,124 +43,116 @@ var/const/Represents a special statement in the code triggered by a keyword.
|
||||
<n_Keyword.Parse()> proc.
|
||||
*/
|
||||
//
|
||||
nS_Keyword
|
||||
New(inline=0)
|
||||
if(inline)
|
||||
qdel(src)
|
||||
/n_Keyword/nS_Keyword/New(inline=0)
|
||||
if(inline)
|
||||
qdel(src)
|
||||
|
||||
kwReturn
|
||||
Parse(n_Parser/nS_Parser/parser)
|
||||
.=KW_PASS
|
||||
if(istype(parser.curBlock, /node/BlockDefinition/GlobalBlock))
|
||||
parser.errors+=new/scriptError/BadReturn(parser.curToken)
|
||||
. = KW_WARN
|
||||
var/node/statement/ReturnStatement/stmt=new
|
||||
parser.NextToken() //skip 'return' token
|
||||
stmt.value=parser.ParseExpression()
|
||||
parser.curBlock.statements+=stmt
|
||||
/n_Keyword/nS_Keyword/kwReturn/Parse(n_Parser/nS_Parser/parser)
|
||||
.=KW_PASS
|
||||
if(istype(parser.curBlock, /node/BlockDefinition/GlobalBlock))
|
||||
parser.errors+=new/scriptError/BadReturn(parser.curToken)
|
||||
. = KW_WARN
|
||||
var/node/statement/ReturnStatement/stmt=new
|
||||
parser.NextToken() //skip 'return' token
|
||||
stmt.value=parser.ParseExpression()
|
||||
parser.curBlock.statements+=stmt
|
||||
|
||||
kwIf
|
||||
Parse(n_Parser/nS_Parser/parser)
|
||||
.=KW_PASS
|
||||
var/node/statement/IfStatement/stmt=new
|
||||
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)
|
||||
/n_Keyword/nS_Keyword/kwIf/Parse(n_Parser/nS_Parser/parser)
|
||||
.=KW_PASS
|
||||
var/node/statement/IfStatement/stmt=new
|
||||
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)
|
||||
|
||||
kwElse
|
||||
Parse(n_Parser/nS_Parser/parser)
|
||||
.=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)
|
||||
/n_Keyword/nS_Keyword/kwElse/Parse(n_Parser/nS_Parser/parser)
|
||||
.=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
|
||||
Parse(n_Parser/nS_Parser/parser)
|
||||
.=KW_PASS
|
||||
var/node/statement/WhileLoop/stmt=new
|
||||
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)
|
||||
/n_Keyword/nS_Keyword/kwWhile/Parse(n_Parser/nS_Parser/parser)
|
||||
.=KW_PASS
|
||||
var/node/statement/WhileLoop/stmt=new
|
||||
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
|
||||
Parse(n_Parser/nS_Parser/parser)
|
||||
.=KW_PASS
|
||||
if(istype(parser.curBlock, /node/BlockDefinition/GlobalBlock))
|
||||
parser.errors+=new/scriptError/BadToken(parser.curToken)
|
||||
. = KW_WARN
|
||||
var/node/statement/BreakStatement/stmt=new
|
||||
parser.NextToken() //skip 'break' token
|
||||
parser.curBlock.statements+=stmt
|
||||
/n_Keyword/nS_Keyword/kwBreak/Parse(n_Parser/nS_Parser/parser)
|
||||
.=KW_PASS
|
||||
if(istype(parser.curBlock, /node/BlockDefinition/GlobalBlock))
|
||||
parser.errors+=new/scriptError/BadToken(parser.curToken)
|
||||
. = KW_WARN
|
||||
var/node/statement/BreakStatement/stmt=new
|
||||
parser.NextToken() //skip 'break' token
|
||||
parser.curBlock.statements+=stmt
|
||||
|
||||
kwContinue
|
||||
Parse(n_Parser/nS_Parser/parser)
|
||||
.=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
|
||||
/n_Keyword/nS_Keyword/kwContinue/Parse(n_Parser/nS_Parser/parser)
|
||||
.=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
|
||||
Parse(n_Parser/nS_Parser/parser)
|
||||
.=KW_PASS
|
||||
var/node/statement/FunctionDefinition/def=new
|
||||
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
|
||||
while(TRUE) //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))
|
||||
def.parameters+=parser.curToken.value
|
||||
parser.NextToken()
|
||||
else
|
||||
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
|
||||
parser.curBlock.statements+=def
|
||||
else if(parser.curToken.value=="{" && istype(parser.curToken, /token/symbol))
|
||||
def.block = new
|
||||
parser.curBlock.statements+=def
|
||||
parser.curBlock.functions[def.func_name]=def
|
||||
parser.AddBlock(def.block)
|
||||
/n_Keyword/nS_Keyword/kwDef/Parse(n_Parser/nS_Parser/parser)
|
||||
.=KW_PASS
|
||||
var/node/statement/FunctionDefinition/def=new
|
||||
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
|
||||
while(TRUE) //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_FAIL
|
||||
return KW_ERR
|
||||
|
||||
else if(istype(parser.curToken, /token/word))
|
||||
def.parameters+=parser.curToken.value
|
||||
parser.NextToken()
|
||||
else
|
||||
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
|
||||
parser.curBlock.statements+=def
|
||||
else if(parser.curToken.value=="{" && istype(parser.curToken, /token/symbol))
|
||||
def.block = new
|
||||
parser.curBlock.statements+=def
|
||||
parser.curBlock.functions[def.func_name]=def
|
||||
parser.AddBlock(def.block)
|
||||
else
|
||||
parser.errors+=new/scriptError/BadToken(parser.curToken)
|
||||
return KW_FAIL
|
||||
|
||||
@@ -6,18 +6,16 @@
|
||||
An object that reads tokens and produces an AST (abstract syntax tree).
|
||||
*/
|
||||
/n_Parser
|
||||
var
|
||||
var/index = 1
|
||||
/*
|
||||
Var: index
|
||||
The parser's current position in the token's list.
|
||||
*/
|
||||
index = 1
|
||||
list
|
||||
/*
|
||||
Var: tokens
|
||||
A list of tokens in the source code generated by a scanner.
|
||||
*/
|
||||
tokens = new
|
||||
var/list/tokens = new
|
||||
/*
|
||||
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.
|
||||
@@ -25,41 +23,37 @@
|
||||
See Also:
|
||||
- <scriptError>
|
||||
*/
|
||||
errors = new
|
||||
var/list/errors = new
|
||||
/*
|
||||
Var: warnings
|
||||
A list of non-fatal problems in the script.
|
||||
*/
|
||||
warnings = new
|
||||
token
|
||||
var/list/warnings = new
|
||||
/*
|
||||
Var: curToken
|
||||
The token at <index> in <tokens>.
|
||||
*/
|
||||
curToken
|
||||
stack
|
||||
blocks=new
|
||||
node/BlockDefinition
|
||||
GlobalBlock/global_block=new
|
||||
curBlock
|
||||
var/token/curToken
|
||||
var/stack/blocks=new
|
||||
var/node/BlockDefinition/GlobalBlock/global_block=new
|
||||
var/node/BlockDefinition/curBlock
|
||||
|
||||
proc
|
||||
/*
|
||||
Proc: Parse
|
||||
Reads the tokens and returns the AST's <GlobalBlock> node. Be sure to populate the tokens list before calling this procedure.
|
||||
*/
|
||||
Parse()
|
||||
/n_Parser/proc/Parse()
|
||||
|
||||
/*
|
||||
Proc: NextToken
|
||||
Sets <curToken> to the next token in the <tokens> list, or null if there are no more tokens.
|
||||
*/
|
||||
NextToken()
|
||||
if(index>=tokens.len)
|
||||
curToken=null
|
||||
else
|
||||
curToken=tokens[++index]
|
||||
return curToken
|
||||
/n_Parser/proc/NextToken()
|
||||
if(index>=tokens.len)
|
||||
curToken=null
|
||||
else
|
||||
curToken=tokens[++index]
|
||||
return curToken
|
||||
|
||||
/*
|
||||
Class: nS_Parser
|
||||
@@ -74,116 +68,115 @@
|
||||
tokens - A list of tokens to parse.
|
||||
options - An object used for configuration.
|
||||
*/
|
||||
New(tokens[], n_scriptOptions/options)
|
||||
src.tokens=tokens
|
||||
src.options=options
|
||||
curBlock=global_block
|
||||
return ..()
|
||||
/n_Parser/nS_Parser/New(tokens[], n_scriptOptions/options)
|
||||
src.tokens=tokens
|
||||
src.options=options
|
||||
curBlock=global_block
|
||||
return ..()
|
||||
|
||||
Parse()
|
||||
ASSERT(tokens)
|
||||
for(,src.index<=src.tokens.len, src.index++)
|
||||
curToken=tokens[index]
|
||||
switch(curToken.type)
|
||||
if(/token/keyword)
|
||||
var/n_Keyword/kw=options.keywords[curToken.value]
|
||||
kw=new kw()
|
||||
if(kw)
|
||||
if(!kw.Parse(src))
|
||||
return
|
||||
if(/token/word)
|
||||
var/token/ntok
|
||||
if(index+1>tokens.len)
|
||||
errors+=new/scriptError/BadToken(curToken)
|
||||
continue
|
||||
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)
|
||||
/n_Parser/nS_Parser/Parse()
|
||||
ASSERT(tokens)
|
||||
for(,src.index<=src.tokens.len, src.index++)
|
||||
curToken=tokens[index]
|
||||
switch(curToken.type)
|
||||
if(/token/keyword)
|
||||
var/n_Keyword/kw=options.keywords[curToken.value]
|
||||
kw=new kw()
|
||||
if(kw)
|
||||
if(!kw.Parse(src))
|
||||
return
|
||||
if(/token/word)
|
||||
var/token/ntok
|
||||
if(index+1>tokens.len)
|
||||
errors+=new/scriptError/BadToken(curToken)
|
||||
continue
|
||||
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)
|
||||
return
|
||||
return global_block
|
||||
|
||||
proc
|
||||
CheckToken(val, type, err=1, skip=1)
|
||||
if(curToken.value!=val || !istype(curToken,type))
|
||||
if(err)
|
||||
errors+=new/scriptError/ExpectedToken(val, curToken)
|
||||
return 0
|
||||
if(skip)NextToken()
|
||||
return 1
|
||||
|
||||
AddBlock(node/BlockDefinition/B)
|
||||
blocks.Push(curBlock)
|
||||
curBlock=B
|
||||
|
||||
EndBlock()
|
||||
if(curBlock==global_block) return 0
|
||||
curBlock=blocks.Pop()
|
||||
return 1
|
||||
|
||||
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()
|
||||
continue
|
||||
if(/token/end)
|
||||
warnings+=new/scriptError/BadToken(curToken)
|
||||
continue
|
||||
else
|
||||
stmt.value=ParseExpression()
|
||||
errors+=new/scriptError/BadToken(curToken)
|
||||
return
|
||||
return global_block
|
||||
|
||||
/n_Parser/nS_Parser/proc/CheckToken(val, type, err=1, skip=1)
|
||||
if(curToken.value!=val || !istype(curToken,type))
|
||||
if(err)
|
||||
errors+=new/scriptError/ExpectedToken(val, curToken)
|
||||
return 0
|
||||
if(skip)NextToken()
|
||||
return 1
|
||||
|
||||
/n_Parser/nS_Parser/proc/AddBlock(node/BlockDefinition/B)
|
||||
blocks.Push(curBlock)
|
||||
curBlock=B
|
||||
|
||||
/n_Parser/nS_Parser/proc/EndBlock()
|
||||
if(curBlock==global_block) return 0
|
||||
curBlock=blocks.Pop()
|
||||
return 1
|
||||
|
||||
/n_Parser/nS_Parser/proc/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
|
||||
stmt.value=ParseExpression()
|
||||
curBlock.statements+=stmt
|
||||
|
||||
/n_Parser/nS_Parser/proc/ParseFunctionStatement()
|
||||
if(!istype(curToken, /token/word))
|
||||
errors+=new/scriptError("Bad identifier in function call.")
|
||||
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
|
||||
while(TRUE)
|
||||
loops++
|
||||
if(loops>=6000)
|
||||
CRASH("Something TERRIBLE has gone wrong in ParseFunctionStatement ;__;")
|
||||
|
||||
if(!curToken)
|
||||
errors+=new/scriptError/EndOfFile()
|
||||
return
|
||||
if(istype(curToken, /token/symbol) && curToken.value==")")
|
||||
curBlock.statements+=stmt
|
||||
|
||||
ParseFunctionStatement()
|
||||
if(!istype(curToken, /token/word))
|
||||
errors+=new/scriptError("Bad identifier in function call.")
|
||||
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
|
||||
while(TRUE)
|
||||
loops++
|
||||
if(loops>=6000)
|
||||
CRASH("Something TERRIBLE has gone wrong in ParseFunctionStatement ;__;")
|
||||
|
||||
if(!curToken)
|
||||
errors+=new/scriptError/EndOfFile()
|
||||
return
|
||||
if(istype(curToken, /token/symbol) && curToken.value==")")
|
||||
curBlock.statements+=stmt
|
||||
NextToken() //Skip close parenthesis
|
||||
return
|
||||
var/node/expression/P=ParseParamExpression()
|
||||
stmt.parameters+=P
|
||||
if(istype(curToken, /token/symbol) && curToken.value==",") NextToken()
|
||||
NextToken() //Skip close parenthesis
|
||||
return
|
||||
var/node/expression/P=ParseParamExpression()
|
||||
stmt.parameters+=P
|
||||
if(istype(curToken, /token/symbol) && curToken.value==",") NextToken()
|
||||
@@ -6,9 +6,7 @@
|
||||
An object responsible for breaking up source code into tokens for use by the parser.
|
||||
*/
|
||||
/n_Scanner
|
||||
var
|
||||
code
|
||||
list
|
||||
var/code
|
||||
/*
|
||||
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.
|
||||
@@ -16,33 +14,32 @@
|
||||
See Also:
|
||||
- <scriptError>
|
||||
*/
|
||||
errors = new
|
||||
var/list/errors = new
|
||||
/*
|
||||
Var: warnings
|
||||
A list of non-fatal problems in the source code found by the scanner.
|
||||
*/
|
||||
warnings = new
|
||||
var/list/warnings = new
|
||||
|
||||
proc
|
||||
/*
|
||||
Proc: LoadCode
|
||||
Loads source code.
|
||||
*/
|
||||
LoadCode(c)
|
||||
/n_Scanner/proc/LoadCode(c)
|
||||
code=c
|
||||
|
||||
/*
|
||||
Proc: LoadCodeFromFile
|
||||
Gets the code from a file and calls <LoadCode()>.
|
||||
*/
|
||||
LoadCodeFromFile(f)
|
||||
/n_Scanner/proc/LoadCodeFromFile(f)
|
||||
LoadCode(file2text(f))
|
||||
|
||||
/*
|
||||
Proc: Scan
|
||||
Runs the scanner and returns the resulting list of tokens. Ensure that <LoadCode()> has been called first.
|
||||
*/
|
||||
Scan()
|
||||
/n_Scanner/proc/Scan()
|
||||
|
||||
/*
|
||||
Class: nS_Scanner
|
||||
@@ -50,20 +47,15 @@
|
||||
*/
|
||||
/n_Scanner/nS_Scanner
|
||||
|
||||
var
|
||||
/*
|
||||
Variable: codepos
|
||||
The scanner's position in the source code.
|
||||
*/
|
||||
codepos = 1
|
||||
line = 1
|
||||
linepos = 0 //column=codepos-linepos
|
||||
n_scriptOptions/nS_Options/options
|
||||
|
||||
commenting = 0
|
||||
// 1: single-line
|
||||
// 2: multi-line
|
||||
list
|
||||
var/codepos = 1
|
||||
var/line = 1
|
||||
var/linepos = 0 //column=codepos-linepos
|
||||
var/n_scriptOptions/nS_Options/options
|
||||
var/commenting = 0 /// 1 is a single-line comment, 2 is a multi-line comment
|
||||
/*
|
||||
Variable: ignore
|
||||
A list of characters that are ignored by the scanner.
|
||||
@@ -71,7 +63,7 @@
|
||||
Default Value:
|
||||
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
|
||||
A list of characters that end a statement. Each item may only be one character long.
|
||||
@@ -79,7 +71,7 @@
|
||||
Default Value:
|
||||
Semicolon
|
||||
*/
|
||||
end_stmt = list(";")
|
||||
var/list/end_stmt = list(";")
|
||||
/*
|
||||
Variable: string_delim
|
||||
A list of characters that can start and end strings.
|
||||
@@ -87,12 +79,12 @@
|
||||
Default Value:
|
||||
Double and single quotes.
|
||||
*/
|
||||
string_delim = list("\"", "'")
|
||||
var/list/string_delim = list("\"", "'")
|
||||
/*
|
||||
Variable: delim
|
||||
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
|
||||
@@ -106,45 +98,44 @@
|
||||
code - The source code to tokenize.
|
||||
options - An <nS_Options> object used to configure the scanner.
|
||||
*/
|
||||
New(code, n_scriptOptions/nS_Options/options)
|
||||
.=..()
|
||||
ignore+= ascii2text(13) //Carriage return
|
||||
delim += ignore + options.symbols + end_stmt + string_delim
|
||||
src.options=options
|
||||
LoadCode(code)
|
||||
/n_Scanner/nS_Scanner/New(code, n_scriptOptions/nS_Options/options)
|
||||
.=..()
|
||||
ignore+= ascii2text(13) //Carriage return
|
||||
delim += ignore + options.symbols + end_stmt + string_delim
|
||||
src.options=options
|
||||
LoadCode(code)
|
||||
|
||||
Scan() //Creates a list of tokens from source code
|
||||
var/list/tokens=new
|
||||
for(, src.codepos<=length(code), src.codepos++)
|
||||
/n_Scanner/nS_Scanner/Scan() //Creates a list of tokens from source code
|
||||
var/list/tokens=new
|
||||
for(, src.codepos<=length(code), src.codepos++)
|
||||
|
||||
var/char=copytext(code, codepos, codepos+1)
|
||||
if(char=="\n")
|
||||
line++
|
||||
linepos=codepos
|
||||
var/char=copytext(code, codepos, codepos+1)
|
||||
if(char=="\n")
|
||||
line++
|
||||
linepos=codepos
|
||||
|
||||
if(ignore.Find(char))
|
||||
continue
|
||||
else if(char == "/")
|
||||
ReadComment()
|
||||
else if(end_stmt.Find(char))
|
||||
tokens+=new /token/end(char, line, COL)
|
||||
else if(string_delim.Find(char))
|
||||
codepos++ //skip string delimiter
|
||||
tokens+=ReadString(char)
|
||||
else if(options.CanStartID(char))
|
||||
tokens+=ReadWord()
|
||||
else if(options.IsDigit(char))
|
||||
tokens+=ReadNumber()
|
||||
else if(options.symbols.Find(char))
|
||||
tokens+=ReadSymbol()
|
||||
if(ignore.Find(char))
|
||||
continue
|
||||
else if(char == "/")
|
||||
ReadComment()
|
||||
else if(end_stmt.Find(char))
|
||||
tokens+=new /token/end(char, line, COL)
|
||||
else if(string_delim.Find(char))
|
||||
codepos++ //skip string delimiter
|
||||
tokens+=ReadString(char)
|
||||
else if(options.CanStartID(char))
|
||||
tokens+=ReadWord()
|
||||
else if(options.IsDigit(char))
|
||||
tokens+=ReadNumber()
|
||||
else if(options.symbols.Find(char))
|
||||
tokens+=ReadSymbol()
|
||||
|
||||
|
||||
codepos=initial(codepos)
|
||||
line=initial(line)
|
||||
linepos=initial(linepos)
|
||||
return tokens
|
||||
codepos=initial(codepos)
|
||||
line=initial(line)
|
||||
linepos=initial(linepos)
|
||||
return tokens
|
||||
|
||||
proc
|
||||
/*
|
||||
Proc: ReadString
|
||||
Reads a string in the source code into a token.
|
||||
@@ -152,131 +143,131 @@
|
||||
Parameters:
|
||||
start - The character used to start the string.
|
||||
*/
|
||||
ReadString(start)
|
||||
var/buf
|
||||
for(, codepos <= length(code), codepos++)//codepos to length(code))
|
||||
var/char=copytext(code, codepos, codepos+1)
|
||||
/n_Scanner/nS_Scanner/proc/ReadString(start)
|
||||
var/buf
|
||||
for(, codepos <= length(code), codepos++)//codepos to length(code))
|
||||
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)
|
||||
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)
|
||||
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
|
||||
if("\\") //Double backslash
|
||||
buf+="\\"
|
||||
if("n") //\n Newline
|
||||
buf+="\n"
|
||||
else
|
||||
if(char==start) //string delimiter found, end string
|
||||
break
|
||||
else
|
||||
buf+=char //Just a normal character in a string
|
||||
if(!.) return new/token/string(buf, line, COL)
|
||||
|
||||
/*
|
||||
Proc: ReadWord
|
||||
Reads characters separated by an item in <delim> into a token.
|
||||
*/
|
||||
ReadWord()
|
||||
var/char=copytext(code, codepos, codepos+1)
|
||||
var/buf
|
||||
while(!delim.Find(char) && codepos<=length(code))
|
||||
buf+=char
|
||||
char=copytext(code, ++codepos, codepos+1)
|
||||
codepos-- //allow main Scan() proc to read the delimiter
|
||||
if(options.keywords.Find(buf))
|
||||
return new /token/keyword(buf, line, COL)
|
||||
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
|
||||
return new /token/word(buf, line, COL)
|
||||
if(char==start) //string delimiter found, end string
|
||||
break
|
||||
else
|
||||
buf+=char //Just a normal character in a string
|
||||
if(!.) return new/token/string(buf, line, COL)
|
||||
|
||||
/*
|
||||
Proc: ReadSymbol
|
||||
Reads a symbol into a token.
|
||||
Proc: ReadWord
|
||||
Reads characters separated by an item in <delim> into a token.
|
||||
*/
|
||||
ReadSymbol()
|
||||
var/char=copytext(code, codepos, codepos+1)
|
||||
var/buf
|
||||
|
||||
while(options.symbols.Find(buf+char))
|
||||
buf+=char
|
||||
if(++codepos>length(code)) break
|
||||
char=copytext(code, codepos, codepos+1)
|
||||
|
||||
codepos-- //allow main Scan() proc to read the next character
|
||||
return new /token/symbol(buf, line, COL)
|
||||
/n_Scanner/nS_Scanner/proc/ReadWord()
|
||||
var/char=copytext(code, codepos, codepos+1)
|
||||
var/buf
|
||||
while(!delim.Find(char) && codepos<=length(code))
|
||||
buf+=char
|
||||
char=copytext(code, ++codepos, codepos+1)
|
||||
codepos-- //allow main Scan() proc to read the delimiter
|
||||
if(options.keywords.Find(buf))
|
||||
return new /token/keyword(buf, line, COL)
|
||||
else
|
||||
return new /token/word(buf, line, COL)
|
||||
|
||||
/*
|
||||
Proc: ReadNumber
|
||||
Reads a number into a token.
|
||||
Proc: ReadSymbol
|
||||
Reads a symbol into a token.
|
||||
*/
|
||||
ReadNumber()
|
||||
var/char=copytext(code, codepos, codepos+1)
|
||||
var/buf
|
||||
var/dec=0
|
||||
/n_Scanner/nS_Scanner/proc/ReadSymbol()
|
||||
var/char=copytext(code, codepos, codepos+1)
|
||||
var/buf
|
||||
|
||||
while(options.IsDigit(char) || (char=="." && !dec))
|
||||
if(char==".") dec=1
|
||||
buf+=char
|
||||
codepos++
|
||||
char=copytext(code, codepos, codepos+1)
|
||||
var/token/number/T=new(buf, line, COL)
|
||||
if(isnull(text2num(buf)))
|
||||
errors+=new/scriptError("Bad number: ", T)
|
||||
T.value=0
|
||||
codepos-- //allow main Scan() proc to read the next character
|
||||
return T
|
||||
while(options.symbols.Find(buf+char))
|
||||
buf+=char
|
||||
if(++codepos>length(code)) break
|
||||
char=copytext(code, codepos, codepos+1)
|
||||
|
||||
codepos-- //allow main Scan() proc to read the next character
|
||||
return new /token/symbol(buf, line, COL)
|
||||
|
||||
/*
|
||||
Proc: ReadComment
|
||||
Reads a comment and outputs the type of comment
|
||||
Proc: ReadNumber
|
||||
Reads a number into a token.
|
||||
*/
|
||||
/n_Scanner/nS_Scanner/proc/ReadNumber()
|
||||
var/char=copytext(code, codepos, codepos+1)
|
||||
var/buf
|
||||
var/dec=0
|
||||
|
||||
while(options.IsDigit(char) || (char=="." && !dec))
|
||||
if(char==".") dec=1
|
||||
buf+=char
|
||||
codepos++
|
||||
char=copytext(code, codepos, codepos+1)
|
||||
var/token/number/T=new(buf, line, COL)
|
||||
if(isnull(text2num(buf)))
|
||||
errors+=new/scriptError("Bad number: ", T)
|
||||
T.value=0
|
||||
codepos-- //allow main Scan() proc to read the next character
|
||||
return T
|
||||
|
||||
/*
|
||||
Proc: ReadComment
|
||||
Reads a comment and outputs the type of comment
|
||||
*/
|
||||
|
||||
ReadComment()
|
||||
var/char=copytext(code, codepos, codepos+1)
|
||||
var/nextchar=copytext(code, codepos+1, codepos+2)
|
||||
var/charstring = char+nextchar
|
||||
var/comm = 1
|
||||
// 1: single-line comment
|
||||
// 2: multi-line comment
|
||||
var/expectedend = 0
|
||||
/n_Scanner/nS_Scanner/proc/ReadComment()
|
||||
var/char=copytext(code, codepos, codepos+1)
|
||||
var/nextchar=copytext(code, codepos+1, codepos+2)
|
||||
var/charstring = char+nextchar
|
||||
var/comm = 1
|
||||
// 1: single-line comment
|
||||
// 2: multi-line comment
|
||||
var/expectedend = 0
|
||||
|
||||
if(charstring == "//" || charstring == "/*")
|
||||
if(charstring == "/*")
|
||||
comm = 2 // starts a multi-line comment
|
||||
if(charstring == "//" || charstring == "/*")
|
||||
if(charstring == "/*")
|
||||
comm = 2 // starts a multi-line comment
|
||||
|
||||
while(comm)
|
||||
if(++codepos>length(code)) break
|
||||
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(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(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(expectedend) expectedend = 0
|
||||
|
||||
if(comm == 2)
|
||||
errors+=new/scriptError/UnterminatedComment()
|
||||
if(comm == 2)
|
||||
errors+=new/scriptError/UnterminatedComment()
|
||||
|
||||
|
||||
@@ -9,30 +9,29 @@
|
||||
var/line
|
||||
var/column
|
||||
|
||||
New(v, l=0, c=0)
|
||||
value=v
|
||||
line=l
|
||||
column=c
|
||||
/token/New(v, l=0, c=0)
|
||||
value=v
|
||||
line=l
|
||||
column=c
|
||||
|
||||
string
|
||||
symbol
|
||||
word
|
||||
keyword
|
||||
number
|
||||
New()
|
||||
.=..()
|
||||
if(!isnum(value))
|
||||
value=text2num(value)
|
||||
ASSERT(!isnull(value))
|
||||
accessor
|
||||
var/object
|
||||
var/member
|
||||
/token/string
|
||||
/token/symbol
|
||||
/token/word
|
||||
/token/keyword
|
||||
/token/number/New()
|
||||
.=..()
|
||||
if(!isnum(value))
|
||||
value=text2num(value)
|
||||
ASSERT(!isnull(value))
|
||||
/token/accessor
|
||||
var/object
|
||||
var/member
|
||||
|
||||
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
|
||||
/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
|
||||
|
||||
end
|
||||
/token/end
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
/stack
|
||||
var/list
|
||||
contents=new
|
||||
proc
|
||||
Push(value)
|
||||
contents+=value
|
||||
var/list/contents=new
|
||||
/stack/proc/Push(value)
|
||||
contents+=value
|
||||
|
||||
Pop()
|
||||
if(!contents.len) return null
|
||||
. = contents[contents.len]
|
||||
contents.len--
|
||||
/stack/proc/Pop()
|
||||
if(!contents.len) return null
|
||||
. = contents[contents.len]
|
||||
contents.len--
|
||||
|
||||
Top() //returns the item on the top of the stack without removing it
|
||||
if(!contents.len) return null
|
||||
return contents[contents.len]
|
||||
/stack/proc/Top() //returns the item on the top of the stack without removing it
|
||||
if(!contents.len) return null
|
||||
return contents[contents.len]
|
||||
|
||||
Copy()
|
||||
var/stack/S=new()
|
||||
S.contents=src.contents.Copy()
|
||||
return S
|
||||
/stack/proc/Copy()
|
||||
var/stack/S=new()
|
||||
S.contents=src.contents.Copy()
|
||||
return S
|
||||
|
||||
Clear()
|
||||
contents.Cut()
|
||||
/stack/proc/Clear()
|
||||
contents.Cut()
|
||||
Reference in New Issue
Block a user