[MIRROR] Linter diagnostics + bans non-var relative pathing

This commit is contained in:
Chompstation Bot
2021-06-22 22:17:02 +00:00
parent 968067d0e7
commit fb343cec6c
351 changed files with 20094 additions and 11284 deletions

View File

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

View File

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

View File

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